Update Healer Man server

This commit is contained in:
phenom committed 2026-10-06 22:25:34 -04:00
1 parent 6114a2295f
commit 69809f7228
593 files changed
+903643 -40774

No files matched your search

@@ -0,0 +1,746 @@
import { createHash } from "node:crypto";
import { Accessor } from "@gltf-transform/core";
import { KHRMaterialsVariants } from "@gltf-transform/extensions";
import sharp from "sharp";
const BARREN_POLICY_ID = "rom-barren-caves-wretched-tunnel-surface-quality-v2";
const HOTO_POLICY_ID = "rom-hoto-source-rock-detail-v2";
const HOTO_LEGACY_POLICY_ID = "rom-hoto-source-rock-detail-v1";
const BARREN_SOURCE = Object.freeze({
model: "model/dungeon/wretched_grotto/act_world_wretched_grotto.ros",
sha256: "d2de5b83f63248b9b6f6d17e13104ee3cddbc4e77903d4eb561ca5731556bab7",
material: "runewaker_material_12",
texture: "texture/dungeon/wretched_grotto/wretched_tunnel",
image: "a6d9c718_wretched_tunnel",
imageSha256: "0f05ae62b2486ae90a9836873fac287fd89169abcef962ac7dfacd81fb469a7c",
});
const HOTO_SOURCE = Object.freeze({
model: "model/dungeon/echoes_of_the_sea/act_echoes_of_the_sea.ros",
sha256: "a0bd77c09045f8bfa1507484bbb2c2ff0cc72d397df4f769fc4e6d5aab14f8a2",
});
const HOTO_TARGETS = Object.freeze({
runewaker_material_96: Object.freeze({
nativeMaterial: "runewaker_material_96", texture: "texture/dungeon/echoes_of_the_sea/echoes_of_the_sea_01",
image: "122cc01b_echoes_of_the_sea_01", imageSha256: "ea395afa1d0c65aa57ed7e675a352ca47aab0b1f7a629a3bebdf36e03621a3b1",
floorStrength: 0.85, floorBaseStrength: 0, wallStrength: 0.85, legacyWallStrength: 0.45,
wallBaseContrast: 0.6, wallDetailVariation: 1, useOwnBaseMean: true,
}),
runewaker_material_53: Object.freeze({
nativeMaterial: "runewaker_material_53", texture: "texture/dungeon/echoes_of_the_sea/echoes_of_the_sea_04",
image: "5c863840_echoes_of_the_sea_04", imageSha256: "fbb0eb3ac74689cf2e7b6bc043bc15f1c3cbc4638fc15675ff0710037e9666f9",
floorStrength: 0.85, floorBaseStrength: 0, wallStrength: 0.85, legacyWallStrength: 0.45,
wallBaseContrast: 0.6, wallDetailVariation: 1, useOwnBaseMean: true,
}),
runewaker_material_26: Object.freeze({
nativeMaterial: "runewaker_material_94", texture: "texture/dungeon/echoes_of_the_sea/echoes_of_the_sea_02",
image: "5672f062_echoes_of_the_sea_02", imageSha256: "bc32a6751da4db246d50f66d2c9fb625f640be652956d7022a4feb649d62a1d2",
floorVariantRole: "floor", floorStrength: 0, floorBaseStrength: 1, wallStrength: 0, legacyWallStrength: 0,
wallBaseContrast: 1, wallDetailVariation: 0,
}),
runewaker_material_18: Object.freeze({
nativeMaterial: "runewaker_material_95", texture: "texture/dungeon/echoes_of_the_sea/echoes_of_the_sea_03",
image: "b2ababb9_echoes_of_the_sea_03", imageSha256: "7e20e5f22744d638fa9bd983c13844800bc316e3502ab092bf6d8b6f68a1d018",
floorVariantRole: "wall", floorStrength: 0, floorBaseStrength: 1, wallStrength: 0.45, legacyWallStrength: 0.45,
wallBaseContrast: 1, wallDetailVariation: 0,
}),
});
const HOTO_DETAIL = Object.freeze({
floor: Object.freeze({
nativeMaterial: "runewaker_material_94", canonicalMaterial: "runewaker_material_26",
texture: "texture/dungeon/echoes_of_the_sea/echoes_of_the_sea_02",
image: "5672f062_echoes_of_the_sea_02", imageSha256: "bc32a6751da4db246d50f66d2c9fb625f640be652956d7022a4feb649d62a1d2",
}),
wall: Object.freeze({
nativeMaterial: "runewaker_material_95", canonicalMaterial: "runewaker_material_18",
texture: "texture/dungeon/echoes_of_the_sea/echoes_of_the_sea_03",
image: "b2ababb9_echoes_of_the_sea_03", imageSha256: "7e20e5f22744d638fa9bd983c13844800bc316e3502ab092bf6d8b6f68a1d018",
}),
});
const HOTO_VARIANTS = Object.freeze({ floor: "rom-detail-hoto-floor", wall: "rom-detail-hoto-wall" });
const sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex");
const normalizeResource = (value) => String(value ?? "").replaceAll("\\", "/").replace(/^\/+/, "").toLowerCase();
const accessorHash = (accessor) => {
const array = accessor?.getArray();
return array ? sha256(Buffer.from(array.buffer, array.byteOffset, array.byteLength)) : null;
};
function verifySourceReport(sourceReport, source) {
if (normalizeResource(sourceReport?.sourceModel) !== source.model.toLowerCase()) {
throw new Error(`Surface policy source ROS path does not match ${source.model}.`);
}
if (String(sourceReport?.sourceResourceSha256 ?? "").toLowerCase() !== source.sha256) {
throw new Error(`Surface policy source ROS SHA-256 does not match ${source.model}.`);
}
}
function verifyNativeMaterial(sourceReport, nativeName, texturePath) {
const rows = (sourceReport?.materials ?? []).filter((row) => row.name === nativeName);
if (rows.length !== 1 || normalizeResource(rows[0].sourceTexture) !== texturePath.toLowerCase()) {
throw new Error(`Expected exact native material/texture binding ${nativeName} -> ${texturePath}.`);
}
return rows[0];
}
function listVariants(document) {
const root = document.getRoot();
const extension = root.listExtensionsUsed().find((item) => item.extensionName === KHRMaterialsVariants.EXTENSION_NAME)
?? document.createExtension(KHRMaterialsVariants).setRequired(false);
const byName = new Map(extension.listVariants().map((variant) => [variant.getName(), variant]));
const getOrCreate = (name) => byName.get(name) ?? (byName.set(name, extension.createVariant(name)), byName.get(name));
return { extension, getOrCreate };
}
function addSurfaceMappings(document, primitive, carrierByName, variantByName, names = [HOTO_VARIANTS.floor, HOTO_VARIANTS.wall]) {
if (primitive.getExtension(KHRMaterialsVariants.EXTENSION_NAME)) {
throw new Error("Surface-quality policy will not replace an existing KHR_materials_variants mapping.");
}
const variants = document.getRoot().listExtensionsUsed().find((item) => item.extensionName === KHRMaterialsVariants.EXTENSION_NAME);
const mappingList = variants.createMappingList();
for (const name of names) {
const carrier = carrierByName.get(name);
const variant = variantByName.get(name);
if (!carrier || !variant) throw new Error(`Missing surface-detail carrier or variant ${name}.`);
mappingList.addMapping(variants.createMapping().setMaterial(carrier).addVariant(variant));
}
primitive.setExtension(KHRMaterialsVariants.EXTENSION_NAME, mappingList);
}
function getOrCreateCarrier(document, sourceTexture, name, extras) {
const root = document.getRoot();
const found = root.listMaterials().find((material) => material.getName() === `${name}:source-tile-carrier`);
if (found) {
if (found.getBaseColorTexture() !== sourceTexture || found.getExtras()?.runewakerSurfaceDetailCarrier?.policyId !== extras.policyId) {
throw new Error(`Existing surface-detail carrier ${name} does not match its pinned source.`);
}
return found;
}
return document.createMaterial(`${name}:source-tile-carrier`)
.setBaseColorTexture(sourceTexture)
.setMetallicFactor(0)
.setRoughnessFactor(1)
.setExtras({ runewakerSurfaceDetailCarrier: extras });
}
async function meanLinearFromPng(bytes) {
const { data, info } = await sharp(bytes).removeAlpha().raw().toBuffer({ resolveWithObject: true });
if (info.channels < 3 || info.width < 1 || info.height < 1) throw new Error("Surface-detail source image must contain RGB pixels.");
const sums = [0, 0, 0];
for (let offset = 0; offset < data.length; offset += info.channels) {
for (let channel = 0; channel < 3; channel += 1) {
const srgb = data[offset + channel] / 255;
sums[channel] += srgb <= 0.04045 ? srgb / 12.92 : ((srgb + 0.055) / 1.055) ** 2.4;
}
}
const count = info.width * info.height;
return sums.map((value) => value / count);
}
function trianglePositions(matrix, position, indices) {
return indices.map((index) => {
const value = position.getElement(index, [0, 0, 0]);
return [
matrix[0] * value[0] + matrix[4] * value[1] + matrix[8] * value[2] + matrix[12],
matrix[1] * value[0] + matrix[5] * value[1] + matrix[9] * value[2] + matrix[13],
matrix[2] * value[0] + matrix[6] * value[1] + matrix[10] * value[2] + matrix[14],
];
});
}
function sub3(left, right) { return [left[0] - right[0], left[1] - right[1], left[2] - right[2]]; }
function cross3(left, right) {
return [left[1] * right[2] - left[2] * right[1], left[2] * right[0] - left[0] * right[2], left[0] * right[1] - left[1] * right[0]];
}
function length3(value) { return Math.hypot(value[0], value[1], value[2]); }
const BARREN_PATCHES = Object.freeze([
{ key: "u0-v0", col: 0, row: 0, rect: { left: 0, top: 0, width: 256, height: 256 } },
{ key: "u3-v0", col: 3, row: 0, rect: { left: 768, top: 0, width: 256, height: 256 } },
{ key: "u0-v1", col: 0, row: 1, rect: { left: 0, top: 256, width: 256, height: 256 } },
{ key: "u3-v1", col: 3, row: 1, rect: { left: 768, top: 256, width: 256, height: 256 } },
].map((patch) => Object.freeze({
...patch,
uvRect: { u0: patch.col / 4, u1: (patch.col + 1) / 4, v0: patch.row / 2, v1: (patch.row + 1) / 2 },
})));
const BARREN_WALL_DETAIL_PATCH = Object.freeze({
key: "wall-u1-v0",
rect: Object.freeze({ left: 256, top: 0, width: 256, height: 256 }),
textureName: "rom-barren-wretched-tunnel-wall-detail-u1-v0",
});
function barrenPatchForTriangle(uvs, worldNormalY) {
if (worldNormalY < 0.7) return null;
const tolerance = 0.0025;
const minU = Math.min(...uvs.map((uv) => uv[0]));
const maxU = Math.max(...uvs.map((uv) => uv[0]));
const minV = Math.min(...uvs.map((uv) => uv[1]));
const maxV = Math.max(...uvs.map((uv) => uv[1]));
return BARREN_PATCHES.find((patch) => minU >= patch.uvRect.u0 - tolerance && maxU <= patch.uvRect.u1 + tolerance
&& minV >= patch.uvRect.v0 - tolerance && maxV <= patch.uvRect.v1 + tolerance)?.key ?? null;
}
async function applyBarrenFloorPolicy(document, sourceReport) {
verifySourceReport(sourceReport, BARREN_SOURCE);
verifyNativeMaterial(sourceReport, BARREN_SOURCE.material, BARREN_SOURCE.texture);
const root = document.getRoot();
const sourceMaterial = root.listMaterials().find((material) => material.getName() === BARREN_SOURCE.material);
if (!sourceMaterial) throw new Error("Barren atlas-safe policy cannot find native material 12.");
const sourceTexture = sourceMaterial.getBaseColorTexture();
const sourcePng = Buffer.from(sourceTexture?.getImage() ?? []);
if (sourceTexture?.getName() !== BARREN_SOURCE.image || sha256(sourcePng) !== BARREN_SOURCE.imageSha256) {
throw new Error("Barren material 12 embedded atlas differs from the pinned native prepared PNG.");
}
const imageMeta = await sharp(sourcePng).metadata();
if (imageMeta.width !== 1024 || imageMeta.height !== 512) throw new Error("Barren atlas dimensions differ from the reviewed 1024×512 source.");
const baseMeanLinear = await meanLinearFromPng(sourcePng);
// Migrate the previous whole-atlas x4 experiment back to authentic UV0
// before splitting only faces proven to stay inside a native floor island.
let legacyRepeatPrimitivesRestored = 0;
for (const node of root.listNodes()) {
const mesh = node.getMesh();
if (!mesh) continue;
for (const primitive of mesh.listPrimitives()) {
if (primitive.getExtras()?.romUvAdaptation?.id !== "rom-barren-caves-wretched-tunnel-repeat-x4") continue;
const material = primitive.getMaterial();
const uv0 = primitive.getAttribute("TEXCOORD_0");
const uv1 = primitive.getAttribute("TEXCOORD_1");
if (material?.getBaseColorTexture()?.getName() !== BARREN_SOURCE.image || !uv0 || !uv1
|| uv0.getCount() !== uv1.getCount()) {
throw new Error("Cannot safely restore an earlier Barren whole-atlas x4 primitive.");
}
for (let index = 0; index < uv0.getCount(); index += 1) {
const original = uv0.getElement(index, [0, 0]);
const adapted = uv1.getElement(index, [0, 0]);
if (Math.abs(adapted[0] - original[0] * 4) > 1e-6 || Math.abs(adapted[1] - original[1] * 4) > 1e-6) {
throw new Error("Earlier Barren repeat marker does not contain the expected x4 UV1 values.");
}
}
const extras = { ...(primitive.getExtras() ?? {}) };
delete extras.romUvAdaptation;
primitive.setExtras(extras);
primitive.setAttribute("TEXCOORD_1", null);
primitive.setMaterial(sourceMaterial);
legacyRepeatPrimitivesRestored += 1;
}
}
const alreadyApplied = sourceMaterial.getExtras()?.runewakerSurfaceDetail?.policyId === BARREN_POLICY_ID;
if (alreadyApplied) {
const surfaceDetail = sourceMaterial.getExtras()?.runewakerSurfaceDetail;
const cropTextures = root.listTextures().filter((texture) => texture.getExtras()?.romSourcePixelCrop?.policyId === BARREN_POLICY_ID);
const cropMaterials = root.listMaterials().filter((material) => material.getExtras()?.romUvAdaptation?.id === BARREN_POLICY_ID);
const expectedCropMaterialNames = new Set(BARREN_PATCHES.map((patch) => `${BARREN_SOURCE.material}_${patch.key}_crop_repeat4`));
const actualCropMaterialNames = new Set(cropMaterials.map((material) => material.getName()));
const wallTexture = root.listTextures().find((texture) => texture.getName() === BARREN_WALL_DETAIL_PATCH.textureName);
const wallCarrier = root.listMaterials().find((material) => material.getName() === "rom-detail-barren-caves-wall:source-tile-carrier");
const wallInfo = wallCarrier?.getBaseColorTextureInfo();
if (surfaceDetail.wallBaseContrast !== 0.25 || surfaceDetail.wallStrength !== 0.85 || surfaceDetail.wallDetailVariation !== 1
|| JSON.stringify(surfaceDetail.baseMeanLinear) !== JSON.stringify(baseMeanLinear)
|| cropTextures.length !== BARREN_PATCHES.length + 1 || !wallTexture || wallCarrier?.getBaseColorTexture() !== wallTexture
|| wallInfo?.getWrapS() !== 33648 || wallInfo?.getWrapT() !== 33648
|| actualCropMaterialNames.size !== BARREN_PATCHES.length
|| [...expectedCropMaterialNames].some((name) => !actualCropMaterialNames.has(name))) {
throw new Error("Barren atlas-safe policy marker is present but its exact floor/wall crop carriers are incomplete.");
}
return {
id: BARREN_POLICY_ID, status: "already-applied", cropTextureCount: cropTextures.length,
cropMaterialCount: actualCropMaterialNames.size, cropMaterialInstanceCount: cropMaterials.length,
wallDetailTextureName: wallTexture.getName(), wallDetailWrap: "mirrored-repeat",
repeatFactor: 4, trianglePartitionChanged: true,
sourceModel: BARREN_SOURCE.model, sourceModelSha256: BARREN_SOURCE.sha256,
};
}
if (root.listTextures().some((texture) => texture.getExtras()?.romSourcePixelCrop?.policyId === BARREN_POLICY_ID)) {
throw new Error("Barren source GLB already contains partial atlas-safe floor crops.");
}
const buffer = root.listBuffers()[0];
if (!buffer) throw new Error("Barren canonical GLB has no binary buffer for UV1.");
const cropped = new Map();
for (const patch of BARREN_PATCHES) {
const png = await sharp(sourcePng).extract(patch.rect).png().toBuffer();
const rgba = await sharp(png).ensureAlpha().raw().toBuffer();
const meanLinear = await meanLinearFromPng(png);
const sourcePixelCrop = {
version: 1,
sourceImageName: BARREN_SOURCE.image,
sourcePreparedPngSha256: BARREN_SOURCE.imageSha256,
sourceWidth: 1024,
sourceHeight: 512,
rect: patch.rect,
croppedPngSha256: sha256(png),
croppedRgbaSha256: sha256(rgba),
policyId: BARREN_POLICY_ID,
};
const texture = document.createTexture(`rom-barren-wretched-tunnel-${patch.key}`)
.setMimeType("image/png")
.setImage(png)
.setExtras({ romSourcePixelCrop: sourcePixelCrop });
cropped.set(patch.key, { patch, png, meanLinear, sourcePixelCrop, texture });
}
const wallDetailPng = await sharp(sourcePng).extract(BARREN_WALL_DETAIL_PATCH.rect).png().toBuffer();
const wallDetailRgba = await sharp(wallDetailPng).ensureAlpha().raw().toBuffer();
const wallDetailCrop = {
version: 1,
sourceImageName: BARREN_SOURCE.image,
sourcePreparedPngSha256: BARREN_SOURCE.imageSha256,
sourceWidth: 1024,
sourceHeight: 512,
rect: BARREN_WALL_DETAIL_PATCH.rect,
croppedPngSha256: sha256(wallDetailPng),
croppedRgbaSha256: sha256(wallDetailRgba),
policyId: BARREN_POLICY_ID,
};
const wallDetailTexture = document.createTexture(BARREN_WALL_DETAIL_PATCH.textureName)
.setMimeType("image/png")
.setImage(wallDetailPng)
.setExtras({ romSourcePixelCrop: wallDetailCrop });
const wallDetailMeanLinear = await meanLinearFromPng(wallDetailPng);
const variantInfo = listVariants(document);
const variantNames = ["rom-detail-barren-caves-floor", "rom-detail-barren-caves-wall"];
const variantByName = new Map(variantNames.map((name) => [name, variantInfo.getOrCreate(name)]));
const baseExtras = sourceMaterial.getExtras() ?? {};
const cropForDetail = cropped.get("u0-v0");
const sourceProof = {
sourceModel: BARREN_SOURCE.model,
sourceModelSha256: BARREN_SOURCE.sha256,
sourceMaterial: BARREN_SOURCE.material,
sourceTexture: BARREN_SOURCE.texture,
sourceImageName: BARREN_SOURCE.image,
sourcePreparedPngSha256: BARREN_SOURCE.imageSha256,
sourcePixelCrop: cropForDetail.sourcePixelCrop,
baseMeanLinear,
wallBaseContrast: 0.25,
wallDetailVariation: 1,
wallDetailImageName: BARREN_WALL_DETAIL_PATCH.textureName,
wallDetailSourcePixelCrop: wallDetailCrop,
wallDetailMeanLinear,
wallDetailWrap: "mirrored-repeat",
};
sourceMaterial.setExtras({
...baseExtras,
runewakerSurfaceDetail: {
version: 1,
floorVariant: variantNames[0],
wallVariant: variantNames[1],
periodMeters: 2,
floorStrength: 0,
wallStrength: 0.85,
baseMeanLinear,
wallBaseContrast: 0.25,
wallDetailVariation: 1,
floorMeanLinear: cropForDetail.meanLinear,
wallMeanLinear: wallDetailMeanLinear,
policyId: BARREN_POLICY_ID,
sourceProof,
},
});
const carrierExtras = { version: 1, policyId: BARREN_POLICY_ID, sourceImageName: BARREN_SOURCE.image, sourcePixelCrop: cropForDetail.sourcePixelCrop };
const carriers = new Map([
[variantNames[0], getOrCreateCarrier(document, cropForDetail.texture, variantNames[0], { ...carrierExtras, variantName: variantNames[0] })],
[variantNames[1], getOrCreateCarrier(document, wallDetailTexture, variantNames[1], {
...carrierExtras,
variantName: variantNames[1],
sourcePixelCrop: wallDetailCrop,
sourceImageName: BARREN_WALL_DETAIL_PATCH.textureName,
wrap: "mirrored-repeat",
})],
]);
carriers.get(variantNames[1]).getBaseColorTextureInfo().setWrapS(33648).setWrapT(33648);
const reports = [];
let adaptedTriangles = 0;
let untouchedTriangles = 0;
for (const node of root.listNodes()) {
const mesh = node.getMesh();
if (!mesh) continue;
const matrix = node.getWorldMatrix();
const originalPrimitives = [...mesh.listPrimitives()];
for (const [primitiveIndex, primitive] of originalPrimitives.entries()) {
if (primitive.getMaterial() !== sourceMaterial) continue;
if (primitive.getExtension(KHRMaterialsVariants.EXTENSION_NAME)) throw new Error("Barren material 12 already has variant mappings.");
const position = primitive.getAttribute("POSITION");
const normal = primitive.getAttribute("NORMAL");
const uv0 = primitive.getAttribute("TEXCOORD_0");
const indices = primitive.getIndices();
if (!position || !normal || !uv0 || !indices || uv0.getType() !== Accessor.Type.VEC2) {
throw new Error(`${node.getName()} material 12 primitive ${primitiveIndex} lacks position/normal/index/UV0 data.`);
}
const indexArray = indices.getArray();
const indicesByCrop = new Map(BARREN_PATCHES.map((patch) => [patch.key, []]));
const untouched = [];
for (let offset = 0; offset < indexArray.length; offset += 3) {
const triangle = [indexArray[offset], indexArray[offset + 1], indexArray[offset + 2]];
const points = trianglePositions(matrix, position, triangle);
const cross = cross3(sub3(points[1], points[0]), sub3(points[2], points[0]));
const magnitude = length3(cross);
const normalY = magnitude > 1e-12 ? Math.abs(cross[1]) / magnitude : 0;
const triangleUvs = triangle.map((index) => uv0.getElement(index, [0, 0]));
const key = barrenPatchForTriangle(triangleUvs, normalY);
(key ? indicesByCrop.get(key) : untouched).push(...triangle);
}
const activeCrops = [...indicesByCrop].filter(([, group]) => group.length > 0);
if (!activeCrops.length) continue;
const originalTriangleCount = indexArray.length / 3;
const before = accessorHash(indices);
const primitiveOutputs = [];
if (untouched.length) {
primitive.setIndices(document.createAccessor(`${node.getName()}:${BARREN_POLICY_ID}:untouched-indices`)
.setType(Accessor.Type.SCALAR).setArray(new indexArray.constructor(untouched)).setBuffer(buffer));
primitive.setExtension(KHRMaterialsVariants.EXTENSION_NAME, (() => {
const current = document.getRoot().listExtensionsUsed().find((item) => item.extensionName === KHRMaterialsVariants.EXTENSION_NAME);
const mappingList = current.createMappingList();
for (const name of variantNames) mappingList.addMapping(current.createMapping().setMaterial(carriers.get(name)).addVariant(variantByName.get(name)));
return mappingList;
})());
primitiveOutputs.push({ primitive, triangles: untouched.length / 3, category: "untouched-atlas" });
} else mesh.removePrimitive(primitive);
for (const [key, group] of activeCrops) {
const { patch, texture, sourcePixelCrop } = cropped.get(key);
const adaptedMaterial = sourceMaterial.clone()
.setName(`${BARREN_SOURCE.material}_${key}_crop_repeat4`)
.setBaseColorTexture(texture);
adaptedMaterial.getBaseColorTextureInfo().setTexCoord(1).setWrapS(10497).setWrapT(10497);
adaptedMaterial.setExtras({
...baseExtras,
romUvAdaptation: {
version: 1, id: BARREN_POLICY_ID, atlasRect: patch.rect, uvRect: patch.uvRect,
repeatFactor: 4, nativeBaseTexture: BARREN_SOURCE.image, nativeBaseTexturePngSha256: BARREN_SOURCE.imageSha256,
sourcePixelCrop,
},
});
const values = new Float32Array(uv0.getCount() * 2);
for (let index = 0; index < uv0.getCount(); index += 1) {
const uv = uv0.getElement(index, [0, 0]);
values[index * 2] = ((uv[0] - patch.uvRect.u0) / (patch.uvRect.u1 - patch.uvRect.u0)) * 4;
values[index * 2 + 1] = ((uv[1] - patch.uvRect.v0) / (patch.uvRect.v1 - patch.uvRect.v0)) * 4;
}
const uv1 = document.createAccessor(`${node.getName()}:${BARREN_POLICY_ID}:${key}:TEXCOORD_1`)
.setType(Accessor.Type.VEC2).setArray(values).setBuffer(buffer);
const adapted = primitive.clone()
.setIndices(document.createAccessor(`${node.getName()}:${BARREN_POLICY_ID}:${key}:indices`)
.setType(Accessor.Type.SCALAR).setArray(new indexArray.constructor(group)).setBuffer(buffer))
.setAttribute("TEXCOORD_1", uv1)
.setMaterial(adaptedMaterial)
.setExtras({
...(primitive.getExtras() ?? {}),
romUvAdaptation: {
version: 1, id: BARREN_POLICY_ID, atlasRect: patch.rect, uvRect: patch.uvRect, repeatFactor: 4,
sourceMaterial: BARREN_SOURCE.material, sourceTexture: BARREN_SOURCE.image,
sourcePngSha256: BARREN_SOURCE.imageSha256, sourcePixelCrop,
},
});
const variants = document.getRoot().listExtensionsUsed().find((item) => item.extensionName === KHRMaterialsVariants.EXTENSION_NAME);
const mappings = variants.createMappingList();
for (const name of variantNames) mappings.addMapping(variants.createMapping().setMaterial(carriers.get(name)).addVariant(variantByName.get(name)));
adapted.setExtension(KHRMaterialsVariants.EXTENSION_NAME, mappings);
mesh.addPrimitive(adapted);
primitiveOutputs.push({ primitive: adapted, triangles: group.length / 3, category: key });
adaptedTriangles += group.length / 3;
}
untouchedTriangles += untouched.length / 3;
const outputTriangles = primitiveOutputs.reduce((sum, item) => sum + item.triangles, 0);
if (outputTriangles !== originalTriangleCount) throw new Error(`Barren triangle partition failed on ${node.getName()} primitive ${primitiveIndex}.`);
for (const { primitive: outputPrimitive } of primitiveOutputs) {
if (outputPrimitive.getAttribute("POSITION") !== position || outputPrimitive.getAttribute("NORMAL") !== normal
|| outputPrimitive.getAttribute("TEXCOORD_0") !== uv0) {
throw new Error(`Barren UV policy changed source position/normal/UV0 accessors on ${node.getName()}.`);
}
}
reports.push({
node: node.getName(), sourcePrimitiveIndex: primitiveIndex, sourceTriangles: originalTriangleCount,
sourceIndexHash: before, sourcePositionHash: accessorHash(position), sourceNormalHash: accessorHash(normal),
sourceUv0Hash: accessorHash(uv0),
outputGroups: primitiveOutputs.map(({ category, triangles }) => ({ category, triangles })),
triangleCoverageExact: true, positionNormalUv0AccessorsShared: true, indexStreamLayoutChanged: true,
});
}
}
if (!reports.length) throw new Error("Barren atlas-safe policy found no floor triangles in the four reviewed source-image quadrants.");
const patchReports = [...cropped.values()].map(({ patch, png, sourcePixelCrop, meanLinear }) => ({
key: patch.key, sourcePixelRect: patch.rect, sourceUvRect: patch.uvRect, repeatFactor: 4,
croppedPngSha256: sourcePixelCrop.croppedPngSha256, croppedRgbaSha256: sourcePixelCrop.croppedRgbaSha256,
bytes: png.length, meanLinear,
}));
patchReports.push({
key: BARREN_WALL_DETAIL_PATCH.key,
role: "wall-detail-carrier",
sourcePixelRect: BARREN_WALL_DETAIL_PATCH.rect,
repeatMode: "mirrored-repeat",
croppedPngSha256: wallDetailCrop.croppedPngSha256,
croppedRgbaSha256: wallDetailCrop.croppedRgbaSha256,
bytes: wallDetailPng.length,
meanLinear: wallDetailMeanLinear,
});
return {
id: BARREN_POLICY_ID,
status: "applied",
sourceModel: BARREN_SOURCE.model,
sourceModelSha256: BARREN_SOURCE.sha256,
sourceMaterial: BARREN_SOURCE.material,
sourceTexture: BARREN_SOURCE.texture,
sourceImage: BARREN_SOURCE.image,
sourceImageSha256: BARREN_SOURCE.imageSha256,
sourceImageDimensions: [imageMeta.width, imageMeta.height],
repeatFactor: 4,
worldNormalYThreshold: 0.7,
atlasSeamToleranceUv: 0.0025,
trianglePolicy: "floor triangles whose UV0 corners remain inside a reviewed 256x256 floor island receive crop-localized repeat UV1; other faces keep the full native atlas",
trianglePartitionChanged: true,
legacyRepeatPrimitivesRestored,
sourcePositionNormalUv0Preserved: true,
untouchedTriangles,
adaptedTriangles,
primitiveReports: reports,
crops: patchReports,
wallDetail: {
textureName: BARREN_WALL_DETAIL_PATCH.textureName,
sourcePixelCrop: wallDetailCrop,
meanLinear: wallDetailMeanLinear,
samplerWrap: "mirrored-repeat",
},
wallBaseContrast: 0.25,
wallDetailVariation: 1,
baseMeanLinear,
variants: variantNames,
};
}
async function applyHotoDetailPolicy(document, sourceReport) {
verifySourceReport(sourceReport, HOTO_SOURCE);
for (const target of Object.values(HOTO_TARGETS)) verifyNativeMaterial(sourceReport, target.nativeMaterial, target.texture);
const root = document.getRoot();
const sourceTextures = new Map();
const targetMaterials = new Map();
for (const [materialName, target] of Object.entries(HOTO_TARGETS)) {
const material = root.listMaterials().find((item) => item.getName() === materialName);
if (!material) throw new Error(`HOTO surface policy cannot find canonical material ${materialName}.`);
const texture = material.getBaseColorTexture();
const image = Buffer.from(texture?.getImage() ?? []);
if (texture?.getName() !== target.image || sha256(image) !== target.imageSha256) {
throw new Error(`HOTO ${materialName} base atlas/tile is not the pinned source PNG.`);
}
if (material.getNormalTexture() || material.getMetallicRoughnessTexture() || material.getEmissiveTexture()) {
throw new Error(`HOTO surface policy refuses unexpected auxiliary native texture slots on ${materialName}.`);
}
targetMaterials.set(materialName, material);
sourceTextures.set(materialName, texture);
}
const detailSources = new Map();
for (const [role, detail] of Object.entries(HOTO_DETAIL)) {
const material = targetMaterials.get(detail.canonicalMaterial);
const texture = sourceTextures.get(detail.canonicalMaterial);
if (texture?.getName() !== detail.image || sha256(texture.getImage() ?? Buffer.alloc(0)) !== detail.imageSha256) {
throw new Error(`HOTO ${role} detail tile is not the native, byte-identical image.`);
}
detailSources.set(role, { ...detail, sourceTexturePath: detail.texture, textureObject: texture, meanLinear: await meanLinearFromPng(Buffer.from(texture.getImage())) });
}
const expectedByMaterial = new Map();
const detailProofs = {};
for (const [role, detail] of detailSources) {
detailProofs[role] = {
nativeMaterial: detail.nativeMaterial,
canonicalMaterialName: detail.canonicalMaterial,
sourceTexture: detail.sourceTexturePath,
sourceImage: detail.image,
sourcePreparedPngSha256: detail.imageSha256,
meanLinear: detail.meanLinear,
};
}
for (const [materialName, target] of Object.entries(HOTO_TARGETS)) {
const material = targetMaterials.get(materialName);
const baseTexture = sourceTextures.get(materialName);
const baseMeanLinear = target.useOwnBaseMean ? await meanLinearFromPng(Buffer.from(baseTexture.getImage())) : undefined;
const floorRole = target.floorVariantRole ?? "floor";
const sourceProof = {
sourceModel: HOTO_SOURCE.model,
sourceModelSha256: HOTO_SOURCE.sha256,
sourceMaterial: target.nativeMaterial,
canonicalMaterialName: materialName,
sourceTexture: target.texture,
sourceTextureName: target.image,
sourcePreparedPngSha256: target.imageSha256,
floorDetailSource: detailProofs[floorRole],
wallDetailSource: detailProofs.wall,
floorVariantRole: floorRole,
floorBaseStrength: target.floorBaseStrength,
floorStrength: target.floorStrength,
wallStrength: target.wallStrength,
wallBaseContrast: target.wallBaseContrast,
wallDetailVariation: target.wallDetailVariation,
};
const metadata = {
version: 1,
floorVariant: HOTO_VARIANTS[floorRole],
wallVariant: HOTO_VARIANTS.wall,
periodMeters: 2,
floorBaseStrength: target.floorBaseStrength,
floorStrength: target.floorStrength,
wallStrength: target.wallStrength,
floorMeanLinear: detailSources.get(floorRole).meanLinear,
wallMeanLinear: detailSources.get("wall").meanLinear,
wallBaseContrast: target.wallBaseContrast,
wallDetailVariation: target.wallDetailVariation,
policyId: HOTO_POLICY_ID,
sourceProof,
};
if (baseMeanLinear) metadata.baseMeanLinear = baseMeanLinear;
expectedByMaterial.set(materialName, metadata);
}
const carrierNames = new Map(Object.entries(HOTO_VARIANTS).map(([role, variantName]) => [variantName, role]));
const existingMaterialPolicies = [...targetMaterials.values()].map((material) => material.getExtras()?.runewakerSurfaceDetail?.policyId ?? null);
const hasExistingPolicy = existingMaterialPolicies.some(Boolean);
if (hasExistingPolicy) {
if (existingMaterialPolicies.some((id) => id !== HOTO_POLICY_ID && id !== HOTO_LEGACY_POLICY_ID)) {
throw new Error("HOTO surface metadata contains an unknown or partial policy marker.");
}
const existingPolicy = existingMaterialPolicies[0];
if (!existingPolicy || existingMaterialPolicies.some((id) => id !== existingPolicy)) {
throw new Error("HOTO source-rock detail policy is only partially applied.");
}
const extension = root.listExtensionsUsed().find((item) => item.extensionName === KHRMaterialsVariants.EXTENSION_NAME);
const variantByName = new Map((extension?.listVariants() ?? []).map((variant) => [variant.getName(), variant]));
if (!extension || [...carrierNames.keys()].some((name) => !variantByName.has(name))) {
throw new Error("HOTO source-rock detail policy marker exists but its material variants are absent.");
}
const carrierByName = new Map();
for (const [variantName, role] of carrierNames) {
const detail = detailSources.get(role);
const carrier = root.listMaterials().find((material) => material.getName() === `${variantName}:source-tile-carrier`);
const carrierProof = carrier?.getExtras()?.runewakerSurfaceDetailCarrier;
if (!carrier || carrier.getBaseColorTexture() !== detail.textureObject
|| carrierProof?.policyId !== existingPolicy
|| carrierProof?.sourceResourceSha256 !== HOTO_SOURCE.sha256
|| carrierProof?.sourceTextureName !== detail.image
|| carrierProof?.sourcePreparedPngSha256 !== detail.imageSha256) {
throw new Error(`HOTO ${variantName} carrier does not match the pinned source tile/policy.`);
}
carrierByName.set(variantName, carrier);
}
let mappedPrimitiveCount = 0;
for (const node of root.listNodes()) for (const primitive of node.getMesh()?.listPrimitives() ?? []) {
const materialName = primitive.getMaterial()?.getName();
if (!targetMaterials.has(materialName)) continue;
const mappingList = primitive.getExtension(KHRMaterialsVariants.EXTENSION_NAME)?.listMappings() ?? [];
const mappedNames = new Set();
for (const mapping of mappingList) {
if (mapping.listVariants().length !== 1) throw new Error(`HOTO ${materialName} has a bundled or ambiguous surface-detail mapping.`);
const variantName = mapping.listVariants()[0].getName();
if (!carrierByName.has(variantName) || mapping.getMaterial() !== carrierByName.get(variantName) || mappedNames.has(variantName)) {
throw new Error(`HOTO ${materialName} surface-detail mapping does not point to its verified source carrier.`);
}
mappedNames.add(variantName);
}
if (mappedNames.size !== 2 || [...carrierNames.keys()].some((name) => !mappedNames.has(name))) {
throw new Error(`HOTO ${materialName} is missing one of the pinned source-detail mappings.`);
}
mappedPrimitiveCount += 1;
}
if (mappedPrimitiveCount !== 36) throw new Error(`HOTO source-rock detail mappings are incomplete: expected 36, found ${mappedPrimitiveCount}.`);
if (existingPolicy === HOTO_LEGACY_POLICY_ID) {
for (const [materialName, target] of Object.entries(HOTO_TARGETS)) {
const old = targetMaterials.get(materialName).getExtras()?.runewakerSurfaceDetail;
const proof = old?.sourceProof;
if (old?.version !== 1 || old.floorVariant !== HOTO_VARIANTS.floor || old.wallVariant !== HOTO_VARIANTS.wall
|| old.periodMeters !== 2 || old.floorStrength !== 0.65 || old.floorBaseStrength !== undefined && old.floorBaseStrength !== 0
|| old.wallStrength !== (target.legacyWallStrength ?? target.wallStrength)
|| old.wallBaseContrast !== undefined && old.wallBaseContrast !== 1
|| old.wallDetailVariation !== undefined && old.wallDetailVariation !== 0
|| JSON.stringify(old.floorMeanLinear) !== JSON.stringify(detailSources.get("floor").meanLinear)
|| JSON.stringify(old.wallMeanLinear) !== JSON.stringify(detailSources.get("wall").meanLinear)
|| proof?.sourceModelSha256 !== HOTO_SOURCE.sha256 || proof?.canonicalMaterialName !== materialName
|| proof?.sourceMaterial !== target.nativeMaterial || proof?.sourceTexture !== target.texture
|| proof?.sourceTextureName !== target.image || proof?.sourcePreparedPngSha256 !== target.imageSha256) {
throw new Error(`HOTO legacy v1 metadata on ${materialName} is not the reviewed migration source.`);
}
}
for (const carrier of carrierByName.values()) {
const extras = carrier.getExtras() ?? {};
carrier.setExtras({ ...extras, runewakerSurfaceDetailCarrier: { ...extras.runewakerSurfaceDetailCarrier, policyId: HOTO_POLICY_ID } });
}
for (const [materialName, metadata] of expectedByMaterial) {
const material = targetMaterials.get(materialName);
material.setExtras({ ...(material.getExtras() ?? {}), runewakerSurfaceDetail: metadata });
}
return { id: HOTO_POLICY_ID, status: "migrated-v1", migratedFrom: HOTO_LEGACY_POLICY_ID, mappedPrimitiveCount, materials: Object.keys(HOTO_TARGETS) };
}
for (const [variantName, role] of carrierNames) {
const detail = detailSources.get(role);
const proof = carrierByName.get(variantName).getExtras()?.runewakerSurfaceDetailCarrier;
if (proof?.variantName !== variantName || proof?.sourceMaterial !== detail.nativeMaterial
|| proof?.canonicalMaterialName !== detail.canonicalMaterial || proof?.sourceTexturePath !== detail.texture) {
throw new Error(`HOTO ${variantName} carrier provenance does not match the pinned source.`);
}
}
for (const [materialName, metadata] of expectedByMaterial) {
const existing = targetMaterials.get(materialName).getExtras()?.runewakerSurfaceDetail;
if (JSON.stringify(existing) !== JSON.stringify(metadata)) throw new Error(`HOTO v2 surface metadata on ${materialName} differs from the reviewed policy.`);
}
return { id: HOTO_POLICY_ID, status: "already-applied", mappedPrimitiveCount, materials: Object.keys(HOTO_TARGETS) };
}
if (root.listMaterials().some((material) => material.getExtras()?.runewakerSurfaceDetailCarrier?.policyId === HOTO_LEGACY_POLICY_ID
|| material.getExtras()?.runewakerSurfaceDetailCarrier?.policyId === HOTO_POLICY_ID)) {
throw new Error("HOTO source-tile carrier markers exist without the complete source-detail policy.");
}
const { getOrCreate } = listVariants(document);
const variantByName = new Map(Object.values(HOTO_VARIANTS).map((name) => [name, getOrCreate(name)]));
const carrierByName = new Map();
for (const [role, detail] of detailSources) {
const variantName = HOTO_VARIANTS[role];
const extras = {
version: 1, variantName, policyId: HOTO_POLICY_ID, sourceResourceSha256: HOTO_SOURCE.sha256,
sourceMaterial: detail.nativeMaterial, canonicalMaterialName: detail.canonicalMaterial,
sourceTexturePath: detail.sourceTexturePath, sourceTextureName: detail.image, sourcePreparedPngSha256: detail.imageSha256,
};
carrierByName.set(variantName, getOrCreateCarrier(document, detail.textureObject, variantName, extras));
}
let mappedPrimitiveCount = 0;
for (const node of root.listNodes()) {
const mesh = node.getMesh();
if (!mesh) continue;
for (const primitive of mesh.listPrimitives()) {
const materialName = primitive.getMaterial()?.getName();
if (targetMaterials.get(materialName) !== primitive.getMaterial()) continue;
addSurfaceMappings(document, primitive, carrierByName, variantByName);
mappedPrimitiveCount += 1;
}
}
if (mappedPrimitiveCount !== 36) throw new Error(`HOTO source-rock detail target count changed: expected 36, found ${mappedPrimitiveCount}.`);
for (const [materialName, metadata] of expectedByMaterial) {
const material = targetMaterials.get(materialName);
material.setExtras({ ...(material.getExtras() ?? {}), runewakerSurfaceDetail: metadata });
}
return {
id: HOTO_POLICY_ID,
status: "applied",
sourceModel: HOTO_SOURCE.model,
sourceModelSha256: HOTO_SOURCE.sha256,
detail: { periodMeters: 2, sourceSpace: "browser-adapted world-space detail; native base UV0 retained" },
detailSources: detailProofs,
materials: Object.keys(HOTO_TARGETS).map((name) => ({ name, metadata: expectedByMaterial.get(name) })),
mappedPrimitiveCount,
sourceGeometryChanged: false,
nativeBaseTexturesAndMaterialSemanticsPreserved: true,
migration: "v1 metadata migrates only when every target material, tile carrier and variant mapping matches the reviewed legacy configuration",
};
}
/** Source-pinned atlas-safe crops and native source-rock detail carriers. */
export async function applyRunewakerSurfaceQualityPolicies(document, { dungeonId, policyIds = [], sourceReport } = {}) {
if (new Set(policyIds).size !== policyIds.length) throw new Error("RoM surface-quality policy IDs must be unique.");
const results = [];
for (const policyId of policyIds) {
if (dungeonId === "barren-caves" && policyId === BARREN_POLICY_ID) results.push(await applyBarrenFloorPolicy(document, sourceReport));
else if (dungeonId === "hoto" && policyId === HOTO_POLICY_ID) results.push(await applyHotoDetailPolicy(document, sourceReport));
else throw new Error(`Unknown or mismatched RoM surface-quality policy ${policyId} for ${dungeonId}.`);
}
return { schemaVersion: 1, dungeonId, status: policyIds.length ? "applied" : "no-policy", policies: results };
}
export const runewakerSurfaceQualityPolicyIds = Object.freeze({ barrenCaves: BARREN_POLICY_ID, hoto: HOTO_POLICY_ID });