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,785 @@
1
+ import { Layout } from "./layout";
2
+ import { Material, MaterialLibrary } from "./material";
3
+
4
+ export interface MeshOptions {
5
+ enableWTextureCoord?: boolean;
6
+ calcTangentsAndBitangents?: boolean;
7
+ materials?: { [key: string]: Material };
8
+ }
9
+
10
+ interface UnpackedAttrs {
11
+ verts: number[];
12
+ norms: number[];
13
+ textures: number[];
14
+ hashindices: { [k: string]: number };
15
+ indices: number[][];
16
+ materialIndices: number[];
17
+ index: number;
18
+ }
19
+
20
+ export interface MaterialNameToIndex {
21
+ [k: string]: number;
22
+ }
23
+
24
+ export interface IndexToMaterial {
25
+ [k: number]: Material;
26
+ }
27
+
28
+ export interface ArrayBufferWithItemSize extends ArrayBuffer {
29
+ numItems?: number;
30
+ }
31
+
32
+ export interface Uint16ArrayWithItemSize extends Uint16Array {
33
+ numItems?: number;
34
+ }
35
+
36
+ /**
37
+ * The main Mesh class. The constructor will parse through the OBJ file data
38
+ * and collect the vertex, vertex normal, texture, and face information. This
39
+ * information can then be used later on when creating your VBOs. See
40
+ * OBJ.initMeshBuffers for an example of how to use the newly created Mesh
41
+ */
42
+ export default class Mesh {
43
+ public vertices: number[];
44
+ public vertexNormals: number[];
45
+ public textures: number[];
46
+ public indices: number[];
47
+ public name: string = "";
48
+ public vertexMaterialIndices: number[];
49
+ public indicesPerMaterial: number[][] = [];
50
+ public materialNames: string[];
51
+ public materialIndices: MaterialNameToIndex;
52
+ public materialsByIndex: IndexToMaterial = {};
53
+ public tangents: number[] = [];
54
+ public bitangents: number[] = [];
55
+ public textureStride: number;
56
+
57
+ /**
58
+ * Create a Mesh
59
+ * @param {String} objectData - a string representation of an OBJ file with
60
+ * newlines preserved.
61
+ * @param {Object} options - a JS object containing valid options. See class
62
+ * documentation for options.
63
+ * @param {bool} options.enableWTextureCoord - Texture coordinates can have
64
+ * an optional "w" coordinate after the u and v coordinates. This extra
65
+ * value can be used in order to perform fancy transformations on the
66
+ * textures themselves. Default is to truncate to only the u an v
67
+ * coordinates. Passing true will provide a default value of 0 in the
68
+ * event that any or all texture coordinates don't provide a w value.
69
+ * Always use the textureStride attribute in order to determine the
70
+ * stride length of the texture coordinates when rendering the element
71
+ * array.
72
+ * @param {bool} options.calcTangentsAndBitangents - Calculate the tangents
73
+ * and bitangents when loading of the OBJ is completed. This adds two new
74
+ * attributes to the Mesh instance: `tangents` and `bitangents`.
75
+ */
76
+ constructor(objectData: string, options?: MeshOptions) {
77
+ options = options || {};
78
+ options.materials = options.materials || {};
79
+ options.enableWTextureCoord = !!options.enableWTextureCoord;
80
+
81
+ // the list of unique vertex, normal, texture, attributes
82
+ this.vertexNormals = [];
83
+ this.textures = [];
84
+ // the indicies to draw the faces
85
+ this.indices = [];
86
+ this.textureStride = options.enableWTextureCoord ? 3 : 2;
87
+
88
+ /*
89
+ The OBJ file format does a sort of compression when saving a model in a
90
+ program like Blender. There are at least 3 sections (4 including textures)
91
+ within the file. Each line in a section begins with the same string:
92
+ * 'v': indicates vertex section
93
+ * 'vn': indicates vertex normal section
94
+ * 'f': indicates the faces section
95
+ * 'vt': indicates vertex texture section (if textures were used on the model)
96
+ Each of the above sections (except for the faces section) is a list/set of
97
+ unique vertices.
98
+
99
+ Each line of the faces section contains a list of
100
+ (vertex, [texture], normal) groups.
101
+
102
+ **Note:** The following documentation will use a capital "V" Vertex to
103
+ denote the above (vertex, [texture], normal) groups whereas a lowercase
104
+ "v" vertex is used to denote an X, Y, Z coordinate.
105
+
106
+ Some examples:
107
+ // the texture index is optional, both formats are possible for models
108
+ // without a texture applied
109
+ f 1/25 18/46 12/31
110
+ f 1//25 18//46 12//31
111
+
112
+ // A 3 vertex face with texture indices
113
+ f 16/92/11 14/101/22 1/69/1
114
+
115
+ // A 4 vertex face
116
+ f 16/92/11 40/109/40 38/114/38 14/101/22
117
+
118
+ The first two lines are examples of a 3 vertex face without a texture applied.
119
+ The second is an example of a 3 vertex face with a texture applied.
120
+ The third is an example of a 4 vertex face. Note: a face can contain N
121
+ number of vertices.
122
+
123
+ Each number that appears in one of the groups is a 1-based index
124
+ corresponding to an item from the other sections (meaning that indexing
125
+ starts at one and *not* zero).
126
+
127
+ For example:
128
+ `f 16/92/11` is saying to
129
+ - take the 16th element from the [v] vertex array
130
+ - take the 92nd element from the [vt] texture array
131
+ - take the 11th element from the [vn] normal array
132
+ and together they make a unique vertex.
133
+ Using all 3+ unique Vertices from the face line will produce a polygon.
134
+
135
+ Now, you could just go through the OBJ file and create a new vertex for
136
+ each face line and WebGL will draw what appears to be the same model.
137
+ However, vertices will be overlapped and duplicated all over the place.
138
+
139
+ Consider a cube in 3D space centered about the origin and each side is
140
+ 2 units long. The front face (with the positive Z-axis pointing towards
141
+ you) would have a Top Right vertex (looking orthogonal to its normal)
142
+ mapped at (1,1,1) The right face would have a Top Left vertex (looking
143
+ orthogonal to its normal) at (1,1,1) and the top face would have a Bottom
144
+ Right vertex (looking orthogonal to its normal) at (1,1,1). Each face
145
+ has a vertex at the same coordinates, however, three distinct vertices
146
+ will be drawn at the same spot.
147
+
148
+ To solve the issue of duplicate Vertices (the `(vertex, [texture], normal)`
149
+ groups), while iterating through the face lines, when a group is encountered
150
+ the whole group string ('16/92/11') is checked to see if it exists in the
151
+ packed.hashindices object, and if it doesn't, the indices it specifies
152
+ are used to look up each attribute in the corresponding attribute arrays
153
+ already created. The values are then copied to the corresponding unpacked
154
+ array (flattened to play nice with WebGL's ELEMENT_ARRAY_BUFFER indexing),
155
+ the group string is added to the hashindices set and the current unpacked
156
+ index is used as this hashindices value so that the group of elements can
157
+ be reused. The unpacked index is incremented. If the group string already
158
+ exists in the hashindices object, its corresponding value is the index of
159
+ that group and is appended to the unpacked indices array.
160
+ */
161
+ const verts = [];
162
+ const vertNormals = [];
163
+ const textures = [];
164
+ const materialNamesByIndex = [];
165
+ const materialIndicesByName: MaterialNameToIndex = {};
166
+ // keep track of what material we've seen last
167
+ let currentMaterialIndex = -1;
168
+ let currentObjectByMaterialIndex = 0;
169
+ // unpacking stuff
170
+ const unpacked: UnpackedAttrs = {
171
+ verts: [],
172
+ norms: [],
173
+ textures: [],
174
+ hashindices: {},
175
+ indices: [[]],
176
+ materialIndices: [],
177
+ index: 0,
178
+ };
179
+
180
+ const VERTEX_RE = /^v\s/;
181
+ const NORMAL_RE = /^vn\s/;
182
+ const TEXTURE_RE = /^vt\s/;
183
+ const FACE_RE = /^f\s/;
184
+ const WHITESPACE_RE = /\s+/;
185
+ const USE_MATERIAL_RE = /^usemtl/;
186
+ // array of lines separated by the newline
187
+ const lines = objectData.split("\n");
188
+ for (let line of lines) {
189
+ line = line.trim();
190
+ if (!line || line.startsWith("#")) {
191
+ continue;
192
+ }
193
+ const elements = line.split(WHITESPACE_RE);
194
+ elements.shift();
195
+
196
+ if (VERTEX_RE.test(line)) {
197
+ // if this is a vertex
198
+ verts.push(...elements);
199
+ } else if (NORMAL_RE.test(line)) {
200
+ // if this is a vertex normal
201
+ vertNormals.push(...elements);
202
+ } else if (TEXTURE_RE.test(line)) {
203
+ // Parse UV coordinates and flip V for WebGL (like the demo viewer)
204
+ const u = parseFloat(elements[0]);
205
+ const v = elements.length > 1 ? 1.0 - parseFloat(elements[1]) : 0.0; // Flip V coordinate for WebGL
206
+
207
+ if (options.enableWTextureCoord) {
208
+ const w = elements.length > 2 ? parseFloat(elements[2]) : 0.0;
209
+ textures.push(u.toString(), v.toString(), w.toString());
210
+ } else {
211
+ textures.push(u.toString(), v.toString());
212
+ }
213
+ } else if (USE_MATERIAL_RE.test(line)) {
214
+ const materialName = elements[0];
215
+
216
+ // check to see if we've ever seen it before
217
+ if (!(materialName in materialIndicesByName)) {
218
+ // new material we've never seen
219
+ materialNamesByIndex.push(materialName);
220
+ materialIndicesByName[materialName] = materialNamesByIndex.length - 1;
221
+ // push new array into indices
222
+ // already contains an array at index zero, don't add
223
+ if (materialIndicesByName[materialName] > 0) {
224
+ unpacked.indices.push([]);
225
+ }
226
+ }
227
+ // keep track of the current material index
228
+ currentMaterialIndex = materialIndicesByName[materialName];
229
+ // update current index array
230
+ currentObjectByMaterialIndex = currentMaterialIndex;
231
+ } else if (FACE_RE.test(line)) {
232
+ // if this is a face
233
+ /*
234
+ split this face into an array of Vertex groups
235
+ for example:
236
+ f 16/92/11 14/101/22 1/69/1
237
+ becomes:
238
+ ['16/92/11', '14/101/22', '1/69/1'];
239
+ */
240
+
241
+ const triangles = triangulate(elements);
242
+ for (const triangle of triangles) {
243
+ for (let j = 0, eleLen = triangle.length; j < eleLen; j++) {
244
+ const hash = triangle[j] + "," + currentMaterialIndex;
245
+ if (hash in unpacked.hashindices) {
246
+ unpacked.indices[currentObjectByMaterialIndex].push(unpacked.hashindices[hash]);
247
+ } else {
248
+ /*
249
+ Each element of the face line array is a Vertex which has its
250
+ attributes delimited by a forward slash. This will separate
251
+ each attribute into another array:
252
+ '19/92/11'
253
+ becomes:
254
+ Vertex = ['19', '92', '11'];
255
+ where
256
+ Vertex[0] is the vertex index
257
+ Vertex[1] is the texture index
258
+ Vertex[2] is the normal index
259
+ Think of faces having Vertices which are comprised of the
260
+ attributes location (v), texture (vt), and normal (vn).
261
+ */
262
+ const vertex = triangle[j].split("/");
263
+ // it's possible for faces to only specify the vertex
264
+ // and the normal. In this case, vertex will only have
265
+ // a length of 2 and not 3 and the normal will be the
266
+ // second item in the list with an index of 1.
267
+ const normalIndex = vertex.length - 1;
268
+ /*
269
+ The verts, textures, and vertNormals arrays each contain a
270
+ flattend array of coordinates.
271
+
272
+ Because it gets confusing by referring to Vertex and then
273
+ vertex (both are different in my descriptions) I will explain
274
+ what's going on using the vertexNormals array:
275
+
276
+ vertex[2] will contain the one-based index of the vertexNormals
277
+ section (vn). One is subtracted from this index number to play
278
+ nice with javascript's zero-based array indexing.
279
+
280
+ Because vertexNormal is a flattened array of x, y, z values,
281
+ simple pointer arithmetic is used to skip to the start of the
282
+ vertexNormal, then the offset is added to get the correct
283
+ component: +0 is x, +1 is y, +2 is z.
284
+
285
+ This same process is repeated for verts and textures.
286
+ */
287
+ // Vertex position
288
+ unpacked.verts.push(+verts[(+vertex[0] - 1) * 3 + 0]);
289
+ unpacked.verts.push(+verts[(+vertex[0] - 1) * 3 + 1]);
290
+ unpacked.verts.push(+verts[(+vertex[0] - 1) * 3 + 2]);
291
+ // Vertex textures
292
+ if (textures.length && vertex[1] && vertex[1] !== "") {
293
+ const stride = options.enableWTextureCoord ? 3 : 2;
294
+ const texIndex = +vertex[1] - 1;
295
+ // Bounds check and clamp UV coordinates to 0-1 range
296
+ if (texIndex >= 0 && texIndex < textures.length / stride) {
297
+ const u = Math.max(0, Math.min(1, +textures[texIndex * stride + 0]));
298
+ const v = Math.max(0, Math.min(1, +textures[texIndex * stride + 1]));
299
+ unpacked.textures.push(u);
300
+ unpacked.textures.push(v);
301
+ if (options.enableWTextureCoord) {
302
+ unpacked.textures.push(+textures[texIndex * stride + 2]);
303
+ }
304
+ } else {
305
+ // Default UV coordinates if index is out of bounds
306
+ unpacked.textures.push(0);
307
+ unpacked.textures.push(0);
308
+ if (options.enableWTextureCoord) {
309
+ unpacked.textures.push(0);
310
+ }
311
+ }
312
+ } else if (textures.length) {
313
+ // No texture index specified, use default
314
+ unpacked.textures.push(0);
315
+ unpacked.textures.push(0);
316
+ if (options.enableWTextureCoord) {
317
+ unpacked.textures.push(0);
318
+ }
319
+ }
320
+ // Vertex normals
321
+ unpacked.norms.push(+vertNormals[(+vertex[normalIndex] - 1) * 3 + 0]);
322
+ unpacked.norms.push(+vertNormals[(+vertex[normalIndex] - 1) * 3 + 1]);
323
+ unpacked.norms.push(+vertNormals[(+vertex[normalIndex] - 1) * 3 + 2]);
324
+ // Vertex material indices
325
+ unpacked.materialIndices.push(currentMaterialIndex);
326
+ // add the newly created Vertex to the list of indices
327
+ unpacked.hashindices[hash] = unpacked.index;
328
+ unpacked.indices[currentObjectByMaterialIndex].push(unpacked.hashindices[hash]);
329
+ // increment the counter
330
+ unpacked.index += 1;
331
+ }
332
+ }
333
+ }
334
+ }
335
+ }
336
+
337
+ this.vertices = unpacked.verts;
338
+ this.vertexNormals = unpacked.norms;
339
+ this.textures = unpacked.textures;
340
+ this.vertexMaterialIndices = unpacked.materialIndices;
341
+ this.indices = unpacked.indices[currentObjectByMaterialIndex];
342
+ this.indicesPerMaterial = unpacked.indices;
343
+
344
+ this.materialNames = materialNamesByIndex;
345
+ this.materialIndices = materialIndicesByName;
346
+ this.materialsByIndex = {};
347
+
348
+ if (options.calcTangentsAndBitangents) {
349
+ this.calculateTangentsAndBitangents();
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Calculates the tangents and bitangents of the mesh that forms an orthogonal basis together with the
355
+ * normal in the direction of the texture coordinates. These are useful for setting up the TBN matrix
356
+ * when distorting the normals through normal maps.
357
+ * Method derived from: http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/
358
+ *
359
+ * This method requires the normals and texture coordinates to be parsed and set up correctly.
360
+ * Adds the tangents and bitangents as members of the class instance.
361
+ */
362
+ calculateTangentsAndBitangents() {
363
+ console.assert(
364
+ !!(
365
+ this.vertices &&
366
+ this.vertices.length &&
367
+ this.vertexNormals &&
368
+ this.vertexNormals.length &&
369
+ this.textures &&
370
+ this.textures.length
371
+ ),
372
+ "Missing attributes for calculating tangents and bitangents"
373
+ );
374
+
375
+ const unpacked = {
376
+ tangents: [...new Array(this.vertices.length)].map((_) => 0),
377
+ bitangents: [...new Array(this.vertices.length)].map((_) => 0),
378
+ };
379
+
380
+ // Loop through all faces in the whole mesh
381
+ const indices = this.indices;
382
+ const vertices = this.vertices;
383
+ const normals = this.vertexNormals;
384
+ const uvs = this.textures;
385
+
386
+ for (let i = 0; i < indices.length; i += 3) {
387
+ const i0 = indices[i + 0];
388
+ const i1 = indices[i + 1];
389
+ const i2 = indices[i + 2];
390
+
391
+ const x_v0 = vertices[i0 * 3 + 0];
392
+ const y_v0 = vertices[i0 * 3 + 1];
393
+ const z_v0 = vertices[i0 * 3 + 2];
394
+
395
+ const x_uv0 = uvs[i0 * 2 + 0];
396
+ const y_uv0 = uvs[i0 * 2 + 1];
397
+
398
+ const x_v1 = vertices[i1 * 3 + 0];
399
+ const y_v1 = vertices[i1 * 3 + 1];
400
+ const z_v1 = vertices[i1 * 3 + 2];
401
+
402
+ const x_uv1 = uvs[i1 * 2 + 0];
403
+ const y_uv1 = uvs[i1 * 2 + 1];
404
+
405
+ const x_v2 = vertices[i2 * 3 + 0];
406
+ const y_v2 = vertices[i2 * 3 + 1];
407
+ const z_v2 = vertices[i2 * 3 + 2];
408
+
409
+ const x_uv2 = uvs[i2 * 2 + 0];
410
+ const y_uv2 = uvs[i2 * 2 + 1];
411
+
412
+ const x_deltaPos1 = x_v1 - x_v0;
413
+ const y_deltaPos1 = y_v1 - y_v0;
414
+ const z_deltaPos1 = z_v1 - z_v0;
415
+
416
+ const x_deltaPos2 = x_v2 - x_v0;
417
+ const y_deltaPos2 = y_v2 - y_v0;
418
+ const z_deltaPos2 = z_v2 - z_v0;
419
+
420
+ const x_uvDeltaPos1 = x_uv1 - x_uv0;
421
+ const y_uvDeltaPos1 = y_uv1 - y_uv0;
422
+
423
+ const x_uvDeltaPos2 = x_uv2 - x_uv0;
424
+ const y_uvDeltaPos2 = y_uv2 - y_uv0;
425
+
426
+ const rInv = x_uvDeltaPos1 * y_uvDeltaPos2 - y_uvDeltaPos1 * x_uvDeltaPos2;
427
+ const r = 1.0 / Math.abs(rInv < 0.0001 ? 1.0 : rInv);
428
+
429
+ // Tangent
430
+ const x_tangent = (x_deltaPos1 * y_uvDeltaPos2 - x_deltaPos2 * y_uvDeltaPos1) * r;
431
+ const y_tangent = (y_deltaPos1 * y_uvDeltaPos2 - y_deltaPos2 * y_uvDeltaPos1) * r;
432
+ const z_tangent = (z_deltaPos1 * y_uvDeltaPos2 - z_deltaPos2 * y_uvDeltaPos1) * r;
433
+
434
+ // Bitangent
435
+ const x_bitangent = (x_deltaPos2 * x_uvDeltaPos1 - x_deltaPos1 * x_uvDeltaPos2) * r;
436
+ const y_bitangent = (y_deltaPos2 * x_uvDeltaPos1 - y_deltaPos1 * x_uvDeltaPos2) * r;
437
+ const z_bitangent = (z_deltaPos2 * x_uvDeltaPos1 - z_deltaPos1 * x_uvDeltaPos2) * r;
438
+
439
+ // Gram-Schmidt orthogonalize
440
+ //t = glm::normalize(t - n * glm:: dot(n, t));
441
+ const x_n0 = normals[i0 * 3 + 0];
442
+ const y_n0 = normals[i0 * 3 + 1];
443
+ const z_n0 = normals[i0 * 3 + 2];
444
+
445
+ const x_n1 = normals[i1 * 3 + 0];
446
+ const y_n1 = normals[i1 * 3 + 1];
447
+ const z_n1 = normals[i1 * 3 + 2];
448
+
449
+ const x_n2 = normals[i2 * 3 + 0];
450
+ const y_n2 = normals[i2 * 3 + 1];
451
+ const z_n2 = normals[i2 * 3 + 2];
452
+
453
+ // Tangent
454
+ const n0_dot_t = x_tangent * x_n0 + y_tangent * y_n0 + z_tangent * z_n0;
455
+ const n1_dot_t = x_tangent * x_n1 + y_tangent * y_n1 + z_tangent * z_n1;
456
+ const n2_dot_t = x_tangent * x_n2 + y_tangent * y_n2 + z_tangent * z_n2;
457
+
458
+ const x_resTangent0 = x_tangent - x_n0 * n0_dot_t;
459
+ const y_resTangent0 = y_tangent - y_n0 * n0_dot_t;
460
+ const z_resTangent0 = z_tangent - z_n0 * n0_dot_t;
461
+
462
+ const x_resTangent1 = x_tangent - x_n1 * n1_dot_t;
463
+ const y_resTangent1 = y_tangent - y_n1 * n1_dot_t;
464
+ const z_resTangent1 = z_tangent - z_n1 * n1_dot_t;
465
+
466
+ const x_resTangent2 = x_tangent - x_n2 * n2_dot_t;
467
+ const y_resTangent2 = y_tangent - y_n2 * n2_dot_t;
468
+ const z_resTangent2 = z_tangent - z_n2 * n2_dot_t;
469
+
470
+ const magTangent0 = Math.sqrt(
471
+ x_resTangent0 * x_resTangent0 + y_resTangent0 * y_resTangent0 + z_resTangent0 * z_resTangent0
472
+ );
473
+ const magTangent1 = Math.sqrt(
474
+ x_resTangent1 * x_resTangent1 + y_resTangent1 * y_resTangent1 + z_resTangent1 * z_resTangent1
475
+ );
476
+ const magTangent2 = Math.sqrt(
477
+ x_resTangent2 * x_resTangent2 + y_resTangent2 * y_resTangent2 + z_resTangent2 * z_resTangent2
478
+ );
479
+
480
+ // Bitangent
481
+ const n0_dot_bt = x_bitangent * x_n0 + y_bitangent * y_n0 + z_bitangent * z_n0;
482
+ const n1_dot_bt = x_bitangent * x_n1 + y_bitangent * y_n1 + z_bitangent * z_n1;
483
+ const n2_dot_bt = x_bitangent * x_n2 + y_bitangent * y_n2 + z_bitangent * z_n2;
484
+
485
+ const x_resBitangent0 = x_bitangent - x_n0 * n0_dot_bt;
486
+ const y_resBitangent0 = y_bitangent - y_n0 * n0_dot_bt;
487
+ const z_resBitangent0 = z_bitangent - z_n0 * n0_dot_bt;
488
+
489
+ const x_resBitangent1 = x_bitangent - x_n1 * n1_dot_bt;
490
+ const y_resBitangent1 = y_bitangent - y_n1 * n1_dot_bt;
491
+ const z_resBitangent1 = z_bitangent - z_n1 * n1_dot_bt;
492
+
493
+ const x_resBitangent2 = x_bitangent - x_n2 * n2_dot_bt;
494
+ const y_resBitangent2 = y_bitangent - y_n2 * n2_dot_bt;
495
+ const z_resBitangent2 = z_bitangent - z_n2 * n2_dot_bt;
496
+
497
+ const magBitangent0 = Math.sqrt(
498
+ x_resBitangent0 * x_resBitangent0 + y_resBitangent0 * y_resBitangent0 + z_resBitangent0 * z_resBitangent0
499
+ );
500
+ const magBitangent1 = Math.sqrt(
501
+ x_resBitangent1 * x_resBitangent1 + y_resBitangent1 * y_resBitangent1 + z_resBitangent1 * z_resBitangent1
502
+ );
503
+ const magBitangent2 = Math.sqrt(
504
+ x_resBitangent2 * x_resBitangent2 + y_resBitangent2 * y_resBitangent2 + z_resBitangent2 * z_resBitangent2
505
+ );
506
+
507
+ unpacked.tangents[i0 * 3 + 0] += x_resTangent0 / magTangent0;
508
+ unpacked.tangents[i0 * 3 + 1] += y_resTangent0 / magTangent0;
509
+ unpacked.tangents[i0 * 3 + 2] += z_resTangent0 / magTangent0;
510
+
511
+ unpacked.tangents[i1 * 3 + 0] += x_resTangent1 / magTangent1;
512
+ unpacked.tangents[i1 * 3 + 1] += y_resTangent1 / magTangent1;
513
+ unpacked.tangents[i1 * 3 + 2] += z_resTangent1 / magTangent1;
514
+
515
+ unpacked.tangents[i2 * 3 + 0] += x_resTangent2 / magTangent2;
516
+ unpacked.tangents[i2 * 3 + 1] += y_resTangent2 / magTangent2;
517
+ unpacked.tangents[i2 * 3 + 2] += z_resTangent2 / magTangent2;
518
+
519
+ unpacked.bitangents[i0 * 3 + 0] += x_resBitangent0 / magBitangent0;
520
+ unpacked.bitangents[i0 * 3 + 1] += y_resBitangent0 / magBitangent0;
521
+ unpacked.bitangents[i0 * 3 + 2] += z_resBitangent0 / magBitangent0;
522
+
523
+ unpacked.bitangents[i1 * 3 + 0] += x_resBitangent1 / magBitangent1;
524
+ unpacked.bitangents[i1 * 3 + 1] += y_resBitangent1 / magBitangent1;
525
+ unpacked.bitangents[i1 * 3 + 2] += z_resBitangent1 / magBitangent1;
526
+
527
+ unpacked.bitangents[i2 * 3 + 0] += x_resBitangent2 / magBitangent2;
528
+ unpacked.bitangents[i2 * 3 + 1] += y_resBitangent2 / magBitangent2;
529
+ unpacked.bitangents[i2 * 3 + 2] += z_resBitangent2 / magBitangent2;
530
+
531
+ // TODO: check handedness
532
+ }
533
+
534
+ this.tangents = unpacked.tangents;
535
+ this.bitangents = unpacked.bitangents;
536
+ }
537
+
538
+ /**
539
+ * @param layout - A {@link Layout} object that describes the
540
+ * desired memory layout of the generated buffer
541
+ * @return The packed array in the ... TODO
542
+ */
543
+ makeBufferData(layout: Layout): ArrayBufferWithItemSize {
544
+ const numItems = this.vertices.length / 3;
545
+ const buffer: ArrayBufferWithItemSize = new ArrayBuffer(layout.stride * numItems);
546
+ buffer.numItems = numItems;
547
+ const dataView = new DataView(buffer);
548
+ for (let i = 0, vertexOffset = 0; i < numItems; i++) {
549
+ vertexOffset = i * layout.stride;
550
+ // copy in the vertex data in the order and format given by the
551
+ // layout param
552
+ for (const attribute of layout.attributes) {
553
+ const offset = vertexOffset + layout.attributeMap[attribute.key].offset;
554
+ switch (attribute.key) {
555
+ case Layout.POSITION.key:
556
+ dataView.setFloat32(offset, this.vertices[i * 3], true);
557
+ dataView.setFloat32(offset + 4, this.vertices[i * 3 + 1], true);
558
+ dataView.setFloat32(offset + 8, this.vertices[i * 3 + 2], true);
559
+ break;
560
+ case Layout.UV.key:
561
+ dataView.setFloat32(offset, this.textures[i * 2], true);
562
+ dataView.setFloat32(offset + 4, this.textures[i * 2 + 1], true);
563
+ break;
564
+ case Layout.NORMAL.key:
565
+ dataView.setFloat32(offset, this.vertexNormals[i * 3], true);
566
+ dataView.setFloat32(offset + 4, this.vertexNormals[i * 3 + 1], true);
567
+ dataView.setFloat32(offset + 8, this.vertexNormals[i * 3 + 2], true);
568
+ break;
569
+ case Layout.MATERIAL_INDEX.key:
570
+ dataView.setInt16(offset, this.vertexMaterialIndices[i], true);
571
+ break;
572
+ case Layout.AMBIENT.key: {
573
+ const materialIndex = this.vertexMaterialIndices[i];
574
+ const material = this.materialsByIndex[materialIndex];
575
+ if (!material) {
576
+ console.warn(
577
+ 'Material "' +
578
+ this.materialNames[materialIndex] +
579
+ '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
580
+ );
581
+ break;
582
+ }
583
+ dataView.setFloat32(offset, material.ambient[0], true);
584
+ dataView.setFloat32(offset + 4, material.ambient[1], true);
585
+ dataView.setFloat32(offset + 8, material.ambient[2], true);
586
+ break;
587
+ }
588
+ case Layout.DIFFUSE.key: {
589
+ const materialIndex = this.vertexMaterialIndices[i];
590
+ const material = this.materialsByIndex[materialIndex];
591
+ if (!material) {
592
+ console.warn(
593
+ 'Material "' +
594
+ this.materialNames[materialIndex] +
595
+ '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
596
+ );
597
+ break;
598
+ }
599
+ dataView.setFloat32(offset, material.diffuse[0], true);
600
+ dataView.setFloat32(offset + 4, material.diffuse[1], true);
601
+ dataView.setFloat32(offset + 8, material.diffuse[2], true);
602
+ break;
603
+ }
604
+ case Layout.SPECULAR.key: {
605
+ const materialIndex = this.vertexMaterialIndices[i];
606
+ const material = this.materialsByIndex[materialIndex];
607
+ if (!material) {
608
+ console.warn(
609
+ 'Material "' +
610
+ this.materialNames[materialIndex] +
611
+ '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
612
+ );
613
+ break;
614
+ }
615
+ dataView.setFloat32(offset, material.specular[0], true);
616
+ dataView.setFloat32(offset + 4, material.specular[1], true);
617
+ dataView.setFloat32(offset + 8, material.specular[2], true);
618
+ break;
619
+ }
620
+ case Layout.SPECULAR_EXPONENT.key: {
621
+ const materialIndex = this.vertexMaterialIndices[i];
622
+ const material = this.materialsByIndex[materialIndex];
623
+ if (!material) {
624
+ console.warn(
625
+ 'Material "' +
626
+ this.materialNames[materialIndex] +
627
+ '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
628
+ );
629
+ break;
630
+ }
631
+ dataView.setFloat32(offset, material.specularExponent, true);
632
+ break;
633
+ }
634
+ case Layout.EMISSIVE.key: {
635
+ const materialIndex = this.vertexMaterialIndices[i];
636
+ const material = this.materialsByIndex[materialIndex];
637
+ if (!material) {
638
+ console.warn(
639
+ 'Material "' +
640
+ this.materialNames[materialIndex] +
641
+ '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
642
+ );
643
+ break;
644
+ }
645
+ dataView.setFloat32(offset, material.emissive[0], true);
646
+ dataView.setFloat32(offset + 4, material.emissive[1], true);
647
+ dataView.setFloat32(offset + 8, material.emissive[2], true);
648
+ break;
649
+ }
650
+ case Layout.TRANSMISSION_FILTER.key: {
651
+ const materialIndex = this.vertexMaterialIndices[i];
652
+ const material = this.materialsByIndex[materialIndex];
653
+ if (!material) {
654
+ console.warn(
655
+ 'Material "' +
656
+ this.materialNames[materialIndex] +
657
+ '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
658
+ );
659
+ break;
660
+ }
661
+ dataView.setFloat32(offset, material.transmissionFilter[0], true);
662
+ dataView.setFloat32(offset + 4, material.transmissionFilter[1], true);
663
+ dataView.setFloat32(offset + 8, material.transmissionFilter[2], true);
664
+ break;
665
+ }
666
+ case Layout.DISSOLVE.key: {
667
+ const materialIndex = this.vertexMaterialIndices[i];
668
+ const material = this.materialsByIndex[materialIndex];
669
+ if (!material) {
670
+ console.warn(
671
+ 'Material "' +
672
+ this.materialNames[materialIndex] +
673
+ '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
674
+ );
675
+ break;
676
+ }
677
+ dataView.setFloat32(offset, material.dissolve, true);
678
+ break;
679
+ }
680
+ case Layout.ILLUMINATION.key: {
681
+ const materialIndex = this.vertexMaterialIndices[i];
682
+ const material = this.materialsByIndex[materialIndex];
683
+ if (!material) {
684
+ console.warn(
685
+ 'Material "' +
686
+ this.materialNames[materialIndex] +
687
+ '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
688
+ );
689
+ break;
690
+ }
691
+ dataView.setInt16(offset, material.illumination, true);
692
+ break;
693
+ }
694
+ case Layout.REFRACTION_INDEX.key: {
695
+ const materialIndex = this.vertexMaterialIndices[i];
696
+ const material = this.materialsByIndex[materialIndex];
697
+ if (!material) {
698
+ console.warn(
699
+ 'Material "' +
700
+ this.materialNames[materialIndex] +
701
+ '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
702
+ );
703
+ break;
704
+ }
705
+ dataView.setFloat32(offset, material.refractionIndex, true);
706
+ break;
707
+ }
708
+ case Layout.SHARPNESS.key: {
709
+ const materialIndex = this.vertexMaterialIndices[i];
710
+ const material = this.materialsByIndex[materialIndex];
711
+ if (!material) {
712
+ console.warn(
713
+ 'Material "' +
714
+ this.materialNames[materialIndex] +
715
+ '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
716
+ );
717
+ break;
718
+ }
719
+ dataView.setFloat32(offset, material.sharpness, true);
720
+ break;
721
+ }
722
+ case Layout.ANTI_ALIASING.key: {
723
+ const materialIndex = this.vertexMaterialIndices[i];
724
+ const material = this.materialsByIndex[materialIndex];
725
+ if (!material) {
726
+ console.warn(
727
+ 'Material "' +
728
+ this.materialNames[materialIndex] +
729
+ '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
730
+ );
731
+ break;
732
+ }
733
+ dataView.setInt16(offset, material.antiAliasing ? 1 : 0, true);
734
+ break;
735
+ }
736
+ }
737
+ }
738
+ }
739
+ return buffer;
740
+ }
741
+
742
+ makeIndexBufferData(): Uint16ArrayWithItemSize {
743
+ const buffer: Uint16ArrayWithItemSize = new Uint16Array(this.indices);
744
+ buffer.numItems = this.indices.length;
745
+ return buffer;
746
+ }
747
+
748
+ makeIndexBufferDataForMaterials(...materialIndices: Array<number>): Uint16ArrayWithItemSize {
749
+ const indices: number[] = new Array<number>().concat(...materialIndices.map((mtlIdx) => this.indicesPerMaterial[mtlIdx]));
750
+ const buffer: Uint16ArrayWithItemSize = new Uint16Array(indices);
751
+ buffer.numItems = indices.length;
752
+ return buffer;
753
+ }
754
+
755
+ addMaterialLibrary(mtl: MaterialLibrary) {
756
+ for (const name in mtl.materials) {
757
+ if (!(name in this.materialIndices)) {
758
+ // This material is not referenced by the mesh
759
+ continue;
760
+ }
761
+
762
+ const material = mtl.materials[name];
763
+
764
+ // Find the material index for this material
765
+ const materialIndex = this.materialIndices[material.name];
766
+
767
+ // Put the material into the materialsByIndex object at the right
768
+ // spot as determined when the obj file was parsed
769
+ this.materialsByIndex[materialIndex] = material;
770
+ }
771
+ }
772
+ }
773
+
774
+ function* triangulate(elements: string[]) {
775
+ if (elements.length <= 3) {
776
+ yield elements;
777
+ } else if (elements.length === 4) {
778
+ yield [elements[0], elements[1], elements[2]];
779
+ yield [elements[2], elements[3], elements[0]];
780
+ } else {
781
+ for (let i = 1; i < elements.length - 1; i++) {
782
+ yield [elements[0], elements[i], elements[i + 1]];
783
+ }
784
+ }
785
+ }