chore(deps): update dependency @vue/tsconfig to ^0.8.0 (#251)

* chore(deps): update dependency @vue/tsconfig to ^0.8.0

* Fix new ts issues

* Add null checks for selection and model objects throughout frontend

This improves robustness by handling cases where selection or model objects may be missing or undefined, preventing
runtime errors.

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Yeicor <4929005+yeicor@users.noreply.github.com>
This commit is contained in:
renovate[bot]
2025-08-31 15:15:10 +00:00
committed by GitHub
parent fbf2f3e9a1
commit 6a0aa265b6
13 changed files with 650 additions and 508 deletions

View File

@@ -1,75 +1,88 @@
import {BufferAttribute, InterleavedBufferAttribute, Vector3} from 'three';
import type {MObject3D} from "../tools/Selection.vue";
import type {ModelScene} from '@google/model-viewer/lib/three-components/ModelScene';
import type {SelectionInfo} from "../tools/selection";
import { BufferAttribute, InterleavedBufferAttribute, Vector3 } from "three";
import type { MObject3D } from "../tools/Selection.vue";
import type { ModelScene } from "@google/model-viewer/lib/three-components/ModelScene";
import type { SelectionInfo } from "../tools/selection";
function getCenterAndVertexList(selInfo: SelectionInfo, scene: ModelScene): {
center: Vector3,
vertices: Array<Vector3>
function getCenterAndVertexList(
selInfo: SelectionInfo,
scene: ModelScene,
): {
center: Vector3;
vertices: Array<Vector3>;
} {
let pos: BufferAttribute | InterleavedBufferAttribute = selInfo.object.geometry.getAttribute('position');
let ind: BufferAttribute | null = selInfo.object.geometry.index;
if (ind === null) {
ind = new BufferAttribute(new Uint16Array(pos.count), 1);
for (let i = 0; i < pos.count; i++) {
ind.array[i] = i;
}
if (!selInfo.object?.geometry) {
throw new Error("selInfo.object or geometry is undefined");
}
let pos = selInfo.object.geometry.getAttribute("position");
let ind = selInfo.object.geometry.index;
if (ind === null) {
ind = new BufferAttribute(new Uint16Array(pos.count), 1);
for (let i = 0; i < pos.count; i++) {
ind.array[i] = i;
}
let center = new Vector3();
let vertices = [];
for (let i = selInfo.indices[0]; i < selInfo.indices[1]; i++) {
let index = ind.getX(i)
let vertex = new Vector3(pos.getX(index), pos.getY(index), pos.getZ(index));
vertex = scene.target.worldToLocal(selInfo.object.localToWorld(vertex));
center.add(vertex);
vertices.push(vertex);
}
center = center.divideScalar(selInfo.indices[1] - selInfo.indices[0]);
return {center, vertices};
}
let center = new Vector3();
let vertices = [];
for (let i = selInfo.indices[0]; i < selInfo.indices[1]; i++) {
let index = ind.getX(i);
let vertex = new Vector3(pos.getX(index), pos.getY(index), pos.getZ(index));
vertex = scene.target.worldToLocal(selInfo.object.localToWorld(vertex));
center.add(vertex);
vertices.push(vertex);
}
center = center.divideScalar(selInfo.indices[1] - selInfo.indices[0]);
return { center, vertices };
}
/**
* Given two THREE.Object3D objects, returns their closest and farthest vertices, and the geometric centers.
* All of them are approximated and should not be used for precise calculations.
*/
export function distances(a: SelectionInfo, b: SelectionInfo, scene: ModelScene): {
min: Array<Vector3>,
center: Array<Vector3>,
max: Array<Vector3>
export function distances(
a: SelectionInfo,
b: SelectionInfo,
scene: ModelScene,
): {
min: Array<Vector3>;
center: Array<Vector3>;
max: Array<Vector3>;
} {
// Simplify this problem (approximate) by using the distance between each of their vertices.
// Find the center of each object.
let {center: aCenter, vertices: aVertices} = getCenterAndVertexList(a, scene);
let {center: bCenter, vertices: bVertices} = getCenterAndVertexList(b, scene);
// Simplify this problem (approximate) by using the distance between each of their vertices.
// Find the center of each object.
let { center: aCenter, vertices: aVertices } = getCenterAndVertexList(a, scene);
let { center: bCenter, vertices: bVertices } = getCenterAndVertexList(b, scene);
// Find the closest and farthest vertices.
// TODO: Compute actual min and max distances between the two objects.
// FIXME: Really slow... (use a BVH or something)
let minDistance = Infinity;
let minDistanceVertices = [new Vector3(), new Vector3()];
let maxDistance = -Infinity;
let maxDistanceVertices = [new Vector3(), new Vector3()];
for (let i = 0; i < aVertices.length; i++) {
for (let j = 0; j < bVertices.length; j++) {
let distance = aVertices[i].distanceTo(bVertices[j]);
if (distance < minDistance) {
minDistance = distance;
minDistanceVertices[0] = aVertices[i];
minDistanceVertices[1] = bVertices[j];
}
if (distance > maxDistance) {
maxDistance = distance;
maxDistanceVertices[0] = aVertices[i];
maxDistanceVertices[1] = bVertices[j];
}
// Find the closest and farthest vertices.
// TODO: Compute actual min and max distances between the two objects.
// FIXME: Really slow... (use a BVH or something)
let minDistance = Infinity;
let minDistanceVertices = [new Vector3(), new Vector3()];
let maxDistance = -Infinity;
let maxDistanceVertices = [new Vector3(), new Vector3()];
for (let i = 0; i < aVertices.length; i++) {
for (let j = 0; j < bVertices.length; j++) {
const aVertex = aVertices[i];
const bVertex = bVertices[j];
if (aVertex && bVertex) {
let distance = aVertex.distanceTo(bVertex);
if (distance < minDistance) {
minDistance = distance;
minDistanceVertices[0] = aVertex;
minDistanceVertices[1] = bVertex;
}
if (distance > maxDistance) {
maxDistance = distance;
maxDistanceVertices[0] = aVertex;
maxDistanceVertices[1] = bVertex;
}
}
}
}
// Return the results.
return {
min: minDistanceVertices,
center: [aCenter, bCenter],
max: maxDistanceVertices
};
}
// Return the results.
return {
min: minDistanceVertices,
center: [aCenter, bCenter],
max: maxDistanceVertices,
};
}

View File

@@ -1,13 +1,13 @@
import {Buffer, Document, Scene, type Transform, WebIO} from "@gltf-transform/core";
import {mergeDocuments, unpartition} from "@gltf-transform/functions";
import {retrieveFile} from "../tools/upload-file.ts";
import { Buffer, Document, Scene, type Transform, WebIO } from "@gltf-transform/core";
import { mergeDocuments, unpartition } from "@gltf-transform/functions";
import { retrieveFile } from "../tools/upload-file.ts";
let io = new WebIO();
export let extrasNameKey = "__yacv_name";
export let extrasNameValueHelpers = "__helpers";
// @ts-expect-error
let isSmallBuild = typeof __YACV_SMALL_BUILD__ !== 'undefined' && __YACV_SMALL_BUILD__;
let isSmallBuild = typeof __YACV_SMALL_BUILD__ !== "undefined" && __YACV_SMALL_BUILD__;
/**
* Loads a GLB model from a URL and adds it to the document or replaces it if the names match.
@@ -16,133 +16,148 @@ let isSmallBuild = typeof __YACV_SMALL_BUILD__ !== 'undefined' && __YACV_SMALL_B
*
* Remember to call mergeFinalize after all models have been merged (slower required operations).
*/
export async function mergePartial(url: string | Blob, name: string, document: Document, networkFinished: () => void = () => {
}): Promise<Document> {
// Fetch the complete document from the network
// This could be done at the same time as the document is being processed, but I wanted better metrics
let response = await fetchOrRead(url);
let buffer = await response.arrayBuffer();
networkFinished();
export async function mergePartial(
url: string | Blob,
name: string,
document: Document,
networkFinished: () => void = () => {},
): Promise<Document> {
// Fetch the complete document from the network
// This could be done at the same time as the document is being processed, but I wanted better metrics
let response = await fetchOrRead(url);
let buffer = await response.arrayBuffer();
networkFinished();
// Load the new document
let newDoc = null;
let alreadyTried: { [name: string]: boolean } = {}
while (newDoc == null) { // Retry adding extensions as required until the document is loaded
try { // Try to load fast if no extensions are used
newDoc = await io.readBinary(new Uint8Array(buffer));
} catch (e) { // Fallback to wait for download and register big extensions
if (!isSmallBuild && e instanceof Error && e.message.toLowerCase().includes("khr_draco_mesh_compression")) {
if (alreadyTried["draco"]) throw e; else alreadyTried["draco"] = true;
// WARNING: Draco decompression on web is really slow for non-trivial models! (it should work?)
let {KHRDracoMeshCompression} = await import("@gltf-transform/extensions")
// @ts-expect-error
let dracoDecoderWeb = await import("three/examples/jsm/libs/draco/draco_decoder.js");
// @ts-expect-error
let dracoEncoderWeb = await import("three/examples/jsm/libs/draco/draco_encoder.js");
io.registerExtensions([KHRDracoMeshCompression])
.registerDependencies({
'draco3d.decoder': await dracoDecoderWeb.default({}),
'draco3d.encoder': await dracoEncoderWeb.default({})
});
} else if (!isSmallBuild && e instanceof Error && e.message.toLowerCase().includes("ext_texture_webp")) {
if (alreadyTried["webp"]) throw e; else alreadyTried["webp"] = true;
let {EXTTextureWebP} = await import("@gltf-transform/extensions")
io.registerExtensions([EXTTextureWebP]);
} else { // TODO: Add more extensions as required
throw e;
}
}
// Load the new document
let newDoc = null;
let alreadyTried: { [name: string]: boolean } = {};
while (newDoc == null) {
// Retry adding extensions as required until the document is loaded
try {
// Try to load fast if no extensions are used
newDoc = await io.readBinary(new Uint8Array(buffer));
} catch (e) {
// Fallback to wait for download and register big extensions
if (!isSmallBuild && e instanceof Error && e.message.toLowerCase().includes("khr_draco_mesh_compression")) {
if (alreadyTried["draco"]) throw e;
else alreadyTried["draco"] = true;
// WARNING: Draco decompression on web is really slow for non-trivial models! (it should work?)
let { KHRDracoMeshCompression } = await import("@gltf-transform/extensions");
// @ts-expect-error
let dracoDecoderWeb = await import("three/examples/jsm/libs/draco/draco_decoder.js");
// @ts-expect-error
let dracoEncoderWeb = await import("three/examples/jsm/libs/draco/draco_encoder.js");
io.registerExtensions([KHRDracoMeshCompression]).registerDependencies({
"draco3d.decoder": await dracoDecoderWeb.default({}),
"draco3d.encoder": await dracoEncoderWeb.default({}),
});
} else if (!isSmallBuild && e instanceof Error && e.message.toLowerCase().includes("ext_texture_webp")) {
if (alreadyTried["webp"]) throw e;
else alreadyTried["webp"] = true;
let { EXTTextureWebP } = await import("@gltf-transform/extensions");
io.registerExtensions([EXTTextureWebP]);
} else {
// TODO: Add more extensions as required
throw e;
}
}
}
// Remove any previous model with the same name
await document.transform(dropByName(name));
// Remove any previous model with the same name
await document.transform(dropByName(name));
// Ensure consistent names
// noinspection TypeScriptValidateJSTypes
await newDoc.transform(setNames(name));
// Ensure consistent names
// noinspection TypeScriptValidateJSTypes
await newDoc.transform(setNames(name));
// Merge the new document into the current one
mergeDocuments(document, newDoc);
return document;
// Merge the new document into the current one
mergeDocuments(document, newDoc);
return document;
}
export async function mergeFinalize(document: Document): Promise<Document> {
// Single scene & buffer required before loading & rendering
return await document.transform(mergeScenes(), unpartition());
// Single scene & buffer required before loading & rendering
return await document.transform(mergeScenes(), unpartition());
}
export async function toBuffer(doc: Document): Promise<Uint8Array> {
return io.writeBinary(doc);
return io.writeBinary(doc);
}
export async function removeModel(name: string, document: Document): Promise<Document> {
return await document.transform(dropByName(name));
return await document.transform(dropByName(name));
}
/** Given a parsed GLTF document and a name, it forces the names of all elements to be identified by the name (or derivatives) */
function setNames(name: string): Transform {
return (doc: Document) => {
// Do this automatically for all elements changing any name
for (let elem of doc.getGraph().listEdges().map(e => e.getChild())) {
if (!elem.getExtras()) elem.setExtras({});
elem.getExtras()[extrasNameKey] = name;
}
return doc;
return (doc: Document) => {
// Do this automatically for all elements changing any name
for (let elem of doc
.getGraph()
.listEdges()
.map((e) => e.getChild())) {
if (!elem.getExtras()) elem.setExtras({});
elem.getExtras()[extrasNameKey] = name;
}
return doc;
};
}
/** Ensures that all elements with the given name are removed from the document */
function dropByName(name: string): Transform {
return (doc: Document) => {
for (let elem of doc.getGraph().listEdges().map(e => e.getChild())) {
if (elem.getExtras() == null || elem instanceof Scene || elem instanceof Buffer) continue;
if ((elem.getExtras()[extrasNameKey]?.toString() ?? "") == name) {
elem.dispose();
}
}
return doc;
};
return (doc: Document) => {
for (let elem of doc
.getGraph()
.listEdges()
.map((e) => e.getChild())) {
if (elem.getExtras() == null || elem instanceof Scene || elem instanceof Buffer) continue;
if ((elem.getExtras()[extrasNameKey]?.toString() ?? "") == name) {
elem.dispose();
}
}
return doc;
};
}
/** Merges all scenes in the document into a single default scene */
function mergeScenes(): Transform {
return (doc: Document) => {
let root = doc.getRoot();
let scene = root.getDefaultScene() ?? root.listScenes()[0];
for (let dropScene of root.listScenes()) {
if (dropScene === scene) continue;
for (let node of dropScene.listChildren()) {
scene.addChild(node);
}
dropScene.dispose();
}
return (doc: Document) => {
let root = doc.getRoot();
let scene = root.getDefaultScene() ?? root.listScenes()[0];
if (!scene) {
throw new Error("No scene found in GLTF document");
}
for (let dropScene of root.listScenes()) {
if (dropScene === scene) continue;
for (let node of dropScene.listChildren()) {
scene.addChild(node);
}
dropScene.dispose();
}
};
}
/** Fetches a URL or reads it if it is a Blob URL */
async function fetchOrRead(url: string | Blob) {
if (url instanceof Blob) {
// Use the FileReader API as fetch does not support Blob URLs
return new Promise<Response>((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = (event: ProgressEvent<FileReader>) => {
if (event.target && event.target.result) {
resolve(new Response(event.target.result));
} else {
reject(new Error("Failed to read Blob URL: " + url));
}
};
reader.onerror = (error) => {
reject(new Error("Error reading Blob URL: " + url + " - " + error));
};
// Read the Blob URL as an ArrayBuffer
reader.readAsArrayBuffer(new Blob([url]));
});
} else {
// Fetch the URL
return retrieveFile(url);
}
if (url instanceof Blob) {
// Use the FileReader API as fetch does not support Blob URLs
return new Promise<Response>((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = (event: ProgressEvent<FileReader>) => {
if (event.target && event.target.result) {
resolve(new Response(event.target.result));
} else {
reject(new Error("Failed to read Blob URL: " + url));
}
};
reader.onerror = (error) => {
reject(new Error("Error reading Blob URL: " + url + " - " + error));
};
// Read the Blob URL as an ArrayBuffer
reader.readAsArrayBuffer(new Blob([url]));
});
} else {
// Fetch the URL
return retrieveFile(url);
}
}

View File

@@ -1,79 +1,125 @@
// noinspection JSVoidFunctionReturnValueUsed,JSUnresolvedReference
import {Document, type TypedArray} from '@gltf-transform/core'
import {Vector2} from "three/src/math/Vector2.js"
import {Vector3} from "three/src/math/Vector3.js"
import {Matrix4} from "three/src/math/Matrix4.js"
import { Document, type TypedArray } from "@gltf-transform/core";
import { Vector2 } from "three/src/math/Vector2.js";
import { Vector3 } from "three/src/math/Vector3.js";
import { Matrix4 } from "three/src/math/Matrix4.js";
/** Exports the colors used for the axes, primary and secondary. They match the orientation gizmo. */
export const AxesColors = {
x: [[247, 60, 60], [148, 36, 36]],
z: [[108, 203, 38], [65, 122, 23]],
y: [[23, 140, 240], [14, 84, 144]]
}
x: [
[247, 60, 60],
[148, 36, 36],
],
z: [
[108, 203, 38],
[65, 122, 23],
],
y: [
[23, 140, 240],
[14, 84, 144],
],
};
function buildSimpleGltf(doc: Document, rawPositions: number[], rawIndices: number[], rawColors: number[] | null, transform: Matrix4, name: string = '__helper', mode: number = WebGL2RenderingContext.LINES) {
const buffer = doc.getRoot().listBuffers()[0] ?? doc.createBuffer(name + 'Buffer')
const scene = doc.getRoot().getDefaultScene() ?? doc.getRoot().listScenes()[0] ?? doc.createScene(name + 'Scene')
const positions = doc.createAccessor(name + 'Position')
.setArray(new Float32Array(rawPositions) as TypedArray)
.setType('VEC3')
.setBuffer(buffer)
const indices = doc.createAccessor(name + 'Indices')
.setArray(new Uint32Array(rawIndices) as TypedArray)
.setType('SCALAR')
.setBuffer(buffer)
let colors = null;
if (rawColors) {
colors = doc.createAccessor(name + 'Color')
.setArray(new Float32Array(rawColors) as TypedArray)
.setType('VEC4')
.setBuffer(buffer);
}
const material = doc.createMaterial(name + 'Material')
.setAlphaMode('OPAQUE')
const geometry = doc.createPrimitive()
.setIndices(indices)
.setAttribute('POSITION', positions)
.setMode(mode as any)
.setMaterial(material)
if (rawColors) {
geometry.setAttribute('COLOR_0', colors)
}
if (mode == WebGL2RenderingContext.TRIANGLES) {
geometry.setExtras({face_triangles_end: [rawIndices.length / 6, rawIndices.length * 2 / 6, rawIndices.length * 3 / 6, rawIndices.length * 4 / 6, rawIndices.length * 5 / 6, rawIndices.length]})
} else if (mode == WebGL2RenderingContext.LINES) {
geometry.setExtras({edge_points_end: [rawIndices.length / 3, rawIndices.length * 2 / 3, rawIndices.length]})
}
const mesh = doc.createMesh(name + 'Mesh').addPrimitive(geometry)
const node = doc.createNode(name + 'Node').setMesh(mesh).setMatrix(transform.elements as any)
scene.addChild(node)
function buildSimpleGltf(
doc: Document,
rawPositions: number[],
rawIndices: number[],
rawColors: number[] | null,
transform: Matrix4,
name: string = "__helper",
mode: number = WebGL2RenderingContext.LINES,
) {
const buffer = doc.getRoot().listBuffers()[0] ?? doc.createBuffer(name + "Buffer");
const scene = doc.getRoot().getDefaultScene() ?? doc.getRoot().listScenes()[0] ?? doc.createScene(name + "Scene");
if (!scene) throw new Error("Scene is undefined");
if (!rawPositions) throw new Error("rawPositions is undefined");
const positions = doc
.createAccessor(name + "Position")
.setArray(new Float32Array(rawPositions) as TypedArray)
.setType("VEC3")
.setBuffer(buffer);
const indices = doc
.createAccessor(name + "Indices")
.setArray(new Uint32Array(rawIndices) as TypedArray)
.setType("SCALAR")
.setBuffer(buffer);
let colors = null;
if (rawColors) {
colors = doc
.createAccessor(name + "Color")
.setArray(new Float32Array(rawColors) as TypedArray)
.setType("VEC4")
.setBuffer(buffer);
}
const material = doc.createMaterial(name + "Material").setAlphaMode("OPAQUE");
const geometry = doc
.createPrimitive()
.setIndices(indices)
.setAttribute("POSITION", positions)
.setMode(mode as any)
.setMaterial(material);
if (rawColors) {
geometry.setAttribute("COLOR_0", colors);
}
if (mode == WebGL2RenderingContext.TRIANGLES) {
geometry.setExtras({
face_triangles_end: [
rawIndices.length / 6,
(rawIndices.length * 2) / 6,
(rawIndices.length * 3) / 6,
(rawIndices.length * 4) / 6,
(rawIndices.length * 5) / 6,
rawIndices.length,
],
});
} else if (mode == WebGL2RenderingContext.LINES) {
geometry.setExtras({ edge_points_end: [rawIndices.length / 3, (rawIndices.length * 2) / 3, rawIndices.length] });
}
const mesh = doc.createMesh(name + "Mesh").addPrimitive(geometry);
const node = doc
.createNode(name + "Node")
.setMesh(mesh)
.setMatrix(transform.elements as any);
scene.addChild(node);
}
/**
* Create a new Axes helper as a GLTF model, useful for debugging positions and orientations.
*/
export function newAxes(doc: Document, size: Vector3, transform: Matrix4) {
let rawIndices = [0, 1, 2, 3, 4, 5];
let rawPositions = [
0, 0, 0, size.x, 0, 0,
0, 0, 0, 0, size.y, 0,
0, 0, 0, 0, 0, -size.z,
];
let rawColors = [
...(AxesColors.x[0]), 255, ...(AxesColors.x[1]), 255,
...(AxesColors.y[0]), 255, ...(AxesColors.y[1]), 255,
...(AxesColors.z[0]), 255, ...(AxesColors.z[1]), 255
].map(x => x / 255.0);
// Axes at (0, 0, 0)
buildSimpleGltf(doc, rawPositions, rawIndices, rawColors, new Matrix4(), '__helper_axes');
buildSimpleGltf(doc, [0, 0, 0], [0], [1, 1, 1, 1], new Matrix4(), '__helper_axes', WebGL2RenderingContext.POINTS);
// Axes at center
if (new Matrix4() != transform) {
buildSimpleGltf(doc, rawPositions, rawIndices, rawColors, transform, '__helper_axes_center');
buildSimpleGltf(doc, [0, 0, 0], [0], [1, 1, 1, 1], transform, '__helper_axes_center', WebGL2RenderingContext.POINTS);
}
let rawIndices = [0, 1, 2, 3, 4, 5];
let rawPositions = [0, 0, 0, size.x, 0, 0, 0, 0, 0, 0, size.y, 0, 0, 0, 0, 0, 0, -size.z];
let rawColors = [
...(AxesColors.x[0] ?? [255, 0, 0]),
255,
...(AxesColors.x[1] ?? [255, 0, 0]),
255,
...(AxesColors.y[0] ?? [0, 255, 0]),
255,
...(AxesColors.y[1] ?? [0, 255, 0]),
255,
...(AxesColors.z[0] ?? [0, 0, 255]),
255,
...(AxesColors.z[1] ?? [0, 0, 255]),
255,
].map((x) => x / 255.0);
// Axes at (0, 0, 0)
buildSimpleGltf(doc, rawPositions, rawIndices, rawColors, new Matrix4(), "__helper_axes");
buildSimpleGltf(doc, [0, 0, 0], [0], [1, 1, 1, 1], new Matrix4(), "__helper_axes", WebGL2RenderingContext.POINTS);
// Axes at center
if (new Matrix4() != transform) {
buildSimpleGltf(doc, rawPositions, rawIndices, rawColors, transform, "__helper_axes_center");
buildSimpleGltf(
doc,
[0, 0, 0],
[0],
[1, 1, 1, 1],
transform,
"__helper_axes_center",
WebGL2RenderingContext.POINTS,
);
}
}
/**
@@ -83,61 +129,69 @@ export function newAxes(doc: Document, size: Vector3, transform: Matrix4) {
* This ensures that only the back of the grid is always visible, regardless of the camera position.
*/
export function newGridBox(doc: Document, size: Vector3, baseTransform: Matrix4, divisions = 10) {
// Create transformed positions for the inner faces of the box
let allPositions: number[] = [];
let allIndices: number[] = [];
for (let axis of [new Vector3(1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, -1)]) {
for (let positive of [1, -1]) {
let offset = axis.clone().multiply(size.clone().multiplyScalar(0.5 * positive));
let translation = new Matrix4().makeTranslation(offset.x, offset.y, offset.z)
let rotation = new Matrix4().lookAt(new Vector3(), offset, new Vector3(0, 1, 0))
let size2 = new Vector2();
if (axis.x) size2.set(size.z, size.y);
if (axis.y) size2.set(size.x, size.z);
if (axis.z) size2.set(size.x, size.y);
let transform = new Matrix4().multiply(translation).multiply(rotation);
let [rawPositions, rawIndices] = newGridPlane(size2, divisions);
let baseIndex = allPositions.length / 3;
for (let i of rawIndices) {
allIndices.push(i + baseIndex);
}
// Apply transform to the positions before adding them to the list
for (let i = 0; i < rawPositions.length; i += 3) {
let pos = new Vector3(rawPositions[i], rawPositions[i + 1], rawPositions[i + 2]);
pos.applyMatrix4(transform);
allPositions.push(pos.x, pos.y, pos.z);
}
}
// Create transformed positions for the inner faces of the box
let allPositions: number[] = [];
let allIndices: number[] = [];
for (let axis of [new Vector3(1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, -1)]) {
for (let positive of [1, -1]) {
let offset = axis.clone().multiply(size.clone().multiplyScalar(0.5 * positive));
let translation = new Matrix4().makeTranslation(offset.x, offset.y, offset.z);
let rotation = new Matrix4().lookAt(new Vector3(), offset, new Vector3(0, 1, 0));
let size2 = new Vector2();
if (axis.x) size2.set(size.z, size.y);
if (axis.y) size2.set(size.x, size.z);
if (axis.z) size2.set(size.x, size.y);
let transform = new Matrix4().multiply(translation).multiply(rotation);
let [rawPositions, rawIndices] = newGridPlane(size2, divisions);
let baseIndex = allPositions.length / 3;
for (let i of rawIndices) {
allIndices.push(i + baseIndex);
}
// Apply transform to the positions before adding them to the list
for (let i = 0; i < rawPositions.length; i += 3) {
let pos = new Vector3(rawPositions[i], rawPositions[i + 1], rawPositions[i + 2]);
pos.applyMatrix4(transform);
allPositions.push(pos.x, pos.y, pos.z);
}
}
let colors = new Array(allPositions.length / 3 * 4).fill(1);
buildSimpleGltf(doc, allPositions, allIndices, colors, baseTransform, '__helper_grid', WebGL2RenderingContext.TRIANGLES);
}
let colors = new Array((allPositions.length / 3) * 4).fill(1);
buildSimpleGltf(
doc,
allPositions,
allIndices,
colors,
baseTransform,
"__helper_grid",
WebGL2RenderingContext.TRIANGLES,
);
}
export function newGridPlane(size: Vector2, divisions = 10, divisionWidth = 0.002): [number[], number[]] {
const rawPositions = [];
const rawIndices = [];
// Build the grid as triangles
for (let i = 0; i <= divisions; i++) {
const x = -size.x / 2 + size.x * i / divisions;
const y = -size.y / 2 + size.y * i / divisions;
const rawPositions = [];
const rawIndices = [];
// Build the grid as triangles
for (let i = 0; i <= divisions; i++) {
const x = -size.x / 2 + (size.x * i) / divisions;
const y = -size.y / 2 + (size.y * i) / divisions;
// Vertical quad (two triangles)
rawPositions.push(x - divisionWidth * size.x / 2, -size.y / 2, 0);
rawPositions.push(x + divisionWidth * size.x / 2, -size.y / 2, 0);
rawPositions.push(x + divisionWidth * size.x / 2, size.y / 2, 0);
rawPositions.push(x - divisionWidth * size.x / 2, size.y / 2, 0);
const baseIndex = i * 4;
rawIndices.push(baseIndex, baseIndex + 1, baseIndex + 2);
rawIndices.push(baseIndex, baseIndex + 2, baseIndex + 3);
// Vertical quad (two triangles)
rawPositions.push(x - (divisionWidth * size.x) / 2, -size.y / 2, 0);
rawPositions.push(x + (divisionWidth * size.x) / 2, -size.y / 2, 0);
rawPositions.push(x + (divisionWidth * size.x) / 2, size.y / 2, 0);
rawPositions.push(x - (divisionWidth * size.x) / 2, size.y / 2, 0);
const baseIndex = i * 4;
rawIndices.push(baseIndex, baseIndex + 1, baseIndex + 2);
rawIndices.push(baseIndex, baseIndex + 2, baseIndex + 3);
// Horizontal quad (two triangles)
rawPositions.push(-size.x / 2, y - divisionWidth * size.y / 2, 0);
rawPositions.push(size.x / 2, y - divisionWidth * size.y / 2, 0);
rawPositions.push(size.x / 2, y + divisionWidth * size.y / 2, 0);
rawPositions.push(-size.x / 2, y + divisionWidth * size.y / 2, 0);
const baseIndex2 = (divisions + 1 + i) * 4;
rawIndices.push(baseIndex2, baseIndex2 + 1, baseIndex2 + 2);
rawIndices.push(baseIndex2, baseIndex2 + 2, baseIndex2 + 3);
}
return [rawPositions, rawIndices];
}
// Horizontal quad (two triangles)
rawPositions.push(-size.x / 2, y - (divisionWidth * size.y) / 2, 0);
rawPositions.push(size.x / 2, y - (divisionWidth * size.y) / 2, 0);
rawPositions.push(size.x / 2, y + (divisionWidth * size.y) / 2, 0);
rawPositions.push(-size.x / 2, y + (divisionWidth * size.y) / 2, 0);
const baseIndex2 = (divisions + 1 + i) * 4;
rawIndices.push(baseIndex2, baseIndex2 + 1, baseIndex2 + 2);
rawIndices.push(baseIndex2, baseIndex2 + 2, baseIndex2 + 3);
}
return [rawPositions, rawIndices];
}

View File

@@ -85,7 +85,7 @@ export const settings = (async () => {
url = "dev+http://localhost:32323";
}
}
settings.preload[i] = url;
settings.preload[i] = url ?? "";
}
// Auto-decompress the code and other playground settings