poppygl 0.0.18 → 0.0.19
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 +4 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.js +410 -221
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -53,6 +53,10 @@ const png = await renderGLTFToPNGBufferFromGLBBuffer(glb, {
|
|
|
53
53
|
- `supersampling`: render at `width * supersampling` / `height * supersampling`, then downsample (default `1`).
|
|
54
54
|
- `fov`: vertical field of view in degrees (defaults to `35`).
|
|
55
55
|
- `camPos` and `lookAt`: override the auto-framed camera position and target.
|
|
56
|
+
- `debugPoints`: optional `{ label, position }[]` overlay rendered on top of the final PNG for world-space debugging markers.
|
|
57
|
+
- `debugFontSize`: optional pixel size for `debugPoints` labels; when omitted the renderer derives a size from the image dimensions.
|
|
58
|
+
- `debugPointColor`: optional RGB tuple for debug point markers.
|
|
59
|
+
- `debugLabelColor`: optional RGB tuple for debug point labels.
|
|
56
60
|
- `lightDir`: normalized directional light vector (defaults to a top-right key light).
|
|
57
61
|
- `ambient`: ambient lighting contribution (0–1, defaults to `0.2`).
|
|
58
62
|
- `gamma`: gamma correction applied to the output (defaults to `2.2`).
|
package/dist/index.d.ts
CHANGED
|
@@ -53,6 +53,14 @@ interface GLTFScene {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
declare const DEFAULT_LIGHT_DIR: readonly [-0.4, -0.9, -0.2];
|
|
56
|
+
interface DebugPoint {
|
|
57
|
+
label: string;
|
|
58
|
+
position: {
|
|
59
|
+
x: number;
|
|
60
|
+
y: number;
|
|
61
|
+
z: number;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
56
64
|
/**
|
|
57
65
|
* Convert a hex color string to RGB array with values 0-1
|
|
58
66
|
* @param hex - Hex color string like "#ffffff" or "ffffff"
|
|
@@ -72,6 +80,10 @@ interface RenderOptions {
|
|
|
72
80
|
lookAt?: readonly [number, number, number] | null;
|
|
73
81
|
backgroundColor?: readonly [number, number, number] | string | null;
|
|
74
82
|
grid?: boolean | GridOptions;
|
|
83
|
+
debugPoints?: DebugPoint[] | null;
|
|
84
|
+
debugFontSize?: number | null;
|
|
85
|
+
debugPointColor?: readonly [number, number, number] | null;
|
|
86
|
+
debugLabelColor?: readonly [number, number, number] | null;
|
|
75
87
|
}
|
|
76
88
|
type RenderOptionsInput = Partial<RenderOptions>;
|
|
77
89
|
declare const DEFAULT_RENDER_OPTIONS: RenderOptions;
|
|
@@ -188,4 +200,4 @@ declare function renderGLTFToPNGBuffer(gltfPathOrString: string, options?: Rende
|
|
|
188
200
|
|
|
189
201
|
declare function renderSceneFromGLTF(scene: GLTFScene, options?: RenderOptionsInput, imageFactory?: ImageFactory): RenderResult;
|
|
190
202
|
|
|
191
|
-
export { type BitmapLike, type Camera, DEFAULT_LIGHT_DIR, DEFAULT_RENDER_OPTIONS, type DrawCall, type FetchLike, type GLTFResources, type GLTFScene, type GridOptions, type ImageFactory, type LightSettings, type LoadGLTFWithResourcesFromURLOptions, type Material, type MutableRGBA, type RGBA, type RenderGLTFToPNGBufferFromGLBBufferOptions, type RenderGLTFToPNGBufferFromURLOptions, type RenderOptions, type RenderOptionsInput, type RenderResult, SoftwareRenderer, bufferFromDataURI, buildCamera, computeSmoothNormals, computeWorldAABB, createGrid, createInfiniteGrid, createSceneFromGLTF, createUint8Bitmap, decodeImageFromBuffer, drawInfiniteGrid, encodePNGToBuffer, getDefaultRenderOptions, hexToRgb, isJPG, isPNG, loadGLTFWithResourcesFromURL, pureImageFactory, renderDrawCalls, renderGLTFToPNGBuffer, renderGLTFToPNGBufferFromGLBBuffer, renderGLTFToPNGBufferFromURL, renderSceneFromGLTF, resolveRenderOptions };
|
|
203
|
+
export { type BitmapLike, type Camera, DEFAULT_LIGHT_DIR, DEFAULT_RENDER_OPTIONS, type DebugPoint, type DrawCall, type FetchLike, type GLTFResources, type GLTFScene, type GridOptions, type ImageFactory, type LightSettings, type LoadGLTFWithResourcesFromURLOptions, type Material, type MutableRGBA, type RGBA, type RenderGLTFToPNGBufferFromGLBBufferOptions, type RenderGLTFToPNGBufferFromURLOptions, type RenderOptions, type RenderOptionsInput, type RenderResult, SoftwareRenderer, bufferFromDataURI, buildCamera, computeSmoothNormals, computeWorldAABB, createGrid, createInfiniteGrid, createSceneFromGLTF, createUint8Bitmap, decodeImageFromBuffer, drawInfiniteGrid, encodePNGToBuffer, getDefaultRenderOptions, hexToRgb, isJPG, isPNG, loadGLTFWithResourcesFromURL, pureImageFactory, renderDrawCalls, renderGLTFToPNGBuffer, renderGLTFToPNGBufferFromGLBBuffer, renderGLTFToPNGBufferFromURL, renderSceneFromGLTF, resolveRenderOptions };
|
package/dist/index.js
CHANGED
|
@@ -174,207 +174,16 @@ function downsampleBitmap(source, targetWidth, targetHeight, imageFactory) {
|
|
|
174
174
|
return target;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
// lib/render/
|
|
178
|
-
import {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// lib/utils/clamp.ts
|
|
187
|
-
var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
|
|
188
|
-
|
|
189
|
-
// lib/render/drawInfiniteGrid.ts
|
|
190
|
-
function computeGridLineIntensity(params) {
|
|
191
|
-
const rx = params.world_x / params.cell_size;
|
|
192
|
-
const rz = params.world_z / params.cell_size;
|
|
193
|
-
const fract_x = rx - Math.floor(rx + 0.5);
|
|
194
|
-
const fract_z = rz - Math.floor(rz + 0.5);
|
|
195
|
-
const grid_x = Math.abs(fract_x);
|
|
196
|
-
const grid_z = Math.abs(fract_z);
|
|
197
|
-
const pixel_size_in_world = params.dist / Math.min(params.screen_width, params.screen_height);
|
|
198
|
-
const fwidth_x = pixel_size_in_world / params.cell_size;
|
|
199
|
-
const fwidth_z = pixel_size_in_world / params.cell_size;
|
|
200
|
-
const line_x = grid_x / (fwidth_x + 1e-4);
|
|
201
|
-
const line_z = grid_z / (fwidth_z + 1e-4);
|
|
202
|
-
const line = Math.min(line_x, line_z);
|
|
203
|
-
return 1 - Math.min(line * params.line_thickness_multiplier, 1);
|
|
204
|
-
}
|
|
205
|
-
function computeGridFadeAlpha(params) {
|
|
206
|
-
const fade_start = params.fade_distance;
|
|
207
|
-
const fade_end = params.fade_distance * params.fade_strength;
|
|
208
|
-
return 1 - Math.min(
|
|
209
|
-
1,
|
|
210
|
-
Math.max(0, (params.dist - fade_start) / (fade_end - fade_start))
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
function drawInfiniteGrid(software_renderer, params) {
|
|
214
|
-
const grid_y = params.grid_y ?? 0;
|
|
215
|
-
const cell_size = params.cell_size ?? 1;
|
|
216
|
-
const section_size = params.section_size ?? 10;
|
|
217
|
-
const fade_distance = params.fade_distance ?? 100;
|
|
218
|
-
const fade_strength = params.fade_strength ?? 1.5;
|
|
219
|
-
const grid_color = params.grid_color ?? [0.93, 0.93, 0.93];
|
|
220
|
-
const section_color = params.section_color ?? [0.8, 0.8, 1];
|
|
221
|
-
const gamma_out = params.gamma_out ?? true;
|
|
222
|
-
const camera = params.camera;
|
|
223
|
-
const view = camera.view;
|
|
224
|
-
const proj = camera.proj;
|
|
225
|
-
const inv_view = mat43.create();
|
|
226
|
-
mat43.invert(inv_view, view);
|
|
227
|
-
const cam_pos_x = inv_view[12];
|
|
228
|
-
const cam_pos_y = inv_view[13];
|
|
229
|
-
const cam_pos_z = inv_view[14];
|
|
230
|
-
const vp = mat43.create();
|
|
231
|
-
mat43.multiply(vp, proj, view);
|
|
232
|
-
const inv_vp = mat43.create();
|
|
233
|
-
mat43.invert(inv_vp, vp);
|
|
234
|
-
for (let y = 0; y < software_renderer.height; y++) {
|
|
235
|
-
for (let x = 0; x < software_renderer.width; x++) {
|
|
236
|
-
const ndc_x = x / (software_renderer.width - 1) * 2 - 1;
|
|
237
|
-
const ndc_y = 1 - y / (software_renderer.height - 1) * 2;
|
|
238
|
-
const near_point = vec42.fromValues(ndc_x, ndc_y, -1, 1);
|
|
239
|
-
const far_point = vec42.fromValues(ndc_x, ndc_y, 1, 1);
|
|
240
|
-
vec42.transformMat4(near_point, near_point, inv_vp);
|
|
241
|
-
vec42.transformMat4(far_point, far_point, inv_vp);
|
|
242
|
-
near_point[0] /= near_point[3];
|
|
243
|
-
near_point[1] /= near_point[3];
|
|
244
|
-
near_point[2] /= near_point[3];
|
|
245
|
-
far_point[0] /= far_point[3];
|
|
246
|
-
far_point[1] /= far_point[3];
|
|
247
|
-
far_point[2] /= far_point[3];
|
|
248
|
-
const ray_dir_x = far_point[0] - near_point[0];
|
|
249
|
-
const ray_dir_y = far_point[1] - near_point[1];
|
|
250
|
-
const ray_dir_z = far_point[2] - near_point[2];
|
|
251
|
-
if (Math.abs(ray_dir_y) < 1e-6) continue;
|
|
252
|
-
const t = (grid_y - near_point[1]) / ray_dir_y;
|
|
253
|
-
if (t < 0 || t > 1) continue;
|
|
254
|
-
const world_x = near_point[0] + t * ray_dir_x;
|
|
255
|
-
const world_z = near_point[2] + t * ray_dir_z;
|
|
256
|
-
const world_pt = vec42.fromValues(world_x, grid_y, world_z, 1);
|
|
257
|
-
const clip_pt = vec42.create();
|
|
258
|
-
vec42.transformMat4(clip_pt, world_pt, vp);
|
|
259
|
-
const ndc_z = clip_pt[2] / clip_pt[3];
|
|
260
|
-
const depth_01 = ndc_z * 0.5 + 0.5;
|
|
261
|
-
const di = y * software_renderer.width + x;
|
|
262
|
-
if (depth_01 >= software_renderer.depth[di]) continue;
|
|
263
|
-
const dx = world_x - cam_pos_x;
|
|
264
|
-
const dy = grid_y - cam_pos_y;
|
|
265
|
-
const dz = world_z - cam_pos_z;
|
|
266
|
-
const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
267
|
-
const alpha = computeGridFadeAlpha({
|
|
268
|
-
dist,
|
|
269
|
-
fade_distance,
|
|
270
|
-
fade_strength
|
|
271
|
-
});
|
|
272
|
-
if (alpha <= 0) continue;
|
|
273
|
-
const g1 = computeGridLineIntensity({
|
|
274
|
-
world_x,
|
|
275
|
-
world_z,
|
|
276
|
-
cell_size,
|
|
277
|
-
dist,
|
|
278
|
-
screen_width: software_renderer.width,
|
|
279
|
-
screen_height: software_renderer.height,
|
|
280
|
-
line_thickness_multiplier: 0.8
|
|
281
|
-
});
|
|
282
|
-
const g2 = computeGridLineIntensity({
|
|
283
|
-
world_x,
|
|
284
|
-
world_z,
|
|
285
|
-
cell_size: section_size,
|
|
286
|
-
dist,
|
|
287
|
-
screen_width: software_renderer.width,
|
|
288
|
-
screen_height: software_renderer.height,
|
|
289
|
-
line_thickness_multiplier: 0.5
|
|
290
|
-
// Thicker section lines
|
|
291
|
-
});
|
|
292
|
-
const grid_strength = Math.max(g1, g2);
|
|
293
|
-
if (grid_strength <= 0) continue;
|
|
294
|
-
const final_r = grid_color[0] * (1 - g2) + section_color[0] * g2;
|
|
295
|
-
const final_g = grid_color[1] * (1 - g2) + section_color[1] * g2;
|
|
296
|
-
const final_b = grid_color[2] * (1 - g2) + section_color[2] * g2;
|
|
297
|
-
const final_alpha = grid_strength * alpha;
|
|
298
|
-
if (final_alpha <= 0.01) continue;
|
|
299
|
-
let out_r = final_r;
|
|
300
|
-
let out_g = final_g;
|
|
301
|
-
let out_b = final_b;
|
|
302
|
-
if (gamma_out) {
|
|
303
|
-
out_r = srgbEncodeLinear01(clamp(out_r, 0, 1));
|
|
304
|
-
out_g = srgbEncodeLinear01(clamp(out_g, 0, 1));
|
|
305
|
-
out_b = srgbEncodeLinear01(clamp(out_b, 0, 1));
|
|
306
|
-
} else {
|
|
307
|
-
out_r = clamp(out_r, 0, 1);
|
|
308
|
-
out_g = clamp(out_g, 0, 1);
|
|
309
|
-
out_b = clamp(out_b, 0, 1);
|
|
310
|
-
}
|
|
311
|
-
const dst_idx = (y * software_renderer.width + x) * 4;
|
|
312
|
-
const dst_r = (software_renderer.buffer[dst_idx + 0] ?? 0) / 255;
|
|
313
|
-
const dst_g = (software_renderer.buffer[dst_idx + 1] ?? 0) / 255;
|
|
314
|
-
const dst_b = (software_renderer.buffer[dst_idx + 2] ?? 0) / 255;
|
|
315
|
-
const dst_a = (software_renderer.buffer[dst_idx + 3] ?? 0) / 255;
|
|
316
|
-
const one_minus_a = 1 - final_alpha;
|
|
317
|
-
const blend_r = out_r * final_alpha + dst_r * one_minus_a;
|
|
318
|
-
const blend_g = out_g * final_alpha + dst_g * one_minus_a;
|
|
319
|
-
const blend_b = out_b * final_alpha + dst_b * one_minus_a;
|
|
320
|
-
const blend_a = final_alpha + dst_a * one_minus_a;
|
|
321
|
-
software_renderer.setPixel(
|
|
322
|
-
x,
|
|
323
|
-
y,
|
|
324
|
-
clamp(blend_r, 0, 1) * 255 | 0,
|
|
325
|
-
clamp(blend_g, 0, 1) * 255 | 0,
|
|
326
|
-
clamp(blend_b, 0, 1) * 255 | 0,
|
|
327
|
-
clamp(blend_a, 0, 1) * 255 | 0
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// lib/render/getDefaultRenderOptions.ts
|
|
334
|
-
var DEFAULT_LIGHT_DIR = [-0.4, -0.9, -0.2];
|
|
335
|
-
function hexToRgb(hex) {
|
|
336
|
-
const cleanHex = hex.replace(/^#/, "");
|
|
337
|
-
if (!/^[0-9A-Fa-f]{6}$/.test(cleanHex)) {
|
|
338
|
-
return null;
|
|
339
|
-
}
|
|
340
|
-
const r = Number.parseInt(cleanHex.substring(0, 2), 16) / 255;
|
|
341
|
-
const g = Number.parseInt(cleanHex.substring(2, 4), 16) / 255;
|
|
342
|
-
const b = Number.parseInt(cleanHex.substring(4, 6), 16) / 255;
|
|
343
|
-
return [r, g, b];
|
|
344
|
-
}
|
|
345
|
-
var DEFAULT_RENDER_OPTIONS = {
|
|
346
|
-
width: 800,
|
|
347
|
-
height: 600,
|
|
348
|
-
supersampling: 1,
|
|
349
|
-
fov: 60,
|
|
350
|
-
cull: true,
|
|
351
|
-
gamma: true,
|
|
352
|
-
ambient: 0.15,
|
|
353
|
-
lightDir: DEFAULT_LIGHT_DIR,
|
|
354
|
-
camPos: null,
|
|
355
|
-
lookAt: null,
|
|
356
|
-
backgroundColor: null,
|
|
357
|
-
grid: false
|
|
358
|
-
};
|
|
359
|
-
function getDefaultRenderOptions() {
|
|
360
|
-
return { ...DEFAULT_RENDER_OPTIONS };
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// lib/render/resolveRenderOptions.ts
|
|
364
|
-
function resolveRenderOptions(options = {}) {
|
|
365
|
-
const supersampling = typeof options.supersampling === "number" && Number.isFinite(options.supersampling) ? Math.max(1, Math.floor(options.supersampling)) : DEFAULT_RENDER_OPTIONS.supersampling;
|
|
366
|
-
return {
|
|
367
|
-
...DEFAULT_RENDER_OPTIONS,
|
|
368
|
-
...options,
|
|
369
|
-
supersampling,
|
|
370
|
-
lightDir: options.lightDir != null ? options.lightDir : DEFAULT_RENDER_OPTIONS.lightDir,
|
|
371
|
-
camPos: options.camPos !== void 0 ? options.camPos : DEFAULT_RENDER_OPTIONS.camPos,
|
|
372
|
-
lookAt: options.lookAt !== void 0 ? options.lookAt : DEFAULT_RENDER_OPTIONS.lookAt
|
|
373
|
-
};
|
|
374
|
-
}
|
|
177
|
+
// lib/render/drawDebugPoints.ts
|
|
178
|
+
import {
|
|
179
|
+
glyphAdvanceRatio,
|
|
180
|
+
glyphLineAlphabet,
|
|
181
|
+
textMetrics
|
|
182
|
+
} from "@tscircuit/alphabet";
|
|
183
|
+
import { mat4 as mat44, vec4 as vec43 } from "gl-matrix";
|
|
375
184
|
|
|
376
185
|
// lib/render/SoftwareRenderer.ts
|
|
377
|
-
import { mat3, mat4 as
|
|
186
|
+
import { mat3, mat4 as mat43, vec3 as vec32, vec4 as vec42 } from "gl-matrix";
|
|
378
187
|
|
|
379
188
|
// lib/gltf/computeSmoothNormals.ts
|
|
380
189
|
function computeSmoothNormals(positions, indices) {
|
|
@@ -442,11 +251,54 @@ function computeSmoothNormals(positions, indices) {
|
|
|
442
251
|
return normals;
|
|
443
252
|
}
|
|
444
253
|
|
|
254
|
+
// lib/render/getDefaultRenderOptions.ts
|
|
255
|
+
var DEFAULT_LIGHT_DIR = [-0.4, -0.9, -0.2];
|
|
256
|
+
function hexToRgb(hex) {
|
|
257
|
+
const cleanHex = hex.replace(/^#/, "");
|
|
258
|
+
if (!/^[0-9A-Fa-f]{6}$/.test(cleanHex)) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
const r = Number.parseInt(cleanHex.substring(0, 2), 16) / 255;
|
|
262
|
+
const g = Number.parseInt(cleanHex.substring(2, 4), 16) / 255;
|
|
263
|
+
const b = Number.parseInt(cleanHex.substring(4, 6), 16) / 255;
|
|
264
|
+
return [r, g, b];
|
|
265
|
+
}
|
|
266
|
+
var DEFAULT_RENDER_OPTIONS = {
|
|
267
|
+
width: 800,
|
|
268
|
+
height: 600,
|
|
269
|
+
supersampling: 1,
|
|
270
|
+
fov: 60,
|
|
271
|
+
cull: true,
|
|
272
|
+
gamma: true,
|
|
273
|
+
ambient: 0.15,
|
|
274
|
+
lightDir: DEFAULT_LIGHT_DIR,
|
|
275
|
+
camPos: null,
|
|
276
|
+
lookAt: null,
|
|
277
|
+
backgroundColor: null,
|
|
278
|
+
grid: false,
|
|
279
|
+
debugPoints: null,
|
|
280
|
+
debugFontSize: null,
|
|
281
|
+
debugPointColor: null,
|
|
282
|
+
debugLabelColor: null
|
|
283
|
+
};
|
|
284
|
+
function getDefaultRenderOptions() {
|
|
285
|
+
return { ...DEFAULT_RENDER_OPTIONS };
|
|
286
|
+
}
|
|
287
|
+
|
|
445
288
|
// lib/utils/mulColor.ts
|
|
446
289
|
function mulColor(a, b) {
|
|
447
290
|
return [a[0] * b[0], a[1] * b[1], a[2] * b[2], a[3] * b[3]];
|
|
448
291
|
}
|
|
449
292
|
|
|
293
|
+
// lib/utils/srgbEncodeLinear01.ts
|
|
294
|
+
function srgbEncodeLinear01(linear) {
|
|
295
|
+
if (linear <= 31308e-7) return 12.92 * linear;
|
|
296
|
+
return 1.055 * Math.pow(linear, 1 / 2.4) - 0.055;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// lib/utils/clamp.ts
|
|
300
|
+
var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
|
|
301
|
+
|
|
450
302
|
// lib/render/SoftwareRenderer.ts
|
|
451
303
|
var SoftwareRenderer = class {
|
|
452
304
|
width;
|
|
@@ -486,21 +338,21 @@ var SoftwareRenderer = class {
|
|
|
486
338
|
if (!indices) return;
|
|
487
339
|
const view = camera.view;
|
|
488
340
|
const proj = camera.proj;
|
|
489
|
-
const mvp =
|
|
490
|
-
|
|
341
|
+
const mvp = mat43.create();
|
|
342
|
+
mat43.multiply(mvp, proj, mat43.multiply(mat43.create(), view, model));
|
|
491
343
|
const vertexCount = positions.length / 3 | 0;
|
|
492
344
|
const vScreen = new Array(vertexCount);
|
|
493
345
|
const vInvW = new Float32Array(vertexCount);
|
|
494
346
|
const vNDCz = new Float32Array(vertexCount);
|
|
495
347
|
for (let i = 0; i < vertexCount; i++) {
|
|
496
|
-
const p =
|
|
348
|
+
const p = vec42.fromValues(
|
|
497
349
|
positions[i * 3 + 0],
|
|
498
350
|
positions[i * 3 + 1],
|
|
499
351
|
positions[i * 3 + 2],
|
|
500
352
|
1
|
|
501
353
|
);
|
|
502
|
-
const c =
|
|
503
|
-
|
|
354
|
+
const c = vec42.create();
|
|
355
|
+
vec42.transformMat4(c, p, mvp);
|
|
504
356
|
const invW = 1 / c[3];
|
|
505
357
|
vInvW[i] = invW;
|
|
506
358
|
if (!isFinite(invW)) {
|
|
@@ -600,8 +452,8 @@ var SoftwareRenderer = class {
|
|
|
600
452
|
const { positions, normals, uvs, indices, model, colors } = mesh;
|
|
601
453
|
const view = camera.view;
|
|
602
454
|
const proj = camera.proj;
|
|
603
|
-
const mvp =
|
|
604
|
-
|
|
455
|
+
const mvp = mat43.create();
|
|
456
|
+
mat43.multiply(mvp, proj, mat43.multiply(mat43.create(), view, model));
|
|
605
457
|
const normalMat = mat3.create();
|
|
606
458
|
mat3.normalFromMat4(normalMat, model);
|
|
607
459
|
const vertexCount = positions.length / 3 | 0;
|
|
@@ -620,14 +472,14 @@ var SoftwareRenderer = class {
|
|
|
620
472
|
const vWorldN = new Array(vertexCount);
|
|
621
473
|
const vColor = new Array(vertexCount);
|
|
622
474
|
for (let i = 0; i < vertexCount; i++) {
|
|
623
|
-
const p =
|
|
475
|
+
const p = vec42.fromValues(
|
|
624
476
|
positions[i * 3 + 0],
|
|
625
477
|
positions[i * 3 + 1],
|
|
626
478
|
positions[i * 3 + 2],
|
|
627
479
|
1
|
|
628
480
|
);
|
|
629
|
-
const c =
|
|
630
|
-
|
|
481
|
+
const c = vec42.create();
|
|
482
|
+
vec42.transformMat4(c, p, mvp);
|
|
631
483
|
const invW = 1 / c[3];
|
|
632
484
|
const ndcX = c[0] * invW;
|
|
633
485
|
const ndcY = c[1] * invW;
|
|
@@ -806,6 +658,333 @@ function edge(a, b, p) {
|
|
|
806
658
|
return (p[0] - a[0]) * (b[1] - a[1]) - (p[1] - a[1]) * (b[0] - a[0]);
|
|
807
659
|
}
|
|
808
660
|
|
|
661
|
+
// lib/render/drawDebugPoints.ts
|
|
662
|
+
var DEFAULT_MARKER_COLOR = [
|
|
663
|
+
255,
|
|
664
|
+
0,
|
|
665
|
+
170,
|
|
666
|
+
255
|
|
667
|
+
];
|
|
668
|
+
var DEFAULT_TEXT_COLOR = [
|
|
669
|
+
255,
|
|
670
|
+
24,
|
|
671
|
+
170,
|
|
672
|
+
255
|
|
673
|
+
];
|
|
674
|
+
var CHARACTER_WIDTH_RATIO = 0.62;
|
|
675
|
+
function drawDebugPoints(renderer, camera, debugPoints, debugFontSize, debugPointColor, debugLabelColor) {
|
|
676
|
+
if (debugPoints.length === 0) return;
|
|
677
|
+
const viewProj = mat44.create();
|
|
678
|
+
mat44.multiply(viewProj, camera.proj, camera.view);
|
|
679
|
+
const markerColor = toColorRGBA(debugPointColor, DEFAULT_MARKER_COLOR);
|
|
680
|
+
const textColor = toColorRGBA(debugLabelColor, DEFAULT_TEXT_COLOR);
|
|
681
|
+
const labelScale = typeof debugFontSize === "number" && Number.isFinite(debugFontSize) ? Math.max(1, Math.round(debugFontSize)) : Math.max(
|
|
682
|
+
12,
|
|
683
|
+
Math.round(Math.min(renderer.width, renderer.height) * 0.028)
|
|
684
|
+
);
|
|
685
|
+
const textThickness = Math.max(
|
|
686
|
+
2,
|
|
687
|
+
Math.round(labelScale * textMetrics.strokeWidthRatio)
|
|
688
|
+
);
|
|
689
|
+
for (const debugPoint of debugPoints) {
|
|
690
|
+
const projected = projectWorldToScreen(
|
|
691
|
+
debugPoint.position,
|
|
692
|
+
viewProj,
|
|
693
|
+
renderer
|
|
694
|
+
);
|
|
695
|
+
if (!projected) continue;
|
|
696
|
+
drawMarker(renderer, projected, markerColor);
|
|
697
|
+
drawLabel(
|
|
698
|
+
renderer,
|
|
699
|
+
projected,
|
|
700
|
+
debugPoint.label,
|
|
701
|
+
textColor,
|
|
702
|
+
labelScale,
|
|
703
|
+
textThickness
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
function projectWorldToScreen(position, viewProj, renderer) {
|
|
708
|
+
const clip = vec43.fromValues(position.x, position.y, position.z, 1);
|
|
709
|
+
vec43.transformMat4(clip, clip, viewProj);
|
|
710
|
+
if (!Number.isFinite(clip[3]) || clip[3] <= 0) return null;
|
|
711
|
+
const invW = 1 / clip[3];
|
|
712
|
+
const ndcX = clip[0] * invW;
|
|
713
|
+
const ndcY = clip[1] * invW;
|
|
714
|
+
const ndcZ = clip[2] * invW;
|
|
715
|
+
if (!Number.isFinite(ndcX) || !Number.isFinite(ndcY) || !Number.isFinite(ndcZ) || ndcZ < -1 || ndcZ > 1) {
|
|
716
|
+
return null;
|
|
717
|
+
}
|
|
718
|
+
return {
|
|
719
|
+
x: (ndcX * 0.5 + 0.5) * (renderer.width - 1),
|
|
720
|
+
y: (1 - (ndcY * 0.5 + 0.5)) * (renderer.height - 1)
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
function drawMarker(renderer, projected, color) {
|
|
724
|
+
const centerX = Math.round(projected.x);
|
|
725
|
+
const centerY = Math.round(projected.y);
|
|
726
|
+
for (let offset = -5; offset <= 4; offset += 1) {
|
|
727
|
+
renderer.setPixel(
|
|
728
|
+
centerX + offset,
|
|
729
|
+
centerY,
|
|
730
|
+
color[0],
|
|
731
|
+
color[1],
|
|
732
|
+
color[2],
|
|
733
|
+
color[3]
|
|
734
|
+
);
|
|
735
|
+
renderer.setPixel(
|
|
736
|
+
centerX,
|
|
737
|
+
centerY + offset,
|
|
738
|
+
color[0],
|
|
739
|
+
color[1],
|
|
740
|
+
color[2],
|
|
741
|
+
color[3]
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
function drawLabel(renderer, projected, label, color, scale, textThickness) {
|
|
746
|
+
if (label.length === 0) return;
|
|
747
|
+
const scaleX = scale * CHARACTER_WIDTH_RATIO;
|
|
748
|
+
const labelWidth = measureLabelWidth(label, scaleX);
|
|
749
|
+
let x = projected.x + scale * 0.6;
|
|
750
|
+
let y = projected.y - scale * 1.1;
|
|
751
|
+
if (x + labelWidth > renderer.width - 2) {
|
|
752
|
+
x = projected.x - scale * 0.6 - labelWidth;
|
|
753
|
+
}
|
|
754
|
+
if (y < 2) {
|
|
755
|
+
y = projected.y + scale * 0.2;
|
|
756
|
+
}
|
|
757
|
+
drawStrokeText(renderer, label, x, y, scaleX, scale, color, textThickness);
|
|
758
|
+
}
|
|
759
|
+
function drawStrokeText(renderer, text, x, y, scaleX, scaleY, color, thickness) {
|
|
760
|
+
let cursorX = x;
|
|
761
|
+
for (const rawChar of text) {
|
|
762
|
+
const char = resolveGlyph(rawChar);
|
|
763
|
+
const glyph = glyphLineAlphabet[char];
|
|
764
|
+
const advanceRatio = glyphAdvanceRatio[char] ?? (char === " " ? textMetrics.spaceWidthRatio : textMetrics.glyphWidthRatio);
|
|
765
|
+
if (glyph) {
|
|
766
|
+
for (const segment of glyph) {
|
|
767
|
+
drawLine(
|
|
768
|
+
renderer,
|
|
769
|
+
cursorX + segment.x1 * scaleX,
|
|
770
|
+
y + (1 - segment.y1) * scaleY,
|
|
771
|
+
cursorX + segment.x2 * scaleX,
|
|
772
|
+
y + (1 - segment.y2) * scaleY,
|
|
773
|
+
color,
|
|
774
|
+
thickness
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
cursorX += advanceRatio * scaleX;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
function resolveGlyph(char) {
|
|
782
|
+
if (glyphLineAlphabet[char]) return char;
|
|
783
|
+
const uppercase = char.toUpperCase();
|
|
784
|
+
if (glyphLineAlphabet[uppercase]) return uppercase;
|
|
785
|
+
return "?";
|
|
786
|
+
}
|
|
787
|
+
function measureLabelWidth(text, scaleX) {
|
|
788
|
+
let width = 0;
|
|
789
|
+
for (const rawChar of text) {
|
|
790
|
+
const char = resolveGlyph(rawChar);
|
|
791
|
+
width += glyphAdvanceRatio[char] ?? (char === " " ? textMetrics.spaceWidthRatio : textMetrics.glyphWidthRatio);
|
|
792
|
+
}
|
|
793
|
+
return width * scaleX;
|
|
794
|
+
}
|
|
795
|
+
function drawLine(renderer, x0, y0, x1, y1, color, thickness) {
|
|
796
|
+
const dx = x1 - x0;
|
|
797
|
+
const dy = y1 - y0;
|
|
798
|
+
const steps = Math.max(Math.abs(dx), Math.abs(dy));
|
|
799
|
+
if (steps === 0) {
|
|
800
|
+
drawBrush(renderer, x0, y0, color, thickness);
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
for (let step = 0; step <= steps; step += 1) {
|
|
804
|
+
const t = step / steps;
|
|
805
|
+
drawBrush(renderer, x0 + dx * t, y0 + dy * t, color, thickness);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
function drawBrush(renderer, x, y, color, thickness) {
|
|
809
|
+
const radius = Math.max(0, Math.floor((thickness - 1) / 2));
|
|
810
|
+
const centerX = Math.round(x);
|
|
811
|
+
const centerY = Math.round(y);
|
|
812
|
+
for (let offsetY = -radius; offsetY <= radius; offsetY += 1) {
|
|
813
|
+
for (let offsetX = -radius; offsetX <= radius; offsetX += 1) {
|
|
814
|
+
renderer.setPixel(
|
|
815
|
+
centerX + offsetX,
|
|
816
|
+
centerY + offsetY,
|
|
817
|
+
color[0],
|
|
818
|
+
color[1],
|
|
819
|
+
color[2],
|
|
820
|
+
color[3]
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
function toColorRGBA(color, fallback) {
|
|
826
|
+
if (!color) return fallback;
|
|
827
|
+
return [color[0], color[1], color[2], 255];
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// lib/render/drawInfiniteGrid.ts
|
|
831
|
+
import { mat4 as mat45, vec4 as vec44 } from "gl-matrix";
|
|
832
|
+
function computeGridLineIntensity(params) {
|
|
833
|
+
const rx = params.world_x / params.cell_size;
|
|
834
|
+
const rz = params.world_z / params.cell_size;
|
|
835
|
+
const fract_x = rx - Math.floor(rx + 0.5);
|
|
836
|
+
const fract_z = rz - Math.floor(rz + 0.5);
|
|
837
|
+
const grid_x = Math.abs(fract_x);
|
|
838
|
+
const grid_z = Math.abs(fract_z);
|
|
839
|
+
const pixel_size_in_world = params.dist / Math.min(params.screen_width, params.screen_height);
|
|
840
|
+
const fwidth_x = pixel_size_in_world / params.cell_size;
|
|
841
|
+
const fwidth_z = pixel_size_in_world / params.cell_size;
|
|
842
|
+
const line_x = grid_x / (fwidth_x + 1e-4);
|
|
843
|
+
const line_z = grid_z / (fwidth_z + 1e-4);
|
|
844
|
+
const line = Math.min(line_x, line_z);
|
|
845
|
+
return 1 - Math.min(line * params.line_thickness_multiplier, 1);
|
|
846
|
+
}
|
|
847
|
+
function computeGridFadeAlpha(params) {
|
|
848
|
+
const fade_start = params.fade_distance;
|
|
849
|
+
const fade_end = params.fade_distance * params.fade_strength;
|
|
850
|
+
return 1 - Math.min(
|
|
851
|
+
1,
|
|
852
|
+
Math.max(0, (params.dist - fade_start) / (fade_end - fade_start))
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
function drawInfiniteGrid(software_renderer, params) {
|
|
856
|
+
const grid_y = params.grid_y ?? 0;
|
|
857
|
+
const cell_size = params.cell_size ?? 1;
|
|
858
|
+
const section_size = params.section_size ?? 10;
|
|
859
|
+
const fade_distance = params.fade_distance ?? 100;
|
|
860
|
+
const fade_strength = params.fade_strength ?? 1.5;
|
|
861
|
+
const grid_color = params.grid_color ?? [0.93, 0.93, 0.93];
|
|
862
|
+
const section_color = params.section_color ?? [0.8, 0.8, 1];
|
|
863
|
+
const gamma_out = params.gamma_out ?? true;
|
|
864
|
+
const camera = params.camera;
|
|
865
|
+
const view = camera.view;
|
|
866
|
+
const proj = camera.proj;
|
|
867
|
+
const inv_view = mat45.create();
|
|
868
|
+
mat45.invert(inv_view, view);
|
|
869
|
+
const cam_pos_x = inv_view[12];
|
|
870
|
+
const cam_pos_y = inv_view[13];
|
|
871
|
+
const cam_pos_z = inv_view[14];
|
|
872
|
+
const vp = mat45.create();
|
|
873
|
+
mat45.multiply(vp, proj, view);
|
|
874
|
+
const inv_vp = mat45.create();
|
|
875
|
+
mat45.invert(inv_vp, vp);
|
|
876
|
+
for (let y = 0; y < software_renderer.height; y++) {
|
|
877
|
+
for (let x = 0; x < software_renderer.width; x++) {
|
|
878
|
+
const ndc_x = x / (software_renderer.width - 1) * 2 - 1;
|
|
879
|
+
const ndc_y = 1 - y / (software_renderer.height - 1) * 2;
|
|
880
|
+
const near_point = vec44.fromValues(ndc_x, ndc_y, -1, 1);
|
|
881
|
+
const far_point = vec44.fromValues(ndc_x, ndc_y, 1, 1);
|
|
882
|
+
vec44.transformMat4(near_point, near_point, inv_vp);
|
|
883
|
+
vec44.transformMat4(far_point, far_point, inv_vp);
|
|
884
|
+
near_point[0] /= near_point[3];
|
|
885
|
+
near_point[1] /= near_point[3];
|
|
886
|
+
near_point[2] /= near_point[3];
|
|
887
|
+
far_point[0] /= far_point[3];
|
|
888
|
+
far_point[1] /= far_point[3];
|
|
889
|
+
far_point[2] /= far_point[3];
|
|
890
|
+
const ray_dir_x = far_point[0] - near_point[0];
|
|
891
|
+
const ray_dir_y = far_point[1] - near_point[1];
|
|
892
|
+
const ray_dir_z = far_point[2] - near_point[2];
|
|
893
|
+
if (Math.abs(ray_dir_y) < 1e-6) continue;
|
|
894
|
+
const t = (grid_y - near_point[1]) / ray_dir_y;
|
|
895
|
+
if (t < 0 || t > 1) continue;
|
|
896
|
+
const world_x = near_point[0] + t * ray_dir_x;
|
|
897
|
+
const world_z = near_point[2] + t * ray_dir_z;
|
|
898
|
+
const world_pt = vec44.fromValues(world_x, grid_y, world_z, 1);
|
|
899
|
+
const clip_pt = vec44.create();
|
|
900
|
+
vec44.transformMat4(clip_pt, world_pt, vp);
|
|
901
|
+
const ndc_z = clip_pt[2] / clip_pt[3];
|
|
902
|
+
const depth_01 = ndc_z * 0.5 + 0.5;
|
|
903
|
+
const di = y * software_renderer.width + x;
|
|
904
|
+
if (depth_01 >= software_renderer.depth[di]) continue;
|
|
905
|
+
const dx = world_x - cam_pos_x;
|
|
906
|
+
const dy = grid_y - cam_pos_y;
|
|
907
|
+
const dz = world_z - cam_pos_z;
|
|
908
|
+
const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
909
|
+
const alpha = computeGridFadeAlpha({
|
|
910
|
+
dist,
|
|
911
|
+
fade_distance,
|
|
912
|
+
fade_strength
|
|
913
|
+
});
|
|
914
|
+
if (alpha <= 0) continue;
|
|
915
|
+
const g1 = computeGridLineIntensity({
|
|
916
|
+
world_x,
|
|
917
|
+
world_z,
|
|
918
|
+
cell_size,
|
|
919
|
+
dist,
|
|
920
|
+
screen_width: software_renderer.width,
|
|
921
|
+
screen_height: software_renderer.height,
|
|
922
|
+
line_thickness_multiplier: 0.8
|
|
923
|
+
});
|
|
924
|
+
const g2 = computeGridLineIntensity({
|
|
925
|
+
world_x,
|
|
926
|
+
world_z,
|
|
927
|
+
cell_size: section_size,
|
|
928
|
+
dist,
|
|
929
|
+
screen_width: software_renderer.width,
|
|
930
|
+
screen_height: software_renderer.height,
|
|
931
|
+
line_thickness_multiplier: 0.5
|
|
932
|
+
// Thicker section lines
|
|
933
|
+
});
|
|
934
|
+
const grid_strength = Math.max(g1, g2);
|
|
935
|
+
if (grid_strength <= 0) continue;
|
|
936
|
+
const final_r = grid_color[0] * (1 - g2) + section_color[0] * g2;
|
|
937
|
+
const final_g = grid_color[1] * (1 - g2) + section_color[1] * g2;
|
|
938
|
+
const final_b = grid_color[2] * (1 - g2) + section_color[2] * g2;
|
|
939
|
+
const final_alpha = grid_strength * alpha;
|
|
940
|
+
if (final_alpha <= 0.01) continue;
|
|
941
|
+
let out_r = final_r;
|
|
942
|
+
let out_g = final_g;
|
|
943
|
+
let out_b = final_b;
|
|
944
|
+
if (gamma_out) {
|
|
945
|
+
out_r = srgbEncodeLinear01(clamp(out_r, 0, 1));
|
|
946
|
+
out_g = srgbEncodeLinear01(clamp(out_g, 0, 1));
|
|
947
|
+
out_b = srgbEncodeLinear01(clamp(out_b, 0, 1));
|
|
948
|
+
} else {
|
|
949
|
+
out_r = clamp(out_r, 0, 1);
|
|
950
|
+
out_g = clamp(out_g, 0, 1);
|
|
951
|
+
out_b = clamp(out_b, 0, 1);
|
|
952
|
+
}
|
|
953
|
+
const dst_idx = (y * software_renderer.width + x) * 4;
|
|
954
|
+
const dst_r = (software_renderer.buffer[dst_idx + 0] ?? 0) / 255;
|
|
955
|
+
const dst_g = (software_renderer.buffer[dst_idx + 1] ?? 0) / 255;
|
|
956
|
+
const dst_b = (software_renderer.buffer[dst_idx + 2] ?? 0) / 255;
|
|
957
|
+
const dst_a = (software_renderer.buffer[dst_idx + 3] ?? 0) / 255;
|
|
958
|
+
const one_minus_a = 1 - final_alpha;
|
|
959
|
+
const blend_r = out_r * final_alpha + dst_r * one_minus_a;
|
|
960
|
+
const blend_g = out_g * final_alpha + dst_g * one_minus_a;
|
|
961
|
+
const blend_b = out_b * final_alpha + dst_b * one_minus_a;
|
|
962
|
+
const blend_a = final_alpha + dst_a * one_minus_a;
|
|
963
|
+
software_renderer.setPixel(
|
|
964
|
+
x,
|
|
965
|
+
y,
|
|
966
|
+
clamp(blend_r, 0, 1) * 255 | 0,
|
|
967
|
+
clamp(blend_g, 0, 1) * 255 | 0,
|
|
968
|
+
clamp(blend_b, 0, 1) * 255 | 0,
|
|
969
|
+
clamp(blend_a, 0, 1) * 255 | 0
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// lib/render/resolveRenderOptions.ts
|
|
976
|
+
function resolveRenderOptions(options = {}) {
|
|
977
|
+
const supersampling = typeof options.supersampling === "number" && Number.isFinite(options.supersampling) ? Math.max(1, Math.floor(options.supersampling)) : DEFAULT_RENDER_OPTIONS.supersampling;
|
|
978
|
+
return {
|
|
979
|
+
...DEFAULT_RENDER_OPTIONS,
|
|
980
|
+
...options,
|
|
981
|
+
supersampling,
|
|
982
|
+
lightDir: options.lightDir != null ? options.lightDir : DEFAULT_RENDER_OPTIONS.lightDir,
|
|
983
|
+
camPos: options.camPos !== void 0 ? options.camPos : DEFAULT_RENDER_OPTIONS.camPos,
|
|
984
|
+
lookAt: options.lookAt !== void 0 ? options.lookAt : DEFAULT_RENDER_OPTIONS.lookAt
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
|
|
809
988
|
// lib/render/renderDrawCalls.ts
|
|
810
989
|
function renderDrawCalls(drawCalls, optionsInput = {}, imageFactory = createUint8Bitmap) {
|
|
811
990
|
const options = resolveRenderOptions(optionsInput);
|
|
@@ -915,6 +1094,16 @@ function renderDrawCalls(drawCalls, optionsInput = {}, imageFactory = createUint
|
|
|
915
1094
|
});
|
|
916
1095
|
}
|
|
917
1096
|
renderGroup(blendDraws);
|
|
1097
|
+
if (options.debugPoints?.length) {
|
|
1098
|
+
drawDebugPoints(
|
|
1099
|
+
renderer,
|
|
1100
|
+
camera,
|
|
1101
|
+
options.debugPoints,
|
|
1102
|
+
options.debugFontSize,
|
|
1103
|
+
options.debugPointColor,
|
|
1104
|
+
options.debugLabelColor
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
918
1107
|
const bitmap = options.supersampling > 1 ? downsampleBitmap(
|
|
919
1108
|
renderer.bitmap,
|
|
920
1109
|
options.width,
|
|
@@ -925,7 +1114,7 @@ function renderDrawCalls(drawCalls, optionsInput = {}, imageFactory = createUint
|
|
|
925
1114
|
}
|
|
926
1115
|
|
|
927
1116
|
// lib/gltf/createInfiniteGrid.ts
|
|
928
|
-
import { mat4 as
|
|
1117
|
+
import { mat4 as mat46 } from "gl-matrix";
|
|
929
1118
|
function createInfiniteGrid(options = {}) {
|
|
930
1119
|
const cellSize = options.cellSize ?? 1;
|
|
931
1120
|
const sectionSize = options.sectionSize ?? 10;
|
|
@@ -949,9 +1138,9 @@ function createInfiniteGrid(options = {}) {
|
|
|
949
1138
|
size
|
|
950
1139
|
]);
|
|
951
1140
|
const indices = new Uint32Array([0, 1, 2, 0, 2, 3]);
|
|
952
|
-
const model =
|
|
1141
|
+
const model = mat46.create();
|
|
953
1142
|
const offset = options.offset ?? {};
|
|
954
|
-
|
|
1143
|
+
mat46.fromTranslation(model, [offset.x ?? 0, offset.y ?? 0, offset.z ?? 0]);
|
|
955
1144
|
return {
|
|
956
1145
|
positions,
|
|
957
1146
|
indices,
|
|
@@ -976,7 +1165,7 @@ function createInfiniteGrid(options = {}) {
|
|
|
976
1165
|
}
|
|
977
1166
|
|
|
978
1167
|
// lib/gltf/createSceneFromGLTF.ts
|
|
979
|
-
import { mat4 as
|
|
1168
|
+
import { mat4 as mat47 } from "gl-matrix";
|
|
980
1169
|
var COMPONENT_INFO = {
|
|
981
1170
|
5120: {
|
|
982
1171
|
name: "BYTE",
|
|
@@ -1174,17 +1363,17 @@ function readIndices(ctx, accessorIndex) {
|
|
|
1174
1363
|
}
|
|
1175
1364
|
function nodeLocalMatrix(node) {
|
|
1176
1365
|
if (node.matrix) {
|
|
1177
|
-
const m2 =
|
|
1366
|
+
const m2 = mat47.create();
|
|
1178
1367
|
for (let i = 0; i < 16; i++) m2[i] = node.matrix[i];
|
|
1179
1368
|
return m2;
|
|
1180
1369
|
}
|
|
1181
1370
|
const translation = node.translation || [0, 0, 0];
|
|
1182
1371
|
const rotation = node.rotation || [0, 0, 0, 1];
|
|
1183
1372
|
const scale = node.scale || [1, 1, 1];
|
|
1184
|
-
const m =
|
|
1185
|
-
const tmp =
|
|
1186
|
-
|
|
1187
|
-
|
|
1373
|
+
const m = mat47.create();
|
|
1374
|
+
const tmp = mat47.create();
|
|
1375
|
+
mat47.fromRotationTranslationScale(tmp, rotation, translation, scale);
|
|
1376
|
+
mat47.copy(m, tmp);
|
|
1188
1377
|
return m;
|
|
1189
1378
|
}
|
|
1190
1379
|
function getMaterial(gltf, textures, images, materialIndex) {
|
|
@@ -1222,8 +1411,8 @@ function createSceneFromGLTF(gltf, resources) {
|
|
|
1222
1411
|
const node = nodes[nodeIndex];
|
|
1223
1412
|
if (!node) return;
|
|
1224
1413
|
const local = nodeLocalMatrix(node);
|
|
1225
|
-
const world =
|
|
1226
|
-
|
|
1414
|
+
const world = mat47.create();
|
|
1415
|
+
mat47.multiply(world, parentMatrix, local);
|
|
1227
1416
|
if (Number.isInteger(node.mesh)) {
|
|
1228
1417
|
const mesh = meshes[node.mesh];
|
|
1229
1418
|
if (mesh) {
|
|
@@ -1261,7 +1450,7 @@ function createSceneFromGLTF(gltf, resources) {
|
|
|
1261
1450
|
traverse(childIndex, world);
|
|
1262
1451
|
}
|
|
1263
1452
|
}
|
|
1264
|
-
const identity =
|
|
1453
|
+
const identity = mat47.create();
|
|
1265
1454
|
for (const rootIndex of scene.nodes || []) {
|
|
1266
1455
|
traverse(rootIndex, identity);
|
|
1267
1456
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poppygl",
|
|
3
3
|
"main": "dist/index.js",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.19",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/tscircuit/poppygl.git"
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"type": "module",
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@biomejs/biome": "^2.2.4",
|
|
23
|
+
"@tscircuit/alphabet": "^0.0.22",
|
|
23
24
|
"@types/bun": "^1.2.23",
|
|
24
25
|
"@types/readable-stream": "^4.0.21",
|
|
25
26
|
"looks-same": "^10.0.1",
|