pixospritz-core 0.10.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/README.md +36 -286
  2. package/dist/bundle.js +13 -3
  3. package/dist/bundle.js.map +1 -1
  4. package/dist/style.css +1 -0
  5. package/package.json +43 -44
  6. package/src/components/WebGLView.jsx +318 -0
  7. package/src/css/pixos.css +372 -0
  8. package/src/engine/actions/animate.js +41 -0
  9. package/src/engine/actions/changezone.js +135 -0
  10. package/src/engine/actions/chat.js +109 -0
  11. package/src/engine/actions/dialogue.js +90 -0
  12. package/src/engine/actions/face.js +22 -0
  13. package/src/engine/actions/greeting.js +28 -0
  14. package/src/engine/actions/interact.js +86 -0
  15. package/src/engine/actions/move.js +67 -0
  16. package/src/engine/actions/patrol.js +109 -0
  17. package/src/engine/actions/prompt.js +185 -0
  18. package/src/engine/actions/script.js +42 -0
  19. package/src/engine/core/audio/AudioSystem.js +543 -0
  20. package/src/engine/core/cutscene/PxcPlayer.js +956 -0
  21. package/src/engine/core/cutscene/manager.js +243 -0
  22. package/src/engine/core/database/index.js +75 -0
  23. package/src/engine/core/debug/index.js +371 -0
  24. package/src/engine/core/hud/index.js +765 -0
  25. package/src/engine/core/index.js +540 -0
  26. package/src/engine/core/input/gamepad/Controller.js +71 -0
  27. package/src/engine/core/input/gamepad/ControllerButtons.js +231 -0
  28. package/src/engine/core/input/gamepad/ControllerStick.js +173 -0
  29. package/src/engine/core/input/gamepad/index.js +592 -0
  30. package/src/engine/core/input/keyboard.js +196 -0
  31. package/src/engine/core/input/manager.js +485 -0
  32. package/src/engine/core/input/mouse.js +203 -0
  33. package/src/engine/core/input/touch.js +175 -0
  34. package/src/engine/core/mode/manager.js +199 -0
  35. package/src/engine/core/net/manager.js +535 -0
  36. package/src/engine/core/queue/action.js +83 -0
  37. package/src/engine/core/queue/event.js +82 -0
  38. package/src/engine/core/queue/index.js +44 -0
  39. package/src/engine/core/queue/loadable.js +33 -0
  40. package/src/engine/core/render/CameraEffects.js +494 -0
  41. package/src/engine/core/render/FrustumCuller.js +417 -0
  42. package/src/engine/core/render/LODManager.js +285 -0
  43. package/src/engine/core/render/ParticleManager.js +529 -0
  44. package/src/engine/core/render/TextureAtlas.js +465 -0
  45. package/src/engine/core/render/camera.js +338 -0
  46. package/src/engine/core/render/light.js +197 -0
  47. package/src/engine/core/render/manager.js +1079 -0
  48. package/src/engine/core/render/shaders.js +110 -0
  49. package/src/engine/core/render/skybox.js +342 -0
  50. package/src/engine/core/resource/manager.js +133 -0
  51. package/src/engine/core/resource/object.js +611 -0
  52. package/src/engine/core/resource/texture.js +103 -0
  53. package/src/engine/core/resource/tileset.js +177 -0
  54. package/src/engine/core/scene/avatar.js +215 -0
  55. package/src/engine/core/scene/speech.js +138 -0
  56. package/src/engine/core/scene/sprite.js +702 -0
  57. package/src/engine/core/scene/spritz.js +189 -0
  58. package/src/engine/core/scene/world.js +681 -0
  59. package/src/engine/core/scene/zone.js +1167 -0
  60. package/src/engine/core/store/index.js +110 -0
  61. package/src/engine/dynamic/animatedSprite.js +64 -0
  62. package/src/engine/dynamic/animatedTile.js +98 -0
  63. package/src/engine/dynamic/avatar.js +110 -0
  64. package/src/engine/dynamic/map.js +174 -0
  65. package/src/engine/dynamic/sprite.js +255 -0
  66. package/src/engine/dynamic/spritz.js +119 -0
  67. package/src/engine/events/EventSystem.js +609 -0
  68. package/src/engine/events/camera.js +142 -0
  69. package/src/engine/events/chat.js +75 -0
  70. package/src/engine/events/menu.js +186 -0
  71. package/src/engine/scripting/CallbackManager.js +514 -0
  72. package/src/engine/scripting/PixoScriptInterpreter.js +81 -0
  73. package/src/engine/scripting/PixoScriptLibrary.js +704 -0
  74. package/src/engine/shaders/effects/index.js +450 -0
  75. package/src/engine/shaders/fs.js +222 -0
  76. package/src/engine/shaders/particles/fs.js +41 -0
  77. package/src/engine/shaders/particles/vs.js +61 -0
  78. package/src/engine/shaders/picker/fs.js +34 -0
  79. package/src/engine/shaders/picker/init.js +62 -0
  80. package/src/engine/shaders/picker/vs.js +42 -0
  81. package/src/engine/shaders/pxsl/README.md +250 -0
  82. package/src/engine/shaders/pxsl/index.js +25 -0
  83. package/src/engine/shaders/pxsl/library.js +608 -0
  84. package/src/engine/shaders/pxsl/manager.js +338 -0
  85. package/src/engine/shaders/pxsl/specification.js +363 -0
  86. package/src/engine/shaders/pxsl/transpiler.js +753 -0
  87. package/src/engine/shaders/skybox/cosmic/fs.js +147 -0
  88. package/src/engine/shaders/skybox/cosmic/vs.js +23 -0
  89. package/src/engine/shaders/skybox/matrix/fs.js +127 -0
  90. package/src/engine/shaders/skybox/matrix/vs.js +23 -0
  91. package/src/engine/shaders/skybox/morning/fs.js +109 -0
  92. package/src/engine/shaders/skybox/morning/vs.js +23 -0
  93. package/src/engine/shaders/skybox/neon/fs.js +119 -0
  94. package/src/engine/shaders/skybox/neon/vs.js +23 -0
  95. package/src/engine/shaders/skybox/sky/fs.js +114 -0
  96. package/src/engine/shaders/skybox/sky/vs.js +23 -0
  97. package/src/engine/shaders/skybox/sunset/fs.js +101 -0
  98. package/src/engine/shaders/skybox/sunset/vs.js +23 -0
  99. package/src/engine/shaders/transition/blur/fs.js +42 -0
  100. package/src/engine/shaders/transition/blur/vs.js +26 -0
  101. package/src/engine/shaders/transition/cross/fs.js +36 -0
  102. package/src/engine/shaders/transition/cross/vs.js +26 -0
  103. package/src/engine/shaders/transition/crossBlur/fs.js +41 -0
  104. package/src/engine/shaders/transition/crossBlur/vs.js +25 -0
  105. package/src/engine/shaders/transition/dissolve/fs.js +78 -0
  106. package/src/engine/shaders/transition/dissolve/vs.js +24 -0
  107. package/src/engine/shaders/transition/fade/fs.js +31 -0
  108. package/src/engine/shaders/transition/fade/vs.js +27 -0
  109. package/src/engine/shaders/transition/iris/fs.js +52 -0
  110. package/src/engine/shaders/transition/iris/vs.js +24 -0
  111. package/src/engine/shaders/transition/pixelate/fs.js +44 -0
  112. package/src/engine/shaders/transition/pixelate/vs.js +24 -0
  113. package/src/engine/shaders/transition/slide/fs.js +53 -0
  114. package/src/engine/shaders/transition/slide/vs.js +24 -0
  115. package/src/engine/shaders/transition/swirl/fs.js +39 -0
  116. package/src/engine/shaders/transition/swirl/vs.js +26 -0
  117. package/src/engine/shaders/transition/wipe/fs.js +50 -0
  118. package/src/engine/shaders/transition/wipe/vs.js +24 -0
  119. package/src/engine/shaders/vs.js +60 -0
  120. package/src/engine/utils/CameraController.js +506 -0
  121. package/src/engine/utils/ObjHelper.js +551 -0
  122. package/src/engine/utils/debug-logger.js +110 -0
  123. package/src/engine/utils/enums.js +305 -0
  124. package/src/engine/utils/generator.js +156 -0
  125. package/src/engine/utils/index.js +21 -0
  126. package/src/engine/utils/loaders/ActionLoader.js +77 -0
  127. package/src/engine/utils/loaders/AudioLoader.js +157 -0
  128. package/src/engine/utils/loaders/EventLoader.js +66 -0
  129. package/src/engine/utils/loaders/ObjectLoader.js +67 -0
  130. package/src/engine/utils/loaders/SpriteLoader.js +77 -0
  131. package/src/engine/utils/loaders/TilesetLoader.js +103 -0
  132. package/src/engine/utils/loaders/index.js +21 -0
  133. package/src/engine/utils/math/matrix4.js +367 -0
  134. package/src/engine/utils/math/vector.js +458 -0
  135. package/src/engine/utils/obj/_old_js/index.js +46 -0
  136. package/src/engine/utils/obj/_old_js/layout.js +308 -0
  137. package/src/engine/utils/obj/_old_js/material.js +711 -0
  138. package/src/engine/utils/obj/_old_js/mesh.js +761 -0
  139. package/src/engine/utils/obj/_old_js/utils.js +647 -0
  140. package/src/engine/utils/obj/index.js +24 -0
  141. package/src/engine/utils/obj/js/index.js +277 -0
  142. package/src/engine/utils/obj/js/loader.js +232 -0
  143. package/src/engine/utils/obj/layout.js +246 -0
  144. package/src/engine/utils/obj/material.js +665 -0
  145. package/src/engine/utils/obj/mesh.js +657 -0
  146. package/src/engine/utils/obj/ts/index.ts +72 -0
  147. package/src/engine/utils/obj/ts/layout.ts +265 -0
  148. package/src/engine/utils/obj/ts/material.ts +760 -0
  149. package/src/engine/utils/obj/ts/mesh.ts +785 -0
  150. package/src/engine/utils/obj/ts/utils.ts +501 -0
  151. package/src/engine/utils/obj/utils.js +428 -0
  152. package/src/engine/utils/resources.js +18 -0
  153. package/src/index.jsx +55 -0
  154. package/src/spritz/player.js +18 -0
  155. package/src/spritz/readme.md +18 -0
  156. package/LICENSE +0 -437
  157. package/dist/bundle.js.LICENSE.txt +0 -31
