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,277 @@
1
+ import { parseOBJ, parseMTL } from "./loader";
2
+ async function main() {
3
+ // Get A WebGL context
4
+ /** @type {HTMLCanvasElement} */
5
+ const canvas = document.querySelector("#canvas");
6
+ const gl = canvas.getContext("webgl2");
7
+ if (!gl) {
8
+ return;
9
+ }
10
+
11
+ // Tell the twgl to match position with a_position etc..
12
+ twgl.setAttributePrefix("a_");
13
+
14
+ const vs = `#version 300 es
15
+ in vec4 a_position;
16
+ in vec3 a_normal;
17
+ in vec2 a_texcoord;
18
+ in vec4 a_color;
19
+
20
+ uniform mat4 u_projection;
21
+ uniform mat4 u_view;
22
+ uniform mat4 u_world;
23
+ uniform vec3 u_viewWorldPosition;
24
+
25
+ out vec3 v_normal;
26
+ out vec3 v_surfaceToView;
27
+ out vec2 v_texcoord;
28
+ out vec4 v_color;
29
+
30
+ void main() {
31
+ vec4 worldPosition = u_world * a_position;
32
+ gl_Position = u_projection * u_view * worldPosition;
33
+ v_surfaceToView = u_viewWorldPosition - worldPosition.xyz;
34
+ v_normal = mat3(u_world) * a_normal;
35
+ v_texcoord = a_texcoord;
36
+ v_color = a_color;
37
+ }
38
+ `;
39
+
40
+ const fs = `#version 300 es
41
+ precision highp float;
42
+
43
+ in vec3 v_normal;
44
+ in vec3 v_surfaceToView;
45
+ in vec2 v_texcoord;
46
+ in vec4 v_color;
47
+
48
+ uniform vec3 diffuse;
49
+ uniform sampler2D diffuseMap;
50
+ uniform vec3 ambient;
51
+ uniform vec3 emissive;
52
+ uniform vec3 specular;
53
+ uniform sampler2D specularMap;
54
+ uniform float shininess;
55
+ uniform float opacity;
56
+ uniform vec3 u_lightDirection;
57
+ uniform vec3 u_ambientLight;
58
+
59
+ out vec4 outColor;
60
+
61
+ void main () {
62
+ vec3 normal = normalize(v_normal);
63
+
64
+ vec3 surfaceToViewDirection = normalize(v_surfaceToView);
65
+ vec3 halfVector = normalize(u_lightDirection + surfaceToViewDirection);
66
+
67
+ float fakeLight = dot(u_lightDirection, normal) * .5 + .5;
68
+ float specularLight = clamp(dot(normal, halfVector), 0.0, 1.0);
69
+ vec4 specularMapColor = texture(specularMap, v_texcoord);
70
+ vec3 effectiveSpecular = specular * specularMapColor.rgb;
71
+
72
+ vec4 diffuseMapColor = texture(diffuseMap, v_texcoord);
73
+ vec3 effectiveDiffuse = diffuse * diffuseMapColor.rgb * v_color.rgb;
74
+ float effectiveOpacity = opacity * diffuseMapColor.a * v_color.a;
75
+
76
+ outColor = vec4(
77
+ emissive +
78
+ ambient * u_ambientLight +
79
+ effectiveDiffuse * fakeLight +
80
+ effectiveSpecular * pow(specularLight, shininess),
81
+ effectiveOpacity);
82
+ }
83
+ `;
84
+
85
+ // compiles and links the shaders, looks up attribute and uniform locations
86
+ const meshProgramInfo = twgl.createProgramInfo(gl, [vs, fs]);
87
+
88
+ const objHref = "https://webgl2fundamentals.org/webgl/resources/models/windmill/windmill.obj";
89
+ const response = await fetch(objHref);
90
+ const text = await response.text();
91
+ const obj = parseOBJ(text);
92
+ const baseHref = new URL(objHref, window.location.href);
93
+ const matTexts = await Promise.all(
94
+ obj.materialLibs.map(async (filename) => {
95
+ const matHref = new URL(filename, baseHref).href;
96
+ const response = await fetch(matHref);
97
+ return await response.text();
98
+ })
99
+ );
100
+ const materials = parseMTL(matTexts.join("\n"));
101
+
102
+ const textures = {
103
+ defaultWhite: twgl.createTexture(gl, { src: [255, 255, 255, 255] }),
104
+ };
105
+
106
+ // load texture for materials
107
+ for (const material of Object.values(materials)) {
108
+ Object.entries(material)
109
+ .filter(([key]) => key.endsWith("Map"))
110
+ .forEach(([key, filename]) => {
111
+ let texture = textures[filename];
112
+ if (!texture) {
113
+ const textureHref = new URL(filename, baseHref).href;
114
+ texture = twgl.createTexture(gl, { src: textureHref, flipY: true });
115
+ textures[filename] = texture;
116
+ }
117
+ material[key] = texture;
118
+ });
119
+ }
120
+
121
+ // hack the materials so we can see the specular map
122
+ Object.values(materials).forEach((m) => {
123
+ m.shininess = 25;
124
+ m.specular = [3, 2, 1];
125
+ });
126
+
127
+ const defaultMaterial = {
128
+ diffuse: [1, 1, 1],
129
+ diffuseMap: textures.defaultWhite,
130
+ ambient: [0, 0, 0],
131
+ specular: [1, 1, 1],
132
+ specularMap: textures.defaultWhite,
133
+ shininess: 400,
134
+ opacity: 1,
135
+ };
136
+
137
+ const parts = obj.geometries.map(({ material, data }) => {
138
+ // Because data is just named arrays like this
139
+ //
140
+ // {
141
+ // position: [...],
142
+ // texcoord: [...],
143
+ // normal: [...],
144
+ // }
145
+ //
146
+ // and because those names match the attributes in our vertex
147
+ // shader we can pass it directly into `createBufferInfoFromArrays`
148
+ // from the article "less code more fun".
149
+
150
+ if (data.color) {
151
+ if (data.position.length === data.color.length) {
152
+ // it's 3. The our helper library assumes 4 so we need
153
+ // to tell it there are only 3.
154
+ data.color = { numComponents: 3, data: data.color };
155
+ }
156
+ } else {
157
+ // there are no vertex colors so just use constant white
158
+ data.color = { value: [1, 1, 1, 1] };
159
+ }
160
+
161
+ // create a buffer for each array by calling
162
+ // gl.createBuffer, gl.bindBuffer, gl.bufferData
163
+ const bufferInfo = twgl.createBufferInfoFromArrays(gl, data);
164
+ const vao = twgl.createVAOFromBufferInfo(gl, meshProgramInfo, bufferInfo);
165
+ return {
166
+ material: {
167
+ ...defaultMaterial,
168
+ ...materials[material],
169
+ },
170
+ bufferInfo,
171
+ vao,
172
+ };
173
+ });
174
+
175
+ function getExtents(positions) {
176
+ const min = positions.slice(0, 3);
177
+ const max = positions.slice(0, 3);
178
+ for (let i = 3; i < positions.length; i += 3) {
179
+ for (let j = 0; j < 3; ++j) {
180
+ const v = positions[i + j];
181
+ min[j] = Math.min(v, min[j]);
182
+ max[j] = Math.max(v, max[j]);
183
+ }
184
+ }
185
+ return { min, max };
186
+ }
187
+
188
+ function getGeometriesExtents(geometries) {
189
+ return geometries.reduce(
190
+ ({ min, max }, { data }) => {
191
+ const minMax = getExtents(data.position);
192
+ return {
193
+ min: min.map((min, ndx) => Math.min(minMax.min[ndx], min)),
194
+ max: max.map((max, ndx) => Math.max(minMax.max[ndx], max)),
195
+ };
196
+ },
197
+ {
198
+ min: Array(3).fill(Number.POSITIVE_INFINITY),
199
+ max: Array(3).fill(Number.NEGATIVE_INFINITY),
200
+ }
201
+ );
202
+ }
203
+
204
+ const extents = getGeometriesExtents(obj.geometries);
205
+ const range = m4.subtractVectors(extents.max, extents.min);
206
+ // amount to move the object so its center is at the origin
207
+ const objOffset = m4.scaleVector(m4.addVectors(extents.min, m4.scaleVector(range, 0.5)), -1);
208
+ const cameraTarget = [0, 0, 0];
209
+ // figure out how far away to move the camera so we can likely
210
+ // see the object.
211
+ const radius = m4.length(range) * 1.2;
212
+ const cameraPosition = m4.addVectors(cameraTarget, [0, 0, radius]);
213
+ // Set zNear and zFar to something hopefully appropriate
214
+ // for the size of this object.
215
+ const zNear = radius / 100;
216
+ const zFar = radius * 3;
217
+
218
+ function degToRad(deg) {
219
+ return (deg * Math.PI) / 180;
220
+ }
221
+
222
+ function render(time) {
223
+ time *= 0.001; // convert to seconds
224
+
225
+ twgl.resizeCanvasToDisplaySize(gl.canvas);
226
+ gl.viewport(0, 0, gl.canvas.clientWidth, gl.canvas.clientHeight);
227
+ gl.enable(gl.DEPTH_TEST);
228
+
229
+ const fieldOfViewRadians = degToRad(60);
230
+ const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
231
+ const projection = m4.perspective(fieldOfViewRadians, aspect, zNear, zFar);
232
+
233
+ const up = [0, 1, 0];
234
+ // Compute the camera's matrix using look at.
235
+ const camera = m4.lookAt(cameraPosition, cameraTarget, up);
236
+
237
+ // Make a view matrix from the camera matrix.
238
+ const view = m4.inverse(camera);
239
+
240
+ const sharedUniforms = {
241
+ u_lightDirection: m4.normalize([-1, 3, 5]),
242
+ u_view: view,
243
+ u_projection: projection,
244
+ u_viewWorldPosition: cameraPosition,
245
+ };
246
+
247
+ gl.useProgram(meshProgramInfo.program);
248
+
249
+ // calls gl.uniform
250
+ twgl.setUniforms(meshProgramInfo, sharedUniforms);
251
+
252
+ // compute the world matrix once since all parts
253
+ // are at the same space.
254
+ let u_world = m4.yRotation(time);
255
+ u_world = m4.translate(u_world, ...objOffset);
256
+
257
+ for (const { bufferInfo, vao, material } of parts) {
258
+ // set the attributes for this part.
259
+ gl.bindVertexArray(vao);
260
+ // calls gl.uniform
261
+ twgl.setUniforms(
262
+ meshProgramInfo,
263
+ {
264
+ u_world,
265
+ },
266
+ material
267
+ );
268
+ // calls gl.drawArrays or gl.drawElements
269
+ twgl.drawBufferInfo(gl, bufferInfo);
270
+ }
271
+
272
+ requestAnimationFrame(render);
273
+ }
274
+ requestAnimationFrame(render);
275
+ }
276
+
277
+ main();
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+
3
+ // This is not a full .obj parser.
4
+ // see http://paulbourke.net/dataformats/obj/
5
+
6
+ export function parseOBJ(text) {
7
+ // because indices are base 1 let's just fill in the 0th data
8
+ const objPositions = [[0, 0, 0]];
9
+ const objTexcoords = [[0, 0]];
10
+ const objNormals = [[0, 0, 0]];
11
+ const objColors = [[0, 0, 0]];
12
+
13
+ // same order as `f` indices
14
+ const objVertexData = [
15
+ objPositions,
16
+ objTexcoords,
17
+ objNormals,
18
+ objColors,
19
+ ];
20
+
21
+ // same order as `f` indices
22
+ let webglVertexData = [
23
+ [], // positions
24
+ [], // texcoords
25
+ [], // normals
26
+ [], // colors
27
+ ];
28
+
29
+ const materialLibs = [];
30
+ const geometries = [];
31
+ let geometry;
32
+ let groups = ['default'];
33
+ let material = 'default';
34
+ let object = 'default';
35
+
36
+ const noop = () => {};
37
+
38
+ function newGeometry() {
39
+ // If there is an existing geometry and it's
40
+ // not empty then start a new one.
41
+ if (geometry && geometry.data.position.length) {
42
+ geometry = undefined;
43
+ }
44
+ }
45
+
46
+ function setGeometry() {
47
+ if (!geometry) {
48
+ const position = [];
49
+ const texcoord = [];
50
+ const normal = [];
51
+ const color = [];
52
+ webglVertexData = [
53
+ position,
54
+ texcoord,
55
+ normal,
56
+ color,
57
+ ];
58
+ geometry = {
59
+ object,
60
+ groups,
61
+ material,
62
+ data: {
63
+ position,
64
+ texcoord,
65
+ normal,
66
+ color,
67
+ },
68
+ };
69
+ geometries.push(geometry);
70
+ }
71
+ }
72
+
73
+ function addVertex(vert) {
74
+ const ptn = vert.split('/');
75
+ ptn.forEach((objIndexStr, i) => {
76
+ if (!objIndexStr) {
77
+ return;
78
+ }
79
+ const objIndex = parseInt(objIndexStr);
80
+ const index = objIndex + (objIndex >= 0 ? 0 : objVertexData[i].length);
81
+ webglVertexData[i].push(...objVertexData[i][index]);
82
+ // if this is the position index (index 0) and we parsed
83
+ // vertex colors then copy the vertex colors to the webgl vertex color data
84
+ if (i === 0 && objColors.length > 1) {
85
+ geometry.data.color.push(...objColors[index]);
86
+ }
87
+ });
88
+ }
89
+
90
+ const keywords = {
91
+ v(parts) {
92
+ // if there are more than 3 values here they are vertex colors
93
+ if (parts.length > 3) {
94
+ objPositions.push(parts.slice(0, 3).map(parseFloat));
95
+ objColors.push(parts.slice(3).map(parseFloat));
96
+ } else {
97
+ objPositions.push(parts.map(parseFloat));
98
+ }
99
+ },
100
+ vn(parts) {
101
+ objNormals.push(parts.map(parseFloat));
102
+ },
103
+ vt(parts) {
104
+ // should check for missing v and extra w?
105
+ objTexcoords.push(parts.map(parseFloat));
106
+ },
107
+ f(parts) {
108
+ setGeometry();
109
+ const numTriangles = parts.length - 2;
110
+ for (let tri = 0; tri < numTriangles; ++tri) {
111
+ addVertex(parts[0]);
112
+ addVertex(parts[tri + 1]);
113
+ addVertex(parts[tri + 2]);
114
+ }
115
+ },
116
+ s: noop, // smoothing group
117
+ mtllib(parts, unparsedArgs) {
118
+ // the spec says there can be multiple filenames here
119
+ // but many exist with spaces in a single filename
120
+ materialLibs.push(unparsedArgs);
121
+ },
122
+ usemtl(parts, unparsedArgs) {
123
+ material = unparsedArgs;
124
+ newGeometry();
125
+ },
126
+ g(parts) {
127
+ groups = parts;
128
+ newGeometry();
129
+ },
130
+ o(parts, unparsedArgs) {
131
+ object = unparsedArgs;
132
+ newGeometry();
133
+ },
134
+ };
135
+
136
+ const keywordRE = /(\w*)(?: )*(.*)/;
137
+ const lines = text.split('\n');
138
+ for (let lineNo = 0; lineNo < lines.length; ++lineNo) {
139
+ const line = lines[lineNo].trim();
140
+ if (line === '' || line.startsWith('#')) {
141
+ continue;
142
+ }
143
+ const m = keywordRE.exec(line);
144
+ if (!m) {
145
+ continue;
146
+ }
147
+ const [, keyword, unparsedArgs] = m;
148
+ const parts = line.split(/\s+/).slice(1);
149
+ const handler = keywords[keyword];
150
+ if (!handler) {
151
+ console.warn('unhandled keyword:', keyword); // eslint-disable-line no-console
152
+ continue;
153
+ }
154
+ handler(parts, unparsedArgs);
155
+ }
156
+
157
+ // remove any arrays that have no entries.
158
+ for (const geometry of geometries) {
159
+ geometry.data = Object.fromEntries(
160
+ Object.entries(geometry.data).filter(([, array]) => array.length > 0));
161
+ }
162
+
163
+ return {
164
+ geometries,
165
+ materialLibs,
166
+ };
167
+ }
168
+
169
+ export function parseMapArgs(unparsedArgs) {
170
+ // TODO: handle options
171
+ return unparsedArgs;
172
+ }
173
+
174
+ export function parseMTL(text) {
175
+ const materials = {};
176
+ let material;
177
+
178
+ const keywords = {
179
+ newmtl(parts, unparsedArgs) {
180
+ material = {};
181
+ materials[unparsedArgs] = material;
182
+ },
183
+ /* eslint brace-style:0 */
184
+ Ns(parts) { material.shininess = parseFloat(parts[0]); },
185
+ Ka(parts) { material.ambient = parts.map(parseFloat); },
186
+ Kd(parts) { material.diffuse = parts.map(parseFloat); },
187
+ Ks(parts) { material.specular = parts.map(parseFloat); },
188
+ Ke(parts) { material.emissive = parts.map(parseFloat); },
189
+ map_Kd(parts, unparsedArgs) { material.diffuseMap = parseMapArgs(unparsedArgs); },
190
+ map_Ns(parts, unparsedArgs) { material.specularMap = parseMapArgs(unparsedArgs); },
191
+ map_Bump(parts, unparsedArgs) { material.normalMap = parseMapArgs(unparsedArgs); },
192
+ Ni(parts) { material.opticalDensity = parseFloat(parts[0]); },
193
+ d(parts) { material.opacity = parseFloat(parts[0]); },
194
+ illum(parts) { material.illum = parseInt(parts[0]); },
195
+ };
196
+
197
+ const keywordRE = /(\w*)(?: )*(.*)/;
198
+ const lines = text.split('\n');
199
+ for (let lineNo = 0; lineNo < lines.length; ++lineNo) {
200
+ const line = lines[lineNo].trim();
201
+ if (line === '' || line.startsWith('#')) {
202
+ continue;
203
+ }
204
+ const m = keywordRE.exec(line);
205
+ if (!m) {
206
+ continue;
207
+ }
208
+ const [, keyword, unparsedArgs] = m;
209
+ const parts = line.split(/\s+/).slice(1);
210
+ const handler = keywords[keyword];
211
+ if (!handler) {
212
+ console.warn('unhandled keyword:', keyword); // eslint-disable-line no-console
213
+ continue;
214
+ }
215
+ handler(parts, unparsedArgs);
216
+ }
217
+
218
+ return materials;
219
+ }
220
+
221
+ export function makeIndexedIndicesFn(arrays) {
222
+ const indices = arrays.indices;
223
+ let ndx = 0;
224
+ const fn = function() {
225
+ return indices[ndx++];
226
+ };
227
+ fn.reset = function() {
228
+ ndx = 0;
229
+ };
230
+ fn.numElements = indices.length;
231
+ return fn;
232
+ }