├── .gitignore ├── README.md ├── Web.config ├── WebGLFPS.sln ├── api └── resources.ashx ├── css └── bootstrap-darkly.min.css ├── dev └── notes.txt ├── editor.html ├── fonts ├── glyphicons-halflings-regular.eot ├── glyphicons-halflings-regular.svg ├── glyphicons-halflings-regular.ttf ├── glyphicons-halflings-regular.woff └── glyphicons-halflings-regular.woff2 ├── html └── editor │ ├── directives │ ├── actor-editor.html │ ├── choose-actor-modal.html │ ├── choose-controller-modal.html │ ├── choose-resource-modal.html │ ├── light-editor.html │ ├── material-editor.html │ ├── object-data-editor.html │ ├── trigger-editor.html │ └── vec3-editor.html │ ├── main.html │ └── tabs │ ├── actors.html │ ├── lights.html │ ├── map.html │ ├── player.html │ ├── resources.html │ └── triggers.html ├── index.html ├── js ├── editor │ ├── controllers │ │ ├── actors-controller.js │ │ ├── editor-controller.js │ │ ├── lights-controller.js │ │ ├── map-controller.js │ │ ├── rendering-controller.js │ │ ├── resources-controller.js │ │ └── triggers-controller.js │ ├── directives │ │ ├── actor-editor.js │ │ ├── choose-actor-modal.js │ │ ├── choose-controller-modal.js │ │ ├── choose-resource-modal.js │ │ ├── light-editor.js │ │ ├── material-editor.js │ │ ├── object-data-editor.js │ │ ├── trigger-editor.js │ │ └── vec3-editor.js │ ├── editor-app.js │ └── services │ │ ├── sector-set-builder.js │ │ ├── util.js │ │ └── ws.js ├── engine │ ├── bit-field.js │ ├── camera.js │ ├── canvas-initialiser.js │ ├── constants.js │ ├── editor-helper.js │ ├── effect-manager.js │ ├── engine.js │ ├── fixed-length-array.js │ ├── frame-timer.js │ ├── free-look-camera-controller.js │ ├── gl-manager.js │ ├── gui-draw-spec-builder.js │ ├── gui-layout-animation-manager.js │ ├── gui-layout-manager.js │ ├── keyboard.js │ ├── line-drawer.js │ ├── map-data-helper.js │ ├── map-manager.js │ ├── material-manager.js │ ├── math │ │ ├── math-3d.js │ │ ├── math-aabb.js │ │ ├── math-collision-face.js │ │ ├── math-collision-line.js │ │ ├── math-frustum.js │ │ ├── math-plane.js │ │ ├── math-ray.js │ │ ├── math-sphere.js │ │ └── math-types.js │ ├── mouse.js │ ├── particle-manager.js │ ├── physics-manager.js │ ├── player-controller.js │ ├── render-state-manager.js │ ├── renderer.js │ ├── resource-checker.js │ ├── resource-loader.js │ ├── shadow-map-manager.js │ ├── skinned-mesh-animation-manager.js │ ├── skinned-mesh-manager.js │ ├── sprite-sheet-manager.js │ ├── static-mesh-manager.js │ ├── static-mesh-math-helper.js │ ├── texture-manager.js │ ├── ticker.js │ ├── trigger-manager.js │ ├── unit-tests.js │ ├── util.js │ └── visibility-manager.js ├── game │ └── main.js └── lib │ ├── angular.min.js │ ├── bootstrap.min.js │ ├── gl-matrix-min.js │ ├── gl-matrix.js │ └── jquery.min.js └── resources ├── gui-layouts └── hud.json ├── maps ├── test-map-1.json └── test-map-2.json ├── materials ├── brick-wall-1.json ├── pipe-1.json └── tiled-floor-1.json ├── sector-sets ├── test-map-1.json └── test-map-2.json ├── shaders ├── gui-fs.txt ├── gui-vs.txt ├── line-fs.txt ├── line-vs.txt ├── particle-fs.txt ├── particle-vs.txt ├── skinned-mesh-main-render-fs.txt ├── skinned-mesh-main-render-vs.txt ├── skinned-mesh-shadow-map-build-back-pass-fs.txt ├── skinned-mesh-shadow-map-build-back-pass-vs.txt ├── skinned-mesh-shadow-map-build-front-pass-fs.txt ├── skinned-mesh-shadow-map-build-front-pass-vs.txt ├── static-mesh-main-render-fs.txt ├── static-mesh-main-render-vs.txt ├── static-mesh-shadow-map-build-back-pass-fs.txt ├── static-mesh-shadow-map-build-back-pass-vs.txt ├── static-mesh-shadow-map-build-front-pass-fs.txt └── static-mesh-shadow-map-build-front-pass-vs.txt ├── skinned-mesh-animations ├── column-bend.json └── robot-walk.json ├── skinned-meshes ├── column.json └── robot.json ├── sprite-sheets └── sprite-sheet-1.json ├── static-meshes ├── door-1.json ├── fancy-cube.json ├── test-map-1.json └── test-map-2.json ├── system └── effects.json └── textures ├── brick-wall-1-d.png ├── brick-wall-1-n.png ├── light-1.png ├── particle-1.png ├── particle-2.png ├── pipe-1-d.png ├── pipe-1-n.png ├── sprite-sheet-1.png ├── system ├── dummy-cube-nx.png ├── dummy-cube-ny.png ├── dummy-cube-nz.png ├── dummy-cube-px.png ├── dummy-cube-py.png ├── dummy-cube-pz.png ├── missing-diffuse-texture.png └── missing-normal-texture.png ├── tiled-floor-1-d.png └── tiled-floor-1-n.png /.gitignore: -------------------------------------------------------------------------------- 1 | /.vs 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wegl-fps 2 | This is my attempt to build a rundimentary first person shooter in WebGL. It's very much a work in progress, so don't judge me! 3 | -------------------------------------------------------------------------------- /Web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /WebGLFPS.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "wegl-fps", ".", "{6EA9508A-A82C-4AFC-B0D8-7BCEFE12D4E6}" 7 | ProjectSection(WebsiteProperties) = preProject 8 | TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.0" 9 | Debug.AspNetCompiler.VirtualPath = "/localhost_50116" 10 | Debug.AspNetCompiler.PhysicalPath = "..\wegl-fps\" 11 | Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_50116\" 12 | Debug.AspNetCompiler.Updateable = "true" 13 | Debug.AspNetCompiler.ForceOverwrite = "true" 14 | Debug.AspNetCompiler.FixedNames = "false" 15 | Debug.AspNetCompiler.Debug = "True" 16 | Release.AspNetCompiler.VirtualPath = "/localhost_50116" 17 | Release.AspNetCompiler.PhysicalPath = "..\wegl-fps\" 18 | Release.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_50116\" 19 | Release.AspNetCompiler.Updateable = "true" 20 | Release.AspNetCompiler.ForceOverwrite = "true" 21 | Release.AspNetCompiler.FixedNames = "false" 22 | Release.AspNetCompiler.Debug = "False" 23 | VWDPort = "50116" 24 | SlnRelativePath = "..\wegl-fps\" 25 | EndProjectSection 26 | EndProject 27 | Global 28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 29 | Debug|Any CPU = Debug|Any CPU 30 | EndGlobalSection 31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 32 | {6EA9508A-A82C-4AFC-B0D8-7BCEFE12D4E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {6EA9508A-A82C-4AFC-B0D8-7BCEFE12D4E6}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /api/resources.ashx: -------------------------------------------------------------------------------- 1 | <%@ WebHandler Language="C#" Class="ResourceApiHandler" %> 2 | 3 | using System; 4 | using System.Web; 5 | using System.IO; 6 | using System.Linq; 7 | 8 | public class ResourceApiHandler : IHttpHandler { 9 | 10 | public void ProcessRequest(HttpContext context) 11 | { 12 | string action = HttpContext.Current.Request["action"]; 13 | 14 | if (action == "load-resource-id-list") 15 | { 16 | DoLoadResourceIdList(); 17 | } 18 | else if (action == "save-json-resource") 19 | { 20 | DoSaveJsonResource(); 21 | } 22 | } 23 | 24 | private void DoLoadResourceIdList() 25 | { 26 | string folder = HttpContext.Current.Request["folder"]; 27 | 28 | string folderPath = GetFolderPath(folder); 29 | 30 | string[] resourceIds = Directory.GetFiles(folderPath, "*.*", SearchOption.AllDirectories) 31 | .Select(s => s.Replace(folderPath, "").Replace(Path.GetExtension(s), "").Replace("\\", "/")) 32 | .ToArray(); 33 | 34 | string json = "[" + string.Join(", ", resourceIds.Select(id => "\"" + id + "\"").ToArray()) + "]"; 35 | 36 | HttpContext.Current.Response.ContentType = "text/json"; 37 | HttpContext.Current.Response.Write(json); 38 | } 39 | 40 | private void DoSaveJsonResource() 41 | { 42 | string folder = HttpContext.Current.Request["folder"]; 43 | string newResourceId = HttpContext.Current.Request["newResourceId"]; 44 | string json = HttpContext.Current.Request["json"]; 45 | string oldResourceId = HttpContext.Current.Request["oldResourceId"]; 46 | 47 | string newResourceFilePath = BuildResourceFilePath(folder, newResourceId, ".json"); 48 | 49 | string newResourceFolderPath = Path.GetDirectoryName(newResourceFilePath); 50 | 51 | if (!Directory.Exists(newResourceFolderPath)) 52 | { 53 | Directory.CreateDirectory(newResourceFolderPath); 54 | } 55 | 56 | File.WriteAllText(newResourceFilePath, json); 57 | 58 | if (!string.IsNullOrWhiteSpace(oldResourceId) && oldResourceId != newResourceId) 59 | { 60 | string oldResourceFilePath = BuildResourceFilePath(folder, oldResourceId, ".json"); 61 | 62 | if (System.IO.File.Exists(oldResourceFilePath)) 63 | { 64 | File.Delete(oldResourceFilePath); 65 | } 66 | } 67 | 68 | HttpContext.Current.Response.ContentType = "text/plain"; 69 | HttpContext.Current.Response.Write("OK"); 70 | } 71 | 72 | private string GetFolderPath(string folder) 73 | { 74 | if (folder.Contains(".")) 75 | { 76 | throw new Exception("Not so fast, sonny Jim."); 77 | } 78 | 79 | string folderPath = HttpContext.Current.Server.MapPath("../resources/" + folder + "/"); 80 | 81 | return folderPath; 82 | } 83 | 84 | private string BuildResourceFilePath(string folder, string resourceId, string fileExtension) 85 | { 86 | if (resourceId.Contains(".")) 87 | { 88 | throw new Exception("Not so fast, sonny Jim."); 89 | } 90 | 91 | string folderPath = GetFolderPath(folder); 92 | 93 | return folderPath + resourceId.Replace("/", "\\") + fileExtension; 94 | } 95 | 96 | public bool IsReusable { get { return false; } } 97 | } -------------------------------------------------------------------------------- /dev/notes.txt: -------------------------------------------------------------------------------- 1 | heartbeat 2 | { 3 | 4 | Check resources are loaded (done) 5 | 6 | Coalesce render states exist (done) 7 | * Actors 8 | * Lights 9 | * World static mesh chunks 10 | * GUIs 11 | 12 | Run Physics 13 | 14 | * Offset actors through ether 15 | - Qualifying actors must have: 16 | - physicsMode: ActorPhysicsMode.OffsetThroughEther 17 | - targetOffset: vec3 18 | - speed 19 | 20 | * Push actors through map 21 | - Qualifying actors must have: 22 | - physicsMode: ActorPhysicsMode.PushThroughMap 23 | - applyGravity: true/false 24 | - movementNormal 25 | - speed 26 | - collisionSphere 27 | 28 | * Move particles objects through map 29 | - Qualifying particles must have: 30 | - physicsMode: ParticlePhysicsMode.MoveThroughMap 31 | - direction 32 | - speed 33 | 34 | 35 | Calculate actor final positions (done) 36 | 37 | 38 | Rebuild bounding volumes (done) 39 | * Lights 40 | * Actors 41 | 42 | Update actor resident sectors (done) 43 | 44 | Update render states 45 | * Lights 46 | * World static chunks 47 | * Actors 48 | * GUIs 49 | 50 | Update animations (done) 51 | * Actors 52 | * GUIs 53 | 54 | Check shadow map allocations (done) 55 | 56 | Render (done) 57 | 58 | Game logic 59 | } 60 | 61 | -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /html/editor/directives/actor-editor.html: -------------------------------------------------------------------------------- 1 | 
2 |
{{actor.id}}
3 |
4 | 5 |
6 | 7 | 8 |
9 | 10 |
11 | 12 | 13 |
14 | 15 |
16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 | 26 |
27 | 28 |
29 | 30 | 31 | 32 | 33 | 34 |
35 |
36 | 37 |
38 | 39 |
40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | 48 |
49 | 50 |
51 | 52 |
53 |
54 | 55 |
56 | 57 |
58 |
59 | 60 |
61 |
62 | 63 |
64 | 65 |
66 |
67 | 68 |
69 |
70 | 71 |
72 | 73 |
74 | 75 | 76 | 77 | 78 | 79 |
80 |
81 | 82 | 85 | 86 | 87 |
88 | 89 |
-------------------------------------------------------------------------------- /html/editor/directives/choose-actor-modal.html: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /html/editor/directives/choose-controller-modal.html: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /html/editor/directives/choose-resource-modal.html: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /html/editor/directives/light-editor.html: -------------------------------------------------------------------------------- 1 | 
2 |
{{light.id}}
3 |
4 | 5 |
6 | 7 | 10 |
11 | 12 |
13 | 14 | 15 |
16 | 17 |
18 | 19 | 20 |
21 | 22 |
23 | 24 | 25 |
26 | 27 |
28 | 31 |
32 | 33 |
34 | 37 |
38 | 39 |
40 | 41 |
-------------------------------------------------------------------------------- /html/editor/directives/material-editor.html: -------------------------------------------------------------------------------- 1 | 
2 |
Material Editor
3 |
4 | 5 |
6 | 7 | 8 |
9 | 10 |
11 | 12 |
13 | 14 | 15 | 16 | 17 |
18 |
19 | 20 |
21 | 22 |
23 | 24 | 25 | 26 | 27 |
28 |
29 | 30 |
31 | 32 |
33 | 34 | 35 | 36 | 37 | 38 |
39 |
40 | 41 |

