Add local glTF viewer and visual coverage
Amp-Thread-ID: https://ampcode.com/threads/T-01a02d1c-27fc-7761-9f51-ed97b2dd4e3e Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
This commit is contained in:
+55
-17
@@ -2,7 +2,15 @@ import { Node, type NodeOptions } from "./Node";
|
||||
import type { Scene } from "./Scene";
|
||||
import type { PBRMaterial } from "./materials/PBRMaterial";
|
||||
|
||||
export const VertexKinds = Object.freeze(["positions", "normals", "tangents", "uvs", "colors", "indices"] as const);
|
||||
export const VertexKinds = Object.freeze([
|
||||
"positions",
|
||||
"normals",
|
||||
"tangents",
|
||||
"uvs",
|
||||
"uvs2",
|
||||
"colors",
|
||||
"indices",
|
||||
] as const);
|
||||
export type VertexKind = (typeof VertexKinds)[number];
|
||||
export type MeshOptions = NodeOptions & {
|
||||
geometryId?: number;
|
||||
@@ -27,15 +35,22 @@ export class Mesh extends Node {
|
||||
this.ready = nodeReady.then(async () => {
|
||||
if (options.material) await options.material.ready;
|
||||
await scene.ensureRows(`mesh.${this.id}.faceMaterials`, 1, 16, "u32");
|
||||
this.vertexCount = Number(scene.geometryData(this.geometryId, "positions")?.length ?? 0) / 3;
|
||||
this.indexCount = Number(scene.geometryData(this.geometryId, "indices")?.length ?? 0);
|
||||
this.instanceOf = options.geometryId === undefined ? this.id : this.geometryId;
|
||||
scene.array("meshInfo").write(this.id, [
|
||||
this.geometryId,
|
||||
options.material?.id ?? 0,
|
||||
options.visible === false ? 0 : 1,
|
||||
this.instanceOf,
|
||||
]);
|
||||
this.vertexCount =
|
||||
Number(scene.geometryData(this.geometryId, "positions")?.length ?? 0) /
|
||||
3;
|
||||
this.indexCount = Number(
|
||||
scene.geometryData(this.geometryId, "indices")?.length ?? 0,
|
||||
);
|
||||
this.instanceOf =
|
||||
options.geometryId === undefined ? this.id : this.geometryId;
|
||||
scene
|
||||
.array("meshInfo")
|
||||
.write(this.id, [
|
||||
this.geometryId,
|
||||
options.material?.id ?? 0,
|
||||
options.visible === false ? 0 : 1,
|
||||
this.instanceOf,
|
||||
]);
|
||||
for (const [kind, data] of Object.entries(options.vertexData ?? {}))
|
||||
await this.#setVertexData(kind as VertexKind, data!, false);
|
||||
this.#writeBounds();
|
||||
@@ -60,7 +75,8 @@ export class Mesh extends Node {
|
||||
}
|
||||
|
||||
set material(value: PBRMaterial) {
|
||||
if (value.scene !== this.scene || value.id < 0) throw new Error("Await a material from the same Scene");
|
||||
if (value.scene !== this.scene || value.id < 0)
|
||||
throw new Error("Await a material from the same Scene");
|
||||
this.scene.markDirty(true);
|
||||
this.scene.array("meshInfo").row(this.id)[1] = value.id;
|
||||
void this.scene.updateRenderGraph().catch(() => undefined);
|
||||
@@ -72,13 +88,18 @@ export class Mesh extends Node {
|
||||
}
|
||||
|
||||
async setVertexData(kind: VertexKind, data: ArrayLike<number>) {
|
||||
if (!VertexKinds.includes(kind)) throw new TypeError(`Unknown vertex kind: ${kind}`);
|
||||
if (!VertexKinds.includes(kind))
|
||||
throw new TypeError(`Unknown vertex kind: ${kind}`);
|
||||
await this.ready;
|
||||
await this.#setVertexData(kind, data, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
async #setVertexData(kind: VertexKind, data: ArrayLike<number>, makeUnique: boolean) {
|
||||
async #setVertexData(
|
||||
kind: VertexKind,
|
||||
data: ArrayLike<number>,
|
||||
makeUnique: boolean,
|
||||
) {
|
||||
if (makeUnique && this.scene.geometryReferences(this.geometryId) > 1) {
|
||||
const original = this.geometryId;
|
||||
this.geometryId = await this.scene.cloneGeometry(original);
|
||||
@@ -86,7 +107,15 @@ export class Mesh extends Node {
|
||||
this.scene.referenceGeometry(this.geometryId);
|
||||
this.instanceOf = this.id;
|
||||
this.scene.markDirty(true);
|
||||
this.scene.array("meshInfo").row(this.id).set([this.geometryId, this.materialId, this.isVisible ? 1 : 0, this.id]);
|
||||
this.scene
|
||||
.array("meshInfo")
|
||||
.row(this.id)
|
||||
.set([
|
||||
this.geometryId,
|
||||
this.materialId,
|
||||
this.isVisible ? 1 : 0,
|
||||
this.id,
|
||||
]);
|
||||
}
|
||||
if (kind === "positions") this.vertexCount = data.length / 3;
|
||||
if (kind === "indices") this.indexCount = data.length;
|
||||
@@ -96,10 +125,16 @@ export class Mesh extends Node {
|
||||
|
||||
async setMaterialForFaces(material: PBRMaterial, faces: number | number[]) {
|
||||
await Promise.all([this.ready, material.ready]);
|
||||
if (material.scene !== this.scene) throw new Error("Material must belong to the same Scene");
|
||||
if (material.scene !== this.scene)
|
||||
throw new Error("Material must belong to the same Scene");
|
||||
const list = Array.isArray(faces) ? faces : [faces];
|
||||
const maximum = Math.max(...list);
|
||||
const array = await this.scene.ensureRows(`mesh.${this.id}.faceMaterials`, maximum + 1, 16, "u32");
|
||||
const array = await this.scene.ensureRows(
|
||||
`mesh.${this.id}.faceMaterials`,
|
||||
maximum + 1,
|
||||
16,
|
||||
"u32",
|
||||
);
|
||||
this.scene.markDirty(true);
|
||||
for (const face of list) {
|
||||
if (!Number.isInteger(face) || face < 0) throw new RangeError("face");
|
||||
@@ -120,7 +155,10 @@ export class Mesh extends Node {
|
||||
minimum[lane] = Math.min(minimum[lane], positions[index + lane]);
|
||||
maximum[lane] = Math.max(maximum[lane], positions[index + lane]);
|
||||
}
|
||||
this.scene.array("bounds").row(this.id).set([...minimum, 0, ...maximum, 0]);
|
||||
this.scene
|
||||
.array("bounds")
|
||||
.row(this.id)
|
||||
.set([...minimum, 0, ...maximum, 0]);
|
||||
}
|
||||
|
||||
override async dispose() {
|
||||
|
||||
Reference in New Issue
Block a user