Release Healer Man 0.1.16
This commit is contained in:
1 parent
f49e54f35f
commit
b57d2b2839
89 files changed
+6055
-17
No files matched your search
@@ -0,0 +1,101 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { MeshoptDecoder } from "meshoptimizer";
|
||||
import { Vector3 } from "three";
|
||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
||||
import { writeWmoGeometry } from "./build-wmo-arenas.mjs";
|
||||
|
||||
globalThis.self = globalThis;
|
||||
globalThis.ProgressEvent ??= class ProgressEvent {
|
||||
constructor(type, values = {}) { this.type = type; Object.assign(this, values); }
|
||||
};
|
||||
|
||||
class UnionFind {
|
||||
constructor(size) { this.parent = Array.from({ length: size }, (_, index) => index); }
|
||||
find(value) {
|
||||
if (this.parent[value] !== value) this.parent[value] = this.find(this.parent[value]);
|
||||
return this.parent[value];
|
||||
}
|
||||
union(left, right) {
|
||||
const a = this.find(left);
|
||||
const b = this.find(right);
|
||||
if (a !== b) this.parent[b] = a;
|
||||
}
|
||||
}
|
||||
|
||||
function key(point) {
|
||||
return point.map((value) => Math.round(value * 1000)).join(":");
|
||||
}
|
||||
|
||||
async function loadTriangles(file) {
|
||||
await MeshoptDecoder.ready;
|
||||
const bytes = await readFile(file);
|
||||
const buffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
||||
const scene = await new Promise((resolve, reject) => new GLTFLoader()
|
||||
.setMeshoptDecoder(MeshoptDecoder)
|
||||
.parse(buffer, "", (gltf) => resolve(gltf.scene), reject));
|
||||
scene.updateMatrixWorld(true);
|
||||
const triangles = [];
|
||||
const points = [new Vector3(), new Vector3(), new Vector3()];
|
||||
scene.traverse((object) => {
|
||||
if (!object.isMesh || !object.geometry?.getAttribute("position")) return;
|
||||
const positions = object.geometry.getAttribute("position");
|
||||
const indices = object.geometry.getIndex();
|
||||
const count = indices?.count ?? positions.count;
|
||||
const vertexIndex = (element) => indices ? indices.getX(element) : element;
|
||||
for (let element = 0; element < count; element += 3) {
|
||||
triangles.push(points.map((point, corner) => [
|
||||
...point.fromBufferAttribute(positions, vertexIndex(element + corner)).applyMatrix4(object.matrixWorld).toArray(),
|
||||
]));
|
||||
}
|
||||
});
|
||||
return triangles;
|
||||
}
|
||||
|
||||
function distanceSquared(point, target) {
|
||||
return (point[0] - target[0]) ** 2 + (point[1] - target[1]) ** 2 + (point[2] - target[2]) ** 2;
|
||||
}
|
||||
|
||||
export async function extractNavigationComponent({ source, output, teamSpawns }) {
|
||||
const triangles = await loadTriangles(source);
|
||||
const union = new UnionFind(triangles.length);
|
||||
const ownerByVertex = new Map();
|
||||
for (const [triangleIndex, triangle] of triangles.entries()) {
|
||||
for (const point of triangle) {
|
||||
const positionKey = key(point);
|
||||
const owner = ownerByVertex.get(positionKey);
|
||||
if (owner == null) ownerByVertex.set(positionKey, triangleIndex);
|
||||
else union.union(owner, triangleIndex);
|
||||
}
|
||||
}
|
||||
const components = new Map();
|
||||
for (let index = 0; index < triangles.length; index += 1) {
|
||||
const root = union.find(index);
|
||||
const component = components.get(root) ?? [];
|
||||
component.push(triangles[index]);
|
||||
components.set(root, component);
|
||||
}
|
||||
const scored = [...components.values()].map((component) => {
|
||||
const vertices = component.flat();
|
||||
const spawnDistances = teamSpawns.map((spawn) => Math.sqrt(Math.min(...vertices.map((point) => distanceSquared(point, spawn)))));
|
||||
return { component, spawnDistances, score: Math.max(...spawnDistances) };
|
||||
}).sort((left, right) => left.score - right.score || right.component.length - left.component.length);
|
||||
const selected = scored[0];
|
||||
if (!selected?.component.length) throw new Error(`${source}: no navigation component could be selected.`);
|
||||
const values = selected.component.flat(2);
|
||||
const positions = new Float32Array(values);
|
||||
const indices = Uint32Array.from({ length: positions.length / 3 }, (_, index) => index);
|
||||
await writeWmoGeometry(output, [{
|
||||
name: "rated-connected-navigation",
|
||||
positions,
|
||||
normals: null,
|
||||
indices,
|
||||
collisionIndices: indices,
|
||||
flagCounts: {},
|
||||
}], false);
|
||||
return {
|
||||
sourceComponents: components.size,
|
||||
triangles: selected.component.length,
|
||||
vertices: positions.length / 3,
|
||||
spawnDistances: selected.spawnDistances,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user