@@ -0,0 +1,501 @@
1
+ import Mesh from './mesh';
2
+ import { MaterialLibrary, TextureMapData } from './material';
3
+ function create1PixelTexture(gl, pixel) {
4
+ const texture = gl.createTexture();
5
+ gl.bindTexture(gl.TEXTURE_2D, texture);
6
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(pixel));
7
+ return texture;
8
+ }
9
+ function downloadMtlTextures(gl, mtl: MaterialLibrary, root: string) {
10
+ const mapAttributes = ['mapDiffuse', 'mapAmbient', 'mapSpecular', 'mapDissolve', 'mapBump', 'mapDisplacement', 'mapDecal', 'mapEmissive'];
11
+ if (!root.endsWith('/')) {
12
+ root += '/';
13
+ }
14
+ const textures = [];
15
+
16
+ for (const materialName in mtl.materials) {
17
+ if (!mtl.materials.hasOwnProperty(materialName)) {
18
+ continue;
19
+ }
20
+ const material = mtl.materials[materialName];
21
+
22
+ for (const attr of mapAttributes) {
23
+ const mapData = (material as any)[attr] as TextureMapData;
24
+ if (!mapData || !mapData.filename) {
25
+ continue;
26
+ }
27
+ const url = root + mapData.filename;
28
+ textures.push(
29
+ fetch(url)
30
+ .then((response) => {
31
+ if (!response.ok) {
32
+ throw new Error();
33
+ }
34
+ return response.blob();
35
+ })
36
+ .then(function (data) {
37
+ const image = new Image();
38
+ image.src = URL.createObjectURL(data);
39
+ mapData.texture = image;
40
+ var texture = create1PixelTexture(gl, [128, 192, 86, 255]);
41
+ mapData.glTexture = texture;
42
+ // Set the parameters so we can render any size image.
43
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
44
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
45
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
46
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
47
+ gl.bindTexture(gl.TEXTURE_2D, mapData.glTexture);
48
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, mapData.texture);
49
+ // Upload the image into the texture.
50
+ image.onload = () => {
51
+ gl.bindTexture(gl.TEXTURE_2D, mapData.glTexture);
52
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, mapData.texture);
53
+ };
54
+
55
+ return new Promise((resolve) => (image.onload = () => resolve(mapData)));
56
+ })
57
+ .catch(() => {
58
+ console.error(`Unable to download texture: ${url}`);
59
+ })
60
+ );
61
+ }
62
+ }
63
+
64
+ return Promise.all(textures);
65
+ }
66
+
67
+ function downloadMtlTexturesFromZip(gl, mtl: MaterialLibrary, root: string, zip: any) {
68
+ const mapAttributes = ['mapDiffuse', 'mapAmbient', 'mapSpecular', 'mapDissolve', 'mapBump', 'mapDisplacement', 'mapDecal', 'mapEmissive'];
69
+ if (!root.endsWith('/')) {
70
+ root += '/';
71
+ }
72
+ const textures = [];
73
+
74
+ for (const materialName in mtl.materials) {
75
+ if (!mtl.materials.hasOwnProperty(materialName)) {
76
+ continue;
77
+ }
78
+ const material = mtl.materials[materialName];
79
+
80
+ for (const attr of mapAttributes) {
81
+ const mapData = (material as any)[attr] as TextureMapData;
82
+ if (!mapData || !mapData.filename) {
83
+ continue;
84
+ }
85
+ const url = root + mapData.filename;
86
+ textures.push(
87
+ zip
88
+ .file(`textures/${mapData.filename}`)
89
+ .async('arrayBuffer')
90
+ .then((imageData) => {
91
+ let buffer = new Uint8Array(imageData);
92
+ return new Blob([buffer.buffer]);
93
+ })
94
+ .then(function (data) {
95
+ const image = new Image();
96
+ image.src = URL.createObjectURL(data);
97
+ mapData.texture = image;
98
+ var texture = create1PixelTexture(gl, [128, 192, 86, 255]);
99
+ mapData.glTexture = texture;
100
+ // Set the parameters so we can render any size image.
101
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
102
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
103
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
104
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
105
+ gl.bindTexture(gl.TEXTURE_2D, mapData.glTexture);
106
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, mapData.texture);
107
+ // Upload the image into the texture.
108
+ image.onload = () => {
109
+ gl.bindTexture(gl.TEXTURE_2D, mapData.glTexture);
110
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, mapData.texture);
111
+ };
112
+
113
+ return new Promise((resolve) => (image.onload = () => resolve(mapData)));
114
+ })
115
+ .catch(() => {
116
+ console.error(`Unable to download texture: ${url}`);
117
+ })
118
+ );
119
+ }
120
+ }
121
+
122
+ return Promise.all(textures);
123
+ }
124
+
125
+ function getMtl(modelOptions: DownloadModelsOptions): string {
126
+ if (!(typeof modelOptions.mtl === 'string')) {
127
+ return modelOptions.obj.replace(/\.obj$/, '.mtl');
128
+ }
129
+
130
+ return modelOptions.mtl;
131
+ }
132
+
133
+ export interface DownloadModelsOptions {
134
+ obj: string;
135
+ mtl?: boolean | string;
136
+ downloadMtlTextures?: boolean;
137
+ mtlTextureRoot?: string;
138
+ name?: string;
139
+ indicesPerMaterial?: boolean;
140
+ calcTangentsAndBitangents?: boolean;
141
+ }
142
+
143
+ type ModelPromises = [Promise<string>, Promise<Mesh>, undefined | Promise<MaterialLibrary>];
144
+ export type MeshMap = { [name: string]: Mesh };
145
+ /**
146
+ * Accepts a list of model request objects and returns a Promise that
147
+ * resolves when all models have been downloaded and parsed.
148
+ *
149
+ * The list of model objects follow this interface:
150
+ * {
151
+ * obj: 'path/to/model.obj',
152
+ * mtl: true | 'path/to/model.mtl',
153
+ * downloadMtlTextures: true | false
154
+ * mtlTextureRoot: '/models/suzanne/maps'
155
+ * name: 'suzanne'
156
+ * }
157
+ *
158
+ * The `obj` attribute is required and should be the path to the
159
+ * model's .obj file relative to the current repo (absolute URLs are
160
+ * suggested).
161
+ *
162
+ * The `mtl` attribute is optional and can either be a boolean or
163
+ * a path to the model's .mtl file relative to the current URL. If
164
+ * the value is `true`, then the path and basename given for the `obj`
165
+ * attribute is used replacing the .obj suffix for .mtl
166
+ * E.g.: {obj: 'models/foo.obj', mtl: true} would search for 'models/foo.mtl'
167
+ *
168
+ * The `name` attribute is optional and is a human friendly name to be
169
+ * included with the parsed OBJ and MTL files. If not given, the base .obj
170
+ * filename will be used.
171
+ *
172
+ * The `downloadMtlTextures` attribute is a flag for automatically downloading
173
+ * any images found in the MTL file and attaching them to each Material
174
+ * created from that file. For example, if material.mapDiffuse is set (there
175
+ * was data in the MTL file), then material.mapDiffuse.texture will contain
176
+ * the downloaded image. This option defaults to `true`. By default, the MTL's
177
+ * URL will be used to determine the location of the images.
178
+ *
179
+ * The `mtlTextureRoot` attribute is optional and should point to the location
180
+ * on the server that this MTL's texture files are located. The default is to
181
+ * use the MTL file's location.
182
+ *
183
+ * @returns {Promise} the result of downloading the given list of models. The
184
+ * promise will resolve with an object whose keys are the names of the models
185
+ * and the value is its Mesh object. Each Mesh object will automatically
186
+ * have its addMaterialLibrary() method called to set the given MTL data (if given).
187
+ */
188
+ export function downloadModels(gl, models: DownloadModelsOptions[]): Promise<MeshMap> {
189
+ const finished = [];
190
+
191
+ for (const model of models) {
192
+ if (!model.obj) {
193
+ throw new Error('"obj" attribute of model object not set. The .obj file is required to be set ' + 'in order to use downloadModels()');
194
+ }
195
+
196
+ const options = {
197
+ indicesPerMaterial: !!model.indicesPerMaterial,
198
+ calcTangentsAndBitangents: !!model.calcTangentsAndBitangents,
199
+ };
200
+
201
+ // if the name is not provided, dervive it from the given OBJ
202
+ let name = model.name;
203
+ if (!name) {
204
+ const parts = model.obj.split('/');
205
+ name = parts[parts.length - 1].replace('.obj', '');
206
+ }
207
+ const namePromise = Promise.resolve(name);
208
+
209
+ const meshPromise = fetch(model.obj)
210
+ .then((response) => response.text())
211
+ .then((data) => {
212
+ return new Mesh(data, options);
213
+ });
214
+
215
+ let mtlPromise;
216
+ // Download MaterialLibrary file?
217
+ if (model.mtl) {
218
+ const mtl = getMtl(model);
219
+ mtlPromise = fetch(mtl)
220
+ .then((response) => response.text())
221
+ .then((data: string): Promise<[MaterialLibrary, any]> => {
222
+ const material = new MaterialLibrary(data);
223
+ if (model.downloadMtlTextures !== false) {
224
+ let root = model.mtlTextureRoot;
225
+ if (!root) {
226
+ // get the directory of the MTL file as default
227
+ root = mtl.substr(0, mtl.lastIndexOf('/'));
228
+ }
229
+ // downloadMtlTextures returns a Promise that
230
+ // is resolved once all of the images it
231
+ // contains are downloaded. These are then
232
+ // attached to the map data objects
233
+ return Promise.all([Promise.resolve(material), downloadMtlTextures(gl, material, root)]);
234
+ }
235
+ return Promise.all([Promise.resolve(material), undefined]);
236
+ })
237
+ .then((value: [MaterialLibrary, any]) => {
238
+ return value;
239
+ });
240
+ }
241
+
242
+ const parsed: ModelPromises = [namePromise, meshPromise, mtlPromise];
243
+ finished.push(Promise.all(parsed));
244
+ }
245
+
246
+ return Promise.all(finished).then((ms) => {
247
+ // the "finished" promise is a list of name, Mesh instance,
248
+ // and MaterialLibary instance. This unpacks and returns an
249
+ // object mapping name to Mesh (Mesh points to MTL).
250
+ const models: MeshMap = {};
251
+
252
+ for (const model of ms) {
253
+ const [name, mesh, mtl] = model;
254
+ mesh.name = name;
255
+ if (mtl) {
256
+ mesh.addMaterialLibrary(mtl[0]);
257
+ }
258
+ models[name] = mesh;
259
+ }
260
+
261
+ return models;
262
+ });
263
+ }
264
+
265
+ export function downloadModelsFromZip(gl, models: DownloadModelsOptions[], zip: any): Promise<MeshMap> {
266
+ const finished = [];
267
+
268
+ for (const model of models) {
269
+ if (!model.obj) {
270
+ throw new Error('"obj" attribute of model object not set. The .obj file is required to be set ' + 'in order to use downloadModels()');
271
+ }
272
+
273
+ const options = {
274
+ indicesPerMaterial: !!model.indicesPerMaterial,
275
+ calcTangentsAndBitangents: !!model.calcTangentsAndBitangents,
276
+ };
277
+
278
+ // if the name is not provided, dervive it from the given OBJ
279
+ const parts = model.obj.split('/');
280
+ let name = parts[parts.length - 1].replace('.obj', '');
281
+ const namePromise = Promise.resolve(name);
282
+ const meshPromise = zip
283
+ .file(`models/${name}.obj`)
284
+ .async('string')
285
+ .then((data) => {
286
+ return new Mesh(data, options);
287
+ });
288
+
289
+ let mtlPromise;
290
+ // Download MaterialLibrary file?
291
+ if (model.mtl) {
292
+ const mtl = getMtl(model);
293
+ mtlPromise = zip
294
+ .file(`models/${mtl}`)
295
+ .async('string')
296
+ .then((data: string): Promise<[MaterialLibrary, any]> => {
297
+ const material = new MaterialLibrary(data);
298
+ if (model.downloadMtlTextures !== false) {
299
+ let root = model.mtlTextureRoot;
300
+ if (!root) {
301
+ // get the directory of the MTL file as default
302
+ root = mtl.substr(0, mtl.lastIndexOf('/'));
303
+ }
304
+ // downloadMtlTextures returns a Promise that
305
+ // is resolved once all of the images it
306
+ // contains are downloaded. These are then
307
+ // attached to the map data objects
308
+ return Promise.all([Promise.resolve(material), downloadMtlTexturesFromZip(gl, material, root, zip)]);
309
+ }
310
+ return Promise.all([Promise.resolve(material), undefined]);
311
+ })
312
+ .then((value: [MaterialLibrary, any]) => {
313
+ return value;
314
+ });
315
+ }
316
+
317
+ const parsed: ModelPromises = [namePromise, meshPromise, mtlPromise];
318
+ finished.push(Promise.all(parsed));
319
+ }
320
+
321
+ return Promise.all(finished).then((ms) => {
322
+ // the "finished" promise is a list of name, Mesh instance,
323
+ // and MaterialLibary instance. This unpacks and returns an
324
+ // object mapping name to Mesh (Mesh points to MTL).
325
+ const models: MeshMap = {};
326
+
327
+ for (const model of ms) {
328
+ const [name, mesh, mtl] = model;
329
+ mesh.name = name;
330
+ if (mtl) {
331
+ mesh.addMaterialLibrary(mtl[0]);
332
+ }
333
+ models[name] = mesh;
334
+ }
335
+
336
+ return models;
337
+ });
338
+ }
339
+
340
+ export interface NameAndUrls {
341
+ [meshName: string]: string;
342
+ }
343
+
344
+ /**
345
+ * Takes in an object of `mesh_name`, `'/url/to/OBJ/file'` pairs and a callback
346
+ * function. Each OBJ file will be ajaxed in and automatically converted to
347
+ * an OBJ.Mesh. When all files have successfully downloaded the callback
348
+ * function provided will be called and passed in an object containing
349
+ * the newly created meshes.
350
+ *
351
+ * **Note:** In order to use this function as a way to download meshes, a
352
+ * webserver of some sort must be used.
353
+ *
354
+ * @param {Object} nameAndAttrs an object where the key is the name of the mesh and the value is the url to that mesh's OBJ file
355
+ *
356
+ * @param {Function} completionCallback should contain a function that will take one parameter: an object array where the keys will be the unique object name and the value will be a Mesh object
357
+ *
358
+ * @param {Object} meshes In case other meshes are loaded separately or if a previously declared variable is desired to be used, pass in a (possibly empty) json object of the pattern: { '<mesh_name>': OBJ.Mesh }
359
+ *
360
+ */
361
+ export function downloadMeshes(nameAndURLs: NameAndUrls, completionCallback: (meshes: MeshMap) => void, meshes: MeshMap) {
362
+ if (meshes === undefined) {
363
+ meshes = {};
364
+ }
365
+
366
+ const completed: Promise<[string, Mesh]>[] = [];
367
+
368
+ for (const mesh_name in nameAndURLs) {
369
+ if (!nameAndURLs.hasOwnProperty(mesh_name)) {
370
+ continue;
371
+ }
372
+ const url = nameAndURLs[mesh_name];
373
+ completed.push(
374
+ fetch(url)
375
+ .then((response) => response.text())
376
+ .then((data) => {
377
+ return [mesh_name, new Mesh(data)] as [string, Mesh];
378
+ })
379
+ );
380
+ }
381
+
382
+ Promise.all(completed).then((ms) => {
383
+ for (const [name, mesh] of ms) {
384
+ meshes[name] = mesh;
385
+ }
386
+
387
+ return completionCallback(meshes);
388
+ });
389
+ }
390
+
391
+ export interface ExtendedGLBuffer extends WebGLBuffer {
392
+ itemSize: number;
393
+ numItems: number;
394
+ }
395
+
396
+ export function _buildBuffer(gl: WebGLRenderingContext, type: GLenum, data: number[], itemSize: number): ExtendedGLBuffer {
397
+ const buffer = gl.createBuffer() as ExtendedGLBuffer;
398
+ const arrayView = type === gl.ARRAY_BUFFER ? Float32Array : Uint16Array;
399
+ gl.bindBuffer(type, buffer);
400
+ gl.bufferData(type, new arrayView(data), gl.STATIC_DRAW);
401
+ buffer.itemSize = itemSize;
402
+ buffer.numItems = data.length / itemSize;
403
+ return buffer;
404
+ }
405
+
406
+ export interface MeshWithBuffers extends Mesh {
407
+ normalBuffer: ExtendedGLBuffer;
408
+ textureBuffer: ExtendedGLBuffer;
409
+ vertexBuffer: ExtendedGLBuffer;
410
+ indexBuffer: ExtendedGLBuffer;
411
+ }
412
+
413
+ /**
414
+ * Takes in the WebGL context and a Mesh, then creates and appends the buffers
415
+ * to the mesh object as attributes.
416
+ *
417
+ * @param {WebGLRenderingContext} gl the `canvas.getContext('webgl')` context instance
418
+ * @param {Mesh} mesh a single `OBJ.Mesh` instance
419
+ *
420
+ * The newly created mesh attributes are:
421
+ *
422
+ * Attrbute | Description
423
+ * :--- | ---
424
+ * **normalBuffer** |contains the model&#39;s Vertex Normals
425
+ * normalBuffer.itemSize |set to 3 items
426
+ * normalBuffer.numItems |the total number of vertex normals
427
+ * |
428
+ * **textureBuffer** |contains the model&#39;s Texture Coordinates
429
+ * textureBuffer.itemSize |set to 2 items
430
+ * textureBuffer.numItems |the number of texture coordinates
431
+ * |
432
+ * **vertexBuffer** |contains the model&#39;s Vertex Position Coordinates (does not include w)
433
+ * vertexBuffer.itemSize |set to 3 items
434
+ * vertexBuffer.numItems |the total number of vertices
435
+ * |
436
+ * **indexBuffer** |contains the indices of the faces
437
+ * indexBuffer.itemSize |is set to 1
438
+ * indexBuffer.numItems |the total number of indices
439
+ *
440
+ * A simple example (a lot of steps are missing, so don't copy and paste):
441
+ *
442
+ * const gl = canvas.getContext('webgl'),
443
+ * mesh = OBJ.Mesh(obj_file_data);
444
+ * // compile the shaders and create a shader program
445
+ * const shaderProgram = gl.createProgram();
446
+ * // compilation stuff here
447
+ * ...
448
+ * // make sure you have vertex, vertex normal, and texture coordinate
449
+ * // attributes located in your shaders and attach them to the shader program
450
+ * shaderProgram.aVertexPosition = gl.getAttribLocation(shaderProgram, "aVertexPosition");
451
+ * gl.enableVertexAttribArray(shaderProgram.aVertexPosition);
452
+ *
453
+ * shaderProgram.aVertexNormal = gl.getAttribLocation(shaderProgram, "aVertexNormal");
454
+ * gl.enableVertexAttribArray(shaderProgram.aVertexNormal);
455
+ *
456
+ * shaderProgram.aTextureCoord = gl.getAttribLocation(shaderProgram, "aTextureCoord");
457
+ * gl.enableVertexAttribArray(shaderProgram.aTextureCoord);
458
+ *
459
+ * // create and initialize the vertex, vertex normal, and texture coordinate buffers
460
+ * // and save on to the mesh object
461
+ * OBJ.initMeshBuffers(gl, mesh);
462
+ *
463
+ * // now to render the mesh
464
+ * gl.bindBuffer(gl.ARRAY_BUFFER, mesh.vertexBuffer);
465
+ * gl.vertexAttribPointer(shaderProgram.aVertexPosition, mesh.vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
466
+ * // it's possible that the mesh doesn't contain
467
+ * // any texture coordinates (e.g. suzanne.obj in the development branch).
468
+ * // in this case, the texture vertexAttribArray will need to be disabled
469
+ * // before the call to drawElements
470
+ * if(!mesh.textures.length){
471
+ * gl.disableVertexAttribArray(shaderProgram.aTextureCoord);
472
+ * }
473
+ * else{
474
+ * // if the texture vertexAttribArray has been previously
475
+ * // disabled, then it needs to be re-enabled
476
+ * gl.enableVertexAttribArray(shaderProgram.aTextureCoord);
477
+ * gl.bindBuffer(gl.ARRAY_BUFFER, mesh.textureBuffer);
478
+ * gl.vertexAttribPointer(shaderProgram.aTextureCoord, mesh.textureBuffer.itemSize, gl.FLOAT, false, 0, 0);
479
+ * }
480
+ *
481
+ * gl.bindBuffer(gl.ARRAY_BUFFER, mesh.normalBuffer);
482
+ * gl.vertexAttribPointer(shaderProgram.aVertexNormal, mesh.normalBuffer.itemSize, gl.FLOAT, false, 0, 0);
483
+ *
484
+ * gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, model.mesh.indexBuffer);
485
+ * gl.drawElements(gl.TRIANGLES, model.mesh.indexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
486
+ */
487
+ export function initMeshBuffers(gl: WebGLRenderingContext, mesh: Mesh): MeshWithBuffers {
488
+ (mesh as MeshWithBuffers).normalBuffer = _buildBuffer(gl, gl.ARRAY_BUFFER, mesh.vertexNormals, 3);
489
+ (mesh as MeshWithBuffers).textureBuffer = _buildBuffer(gl, gl.ARRAY_BUFFER, mesh.textures, mesh.textureStride);
490
+ (mesh as MeshWithBuffers).vertexBuffer = _buildBuffer(gl, gl.ARRAY_BUFFER, mesh.vertices, 3);
491
+ (mesh as MeshWithBuffers).indexBuffer = _buildBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, mesh.indices, 1);
492
+
493
+ return mesh as MeshWithBuffers;
494
+ }
495
+
496
+ export function deleteMeshBuffers(gl: WebGLRenderingContext, mesh: MeshWithBuffers) {
497
+ gl.deleteBuffer(mesh.normalBuffer);
498
+ gl.deleteBuffer(mesh.textureBuffer);
499
+ gl.deleteBuffer(mesh.vertexBuffer);
500
+ gl.deleteBuffer(mesh.indexBuffer);
501
+ }