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.
- package/README.md +36 -286
- package/dist/bundle.js +13 -3
- package/dist/bundle.js.map +1 -1
- package/dist/style.css +1 -0
- package/package.json +43 -44
- package/src/components/WebGLView.jsx +318 -0
- package/src/css/pixos.css +372 -0
- package/src/engine/actions/animate.js +41 -0
- package/src/engine/actions/changezone.js +135 -0
- package/src/engine/actions/chat.js +109 -0
- package/src/engine/actions/dialogue.js +90 -0
- package/src/engine/actions/face.js +22 -0
- package/src/engine/actions/greeting.js +28 -0
- package/src/engine/actions/interact.js +86 -0
- package/src/engine/actions/move.js +67 -0
- package/src/engine/actions/patrol.js +109 -0
- package/src/engine/actions/prompt.js +185 -0
- package/src/engine/actions/script.js +42 -0
- package/src/engine/core/audio/AudioSystem.js +543 -0
- package/src/engine/core/cutscene/PxcPlayer.js +956 -0
- package/src/engine/core/cutscene/manager.js +243 -0
- package/src/engine/core/database/index.js +75 -0
- package/src/engine/core/debug/index.js +371 -0
- package/src/engine/core/hud/index.js +765 -0
- package/src/engine/core/index.js +540 -0
- package/src/engine/core/input/gamepad/Controller.js +71 -0
- package/src/engine/core/input/gamepad/ControllerButtons.js +231 -0
- package/src/engine/core/input/gamepad/ControllerStick.js +173 -0
- package/src/engine/core/input/gamepad/index.js +592 -0
- package/src/engine/core/input/keyboard.js +196 -0
- package/src/engine/core/input/manager.js +485 -0
- package/src/engine/core/input/mouse.js +203 -0
- package/src/engine/core/input/touch.js +175 -0
- package/src/engine/core/mode/manager.js +199 -0
- package/src/engine/core/net/manager.js +535 -0
- package/src/engine/core/queue/action.js +83 -0
- package/src/engine/core/queue/event.js +82 -0
- package/src/engine/core/queue/index.js +44 -0
- package/src/engine/core/queue/loadable.js +33 -0
- package/src/engine/core/render/CameraEffects.js +494 -0
- package/src/engine/core/render/FrustumCuller.js +417 -0
- package/src/engine/core/render/LODManager.js +285 -0
- package/src/engine/core/render/ParticleManager.js +529 -0
- package/src/engine/core/render/TextureAtlas.js +465 -0
- package/src/engine/core/render/camera.js +338 -0
- package/src/engine/core/render/light.js +197 -0
- package/src/engine/core/render/manager.js +1079 -0
- package/src/engine/core/render/shaders.js +110 -0
- package/src/engine/core/render/skybox.js +342 -0
- package/src/engine/core/resource/manager.js +133 -0
- package/src/engine/core/resource/object.js +611 -0
- package/src/engine/core/resource/texture.js +103 -0
- package/src/engine/core/resource/tileset.js +177 -0
- package/src/engine/core/scene/avatar.js +215 -0
- package/src/engine/core/scene/speech.js +138 -0
- package/src/engine/core/scene/sprite.js +702 -0
- package/src/engine/core/scene/spritz.js +189 -0
- package/src/engine/core/scene/world.js +681 -0
- package/src/engine/core/scene/zone.js +1167 -0
- package/src/engine/core/store/index.js +110 -0
- package/src/engine/dynamic/animatedSprite.js +64 -0
- package/src/engine/dynamic/animatedTile.js +98 -0
- package/src/engine/dynamic/avatar.js +110 -0
- package/src/engine/dynamic/map.js +174 -0
- package/src/engine/dynamic/sprite.js +255 -0
- package/src/engine/dynamic/spritz.js +119 -0
- package/src/engine/events/EventSystem.js +609 -0
- package/src/engine/events/camera.js +142 -0
- package/src/engine/events/chat.js +75 -0
- package/src/engine/events/menu.js +186 -0
- package/src/engine/scripting/CallbackManager.js +514 -0
- package/src/engine/scripting/PixoScriptInterpreter.js +81 -0
- package/src/engine/scripting/PixoScriptLibrary.js +704 -0
- package/src/engine/shaders/effects/index.js +450 -0
- package/src/engine/shaders/fs.js +222 -0
- package/src/engine/shaders/particles/fs.js +41 -0
- package/src/engine/shaders/particles/vs.js +61 -0
- package/src/engine/shaders/picker/fs.js +34 -0
- package/src/engine/shaders/picker/init.js +62 -0
- package/src/engine/shaders/picker/vs.js +42 -0
- package/src/engine/shaders/pxsl/README.md +250 -0
- package/src/engine/shaders/pxsl/index.js +25 -0
- package/src/engine/shaders/pxsl/library.js +608 -0
- package/src/engine/shaders/pxsl/manager.js +338 -0
- package/src/engine/shaders/pxsl/specification.js +363 -0
- package/src/engine/shaders/pxsl/transpiler.js +753 -0
- package/src/engine/shaders/skybox/cosmic/fs.js +147 -0
- package/src/engine/shaders/skybox/cosmic/vs.js +23 -0
- package/src/engine/shaders/skybox/matrix/fs.js +127 -0
- package/src/engine/shaders/skybox/matrix/vs.js +23 -0
- package/src/engine/shaders/skybox/morning/fs.js +109 -0
- package/src/engine/shaders/skybox/morning/vs.js +23 -0
- package/src/engine/shaders/skybox/neon/fs.js +119 -0
- package/src/engine/shaders/skybox/neon/vs.js +23 -0
- package/src/engine/shaders/skybox/sky/fs.js +114 -0
- package/src/engine/shaders/skybox/sky/vs.js +23 -0
- package/src/engine/shaders/skybox/sunset/fs.js +101 -0
- package/src/engine/shaders/skybox/sunset/vs.js +23 -0
- package/src/engine/shaders/transition/blur/fs.js +42 -0
- package/src/engine/shaders/transition/blur/vs.js +26 -0
- package/src/engine/shaders/transition/cross/fs.js +36 -0
- package/src/engine/shaders/transition/cross/vs.js +26 -0
- package/src/engine/shaders/transition/crossBlur/fs.js +41 -0
- package/src/engine/shaders/transition/crossBlur/vs.js +25 -0
- package/src/engine/shaders/transition/dissolve/fs.js +78 -0
- package/src/engine/shaders/transition/dissolve/vs.js +24 -0
- package/src/engine/shaders/transition/fade/fs.js +31 -0
- package/src/engine/shaders/transition/fade/vs.js +27 -0
- package/src/engine/shaders/transition/iris/fs.js +52 -0
- package/src/engine/shaders/transition/iris/vs.js +24 -0
- package/src/engine/shaders/transition/pixelate/fs.js +44 -0
- package/src/engine/shaders/transition/pixelate/vs.js +24 -0
- package/src/engine/shaders/transition/slide/fs.js +53 -0
- package/src/engine/shaders/transition/slide/vs.js +24 -0
- package/src/engine/shaders/transition/swirl/fs.js +39 -0
- package/src/engine/shaders/transition/swirl/vs.js +26 -0
- package/src/engine/shaders/transition/wipe/fs.js +50 -0
- package/src/engine/shaders/transition/wipe/vs.js +24 -0
- package/src/engine/shaders/vs.js +60 -0
- package/src/engine/utils/CameraController.js +506 -0
- package/src/engine/utils/ObjHelper.js +551 -0
- package/src/engine/utils/debug-logger.js +110 -0
- package/src/engine/utils/enums.js +305 -0
- package/src/engine/utils/generator.js +156 -0
- package/src/engine/utils/index.js +21 -0
- package/src/engine/utils/loaders/ActionLoader.js +77 -0
- package/src/engine/utils/loaders/AudioLoader.js +157 -0
- package/src/engine/utils/loaders/EventLoader.js +66 -0
- package/src/engine/utils/loaders/ObjectLoader.js +67 -0
- package/src/engine/utils/loaders/SpriteLoader.js +77 -0
- package/src/engine/utils/loaders/TilesetLoader.js +103 -0
- package/src/engine/utils/loaders/index.js +21 -0
- package/src/engine/utils/math/matrix4.js +367 -0
- package/src/engine/utils/math/vector.js +458 -0
- package/src/engine/utils/obj/_old_js/index.js +46 -0
- package/src/engine/utils/obj/_old_js/layout.js +308 -0
- package/src/engine/utils/obj/_old_js/material.js +711 -0
- package/src/engine/utils/obj/_old_js/mesh.js +761 -0
- package/src/engine/utils/obj/_old_js/utils.js +647 -0
- package/src/engine/utils/obj/index.js +24 -0
- package/src/engine/utils/obj/js/index.js +277 -0
- package/src/engine/utils/obj/js/loader.js +232 -0
- package/src/engine/utils/obj/layout.js +246 -0
- package/src/engine/utils/obj/material.js +665 -0
- package/src/engine/utils/obj/mesh.js +657 -0
- package/src/engine/utils/obj/ts/index.ts +72 -0
- package/src/engine/utils/obj/ts/layout.ts +265 -0
- package/src/engine/utils/obj/ts/material.ts +760 -0
- package/src/engine/utils/obj/ts/mesh.ts +785 -0
- package/src/engine/utils/obj/ts/utils.ts +501 -0
- package/src/engine/utils/obj/utils.js +428 -0
- package/src/engine/utils/resources.js +18 -0
- package/src/index.jsx +55 -0
- package/src/spritz/player.js +18 -0
- package/src/spritz/readme.md +18 -0
- package/LICENSE +0 -437
- 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
|
+
}
|