Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7cc588bfc | ||
|
|
bac0da9300 |
No files matched your search
@@ -1839,3 +1839,18 @@ Vite's existing large-chunk advisory remains informational.
|
||||
no environment-performance regressions. The release-candidate check accepted
|
||||
all 118 changed paths. The existing large-chunk advisory and Rapier
|
||||
initialization deprecation warning remain informational.
|
||||
|
||||
# 2026-09-09 — fixed per-rank classic mana costs
|
||||
|
||||
- Imported WoW mana percentages are now converted into fixed costs at each
|
||||
spell rank's source level. Player level, Intellect, equipment, and maximum-mana
|
||||
modifiers no longer increase the cost of an already learned rank; legitimate
|
||||
talent and aura cost reductions are still applied afterward.
|
||||
- Shadow Word: Pain rank 4 now costs 133 mana at every level from 26 through 33,
|
||||
including level 32. Learning rank 5 at level 34 raises the cost to 188 mana.
|
||||
- AI companions now use real character-scale mana pools so fixed spell costs do
|
||||
not prevent healing or resurrection. The companion display also resolves the
|
||||
active spell rank and the same final affordability result as the primary HUD.
|
||||
- Verification passed all 1,133 Vitest cases across 208 files. The production
|
||||
build completed with 974 modules transformed; arena, KTX2, and environment-
|
||||
performance audits remained green, and the release-candidate check passed.
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "healer-man",
|
||||
"version": "0.1.23",
|
||||
"version": "0.1.25",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "healer-man",
|
||||
"version": "0.1.23",
|
||||
"version": "0.1.25",
|
||||
"dependencies": {
|
||||
"@capacitor/android": "8.4.1",
|
||||
"@capacitor/core": "8.4.1",
|
||||
|
||||
+2
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "healer-man",
|
||||
"private": true,
|
||||
"version": "0.1.23",
|
||||
"version": "0.1.25",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host 0.0.0.0",
|
||||
@@ -95,6 +95,7 @@
|
||||
"runewaker:audit:encounters": "node scripts/runewaker-pipeline/audit-encounter-implementation.mjs && node --test scripts/runewaker-pipeline/encounter-implementation-report.test.mjs",
|
||||
"runewaker:audit:environment-assembly": "node scripts/runewaker-pipeline/audit-environment-assembly.mjs && node --test scripts/runewaker-pipeline/environment-assembly.test.mjs",
|
||||
"runewaker:audit:navigation": "node scripts/runewaker-pipeline/analyze-navigation-gaps.mjs && vitest run src/game/runewakerObjectiveRouteAudit.test.ts",
|
||||
"runewaker:audit:material-alpha": "node scripts/runewaker-pipeline/audit-material-alpha.mjs",
|
||||
"assets:wailing:instance": "gltf-transform instance ../wow335a/dungeon-export-work/WailingCaverns/converted/wailing-caverns.visual.glb src/assets/game/dungeons/wailing-caverns/wailing-caverns-visual.glb --min 3",
|
||||
"assets:wailing:creatures:animated": "node scripts/prepareAnimatedCreatures.mjs",
|
||||
"assets:wailing:creatures:combat": "node scripts/add-combat-creature-clips.mjs",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"versionName": "0.1.23",
|
||||
"versionCode": 1023
|
||||
"versionName": "0.1.25",
|
||||
"versionCode": 1025
|
||||
}
|
||||
@@ -36,6 +36,9 @@ async function main() {
|
||||
&& hasBasisu
|
||||
&& /^\/images\/\d+\/mimeType$/.test(issue.pointer ?? "")
|
||||
&& issue.message.includes("'image/ktx2'")) return false;
|
||||
if (issue.code === "IMAGE_UNRECOGNIZED_FORMAT"
|
||||
&& hasBasisu
|
||||
&& /^\/images\/\d+$/.test(issue.pointer ?? "")) return false;
|
||||
return true;
|
||||
});
|
||||
rows.push({
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { readFile, readdir, stat, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import sharp from "sharp";
|
||||
|
||||
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||
const recipeDirectory = path.join(projectRoot, "scripts", "runewaker-pipeline", "recipes");
|
||||
const workRoot = path.join(projectRoot, "runewaker-export-work");
|
||||
const shippingRoot = path.join(projectRoot, "src", "assets", "game", "dungeons");
|
||||
const resourceCacheRoot = path.join(workRoot, "resource-cache");
|
||||
const requestedSlugs = new Set(process.argv.slice(2).filter((value) => !value.startsWith("--")));
|
||||
const fix = process.argv.includes("--fix");
|
||||
const alphaClassifications = new Map();
|
||||
let resourceTextureIndex;
|
||||
|
||||
async function exists(file) {
|
||||
try {
|
||||
await stat(file);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function parseGlb(buffer, file) {
|
||||
if (buffer.length < 20 || buffer.toString("utf8", 0, 4) !== "glTF") {
|
||||
throw new Error(`${file} is not a GLB file.`);
|
||||
}
|
||||
const chunks = [];
|
||||
let json;
|
||||
let offset = 12;
|
||||
while (offset + 8 <= buffer.length) {
|
||||
const chunkLength = buffer.readUInt32LE(offset);
|
||||
const chunkType = buffer.readUInt32LE(offset + 4);
|
||||
const chunkStart = offset + 8;
|
||||
const chunkEnd = chunkStart + chunkLength;
|
||||
if (chunkEnd > buffer.length) break;
|
||||
const data = buffer.subarray(chunkStart, chunkEnd);
|
||||
chunks.push({ type: chunkType, data });
|
||||
if (chunkType === 0x4e4f534a) {
|
||||
json = JSON.parse(data.toString("utf8").replace(/\0+$/u, "").trim());
|
||||
}
|
||||
offset = chunkEnd;
|
||||
}
|
||||
if (!json) throw new Error(`${file} does not contain a JSON chunk.`);
|
||||
return { json, chunks };
|
||||
}
|
||||
|
||||
function serializeGlb(source, json, chunks) {
|
||||
const encodedJson = Buffer.from(JSON.stringify(json));
|
||||
const paddedJson = Buffer.alloc(Math.ceil(encodedJson.length / 4) * 4, 0x20);
|
||||
encodedJson.copy(paddedJson);
|
||||
const updatedChunks = chunks.map((chunk) => (
|
||||
chunk.type === 0x4e4f534a ? { ...chunk, data: paddedJson } : chunk
|
||||
));
|
||||
const totalLength = 12 + updatedChunks.reduce((total, chunk) => total + 8 + chunk.data.length, 0);
|
||||
const output = Buffer.alloc(totalLength);
|
||||
source.copy(output, 0, 0, 12);
|
||||
output.writeUInt32LE(totalLength, 8);
|
||||
let offset = 12;
|
||||
for (const chunk of updatedChunks) {
|
||||
output.writeUInt32LE(chunk.data.length, offset);
|
||||
output.writeUInt32LE(chunk.type, offset + 4);
|
||||
chunk.data.copy(output, offset + 8);
|
||||
offset += 8 + chunk.data.length;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
async function classifyAlpha(file) {
|
||||
if (alphaClassifications.has(file)) return alphaClassifications.get(file);
|
||||
const metadata = await sharp(file).metadata();
|
||||
if (!metadata.hasAlpha) {
|
||||
alphaClassifications.set(file, "opaque");
|
||||
return "opaque";
|
||||
}
|
||||
|
||||
const { data, info } = await sharp(file).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
||||
let hasTransparentPixel = false;
|
||||
for (let offset = 3; offset < data.length; offset += info.channels) {
|
||||
const alpha = data[offset];
|
||||
if (alpha === 255) continue;
|
||||
hasTransparentPixel = true;
|
||||
if (alpha > 0) {
|
||||
alphaClassifications.set(file, "blend");
|
||||
return "blend";
|
||||
}
|
||||
}
|
||||
const role = hasTransparentPixel ? "cutout" : "opaque";
|
||||
alphaClassifications.set(file, role);
|
||||
return role;
|
||||
}
|
||||
|
||||
function sourceImageIndex(texture) {
|
||||
return texture?.extensions?.KHR_texture_basisu?.source ?? texture?.source;
|
||||
}
|
||||
|
||||
async function indexResourceTextures() {
|
||||
if (resourceTextureIndex) return resourceTextureIndex;
|
||||
resourceTextureIndex = new Map();
|
||||
if (!(await exists(resourceCacheRoot))) return resourceTextureIndex;
|
||||
const cacheDirectories = (await readdir(resourceCacheRoot, { withFileTypes: true }))
|
||||
.filter((entry) => entry.isDirectory())
|
||||
.sort((left, right) => left.name.localeCompare(right.name));
|
||||
for (const cacheDirectory of cacheDirectories) {
|
||||
const textureDirectory = path.join(resourceCacheRoot, cacheDirectory.name, "prepared", "textures");
|
||||
if (!(await exists(textureDirectory))) continue;
|
||||
for (const texture of (await readdir(textureDirectory)).filter((file) => file.endsWith(".png")).sort()) {
|
||||
if (!resourceTextureIndex.has(texture)) {
|
||||
resourceTextureIndex.set(texture, path.join(textureDirectory, texture));
|
||||
}
|
||||
}
|
||||
}
|
||||
return resourceTextureIndex;
|
||||
}
|
||||
|
||||
async function resolveSourceTexture(localDirectory, imageName) {
|
||||
const fileName = `${imageName}.png`;
|
||||
const localFile = path.join(localDirectory, fileName);
|
||||
if (await exists(localFile)) return localFile;
|
||||
return (await indexResourceTextures()).get(fileName);
|
||||
}
|
||||
|
||||
function sha256(buffer) {
|
||||
return createHash("sha256").update(buffer).digest("hex");
|
||||
}
|
||||
|
||||
async function refreshShippingMetadata(metadataFile, metadata, visualArtifact, visualBuffer) {
|
||||
visualArtifact.size = visualBuffer.length;
|
||||
visualArtifact.sha256 = sha256(visualBuffer);
|
||||
await writeFile(metadataFile, `${JSON.stringify(metadata, null, 2)}\n`, "utf8");
|
||||
|
||||
const optimizationFile = path.join(path.dirname(metadataFile), "optimization-report.json");
|
||||
if (!(await exists(optimizationFile))) return;
|
||||
const optimization = JSON.parse(await readFile(optimizationFile, "utf8"));
|
||||
if (optimization.optimized) {
|
||||
optimization.optimized.size = visualBuffer.length;
|
||||
if (Object.hasOwn(optimization.optimized, "checksum")) {
|
||||
optimization.optimized.checksum = sha256(visualBuffer);
|
||||
}
|
||||
}
|
||||
await writeFile(optimizationFile, `${JSON.stringify(optimization, null, 2)}\n`, "utf8");
|
||||
}
|
||||
|
||||
async function auditDungeon(recipe) {
|
||||
const slug = recipe.slug;
|
||||
const metadataFile = path.join(shippingRoot, slug, "import-metadata.json");
|
||||
const preparedTextureDirectory = path.join(workRoot, slug, "prepared", "textures");
|
||||
if (!(await exists(metadataFile))) {
|
||||
return { slug, status: "incomplete", problems: [`Missing ${path.relative(projectRoot, metadataFile)}`] };
|
||||
}
|
||||
if (!(await exists(preparedTextureDirectory))) {
|
||||
return {
|
||||
slug,
|
||||
status: "incomplete",
|
||||
problems: [`Missing ${path.relative(projectRoot, preparedTextureDirectory)}`],
|
||||
};
|
||||
}
|
||||
|
||||
const metadata = JSON.parse(await readFile(metadataFile, "utf8"));
|
||||
const visualArtifact = (metadata.assets ?? metadata.artifacts ?? [])
|
||||
.find((artifact) => artifact.role === "visual");
|
||||
const visualFile = path.join(shippingRoot, slug, visualArtifact?.fileName ?? `${slug}-visual.glb`);
|
||||
if (!(await exists(visualFile))) {
|
||||
return { slug, status: "incomplete", problems: [`Missing ${path.relative(projectRoot, visualFile)}`] };
|
||||
}
|
||||
|
||||
const sourceGlb = await readFile(visualFile);
|
||||
const { json: gltf, chunks } = parseGlb(sourceGlb, visualFile);
|
||||
const materialResults = [];
|
||||
const missingTextures = [];
|
||||
for (const [materialIndex, material] of (gltf.materials ?? []).entries()) {
|
||||
const textureIndex = material.pbrMetallicRoughness?.baseColorTexture?.index;
|
||||
const texture = gltf.textures?.[textureIndex];
|
||||
const image = gltf.images?.[sourceImageIndex(texture)];
|
||||
if (!image?.name) continue;
|
||||
|
||||
const sourceFile = await resolveSourceTexture(preparedTextureDirectory, image.name);
|
||||
if (!sourceFile) {
|
||||
missingTextures.push(`${image.name}.png`);
|
||||
continue;
|
||||
}
|
||||
const expectedRole = await classifyAlpha(sourceFile);
|
||||
const expectedAlphaMode = expectedRole === "blend" ? "BLEND" : expectedRole === "cutout" ? "MASK" : "OPAQUE";
|
||||
const actualAlphaMode = material.alphaMode ?? "OPAQUE";
|
||||
const actualRole = material.extras?.semanticRole ?? "missing";
|
||||
materialResults.push({
|
||||
materialIndex,
|
||||
material: material.name ?? `material-${materialIndex}`,
|
||||
texture: image.name,
|
||||
expectedRole,
|
||||
expectedAlphaMode,
|
||||
actualRole,
|
||||
actualAlphaMode,
|
||||
matches: expectedAlphaMode === actualAlphaMode && expectedRole === actualRole,
|
||||
});
|
||||
if (fix && (expectedAlphaMode !== actualAlphaMode || expectedRole !== actualRole)) {
|
||||
if (expectedAlphaMode === "OPAQUE") delete material.alphaMode;
|
||||
else material.alphaMode = expectedAlphaMode;
|
||||
if (expectedAlphaMode === "MASK") material.alphaCutoff = 0.5;
|
||||
else delete material.alphaCutoff;
|
||||
material.extras = { ...(material.extras ?? {}), semanticRole: expectedRole };
|
||||
}
|
||||
}
|
||||
|
||||
const mismatches = materialResults.filter((result) => !result.matches);
|
||||
const alphaMaterials = materialResults.filter((result) => result.expectedRole !== "opaque");
|
||||
const problems = [
|
||||
...mismatches.map((result) =>
|
||||
`${result.texture}: expected ${result.expectedAlphaMode}/${result.expectedRole}, got ${result.actualAlphaMode}/${result.actualRole}`,
|
||||
),
|
||||
...[...new Set(missingTextures)].map((file) => `Missing prepared source texture ${file}`),
|
||||
];
|
||||
if (fix && mismatches.length > 0 && missingTextures.length === 0) {
|
||||
const visualBuffer = serializeGlb(sourceGlb, gltf, chunks);
|
||||
await writeFile(visualFile, visualBuffer);
|
||||
if (visualArtifact) {
|
||||
await refreshShippingMetadata(metadataFile, metadata, visualArtifact, visualBuffer);
|
||||
}
|
||||
}
|
||||
return {
|
||||
slug,
|
||||
status: problems.length === 0 ? "green" : fix && missingTextures.length === 0 ? "repaired" : "mismatch",
|
||||
materials: materialResults.length,
|
||||
alphaMaterials: alphaMaterials.map(({ material, texture, expectedAlphaMode }) => ({
|
||||
material,
|
||||
texture,
|
||||
alphaMode: expectedAlphaMode,
|
||||
})),
|
||||
mismatches,
|
||||
problems,
|
||||
};
|
||||
}
|
||||
|
||||
const recipeFiles = (await readdir(recipeDirectory))
|
||||
.filter((file) => file.endsWith(".json") && !file.endsWith("-population.json"))
|
||||
.sort();
|
||||
const recipes = [];
|
||||
for (const recipeFile of recipeFiles) {
|
||||
const recipe = JSON.parse(await readFile(path.join(recipeDirectory, recipeFile), "utf8"));
|
||||
if (!recipe.slug || recipe.slug === "runewaker-player-avatars") continue;
|
||||
if (requestedSlugs.size > 0 && !requestedSlugs.has(recipe.slug)) continue;
|
||||
recipes.push(recipe);
|
||||
}
|
||||
|
||||
const results = [];
|
||||
for (const recipe of recipes) {
|
||||
results.push(await auditDungeon(recipe));
|
||||
}
|
||||
|
||||
const failures = results.filter((result) => !["green", "repaired"].includes(result.status));
|
||||
const report = {
|
||||
schemaVersion: 1,
|
||||
dungeonCount: results.length,
|
||||
status: failures.length === 0 ? fix ? "repaired" : "green" : "mismatch",
|
||||
failureCount: failures.length,
|
||||
alphaDungeonCount: results.filter((result) => result.alphaMaterials?.length > 0).length,
|
||||
results,
|
||||
};
|
||||
console.log(JSON.stringify(report, null, 2));
|
||||
if (failures.length > 0) process.exitCode = 1;
|
||||
@@ -6,6 +6,7 @@ Expected invocation:
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from array import array
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
@@ -111,6 +112,69 @@ def material_image_key(material: bpy.types.Material) -> str:
|
||||
return f"material:{material.name.casefold()}"
|
||||
|
||||
|
||||
def material_image(material: bpy.types.Material) -> bpy.types.Image | None:
|
||||
if not material.use_nodes or material.node_tree is None:
|
||||
return None
|
||||
for node in material.node_tree.nodes:
|
||||
if node.type == "TEX_IMAGE" and node.image is not None:
|
||||
return node.image
|
||||
return None
|
||||
|
||||
|
||||
def image_alpha_role(image: bpy.types.Image | None) -> str:
|
||||
"""Classify source alpha without turning fully opaque RGBA textures translucent."""
|
||||
if image is None or image.channels < 4:
|
||||
return "opaque"
|
||||
pixels = array("f", [0.0]) * len(image.pixels)
|
||||
image.pixels.foreach_get(pixels)
|
||||
alpha_values = pixels[3::image.channels]
|
||||
if min(alpha_values, default=1.0) >= 0.999:
|
||||
return "opaque"
|
||||
if any(0.001 < alpha < 0.999 for alpha in alpha_values):
|
||||
return "blend"
|
||||
return "cutout"
|
||||
|
||||
|
||||
def preserve_material_alpha(material: bpy.types.Material) -> str:
|
||||
image = material_image(material)
|
||||
role = image_alpha_role(image)
|
||||
if role == "opaque" or material.node_tree is None:
|
||||
return role
|
||||
|
||||
image_node = next(
|
||||
node
|
||||
for node in material.node_tree.nodes
|
||||
if node.type == "TEX_IMAGE" and node.image == image
|
||||
)
|
||||
principled = next(
|
||||
(node for node in material.node_tree.nodes if node.type == "BSDF_PRINCIPLED"),
|
||||
None,
|
||||
)
|
||||
if principled is None:
|
||||
return role
|
||||
alpha_output = image_node.outputs.get("Alpha")
|
||||
alpha_input = principled.inputs.get("Alpha")
|
||||
if alpha_output is None or alpha_input is None:
|
||||
return role
|
||||
|
||||
if role == "blend":
|
||||
if not alpha_input.is_linked:
|
||||
material.node_tree.links.new(alpha_output, alpha_input)
|
||||
material.blend_method = "BLEND"
|
||||
if hasattr(material, "surface_render_method"):
|
||||
material.surface_render_method = "BLENDED"
|
||||
else:
|
||||
alpha_clip = material.node_tree.nodes.new("ShaderNodeMath")
|
||||
alpha_clip.name = "RuneWaker Alpha Clip"
|
||||
alpha_clip.operation = "GREATER_THAN"
|
||||
alpha_clip.inputs[1].default_value = 0.5
|
||||
material.node_tree.links.new(alpha_output, alpha_clip.inputs[0])
|
||||
material.node_tree.links.new(alpha_clip.outputs[0], alpha_input)
|
||||
material.blend_method = "CLIP"
|
||||
material.alpha_threshold = 0.5
|
||||
return role
|
||||
|
||||
|
||||
def deduplicate_materials(objects: list[bpy.types.Object]) -> dict[str, int]:
|
||||
canonical: dict[str, bpy.types.Material] = {}
|
||||
replacements = 0
|
||||
@@ -126,7 +190,7 @@ def deduplicate_materials(objects: list[bpy.types.Object]) -> dict[str, int]:
|
||||
replacements += 1
|
||||
else:
|
||||
canonical[key] = material
|
||||
material["semanticRole"] = "opaque"
|
||||
material["semanticRole"] = preserve_material_alpha(material)
|
||||
material["sourceFormat"] = "RuneWaker DDS"
|
||||
return {"unique": len(canonical), "replacements": replacements}
|
||||
|
||||
|
||||
@@ -241,6 +241,7 @@ async function cachedResourceAsset(resourceRecord) {
|
||||
throw new Error("source ROS is missing: " + projectPath(sourceFile));
|
||||
}
|
||||
const sourceSha256 = await sha256(sourceFile);
|
||||
const converterSha256 = await sha256(blenderScript);
|
||||
const cacheRoot = path.join(resourceCacheDirectory, resourceRecord.cacheKey);
|
||||
const raw = path.join(cacheRoot, "raw");
|
||||
const prepared = path.join(cacheRoot, "prepared");
|
||||
@@ -253,6 +254,7 @@ async function cachedResourceAsset(resourceRecord) {
|
||||
.map((file) => path.join(cacheRoot, file));
|
||||
if (cached.source?.resource === resourceRecord.normalizedResource
|
||||
&& cached.source?.sha256 === sourceSha256
|
||||
&& cached.toolchain?.converterSha256 === converterSha256
|
||||
&& cached.coordinateSystem?.metersPerSourceUnit === recipe.coordinateSystem.metersPerSourceUnit
|
||||
&& (await Promise.all(outputs.map(exists))).every(Boolean)) {
|
||||
return {
|
||||
@@ -300,6 +302,9 @@ async function cachedResourceAsset(resourceRecord) {
|
||||
nativeBridge: recipe.coordinateSystem.nativeBridge,
|
||||
metersPerSourceUnit: recipe.coordinateSystem.metersPerSourceUnit,
|
||||
},
|
||||
toolchain: {
|
||||
converterSha256,
|
||||
},
|
||||
visual: {
|
||||
file: path.relative(
|
||||
cacheRoot,
|
||||
|
||||
Binary file not shown.
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "aeternal-circle-visual.glb",
|
||||
"size": 4963912,
|
||||
"sha256": "42fdcff1b25b58688943a030f69fd14ea839247a7baabbc31b5f6f7faaf49785",
|
||||
"size": 4963968,
|
||||
"sha256": "de1ba06b7e3a9237fcff75aff2b00093287975e755cc7e98e77de1b7af6a9c14",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "aeternal-circle-visual.optimized.glb",
|
||||
"size": 4963912,
|
||||
"checksum": "42fdcff1b25b58688943a030f69fd14ea839247a7baabbc31b5f6f7faaf49785"
|
||||
"size": 4963968,
|
||||
"checksum": "de1ba06b7e3a9237fcff75aff2b00093287975e755cc7e98e77de1b7af6a9c14"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
Binary file not shown.
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "barren-caves-visual.glb",
|
||||
"size": 1710152,
|
||||
"sha256": "d0f733de4b26c0f432c5e160614765f320bf1753a829be269740b340e7415a12",
|
||||
"size": 1710188,
|
||||
"sha256": "e6dc49e6aa6fd20cee1b2afa43324a714f8f8284a6becef8dc514509b432f9bb",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "barren-caves-visual.optimized.glb",
|
||||
"size": 1710152,
|
||||
"checksum": "d0f733de4b26c0f432c5e160614765f320bf1753a829be269740b340e7415a12"
|
||||
"size": 1710188,
|
||||
"checksum": "e6dc49e6aa6fd20cee1b2afa43324a714f8f8284a6becef8dc514509b432f9bb"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
Binary file not shown.
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "cave-water-dragon-visual.glb",
|
||||
"size": 8893112,
|
||||
"sha256": "cc57c0285de77690c4a1cdabaecd8b192791cbd05aca38aa21ed96462688ca31",
|
||||
"size": 8893204,
|
||||
"sha256": "dd1887f88d6ee9102da0e34ea2b4087742e37a2b8ce779c722bc124effe1faca",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "cave-water-dragon-visual.optimized.glb",
|
||||
"size": 8893112,
|
||||
"checksum": "cc57c0285de77690c4a1cdabaecd8b192791cbd05aca38aa21ed96462688ca31"
|
||||
"size": 8893204,
|
||||
"checksum": "dd1887f88d6ee9102da0e34ea2b4087742e37a2b8ce779c722bc124effe1faca"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
Binary file not shown.
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "demon-stronghold-125-visual.glb",
|
||||
"size": 15134500,
|
||||
"sha256": "ecbacfe274e7eecb104c5807eedf3cc51cd4b7a628967c6331fc93b86396b842",
|
||||
"size": 15135060,
|
||||
"sha256": "e2106115dcc216999d2e37d26eeb52844ce4f41b44e19594699f37a54d7f19b9",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "demon-stronghold-125-visual.optimized.glb",
|
||||
"size": 15134500,
|
||||
"checksum": "ecbacfe274e7eecb104c5807eedf3cc51cd4b7a628967c6331fc93b86396b842"
|
||||
"size": 15135060,
|
||||
"checksum": "e2106115dcc216999d2e37d26eeb52844ce4f41b44e19594699f37a54d7f19b9"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
Binary file not shown.
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "demon-stronghold-visual.glb",
|
||||
"size": 15166984,
|
||||
"sha256": "9368984b3c183a8db6e8b7e18b7818edcf41b8edd45ffeca1d1df28aadc592a2",
|
||||
"size": 15167544,
|
||||
"sha256": "664181dc790b9e68cdc7451dbd0b1170a033a71ca19375743b6e66ca25889723",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "demon-stronghold-visual.optimized.glb",
|
||||
"size": 15166984,
|
||||
"checksum": "9368984b3c183a8db6e8b7e18b7818edcf41b8edd45ffeca1d1df28aadc592a2"
|
||||
"size": 15167544,
|
||||
"checksum": "664181dc790b9e68cdc7451dbd0b1170a033a71ca19375743b6e66ca25889723"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
Binary file not shown.
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "dungeon-of-dalanis-visual.glb",
|
||||
"size": 11936864,
|
||||
"sha256": "4c584ef34779e516f8e6ff3a4907eaab1c7c8d49d4c302de66e17b09222d8829",
|
||||
"size": 11937088,
|
||||
"sha256": "45cad819bc348dec8926f7de237c625e9d18697b4200804e0440dd16b8976959",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "dungeon-of-dalanis-visual.optimized.glb",
|
||||
"size": 11936864,
|
||||
"checksum": "4c584ef34779e516f8e6ff3a4907eaab1c7c8d49d4c302de66e17b09222d8829"
|
||||
"size": 11937088,
|
||||
"checksum": "45cad819bc348dec8926f7de237c625e9d18697b4200804e0440dd16b8976959"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
Binary file not shown.
@@ -284,8 +284,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "forsaken-abbey-visual.glb",
|
||||
"size": 9580212,
|
||||
"sha256": "37800c26cdf784658a57dcc7328ab1b9b23e46d018be6d1e6420f127768b0332",
|
||||
"size": 9580568,
|
||||
"sha256": "ee7afd5e9179e6bcb7ec9171798466282e09d4ef696a3f9639bcd2bbc2ae8f48",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "forsaken-abbey-visual.optimized.glb",
|
||||
"size": 9580212,
|
||||
"checksum": "37800c26cdf784658a57dcc7328ab1b9b23e46d018be6d1e6420f127768b0332"
|
||||
"size": 9580568,
|
||||
"checksum": "ee7afd5e9179e6bcb7ec9171798466282e09d4ef696a3f9639bcd2bbc2ae8f48"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
Binary file not shown.
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "grafu-castle-visual.glb",
|
||||
"size": 11149716,
|
||||
"sha256": "ba4552484ef9cddc1c00607d0dfd4c8fb0bae5b3cd3f992d215a580dac1e96ed",
|
||||
"size": 11150124,
|
||||
"sha256": "b775778f457d639e3f21982aaa6bd3ee1b0eba951c711d1ba319b0588e24a38f",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "grafu-castle-visual.optimized.glb",
|
||||
"size": 11149716,
|
||||
"checksum": "ba4552484ef9cddc1c00607d0dfd4c8fb0bae5b3cd3f992d215a580dac1e96ed"
|
||||
"size": 11150124,
|
||||
"checksum": "b775778f457d639e3f21982aaa6bd3ee1b0eba951c711d1ba319b0588e24a38f"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
Binary file not shown.
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "hall-of-survivors-visual.glb",
|
||||
"size": 15216768,
|
||||
"sha256": "18b9d116da599159719ad76eeee45a4d89056ee8c61cc4f21cab9a1d9228fff7",
|
||||
"size": 15216936,
|
||||
"sha256": "96b91b24462b8428e8808295dcea29d26de9be8431ea6667dde92cdded4ddf2a",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "hall-of-survivors-visual.optimized.glb",
|
||||
"size": 15216768,
|
||||
"checksum": "18b9d116da599159719ad76eeee45a4d89056ee8c61cc4f21cab9a1d9228fff7"
|
||||
"size": 15216936,
|
||||
"checksum": "96b91b24462b8428e8808295dcea29d26de9be8431ea6667dde92cdded4ddf2a"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.8 MiB |
Binary file not shown.
@@ -3,7 +3,7 @@
|
||||
"dungeonId": "hoto",
|
||||
"title": "HOTO",
|
||||
"status": "source-packaged",
|
||||
"generatedAt": "2026-08-01T01:00:34.655Z",
|
||||
"generatedAt": "2026-09-09T19:39:49.407Z",
|
||||
"source": {
|
||||
"recipe": "scripts/runewaker-pipeline/recipes/hoto.json",
|
||||
"wdb": "Resource/wdb/dungeon/dgn_echoes_of_the_sea.wdb",
|
||||
@@ -261,33 +261,37 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "hoto-visual.glb",
|
||||
"size": 6667208,
|
||||
"sha256": "0d12b9c6dc477704d2833c81f76113d340da7f07f5885a40bafd1321524e455e",
|
||||
"compression": "meshopt+ktx2"
|
||||
"relativeFile": "hoto-visual.glb",
|
||||
"size": 6710184,
|
||||
"sha256": "beff3a180c55166d5ebd7e3610e65b59f8104edfd7596aa22ac3a49c578fcdc0"
|
||||
},
|
||||
{
|
||||
"role": "collision",
|
||||
"fileName": "hoto-collision-000.glb",
|
||||
"relativeFile": "hoto-collision-000.glb",
|
||||
"size": 3088036,
|
||||
"sha256": "3116529f2d262ff4cab7997e852f9d15caacab6298921360b49dcafd52b8c475"
|
||||
},
|
||||
{
|
||||
"role": "collision",
|
||||
"fileName": "hoto-collision-001.glb",
|
||||
"relativeFile": "hoto-collision-001.glb",
|
||||
"size": 2989324,
|
||||
"sha256": "1f5cbd4b04b5a060029fd7c023ee19db5d8646566fcd01b931473b5b13c13b9f"
|
||||
},
|
||||
{
|
||||
"role": "collision",
|
||||
"fileName": "hoto-collision-002.glb",
|
||||
"relativeFile": "hoto-collision-002.glb",
|
||||
"size": 526648,
|
||||
"sha256": "0a468a6ef30941c04a20456866f0593f7e1f9f9bf8dc0024176d2128975f1879"
|
||||
},
|
||||
{
|
||||
"role": "navigation",
|
||||
"fileName": "hoto-navigation.glb",
|
||||
"relativeFile": "hoto-navigation.glb",
|
||||
"size": 482260,
|
||||
"sha256": "2cd78e6d8f52cb6488747a1f87cb688eedb77ccf412fb50674c25f70306477fd"
|
||||
"sha256": "a2bde2e4b8ed0d0bac8c4669a05192ff0a35ae9c64df0c0a29fe3735b65cbf4b"
|
||||
}
|
||||
],
|
||||
"review": {
|
||||
@@ -300,6 +304,6 @@
|
||||
"exceptions": [
|
||||
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
|
||||
"Collision initially includes decorative primary-ROS geometry.",
|
||||
"Original skeletal animations and complex scripted mechanics remain later passes."
|
||||
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work."
|
||||
]
|
||||
}
|
||||
@@ -4,15 +4,11 @@
|
||||
"status": "review-required",
|
||||
"source": {
|
||||
"file": "hoto-visual.glb",
|
||||
"size": 26313948
|
||||
"size": 26314004
|
||||
},
|
||||
"optimized": {
|
||||
"file": "hoto-visual.optimized.glb",
|
||||
"size": 6667208,
|
||||
"checksum": "0d12b9c6dc477704d2833c81f76113d340da7f07f5885a40bafd1321524e455e"
|
||||
"size": 6710184
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
"node scripts/asset-pipeline/compress-ktx2.mjs <optimized.glb> --concurrency 1 --jobs 2"
|
||||
]
|
||||
"compression": "meshopt+ktx2"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"sourceWdb": "D:\\Projects\\rom-pvt-server-backups\\Runewaker\\Resource\\wdb\\dungeon\\dgn_echoes_of_the_sea.wdb",
|
||||
"sourceWdb": "F:\\Projects\\rom-pvt-server-backups\\Runewaker\\Resource\\wdb\\dungeon\\dgn_echoes_of_the_sea.wdb",
|
||||
"coordinateSystem": "runewaker-left-handed-y-up",
|
||||
"descriptorCount": 1627,
|
||||
"descriptors": [
|
||||
|
||||
Binary file not shown.
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "ice-dwarf-kingdom-visual.glb",
|
||||
"size": 9880192,
|
||||
"sha256": "a6b902e85bb6026f18281d77efa235ec880ffc2fc13ea6bfdc431d24af768e00",
|
||||
"size": 9880304,
|
||||
"sha256": "41cef8ce1bf6dbf7df0c4e2797a325e4d50a3870d435d46684600894d4aa92e7",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "ice-dwarf-kingdom-visual.optimized.glb",
|
||||
"size": 9880192,
|
||||
"checksum": "a6b902e85bb6026f18281d77efa235ec880ffc2fc13ea6bfdc431d24af768e00"
|
||||
"size": 9880304,
|
||||
"checksum": "41cef8ce1bf6dbf7df0c4e2797a325e4d50a3870d435d46684600894d4aa92e7"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "kal-turok-hive-visual.glb",
|
||||
"size": 1777616,
|
||||
"sha256": "012867deefc4dc5a18c3e1a507309aa48f119cf4c2e88968fdd2787936ed7c24",
|
||||
"size": 1777728,
|
||||
"sha256": "75c0c596218cec07003e2def1d74c8e1f5ca3010ed370db558a1786b003f4f1c",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
Binary file not shown.
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "kal-turok-hive-visual.optimized.glb",
|
||||
"size": 1777616,
|
||||
"checksum": "012867deefc4dc5a18c3e1a507309aa48f119cf4c2e88968fdd2787936ed7c24"
|
||||
"size": 1777728,
|
||||
"checksum": "75c0c596218cec07003e2def1d74c8e1f5ca3010ed370db558a1786b003f4f1c"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"dungeonId": "kalins-shrine",
|
||||
"title": "Kalin's Shrine",
|
||||
"status": "source-packaged",
|
||||
"generatedAt": "2026-08-01T00:57:43.432Z",
|
||||
"generatedAt": "2026-09-09T18:51:01.674Z",
|
||||
"source": {
|
||||
"recipe": "scripts/runewaker-pipeline/recipes/kalins-shrine.json",
|
||||
"wdb": "Resource/wdb/dungeon/dgn_kleeon_temple.wdb",
|
||||
@@ -277,33 +277,37 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "kalins-shrine-visual.glb",
|
||||
"size": 8747072,
|
||||
"sha256": "769accb48e23c9f80e28697e3e8454ef4148243a69ce247ed06ba4eb35bb0da7",
|
||||
"compression": "meshopt+ktx2"
|
||||
"relativeFile": "kalins-shrine-visual.glb",
|
||||
"size": 8851724,
|
||||
"sha256": "023c43a56253e97fa0526185c61f76e0e9fa8398b7a35771728566c331cd22a9"
|
||||
},
|
||||
{
|
||||
"role": "collision",
|
||||
"fileName": "kalins-shrine-collision-000.glb",
|
||||
"relativeFile": "kalins-shrine-collision-000.glb",
|
||||
"size": 4748976,
|
||||
"sha256": "ed1e713c159e024b4786ef8befadbfedd3849ba580e7f49970e2785b6f4fff7d"
|
||||
},
|
||||
{
|
||||
"role": "collision",
|
||||
"fileName": "kalins-shrine-collision-001.glb",
|
||||
"relativeFile": "kalins-shrine-collision-001.glb",
|
||||
"size": 4555800,
|
||||
"sha256": "5fea14212353593c8daced2e303f88dae010f8f73aedb9890a3eee67eb8dd7cd"
|
||||
},
|
||||
{
|
||||
"role": "collision",
|
||||
"fileName": "kalins-shrine-collision-002.glb",
|
||||
"relativeFile": "kalins-shrine-collision-002.glb",
|
||||
"size": 1215728,
|
||||
"sha256": "e51d48d1f4521f1a4722fd4c528a90bdfd2978a87d2ace0dd662f23475490565"
|
||||
},
|
||||
{
|
||||
"role": "navigation",
|
||||
"fileName": "kalins-shrine-navigation.glb",
|
||||
"relativeFile": "kalins-shrine-navigation.glb",
|
||||
"size": 327792,
|
||||
"sha256": "37fd7f8efac36f83279efbfb33df37646453af214db29597bd14c8e9f19ddc04"
|
||||
"sha256": "6b77496f7b46890d401f955f9254ae37d25d9a1287a3bc9e787e9a254f9d9071"
|
||||
}
|
||||
],
|
||||
"review": {
|
||||
@@ -316,6 +320,6 @@
|
||||
"exceptions": [
|
||||
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
|
||||
"Collision initially includes decorative primary-ROS geometry.",
|
||||
"Original skeletal animations and complex scripted mechanics remain later passes."
|
||||
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work."
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 892 KiB After Width: | Height: | Size: 892 KiB |
Binary file not shown.
@@ -4,15 +4,11 @@
|
||||
"status": "review-required",
|
||||
"source": {
|
||||
"file": "kalins-shrine-visual.glb",
|
||||
"size": 26551164
|
||||
"size": 26551296
|
||||
},
|
||||
"optimized": {
|
||||
"file": "kalins-shrine-visual.optimized.glb",
|
||||
"size": 8747072,
|
||||
"checksum": "769accb48e23c9f80e28697e3e8454ef4148243a69ce247ed06ba4eb35bb0da7"
|
||||
"size": 8851724
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
"node scripts/asset-pipeline/compress-ktx2.mjs <optimized.glb> --concurrency 1 --jobs 2"
|
||||
]
|
||||
"compression": "meshopt+ktx2"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"sourceWdb": "D:\\Projects\\rom-pvt-server-backups\\Runewaker\\Resource\\wdb\\dungeon\\dgn_kleeon_temple.wdb",
|
||||
"sourceWdb": "F:\\Projects\\rom-pvt-server-backups\\Runewaker\\Resource\\wdb\\dungeon\\dgn_kleeon_temple.wdb",
|
||||
"coordinateSystem": "runewaker-left-handed-y-up",
|
||||
"descriptorCount": 943,
|
||||
"descriptors": [
|
||||
|
||||
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "kawaks-tomb-visual.glb",
|
||||
"size": 8963188,
|
||||
"sha256": "de54d223725e2c3dfb0c47a97c208a212ef9423860bd0ac390f1482b302d3a32",
|
||||
"size": 8963376,
|
||||
"sha256": "3f1755780540c54a1ba9b528e646ebd39b12ab127e41fa2d497fd7749d202018",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
Binary file not shown.
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "kawaks-tomb-visual.optimized.glb",
|
||||
"size": 8963188,
|
||||
"checksum": "de54d223725e2c3dfb0c47a97c208a212ef9423860bd0ac390f1482b302d3a32"
|
||||
"size": 8963376,
|
||||
"checksum": "3f1755780540c54a1ba9b528e646ebd39b12ab127e41fa2d497fd7749d202018"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "kulech-bones-nest-visual.glb",
|
||||
"size": 12146892,
|
||||
"sha256": "2d912dcf7c0c8e59e36362fd947df1d5554c5dd5060590e1abb112e4c39f606f",
|
||||
"size": 12147008,
|
||||
"sha256": "b06b42b3c7f19abf9d645eec6be78f868cf6966e0b472e2ca4a1c36d3cf4d2d1",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
Binary file not shown.
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "kulech-bones-nest-visual.optimized.glb",
|
||||
"size": 12146892,
|
||||
"checksum": "2d912dcf7c0c8e59e36362fd947df1d5554c5dd5060590e1abb112e4c39f606f"
|
||||
"size": 12147008,
|
||||
"checksum": "b06b42b3c7f19abf9d645eec6be78f868cf6966e0b472e2ca4a1c36d3cf4d2d1"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "lair-demon-dragon-visual.glb",
|
||||
"size": 4551300,
|
||||
"sha256": "d28481504802203e800985836f2b0907d67f6d6d0495c67500914e749d771ffe",
|
||||
"size": 4551356,
|
||||
"sha256": "b371465e96ef978915c9a515846ecf9dccb98607fd549617a1c71f45468fa5a7",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
Binary file not shown.
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "lair-demon-dragon-visual.optimized.glb",
|
||||
"size": 4551300,
|
||||
"checksum": "d28481504802203e800985836f2b0907d67f6d6d0495c67500914e749d771ffe"
|
||||
"size": 4551356,
|
||||
"checksum": "b371465e96ef978915c9a515846ecf9dccb98607fd549617a1c71f45468fa5a7"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "mystic-altar-visual.glb",
|
||||
"size": 5552872,
|
||||
"sha256": "765dd1b15bbe79fe1ffc2104e10caece19acaf86c919f0327419cb3609dbf600",
|
||||
"size": 5552892,
|
||||
"sha256": "c37f27643598c8fe6401828be2950bc08f299dfe491df37736fda4662c70a96e",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
Binary file not shown.
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "mystic-altar-visual.optimized.glb",
|
||||
"size": 5552872,
|
||||
"checksum": "765dd1b15bbe79fe1ffc2104e10caece19acaf86c919f0327419cb3609dbf600"
|
||||
"size": 5552892,
|
||||
"checksum": "c37f27643598c8fe6401828be2950bc08f299dfe491df37736fda4662c70a96e"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"dungeonId": "paspers-shrine",
|
||||
"title": "Pasper's Shrine",
|
||||
"status": "source-packaged",
|
||||
"generatedAt": "2026-08-01T01:00:57.399Z",
|
||||
"generatedAt": "2026-09-09T19:19:45.719Z",
|
||||
"source": {
|
||||
"recipe": "scripts/runewaker-pipeline/recipes/paspers-shrine.json",
|
||||
"wdb": "Resource/wdb/dungeon/dgn_borsburs.wdb",
|
||||
@@ -137,27 +137,30 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "paspers-shrine-visual.glb",
|
||||
"size": 5625952,
|
||||
"sha256": "82ea6cafd19c0a323c994795699223a07193807da29b300eb12dac41146f1034",
|
||||
"compression": "meshopt+ktx2"
|
||||
"relativeFile": "paspers-shrine-visual.glb",
|
||||
"size": 5674908,
|
||||
"sha256": "bcf6443e8adb8b7f7dd81f4a97b79aac1c99756a83289bbada31853d2d9adb7c"
|
||||
},
|
||||
{
|
||||
"role": "collision",
|
||||
"fileName": "paspers-shrine-collision-000.glb",
|
||||
"relativeFile": "paspers-shrine-collision-000.glb",
|
||||
"size": 4291972,
|
||||
"sha256": "b6efbe1b90545dd2c2af3bfc7396616147211f6f79b999ccf81ca3680fdc984e"
|
||||
},
|
||||
{
|
||||
"role": "collision",
|
||||
"fileName": "paspers-shrine-collision-001.glb",
|
||||
"relativeFile": "paspers-shrine-collision-001.glb",
|
||||
"size": 4479896,
|
||||
"sha256": "1c6f52d7e21171acfbe3fb43335b2c7f3de13217881840947f15fd0722adb4de"
|
||||
},
|
||||
{
|
||||
"role": "navigation",
|
||||
"fileName": "paspers-shrine-navigation.glb",
|
||||
"relativeFile": "paspers-shrine-navigation.glb",
|
||||
"size": 152356,
|
||||
"sha256": "e05fb0b7a3c86bba3ac46606365169dea6c575281b2bb15707addcedb279f108"
|
||||
"sha256": "4ebba3fb97a99df49c7301dbfcdb8154634cf4e56a85c5ed587c7515221d0a10"
|
||||
}
|
||||
],
|
||||
"review": {
|
||||
@@ -170,6 +173,6 @@
|
||||
"exceptions": [
|
||||
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
|
||||
"Collision initially includes decorative primary-ROS geometry.",
|
||||
"Original skeletal animations and complex scripted mechanics remain later passes."
|
||||
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work."
|
||||
]
|
||||
}
|
||||
@@ -4,15 +4,11 @@
|
||||
"status": "review-required",
|
||||
"source": {
|
||||
"file": "paspers-shrine-visual.glb",
|
||||
"size": 14723576
|
||||
"size": 14723652
|
||||
},
|
||||
"optimized": {
|
||||
"file": "paspers-shrine-visual.optimized.glb",
|
||||
"size": 5625952,
|
||||
"checksum": "82ea6cafd19c0a323c994795699223a07193807da29b300eb12dac41146f1034"
|
||||
"size": 5674908
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
"node scripts/asset-pipeline/compress-ktx2.mjs <optimized.glb> --concurrency 1 --jobs 2"
|
||||
]
|
||||
"compression": "meshopt+ktx2"
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 1024 KiB After Width: | Height: | Size: 1024 KiB |
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"sourceWdb": "D:\\Projects\\rom-pvt-server-backups\\Runewaker\\Resource\\wdb\\dungeon\\dgn_borsburs.wdb",
|
||||
"sourceWdb": "F:\\Projects\\rom-pvt-server-backups\\Runewaker\\Resource\\wdb\\dungeon\\dgn_borsburs.wdb",
|
||||
"coordinateSystem": "runewaker-left-handed-y-up",
|
||||
"descriptorCount": 564,
|
||||
"descriptors": [
|
||||
|
||||
@@ -149,8 +149,8 @@
|
||||
"role": "visual",
|
||||
"fileName": "raksha-temple-visual.glb",
|
||||
"relativeFile": "raksha-temple-visual.glb",
|
||||
"size": 23554528,
|
||||
"sha256": "3a61098e772a0c656aa25d88aee357f78b84c738e45150fca6fd5059c2b675f7"
|
||||
"size": 23556584,
|
||||
"sha256": "1fad5149acfce870be822f5355369f15dd76fba280bbeda31ff7f371c442082f"
|
||||
},
|
||||
{
|
||||
"role": "collision",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "raksha-temple-visual.optimized.glb",
|
||||
"size": 23554528
|
||||
"size": 23556584
|
||||
},
|
||||
"compression": "meshopt+ktx2"
|
||||
}
|
||||
Binary file not shown.
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "sardo-castle-visual.glb",
|
||||
"size": 7756568,
|
||||
"sha256": "16d759af9f8a3f98281e89362c7fb52dceff6543bbd837a7c6f89015920cdd94",
|
||||
"size": 7756828,
|
||||
"sha256": "191396625e301a41afab38e168848a198a9be5918a071dadda841683a7c6261e",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "sardo-castle-visual.optimized.glb",
|
||||
"size": 7756568,
|
||||
"checksum": "16d759af9f8a3f98281e89362c7fb52dceff6543bbd837a7c6f89015920cdd94"
|
||||
"size": 7756828,
|
||||
"checksum": "191396625e301a41afab38e168848a198a9be5918a071dadda841683a7c6261e"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
Binary file not shown.
@@ -149,8 +149,8 @@
|
||||
"role": "visual",
|
||||
"fileName": "the-origin-visual.glb",
|
||||
"relativeFile": "the-origin-visual.glb",
|
||||
"size": 28683180,
|
||||
"sha256": "d1ded0316b2cbaa07458c56864c47c01afaa85e4f33dd13f5d95984d7305fb92"
|
||||
"size": 28684736,
|
||||
"sha256": "1f96c2b57a24470a003834a8c91cb0ceda5b01824d5bfa31904afd247c404e88"
|
||||
},
|
||||
{
|
||||
"role": "collision",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "the-origin-visual.optimized.glb",
|
||||
"size": 28683180
|
||||
"size": 28684736
|
||||
},
|
||||
"compression": "meshopt+ktx2"
|
||||
}
|
||||
Binary file not shown.
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "tomb-seven-heroes-visual.glb",
|
||||
"size": 10910804,
|
||||
"sha256": "d9f39da6b61c9f841c5d075e4985bc67e533ce422313fdf38d5f6c99dbb304c1",
|
||||
"size": 10911104,
|
||||
"sha256": "09250a1fbabf8a03f922c9a3950aee3daa1edc54215cd03ddc9bf5c96e1429bd",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "tomb-seven-heroes-visual.optimized.glb",
|
||||
"size": 10910804,
|
||||
"checksum": "d9f39da6b61c9f841c5d075e4985bc67e533ce422313fdf38d5f6c99dbb304c1"
|
||||
"size": 10911104,
|
||||
"checksum": "09250a1fbabf8a03f922c9a3950aee3daa1edc54215cd03ddc9bf5c96e1429bd"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
Binary file not shown.
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "treasure-trove-visual.glb",
|
||||
"size": 7433280,
|
||||
"sha256": "f4c4dc12df65f8afc3c696308db17cb42b6a5894086dbe870dae2e21e1258448",
|
||||
"size": 7433780,
|
||||
"sha256": "c56d429187fd2ffbe20e11d5e7bbfd2128a779c9251fa2ab4b12286caf0de0b6",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "treasure-trove-visual.optimized.glb",
|
||||
"size": 7433280,
|
||||
"checksum": "f4c4dc12df65f8afc3c696308db17cb42b6a5894086dbe870dae2e21e1258448"
|
||||
"size": 7433780,
|
||||
"checksum": "c56d429187fd2ffbe20e11d5e7bbfd2128a779c9251fa2ab4b12286caf0de0b6"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
Binary file not shown.
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "warnorken-arena-visual.glb",
|
||||
"size": 3752136,
|
||||
"sha256": "c452a843c2e4123d4fe0d20da41558f5b5a1906548b3d091dc691ffc42ed19d9",
|
||||
"size": 3752248,
|
||||
"sha256": "ee81c9e7c94d624977143297ca6c070c40799fdda1c51b92044c70dff53987ea",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "warnorken-arena-visual.optimized.glb",
|
||||
"size": 3752136,
|
||||
"checksum": "c452a843c2e4123d4fe0d20da41558f5b5a1906548b3d091dc691ffc42ed19d9"
|
||||
"size": 3752248,
|
||||
"checksum": "ee81c9e7c94d624977143297ca6c070c40799fdda1c51b92044c70dff53987ea"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
Binary file not shown.
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "ystra-labyrinth-boss1-visual.glb",
|
||||
"size": 2709352,
|
||||
"sha256": "c8ca72a5fdf487fa71a1aecd1e66ea821183f476540be9a89444b409be957c25",
|
||||
"size": 2709464,
|
||||
"sha256": "5e35eeccf5e207477bfb3a38dee75d53a5c5467433a90270b79a9ba8619a6260",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "ystra-labyrinth-boss1-visual.optimized.glb",
|
||||
"size": 2709352,
|
||||
"checksum": "c8ca72a5fdf487fa71a1aecd1e66ea821183f476540be9a89444b409be957c25"
|
||||
"size": 2709464,
|
||||
"checksum": "5e35eeccf5e207477bfb3a38dee75d53a5c5467433a90270b79a9ba8619a6260"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
Binary file not shown.
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "ystra-labyrinth-boss2-visual.glb",
|
||||
"size": 2682688,
|
||||
"sha256": "d52524621220e16d844badc24ee745f4374798031b2b0bc7236decbe3b9588ab",
|
||||
"size": 2682800,
|
||||
"sha256": "be717e3d6e6301ff60762552283386d73da04240e6a7d1e36933e83e02d0250a",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "ystra-labyrinth-boss2-visual.optimized.glb",
|
||||
"size": 2682688,
|
||||
"checksum": "d52524621220e16d844badc24ee745f4374798031b2b0bc7236decbe3b9588ab"
|
||||
"size": 2682800,
|
||||
"checksum": "be717e3d6e6301ff60762552283386d73da04240e6a7d1e36933e83e02d0250a"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
Binary file not shown.
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "ystra-labyrinth-boss3-visual.glb",
|
||||
"size": 2682688,
|
||||
"sha256": "d52524621220e16d844badc24ee745f4374798031b2b0bc7236decbe3b9588ab",
|
||||
"size": 2682800,
|
||||
"sha256": "be717e3d6e6301ff60762552283386d73da04240e6a7d1e36933e83e02d0250a",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "ystra-labyrinth-boss3-visual.optimized.glb",
|
||||
"size": 2682688,
|
||||
"checksum": "d52524621220e16d844badc24ee745f4374798031b2b0bc7236decbe3b9588ab"
|
||||
"size": 2682800,
|
||||
"checksum": "be717e3d6e6301ff60762552283386d73da04240e6a7d1e36933e83e02d0250a"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
Binary file not shown.
@@ -137,8 +137,8 @@
|
||||
{
|
||||
"role": "visual",
|
||||
"fileName": "zurhidon-stronghold-124-visual.glb",
|
||||
"size": 17189492,
|
||||
"sha256": "a2d450946ca9687395cf65815b931c7817d9f41041afeb36ba1a351ec24104c4",
|
||||
"size": 17189904,
|
||||
"sha256": "58be5be365b14e42772a4560c5c1d10fceb8aa8abb2c1771cc21ba2b2e02872a",
|
||||
"compression": "meshopt+ktx2"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
},
|
||||
"optimized": {
|
||||
"file": "zurhidon-stronghold-124-visual.optimized.glb",
|
||||
"size": 17189492,
|
||||
"checksum": "a2d450946ca9687395cf65815b931c7817d9f41041afeb36ba1a351ec24104c4"
|
||||
"size": 17189904,
|
||||
"checksum": "58be5be365b14e42772a4560c5c1d10fceb8aa8abb2c1771cc21ba2b2e02872a"
|
||||
},
|
||||
"compression": "meshopt+ktx2",
|
||||
"commands": [
|
||||
|
||||
Loaded 100 of 120 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user