Update Healer Man server
This commit is contained in:
1 parent
69809f7228
commit
ff20ab8779
101 files changed
+16713
-945
No files matched your search
@@ -14,6 +14,10 @@ import { prepareDungeonMaterial } from "../../src/scene/productionDungeonMateria
|
||||
import { applyDungeonMeshGraphics } from "../../src/scene/graphicsMaterialQuality";
|
||||
import { setDungeonShadowLightPose } from "../../src/scene/dungeonShadowLightRuntime";
|
||||
import type { ShadowQuality } from "../../src/game/graphicsSettings";
|
||||
import { cacheStaticMatrices, instanceRepeatedStaticMeshes } from "../../src/scene/staticSceneInstancing";
|
||||
import { createStaticVisibilityCells, registerStaticVisibility } from "../../src/scene/staticSceneVisibility";
|
||||
import { materialTextures } from "../../src/scene/threeResourceDisposal";
|
||||
import { configureWowTerrainLoader } from "../../src/scene/wowTerrainMaterial";
|
||||
|
||||
// Isolated asset QA uses shipping assets and the production material/light helpers.
|
||||
// It intentionally does not claim gameplay, collision or encounter certification.
|
||||
@@ -59,6 +63,7 @@ const controls = new OrbitControls(camera, renderer.domElement);
|
||||
const loader = new GLTFLoader();
|
||||
const decoder = new KTX2Loader().setTranscoderPath("/basis/").detectSupport(renderer);
|
||||
loader.setKTX2Loader(decoder).setMeshoptDecoder(MeshoptDecoder);
|
||||
configureWowTerrainLoader(loader);
|
||||
const root = new THREE.Group();
|
||||
root.position.fromArray(definition.transform.position);
|
||||
root.rotation.fromArray([...definition.transform.rotation, "XYZ"]);
|
||||
@@ -66,20 +71,49 @@ root.scale.setScalar(definition.transform.scale);
|
||||
scene.add(root);
|
||||
const assets = resolveDungeonAssets(definition);
|
||||
if (assets.visual.length !== definition.assets.visual.length) throw new Error(`Missing shipping visual for ${id}`);
|
||||
const visualOverride = params.get("visualOverride");
|
||||
const runtimePreparation = params.get("runtimePreparation") === "1";
|
||||
const preparationReports: { batches: number; instances: number; removedMeshes: number; visibilityCells: number }[] = [];
|
||||
if (visualOverride && (assets.visual.length !== 1
|
||||
|| !/^\/artifacts\/dungeon-pipeline\/[a-zA-Z0-9_./-]+\.glb$/.test(visualOverride)
|
||||
|| visualOverride.includes(".."))) {
|
||||
throw new Error("A review override must be a single GLB in artifacts/dungeon-pipeline.");
|
||||
}
|
||||
for (const url of assets.visual) {
|
||||
const reviewUrl = id === "shadowfang-keep" && params.get("stage") === "before-shadowfang"
|
||||
const reviewUrl = visualOverride ?? (id === "shadowfang-keep" && params.get("stage") === "before-shadowfang"
|
||||
? "/artifacts/playtest/wow-environment-quality/before-shadowfang/visual.glb"
|
||||
: id === "shadowfang-keep" && params.get("stage") === "repacked-shadowfang"
|
||||
? "/artifacts/dungeon-pipeline/shadowfang-keep-visual.reoptimized-repacked.glb"
|
||||
: id === "shadowfang-keep" && params.get("stage") === "selective-shadowfang"
|
||||
? "/artifacts/dungeon-pipeline/shadowfang-keep-selective-error-0012.glb" : url;
|
||||
? "/artifacts/dungeon-pipeline/shadowfang-keep-selective-error-0012.glb" : url);
|
||||
const gltf = await loader.loadAsync(reviewUrl);
|
||||
const preparedMaterials = new Map<string, THREE.Material>();
|
||||
const prepare = (material: THREE.Material) => {
|
||||
if (runtimePreparation && preparedMaterials.has(material.uuid)) return preparedMaterials.get(material.uuid)!;
|
||||
const next = prepareDungeonMaterial(material, p.materials, definition.environmentMode);
|
||||
preparedMaterials.set(material.uuid, next);
|
||||
return next;
|
||||
};
|
||||
gltf.scene.traverse((node) => {
|
||||
if (!(node instanceof THREE.Mesh)) return;
|
||||
node.material = Array.isArray(node.material)
|
||||
? node.material.map((material) => prepareDungeonMaterial(material, p.materials, definition.environmentMode))
|
||||
: prepareDungeonMaterial(node.material, p.materials, definition.environmentMode);
|
||||
? node.material.map(prepare) : prepare(node.material);
|
||||
if (runtimePreparation) {
|
||||
node.castShadow = false;
|
||||
node.receiveShadow = true;
|
||||
}
|
||||
if (runtimePreparation && node instanceof THREE.InstancedMesh) {
|
||||
node.computeBoundingBox();
|
||||
node.computeBoundingSphere();
|
||||
}
|
||||
});
|
||||
if (runtimePreparation) {
|
||||
const instancing = instanceRepeatedStaticMeshes(gltf.scene, 3, undefined, definition.id === "scholomance");
|
||||
const cells = createStaticVisibilityCells(gltf.scene);
|
||||
cacheStaticMatrices(gltf.scene);
|
||||
registerStaticVisibility(scene, cells);
|
||||
preparationReports.push({ ...instancing, visibilityCells: cells.length });
|
||||
}
|
||||
root.add(gltf.scene);
|
||||
}
|
||||
let quality: ShadowQuality = "standard";
|
||||
@@ -122,14 +156,42 @@ function diagnostics() {
|
||||
meshes++;
|
||||
if (node.castShadow) casters++;
|
||||
for (const material of Array.isArray(node.material) ? node.material : [node.material]) {
|
||||
const map = (material as THREE.MeshStandardMaterial).map;
|
||||
if (map) textures.set(map.uuid, { name: map.name, width: map.image?.width, height: map.image?.height, anisotropy: map.anisotropy });
|
||||
for (const texture of materialTextures(material)) {
|
||||
textures.set(texture.uuid, { name: texture.name, width: texture.image?.width,
|
||||
height: texture.image?.height, anisotropy: texture.anisotropy });
|
||||
}
|
||||
}
|
||||
});
|
||||
return { meshes, casters, textures: [...textures.values()], drawCalls: renderer.info.render.calls,
|
||||
return { meshes, casters, textures: [...textures.values()], runtimePreparation, preparationReports, drawCalls: renderer.info.render.calls,
|
||||
renderedTriangles: renderer.info.render.triangles, geometries: renderer.info.memory.geometries,
|
||||
lightTarget: light.target.position.toArray(), camera: camera.position.toArray(), maxAnisotropy: renderer.capabilities.getMaxAnisotropy() };
|
||||
}
|
||||
function setCamera(eye: [number, number, number], target: [number, number, number]) {
|
||||
camera.position.fromArray(eye);
|
||||
controls.target.fromArray(target);
|
||||
controls.update();
|
||||
}
|
||||
function groundSamples() {
|
||||
root.updateMatrixWorld(true);
|
||||
const ray = new THREE.Raycaster();
|
||||
return viewpoints.map((entry) => {
|
||||
const origin = new THREE.Vector3(...entry.position).add(new THREE.Vector3(0, 20, 0));
|
||||
ray.set(origin, new THREE.Vector3(0, -1, 0));
|
||||
ray.far = 80;
|
||||
const seen = new Set<string>();
|
||||
const hits = ray.intersectObject(root, true).flatMap((hit) => {
|
||||
const mesh = hit.object as THREE.Mesh;
|
||||
const material = (Array.isArray(mesh.material)
|
||||
? mesh.material[hit.face?.materialIndex ?? 0] : mesh.material) as THREE.MeshStandardMaterial;
|
||||
const key = `${mesh.uuid}:${hit.instanceId}:${Math.round(hit.point.y * 1000)}`;
|
||||
if (seen.has(key)) return [];
|
||||
seen.add(key);
|
||||
return [{ mesh: mesh.name, material: material.name, textured: materialTextures(material).length > 0,
|
||||
height: hit.point.y, heightAboveFoot: hit.point.y - entry.position[1], point: hit.point.toArray() }];
|
||||
}).slice(0, 8);
|
||||
return { name: entry.name, position: entry.position, hits };
|
||||
});
|
||||
}
|
||||
view(0);
|
||||
setQuality("standard");
|
||||
let frames = 0;
|
||||
@@ -148,5 +210,5 @@ renderer.setAnimationLoop(() => {
|
||||
renderer.render(scene, camera);
|
||||
frames++;
|
||||
});
|
||||
Object.assign(window, { wowEnvironmentReview: { ready: true, id, viewpoints, view, setQuality, diagnostics, resetFrameSample, frameSample, presentation: p,
|
||||
Object.assign(window, { wowEnvironmentReview: { ready: true, id, viewpoints, view, setCamera, setQuality, diagnostics, groundSamples, resetFrameSample, frameSample, presentation: p,
|
||||
catalog: definitions.filter((entry) => !entry.id.includes("bite-sized")).map((entry) => ({ id: entry.id, title: entry.title })) } });
|
||||
Reference in new issue
Block a user