42 | 43 |
44 | 45 |
-------------------------------------------------------------------------------- /html/editor/directives/object-data-editor.html: -------------------------------------------------------------------------------- 1 | 
2 |
3 | 4 |
5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 |
-------------------------------------------------------------------------------- /html/editor/directives/trigger-editor.html: -------------------------------------------------------------------------------- 1 | 
2 |
{{trigger.id}}
3 |
4 | 5 |
6 | 7 | 8 |
9 | 10 |
11 | 12 | 13 |
14 | 15 |
16 | 17 |
18 | 19 |
20 |
21 | 22 | 25 | 26 |
27 | 28 |
-------------------------------------------------------------------------------- /html/editor/directives/vec3-editor.html: -------------------------------------------------------------------------------- 1 | 
2 |
3 |
4 | X: 5 | 6 |
7 |
8 |
9 |
10 | Y: 11 | 12 |
13 |
14 |
15 |
16 | Z: 17 | 18 |
19 |
20 |
-------------------------------------------------------------------------------- /html/editor/main.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 14 | 15 | 16 | 17 | 25 | 26 | 27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /html/editor/tabs/actors.html: -------------------------------------------------------------------------------- 1 | 
2 |
3 |
4 | 5 | 6 | 7 | 8 | 9 |
{{actor.id}}
10 |
11 | 12 |
13 | 14 |
15 | 16 | 17 | 18 |
19 | 20 | 23 |
-------------------------------------------------------------------------------- /html/editor/tabs/lights.html: -------------------------------------------------------------------------------- 1 | 
2 |
3 |
4 | 5 | 6 | 7 | 8 | 9 |
{{light.id}}
10 |
11 | 12 |
13 | 14 |
15 | 16 | 17 | 18 |
19 | 20 | 23 |
-------------------------------------------------------------------------------- /html/editor/tabs/map.html: -------------------------------------------------------------------------------- 1 | 
2 |
3 |
4 | 5 |
6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 | 14 |
15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 | 23 |
24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 | 35 |
-------------------------------------------------------------------------------- /html/editor/tabs/player.html: -------------------------------------------------------------------------------- 1 | 
2 |
3 | 4 | 5 |
6 |
7 | 8 | 9 |
10 |
11 | 12 | -------------------------------------------------------------------------------- /html/editor/tabs/resources.html: -------------------------------------------------------------------------------- 1 | 
2 |
3 | 4 | 12 |
13 | 14 |
15 | 16 | 17 | 18 | 19 |
{{resourceId}}
20 |
21 | 22 |
23 |
24 | New Material 25 | 26 | 27 | 28 | 29 |
30 |
31 | 32 | 33 |
-------------------------------------------------------------------------------- /html/editor/tabs/triggers.html: -------------------------------------------------------------------------------- 1 | 
2 |
3 |
4 | 5 | 6 | 7 | 8 | 9 |
{{trigger.id}}
10 |
11 | 12 |
13 | 14 |
15 | 16 | 17 | 18 |
19 | 20 | 23 |
-------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 70 | 71 | 72 | 73 | 74 |
75 | 76 |
77 | 78 | 79 |
80 | 81 |
82 | 83 | 87 |
88 | 89 | 90 |
91 | 92 |
93 |
94 | FPS: () 95 |
96 | 97 | 98 | 99 | 100 | 101 | 102 |
103 |
104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /js/editor/controllers/actors-controller.js: -------------------------------------------------------------------------------- 1 | editorApp.controller('ActorsController', ['$scope', 'util', function ($scope, util) { 2 | 3 | $scope.editActor = function (actorId) { 4 | 5 | $scope.$broadcast('edit-actor', { 6 | actorId: actorId 7 | }); 8 | } 9 | 10 | $scope.createActor = function () { 11 | 12 | var actor = { 13 | id: 'actor-' + (util.countHashTableKeys(engine.map.actorsById) + 1), 14 | position: [0, 0, 0], 15 | positionOffset: [0, 0, 0], 16 | rotation: [0, 0, 0], 17 | staticMeshId: null, 18 | skinnedMeshId: null, 19 | skinnedMeshAnimationId: null, 20 | frameIndex: 0, 21 | controllerId: null, 22 | data: {} 23 | } 24 | 25 | engine.editorHelper.addActor(actor); 26 | } 27 | 28 | $scope.removeActor = function (actorId) { 29 | 30 | engine.editorHelper.removeActor(actorId); 31 | } 32 | }]); -------------------------------------------------------------------------------- /js/editor/controllers/editor-controller.js: -------------------------------------------------------------------------------- 1 | editorApp.controller('EditorController', ['$scope', 'ws', function ($scope, ws) { 2 | 3 | $scope.mapMetaData = { 4 | mapId: 'test-map-2' 5 | } 6 | 7 | $scope.map = null; 8 | 9 | $scope.startMap = function () { 10 | 11 | engine.startMap(); 12 | } 13 | 14 | $scope.loadMap = function () { 15 | 16 | engine.loadMap($scope.mapMetaData.mapId, function () { 17 | 18 | $scope.map = engine.map; 19 | $scope.$apply(); 20 | }); 21 | } 22 | 23 | $scope.saveMap = function () { 24 | 25 | ws.saveJsonResource('map', $scope.mapMetaData.mapId, $scope.map, null) 26 | .then(function () { }); 27 | } 28 | 29 | /*$scope.chooseWorldMeshSet = function () { 30 | 31 | $scope.map.worldMeshSetId = 'map1'; 32 | 33 | engine.reloadWorldMeshSet(); 34 | }*/ 35 | }]); -------------------------------------------------------------------------------- /js/editor/controllers/lights-controller.js: -------------------------------------------------------------------------------- 1 | editorApp.controller('LightsController', ['$scope', 'util', function ($scope, util) { 2 | 3 | $scope.editLight = function (lightId) { 4 | 5 | $scope.$broadcast('edit-light', { 6 | lightId: lightId 7 | }); 8 | } 9 | 10 | $scope.createLight = function () { 11 | 12 | var light = { 13 | id: 'light-' + (util.countHashTableKeys(engine.map.lightsById) + 1), 14 | type: 'point', 15 | position: [0, 0, 0], 16 | radius: 1, 17 | colour: [1, 1, 1] 18 | } 19 | 20 | engine.editorHelper.addLight(light); 21 | } 22 | 23 | $scope.removeLight = function (lightId) { 24 | 25 | engine.editorHelper.removeLight(lightId); 26 | } 27 | }]); -------------------------------------------------------------------------------- /js/editor/controllers/map-controller.js: -------------------------------------------------------------------------------- 1 | editorApp.controller('MapController', ['$scope', '$rootScope', 'sectorSetBuilder', 'ws', function ($scope, $rootScope, sectorSetBuilder, ws) { 2 | 3 | $scope.chooseWorldStaticMesh = function () { 4 | 5 | $rootScope.$broadcast('choose-resource', { 6 | resourceType: 'static-mesh', 7 | callback: function (staticMeshId) { 8 | 9 | $scope.map.worldStaticMeshId = staticMeshId; 10 | engine.reloadWorldMeshSet(); 11 | } 12 | }); 13 | } 14 | 15 | $scope.rebuildSectorSet = function () { 16 | 17 | engine.resourceLoader.loadJsonResource('static-mesh', $scope.map.worldStaticMeshId, function (staticMesh) { 18 | 19 | var sectorSet = sectorSetBuilder.buildSectorSetForStaticMesh(staticMesh); 20 | 21 | ws.saveJsonResource('sector-set', $scope.map.sectorSetId, sectorSet, $scope.map.sectorSetId); 22 | }); 23 | } 24 | }]); -------------------------------------------------------------------------------- /js/editor/controllers/rendering-controller.js: -------------------------------------------------------------------------------- 1 | editorApp.controller('RenderingController', ['$scope', function ($scope) { 2 | 3 | $scope.renderingOptions = { 4 | renderLightVolumes: false, 5 | renderWorldMeshChunkAABBs: false, 6 | renderActorIdentifiers: false, 7 | renderActorBoundingSpheres: false, 8 | renderTriggers: false 9 | } 10 | 11 | $scope.init = function () { 12 | 13 | $scope.$watch('renderingOptions', function () { 14 | 15 | util.copyObjectPropertiesToOtherObject($scope.renderingOptions, engine.renderer.renderingOptions) 16 | 17 | }, true); 18 | } 19 | 20 | $scope.init(); 21 | }]); -------------------------------------------------------------------------------- /js/editor/controllers/resources-controller.js: -------------------------------------------------------------------------------- 1 | editorApp.controller('ResourcesController', ['$rootScope', '$scope', 'ws', function ($rootScope, $scope, ws) { 2 | 3 | $scope.resourceType = 'map'; 4 | $scope.resourceIds = []; 5 | $scope.newMaterial = { 6 | materialId: '' 7 | } 8 | 9 | $scope.init = function () { 10 | 11 | $scope.$watch('resourceType', function () { 12 | $scope.loadResourceList(); 13 | }); 14 | } 15 | 16 | $scope.loadResourceList = function () { 17 | 18 | ws.loadResourceIdList($scope.resourceType) 19 | .then(function (resourceIds) { 20 | $scope.resourceIds = resourceIds; 21 | }); 22 | } 23 | 24 | $scope.editResource = function (resourceId) { 25 | 26 | if ($scope.resourceType == 'material') { 27 | 28 | $scope.$broadcast('load-and-edit-material', { 29 | materialId: resourceId, 30 | callback: function () { 31 | $scope.loadResourceList(); 32 | } 33 | }); 34 | } 35 | } 36 | 37 | $scope.importStaticMesh = function () { 38 | 39 | $rootScope.$broadcast('import-static-mesh'); 40 | } 41 | 42 | $scope.importSkinnedMesh = function () { 43 | 44 | $rootScope.$broadcast('import-skinned-mesh'); 45 | } 46 | 47 | $scope.importSkinnedMeshAnimation = function () { 48 | 49 | $rootScope.$broadcast('import-skinned-mesh-animation'); 50 | } 51 | 52 | $scope.createMaterial = function () { 53 | 54 | var materialId = $scope.newMaterial.materialId; 55 | 56 | if (materialId == '') { 57 | alert('Please enter a name for the new material.'); 58 | return; 59 | } 60 | 61 | var material = { 62 | 63 | } 64 | 65 | ws.saveJsonResource('material', materialId, material, null) 66 | .then(function () { $scope.loadResourceList(); }); 67 | } 68 | 69 | $scope.init(); 70 | }]); -------------------------------------------------------------------------------- /js/editor/controllers/triggers-controller.js: -------------------------------------------------------------------------------- 1 | editorApp.controller('TriggersController', ['$scope', 'util', function ($scope, util) { 2 | 3 | $scope.editTrigger = function (triggerId) { 4 | 5 | $scope.$broadcast('edit-trigger', { 6 | triggerId: triggerId 7 | }); 8 | } 9 | 10 | $scope.createTrigger = function () { 11 | 12 | var trigger = { 13 | id: 'trigger-' + (util.countHashTableKeys(engine.map.triggersById) + 1), 14 | position: [0, 0, 0], 15 | size: [0, 0, 0], 16 | controllerId: null, 17 | data: {} 18 | } 19 | 20 | engine.editorHelper.addTrigger(trigger); 21 | } 22 | 23 | $scope.removeTrigger = function (triggerId) { 24 | 25 | engine.editorHelper.removeTrigger(triggerId); 26 | } 27 | }]); -------------------------------------------------------------------------------- /js/editor/directives/actor-editor.js: -------------------------------------------------------------------------------- 1 | editorApp.directive('actorEditor', ['$rootScope', function ($rootScope) { 2 | return { 3 | templateUrl: 'html/editor/directives/actor-editor.html', 4 | scope: { 5 | 6 | }, 7 | controller: function ($scope) { 8 | 9 | $scope.chooseStaticMesh = function () { 10 | 11 | $rootScope.$broadcast('choose-resource', { 12 | resourceType: 'static-mesh', 13 | callback: function (resourceId) { 14 | $scope.actor.staticMeshId = resourceId; 15 | } 16 | }); 17 | } 18 | 19 | $scope.clearStaticMesh = function () { 20 | 21 | $scope.actor.staticMeshId = null; 22 | } 23 | 24 | $scope.chooseSkinnedMesh = function () { 25 | 26 | $rootScope.$broadcast('choose-resource', { 27 | resourceType: 'skinned-mesh', 28 | callback: function (resourceId) { 29 | $scope.actor.skinnedMeshId = resourceId; 30 | } 31 | }); 32 | } 33 | 34 | $scope.clearSkinnedMesh = function () { 35 | 36 | $scope.actor.skinnedMeshId = null; 37 | } 38 | 39 | $scope.chooseSkinnedMeshAnimation = function () { 40 | 41 | $rootScope.$broadcast('choose-resource', { 42 | resourceType: 'skinned-mesh-animation', 43 | callback: function (resourceId) { 44 | $scope.actor.skinnedMeshAnimationId = resourceId; 45 | } 46 | }); 47 | } 48 | 49 | $scope.clearSkinnedMeshAnimation = function () { 50 | 51 | $scope.actor.skinnedMeshAnimationId = null; 52 | } 53 | 54 | $scope.createHitBox = function () { 55 | 56 | $scope.actor.hitBox = new AABB([-0.5, 1, 0.5], [0.5, 0, -0.5]); 57 | } 58 | 59 | $scope.removeHitBox = function () { 60 | 61 | $scope.actor.hitBox = null; 62 | } 63 | 64 | $scope.chooseController = function () { 65 | 66 | $rootScope.$broadcast('choose-controller', { 67 | controllerType: 'actor', 68 | callback: function (controllerId) { 69 | $scope.actor.controllerId = controllerId; 70 | 71 | engine.mapDataHelper.checkActorData($scope.actor); 72 | $scope.loadDataSchema(); 73 | } 74 | }); 75 | } 76 | 77 | $scope.clearController = function () { 78 | 79 | $scope.actor.controllerId = null; 80 | $scope.loadDataSchema(); 81 | } 82 | 83 | $scope.loadDataSchema = function () { 84 | 85 | $scope.dataSchema = null; 86 | var controller = engine.actorControllersById[$scope.actor.controllerId]; 87 | if (controller != null) { 88 | $scope.dataSchema = controller.dataSchema; 89 | } 90 | } 91 | }, 92 | link: function ($scope, element, attrs) { 93 | 94 | $scope.$on('edit-actor', function (event, args) { 95 | 96 | $scope.actor = engine.map.actorsById[args.actorId]; 97 | 98 | $scope.loadDataSchema(); 99 | 100 | engine.renderer.renderingParameters.renderActorIdentifierForActorId = $scope.actor.id; 101 | }); 102 | } 103 | } 104 | }]); -------------------------------------------------------------------------------- /js/editor/directives/choose-actor-modal.js: -------------------------------------------------------------------------------- 1 | editorApp.directive('chooseActorModal', [function () { 2 | return { 3 | templateUrl: 'html/editor/directives/choose-actor-modal.html', 4 | scope: { 5 | 6 | }, 7 | controller: function ($scope) { 8 | 9 | $scope.chooseActor = function (actor) { 10 | 11 | $scope.callback(actor); 12 | 13 | $scope.closeModal(); 14 | } 15 | }, 16 | link: function ($scope, element, attrs) { 17 | 18 | $scope.$on('choose-actor', function (event, args) { 19 | 20 | $scope.callback = args.callback; 21 | $scope.actorsById = engine.map.actorsById; 22 | 23 | $scope.showModal(); 24 | }); 25 | 26 | $scope.showModal = function () { 27 | 28 | $(element).find('.modal').modal('show'); 29 | } 30 | 31 | $scope.closeModal = function () { 32 | 33 | $(element).find('.modal').modal('hide'); 34 | } 35 | } 36 | } 37 | }]);; -------------------------------------------------------------------------------- /js/editor/directives/choose-controller-modal.js: -------------------------------------------------------------------------------- 1 | editorApp.directive('chooseControllerModal', [function () { 2 | return { 3 | templateUrl: 'html/editor/directives/choose-controller-modal.html', 4 | scope: { 5 | 6 | }, 7 | controller: function ($scope) { 8 | 9 | $scope.chooseController = function (controllerId) { 10 | 11 | $scope.callback(controllerId); 12 | 13 | $scope.closeModal(); 14 | } 15 | }, 16 | link: function ($scope, element, attrs) { 17 | 18 | $scope.$on('choose-controller', function (event, args) { 19 | 20 | $scope.callback = args.callback; 21 | 22 | var controllerLookupName = 23 | args.controllerType == 'game' ? 'gameControllersById' : 24 | args.controllerType == 'actor' ? 'actorControllersById' : 25 | args.controllerType == 'emitter' ? 'emitterControllersById' : 26 | args.controllerType == 'particle' ? 'particleControllersById' : 27 | args.controllerType == 'trigger' ? 'triggerControllersById' : null; 28 | 29 | $scope.controllerLookup = engine[controllerLookupName]; 30 | 31 | $scope.showModal(); 32 | }); 33 | 34 | $scope.showModal = function () { 35 | 36 | $(element).find('.modal').modal('show'); 37 | } 38 | 39 | $scope.closeModal = function () { 40 | 41 | $(element).find('.modal').modal('hide'); 42 | } 43 | } 44 | } 45 | }]);; -------------------------------------------------------------------------------- /js/editor/directives/choose-resource-modal.js: -------------------------------------------------------------------------------- 1 | editorApp.directive('chooseResourceModal', ['ws', function (ws) { 2 | return { 3 | templateUrl: 'html/editor/directives/choose-resource-modal.html', 4 | scope: { 5 | 6 | }, 7 | controller: function ($scope) { 8 | 9 | $scope.chooseResource = function (resourceId) { 10 | 11 | $scope.callback(resourceId); 12 | 13 | $scope.closeModal(); 14 | } 15 | }, 16 | link: function ($scope, element, attrs) { 17 | 18 | $scope.$on('choose-resource', function (event, args) { 19 | 20 | $scope.callback = args.callback; 21 | $scope.resourceType = args.resourceType; 22 | 23 | ws.loadResourceIdList($scope.resourceType).then(function (resourceIds) { 24 | $scope.resourceIds = resourceIds; 25 | }); 26 | 27 | $scope.showModal(); 28 | }); 29 | 30 | $scope.showModal = function () { 31 | 32 | $(element).find('.modal').modal('show'); 33 | } 34 | 35 | $scope.closeModal = function () { 36 | 37 | $(element).find('.modal').modal('hide'); 38 | } 39 | } 40 | } 41 | }]);; -------------------------------------------------------------------------------- /js/editor/directives/light-editor.js: -------------------------------------------------------------------------------- 1 | editorApp.directive('lightEditor', [function () { 2 | return { 3 | templateUrl: 'html/editor/directives/light-editor.html', 4 | scope: { 5 | 6 | }, 7 | controller: function ($scope) { 8 | 9 | $scope.$watch('light', function () { 10 | 11 | if ($scope.light != null) { 12 | engine.editorHelper.invalidateLight($scope.light.id); 13 | } 14 | 15 | }, true); 16 | }, 17 | link: function ($scope, element, attrs) { 18 | 19 | $scope.$on('edit-light', function (event, args) { 20 | 21 | $scope.light = engine.map.lightsById[args.lightId]; 22 | 23 | engine.renderer.renderingOptions.renderLightVolumeForLightId = $scope.light.id; 24 | }); 25 | } 26 | } 27 | }]); -------------------------------------------------------------------------------- /js/editor/directives/material-editor.js: -------------------------------------------------------------------------------- 1 | editorApp.directive('materialEditor', ['$rootScope', '$timeout', 'ws', function ($rootScope, $timeout, ws) { 2 | return { 3 | templateUrl: 'html/editor/directives/material-editor.html', 4 | scope: { 5 | 6 | }, 7 | controller: function ($scope) { 8 | 9 | $scope.save = function () { 10 | 11 | /*ws.get( 12 | '/ResourceApi/SaveJsonResource', 13 | { 14 | folder: engine.resourceLoader.getFolderNameForResourceType('material'), 15 | oldResourceId: $scope.materialMetaData.oldMaterialId, 16 | newResourceId: $scope.materialMetaData.newMaterialId, 17 | json: angular.toJson($scope.material) 18 | }) 19 | .then(function () { 20 | 21 | $scope.materialMetaData.oldMaterialId = $scope.materialMetaData.newMaterialId; 22 | 23 | if ($scope.callback != null) { 24 | $scope.callback(); 25 | } 26 | });*/ 27 | 28 | ws.saveJsonResource('material', $scope.materialMetaData.newMaterialId, $scope.material, $scope.materialMetaData.oldMaterialId) 29 | .then(function () { 30 | $scope.materialMetaData.oldMaterialId = $scope.materialMetaData.newMaterialId; 31 | 32 | if ($scope.callback != null) { 33 | $scope.callback(); 34 | } 35 | }); 36 | } 37 | 38 | $scope.chooseDiffuseTexture = function () { 39 | 40 | $rootScope.$broadcast('choose-resource', { 41 | resourceType: 'texture', 42 | callback: function (resourceId) { 43 | $scope.material.diffuseTextureId = resourceId; 44 | } 45 | }); 46 | } 47 | 48 | $scope.chooseNormalTexture = function () { 49 | 50 | $rootScope.$broadcast('choose-resource', { 51 | resourceType: 'texture', 52 | callback: function (resourceId) { 53 | $scope.material.normalTextureId = resourceId; 54 | } 55 | }); 56 | } 57 | 58 | $scope.chooseSelfIlluminationTexture = function () { 59 | 60 | $rootScope.$broadcast('choose-resource', { 61 | resourceType: 'texture', 62 | callback: function (resourceId) { 63 | $scope.material.selfIlluminationTextureId = resourceId; 64 | } 65 | }); 66 | } 67 | 68 | $scope.clearSelfIlluminationTexture = function () { 69 | 70 | $scope.material.selfIlluminationTextureId = null; 71 | } 72 | }, 73 | link: function ($scope, element, attrs) { 74 | 75 | $scope.$on('load-and-edit-material', function (event, args) { 76 | 77 | $scope.callback = args.callback; 78 | 79 | $scope.materialMetaData = { 80 | oldMaterialId: args.materialId, 81 | newMaterialId: args.materialId 82 | } 83 | 84 | engine.materialManager.loadMaterial($scope.materialMetaData.oldMaterialId, function (material) { 85 | 86 | $timeout(function () { 87 | $scope.material = material; 88 | }) 89 | }); 90 | }); 91 | } 92 | } 93 | }]); -------------------------------------------------------------------------------- /js/editor/directives/object-data-editor.js: -------------------------------------------------------------------------------- 1 | editorApp.directive('objectDataEditor', ['$rootScope', function ($rootScope) { 2 | return { 3 | templateUrl: 'html/editor/directives/object-data-editor.html', 4 | scope: { 5 | obj: '=obj', 6 | schema: '=schema' 7 | }, 8 | controller: function ($scope) { 9 | 10 | $scope.chooseActor = function (propertyName) { 11 | 12 | $rootScope.$broadcast('choose-actor', { 13 | callback: function (actor) { 14 | $scope.obj.data[propertyName] = actor.id; 15 | } 16 | }); 17 | } 18 | 19 | $scope.clearValue = function (propertyName) { 20 | 21 | $scope.obj.data[propertyName] = ''; 22 | } 23 | }, 24 | link: function ($scope, element, attrs) { 25 | 26 | } 27 | } 28 | }]); -------------------------------------------------------------------------------- /js/editor/directives/trigger-editor.js: -------------------------------------------------------------------------------- 1 | editorApp.directive('triggerEditor', ['$rootScope', function ($rootScope) { 2 | return { 3 | templateUrl: 'html/editor/directives/trigger-editor.html', 4 | scope: { 5 | 6 | }, 7 | controller: function ($scope) { 8 | 9 | $scope.loadDataSchema = function () { 10 | 11 | $scope.dataSchema = null; 12 | var controller = engine.triggerControllersById[$scope.trigger.controllerId]; 13 | if (controller != null) { 14 | $scope.dataSchema = controller.dataSchema; 15 | } 16 | } 17 | }, 18 | link: function ($scope, element, attrs) { 19 | 20 | $scope.$on('edit-trigger', function (event, args) { 21 | 22 | $scope.trigger = engine.map.triggersById[args.triggerId]; 23 | 24 | $scope.loadDataSchema(); 25 | }); 26 | } 27 | } 28 | }]); -------------------------------------------------------------------------------- /js/editor/directives/vec3-editor.js: -------------------------------------------------------------------------------- 1 | editorApp.directive('vec3Editor', function () { 2 | return { 3 | templateUrl: 'html/editor/directives/vec3-editor.html', 4 | scope: { 5 | vec: '=vec' 6 | }, 7 | controller: function ($scope) { 8 | }, 9 | link: function ($scope, element, attrs) { 10 | } 11 | } 12 | }); -------------------------------------------------------------------------------- /js/editor/editor-app.js: -------------------------------------------------------------------------------- 1 | var editorApp = angular.module('editorApp', []); -------------------------------------------------------------------------------- /js/editor/services/sector-set-builder.js: -------------------------------------------------------------------------------- 1 | editorApp.factory('sectorSetBuilder', [function () { 2 | 3 | var sectorSetBuilder = { 4 | 5 | buildSectorSetForStaticMesh: function (staticMesh) { 6 | 7 | var self = this; 8 | 9 | engine.staticMeshMathHelper.buildStaticMeshChunkCollisionFaces(staticMesh); 10 | engine.staticMeshMathHelper.findStaticMeshPointCompletelyOutsideOfExtremities(staticMesh); 11 | 12 | var sectorSet = { 13 | metrics: { 14 | sectorCount: [4, 2, 3], 15 | sectorSize: [12, 12, 12], 16 | rootOrigin: [-24, 0, 12] 17 | }, 18 | sectors: [] 19 | } 20 | 21 | var sectorPointsByIndex = []; 22 | 23 | console.log('Creating sector points.'); 24 | 25 | self.forEachSector(sectorSet, function (sectorIndex, sectorOrigin) { 26 | 27 | sectorPointsByIndex[sectorIndex] = []; 28 | 29 | for (var i = 0; i < 150; i++) { 30 | 31 | var point = vec3.create(); 32 | 33 | self.createRandomPointWithinSector(point, sectorSet.metrics, sectorOrigin); 34 | 35 | if (engine.staticMeshMathHelper.determineIfPointIsWithinStaticMesh(point, staticMesh)) { 36 | sectorPointsByIndex[sectorIndex].push(point); 37 | } 38 | } 39 | }); 40 | 41 | var line = new CollisionLine(); 42 | 43 | self.forEachSector(sectorSet, function (sectorAIndex, sectorAOrigin) { 44 | 45 | console.log('Checking sector ' + sectorAIndex + '.'); 46 | 47 | var sector = { 48 | visibleSectorIndexes: [] 49 | } 50 | 51 | sector.visibleSectorIndexes.push(sectorAIndex); 52 | 53 | sectorSet.sectors[sectorAIndex] = sector; 54 | 55 | self.forEachSector(sectorSet, function (sectorBIndex, sectorBOrigin) { 56 | 57 | if (sectorAIndex == sectorBIndex) { 58 | return; 59 | } 60 | 61 | for (var i = 0; i < sectorPointsByIndex[sectorAIndex].length; i++) { 62 | for (var j = 0; j < sectorPointsByIndex[sectorBIndex].length; j++) { 63 | 64 | line.from = sectorPointsByIndex[sectorAIndex][i]; 65 | line.to = sectorPointsByIndex[sectorBIndex][j]; 66 | math3D.buildCollisionLineFromFromAndToPoints(line); 67 | 68 | if (!self.determineIfLineIntersectsAnyFace(line, staticMesh)) { 69 | sector.visibleSectorIndexes.push(sectorBIndex); 70 | return; 71 | } 72 | } 73 | } 74 | }); 75 | }); 76 | 77 | console.log('Done!'); 78 | 79 | return sectorSet; 80 | }, 81 | 82 | forEachSector: function (sectorSet, callback) { 83 | 84 | var index = 0; 85 | var origin = vec3.create(); 86 | 87 | for (var x = 0; x < sectorSet.metrics.sectorCount[0]; x++) { 88 | 89 | for (var y = 0; y < sectorSet.metrics.sectorCount[1]; y++) { 90 | 91 | for (var z = 0; z < sectorSet.metrics.sectorCount[2]; z++) { 92 | 93 | vec3.set( 94 | origin, 95 | x * sectorSet.metrics.sectorSize[0], 96 | y * sectorSet.metrics.sectorSize[1], 97 | z * -sectorSet.metrics.sectorSize[2]); 98 | 99 | vec3.add(origin, sectorSet.metrics.rootOrigin, origin); 100 | 101 | callback(index, origin); 102 | 103 | index++ 104 | } 105 | } 106 | } 107 | }, 108 | 109 | /*createRandomLineBetweenSectors: function (out, sectorMetrics, sectorAOrigin, sectorBOrigin) { 110 | 111 | this.createRandomPointWithinSector(out.from, sectorMetrics, sectorAOrigin); 112 | this.createRandomPointWithinSector(out.to, sectorMetrics, sectorBOrigin); 113 | 114 | math3D.buildCollisionLineFromFromAndToPoints(out); 115 | },*/ 116 | 117 | createRandomPointWithinSector: function (out, sectorMetrics, sectorOrigin) { 118 | 119 | vec3.copy(out, sectorOrigin); 120 | 121 | out[0] += Math.random() * sectorMetrics.sectorSize[0]; 122 | out[1] += Math.random() * sectorMetrics.sectorSize[1]; 123 | out[2] -= Math.random() * sectorMetrics.sectorSize[2]; 124 | }, 125 | 126 | determineIfLineIntersectsAnyFace: function (line, staticMesh) { 127 | 128 | for (var chunkIndex = 0; chunkIndex < staticMesh.chunks.length; chunkIndex++) { 129 | 130 | var chunk = staticMesh.chunks[chunkIndex]; 131 | 132 | // TODO - AABB check 133 | 134 | for (var faceIndex = 0; faceIndex < chunk.collisionFaces.length; faceIndex++) { 135 | 136 | var collisionFace = chunk.collisionFaces[faceIndex]; 137 | 138 | var faceIntersectionType = math3D.calculateCollisionLineIntersectionWithCollisionFace(null, line, collisionFace) 139 | 140 | if (faceIntersectionType != FaceIntersectionType.None) { 141 | 142 | return true; 143 | } 144 | } 145 | } 146 | 147 | return false; 148 | } 149 | } 150 | 151 | return sectorSetBuilder; 152 | }]); -------------------------------------------------------------------------------- /js/editor/services/util.js: -------------------------------------------------------------------------------- 1 | editorApp.factory('util', [function () { 2 | 3 | var util = { 4 | 5 | countHashTableKeys: function (hashtable) { 6 | 7 | var count = 0; 8 | 9 | for (var propName in hashtable) { 10 | 11 | count++; 12 | } 13 | 14 | return count; 15 | } 16 | } 17 | 18 | return util; 19 | }]); -------------------------------------------------------------------------------- /js/editor/services/ws.js: -------------------------------------------------------------------------------- 1 | editorApp.factory('ws', ['$http', '$q', function ($http, $q) { 2 | 3 | $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8;'; 4 | 5 | var ws = { 6 | 7 | loadResourceIdList: function (resourceType) { 8 | 9 | return this.get( 10 | 'api/resources.ashx', 11 | { 12 | action: 'load-resource-id-list', 13 | folder: engine.resourceLoader.getFolderNameForResourceType(resourceType) 14 | }); 15 | }, 16 | 17 | saveJsonResource: function (resourceType, newResourceId, resource, oldResourceId) { 18 | 19 | return this.post( 20 | 'api/resources.ashx', 21 | { 22 | action: 'save-json-resource', 23 | folder: engine.resourceLoader.getFolderNameForResourceType(resourceType), 24 | newResourceId: newResourceId, 25 | oldResourceId: oldResourceId, 26 | json: angular.toJson(resource) 27 | }); 28 | }, 29 | 30 | get: function (url, parameters) { 31 | 32 | var deferred = $q.defer(); 33 | 34 | $http.get( 35 | url + '?noCache=' + Math.round(Math.random() * 10000), 36 | { 37 | params: parameters 38 | }) 39 | .success(function (response) { 40 | deferred.resolve(response); 41 | }); 42 | 43 | return deferred.promise; 44 | }, 45 | 46 | post: function (url, parameters) { 47 | 48 | var deferred = $q.defer(); 49 | 50 | var postDataChunks = []; 51 | for (var propertyName in parameters) { 52 | postDataChunks.push(encodeURIComponent(propertyName) + '=' + encodeURIComponent(parameters[propertyName])); 53 | } 54 | var postData = postDataChunks.join("&"); 55 | 56 | $http.post( 57 | url + '?noCache=' + Math.round(Math.random() * 10000), 58 | postData) 59 | .success(function (response) { 60 | deferred.resolve(response); 61 | }); 62 | 63 | return deferred.promise; 64 | } 65 | } 66 | 67 | return ws; 68 | }]); -------------------------------------------------------------------------------- /js/engine/bit-field.js: -------------------------------------------------------------------------------- 1 | function BitField() { 2 | 3 | this.length = 0;//length; 4 | this.sizeInBytes = 0;//Math.ceil(this.length / 8); 5 | this.data = null;//new Uint8Array(this.sizeInBytes); 6 | 7 | this.getBit = function (index) { 8 | 9 | var byteIndex = Math.floor(index / 8); 10 | var bitOffset = index % 8; 11 | var bitMask = 0x80 >> bitOffset; 12 | 13 | var maskedByte = this.data[byteIndex] & bitMask; 14 | var bitSet = maskedByte != 0; 15 | 16 | return bitSet; 17 | } 18 | 19 | this.setBit = function (index) { 20 | 21 | var byteIndex = Math.floor(index / 8); 22 | var bitOffset = index % 8; 23 | var bitMask = 0x80 >> bitOffset; 24 | var invBitMask = ~bitMask; 25 | 26 | var byte = this.data[byteIndex]; 27 | this.data[byteIndex] = (byte & invBitMask) | bitMask; 28 | } 29 | 30 | this.unsetBit = function (index) { 31 | 32 | var byteIndex = Math.floor(index / 8); 33 | var bitOffset = index % 8; 34 | var bitMask = 0x80 >> bitOffset; 35 | var invBitMask = ~bitMask; 36 | 37 | var byte = this.data[byteIndex]; 38 | this.data[byteIndex] = (byte & invBitMask); 39 | } 40 | 41 | this.reset = function (length) { 42 | 43 | if (this.length != length) { 44 | this.length = length; 45 | this.sizeInBytes = Math.ceil(this.length / 8); 46 | this.data = new Uint8Array(this.sizeInBytes); 47 | } 48 | 49 | for (var i = 0; i < this.data.length; i++) { 50 | this.data[i] = 0; 51 | } 52 | } 53 | 54 | this.countSetBits = function () { 55 | 56 | var count = 0; 57 | for (var i = 0; i < this.length; i++) { 58 | if (this.getBit(i)) { 59 | count++; 60 | } 61 | } 62 | return count; 63 | } 64 | } -------------------------------------------------------------------------------- /js/engine/camera.js: -------------------------------------------------------------------------------- 1 | function Camera(engine) { 2 | 3 | // Members. 4 | this.position = vec3.create(); 5 | 6 | /*this.axes = { 7 | xAxis: [1, 0, 0], 8 | yAxis: [0, 1, 0], 9 | zAxis: [0, 0, 1] 10 | };*/ 11 | this.axes = new Axes(); 12 | 13 | this.lookAt = vec3.create(); 14 | this.viewMatrix = mat4.create(); 15 | this.projMatrix = mat4.create(); 16 | this.viewProjMatrix = mat4.create(); 17 | 18 | this.init = function (callback) { 19 | 20 | callback(); 21 | } 22 | 23 | this.updateMatrixes = function (fov, aspectRatio, nearClippingDistance, farClippingDistance) { 24 | 25 | this.updateViewMatrix(); 26 | this.updateProjMatrix(fov, aspectRatio, nearClippingDistance, farClippingDistance); 27 | this.updateViewProjMatrix(); 28 | } 29 | 30 | this.updateViewMatrix = function () { 31 | 32 | //var viewMatrix = mat4.create(); 33 | 34 | //var lookAt = vec3.create(); 35 | vec3.add(this.lookAt, this.position, this.axes.zAxis); 36 | 37 | mat4.lookAt(this.viewMatrix, this.position, this.lookAt, this.axes.yAxis); 38 | 39 | //return viewMatrix; 40 | } 41 | 42 | this.updateProjMatrix = function (fov, aspectRatio, nearClippingDistance, farClippingDistance) { 43 | 44 | mat4.perspective(this.projMatrix, fov, aspectRatio, nearClippingDistance, farClippingDistance); 45 | } 46 | 47 | this.updateViewProjMatrix = function () { 48 | 49 | //var viewMatrix = this.makeViewMatrix(); 50 | 51 | //var projMatrix = mat4.create(); 52 | //mat4.perspective(projMatrix, fov, aspectRatio, nearClippingDistance, farClippingDistance); 53 | 54 | //var viewProjMatrix = mat4.create(); 55 | mat4.multiply(this.viewProjMatrix, this.projMatrix, this.viewMatrix); 56 | 57 | //return viewProjMatrix; 58 | } 59 | } -------------------------------------------------------------------------------- /js/engine/canvas-initialiser.js: -------------------------------------------------------------------------------- 1 | function CanvasInitialiser(engine) { 2 | 3 | this.init = function (callback) { 4 | 5 | if (document.getElementById('canvas') != null) { 6 | callback(); 7 | return; 8 | } 9 | 10 | var fullscreenMode = document.getElementById('cb-fullscreen').checked; 11 | var resolutionMultiplier = document.getElementById('lb-resolution-scale').value; 12 | 13 | var canvasSize = { 14 | width: 0, 15 | height: 0 16 | } 17 | 18 | if (fullscreenMode) { 19 | 20 | canvasSize.width = window.screen.width; 21 | canvasSize.height = window.screen.height; 22 | 23 | } else { 24 | 25 | canvasSize.width = 960;//240 26 | canvasSize.height = canvasSize.width * 0.5625; 27 | } 28 | 29 | var canvasResolution = { 30 | width: canvasSize.width * resolutionMultiplier, 31 | height: canvasSize.height * resolutionMultiplier 32 | } 33 | 34 | var canvasContainer = document.getElementById('canvas-container'); 35 | canvasContainer.innerHTML = ''; 36 | 37 | if (!fullscreenMode) { 38 | canvasContainer.style.width = canvasSize.width + 'px'; 39 | canvasContainer.style.height = canvasSize.height + 'px'; 40 | canvasContainer.style.borderWidth = '4px'; 41 | canvasContainer.style.borderColor = 'grey'; 42 | canvasContainer.style.borderStyle = 'solid' 43 | canvasContainer.style.marginTop = '100px'; 44 | canvasContainer.style.marginLeft = 'auto'; 45 | canvasContainer.style.marginRight = 'auto'; 46 | } 47 | 48 | var settingsContainer = document.getElementById('settings-container'); 49 | settingsContainer.style.display = 'none'; 50 | 51 | var statsContainer = document.getElementById('stats-container'); 52 | statsContainer.style.display = 'block'; 53 | 54 | if (fullscreenMode) { 55 | 56 | var mainContainer = document.getElementById('main-container'); 57 | 58 | if (mainContainer.webkitRequestFullscreen) { 59 | 60 | document.addEventListener('webkitfullscreenchange', function () { 61 | callback(); 62 | }); 63 | 64 | } else if (mainContainer.mozRequestFullscreen) { 65 | 66 | document.addEventListener('mozfullscreenchange', function () { 67 | callback(); 68 | }); 69 | 70 | } else if (mainContainer.requestFullscreen) { 71 | 72 | document.addEventListener('fullscreenchange', function () { 73 | callback(); 74 | }); 75 | } 76 | 77 | mainContainer.requestFullscreen = 78 | mainContainer.requestFullscreen || 79 | mainContainer.mozRequestFullscreen || 80 | mainContainer.webkitRequestFullscreen; 81 | 82 | mainContainer.requestFullscreen(); 83 | 84 | } else { 85 | callback(); 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /js/engine/constants.js: -------------------------------------------------------------------------------- 1 | var ShadowMapBuildResult = { 2 | NotBuilt: 0, 3 | Built: 1, 4 | BuiltWithDynamicObjects: 2, 5 | BuiltWithoutDynamicObjects: 3 6 | } 7 | 8 | var FrustumPlane = { 9 | Near: 0, 10 | Far: 1, 11 | Top: 2, 12 | Bottom: 3, 13 | Left: 4, 14 | Right: 5 15 | } 16 | 17 | var EngineLimits = { 18 | //MaxVisibleWorldStaticMeshChunkIndexesForCamera: 1000, 19 | //MaxVisibleWorldStaticMeshChunkIndexesPerLight: 400, 20 | MaxVisibleActorsIdsPerLight: 100, 21 | MaxVisibleActorsIdsForCamera: 100, 22 | //MaxActorResidentSectorIndexes: 16, 23 | MaxVisibleLightIdsForCamera: 1000, 24 | MaxEffectiveLightsPerObject: 5 25 | } 26 | 27 | var RgbColours = { 28 | Red: [1, 0, 0], 29 | Green: [0, 1, 0], 30 | Blue: [0, 0, 1], 31 | White: [1, 1, 1] 32 | } 33 | 34 | var FaceIntersectionType = { 35 | None: 0, 36 | FrontSide: 1, 37 | BackSide: 2 38 | } 39 | 40 | var SpritePropertyId = { 41 | PositionXOffset: 1, 42 | PositionYOffset: 2, 43 | SizeXOffset: 3, 44 | SizeYOffset: 4, 45 | RotationOffset: 5, 46 | Visible: 6 47 | } 48 | 49 | var ActorPhysicsMode = { 50 | None: 0, 51 | OffsetThroughEther: 1, 52 | PushThroughMapTowardsDirection: 2, 53 | PushThroughMapTowardsDestination: 3 54 | } 55 | 56 | var ParticlePhysicsMode = { 57 | None: 0, 58 | MoveThroughMap: 1 59 | } 60 | 61 | var ParticleCreator = { 62 | None: 0, 63 | Player: 1, 64 | Actor: 2 65 | } 66 | 67 | var ParticleCollisionType = { 68 | Actor: 0, 69 | Map: 1, 70 | Player: 2 71 | } -------------------------------------------------------------------------------- /js/engine/editor-helper.js: -------------------------------------------------------------------------------- 1 | function EditorHelper(engine) { 2 | 3 | this.init = function (callback) { 4 | 5 | callback(); 6 | } 7 | 8 | this.addLight = function (light) { 9 | 10 | engine.map.lightsById[light.id] = light; 11 | } 12 | 13 | this.removeLight = function (lightId) { 14 | 15 | delete engine.map.lightsById[lightId]; 16 | 17 | // TODO - should really clean up the light render state and shadow map allocation. 18 | } 19 | 20 | this.invalidateLight = function (lightId) { 21 | 22 | var lightRenderState = engine.renderStateManager.coalesceLightRenderState(lightId); 23 | 24 | lightRenderState.isDirty = true; 25 | } 26 | 27 | this.addActor = function (actor) { 28 | 29 | engine.map.actorsById[actor.id] = actor; 30 | 31 | engine.mapDataHelper.checkActorData(actor); 32 | } 33 | 34 | this.removeActor = function (actorId) { 35 | 36 | delete engine.map.actorsById[actorId]; 37 | } 38 | 39 | this.addTrigger = function (trigger) { 40 | 41 | engine.map.triggersById[trigger.id] = trigger; 42 | 43 | engine.mapDataHelper.checkTriggerData(trigger); 44 | } 45 | 46 | this.removeTrigger = function (triggerId) { 47 | 48 | delete engine.map.triggersById[triggerId]; 49 | } 50 | } -------------------------------------------------------------------------------- /js/engine/effect-manager.js: -------------------------------------------------------------------------------- 1 | function EffectManager(engine) { 2 | 3 | var self = this; 4 | var gl = null; 5 | 6 | this.effectsById = {}; 7 | this.currentEffect = null; 8 | 9 | this.init = function (callback) { 10 | 11 | self.log('Loading effects...'); 12 | 13 | gl = engine.glManager.gl; 14 | 15 | engine.resourceLoader.loadJsonResource('system', 'effects', function (effectsById) { 16 | 17 | self.effectsById = effectsById; 18 | 19 | var effectIds = util.getObjectPropertyNames(effectsById); 20 | 21 | util.recurse(function (recursor, recursionCount) { 22 | if (recursionCount < effectIds.length) { 23 | var effect = effectsById[effectIds[recursionCount]]; 24 | self.initEffect(effect, recursor); 25 | } else { 26 | self.log('... done.'); 27 | callback(); 28 | } 29 | }); 30 | 31 | }); 32 | } 33 | 34 | this.useEffect = function (effectId) { 35 | 36 | this.currentEffect = this.effectsById[effectId]; 37 | 38 | gl.useProgram(this.currentEffect.shaderProgram); 39 | 40 | return this.currentEffect; 41 | } 42 | 43 | this.initEffect = function (effect, callback) { 44 | 45 | this.loadShader(effect.id + '-vs', 'vertex', function (shader) { 46 | 47 | effect.vertexShader = shader; 48 | 49 | self.loadShader(effect.id + '-fs', 'fragment', function (shader) { 50 | 51 | effect.fragmentShader = shader; 52 | 53 | effect.shaderProgram = gl.createProgram(); 54 | gl.attachShader(effect.shaderProgram, effect.vertexShader); 55 | gl.attachShader(effect.shaderProgram, effect.fragmentShader); 56 | gl.linkProgram(effect.shaderProgram); 57 | 58 | if (!gl.getProgramParameter(effect.shaderProgram, gl.LINK_STATUS)) { 59 | 60 | var lastError = gl.getProgramInfoLog(effect.shaderProgram); 61 | 62 | //var compilationLog = gl.getShaderInfoLog(effect.shaderProgram); 63 | //console.log('Shader compiler log: ' + compilationLog); 64 | 65 | throw 'Failed to initialise shader: ' + name + '. Last error: ' + lastError + '.'; 66 | } 67 | 68 | // Setup the effect's attributes. 69 | for (var attributeName in effect.attributes) { 70 | 71 | var location = gl.getAttribLocation(effect.shaderProgram, attributeName); 72 | if (location < 0) { 73 | console.log('Unable to find attribute: ' + attributeName); 74 | continue; 75 | } 76 | 77 | effect.attributes[attributeName] = location; 78 | } 79 | 80 | // Setup the effects uniforms. 81 | for (var uniformName in effect.uniforms) { 82 | 83 | var location = gl.getUniformLocation(effect.shaderProgram, uniformName); 84 | if (location == null) { 85 | console.log('Unable to find uniform: ' + uniformName); 86 | continue; 87 | } 88 | 89 | effect.uniforms[uniformName] = location; 90 | } 91 | 92 | self.log('Loaded effect: ' + effect.id); 93 | 94 | callback(); 95 | }); 96 | }); 97 | } 98 | 99 | this.loadShader = function (shaderId, type, callback) { 100 | 101 | self.log('Loading shader: ' + shaderId); 102 | 103 | engine.resourceLoader.loadTextResource('shader', shaderId, function (data) { 104 | 105 | var shader = null; 106 | 107 | if (type == 'vertex') { 108 | 109 | shader = gl.createShader(gl.VERTEX_SHADER); 110 | 111 | } else if (type == 'fragment') { 112 | 113 | shader = gl.createShader(gl.FRAGMENT_SHADER); 114 | 115 | } else { 116 | 117 | throw 'Unknown shader type: ' + type; 118 | } 119 | 120 | gl.shaderSource(shader, data); 121 | gl.compileShader(shader); 122 | 123 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 124 | 125 | throw 'Failed to compile shader: ' + gl.getShaderInfoLog(shader); 126 | } 127 | 128 | callback(shader); 129 | }); 130 | } 131 | 132 | this.log = function (message) { 133 | 134 | console.log('Effect Manager: ' + message); 135 | } 136 | } -------------------------------------------------------------------------------- /js/engine/fixed-length-array.js: -------------------------------------------------------------------------------- 1 | function FixedLengthArray(maxLength, initialItemValue) { 2 | 3 | this.items = []; 4 | this.length = 0; 5 | this.maxLength = maxLength; 6 | 7 | for (var i = 0; i < this.maxLength; i++) { 8 | this.items.push(initialItemValue); 9 | } 10 | } -------------------------------------------------------------------------------- /js/engine/frame-timer.js: -------------------------------------------------------------------------------- 1 | function FrameTimer(engine) { 2 | 3 | // Members. 4 | this.frameDelta = 0; 5 | this.lastFrameDurationMillis = 0; 6 | this.lastFrameStartTime = null; 7 | this.lastTenFramesTotalDuration = 0; 8 | this.fpsCounterTickUp = 0; 9 | 10 | this.init = function (callback) { 11 | 12 | callback(); 13 | } 14 | 15 | this.startFrame = function () { 16 | 17 | if (this.lastFrameStartTime == null) { 18 | 19 | this.lastFrameDurationMillis = 0; 20 | this.frameDelta = 0; 21 | 22 | } else { 23 | 24 | this.lastFrameDurationMillis = performance.now() - this.lastFrameStartTime; 25 | 26 | var desiredFrameDurationMillis = 1 / 60 * 1000; 27 | 28 | this.frameDelta = this.lastFrameDurationMillis / desiredFrameDurationMillis; 29 | } 30 | 31 | this.lastTenFramesTotalDuration += this.lastFrameDurationMillis; 32 | this.fpsCounterTickUp++; 33 | 34 | this.lastFrameStartTime = performance.now(); 35 | } 36 | 37 | this.updateStats = function () { 38 | 39 | if (this.fpsCounterTickUp < 10) { 40 | return; 41 | } 42 | 43 | var averageFrameDuration = this.lastTenFramesTotalDuration / 10; 44 | var averagefps = 1000 / averageFrameDuration; 45 | 46 | var averageRenderingTimeElement = document.getElementById('average-rendering-time'); 47 | if (averageRenderingTimeElement != null) { 48 | averageRenderingTimeElement.innerHTML = Math.round(averageFrameDuration) + 'ms'; 49 | } 50 | 51 | var averageFpsElement = document.getElementById('average-fps'); 52 | if (averageFpsElement != null) { 53 | averageFpsElement.innerHTML = Math.round(averagefps) + ' fps'; 54 | } 55 | 56 | var numberOfVisibleWorldStaticMeshChunksElement = document.getElementById('number-of-visible-world-static-mesh-chunks'); 57 | if (numberOfVisibleWorldStaticMeshChunksElement != null) { 58 | numberOfVisibleWorldStaticMeshChunksElement.innerHTML = engine.stats.numberOfVisibleWorldStaticMeshChunks; 59 | } 60 | 61 | var numberOfVisibleActorsElement = document.getElementById('number-of-visible-actors'); 62 | if (numberOfVisibleActorsElement != null) { 63 | numberOfVisibleActorsElement.innerHTML = engine.stats.numberOfVisibleActors; 64 | } 65 | 66 | var numberOfVisibleLightsElement = document.getElementById('number-of-visible-lights'); 67 | if (numberOfVisibleLightsElement != null) { 68 | numberOfVisibleLightsElement.innerHTML = engine.stats.numberOfVisibleLights; 69 | } 70 | 71 | var numberOfShadowMapsBuiltThisFrameElement = document.getElementById('number-of-shadow-maps-built-this-frame'); 72 | if (numberOfShadowMapsBuiltThisFrameElement != null) { 73 | numberOfShadowMapsBuiltThisFrameElement.innerHTML = engine.stats.numberOfShadowMapsBuiltThisFrame; 74 | } 75 | 76 | var cameraIsWithinMapElement = document.getElementById('camera-is-within-map'); 77 | if (cameraIsWithinMapElement != null) { 78 | cameraIsWithinMapElement.innerHTML = engine.stats.cameraIsWithinMap; 79 | } 80 | 81 | 82 | 83 | this.lastTenFramesTotalDuration = 0; 84 | this.fpsCounterTickUp = 0; 85 | } 86 | } -------------------------------------------------------------------------------- /js/engine/free-look-camera-controller.js: -------------------------------------------------------------------------------- 1 | function FreeLookCameraController(engine) { 2 | 3 | var self = this; 4 | 5 | this.rotation = vec3.create(); 6 | this.rotateRate = 0.001; 7 | this.moveRate = 0.3; 8 | 9 | this.init = function (callback) { 10 | 11 | vec3.set(engine.camera.position, 0, 1, 0); 12 | 13 | engine.mouse.addMouseMoveListener(function (event) { 14 | 15 | if (engine.mode != 'editor') { 16 | return; 17 | } 18 | 19 | self.handleMouseMove(event); 20 | }); 21 | 22 | callback(); 23 | } 24 | 25 | this.heartbeat = function () { 26 | 27 | var $ = this.$heartbeat; 28 | 29 | var camera = engine.camera; 30 | 31 | math3D.buildAxesFromRotations(camera.axes, this.rotation); 32 | 33 | math3D.buildMovementNormalFromAxes( 34 | $.movementNormal, camera.axes, engine.keyboard.movementAxisMultipliers); 35 | 36 | var movementAmount = this.moveRate * engine.frameTimer.frameDelta; 37 | 38 | vec3.scaleAndAdd(camera.position, engine.camera.position, $.movementNormal, movementAmount); 39 | } 40 | 41 | this.handleMouseMove = function (event) { 42 | 43 | this.rotation[0] += this.rotateRate * event.movementY * -1; 44 | this.rotation[1] += this.rotateRate * event.movementX * -1; 45 | } 46 | 47 | // Function locals. 48 | this.$heartbeat = { 49 | movementNormal: vec3.create() 50 | } 51 | } -------------------------------------------------------------------------------- /js/engine/gl-manager.js: -------------------------------------------------------------------------------- 1 | function GlManager(engine) { 2 | 3 | var self = this; 4 | 5 | this.gl = null; 6 | this.viewportInfo = null 7 | 8 | this.init = function (callback) { 9 | 10 | self.log('Initialising WebGL...'); 11 | 12 | var canvas = document.getElementById('canvas'); 13 | 14 | self.gl = canvas.getContext('experimental-webgl'); 15 | 16 | var requiredExtensionNames = [ 17 | 'OES_texture_float', 18 | 'OES_texture_float_linear', 19 | //'WEBGL_draw_buffers', 20 | 'WEBGL_depth_texture']; 21 | 22 | for (var i = 0; i < requiredExtensionNames.length; i++) { 23 | var extensionName = requiredExtensionNames[i]; 24 | if (self.gl.getExtension(extensionName) == null) { 25 | throw 'Extention not available: ' + extensionName; 26 | } 27 | } 28 | 29 | //canvas.width = canvas.clientWidth; 30 | //canvas.height = canvas.clientHeight; 31 | 32 | self.viewportInfo = { 33 | width: canvas.width, 34 | height: canvas.height 35 | } 36 | 37 | self.gl.viewport(0, 0, self.viewportInfo.width, self.viewportInfo.height); 38 | 39 | self.log('... done.'); 40 | 41 | callback(); 42 | } 43 | 44 | this.log = function (message) { 45 | 46 | console.log('GL Manger: ' + message); 47 | } 48 | } -------------------------------------------------------------------------------- /js/engine/gui-draw-spec-builder.js: -------------------------------------------------------------------------------- 1 | function GuiDrawSpecBuilder(engine) { 2 | 3 | this.buildGuiDrawSpecs = function (out, gui) { 4 | 5 | out.length = 0; 6 | 7 | var guiLayout = engine.guiLayoutManager.getGuiLayout(gui.layoutId); 8 | if (guiLayout == null) { 9 | return; 10 | } 11 | 12 | var spriteSheet = engine.spriteSheetManager.getSpriteSheet(guiLayout.spriteSheetId); 13 | if (spriteSheet == null) { 14 | return; 15 | } 16 | 17 | var texture = engine.textureManager.getTexture(spriteSheet.textureId); 18 | if (texture == null) { 19 | return; 20 | } 21 | 22 | for (var spriteId in guiLayout.spritesById) { 23 | 24 | var sprite = guiLayout.spritesById[spriteId]; 25 | 26 | var spriteSpec = spriteSheet.spriteSpecsById[sprite.spriteSpecId]; 27 | if (spriteSpec == null) { 28 | continue; 29 | } 30 | 31 | var drawSpec = out.items[out.length]; 32 | if (drawSpec == null) { 33 | drawSpec = { 34 | position: vec2.create(), 35 | size: vec2.create(), 36 | rotation: 0, 37 | uvPosition: vec2.create(), 38 | uvSize: vec2.create(), 39 | visible: false 40 | } 41 | 42 | out.items[out.length] = drawSpec; 43 | } 44 | 45 | vec2.copy(drawSpec.position, sprite.position); 46 | vec2.copy(drawSpec.size, sprite.size); 47 | drawSpec.rotation = sprite.rotation; 48 | 49 | drawSpec.uvPosition[0] = spriteSpec.position[0] / texture.width; 50 | drawSpec.uvPosition[1] = spriteSpec.position[1] / texture.height; 51 | 52 | drawSpec.uvSize[0] = spriteSpec.size[0] / texture.width; 53 | drawSpec.uvSize[1] = spriteSpec.size[1] / texture.height; 54 | 55 | drawSpec.visible = sprite.visible; 56 | 57 | for (var animationId in guiLayout.animationsById) { 58 | 59 | var animationState = gui.animationStatesById[animationId]; 60 | if (animationState == null || !animationState.active) { 61 | continue; 62 | } 63 | 64 | var animation = guiLayout.animationsById[animationId]; 65 | var animationExpansion = engine.guiLayoutAnimationManager.getGuiLayoutAnimationExpansion(guiLayout.id, animation.id); 66 | if (animationExpansion == null) { 67 | continue; 68 | } 69 | 70 | var frameIndex = Math.floor(animationState.frameIndex); 71 | var tweenIndexes = animationExpansion.tweenIndexesByFrameIndex[frameIndex]; 72 | 73 | for (var i = 0; i < tweenIndexes.length; i++) { 74 | 75 | var tweenIndex = tweenIndexes[i]; 76 | var tween = animation.tweens[tweenIndex]; 77 | 78 | if (tween.targetId != sprite.id) { 79 | continue; 80 | } 81 | 82 | var value = 0; 83 | 84 | if (tween.absoluteValue != null) { 85 | 86 | value = tween.absoluteValue; 87 | 88 | } else { 89 | 90 | var lerpFactor = math3D.calculateLerpFactor(tween.startFrameIndex, tween.startFrameIndex + tween.numberOfFrames, frameIndex); 91 | value = math3D.lerp(tween.fromValue, tween.toValue, lerpFactor); 92 | } 93 | 94 | if (tween.propertyId == SpritePropertyId.PositionXOffset) { 95 | drawSpec.position[0] += value; 96 | } else if (tween.propertyId == SpritePropertyId.PositionYOffset) { 97 | drawSpec.position[1] += value; 98 | } else if (tween.propertyId == SpritePropertyId.SizeXOffset) { 99 | drawSpec.size[0] += value; 100 | } else if (tween.propertyId == SpritePropertyId.SizeYOffset) { 101 | drawSpec.size[1] += value; 102 | } else if (tween.propertyId == SpritePropertyId.RotationOffset) { 103 | drawSpec.rotation += value; 104 | } else if (tween.propertyId == SpritePropertyId.Visible) { 105 | drawSpec.visible = value == 1; 106 | } 107 | } 108 | 109 | } 110 | 111 | vec2.divide(drawSpec.position, drawSpec.position, guiLayout.size); 112 | vec2.divide(drawSpec.size, drawSpec.size, guiLayout.size); 113 | 114 | out.length++; 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /js/engine/gui-layout-animation-manager.js: -------------------------------------------------------------------------------- 1 | function GuiLayoutAnimationManager(engine) { 2 | 3 | this.guiLayoutAnimationExpansionSetsByLayoutId = { 4 | } 5 | 6 | this.getGuiLayoutAnimationExpansion = function (layoutId, animationId) { 7 | 8 | var set = this.guiLayoutAnimationExpansionSetsByLayoutId[layoutId]; 9 | if (set == null) { 10 | return null; 11 | } 12 | 13 | var animationExpansion = set[animationId]; 14 | 15 | return animationExpansion; 16 | } 17 | 18 | this.buildGuiLayoutAnimationExpansion = function (layoutId, animationId) { 19 | 20 | var set = this.guiLayoutAnimationExpansionSetsByLayoutId[layoutId]; 21 | if (set == null) { 22 | set = {} 23 | this.guiLayoutAnimationExpansionSetsByLayoutId[layoutId] = set; 24 | } 25 | 26 | var animationExpansion = { 27 | tweenIndexesByFrameIndex: [] 28 | } 29 | 30 | set[animationId] = animationExpansion; 31 | 32 | var layout = engine.guiLayoutManager.getGuiLayout(layoutId); 33 | var animation = layout.animationsById[animationId]; 34 | 35 | for (var i = 0; i < animation.numberOfFrames; i++) { 36 | animationExpansion.tweenIndexesByFrameIndex[i] = []; 37 | } 38 | 39 | for (var tweenIndex = 0; tweenIndex < animation.tweens.length; tweenIndex++) { 40 | 41 | var tween = animation.tweens[tweenIndex]; 42 | 43 | for (var frameIndex = tween.startFrameIndex; frameIndex < tween.startFrameIndex + tween.numberOfFrames; frameIndex++) { 44 | animationExpansion.tweenIndexesByFrameIndex[frameIndex].push(tweenIndex); 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /js/engine/gui-layout-manager.js: -------------------------------------------------------------------------------- 1 | function GuiLayoutManager(engine) { 2 | 3 | var self = this; 4 | 5 | this.guiLayoutsById = {}; 6 | this.loadingGuiLayoutIds = {}; 7 | this.failedGuiLayoutIds = {}; 8 | 9 | this.loadGuiLayout = function (guiLayoutId, callback) { 10 | 11 | callback = callback || function () { } 12 | 13 | if (guiLayoutId == null) { 14 | callback(null); 15 | return; 16 | } 17 | 18 | var guiLayout = this.guiLayoutsById[guiLayoutId]; 19 | 20 | if (guiLayout != null) { 21 | callback(guiLayout); 22 | return; 23 | } 24 | 25 | if (this.loadingGuiLayoutIds[guiLayoutId] || this.failedGuiLayoutIds[guiLayoutId]) { 26 | callback(null); 27 | return; 28 | } 29 | 30 | this.log('Loading GUI layout: ' + guiLayoutId); 31 | 32 | this.loadingGuiLayoutIds[guiLayoutId] = guiLayoutId; 33 | 34 | engine.resourceLoader.loadJsonResource('gui-layout', guiLayoutId, function (guiLayout) { 35 | 36 | if (guiLayout == null) { 37 | 38 | self.failedGuiLayoutIds[guiLayoutId] = true; 39 | 40 | } else { 41 | 42 | self.guiLayoutsById[guiLayoutId] = guiLayout; 43 | } 44 | 45 | delete self.loadingGuiLayoutIds[guiLayoutId]; 46 | 47 | callback(guiLayout); 48 | }); 49 | } 50 | 51 | this.getGuiLayout = function (guiLayoutId) { 52 | 53 | if (guiLayoutId == null) { 54 | return null; 55 | } 56 | 57 | return this.guiLayoutsById[guiLayoutId]; 58 | } 59 | 60 | this.log = function (message) { 61 | 62 | console.log('GUI Layout Manager: ' + message); 63 | } 64 | } -------------------------------------------------------------------------------- /js/engine/keyboard.js: -------------------------------------------------------------------------------- 1 | function Keyboard(engine) { 2 | 3 | this.state = {}; 4 | 5 | this.A = 65; 6 | this.D = 68; 7 | this.W = 83; 8 | this.S = 87; 9 | 10 | this.movementAxisMultipliers = vec3.create(); 11 | 12 | this.init = function (callback) { 13 | 14 | var self = this; 15 | 16 | window.addEventListener('keydown', function (event) { 17 | 18 | self.state[event.which] = true; 19 | 20 | if (event.which == self.A) { 21 | self.movementAxisMultipliers[0] = -1; 22 | } else if (event.which == self.D) { 23 | self.movementAxisMultipliers[0] = 1; 24 | } else if (event.which == self.W) { 25 | self.movementAxisMultipliers[2] = -1; 26 | } else if (event.which == self.S) { 27 | self.movementAxisMultipliers[2] = 1; 28 | } 29 | }); 30 | 31 | window.addEventListener('keyup', function (event) { 32 | 33 | self.state[event.which] = false; 34 | 35 | if (event.which == self.A && self.movementAxisMultipliers[0] == -1) { 36 | self.movementAxisMultipliers[0] = 0; 37 | } else if (event.which == self.D && self.movementAxisMultipliers[0] == 1) { 38 | self.movementAxisMultipliers[0] = 0; 39 | } else if (event.which == self.W && self.movementAxisMultipliers[2] == -1) { 40 | self.movementAxisMultipliers[2] = 0; 41 | } else if (event.which == self.S && self.movementAxisMultipliers[2] == 1) { 42 | self.movementAxisMultipliers[2] = 0; 43 | } 44 | }); 45 | 46 | callback(); 47 | } 48 | } -------------------------------------------------------------------------------- /js/engine/line-drawer.js: -------------------------------------------------------------------------------- 1 | function LineDrawer(engine) { 2 | 3 | var gl = null; 4 | 5 | this.cubeVertexBuffer = null; 6 | this.sphereVertexBuffer = null; 7 | this.numSpherePoints = 0; 8 | 9 | this.init = function (callback) { 10 | 11 | gl = engine.glManager.gl; 12 | 13 | this.initCube(); 14 | this.initSphere(); 15 | 16 | callback(); 17 | } 18 | 19 | this.initCube = function () { 20 | 21 | this.cubeVertexBuffer = gl.createBuffer(); 22 | 23 | var points = [ 24 | 0, 0, 0, 1, 0, 0, // Front bottom 25 | 0, 1, 0, 1, 1, 0, // Front top 26 | 0, 0, 0, 0, 1, 0, // Front left 27 | 1, 0, 0, 1, 1, 0, // Front right 28 | 0, 0, -1, 1, 0, -1, // Back bottom 29 | 0, 1, -1, 1, 1, -1, // Back top 30 | 0, 0, -1, 0, 1, -1, // Back left 31 | 1, 0, -1, 1, 1, -1, // Back right 32 | 0, 0, 0, 0, 0, -1, // Left bottom 33 | 0, 1, 0, 0, 1, -1, // Left top 34 | 1, 0, 0, 1, 0, -1, // Right bottom 35 | 1, 1, 0, 1, 1, -1 // Right top 36 | ]; 37 | 38 | gl.bindBuffer(gl.ARRAY_BUFFER, this.cubeVertexBuffer); 39 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(points), gl.STATIC_DRAW); 40 | } 41 | 42 | this.initSphere = function () { 43 | 44 | this.sphereVertexBuffer = gl.createBuffer(); 45 | 46 | var points = []; 47 | 48 | var numSegments = 20; 49 | var numSegmentPoints = 40; 50 | var segmentRadiansStep = (Math.PI * 2) / numSegments; 51 | var segmentPointsRadiansStep = (Math.PI * 2) / numSegmentPoints; 52 | var segmentRadians = 0; 53 | 54 | for (var i = 0; i < numSegments; i++) { 55 | 56 | var segmentDirection = { 57 | x: Math.cos(segmentRadians), 58 | z: Math.sin(segmentRadians) 59 | } 60 | 61 | segmentRadians += segmentRadiansStep; 62 | 63 | var segmentPointsRadians = 0; 64 | 65 | for (var j = 0; j <= numSegmentPoints; j++) { 66 | 67 | var d = Math.sin(segmentPointsRadians); 68 | 69 | points.push(segmentDirection.x * d); 70 | points.push(Math.cos(segmentPointsRadians)); 71 | points.push(segmentDirection.z * d); 72 | 73 | segmentPointsRadians += segmentPointsRadiansStep; 74 | } 75 | } 76 | 77 | this.numSpherePoints = points.length / 3; 78 | 79 | gl.bindBuffer(gl.ARRAY_BUFFER, this.sphereVertexBuffer); 80 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(points), gl.STATIC_DRAW); 81 | } 82 | 83 | this.drawCube = function (renderingParameters, position, size, colour, enableDepthTest) { 84 | 85 | if (colour == null) { 86 | colour = RgbColours.White; 87 | } 88 | 89 | if (enableDepthTest == null) { 90 | enableDepthTest = true; 91 | } 92 | 93 | var effect = engine.effectManager.useEffect('line'); 94 | 95 | gl.uniformMatrix4fv(effect.uniforms.viewProjMatrix, false, renderingParameters.viewProjMatrix); 96 | gl.uniform3fv(effect.uniforms.position, position); 97 | gl.uniform3fv(effect.uniforms.size, size); 98 | gl.uniform3fv(effect.uniforms.colour, colour); 99 | 100 | gl.disable(gl.BLEND); 101 | 102 | gl.bindBuffer(gl.ARRAY_BUFFER, this.cubeVertexBuffer); 103 | gl.vertexAttribPointer( 104 | effect.attributes.vertexPosition, 105 | 3, gl.FLOAT, false, 0, 0); 106 | gl.enableVertexAttribArray(effect.attributes.vertexPosition); 107 | 108 | if (enableDepthTest) { 109 | gl.enable(gl.DEPTH_TEST); 110 | } else { 111 | gl.disable(gl.DEPTH_TEST); 112 | } 113 | 114 | //gl.disable(gl.DEPTH_TEST); 115 | gl.drawArrays(gl.LINES, 0, 24); 116 | //gl.enable(gl.DEPTH_TEST); 117 | } 118 | 119 | this.drawSphere = function (renderingParameters, position, radius, colour, enableDepthTest) { 120 | 121 | if (colour == null) { 122 | colour = RgbColours.White; 123 | } 124 | 125 | var effect = engine.effectManager.useEffect('line'); 126 | 127 | gl.uniformMatrix4fv(effect.uniforms.viewProjMatrix, false, renderingParameters.viewProjMatrix); 128 | gl.uniform3fv(effect.uniforms.position, position); 129 | gl.uniform3f(effect.uniforms.size, radius, radius, radius); 130 | gl.uniform3fv(effect.uniforms.colour, colour); 131 | 132 | gl.disable(gl.BLEND); 133 | 134 | gl.bindBuffer(gl.ARRAY_BUFFER, this.sphereVertexBuffer); 135 | gl.vertexAttribPointer( 136 | effect.attributes.vertexPosition, 137 | 3, gl.FLOAT, false, 0, 0); 138 | gl.enableVertexAttribArray(effect.attributes.vertexPosition); 139 | 140 | if (enableDepthTest) { 141 | gl.enable(gl.DEPTH_TEST); 142 | } else { 143 | gl.disable(gl.DEPTH_TEST); 144 | } 145 | 146 | //gl.disable(gl.DEPTH_TEST); 147 | gl.drawArrays(gl.LINE_STRIP, 0, this.numSpherePoints); 148 | //gl.enable(gl.DEPTH_TEST); 149 | } 150 | } -------------------------------------------------------------------------------- /js/engine/map-data-helper.js: -------------------------------------------------------------------------------- 1 | function MapDataHelper(engine) { 2 | 3 | this.checkMapData = function () { 4 | 5 | this.checkGameData(); 6 | this.checkActorsData(); 7 | this.checkEmittersData(); 8 | this.checkTriggersData(); 9 | } 10 | 11 | this.checkGameData = function () { 12 | 13 | if (engine.map.gameData == null) { 14 | engine.map.gameData = {} 15 | } 16 | 17 | var controller = engine.gameControllersById[engine.map.gameControllerId]; 18 | 19 | if (controller != null && controller.dataSchema != null) { 20 | 21 | this.checkData(engine.map.gameData, controller.dataSchema); 22 | } 23 | } 24 | 25 | this.checkActorsData = function () { 26 | 27 | for (var actorId in engine.map.actorsById) { 28 | 29 | var actor = engine.map.actorsById[actorId]; 30 | 31 | this.checkActorData(actor); 32 | } 33 | } 34 | 35 | this.checkActorData = function (actor) { 36 | 37 | if (actor.data == null) { 38 | actor.data = {} 39 | } 40 | 41 | var controller = engine.actorControllersById[actor.controllerId]; 42 | 43 | if (controller != null && controller.dataSchema != null) { 44 | 45 | this.checkData(actor.data, controller.dataSchema); 46 | } 47 | } 48 | 49 | this.checkEmittersData = function () { 50 | 51 | for (var emitterId in engine.map.emittersById) { 52 | 53 | var emitter = engine.map.emittersById[emitterId]; 54 | 55 | this.checkEmitterData(emitter); 56 | 57 | for (var i = 0; i < emitter.particles.length; i++) { 58 | 59 | var particle = emitter.particles[i]; 60 | 61 | this.checkParticleData(emitter, particle); 62 | } 63 | } 64 | } 65 | 66 | this.checkEmitterData = function (emitter) { 67 | 68 | if (emitter.data == null) { 69 | emitter.data = {} 70 | } 71 | 72 | var controller = engine.actorControllersById[emitter.emitterControllerId]; 73 | 74 | if (controller != null && controller.dataSchema != null) { 75 | 76 | this.checkData(emitter.data, controller.dataSchema); 77 | } 78 | } 79 | 80 | this.checkParticleData = function (emitter, particle) { 81 | 82 | if (particle.data == null) { 83 | particle.data = {} 84 | } 85 | 86 | var controller = engine.actorControllersById[emitter.particleControllerId]; 87 | 88 | if (controller != null && controller.dataSchema != null) { 89 | 90 | this.checkData(particle.data, controller.dataSchema); 91 | } 92 | } 93 | 94 | this.checkTriggersData = function () { 95 | 96 | for (var triggerId in engine.map.triggersById) { 97 | 98 | var trigger = engine.map.triggersById[triggerId]; 99 | 100 | this.checkTriggerData(trigger); 101 | } 102 | } 103 | 104 | this.checkTriggerData = function (trigger) { 105 | 106 | if (trigger.data == null) { 107 | trigger.data = {} 108 | } 109 | 110 | var controller = engine.triggerControllersById[trigger.controllerId]; 111 | 112 | if (controller != null && controller.dataSchema != null) { 113 | 114 | this.checkData(trigger.data, controller.dataSchema); 115 | } 116 | } 117 | 118 | this.checkData = function (data, dataSchema) { 119 | 120 | for (var propertyName in dataSchema) { 121 | 122 | var property = dataSchema[propertyName]; 123 | 124 | if (data[propertyName] == null) { 125 | data[propertyName] = property.defaultValue; 126 | } 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /js/engine/material-manager.js: -------------------------------------------------------------------------------- 1 | function MaterialManager(engine) { 2 | 3 | var self = this; 4 | 5 | this.materialsById = {}; 6 | this.loadingMaterialIds = {}; 7 | this.failedMaterialIds = {}; 8 | 9 | this.loadMaterial = function (materialId, callback) { 10 | 11 | callback = callback || function () { } 12 | 13 | if (materialId == null) { 14 | callback(null); 15 | return; 16 | } 17 | 18 | var material = this.materialsById[materialId]; 19 | 20 | if (material != null) { 21 | callback(material); 22 | return; 23 | } 24 | 25 | if (this.loadingMaterialIds[materialId] || this.failedMaterialIds[materialId]) { 26 | callback(null); 27 | return; 28 | } 29 | 30 | this.log('Loading material: ' + materialId); 31 | 32 | this.loadingMaterialIds[materialId] = materialId; 33 | 34 | engine.resourceLoader.loadJsonResource('material', materialId, function (material) { 35 | 36 | if (material == null) { 37 | 38 | self.failedMaterialIds[materialId] = true; 39 | 40 | } else { 41 | 42 | self.materialsById[materialId] = material; 43 | } 44 | 45 | delete self.loadingMaterialIds[materialId]; 46 | 47 | callback(material); 48 | }); 49 | } 50 | 51 | this.getMaterial = function (materialId) { 52 | 53 | if (materialId == null) { 54 | return null; 55 | } 56 | 57 | return this.materialsById[materialId]; 58 | } 59 | 60 | this.log = function (message) { 61 | 62 | console.log('Material Manager: ' + message); 63 | } 64 | } -------------------------------------------------------------------------------- /js/engine/math/math-aabb.js: -------------------------------------------------------------------------------- 1 | function MathAABB() { 2 | 3 | this.buildAABBFromPoints = function (out, points) { 4 | 5 | var from = out.from; 6 | var to = out.to; 7 | 8 | var fromIsSet = false; 9 | var toIsSet = false; 10 | 11 | for (var i = 0; i < points.length; i++) { 12 | var point = points[i]; 13 | 14 | if (!fromIsSet) { 15 | vec3.copy(from, point); 16 | fromIsSet = true; 17 | } else if (point[0] < from[0]) { 18 | from[0] = point[0]; 19 | } else if (point[1] < from[1]) { 20 | from[1] = point[1]; 21 | } else if (point[2] > from[2]) { 22 | from[2] = point[2]; 23 | } 24 | 25 | if (!toIsSet) { 26 | vec3.copy(to, point); 27 | toIsSet = true; 28 | } else if (point[0] > to[0]) { 29 | to[0] = point[0]; 30 | } else if (point[1] > to[1]) { 31 | to[1] = point[1]; 32 | } else if (point[2] < to[2]) { 33 | to[2] = point[2]; 34 | } 35 | } 36 | } 37 | 38 | this.buildAABBPoints = function (out, aabb) { 39 | 40 | /*var points = [ 41 | [aabb.from[0], aabb.from[1], aabb.from[2]], 42 | [aabb.to[0], aabb.from[1], aabb.from[2]], 43 | [aabb.from[0], aabb.to[1], aabb.from[2]], 44 | [aabb.to[0], aabb.to[1], aabb.from[2]], 45 | [aabb.from[0], aabb.from[1], aabb.to[2]], 46 | [aabb.to[0], aabb.from[1], aabb.to[2]], 47 | [aabb.from[0], aabb.to[1], aabb.to[2]], 48 | [aabb.to[0], aabb.to[1], aabb.to[2]] 49 | ]; 50 | 51 | return points; 52 | */ 53 | 54 | out[0][0] = aabb.from[0]; out[0][1] = aabb.from[1]; out[0][2] = aabb.from[2]; 55 | out[1][0] = aabb.to[0]; out[1][1] = aabb.from[1]; out[1][2] = aabb.from[2]; 56 | out[2][0] = aabb.from[0]; out[2][1] = aabb.to[1]; out[2][2] = aabb.from[2]; 57 | out[3][0] = aabb.to[0]; out[3][1] = aabb.to[1]; out[3][2] = aabb.from[2]; 58 | out[4][0] = aabb.from[0]; out[4][1] = aabb.from[1]; out[4][2] = aabb.to[2]; 59 | out[5][0] = aabb.to[0]; out[5][1] = aabb.from[1]; out[5][2] = aabb.to[2]; 60 | out[6][0] = aabb.from[0]; out[6][1] = aabb.to[1]; out[6][2] = aabb.to[2]; 61 | out[7][0] = aabb.to[0]; out[7][1] = aabb.to[1]; out[7][2] = aabb.to[2]; 62 | } 63 | 64 | 65 | this.calculateAABBSize = function (out, aabb) { 66 | 67 | //return [aabb.to[0] - aabb.from[0], aabb.to[1] - aabb.from[1], aabb.from[2] - aabb.to[2]]; 68 | 69 | out[0] = aabb.to[0] - aabb.from[0]; 70 | out[1] = aabb.to[1] - aabb.from[1]; 71 | out[2] = aabb.from[2] - aabb.to[2]; 72 | } 73 | 74 | /*this.cloneAABB = function (aabb) { 75 | 76 | return new AABB(vec3.clone(aabb.from), vec3.clone(aabb.to)); 77 | }*/ 78 | 79 | this.translateAABB = function (aabb, amount) { 80 | 81 | vec3.add(aabb.from, aabb.from, amount); 82 | vec3.add(aabb.to, aabb.to, amount); 83 | } 84 | 85 | this.clampPointToAABB = function (out, point, aabb) { 86 | 87 | vec3.copy(out, point); 88 | 89 | if (point[0] < aabb.from[0]) { 90 | out[0] = aabb.from[0]; 91 | } 92 | 93 | if (point[1] < aabb.from[1]) { 94 | out[1] = aabb.from[1]; 95 | } 96 | 97 | if (point[2] > aabb.from[2]) { 98 | out[2] = aabb.from[2]; 99 | } 100 | 101 | if (point[0] > aabb.to[0]) { 102 | out[0] = aabb.to[0]; 103 | } 104 | 105 | if (point[1] > aabb.to[1]) { 106 | out[1] = aabb.to[1]; 107 | } 108 | 109 | if (point[2] < aabb.to[2]) { 110 | out[2] = aabb.to[2]; 111 | } 112 | } 113 | 114 | this.checkAAABIntersectsAABB = function (aabb1, aabb2) { 115 | 116 | if (aabb1.from[0] > aabb2.to[0] || 117 | aabb1.from[1] > aabb2.to[1] || 118 | aabb1.from[2] < aabb2.to[2] || 119 | aabb1.to[0] < aabb2.from[0] || 120 | aabb1.to[1] < aabb2.from[1] || 121 | aabb1.to[2] > aabb2.from[2]) { 122 | 123 | return false; 124 | } 125 | 126 | return true; 127 | } 128 | 129 | this.checkPointIsWithinAABB = function (aabb, point) { 130 | 131 | if (aabb.from[0] > point[0] || 132 | aabb.from[1] > point[1] || 133 | aabb.from[2] < point[2] || 134 | aabb.to[0] < point[0] || 135 | aabb.to[1] < point[1] || 136 | aabb.to[2] > point[2]) { 137 | 138 | return false; 139 | } 140 | 141 | return true; 142 | } 143 | } -------------------------------------------------------------------------------- /js/engine/math/math-collision-face.js: -------------------------------------------------------------------------------- 1 | function MathCollisionFace() { 2 | 3 | this.buildCollisionFaceFromPoints = function (points) { 4 | 5 | var $ = this.$buildCollisionFaceFromPoints; 6 | 7 | // Calculate face normal. 8 | vec3.sub($.freeEdgeAB, points[1], points[0]); 9 | vec3.sub($.freeEdgeAC, points[2], points[0]); 10 | 11 | vec3.cross($.faceNormal, $.freeEdgeAB, $.freeEdgeAC); 12 | vec3.normalize($.faceNormal, $.faceNormal); 13 | 14 | var facePlane = new Plane(); // FIXME 15 | math3D.buildPlaneFromNormalAndPoint(facePlane, $.faceNormal, points[0]); 16 | 17 | // Calculate the edge planes and edge lengths. 18 | var freeNormalisedEdges = [vec3.create(), vec3.create(), vec3.create()]; 19 | var edgeLengths = []; 20 | 21 | vec3.sub(freeNormalisedEdges[0], points[1], points[0]); 22 | edgeLengths[0] = vec3.length(freeNormalisedEdges[0]); 23 | vec3.normalize(freeNormalisedEdges[0], freeNormalisedEdges[0]); 24 | 25 | vec3.sub(freeNormalisedEdges[1], points[2], points[1]); 26 | edgeLengths[1] = vec3.length(freeNormalisedEdges[1]); 27 | vec3.normalize(freeNormalisedEdges[1], freeNormalisedEdges[1]); 28 | 29 | vec3.sub(freeNormalisedEdges[2], points[0], points[2]); 30 | edgeLengths[2] = vec3.length(freeNormalisedEdges[2]); 31 | vec3.normalize(freeNormalisedEdges[2], freeNormalisedEdges[2]); 32 | 33 | var edgePlaneNormals = [vec3.create(), vec3.create(), vec3.create()]; // FIXME 34 | 35 | vec3.cross(edgePlaneNormals[0], freeNormalisedEdges[0], $.faceNormal); 36 | vec3.cross(edgePlaneNormals[1], freeNormalisedEdges[1], $.faceNormal); 37 | vec3.cross(edgePlaneNormals[2], freeNormalisedEdges[2], $.faceNormal); 38 | 39 | var edgePlanes = [new Plane(), new Plane(), new Plane()]; 40 | 41 | math3D.buildPlaneFromNormalAndPoint(edgePlanes[0], edgePlaneNormals[0], points[0]); 42 | math3D.buildPlaneFromNormalAndPoint(edgePlanes[1], edgePlaneNormals[1], points[1]); 43 | math3D.buildPlaneFromNormalAndPoint(edgePlanes[2], edgePlaneNormals[2], points[2]); 44 | 45 | return new CollisionFace(points, facePlane, edgePlanes, freeNormalisedEdges, edgeLengths); 46 | } 47 | 48 | this.findNearestPointOnCollisionFacePerimeterToPoint = function (out, collisionFace, point) { 49 | 50 | var $ = this.$findNearestPointOnCollisionFacePerimeterToPoint; 51 | 52 | var nearestPointDistanceSqr = -1 53 | 54 | for (var i = 0; i < 3; i++) { 55 | 56 | vec3.copy($.ray.origin, collisionFace.points[i]); 57 | vec3.copy($.ray.normal, collisionFace.freeNormalisedEdges[i]); 58 | 59 | math3D.calculateNearestPointOnRayToOtherPoint($.potentialNearestPoint, $.ray, point, collisionFace.edgeLengths[i]); 60 | 61 | var potentialNearestPointDistanceSqr = vec3.sqrDist(point, $.potentialNearestPoint); 62 | 63 | if (nearestPointDistanceSqr == -1 || potentialNearestPointDistanceSqr < nearestPointDistanceSqr) { 64 | nearestPointDistanceSqr = potentialNearestPointDistanceSqr; 65 | vec3.copy(out, $.potentialNearestPoint); 66 | } 67 | } 68 | } 69 | 70 | this.determineIfPointOnFacePlaneIsWithinCollisionFace = function (collisionFace, point) { 71 | 72 | for (var i = 0; i < collisionFace.edgePlanes.length; i++) { 73 | 74 | var edgePlane = collisionFace.edgePlanes[i]; 75 | 76 | var distanceToEdgePlane = math3D.calculatePointDistanceFromPlane(edgePlane, point); 77 | 78 | if (distanceToEdgePlane > 0) { 79 | return false; 80 | } 81 | } 82 | 83 | return true; 84 | } 85 | 86 | // Function locals. 87 | this.$buildCollisionFaceFromPoints = { 88 | freeEdgeAB: vec3.create(), 89 | freeEdgeAC: vec3.create(), 90 | faceNormal: vec3.create() 91 | } 92 | 93 | this.$findNearestPointOnCollisionFacePerimeterToPoint = { 94 | ray: new Ray(), 95 | potentialNearestPoint: vec3.create() 96 | } 97 | } -------------------------------------------------------------------------------- /js/engine/math/math-collision-line.js: -------------------------------------------------------------------------------- 1 | function MathCollisionLine() { 2 | 3 | this.buildCollisionLineFromFromAndToPoints = function (out) { 4 | 5 | vec3.copy(out.ray.origin, out.from); 6 | 7 | vec3.sub(out.ray.normal, out.to, out.from); 8 | out.length = vec3.length(out.ray.normal); 9 | 10 | vec3.normalize(out.ray.normal, out.ray.normal); 11 | } 12 | 13 | this.calculateCollisionLineIntersectionWithCollisionFace = function (out, line, face) { 14 | 15 | var $ = this.$calculateCollisionLineIntersectionWithCollisionFace; 16 | 17 | var isFrontSideCollision = math3D.calculatePointDistanceFromPlane(face.facePlane, line.from) > 0; 18 | 19 | var lineIntersects = math3D.calculateRayIntersectionWithPlane($.facePlaneIntersection, line.ray, face.facePlane); 20 | 21 | if (!lineIntersects) { 22 | return FaceIntersectionType.None; 23 | } 24 | 25 | if (!math3D.determineIfPointOnFacePlaneIsWithinCollisionFace(face, $.facePlaneIntersection)) { 26 | return FaceIntersectionType.None; 27 | } 28 | 29 | var distanceToFacePlaneIntersectionSqr = vec3.squaredDistance(line.from, $.facePlaneIntersection); 30 | 31 | var lineLengthSqr = line.length * line.length; 32 | 33 | if (distanceToFacePlaneIntersectionSqr > lineLengthSqr) { 34 | return FaceIntersectionType.None; 35 | } 36 | 37 | if (out != null) { 38 | vec3.copy(out, $.facePlaneIntersection); 39 | } 40 | 41 | return isFrontSideCollision ? FaceIntersectionType.FrontSide : FaceIntersectionType.BackSide; 42 | } 43 | 44 | this.determineIfCollisionLineIntersectsSphere = function (line, sphere) { 45 | 46 | var $ = this.$determineIfCollisionLineIntersectsSphere; 47 | 48 | var sphereSadiusSqr = sphere.radius * sphere.radius; 49 | var fromPointDistanceSqr = vec3.sqrDist(line.from, sphere.position); 50 | if (fromPointDistanceSqr <= sphereSadiusSqr) { 51 | return true; 52 | } 53 | 54 | var toPointDistanceSqr = vec3.sqrDist(line.from, sphere.position); 55 | if (toPointDistanceSqr <= sphereSadiusSqr) { 56 | return true; 57 | } 58 | 59 | var rayIntersectsSphere = math3D.calculateRayIntersectionWithSphere($.intersectionPoint, line.ray, sphere); 60 | 61 | if (!rayIntersectsSphere) { 62 | return false; 63 | } 64 | 65 | var distanceToIntersectionPointSqr = vec3.squaredDistance(line.from, $.intersectionPoint); 66 | 67 | var lineLengthSqr = line.length * line.length; 68 | 69 | if (distanceToIntersectionPointSqr > lineLengthSqr) { 70 | return false; 71 | } 72 | 73 | return true; 74 | } 75 | 76 | this.checkIfCollisionLineIntersectsAABB = function (line, aabb) { 77 | 78 | var $ = this.$checkIfCollisionLineIntersectsAABB; 79 | 80 | if (this.checkPointIsWithinAABB(aabb, line.from)) { 81 | return true; 82 | } 83 | 84 | if (this.checkPointIsWithinAABB(aabb, line.to)) { 85 | return true; 86 | } 87 | 88 | // Test if the ray intersection with any of the AABB's planes is within the AABB. 89 | this.buildPlaneFromNormalAndPoint($.planes[0], this.axes3D.positiveZ, aabb.from); // Front 90 | this.buildPlaneFromNormalAndPoint($.planes[1], this.axes3D.negativeZ, aabb.to); // Back 91 | this.buildPlaneFromNormalAndPoint($.planes[2], this.axes3D.negativeX, aabb.from); // Left 92 | this.buildPlaneFromNormalAndPoint($.planes[3], this.axes3D.positiveX, aabb.to); // Right 93 | this.buildPlaneFromNormalAndPoint($.planes[4], this.axes3D.positiveY, aabb.from); // Top 94 | this.buildPlaneFromNormalAndPoint($.planes[5], this.axes3D.negativeY, aabb.to); // Bottom 95 | 96 | for (var i = 0; i < $.planes.length; i++) { 97 | var lineIntersects = this.calculateRayIntersectionWithPlane($.planeIntersection, line.ray, $.planes[i]); 98 | if (lineIntersects && this.checkPointIsWithinAABB(aabb, $.planeIntersection)) { 99 | return true; 100 | } 101 | } 102 | 103 | return false; 104 | } 105 | 106 | // Function locals. 107 | this.$calculateCollisionLineIntersectionWithCollisionFace = { 108 | facePlaneIntersection: vec3.create() 109 | } 110 | 111 | this.$determineIfCollisionLineIntersectsSphere = { 112 | intersectionPoint: vec3.create() 113 | } 114 | 115 | this.$checkIfCollisionLineIntersectsAABB = { 116 | planes: [new Plane(), new Plane(), new Plane(), new Plane(), new Plane(), new Plane()], 117 | planeIntersection: vec3.create() 118 | } 119 | } -------------------------------------------------------------------------------- /js/engine/math/math-frustum.js: -------------------------------------------------------------------------------- 1 | function MathFrustum() { 2 | 3 | this.buildFrustumFromViewProjMatrix = function (out, viewProjMatrix) { 4 | 5 | var $ = this.$buildFrustumFromViewProjMatrix; 6 | 7 | mat4.invert($.invViewProjMatrix, viewProjMatrix); 8 | 9 | if (!$.trianglesAreInitialised) { 10 | 11 | vec4.set($.triangles[FrustumPlane.Near][0], -1, 1, -1, 1); 12 | vec4.set($.triangles[FrustumPlane.Near][1], -1, -1, -1, 1); 13 | vec4.set($.triangles[FrustumPlane.Near][2], 1, 1, -1, 1); 14 | 15 | vec4.set($.triangles[FrustumPlane.Far][0], -1, 1, 1, 1); 16 | vec4.set($.triangles[FrustumPlane.Far][1], 1, 1, 1, 1); 17 | vec4.set($.triangles[FrustumPlane.Far][2], -1, -1, 1, 1); 18 | 19 | vec4.set($.triangles[FrustumPlane.Left][0], -1, 1, 1, 1); 20 | vec4.set($.triangles[FrustumPlane.Left][1], -1, -1, 1, 1); 21 | vec4.set($.triangles[FrustumPlane.Left][2], -1, 1, -1, 1); 22 | 23 | vec4.set($.triangles[FrustumPlane.Right][0], 1, 1, -1, 1); 24 | vec4.set($.triangles[FrustumPlane.Right][1], 1, -1, -1, 1); 25 | vec4.set($.triangles[FrustumPlane.Right][2], 1, 1, 1, 1); 26 | 27 | vec4.set($.triangles[FrustumPlane.Top][0], -1, 1, 1, 1); 28 | vec4.set($.triangles[FrustumPlane.Top][1], -1, 1, -1, 1); 29 | vec4.set($.triangles[FrustumPlane.Top][2], 1, 1, 1, 1); 30 | 31 | vec4.set($.triangles[FrustumPlane.Bottom][0], -1, -1, -1, 1); 32 | vec4.set($.triangles[FrustumPlane.Bottom][1], -1, -1, 1, 1); 33 | vec4.set($.triangles[FrustumPlane.Bottom][2], 1, -1, 1, 1); 34 | 35 | $.trianglesAreInitialised = true; 36 | } 37 | 38 | for (var triangleIndex = 0; triangleIndex < $.triangles.length; triangleIndex++) { 39 | 40 | var triangle = $.triangles[triangleIndex]; 41 | 42 | for (var i = 0; i < triangle.length; i++) { 43 | var point = triangle[i]; 44 | vec4.transformMat4($.tempPoint, point, $.invViewProjMatrix); 45 | vec4.set($.transformedPoints[i], $.tempPoint[0] / $.tempPoint[3], $.tempPoint[1] / $.tempPoint[3], $.tempPoint[2] / $.tempPoint[3]); 46 | } 47 | 48 | this.buildPlaneFromPoints(out.planes[triangleIndex], $.transformedPoints); 49 | } 50 | } 51 | 52 | this.checkFrustumIntersectsAABB = function (frustum, aabb) { 53 | 54 | var $ = this.$checkFrustumIntersectsAABB; 55 | 56 | var intersects = true; 57 | 58 | this.buildAABBPoints($.aabbPoints, aabb); 59 | 60 | for (var planeIndex = 0; planeIndex < frustum.planes.length; planeIndex++) { 61 | 62 | var plane = frustum.planes[planeIndex]; 63 | 64 | var allPointsAreInfrontOfPlane = true; 65 | 66 | for (var pointIndex = 0; pointIndex < $.aabbPoints.length; pointIndex++) { 67 | 68 | var point = $.aabbPoints[pointIndex]; 69 | 70 | var pointDistanceFromPlane = this.calculatePointDistanceFromPlane(plane, point); 71 | 72 | if (pointDistanceFromPlane <= 0) { 73 | allPointsAreInfrontOfPlane = false; 74 | break; 75 | } 76 | } 77 | 78 | if (allPointsAreInfrontOfPlane) { 79 | intersects = false; 80 | break; 81 | } 82 | } 83 | 84 | return intersects; 85 | } 86 | 87 | this.checkFrustumIntersectsSphere = function (frustum, sphere) { 88 | 89 | var intersects = true; 90 | 91 | for (var planeIndex = 0; planeIndex < frustum.planes.length; planeIndex++) { 92 | 93 | var plane = frustum.planes[planeIndex]; 94 | 95 | var spherePositionDistanceFromPlane = this.calculatePointDistanceFromPlane(plane, sphere.position); 96 | 97 | if (spherePositionDistanceFromPlane > sphere.radius) { 98 | intersects = false; 99 | break; 100 | } 101 | } 102 | 103 | return intersects; 104 | } 105 | 106 | // Function locals. 107 | this.$checkFrustumIntersectsAABB = { 108 | aabbPoints: [ 109 | vec3.create(), vec3.create(), vec3.create(), vec3.create(), 110 | vec3.create(), vec3.create(), vec3.create(), vec3.create()] 111 | } 112 | 113 | this.$buildFrustumFromViewProjMatrix = { 114 | invViewProjMatrix: mat4.create(), 115 | triangles: [ 116 | [vec4.create(), vec4.create(), vec4.create()], 117 | [vec4.create(), vec4.create(), vec4.create()], 118 | [vec4.create(), vec4.create(), vec4.create()], 119 | [vec4.create(), vec4.create(), vec4.create()], 120 | [vec4.create(), vec4.create(), vec4.create()], 121 | [vec4.create(), vec4.create(), vec4.create()] 122 | ], 123 | trianglesAreInitialised: false, 124 | transformedPoints: [vec4.create(), vec4.create(), vec4.create()], 125 | tempPoint: vec4.create() 126 | } 127 | } -------------------------------------------------------------------------------- /js/engine/math/math-plane.js: -------------------------------------------------------------------------------- 1 | function MathPlane() { 2 | 3 | this.buildPlaneFromPoints = function (out, points) { 4 | 5 | var vecA = vec3.create(); 6 | var vecB = vec3.create(); 7 | vec3.subtract(vecA, points[1], points[0]); 8 | vec3.subtract(vecB, points[2], points[0]); 9 | 10 | //var normal = vec3.create(); 11 | vec3.cross(out.normal, vecA, vecB); 12 | vec3.normalize(out.normal, out.normal); 13 | 14 | out.d = -vec3.dot(out.normal, points[0]); 15 | 16 | //var plane = new Plane(normal, d); 17 | 18 | //return plane; 19 | } 20 | 21 | this.buildPlaneFromNormalAndPoint = function (out, normal, point) { 22 | 23 | vec3.copy(out.normal, normal); 24 | out.d = -vec3.dot(normal, point); 25 | } 26 | 27 | this.calculatePointDistanceFromPlane = function (plane, point) { 28 | 29 | var distance = vec3.dot(plane.normal, point) + plane.d; 30 | 31 | return distance; 32 | } 33 | 34 | this.calculatePlaneIntersectionSlideReaction = function (out, plane, intersection, desiredDirection, desiredDistance) { 35 | 36 | var $ = this.$calculatePlaneIntersectionSlideReaction; 37 | 38 | vec3.scaleAndAdd($.targetPoint, intersection, desiredDirection, desiredDistance); 39 | 40 | vec3.copy($.targetPointToProjectionPointRay.origin, $.targetPoint); 41 | vec3.copy($.targetPointToProjectionPointRay.normal, plane.normal); 42 | 43 | var targetPointToProjectionPointRayIntersectsPlane = math3D.calculateRayIntersectionWithPlane( 44 | $.projectionPoint, $.targetPointToProjectionPointRay, plane); 45 | 46 | if (!targetPointToProjectionPointRayIntersectsPlane) { 47 | return false; 48 | } 49 | 50 | vec3.subtract($.slideVector, $.projectionPoint, intersection); 51 | 52 | var slideDistance = vec3.length($.slideVector); 53 | 54 | vec3.normalize($.slideVector, $.slideVector); 55 | 56 | // Copy out the results. 57 | vec3.copy(out.direction, $.slideVector); 58 | out.distance = slideDistance; 59 | 60 | return true; 61 | } 62 | 63 | this.copyPlane = function (out, plane) { 64 | 65 | vec3.copy(out.normal, plane.normal); 66 | out.d = plane.d; 67 | } 68 | 69 | // Function locals. 70 | this.$calculatePlaneIntersectionSlideReaction = { 71 | targetPoint: vec3.create(), 72 | targetPointToProjectionPointRay: new Ray(), 73 | projectionPoint: vec3.create(), 74 | slideVector: vec3.create() 75 | } 76 | } -------------------------------------------------------------------------------- /js/engine/math/math-types.js: -------------------------------------------------------------------------------- 1 | function Scalar(value) { 2 | 3 | this.value = value; 4 | } 5 | 6 | function Sphere(position, radius) { 7 | 8 | this.position = position || vec3.create(); 9 | this.radius = radius || 0; 10 | } 11 | 12 | function AABB(from, to) { 13 | 14 | this.from = from || vec3.create(); 15 | this.to = to || vec3.create(); 16 | } 17 | 18 | function Plane(normal, d) { 19 | 20 | this.normal = normal || vec3.create(); 21 | this.d = d || 0; 22 | } 23 | 24 | function Frustum(planes) { 25 | 26 | this.planes = planes || [new Plane(), new Plane(), new Plane(), new Plane(), new Plane(), new Plane()]; 27 | } 28 | 29 | function Ray(origin, normal) { 30 | 31 | this.origin = origin || vec3.create(); 32 | this.normal = normal || vec3.create(); 33 | } 34 | 35 | function Axes() { 36 | this.xAxis = vec3.create(); 37 | this.yAxis = vec3.create(); 38 | this.zAxis = vec3.create(); 39 | } 40 | 41 | function CollisionLine(from, to, ray, length) { 42 | 43 | this.from = from || vec3.create(); 44 | this.to = to || vec3.create() 45 | this.ray = ray || new Ray(); 46 | this.length = length || 0; 47 | } 48 | 49 | function CollisionFace(points, facePlane, edgePlanes, freeNormalisedEdges, edgeLengths) { 50 | 51 | this.points = points; 52 | this.facePlane = facePlane; 53 | this.edgePlanes = edgePlanes; 54 | this.freeNormalisedEdges = freeNormalisedEdges; 55 | this.edgeLengths = edgeLengths; 56 | } 57 | 58 | function CalculateSphereCollisionWithCollisionFaceResult() { 59 | 60 | this.intersection = vec3.create(); 61 | this.distance = 0; 62 | this.collisionPlane = new Plane(); 63 | } 64 | 65 | function PlaneIntersectionSlideReaction() { 66 | this.direction = vec3.create(); 67 | this.distance = 0; 68 | } -------------------------------------------------------------------------------- /js/engine/mouse.js: -------------------------------------------------------------------------------- 1 | function Mouse(engine) { 2 | 3 | this.mouseMoveListeners = []; 4 | this.mouseIsDown = false; 5 | this.canvas = null; 6 | 7 | this.init = function (callback) { 8 | 9 | var self = this; 10 | 11 | this.canvas = document.getElementById('canvas'); 12 | 13 | this.canvas.onclick = function () { 14 | 15 | self.canvas.requestPointerLock = 16 | self.canvas.requestPointerLock || 17 | self.canvas.mozRequestPointerLock || 18 | self.canvas.webkitRequestPointerLock; 19 | 20 | self.canvas.requestPointerLock(); 21 | } 22 | 23 | document.addEventListener('mousemove', function (event) { 24 | 25 | if (self.hasPointerLock()) { 26 | for (var i = 0; i < self.mouseMoveListeners.length; i++) { 27 | self.mouseMoveListeners[i](event); 28 | } 29 | } 30 | 31 | }, false); 32 | 33 | document.addEventListener('mousedown', function (event) { 34 | 35 | if (self.hasPointerLock()) { 36 | self.mouseIsDown = true; 37 | } 38 | 39 | }, false); 40 | 41 | document.addEventListener('mouseup', function (event) { 42 | 43 | self.mouseIsDown = false; 44 | 45 | }, false); 46 | 47 | callback(); 48 | } 49 | 50 | this.addMouseMoveListener = function (callback) { 51 | 52 | this.mouseMoveListeners.push(callback); 53 | } 54 | 55 | this.hasPointerLock = function () { 56 | 57 | document.pointerLockElement = 58 | document.pointerLockElement || 59 | document.mozPointerLockElement || 60 | document.webkitPointerLockElement; 61 | 62 | if (document.pointerLockElement !== this.canvas) { 63 | return false; 64 | } 65 | 66 | return true; 67 | } 68 | } -------------------------------------------------------------------------------- /js/engine/particle-manager.js: -------------------------------------------------------------------------------- 1 | function ParticleManager(engine) { 2 | 3 | this.spawnParticle = function (emitter) { 4 | 5 | var particle = null; 6 | 7 | for (var i = 0; i < emitter.particles.length; i++) { 8 | 9 | var possibleParticle = emitter.particles[i]; 10 | 11 | if (!possibleParticle.active) { 12 | particle = possibleParticle; 13 | break; 14 | } 15 | } 16 | 17 | if (particle == null) { 18 | 19 | particle = { 20 | active: false, 21 | position: [0, 0, 0], 22 | direction: [0, 0, 0], 23 | textureIds: [null, null, null, null], 24 | data: null, 25 | physics: {} 26 | } 27 | 28 | emitter.particles.push(particle); 29 | } 30 | 31 | particle.physics.mode = ParticlePhysicsMode.None; 32 | particle.physics.lastCollisionType = ParticleCollisionType.None; 33 | 34 | engine.mapDataHelper.checkParticleData(emitter, particle); 35 | 36 | particle.active = true; 37 | 38 | return particle; 39 | } 40 | 41 | this.heartbeat = function () { 42 | 43 | for (var emitterId in engine.map.emittersById) { 44 | 45 | var emitter = engine.map.emittersById[emitterId]; 46 | 47 | for (var i = 0; i < emitter.particles.length; i++) { 48 | 49 | var particle = emitter.particles[i]; 50 | 51 | if (!particle.active) { 52 | continue; 53 | } 54 | 55 | if (emitter.particleControllerId != null) { 56 | 57 | var particleController = engine.particleControllersById[emitter.particleControllerId]; 58 | 59 | particleController.heartbeat(emitter, i); 60 | } 61 | } 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /js/engine/player-controller.js: -------------------------------------------------------------------------------- 1 | function PlayerController(engine) { 2 | 3 | var self = this; 4 | 5 | this.rotateRate = 0.003; 6 | this.moveRate = 0.2; 7 | 8 | this.init = function (callback) { 9 | 10 | engine.mouse.addMouseMoveListener(function (event) { 11 | 12 | if (engine.mode != 'game') { 13 | return; 14 | } 15 | 16 | self.handleMouseMove(event); 17 | }); 18 | 19 | callback(); 20 | } 21 | 22 | this.heartbeat = function () { 23 | 24 | var $ = this.$heartbeat; 25 | 26 | var player = engine.map.player; 27 | 28 | vec3.set($.playerYAxisOnlyRotation, 0, player.rotation[1], 0); 29 | math3D.buildAxesFromRotations($.movementAxes, $.playerYAxisOnlyRotation); 30 | 31 | math3D.buildMovementNormalFromAxes( 32 | $.movementNormal, $.movementAxes, engine.keyboard.movementAxisMultipliers); 33 | 34 | var movementAmount = this.moveRate * engine.frameTimer.frameDelta; 35 | 36 | this.movePlayerThoughMap($.movementNormal, movementAmount); 37 | 38 | engine.camera.position = player.position; 39 | 40 | math3D.buildAxesFromRotations(engine.camera.axes, player.rotation); 41 | } 42 | 43 | this.handleMouseMove = function (event) { 44 | 45 | var player = engine.map.player; 46 | 47 | player.rotation[0] += this.rotateRate * event.movementY * -1; 48 | player.rotation[1] += this.rotateRate * event.movementX * -1; 49 | } 50 | 51 | this.movePlayerThoughMap = function (movementNormal, movementAmount) { 52 | 53 | var $ = this.$movePlayerThoughMap; 54 | 55 | var player = engine.map.player; 56 | var heightOffGround = 1.0; 57 | 58 | vec3.copy($.collisionTestSphere.position, player.position); 59 | $.collisionTestSphere.radius = 0.45; 60 | 61 | vec3.sub($.collisionTestSphere.position, $.collisionTestSphere.position, [0, heightOffGround, 0]); 62 | 63 | engine.mapManager.moveSphereThroughMap($.collisionTestSphere, movementNormal, movementAmount, true); 64 | 65 | vec3.copy(engine.camera.position, $.collisionTestSphere.position); 66 | vec3.add(engine.camera.position, engine.camera.position, [0, heightOffGround, 0]); 67 | } 68 | 69 | // Function locals. 70 | this.$heartbeat = { 71 | movementAxes: new Axes(), 72 | //lookAxes: new Axes(), 73 | playerYAxisOnlyRotation: vec3.create(), 74 | movementNormal: vec3.create() 75 | } 76 | 77 | this.$movePlayerThoughMap = { 78 | collisionTestSphere: new Sphere() 79 | } 80 | } -------------------------------------------------------------------------------- /js/engine/resource-checker.js: -------------------------------------------------------------------------------- 1 | function ResourceChecker(engine) { 2 | 3 | this.ensureResourcesAreLoaded = function () { 4 | 5 | var allResourcesAreLoaded = true; 6 | 7 | // Check static meshes. 8 | for (var staticMeshId in engine.staticMeshManager.staticMeshesById) { 9 | 10 | var staticMesh = engine.staticMeshManager.staticMeshesById[staticMeshId]; 11 | 12 | for (var chunkIndex = 0; chunkIndex < staticMesh.chunks.length; chunkIndex++) { 13 | var chunk = staticMesh.chunks[chunkIndex]; 14 | if (engine.materialManager.materialsById[chunk.materialId] == null) { 15 | engine.materialManager.loadMaterial(chunk.materialId); 16 | allResourcesAreLoaded = false; 17 | } 18 | } 19 | } 20 | 21 | // Check materials. 22 | for (var materialId in engine.materialManager.materialsById) { 23 | var material = engine.materialManager.materialsById[materialId]; 24 | 25 | if (!util.stringIsNullOrEmpty(material.diffuseTextureId) && engine.textureManager.texturesById[material.diffuseTextureId] == null) { 26 | engine.textureManager.loadTexture(material.diffuseTextureId); 27 | allResourcesAreLoaded = false; 28 | } 29 | 30 | if (!util.stringIsNullOrEmpty(material.normalTextureId) && engine.textureManager.texturesById[material.normalTextureId] == null) { 31 | engine.textureManager.loadTexture(material.normalTextureId); 32 | allResourcesAreLoaded = false; 33 | } 34 | 35 | if (!util.stringIsNullOrEmpty(material.selfIlluminationTextureId) && engine.textureManager.texturesById[material.selfIlluminationTextureId] == null) { 36 | engine.textureManager.loadTexture(material.selfIlluminationTextureId); 37 | allResourcesAreLoaded = false; 38 | } 39 | } 40 | 41 | // Check actors. 42 | for (var actorId in engine.map.actorsById) { 43 | 44 | var actor = engine.map.actorsById[actorId]; 45 | 46 | if (!util.stringIsNullOrEmpty(actor.staticMeshId) && engine.staticMeshManager.staticMeshesById[actor.staticMeshId] == null) { 47 | 48 | engine.staticMeshManager.loadStaticMesh(actor.staticMeshId, { 49 | buildChunkAABBs: true, 50 | buildChunkCollisionFaces: true, 51 | buildRotationSafeBoundingSphere: true 52 | }); 53 | 54 | allResourcesAreLoaded = false; 55 | } 56 | 57 | if (!util.stringIsNullOrEmpty(actor.skinnedMeshId) && engine.skinnedMeshManager.getSkinnedMesh(actor.skinnedMeshId) == null) { 58 | engine.skinnedMeshManager.loadSkinnedMesh(actor.skinnedMeshId, { buildRotationSafeBoundingSphere: true }); 59 | allResourcesAreLoaded = false; 60 | } 61 | 62 | if (!util.stringIsNullOrEmpty(actor.skinnedMeshAnimationId) && engine.skinnedMeshAnimationManager.skinnedMeshAnimationsById[actor.skinnedMeshAnimationId] == null) { 63 | engine.skinnedMeshAnimationManager.loadSkinnedMeshAnimation(actor.skinnedMeshAnimationId, {}); 64 | allResourcesAreLoaded = false; 65 | } 66 | } 67 | 68 | // Check GUIs. 69 | for (var guiId in engine.map.guisById) { 70 | 71 | var gui = engine.map.guisById[guiId]; 72 | 73 | if (engine.guiLayoutManager.guiLayoutsById[gui.layoutId] == null) { 74 | engine.guiLayoutManager.loadGuiLayout(gui.layoutId); 75 | allResourcesAreLoaded = false; 76 | } 77 | } 78 | 79 | // Check GUI layouts. 80 | for (var guiLayoutId in engine.guiLayoutManager.guiLayoutsById) { 81 | 82 | var guiLayout = engine.guiLayoutManager.guiLayoutsById[guiLayoutId]; 83 | 84 | for (var animationId in guiLayout.animationsById) { 85 | if (engine.guiLayoutAnimationManager.getGuiLayoutAnimationExpansion(guiLayout.id, animationId) == null) { 86 | engine.guiLayoutAnimationManager.buildGuiLayoutAnimationExpansion(guiLayout.id, animationId); 87 | allResourcesAreLoaded = false; 88 | } 89 | } 90 | 91 | if (engine.spriteSheetManager.getSpriteSheet(guiLayout.spriteSheetId) == null) { 92 | engine.spriteSheetManager.loadSpriteSheet(guiLayout.spriteSheetId); 93 | allResourcesAreLoaded = false; 94 | } 95 | } 96 | 97 | // Check sprite sheets. 98 | for (var spriteSheetId in engine.spriteSheetManager.spriteSheetsById) { 99 | 100 | var spriteSheet = engine.spriteSheetManager.spriteSheetsById[spriteSheetId]; 101 | 102 | if (engine.textureManager.getTexture(spriteSheet.textureId) == null) { 103 | engine.textureManager.loadTexture(spriteSheet.textureId); 104 | allResourcesAreLoaded = false; 105 | } 106 | } 107 | 108 | return allResourcesAreLoaded; 109 | } 110 | } -------------------------------------------------------------------------------- /js/engine/resource-loader.js: -------------------------------------------------------------------------------- 1 | function ResourceLoader(engine) { 2 | 3 | this.loadTextResource = function (resourceType, resourceId, callback) { 4 | 5 | this.loadTextResourceInternal(resourceType, resourceId, 'txt', function (text) { 6 | callback(text); 7 | }); 8 | } 9 | 10 | this.loadJsonResource = function (resourceType, resourceId, callback) { 11 | 12 | this.loadTextResourceInternal(resourceType, resourceId, 'json', function (json) { 13 | 14 | if (json == null) { 15 | callback(null); 16 | } else { 17 | var obj = JSON.parse(json); 18 | callback(obj); 19 | } 20 | }); 21 | } 22 | 23 | this.loadImageResource = function (resourceType, resourceId, callback) { 24 | 25 | var url = this.getResourceUrl(resourceType, resourceId, 'png'); 26 | 27 | var image = new Image(); 28 | 29 | image.onload = function () { 30 | callback(image); 31 | } 32 | 33 | image.onerror = function () { 34 | console.log('Failed to load resource: ' + resourceId); 35 | callback(null); 36 | } 37 | 38 | image.src = url; 39 | } 40 | 41 | this.loadTextResourceInternal = function (resourceType, resourceId, dataType, callback) { 42 | 43 | var request = new XMLHttpRequest(); 44 | 45 | request.onload = function () { 46 | 47 | if (request.status == 404) { 48 | console.log('Failed to load resource: ' + resourceId); 49 | callback(null); 50 | } else { 51 | callback(this.responseText); 52 | } 53 | } 54 | 55 | var url = this.getResourceUrl(resourceType, resourceId, dataType); 56 | 57 | request.open('get', url, true); 58 | request.send(); 59 | } 60 | 61 | this.getResourceUrl = function (resourceType, resourceId, dataType) { 62 | 63 | return 'resources/' + this.getFolderNameForResourceType(resourceType) + '/' + resourceId + '.' + dataType + '?noCache=' + Math.random(); 64 | } 65 | 66 | this.getFolderNameForResourceType = function (resourceType) { 67 | 68 | var resourceFoldersByResourceType = { 69 | 'map': 'maps', 70 | 'static-mesh': 'static-meshes', 71 | 'skinned-mesh': 'skinned-meshes', 72 | 'skinned-mesh-animation': 'skinned-mesh-animations', 73 | 'sector-set': 'sector-sets', 74 | 'shader': 'shaders', 75 | 'material': 'materials', 76 | 'texture': 'textures', 77 | 'gui-layout': 'gui-layouts', 78 | 'sprite-sheet': 'sprite-sheets', 79 | 'system': 'system' 80 | } 81 | 82 | return resourceFoldersByResourceType[resourceType]; 83 | } 84 | } -------------------------------------------------------------------------------- /js/engine/shadow-map-manager.js: -------------------------------------------------------------------------------- 1 | function ShadowMapManager(engine) { 2 | 3 | var self = this; 4 | var gl = null; 5 | 6 | this.bufferSize = 512; 7 | 8 | this.nextAvailableShadowMapChannel = null; 9 | 10 | this.shadowMaps = []; 11 | 12 | this.init = function (callback) { 13 | 14 | self.log('Initialising point light shadow map manager...'); 15 | 16 | gl = engine.glManager.gl; 17 | 18 | self.log('... done.'); 19 | 20 | callback(); 21 | } 22 | 23 | this.allocateShadowMap = function (lightType) { 24 | 25 | if (this.nextAvailableShadowMapChannel == null) { 26 | 27 | this.createNewShadowMap(lightType); 28 | 29 | this.nextAvailableShadowMapChannel = 0; 30 | } 31 | 32 | var allocation = { 33 | index: this.shadowMaps.length - 1, 34 | channel: this.nextAvailableShadowMapChannel 35 | } 36 | 37 | this.log("Allocated shadow map " + allocation.index + ", channel: " + allocation.channel + "."); 38 | 39 | this.nextAvailableShadowMapChannel++; 40 | if (this.nextAvailableShadowMapChannel == 2) { 41 | this.nextAvailableShadowMapChannel = null; 42 | } 43 | 44 | return allocation; 45 | } 46 | 47 | this.createNewShadowMap = function (lightType) { 48 | 49 | if (lightType == 'point') { 50 | 51 | this.createPointLightShadowMap(); 52 | 53 | } else { 54 | 55 | throw 'Unknown light type: ' + lightType; 56 | } 57 | } 58 | 59 | this.createPointLightShadowMap = function () { 60 | 61 | var shadowMap = { 62 | lightType: 'point', 63 | frameBuffer: null, 64 | cubeTexture: null 65 | }; 66 | 67 | shadowMap.frameBuffer = gl.createFramebuffer(); 68 | gl.bindFramebuffer(gl.FRAMEBUFFER, shadowMap.frameBuffer); 69 | 70 | shadowMap.cubeTexture = gl.createTexture(); 71 | gl.bindTexture(gl.TEXTURE_CUBE_MAP, shadowMap.cubeTexture); 72 | gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 73 | gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 74 | gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 75 | gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 76 | 77 | gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, gl.RGBA, this.bufferSize, this.bufferSize, 0, gl.RGBA, gl.FLOAT, null); 78 | gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, gl.RGBA, this.bufferSize, this.bufferSize, 0, gl.RGBA, gl.FLOAT, null); 79 | gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, gl.RGBA, this.bufferSize, this.bufferSize, 0, gl.RGBA, gl.FLOAT, null); 80 | gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, gl.RGBA, this.bufferSize, this.bufferSize, 0, gl.RGBA, gl.FLOAT, null); 81 | gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, gl.RGBA, this.bufferSize, this.bufferSize, 0, gl.RGBA, gl.FLOAT, null); 82 | gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, gl.RGBA, this.bufferSize, this.bufferSize, 0, gl.RGBA, gl.FLOAT, null); 83 | 84 | gl.bindTexture(gl.TEXTURE_CUBE_MAP, null); 85 | 86 | gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, engine.renderer.shadowMapBuildBuffers.depthRenderBuffer); 87 | 88 | this.shadowMaps.push(shadowMap); 89 | 90 | this.log('Created new point light shadow map.'); 91 | } 92 | 93 | this.buildViewProjMatrixForPointLightCubeMapFaceBuild = function (out, position, face) { 94 | 95 | var lookAt = vec3.create(); 96 | vec3.add(lookAt, position, face.lookAt); 97 | 98 | var viewMatrix = mat4.create(); 99 | mat4.lookAt(viewMatrix, position, lookAt, face.up); 100 | 101 | var projMatrix = mat4.create(); 102 | mat4.perspective(projMatrix, Math.PI / 2.0, 1.0, 0.1, 10000.0); 103 | 104 | //var viewProjMatrix = mat4.create(); 105 | mat4.multiply(out, projMatrix, viewMatrix); 106 | 107 | //return viewProjMatrix; 108 | } 109 | 110 | this.cleanUp = function () { 111 | 112 | this.log('Cleaning up...'); 113 | 114 | for (var i = 0; i < this.shadowMaps.length; i++) { 115 | 116 | var shadowMap = this.shadowMaps[i]; 117 | 118 | if (shadowMap.lightType == 'point') { 119 | gl.deleteFramebuffer(shadowMap.frameBuffer); 120 | gl.deleteTexture(shadowMap.cubeTexture); 121 | } 122 | } 123 | 124 | this.shadowMaps = []; 125 | 126 | this.log('... done.'); 127 | } 128 | 129 | this.log = function (message) { 130 | 131 | console.log('Shadow Map Manager: ' + message); 132 | } 133 | } -------------------------------------------------------------------------------- /js/engine/skinned-mesh-animation-manager.js: -------------------------------------------------------------------------------- 1 | function SkinnedMeshAnimationManager(engine) { 2 | 3 | var self = this; 4 | var gl = null; 5 | 6 | this.skinnedMeshAnimationsById = {}; 7 | this.loadingSkinnedMeshAnimationIds = {}; 8 | this.failedSkinnedMeshAnimationIds = {}; 9 | 10 | this.init = function (callback) { 11 | 12 | gl = engine.glManager.gl; 13 | 14 | callback(); 15 | } 16 | 17 | this.loadSkinnedMeshAnimation = function (skinnedMeshAnimationId, options, callback) { 18 | 19 | options = options || {}; 20 | 21 | callback = callback || function () { } 22 | 23 | if (skinnedMeshAnimationId == null) { 24 | callback(null); 25 | return; 26 | } 27 | 28 | var skinnedMeshAnimation = this.skinnedMeshAnimationsById[skinnedMeshAnimationId]; 29 | 30 | if (skinnedMeshAnimation != null) { 31 | callback(skinnedMeshAnimation); 32 | return; 33 | } 34 | 35 | if (this.loadingSkinnedMeshAnimationIds[skinnedMeshAnimationId] || this.failedSkinnedMeshAnimationIds[skinnedMeshAnimationId]) { 36 | callback(null); 37 | return; 38 | } 39 | 40 | this.log('Loading skinned mesh animation: ' + skinnedMeshAnimationId); 41 | 42 | this.loadingSkinnedMeshAnimationIds[skinnedMeshAnimationId] = skinnedMeshAnimationId; 43 | 44 | engine.resourceLoader.loadJsonResource('skinned-mesh-animation', skinnedMeshAnimationId, function (skinnedMeshAnimation) { 45 | 46 | if (skinnedMeshAnimation == null) { 47 | 48 | self.failedSkinnedMeshAnimationIds[skinnedMeshAnimationId] = true; 49 | 50 | } else { 51 | 52 | self.skinnedMeshAnimationsById[skinnedMeshAnimationId] = skinnedMeshAnimation; 53 | } 54 | 55 | delete self.loadingSkinnedMeshAnimationIds[skinnedMeshAnimationId]; 56 | 57 | callback(skinnedMeshAnimation); 58 | }); 59 | } 60 | 61 | this.getSkinnedMeshAnimation = function (skinnedMeshAnimationId) { 62 | 63 | if (skinnedMeshAnimationId == null) { 64 | return null; 65 | } 66 | 67 | return this.skinnedMeshAnimationsById[skinnedMeshAnimationId]; 68 | } 69 | 70 | this.cleanUp = function () { 71 | 72 | this.log('Cleaning up...'); 73 | 74 | this.skinnedMeshAnimationsById = {}; 75 | 76 | this.log('... done.') 77 | } 78 | 79 | this.log = function (message) { 80 | 81 | console.log('Skinned Mesh Animation Manager: ' + message); 82 | } 83 | } -------------------------------------------------------------------------------- /js/engine/sprite-sheet-manager.js: -------------------------------------------------------------------------------- 1 | function SpriteSheetManager(engine) { 2 | 3 | var self = this; 4 | 5 | this.spriteSheetsById = {}; 6 | this.loadingSpriteSheetIds = {}; 7 | this.failedSpriteSheetIds = {}; 8 | 9 | this.loadSpriteSheet = function (spriteSheetId, callback) { 10 | 11 | callback = callback || function () { } 12 | 13 | if (spriteSheetId == null) { 14 | callback(null); 15 | return; 16 | } 17 | 18 | var spriteSheet = this.spriteSheetsById[spriteSheetId]; 19 | 20 | if (spriteSheet != null) { 21 | callback(spriteSheet); 22 | return; 23 | } 24 | 25 | if (this.loadingSpriteSheetIds[spriteSheetId] || this.failedSpriteSheetIds[spriteSheetId]) { 26 | callback(null); 27 | return; 28 | } 29 | 30 | this.log('Loading sprite sheet: ' + spriteSheetId); 31 | 32 | this.loadingSpriteSheetIds[spriteSheetId] = spriteSheetId; 33 | 34 | engine.resourceLoader.loadJsonResource('sprite-sheet', spriteSheetId, function (spriteSheet) { 35 | 36 | if (spriteSheet == null) { 37 | 38 | self.failedSpriteSheetIds[spriteSheetId] = true; 39 | 40 | } else { 41 | 42 | self.spriteSheetsById[spriteSheetId] = spriteSheet; 43 | } 44 | 45 | delete self.loadingSpriteSheetIds[spriteSheetId]; 46 | 47 | callback(spriteSheet); 48 | }); 49 | } 50 | 51 | this.getSpriteSheet = function (spriteSheetId) { 52 | 53 | if (spriteSheetId == null) { 54 | return null; 55 | } 56 | 57 | return this.spriteSheetsById[spriteSheetId]; 58 | } 59 | 60 | this.log = function (message) { 61 | 62 | console.log('Sprite Sheet Manager: ' + message); 63 | } 64 | } -------------------------------------------------------------------------------- /js/engine/static-mesh-manager.js: -------------------------------------------------------------------------------- 1 | function StaticMeshManager(engine) { 2 | 3 | var self = this; 4 | var gl = null; 5 | 6 | this.staticMeshesById = {}; 7 | this.loadingStaticMeshIds = {}; 8 | this.failedStaticMeshIds = {}; 9 | 10 | this.init = function (callback) { 11 | 12 | gl = engine.glManager.gl; 13 | 14 | callback(); 15 | } 16 | 17 | this.loadStaticMesh = function (staticMeshId, options, callback) { 18 | 19 | options = options || {}; 20 | 21 | callback = callback || function () { } 22 | 23 | if (staticMeshId == null) { 24 | callback(null); 25 | return; 26 | } 27 | 28 | var staticMesh = this.staticMeshesById[staticMeshId]; 29 | 30 | if (staticMesh != null) { 31 | callback(staticMesh); 32 | return; 33 | } 34 | 35 | if (this.loadingStaticMeshIds[staticMeshId] || this.failedStaticMeshIds[staticMeshId]) { 36 | callback(null); 37 | return; 38 | } 39 | 40 | this.log('Loading static mesh: ' + staticMeshId); 41 | 42 | this.loadingStaticMeshIds[staticMeshId] = staticMeshId; 43 | 44 | engine.resourceLoader.loadJsonResource('static-mesh', staticMeshId, function (staticMesh) { 45 | 46 | if (staticMesh == null) { 47 | 48 | self.failedStaticMeshIds[staticMeshId] = true; 49 | 50 | } else { 51 | 52 | self.initStaticMesh(staticMesh, options); 53 | 54 | self.staticMeshesById[staticMeshId] = staticMesh; 55 | } 56 | 57 | delete self.loadingStaticMeshIds[staticMeshId]; 58 | 59 | callback(staticMesh); 60 | }); 61 | } 62 | 63 | this.getStaticMesh = function (staticMeshId) { 64 | 65 | if (staticMeshId == null) { 66 | return null; 67 | } 68 | 69 | return this.staticMeshesById[staticMeshId]; 70 | } 71 | 72 | this.initStaticMesh = function (staticMesh, options) { 73 | 74 | if (options.buildRotationSafeBoundingSphere) { 75 | engine.staticMeshMathHelper.buildStaticMeshRotationSafeBoundingSphereRadius(staticMesh); 76 | } 77 | 78 | if (options.buildChunkAABBs) { 79 | engine.staticMeshMathHelper.buildStaticMeshChunkAABBs(staticMesh); 80 | } 81 | 82 | if (options.buildChunkCollisionFaces) { 83 | engine.staticMeshMathHelper.buildStaticMeshChunkCollisionFaces(staticMesh); 84 | } 85 | 86 | if (options.findPointCompletelyOutsideOfExtremities) { 87 | engine.staticMeshMathHelper.findStaticMeshPointCompletelyOutsideOfExtremities(staticMesh); 88 | } 89 | 90 | staticMesh.buffers = { 91 | vertexBuffer: gl.createBuffer(), 92 | normalsBuffer: gl.createBuffer(), 93 | tangentsBuffer: gl.createBuffer(), 94 | bitangentsBuffer: gl.createBuffer(), 95 | texCoordBuffer: gl.createBuffer(), 96 | indexBuffer: gl.createBuffer() 97 | }; 98 | 99 | gl.bindBuffer(gl.ARRAY_BUFFER, staticMesh.buffers.vertexBuffer); 100 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(staticMesh.verts), gl.STATIC_DRAW); 101 | 102 | gl.bindBuffer(gl.ARRAY_BUFFER, staticMesh.buffers.normalsBuffer); 103 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(staticMesh.normals), gl.STATIC_DRAW); 104 | 105 | gl.bindBuffer(gl.ARRAY_BUFFER, staticMesh.buffers.tangentsBuffer); 106 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(staticMesh.tangents), gl.STATIC_DRAW); 107 | 108 | gl.bindBuffer(gl.ARRAY_BUFFER, staticMesh.buffers.bitangentsBuffer); 109 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(staticMesh.bitangents), gl.STATIC_DRAW); 110 | 111 | gl.bindBuffer(gl.ARRAY_BUFFER, staticMesh.buffers.texCoordBuffer); 112 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(staticMesh.uvs), gl.STATIC_DRAW); 113 | 114 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, staticMesh.buffers.indexBuffer); 115 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(staticMesh.indecies), gl.STATIC_DRAW); 116 | 117 | delete staticMesh.verts; 118 | delete staticMesh.normals; 119 | delete staticMesh.tangents; 120 | delete staticMesh.bitangents; 121 | delete staticMesh.uvs; 122 | delete staticMesh.indecies; 123 | } 124 | 125 | this.cleanUp = function () { 126 | 127 | this.log('Cleaning up...'); 128 | 129 | for (var staticMeshId in this.staticMeshesById) { 130 | 131 | var staticMesh = this.staticMeshesById[staticMeshId]; 132 | 133 | this.cleanUpStaticMeshBuffers(staticMesh) 134 | } 135 | 136 | this.staticMeshesById = {}; 137 | 138 | this.log('... done.') 139 | } 140 | 141 | this.cleanUpStaticMeshBuffers = function (staticMesh) { 142 | 143 | for (var bufferName in staticMesh.buffers) { 144 | 145 | var buffer = staticMesh.buffers[bufferName]; 146 | 147 | if (buffer != null) { 148 | gl.deleteBuffer(buffer); 149 | } 150 | } 151 | } 152 | 153 | this.log = function (message) { 154 | 155 | console.log('Static Mesh Manager: ' + message); 156 | } 157 | } -------------------------------------------------------------------------------- /js/engine/texture-manager.js: -------------------------------------------------------------------------------- 1 | function TextureManager(engine) { 2 | 3 | var self = this; 4 | var gl = null; 5 | 6 | this.texturesById = {}; 7 | this.loadingTextureIds = {}; 8 | this.failedTextureIds = {}; 9 | 10 | this.init = function (callback) { 11 | 12 | gl = engine.glManager.gl; 13 | 14 | callback(); 15 | } 16 | 17 | this.loadTextures = function (textureIds, callback) { 18 | 19 | util.recurse(function (recursor, recursionCount) { 20 | if (recursionCount < textureIds.length) { 21 | self.loadTexture(textureIds[recursionCount], recursor); 22 | } else { 23 | callback(); 24 | } 25 | }); 26 | } 27 | 28 | this.loadTexture = function (textureId, callback) { 29 | 30 | callback = callback || function () { } 31 | 32 | if (textureId == null) { 33 | callback(null); 34 | return; 35 | } 36 | 37 | var texture = this.getTexture(textureId); 38 | 39 | if (texture != null) { 40 | callback(texture); 41 | return; 42 | } 43 | 44 | if (this.loadingTextureIds[textureId] || this.failedTextureIds[textureId]) { 45 | callback(null); 46 | return; 47 | } 48 | 49 | this.log('Loading texture: ' + textureId); 50 | 51 | if (textureId.indexOf('-cube') == -1) { 52 | 53 | this.load2dTextureInternal(textureId, callback); 54 | 55 | } else { 56 | 57 | this.loadCubeTextureInternal(textureId, callback); 58 | } 59 | } 60 | 61 | this.load2dTextureInternal = function (textureId, callback) { 62 | 63 | this.loadingTextureIds[textureId] = true; 64 | 65 | engine.resourceLoader.loadImageResource('texture', textureId, function (image) { 66 | 67 | var texture = null; 68 | 69 | if (image == null) { 70 | 71 | self.failedTextureIds[textureId] = true; 72 | 73 | } else { 74 | 75 | texture = gl.createTexture(); 76 | gl.bindTexture(gl.TEXTURE_2D, texture); 77 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); 78 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); 79 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 80 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 81 | gl.bindTexture(gl.TEXTURE_2D, null); 82 | 83 | texture.width = image.width; 84 | texture.height = image.height; 85 | 86 | self.texturesById[textureId] = texture; 87 | } 88 | 89 | delete self.loadingTextureIds[textureId]; 90 | 91 | callback(texture); 92 | }); 93 | } 94 | 95 | this.loadCubeTextureInternal = function (textureId, callback) { 96 | 97 | var faces = [ 98 | { faceTextureId: textureId + "-px", cubeFace: gl.TEXTURE_CUBE_MAP_POSITIVE_X, image: null }, 99 | { faceTextureId: textureId + "-nx", cubeFace: gl.TEXTURE_CUBE_MAP_NEGATIVE_X, image: null }, 100 | { faceTextureId: textureId + "-py", cubeFace: gl.TEXTURE_CUBE_MAP_POSITIVE_Y, image: null }, 101 | { faceTextureId: textureId + "-ny", cubeFace: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, image: null }, 102 | { faceTextureId: textureId + "-pz", cubeFace: gl.TEXTURE_CUBE_MAP_POSITIVE_Z, image: null }, 103 | { faceTextureId: textureId + "-nz", cubeFace: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, image: null }]; 104 | 105 | var allFaceTexturesWereLoaded = true; 106 | 107 | util.recurse(function (recursor, recursionCount) { 108 | 109 | if (recursionCount < faces.length) { 110 | 111 | var face = faces[recursionCount]; 112 | 113 | engine.resourceLoader.loadImageResource('texture', face.faceTextureId, function (image) { 114 | face.image = image; 115 | allFaceTexturesWereLoaded = allFaceTexturesWereLoaded && (image != null); 116 | recursor(); 117 | }); 118 | 119 | } else { 120 | 121 | delete self.loadingTextureIds[textureId]; 122 | 123 | var texture = null; 124 | 125 | if (allFaceTexturesWereLoaded) { 126 | 127 | texture = gl.createTexture(); 128 | 129 | gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture); 130 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); 131 | 132 | for (var i = 0; i < faces.length; i++) { 133 | var face = faces[i]; 134 | gl.texImage2D(face.cubeFace, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, face.image); 135 | gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 136 | gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 137 | } 138 | 139 | gl.bindTexture(gl.TEXTURE_CUBE_MAP, null); 140 | 141 | self.texturesById[textureId] = texture; 142 | 143 | } else { 144 | 145 | self.failedTextureIds[textureId] = true; 146 | } 147 | 148 | callback(texture); 149 | } 150 | }); 151 | } 152 | 153 | this.getTexture = function (textureId) { 154 | 155 | if (textureId == null) { 156 | return null; 157 | } 158 | 159 | return this.texturesById[textureId]; 160 | } 161 | 162 | this.cleanUp = function () { 163 | 164 | this.log('Cleaning up...'); 165 | 166 | for (var textureId in this.texturesById) { 167 | 168 | if (textureId.indexOf('system/') == 0) { 169 | continue; 170 | } 171 | 172 | var texture = this.texturesById[textureId]; 173 | 174 | gl.deleteTexture(texture); 175 | delete this.texturesById[textureId]; 176 | } 177 | 178 | this.loadingTextureIds = {}; 179 | this.failedTextureIds = {}; 180 | 181 | this.log('... done.'); 182 | } 183 | 184 | this.log = function (message) { 185 | 186 | console.log('Texture Manager: ' + message); 187 | } 188 | } -------------------------------------------------------------------------------- /js/engine/ticker.js: -------------------------------------------------------------------------------- 1 | function Ticker(ticksPerSecond) { 2 | 3 | this.ticksPerSecond = ticksPerSecond; 4 | this.t = -1; 5 | } 6 | 7 | Ticker.prototype.tick = function () { 8 | 9 | var isTick = false; 10 | 11 | if (this.t == -1) { 12 | 13 | this.t = (1000 / this.ticksPerSecond) * Math.random(); 14 | 15 | } else { 16 | 17 | this.t -= engine.frameTimer.lastFrameDurationMillis; 18 | if (this.t <= 0) { 19 | this.t = 1000 / this.ticksPerSecond; 20 | isTick = true; 21 | } 22 | } 23 | 24 | return isTick; 25 | } -------------------------------------------------------------------------------- /js/engine/trigger-manager.js: -------------------------------------------------------------------------------- 1 | function TriggerManager(engine) { 2 | 3 | this.triggerStatesByTriggerId = {}; 4 | 5 | this.heartbeat = function () { 6 | 7 | for (var triggerId in engine.map.triggersById) { 8 | 9 | var trigger = engine.map.triggersById[triggerId]; 10 | var triggerState = this.coalesceTriggerState(triggerId); 11 | var triggerController = engine.triggerControllersById[trigger.controllerId]; 12 | 13 | // Update the trigger's AABB. 14 | vec3.copy(triggerState.aabb.from, trigger.position); 15 | vec3.copy(triggerState.aabb.to, trigger.position); 16 | triggerState.aabb.to[0] += trigger.size[0]; 17 | triggerState.aabb.to[1] += trigger.size[1]; 18 | triggerState.aabb.to[2] -= trigger.size[2]; 19 | 20 | // Determine if the player is within trigger. 21 | var playerWasPreviouslyWithinTrigger = triggerState.playerIsWithinTrigger; 22 | triggerState.playerIsWithinTrigger = math3D.checkPointIsWithinAABB(triggerState.aabb, engine.map.player.position); 23 | 24 | var playerHasJustEnteredTrigger = !playerWasPreviouslyWithinTrigger && triggerState.playerIsWithinTrigger; 25 | var playerHasJustLeftTrigger = playerWasPreviouslyWithinTrigger && !triggerState.playerIsWithinTrigger; 26 | 27 | // Handle events (if there are any). 28 | if (triggerController != null) { 29 | 30 | if (playerHasJustEnteredTrigger && triggerController.handlePlayerEnter != null) { 31 | triggerController.handlePlayerEnter(trigger); 32 | } 33 | 34 | if (playerHasJustLeftTrigger && triggerController.handlePlayerLeave != null) { 35 | triggerController.handlePlayerLeave(trigger); 36 | } 37 | } 38 | } 39 | } 40 | 41 | this.coalesceTriggerState = function (triggerId) { 42 | 43 | var triggerState = this.triggerStatesByTriggerId[triggerId]; 44 | 45 | if (triggerState == null) { 46 | 47 | triggerState = { 48 | aabb: new AABB(), 49 | playerIsWithinTrigger: false 50 | } 51 | 52 | this.triggerStatesByTriggerId[triggerId] = triggerState; 53 | } 54 | 55 | return triggerState; 56 | } 57 | } -------------------------------------------------------------------------------- /js/engine/util.js: -------------------------------------------------------------------------------- 1 | function Util() { 2 | 3 | this.recurse = function (recursionFunction) { 4 | 5 | var recursor = null; 6 | var recursionCount = -1; 7 | 8 | recursor = function () { 9 | recursionCount++; 10 | recursionFunction(recursor, recursionCount); 11 | } 12 | 13 | recursor(); 14 | } 15 | 16 | this.getObjectPropertyNames = function (obj) { 17 | 18 | var names = []; 19 | for (var propertyName in obj) { 20 | names.push(propertyName); 21 | } 22 | 23 | return names; 24 | } 25 | 26 | this.copyObjectPropertiesToOtherObject = function (src, dest) { 27 | for (var propertyName in src) { 28 | dest[propertyName] = src[propertyName]; 29 | } 30 | } 31 | 32 | this.arrayPushMany = function (destArray, srcArray) { 33 | for (var i = 0; i < srcArray.length; i++) { 34 | destArray.push(srcArray[i]); 35 | } 36 | } 37 | 38 | this.arraySetMany = function (destArray, destStartIndex, srcArray) { 39 | for (var i = 0; i < srcArray.length; i++) { 40 | destArray[destStartIndex + i] = srcArray[i]; 41 | } 42 | } 43 | 44 | this.arrayIndexOf = function (array, value) { 45 | var i = 0; 46 | for (var i = 0; i < array.length; i++) { 47 | if (array[i] == value) { 48 | return i; 49 | } 50 | } 51 | 52 | return -1; 53 | } 54 | 55 | this.clearFixedLengthArray = function (array, clearValue) { 56 | 57 | array.length = 0; 58 | 59 | for (var i = 0; i < array.maxLength; i++) { 60 | array.items[i] = clearValue; 61 | } 62 | } 63 | 64 | this.fixedLengthArrayIndexOf = function (array, value) { 65 | var i = 0; 66 | for (var i = 0; i < array.length; i++) { 67 | if (array.items[i] == value) { 68 | return i; 69 | } 70 | } 71 | 72 | return -1; 73 | } 74 | 75 | this.fixedLengthArrayPush = function (array, value) { 76 | 77 | array.items[array.length++] = value; 78 | } 79 | 80 | this.stringIsNullOrEmpty = function (s) { 81 | 82 | return s == null || s == ''; 83 | } 84 | } 85 | 86 | -------------------------------------------------------------------------------- /resources/gui-layouts/hud.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "hud", 3 | "spriteSheetId": "sprite-sheet-1", 4 | "size": [512, 512], 5 | "spritesById": { 6 | "health-bar": { 7 | "id": "health-bar-inner", 8 | "spriteSpecId": "health-bar-inner", 9 | "position": [ 30, 16 ], 10 | "size": [ 50, 12 ], 11 | "rotation": 0, 12 | "visible": true 13 | }, 14 | "health-overlay": { 15 | "id": "health-bar-overlay", 16 | "spriteSpecId": "health-bar-overlay", 17 | "position": [ 10, 10 ], 18 | "size": [ 75, 25 ], 19 | "rotation": 0, 20 | "visible": true 21 | }, 22 | "health-bar-icon": { 23 | "id": "health-bar-icon", 24 | "spriteSpecId": "health-bar-icon", 25 | "position": [ 17, 18 ], 26 | "size": [ 10, 10 ], 27 | "rotation": 0, 28 | "visible": true 29 | }, 30 | "crosshair": { 31 | "id": "crosshair", 32 | "spriteSpecId": "crosshair", 33 | "position": [ 250, 135 ], 34 | "size": [ 10, 10 ], 35 | "rotation": 0, 36 | "visible": true 37 | }, 38 | "player-death-message": { 39 | "id": "player-death-message", 40 | "spriteSpecId": "player-death-message", 41 | "position": [ 100, 120 ], 42 | "size": [ 324, 69 ], 43 | "rotation": 0, 44 | "visible": false 45 | } 46 | }, 47 | "animationsById": { 48 | "health-bar-grow": { 49 | "id": "health-bar-grow", 50 | "numberOfFrames": 100, 51 | "tweens": [ 52 | { 53 | "startFrameIndex": 0, 54 | "numberOfFrames": 100, 55 | "targetId": "health-bar-inner", 56 | "propertyId": 3, 57 | "fromValue": 0.0, 58 | "toValue": -50.0 59 | } 60 | ] 61 | }, 62 | "health-bar-icon": { 63 | "id": "health-bar-icon", 64 | "numberOfFrames": 100, 65 | "tweens": [ 66 | { 67 | "startFrameIndex": 0, 68 | "numberOfFrames": 100, 69 | "targetId": "health-bar-icon", 70 | "propertyId": 5, 71 | "fromValue": 0.0, 72 | "toValue": 6.28318 73 | } 74 | ] 75 | }, 76 | "player-death-message-toggle": { 77 | "id": "player-death-message-toggle", 78 | "numberOfFrames": 2, 79 | "tweens": [ 80 | { 81 | "startFrameIndex": 0, 82 | "numberOfFrames": 1, 83 | "targetId": "player-death-message", 84 | "propertyId": 6, 85 | "absoluteValue": 0 86 | }, 87 | { 88 | "startFrameIndex": 1, 89 | "numberOfFrames": 1, 90 | "targetId": "player-death-message", 91 | "propertyId": 6, 92 | "absoluteValue": 1 93 | } 94 | ] 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /resources/maps/test-map-1.json: -------------------------------------------------------------------------------- 1 | {"worldStaticMeshId":"test-map-1","sectorSetId":"test-map-1","player":{"position":[1.468284249305725,-1.0399999618530273,4.5205793380737305],"rotation":[0.10200000000000142,0.06799999999980647,0]},"globalIlluminationColours":[[0.13,0.13,0.18],[0.02,0.02,0.06],[0.11,0.11,0.14],[0.04,0.04,0.1],[0.15,0.15,0.15],[0.06,0.06,0.06],[0.01,0.01,0.01],[0.02,0.02,0.02]],"lightsById":{"light-1":{"id":"light-1","type":"point","position":[1,-0.5,1],"radius":3,"colour":[1,0,0],"enabled":true,"castsShadows":true},"light-2":{"id":"light-2","type":"point","position":[0,5,-4],"radius":10,"colour":[1,1,1],"enabled":true,"castsShadows":true},"light-3":{"id":"light-3","type":"point","position":[8,-0.8,5],"radius":6,"colour":[0.5,0.5,1],"enabled":true,"castsShadows":false},"light-4":{"id":"light-4","type":"point","position":[-4,3,1],"radius":2.5,"colour":[1,1,0],"enabled":true,"castsShadows":false},"light-5":{"id":"light-5","type":"point","position":[2,5.5,4],"radius":3.8,"colour":[1,0,1],"enabled":true,"castsShadows":false}},"actorsById":{"actor-1":{"id":"actor-1","position":[0,0,0],"rotation":[0,35.13000000000158,35.13000000000158],"controllerId":"TestActorController","staticMeshId":"fancy-cube","data":{"direction":1,"speed":0.01},"frameIndex":0},"actor-2":{"id":"actor-2","position":[-3,3,-5],"rotation":[0,0,0],"staticMeshId":"","controllerId":null,"data":{},"skinnedMeshId":"robot","skinnedMeshAnimationId":"robot-walk","frameIndex":0.8386099999826406}}} -------------------------------------------------------------------------------- /resources/materials/brick-wall-1.json: -------------------------------------------------------------------------------- 1 | {"diffuseTextureId":"brick-wall-1-d","normalTextureId":"brick-wall-1-n","selfIlluminationTextureId":"light-1"} -------------------------------------------------------------------------------- /resources/materials/pipe-1.json: -------------------------------------------------------------------------------- 1 | {"diffuseTextureId":"pipe-1-d","normalTextureId":"pipe-1-n"} -------------------------------------------------------------------------------- /resources/materials/tiled-floor-1.json: -------------------------------------------------------------------------------- 1 | {"diffuseTextureId":"tiled-floor-1-d","normalTextureId":"tiled-floor-1-n"} -------------------------------------------------------------------------------- /resources/sector-sets/test-map-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "metrics": { 3 | "sectorCount": [ 5, 2, 5 ], 4 | "sectorSize": [ 4, 4, 4 ], 5 | "rootOrigin": [ -10, -5, 10 ] 6 | }, 7 | "sectors": [ 8 | { "visibleSectorIndexes": [ ] } 9 | ] 10 | } -------------------------------------------------------------------------------- /resources/sector-sets/test-map-2.json: -------------------------------------------------------------------------------- 1 | {"metrics":{"sectorCount":[4,2,3],"sectorSize":[12,12,12],"rootOrigin":[-24,0,12]},"sectors":[{"visibleSectorIndexes":[0,1,2,6]},{"visibleSectorIndexes":[1,0,2,6]},{"visibleSectorIndexes":[2,0,1,7,8,10,11,13,14,16,17,19,22]},{"visibleSectorIndexes":[3,4,5,9]},{"visibleSectorIndexes":[4,3,5,9]},{"visibleSectorIndexes":[5,3,4,7,8,10,11,13,14,16,17,19,22]},{"visibleSectorIndexes":[6,0,1,12,18]},{"visibleSectorIndexes":[7,2,5,8,10,11,13,14,16,17,19,22]},{"visibleSectorIndexes":[8,2,5,7,10,11,13,14,16,17,19,22]},{"visibleSectorIndexes":[9,3,4,15,21]},{"visibleSectorIndexes":[10,2,5,7,8,11,13,14,16,17,19,22]},{"visibleSectorIndexes":[11,2,5,7,8,10,13,14,16,17,19,22]},{"visibleSectorIndexes":[12,6,18,19]},{"visibleSectorIndexes":[13,2,5,7,8,10,11,14,16,17,19,22]},{"visibleSectorIndexes":[14,2,5,7,8,10,11,13,16,17,19,22]},{"visibleSectorIndexes":[15,9,21,22]},{"visibleSectorIndexes":[16,2,5,7,8,10,11,13,14,17,19,22]},{"visibleSectorIndexes":[17,2,5,7,8,10,11,13,14,16,19,22]},{"visibleSectorIndexes":[18,6,12,19,20]},{"visibleSectorIndexes":[19,2,5,7,8,10,11,12,13,14,16,17,18,20]},{"visibleSectorIndexes":[20,18,19]},{"visibleSectorIndexes":[21,9,15,22,23]},{"visibleSectorIndexes":[22,2,5,7,8,10,11,13,14,15,16,17,21,23]},{"visibleSectorIndexes":[23,21,22]}]} -------------------------------------------------------------------------------- /resources/shaders/gui-fs.txt: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | uniform sampler2D textureSampler; 4 | 5 | varying vec2 texCoord; 6 | 7 | void main(void) 8 | { 9 | vec4 textureColour = texture2D(textureSampler, texCoord); 10 | 11 | gl_FragColor = textureColour; 12 | } -------------------------------------------------------------------------------- /resources/shaders/gui-vs.txt: -------------------------------------------------------------------------------- 1 | attribute vec2 sizeMultiplier; 2 | 3 | uniform vec2 position; 4 | uniform vec2 size; 5 | uniform vec2 scale; 6 | uniform vec2 xAxis; 7 | uniform vec2 yAxis; 8 | uniform vec2 uvPosition; 9 | uniform vec2 uvSize; 10 | 11 | varying vec2 texCoord; 12 | 13 | void main(void) 14 | { 15 | /*vec2 guiSpacePosition = 16 | position + (size * sizeMultiplier); 17 | 18 | guiSpacePosition = 19 | guiSpacePosition - (size / 2.0); 20 | 21 | guiSpacePosition = 22 | ((xAxis * guiSpacePosition.x) + (yAxis * guiSpacePosition.y)); 23 | 24 | guiSpacePosition = 25 | guiSpacePosition + (size / 2.0);*/ 26 | 27 | vec2 guiSpacePosition = 28 | position + (size * sizeMultiplier); 29 | 30 | guiSpacePosition = 31 | guiSpacePosition - position - (size / 2.0); 32 | 33 | guiSpacePosition = 34 | ((xAxis * guiSpacePosition.x) + (yAxis * guiSpacePosition.y)); 35 | 36 | guiSpacePosition = 37 | guiSpacePosition + position + (size / 2.0); 38 | 39 | 40 | vec2 clipSpacePosition = 41 | vec2(-1, 1) + 42 | ((guiSpacePosition * scale * vec2(1.0, -1.0) * 2.0)); 43 | 44 | texCoord = (uvPosition + (uvSize * sizeMultiplier)) * vec2(1.0, -1.0); 45 | 46 | gl_Position = vec4(clipSpacePosition, 0.0, 1.0); 47 | } -------------------------------------------------------------------------------- /resources/shaders/line-fs.txt: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | uniform vec3 colour; 3 | 4 | void main(void) 5 | { 6 | gl_FragColor = vec4(colour, 1.0); 7 | } -------------------------------------------------------------------------------- /resources/shaders/line-vs.txt: -------------------------------------------------------------------------------- 1 | attribute vec3 vertexPosition; 2 | 3 | uniform mat4 viewProjMatrix; 4 | uniform vec3 position; 5 | uniform vec3 size; 6 | 7 | void main(void) 8 | { 9 | vec3 temp = vertexPosition * size + position; 10 | 11 | gl_Position = viewProjMatrix * vec4(temp, 1.0); 12 | } -------------------------------------------------------------------------------- /resources/shaders/particle-fs.txt: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | uniform sampler2D texture1Sampler; 4 | uniform sampler2D texture2Sampler; 5 | 6 | varying vec2 texCoord; 7 | 8 | void main(void) 9 | { 10 | vec4 texture1Colour = texture2D(texture1Sampler, texCoord); 11 | vec4 texture2Colour = texture2D(texture2Sampler, texCoord); 12 | 13 | vec4 finalColour = vec4(0.0, 0.0, 0.0, 0.0); 14 | 15 | finalColour.rgb = texture1Colour.rgb + (texture2Colour.rgb * texture2Colour.a); 16 | finalColour.a = texture1Colour.a + texture2Colour.a; 17 | 18 | gl_FragColor = finalColour; 19 | } -------------------------------------------------------------------------------- /resources/shaders/particle-vs.txt: -------------------------------------------------------------------------------- 1 | //attribute vec3 vertexPosition; 2 | attribute vec2 vertexOffset; 3 | attribute vec2 vertexTexCoord; 4 | 5 | uniform mat4 viewProjMatrix; 6 | uniform vec3 position; 7 | uniform vec2 size; 8 | uniform vec3 cameraXAxis; 9 | uniform vec3 cameraYAxis; 10 | 11 | varying vec2 texCoord; 12 | 13 | void main(void) 14 | { 15 | vec3 temp = position; 16 | 17 | temp += (vertexOffset.x * size.x) * cameraXAxis; 18 | temp += (vertexOffset.y * size.y) * cameraYAxis; 19 | 20 | texCoord = vertexTexCoord; 21 | 22 | gl_Position = viewProjMatrix * vec4(temp, 1.0); 23 | } -------------------------------------------------------------------------------- /resources/shaders/skinned-mesh-main-render-vs.txt: -------------------------------------------------------------------------------- 1 | attribute vec3 vertexPosition; 2 | attribute vec3 vertexNormal; 3 | attribute vec2 vertexTexCoord; 4 | attribute float firstBoneIndex; 5 | attribute float secondBoneIndex; 6 | attribute float thirdBoneIndex; 7 | attribute float fourthBoneIndex; 8 | attribute float fifthBoneIndex; 9 | attribute float firstWeight; 10 | attribute float secondWeight; 11 | attribute float thirdWeight; 12 | attribute float fourthWeight; 13 | attribute float fifthWeight; 14 | 15 | uniform mat4 worldMatrix; 16 | uniform mat4 viewProjMatrix; 17 | uniform mat4 boneMatrices[30]; 18 | 19 | varying vec3 fragmentWorldSpacePosition; 20 | varying vec3 fragmentNormal; 21 | /*varying vec3 fragmentTangent; 22 | varying vec3 fragmentBitangent;*/ 23 | varying vec2 texCoord; 24 | 25 | vec3 transformVectorByBone(int boneIndex, vec3 v); 26 | vec3 transformVectorByBoneRotationOnly(int boneIndex, vec3 v); 27 | mat4 getBoneMatrix(int boneIndex); 28 | 29 | void main(void) 30 | { 31 | // Calculate the skinned position. 32 | vec3 positionTransformedByFirstBone = transformVectorByBone(int(firstBoneIndex), vertexPosition); 33 | vec3 positionTransformedBySecondBone = transformVectorByBone(int(secondBoneIndex), vertexPosition); 34 | vec3 positionTransformedByThirdBone = transformVectorByBone(int(thirdBoneIndex), vertexPosition); 35 | vec3 positionTransformedByFourthBone = transformVectorByBone(int(fourthBoneIndex), vertexPosition); 36 | vec3 positionTransformedByFifthBone = transformVectorByBone(int(fifthBoneIndex), vertexPosition); 37 | 38 | vec3 positionTransformedByBones = 39 | (positionTransformedByFirstBone * firstWeight) + 40 | (positionTransformedBySecondBone * secondWeight) + 41 | (positionTransformedByThirdBone * thirdWeight) + 42 | (positionTransformedByFourthBone * fourthWeight) + 43 | (positionTransformedByFifthBone * fifthWeight); 44 | 45 | vec4 tempWorldSpacePosition = worldMatrix * vec4(positionTransformedByBones, 1.0); 46 | 47 | // Calculate the skinned normal. 48 | vec3 normalTransformedByFirstBone = transformVectorByBoneRotationOnly(int(firstBoneIndex), vertexNormal); 49 | vec3 normalTransformedBySecondBone = transformVectorByBoneRotationOnly(int(secondBoneIndex), vertexNormal); 50 | vec3 normalTransformedByThirdBone = transformVectorByBoneRotationOnly(int(thirdBoneIndex), vertexNormal); 51 | vec3 normalTransformedByFourthBone = transformVectorByBoneRotationOnly(int(fourthBoneIndex), vertexNormal); 52 | vec3 normalTransformedByFifthBone = transformVectorByBoneRotationOnly(int(fifthBoneIndex), vertexNormal); 53 | 54 | vec3 normalTransformedByBones = 55 | (normalTransformedByFirstBone * firstWeight) + 56 | (normalTransformedBySecondBone * secondWeight) + 57 | (normalTransformedByThirdBone * thirdWeight) + 58 | (normalTransformedByFourthBone * fourthWeight) + 59 | (normalTransformedByFifthBone * fifthWeight); 60 | 61 | vec3 tempWorldSpaceNormal = normalize(mat3(worldMatrix) * normalTransformedByBones); 62 | //vec3 tempWorldSpaceNormal = vertexNormal; 63 | 64 | // Output values. 65 | fragmentWorldSpacePosition = tempWorldSpacePosition.xyz; 66 | 67 | texCoord = vertexTexCoord; 68 | 69 | fragmentNormal = tempWorldSpaceNormal; 70 | 71 | gl_Position = viewProjMatrix * tempWorldSpacePosition; 72 | } 73 | 74 | vec3 transformVectorByBone(int boneIndex, vec3 v) 75 | { 76 | if (boneIndex != -1) 77 | { 78 | mat4 boneMatrix = getBoneMatrix(boneIndex); 79 | 80 | vec4 vectorTransformedByBone = boneMatrix * vec4(v, 1.0); 81 | 82 | return vectorTransformedByBone.xyz; 83 | } 84 | else 85 | { 86 | return v;//vec3(0, 0, 0); 87 | } 88 | } 89 | 90 | vec3 transformVectorByBoneRotationOnly(int boneIndex, vec3 v) 91 | { 92 | if (boneIndex != -1) 93 | { 94 | mat4 boneMatrix = getBoneMatrix(boneIndex); 95 | 96 | vec3 vectorTransformedByBone = mat3(boneMatrix) * v; 97 | 98 | return vectorTransformedByBone; 99 | } 100 | else 101 | { 102 | return vec3(0, 0, 0); 103 | } 104 | } 105 | 106 | mat4 getBoneMatrix(int boneIndex) 107 | { 108 | for (int i = 0; i < 30; i++) 109 | { 110 | if (i == boneIndex) 111 | { 112 | return boneMatrices[i]; 113 | } 114 | } 115 | 116 | return boneMatrices[0]; // This just shuts the compiler up. This should never happen. 117 | } -------------------------------------------------------------------------------- /resources/shaders/skinned-mesh-shadow-map-build-back-pass-fs.txt: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | uniform vec3 lightWorldPosition; 4 | uniform vec4 shadowMapMask; 5 | 6 | varying vec3 worldVertexPosition; 7 | 8 | void main(void) 9 | { 10 | vec3 lightToVertex = worldVertexPosition - lightWorldPosition; 11 | 12 | float distanceSquared = dot(lightToVertex, lightToVertex); 13 | 14 | gl_FragColor = shadowMapMask * distanceSquared; 15 | //gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); 16 | } -------------------------------------------------------------------------------- /resources/shaders/skinned-mesh-shadow-map-build-back-pass-vs.txt: -------------------------------------------------------------------------------- 1 | attribute vec3 vertexPosition; 2 | attribute float firstBoneIndex; 3 | attribute float secondBoneIndex; 4 | attribute float thirdBoneIndex; 5 | attribute float fourthBoneIndex; 6 | attribute float fifthBoneIndex; 7 | attribute float firstWeight; 8 | attribute float secondWeight; 9 | attribute float thirdWeight; 10 | attribute float fourthWeight; 11 | attribute float fifthWeight; 12 | 13 | uniform mat4 worldMatrix; 14 | uniform mat4 viewProjMatrix; 15 | uniform mat4 boneMatrices[30]; 16 | 17 | varying vec3 worldVertexPosition; 18 | 19 | vec3 transformVectorByBone(int boneIndex, vec3 v); 20 | vec3 transformVectorByBoneRotationOnly(int boneIndex, vec3 v); 21 | mat4 getBoneMatrix(int boneIndex); 22 | 23 | void main(void) 24 | { 25 | // Calculate the skinned position. 26 | vec3 positionTransformedByFirstBone = transformVectorByBone(int(firstBoneIndex), vertexPosition); 27 | vec3 positionTransformedBySecondBone = transformVectorByBone(int(secondBoneIndex), vertexPosition); 28 | vec3 positionTransformedByThirdBone = transformVectorByBone(int(thirdBoneIndex), vertexPosition); 29 | vec3 positionTransformedByFourthBone = transformVectorByBone(int(fourthBoneIndex), vertexPosition); 30 | vec3 positionTransformedByFifthBone = transformVectorByBone(int(fifthBoneIndex), vertexPosition); 31 | 32 | vec3 positionTransformedByBones = 33 | (positionTransformedByFirstBone * firstWeight) + 34 | (positionTransformedBySecondBone * secondWeight) + 35 | (positionTransformedByThirdBone * thirdWeight) + 36 | (positionTransformedByFourthBone * fourthWeight) + 37 | (positionTransformedByFifthBone * fifthWeight); 38 | 39 | vec4 tempWorldSpacePosition = worldMatrix * vec4(positionTransformedByBones, 1.0); 40 | 41 | // Output values. 42 | worldVertexPosition = tempWorldSpacePosition.xyz; 43 | 44 | gl_Position = viewProjMatrix * tempWorldSpacePosition; 45 | } 46 | 47 | vec3 transformVectorByBone(int boneIndex, vec3 v) 48 | { 49 | if (boneIndex != -1) 50 | { 51 | mat4 boneMatrix = getBoneMatrix(boneIndex); 52 | 53 | vec4 vectorTransformedByBone = boneMatrix * vec4(v, 1.0); 54 | 55 | return vectorTransformedByBone.xyz; 56 | } 57 | else 58 | { 59 | return vec3(0, 0, 0); 60 | } 61 | } 62 | 63 | vec3 transformVectorByBoneRotationOnly(int boneIndex, vec3 v) 64 | { 65 | if (boneIndex != -1) 66 | { 67 | mat4 boneMatrix = getBoneMatrix(boneIndex); 68 | 69 | vec3 vectorTransformedByBone = mat3(boneMatrix) * v; 70 | 71 | return vectorTransformedByBone; 72 | } 73 | else 74 | { 75 | return vec3(0, 0, 0); 76 | } 77 | } 78 | 79 | mat4 getBoneMatrix(int boneIndex) 80 | { 81 | for (int i = 0; i < 30; i++) 82 | { 83 | if (i == boneIndex) 84 | { 85 | return boneMatrices[i]; 86 | } 87 | } 88 | 89 | return boneMatrices[0]; // This just shuts the compiler up. This should never happen. 90 | } -------------------------------------------------------------------------------- /resources/shaders/skinned-mesh-shadow-map-build-front-pass-fs.txt: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | uniform vec3 lightWorldPosition; 4 | uniform vec4 shadowMapMask; 5 | uniform float shadowMapSize; 6 | 7 | uniform sampler2D backPassSampler; 8 | 9 | varying vec3 worldVertexPosition; 10 | 11 | void main(void) 12 | { 13 | vec3 lightToVertex = worldVertexPosition - lightWorldPosition; 14 | 15 | float frontPassDistanceSquared = dot(lightToVertex, lightToVertex); 16 | 17 | vec2 texCoord = gl_FragCoord.xy / vec2(shadowMapSize, shadowMapSize); 18 | 19 | vec4 backPassSample = texture2D(backPassSampler, texCoord); 20 | 21 | float backPassDistanceSquared = dot(backPassSample, shadowMapMask); 22 | 23 | if (backPassDistanceSquared >= 1000.0) { 24 | 25 | gl_FragColor = shadowMapMask * (frontPassDistanceSquared + 10.0); 26 | 27 | } else { 28 | 29 | float middleDistanceSquared = frontPassDistanceSquared + (backPassDistanceSquared - frontPassDistanceSquared) * 0.6; 30 | 31 | gl_FragColor = shadowMapMask * middleDistanceSquared; 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /resources/shaders/skinned-mesh-shadow-map-build-front-pass-vs.txt: -------------------------------------------------------------------------------- 1 | attribute vec3 vertexPosition; 2 | attribute float firstBoneIndex; 3 | attribute float secondBoneIndex; 4 | attribute float thirdBoneIndex; 5 | attribute float fourthBoneIndex; 6 | attribute float fifthBoneIndex; 7 | attribute float firstWeight; 8 | attribute float secondWeight; 9 | attribute float thirdWeight; 10 | attribute float fourthWeight; 11 | attribute float fifthWeight; 12 | 13 | uniform mat4 worldMatrix; 14 | uniform mat4 viewProjMatrix; 15 | uniform mat4 boneMatrices[30]; 16 | 17 | varying vec3 worldVertexPosition; 18 | 19 | vec3 transformVectorByBone(int boneIndex, vec3 v); 20 | vec3 transformVectorByBoneRotationOnly(int boneIndex, vec3 v); 21 | mat4 getBoneMatrix(int boneIndex); 22 | 23 | void main(void) 24 | { 25 | // Calculate the skinned position. 26 | vec3 positionTransformedByFirstBone = transformVectorByBone(int(firstBoneIndex), vertexPosition); 27 | vec3 positionTransformedBySecondBone = transformVectorByBone(int(secondBoneIndex), vertexPosition); 28 | vec3 positionTransformedByThirdBone = transformVectorByBone(int(thirdBoneIndex), vertexPosition); 29 | vec3 positionTransformedByFourthBone = transformVectorByBone(int(fourthBoneIndex), vertexPosition); 30 | vec3 positionTransformedByFifthBone = transformVectorByBone(int(fifthBoneIndex), vertexPosition); 31 | 32 | vec3 positionTransformedByBones = 33 | (positionTransformedByFirstBone * firstWeight) + 34 | (positionTransformedBySecondBone * secondWeight) + 35 | (positionTransformedByThirdBone * thirdWeight) + 36 | (positionTransformedByFourthBone * fourthWeight) + 37 | (positionTransformedByFifthBone * fifthWeight); 38 | 39 | vec4 tempWorldSpacePosition = worldMatrix * vec4(positionTransformedByBones, 1.0); 40 | 41 | // Output values. 42 | worldVertexPosition = tempWorldSpacePosition.xyz; 43 | 44 | gl_Position = viewProjMatrix * tempWorldSpacePosition; 45 | } 46 | 47 | vec3 transformVectorByBone(int boneIndex, vec3 v) 48 | { 49 | if (boneIndex != -1) 50 | { 51 | mat4 boneMatrix = getBoneMatrix(boneIndex); 52 | 53 | vec4 vectorTransformedByBone = boneMatrix * vec4(v, 1.0); 54 | 55 | return vectorTransformedByBone.xyz; 56 | } 57 | else 58 | { 59 | return vec3(0, 0, 0); 60 | } 61 | } 62 | 63 | vec3 transformVectorByBoneRotationOnly(int boneIndex, vec3 v) 64 | { 65 | if (boneIndex != -1) 66 | { 67 | mat4 boneMatrix = getBoneMatrix(boneIndex); 68 | 69 | vec3 vectorTransformedByBone = mat3(boneMatrix) * v; 70 | 71 | return vectorTransformedByBone; 72 | } 73 | else 74 | { 75 | return vec3(0, 0, 0); 76 | } 77 | } 78 | 79 | mat4 getBoneMatrix(int boneIndex) 80 | { 81 | for (int i = 0; i < 30; i++) 82 | { 83 | if (i == boneIndex) 84 | { 85 | return boneMatrices[i]; 86 | } 87 | } 88 | 89 | return boneMatrices[0]; // This just shuts the compiler up. This should never happen. 90 | } -------------------------------------------------------------------------------- /resources/shaders/static-mesh-main-render-vs.txt: -------------------------------------------------------------------------------- 1 | attribute vec3 vertexPosition; 2 | attribute vec3 vertexNormal; 3 | attribute vec3 vertexTangent; 4 | attribute vec3 vertexBitangent; 5 | attribute vec2 vertexTexCoord; 6 | 7 | uniform mat4 worldMatrix; 8 | uniform mat4 viewProjMatrix; 9 | 10 | varying vec3 fragmentWorldSpacePosition; 11 | varying vec3 fragmentNormal; 12 | varying vec3 fragmentTangent; 13 | varying vec3 fragmentBitangent; 14 | varying vec2 texCoord; 15 | 16 | void main(void) 17 | { 18 | vec4 tempWorldSpacePosition = worldMatrix * vec4(vertexPosition, 1.0); 19 | 20 | fragmentWorldSpacePosition = tempWorldSpacePosition.xyz; 21 | 22 | mat3 rotationMatrix = mat3(worldMatrix); 23 | fragmentNormal = rotationMatrix * vertexNormal; 24 | fragmentTangent = rotationMatrix * vertexTangent; 25 | fragmentBitangent = rotationMatrix * vertexBitangent; 26 | 27 | texCoord = vertexTexCoord; 28 | 29 | gl_Position = viewProjMatrix * tempWorldSpacePosition; 30 | } -------------------------------------------------------------------------------- /resources/shaders/static-mesh-shadow-map-build-back-pass-fs.txt: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | uniform vec3 lightWorldPosition; 4 | uniform vec4 shadowMapMask; 5 | 6 | varying vec3 worldVertexPosition; 7 | 8 | void main(void) 9 | { 10 | vec3 lightToVertex = worldVertexPosition - lightWorldPosition; 11 | 12 | float distanceSquared = dot(lightToVertex, lightToVertex); 13 | 14 | gl_FragColor = shadowMapMask * distanceSquared; 15 | //gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); 16 | } -------------------------------------------------------------------------------- /resources/shaders/static-mesh-shadow-map-build-back-pass-vs.txt: -------------------------------------------------------------------------------- 1 | attribute vec3 vertexPosition; 2 | 3 | uniform mat4 viewProjMatrix; 4 | 5 | uniform mat4 worldMatrix; 6 | 7 | varying vec3 worldVertexPosition; 8 | 9 | void main(void) 10 | { 11 | vec4 tempWorldSpacePosition = worldMatrix * vec4(vertexPosition, 1.0); 12 | 13 | worldVertexPosition = tempWorldSpacePosition.xyz; 14 | 15 | gl_Position = viewProjMatrix * tempWorldSpacePosition; 16 | } -------------------------------------------------------------------------------- /resources/shaders/static-mesh-shadow-map-build-front-pass-fs.txt: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | uniform vec3 lightWorldPosition; 4 | uniform vec4 shadowMapMask; 5 | uniform float shadowMapSize; 6 | 7 | uniform sampler2D backPassSampler; 8 | 9 | varying vec3 worldVertexPosition; 10 | 11 | void main(void) 12 | { 13 | vec3 lightToVertex = worldVertexPosition - lightWorldPosition; 14 | 15 | float frontPassDistanceSquared = dot(lightToVertex, lightToVertex); 16 | 17 | vec2 texCoord = gl_FragCoord.xy / vec2(shadowMapSize, shadowMapSize); 18 | 19 | vec4 backPassSample = texture2D(backPassSampler, texCoord); 20 | 21 | float backPassDistanceSquared = dot(backPassSample, shadowMapMask); 22 | 23 | if (backPassDistanceSquared >= 1000.0) { 24 | 25 | gl_FragColor = shadowMapMask * (frontPassDistanceSquared + 10.0); 26 | 27 | } else { 28 | 29 | float middleDistanceSquared = frontPassDistanceSquared + (backPassDistanceSquared - frontPassDistanceSquared) * 0.6; 30 | 31 | gl_FragColor = shadowMapMask * middleDistanceSquared; 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /resources/shaders/static-mesh-shadow-map-build-front-pass-vs.txt: -------------------------------------------------------------------------------- 1 | attribute vec3 vertexPosition; 2 | 3 | uniform mat4 viewProjMatrix; 4 | 5 | uniform mat4 worldMatrix; 6 | 7 | varying vec3 worldVertexPosition; 8 | 9 | void main(void) 10 | { 11 | vec4 tempWorldSpacePosition = worldMatrix * vec4(vertexPosition, 1.0); 12 | 13 | worldVertexPosition = tempWorldSpacePosition.xyz; 14 | 15 | gl_Position = viewProjMatrix * tempWorldSpacePosition; 16 | } -------------------------------------------------------------------------------- /resources/skinned-mesh-animations/column-bend.json: -------------------------------------------------------------------------------- 1 | {"frames": [{"trans":[{ "quat": [0.0000,0.0000,-0.0000,1.0000] },{ "quat": [0.0000,0.0000,-0.0000,1.0000] },{ "quat": [0.0000,0.0000,0.0000,1.0000] },{ "quat": [0.0000,0.0000,-0.0000,1.0000] }]},{"trans":[{ "quat": [0.0000,0.7071,-0.0000,0.7071] },{ "quat": [0.0000,0.0000,-0.0000,1.0000] },{ "quat": [0.0000,0.0000,0.0000,1.0000] },{ "quat": [0.0000,0.0000,-0.0000,1.0000] }]}]} -------------------------------------------------------------------------------- /resources/skinned-mesh-animations/robot-walk.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "frames": [ 4 | { 5 | "trans": [ 6 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 7 | { "quat": [ 0.1300, -0.0114, 0.0864, 0.9877 ] }, 8 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 9 | { "quat": [ -0.2667, -0.1389, -0.4404, 0.8460 ] }, 10 | { "quat": [ -0.0714, -0.4001, 0.0225, 0.9134 ] }, 11 | { "quat": [ 0.0000, -0.0000, -0.0000, 1.0000 ] }, 12 | { "quat": [ 0.3034, -0.1579, 0.4339, 0.8335 ] }, 13 | { "quat": [ 0.0714, 0.1962, 0.0225, 0.9777 ] }, 14 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 15 | { "quat": [ -0.1361, -0.0267, -0.1206, 0.9830 ] }, 16 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 17 | { "quat": [ -0.0000, 0.0000, -0.0872, 0.9962 ] }, 18 | { "quat": [ 0.3007, -0.0000, -0.0000, 0.9537 ] }, 19 | { "quat": [ 0.3007, -0.0000, 0.0000, 0.9537 ] }, 20 | { "quat": [ -0.0000, 0.0000, -0.0436, 0.9990 ] }, 21 | { "quat": [ -0.3420, 0.0000, -0.0000, 0.9397 ] }, 22 | { "quat": [ 0.4617, 0.0000, -0.0000, 0.8870 ] } 23 | ] 24 | }, 25 | { 26 | "trans": [ 27 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 28 | { "quat": [ 0.0436, -0.0019, 0.0436, 0.9981 ] }, 29 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 30 | { "quat": [ -0.1540, -0.0802, -0.4547, 0.8735 ] }, 31 | { "quat": [ 0.0470, -0.2736, -0.0171, 0.9605 ] }, 32 | { "quat": [ 0.0000, -0.0000, -0.0000, 1.0000 ] }, 33 | { "quat": [ 0.1540, -0.0802, 0.4547, 0.8735 ] }, 34 | { "quat": [ 0.0936, 0.2032, 0.0341, 0.9741 ] }, 35 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 36 | { "quat": [ -0.0439, -0.0019, -0.0432, 0.9981 ] }, 37 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 38 | { "quat": [ -0.0000, 0.0000, -0.0436, 0.9990 ] }, 39 | { "quat": [ 0.1305, 0.0000, -0.0000, 0.9914 ] }, 40 | { "quat": [ 0.1736, -0.0000, 0.0000, 0.9848 ] }, 41 | { "quat": [ 0.0000, 0.0000, 0.0000, 1.0000 ] }, 42 | { "quat": [ -0.1736, 0.0000, -0.0000, 0.9848 ] }, 43 | { "quat": [ 0.3007, -0.0000, -0.0000, 0.9537 ] } 44 | ] 45 | }, 46 | { 47 | "trans": [ 48 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 49 | { "quat": [ 0.0000, 0.0000, -0.0436, 0.9990 ] }, 50 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 51 | { "quat": [ 0.0000, 0.0000, -0.4617, 0.8870 ] }, 52 | { "quat": [ 0.1563, -0.1798, -0.0729, 0.9685 ] }, 53 | { "quat": [ 0.0000, -0.0000, -0.0000, 1.0000 ] }, 54 | { "quat": [ 0.0000, -0.0000, 0.4617, 0.8870 ] }, 55 | { "quat": [ 0.1593, 0.1374, 0.0660, 0.9754 ] }, 56 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 57 | { "quat": [ -0.0436, -0.0019, 0.0436, 0.9981 ] }, 58 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 59 | { "quat": [ -0.0000, -0.0000, 0.0000, 1.0000 ] }, 60 | { "quat": [ -0.1736, 0.0000, 0.0000, 0.9848 ] }, 61 | { "quat": [ 0.1736, -0.0000, 0.0000, 0.9848 ] }, 62 | { "quat": [ -0.0000, -0.0000, 0.0436, 0.9990 ] }, 63 | { "quat": [ 0.1305, -0.0000, 0.0000, 0.9914 ] }, 64 | { "quat": [ 0.3827, 0.0000, -0.0000, 0.9239 ] } 65 | ] 66 | }, 67 | { 68 | "trans": [ 69 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 70 | { "quat": [ 0.0435, 0.0038, -0.0871, 0.9952 ] }, 71 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 72 | { "quat": [ 0.3749, 0.1951, -0.4185, 0.8039 ] }, 73 | { "quat": [ 0.0714, -0.1962, -0.0225, 0.9777 ] }, 74 | { "quat": [ 0.0000, -0.0000, -0.0000, 1.0000 ] }, 75 | { "quat": [ -0.3749, 0.1951, 0.4185, 0.8039 ] }, 76 | { "quat": [ -0.0936, 0.4705, -0.0341, 0.8768 ] }, 77 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 78 | { "quat": [ -0.0455, 0.0093, 0.1294, 0.9905 ] }, 79 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 80 | { "quat": [ 0.0000, -0.0000, 0.0436, 0.9990 ] }, 81 | { "quat": [ -0.3827, -0.0000, 0.0000, 0.9239 ] }, 82 | { "quat": [ 0.4226, -0.0000, 0.0000, 0.9063 ] }, 83 | { "quat": [ -0.0000, -0.0000, 0.0872, 0.9962 ] }, 84 | { "quat": [ 0.3007, -0.0000, -0.0000, 0.9537 ] }, 85 | { "quat": [ 0.5373, 0.0000, -0.0000, 0.8434 ] } 86 | ] 87 | }, 88 | { 89 | "trans": [ 90 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 91 | { "quat": [ 0.0436, 0.0000, 0.0000, 0.9990 ] }, 92 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 93 | { "quat": [ 0.2296, 0.1195, -0.4460, 0.8568 ] }, 94 | { "quat": [ 0.0679, -0.3221, -0.0316, 0.9437 ] }, 95 | { "quat": [ 0.0000, -0.0000, -0.0000, 1.0000 ] }, 96 | { "quat": [ -0.0387, 0.0201, 0.4613, 0.8862 ] }, 97 | { "quat": [ 0.1347, 0.2140, 0.0624, 0.9655 ] }, 98 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 99 | { "quat": [ 0.0000, 0.0038, 0.0435, 0.9990 ] }, 100 | { "quat": [ 0.0000, 0.0000, -0.0000, 1.0000 ] }, 101 | { "quat": [ 0.0000, -0.0000, 0.0436, 0.9990 ] }, 102 | { "quat": [ -0.1736, 0.0000, 0.0000, 0.9848 ] }, 103 | { "quat": [ 0.3420, -0.0000, 0.0000, 0.9397 ] }, 104 | { "quat": [ 0.0000, 0.0000, 0.0000, 1.0000 ] }, 105 | { "quat": [ -0.0000, -0.0000, 0.0000, 1.0000 ] }, 106 | { "quat": [ 0.3007, -0.0000, 0.0000, 0.9537 ] } 107 | ] 108 | } 109 | ] 110 | } -------------------------------------------------------------------------------- /resources/skinned-meshes/column.json: -------------------------------------------------------------------------------- 1 | {"verts":[-0.2500000476837158,-2.9802322387695314e-09,0.25000002384185793,0.2499999761581421,0.5,0.25000002384185793,-0.25000002384185793,0.5,0.25000002384185793,-0.2501410722732544,0.5000000476837159,-0.25,-0.25,-2.9802322387695314e-09,0.25000002384185793,-0.24985892772674562,0.5000000476837159,0.25000002384185793,0.25000002384185793,0.5000000476837159,0.25000002384185793,0.25,-2.9802322387695314e-09,-0.25,0.25,0.5,-0.25,-0.2500000476837158,0.5,0.25000002384185793,0.25,0.5,-0.25,-0.25000002384185793,0.5,-0.25,-0.2500000476837158,-2.9802322387695314e-09,0.25000002384185793,0.25000002384185793,-6.617444900424222e-24,0.25000002384185793,0.2499999761581421,0.5,0.25000002384185793,-0.2501410722732544,0.5000000476837159,-0.25,-0.24971785545349123,-2.9802322387695314e-09,-0.25,-0.25,-2.9802322387695314e-09,0.25000002384185793,0.25000002384185793,0.5000000476837159,0.25000002384185793,0.2499999523162842,-2.9802322387695314e-09,0.25000002384185793,0.25,-2.9802322387695314e-09,-0.25,-0.2500000476837158,0.5,0.25000002384185793,0.25,0.5,0.25000002384185793,0.25,0.5,-0.25],"normals":[0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,-0.9999999403953552,-0.00028214440681040287,1.2969653129246694e-10,-0.9999998807907104,-0.0002821445814333856,-5.558421431461902e-11,-0.9999998211860657,0.0002821444650180638,0.0005642889882437885,1.0,-7.152555525635762e-08,2.3841858265427618e-08,1.0,-7.152557657263969e-08,2.3841868923568654e-08,1.0,0.0,-4.768370942542788e-08,0.0,1.0,-0.0,0.0,1.0,-0.0,0.0,1.0,-0.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,-0.9999999403953552,-0.00028214440681040287,1.2969653129246694e-10,-0.9999995231628418,-0.0008464331040158868,-0.0005642887554131448,-0.9999998807907104,-0.0002821445814333856,-5.558421431461902e-11,1.0,-7.152555525635762e-08,2.3841858265427618e-08,1.0,-1.4305113893442467e-07,9.536744016713783e-08,1.0,-7.152557657263969e-08,2.3841868923568654e-08,0.0,1.0,-0.0,0.0,1.0,-0.0,0.0,1.0,-0.0],"uvs":[0.0002,0.0001,0.7692,0.7696,0.0001,0.7696,0.0001,0.9999,0.9999,0.0001,0.9999,0.9999,0.0001,0.9999,0.9999,0.0001,0.9999,0.9999,0.0001,0.0001,0.9999,0.9999,0.0001,0.9999,0.0002,0.0001,0.7692,0.0024,0.7692,0.7696,0.0001,0.9999,0.0001,0.0001,0.9999,0.0001,0.0001,0.9999,0.0001,0.0001,0.9999,0.0001,0.0001,0.0001,0.9999,0.0001,0.9999,0.9999],"firstBoneIndexes":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"secondBoneIndexes":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],"thirdBoneIndexes":[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],"fourthBoneIndexes":[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],"firstWeights":[0.8893013000488281,0.625490128993988,0.7573957443237305,0.5845423936843872,0.8309930562973022,0.5844825506210327,0.5844932794570923,0.8310133814811707,0.5844932794570923,0.4999999701976776,0.5,0.5,0.8893013000488281,0.8597182035446167,0.625490128993988,0.5845423936843872,0.8310688734054565,0.8309930562973022,0.5844932794570923,0.8310133814811707,0.8310133814811707,0.4999999701976776,0.4999999701976776,0.5],"secondWeights":[0.1106986328959465,0.3745098412036896,0.24260424077510834,0.4154576361179352,0.16900698840618134,0.4155174791812897,0.4155066907405853,0.16898660361766815,0.4155066907405853,0.4999999701976776,0.5,0.5,0.1106986328959465,0.14028175175189972,0.3745098412036896,0.4154576361179352,0.16893114149570465,0.16900698840618134,0.4155066907405853,0.16898658871650696,0.16898660361766815,0.4999999701976776,0.4999999701976776,0.5],"thirdWeights":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"fourthWeights":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"bones": [{ "name": "Bone" , "position": [0.0,0.0,-0.0], "parentBoneIndex": -1},{ "name": "Bone.001" , "position": [0.0,0.2,-0.0], "parentBoneIndex": 0},{ "name": "Bone.002" , "position": [0.0,0.4,4.4862107984045e-08], "parentBoneIndex": 1},{ "name": "Bone.003" , "position": [0.0,0.6000000000000001,1.422419018126675e-07], "parentBoneIndex": 2}]} -------------------------------------------------------------------------------- /resources/sprite-sheets/sprite-sheet-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "textureId": "sprite-sheet-1", 3 | "spriteSpecsById": { 4 | "health-bar-overlay": { 5 | "id": "health-bar-overlay", 6 | "position": [0, 0], 7 | "size": [290, 95] 8 | }, 9 | "health-bar-inner": { 10 | "id": "health-bar-inner", 11 | "position": [ 0, 98 ], 12 | "size": [ 184, 40 ] 13 | }, 14 | "health-bar-icon": { 15 | "id": "health-bar-icon", 16 | "position": [ 190, 96 ], 17 | "size": [ 42, 42 ] 18 | }, 19 | "crosshair": { 20 | "id": "crosshair", 21 | "position": [ 298, 11 ], 22 | "size": [ 68, 72 ] 23 | }, 24 | "player-death-message": { 25 | "id": "player-death-message", 26 | "position": [ 3, 140 ], 27 | "size": [ 324, 69 ] 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /resources/system/effects.json: -------------------------------------------------------------------------------- 1 | { 2 | "static-mesh-main-render": { 3 | "id": "static-mesh-main-render", 4 | 5 | "attributes": { 6 | "vertexPosition": null, 7 | "vertexNormal": null, 8 | "vertexTangent": null, 9 | "vertexBitangent": null, 10 | "vertexTexCoord": null 11 | }, 12 | 13 | "uniforms": { 14 | "worldMatrix": null, 15 | "viewProjMatrix": null, 16 | 17 | "diffuseSampler": null, 18 | "normalSampler": null, 19 | "selfIlluminationSampler": null, 20 | "pointLightShadowMapSamplers": null, 21 | 22 | "lightEnableds": null, 23 | "lightWorldSpacePositions": null, 24 | "lightRadiusSqrs": null, 25 | "lightColours": null, 26 | "lightCastsShadows": null, 27 | "lightStaticObjectShadowMapMasks": null, 28 | "lightDynamicObjectShadowMapMasks": null, 29 | "cameraWorldSpacePosition": null, 30 | 31 | "globalIlluminationNormals": null, 32 | "globalIlluminationColours": null, 33 | 34 | "hasSelfIllumination": null 35 | } 36 | }, 37 | 38 | "skinned-mesh-main-render": { 39 | "id": "skinned-mesh-main-render", 40 | 41 | "attributes": { 42 | "vertexPosition": null, 43 | "vertexNormal": null, 44 | "vertexTexCoord": null, 45 | "firstBoneIndex": null, 46 | "secondBoneIndex": null, 47 | "thirdBoneIndex": null, 48 | "fourthBoneIndex": null, 49 | "fifthBoneIndex": null, 50 | "firstWeight": null, 51 | "secondWeight": null, 52 | "thirdWeight": null, 53 | "fourthWeight": null, 54 | "fifthWeight": null 55 | }, 56 | 57 | "uniforms": { 58 | "worldMatrix": null, 59 | "viewProjMatrix": null, 60 | "boneMatrices": null, 61 | 62 | "diffuseSampler": null, 63 | "normalSampler": null, 64 | "selfIlluminationSampler": null, 65 | "pointLightShadowMapSamplers": null, 66 | 67 | "lightEnableds": null, 68 | "lightWorldSpacePositions": null, 69 | "lightRadiusSqrs": null, 70 | "lightColours": null, 71 | "lightCastsShadows": null, 72 | "lightStaticObjectShadowMapMasks": null, 73 | "lightDynamicObjectShadowMapMasks": null, 74 | "cameraWorldSpacePosition": null, 75 | 76 | "globalIlluminationNormals": null, 77 | "globalIlluminationColours": null, 78 | 79 | "hasSelfIllumination": null 80 | } 81 | }, 82 | 83 | "static-mesh-shadow-map-build-back-pass": { 84 | "id": "static-mesh-shadow-map-build-back-pass", 85 | 86 | "attributes": { 87 | "vertexPosition": null 88 | }, 89 | 90 | "uniforms": { 91 | "worldMatrix": null, 92 | "viewProjMatrix": null, 93 | "lightWorldPosition": null, 94 | "shadowMapMask": null 95 | } 96 | }, 97 | 98 | "static-mesh-shadow-map-build-front-pass": { 99 | "id": "static-mesh-shadow-map-build-front-pass", 100 | 101 | "attributes": { 102 | "vertexPosition": null 103 | }, 104 | 105 | "uniforms": { 106 | "worldMatrix": null, 107 | "viewProjMatrix": null, 108 | "lightWorldPosition": null, 109 | "shadowMapMask": null, 110 | "shadowMapSize": null 111 | } 112 | }, 113 | 114 | "skinned-mesh-shadow-map-build-back-pass": { 115 | "id": "skinned-mesh-shadow-map-build-back-pass", 116 | 117 | "attributes": { 118 | "vertexPosition": null, 119 | "firstBoneIndex": null, 120 | "secondBoneIndex": null, 121 | "thirdBoneIndex": null, 122 | "fourthBoneIndex": null, 123 | "fifthBoneIndex": null, 124 | "firstWeight": null, 125 | "secondWeight": null, 126 | "thirdWeight": null, 127 | "fourthWeight": null, 128 | "fifthWeight": null 129 | }, 130 | 131 | "uniforms": { 132 | "worldMatrix": null, 133 | "viewProjMatrix": null, 134 | "boneMatrices": null, 135 | "lightWorldPosition": null, 136 | "shadowMapMask": null 137 | } 138 | }, 139 | 140 | "skinned-mesh-shadow-map-build-front-pass": { 141 | "id": "skinned-mesh-shadow-map-build-front-pass", 142 | 143 | "attributes": { 144 | "vertexPosition": null, 145 | "firstBoneIndex": null, 146 | "secondBoneIndex": null, 147 | "thirdBoneIndex": null, 148 | "fourthBoneIndex": null, 149 | "fifthBoneIndex": null, 150 | "firstWeight": null, 151 | "secondWeight": null, 152 | "thirdWeight": null, 153 | "fourthWeight": null, 154 | "fifthWeight": null 155 | }, 156 | 157 | "uniforms": { 158 | "worldMatrix": null, 159 | "viewProjMatrix": null, 160 | "boneMatrices": null, 161 | "lightWorldPosition": null, 162 | "shadowMapMask": null, 163 | "shadowMapSize": null 164 | } 165 | }, 166 | 167 | "particle": { 168 | "id": "particle", 169 | 170 | "attributes": { 171 | "vertexOffset": null, 172 | "vertexTexCoord": null 173 | }, 174 | 175 | "uniforms": { 176 | "viewProjMatrix": null, 177 | "position": null, 178 | "size": null, 179 | "cameraXAxis": null, 180 | "cameraYAxis": null, 181 | "texture1Sampler": null, 182 | "texture2Sampler": null 183 | } 184 | }, 185 | 186 | "gui": { 187 | "id": "gui", 188 | 189 | "attributes": { 190 | "sizeMultiplier": null 191 | }, 192 | 193 | "uniforms": { 194 | "position": null, 195 | "size": null, 196 | "scale": null, 197 | "xAxis": null, 198 | "yAxis": null, 199 | "uvPosition": null, 200 | "uvSize": null, 201 | "textureSampler": null 202 | } 203 | }, 204 | 205 | "line": { 206 | "id": "line", 207 | 208 | "attributes": { 209 | "vertexPosition": null 210 | }, 211 | 212 | "uniforms": { 213 | "viewProjMatrix": null, 214 | "position": null, 215 | "size": null, 216 | "colour": null 217 | } 218 | } 219 | } -------------------------------------------------------------------------------- /resources/textures/brick-wall-1-d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/brick-wall-1-d.png -------------------------------------------------------------------------------- /resources/textures/brick-wall-1-n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/brick-wall-1-n.png -------------------------------------------------------------------------------- /resources/textures/light-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/light-1.png -------------------------------------------------------------------------------- /resources/textures/particle-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/particle-1.png -------------------------------------------------------------------------------- /resources/textures/particle-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/particle-2.png -------------------------------------------------------------------------------- /resources/textures/pipe-1-d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/pipe-1-d.png -------------------------------------------------------------------------------- /resources/textures/pipe-1-n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/pipe-1-n.png -------------------------------------------------------------------------------- /resources/textures/sprite-sheet-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/sprite-sheet-1.png -------------------------------------------------------------------------------- /resources/textures/system/dummy-cube-nx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/system/dummy-cube-nx.png -------------------------------------------------------------------------------- /resources/textures/system/dummy-cube-ny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/system/dummy-cube-ny.png -------------------------------------------------------------------------------- /resources/textures/system/dummy-cube-nz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/system/dummy-cube-nz.png -------------------------------------------------------------------------------- /resources/textures/system/dummy-cube-px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/system/dummy-cube-px.png -------------------------------------------------------------------------------- /resources/textures/system/dummy-cube-py.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/system/dummy-cube-py.png -------------------------------------------------------------------------------- /resources/textures/system/dummy-cube-pz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/system/dummy-cube-pz.png -------------------------------------------------------------------------------- /resources/textures/system/missing-diffuse-texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/system/missing-diffuse-texture.png -------------------------------------------------------------------------------- /resources/textures/system/missing-normal-texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/system/missing-normal-texture.png -------------------------------------------------------------------------------- /resources/textures/tiled-floor-1-d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/tiled-floor-1-d.png -------------------------------------------------------------------------------- /resources/textures/tiled-floor-1-n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thunderholt/webgl-fps/0e1a40c4e3c424044cf254767ad215ef98f82fec/resources/textures/tiled-floor-1-n.png --------------------------------------------------------------------------------