mirror of
https://github.com/skillbert/rsmv.git
synced 2025-12-23 21:47:48 -05:00
add option to compare map versions in viewer
This commit is contained in:
8
generated/maplabels.d.ts
vendored
8
generated/maplabels.d.ts
vendored
@@ -26,6 +26,7 @@ export type maplabels = {
|
||||
upper: number,
|
||||
} | null
|
||||
rightclick_1?: string | null
|
||||
unktext_0b?: string | null
|
||||
polygon?: {
|
||||
pointcount: number,
|
||||
points: {
|
||||
@@ -38,14 +39,14 @@ export type maplabels = {
|
||||
number,
|
||||
number,
|
||||
],
|
||||
always_1: number,
|
||||
always_1: (number|1),
|
||||
back_color: [
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
],
|
||||
pointplanes: number[],
|
||||
pointplanes: (number[]|null),
|
||||
} | null
|
||||
rightclick_2?: string | null
|
||||
category?: number | null
|
||||
@@ -55,7 +56,8 @@ export type maplabels = {
|
||||
lower: number,
|
||||
upper: number,
|
||||
} | null
|
||||
unknown_15?: number[] | null
|
||||
unknown_15?: number | null
|
||||
unknown_16?: number | null
|
||||
background_sprite?: number | null
|
||||
legacy_switch?: {
|
||||
varbit: number,
|
||||
|
||||
@@ -26,6 +26,7 @@ import { HSL2RGB, HSL2RGBfloat, packedHSL2HSL } from "../utils";
|
||||
import { loadProcTexture } from "./proceduraltexture";
|
||||
import { maplabels } from "../../generated/maplabels";
|
||||
import { minimapLocMaterial } from "../rs3shaders";
|
||||
import { DependencyGraph, getDependencies } from "../scripts/dependencies";
|
||||
|
||||
const constModelOffset = 1000000;
|
||||
|
||||
@@ -199,6 +200,7 @@ export class EngineCache extends CachingFileSource {
|
||||
mapMapscenes: mapscenes[] = [];
|
||||
mapMaplabels: maplabels[] = [];
|
||||
jsonSearchCache = new Map<string, { files: Promise<any[]>, schema: JSONSchema6Definition }>();
|
||||
dependencyGraph: Promise<DependencyGraph> | null = null;
|
||||
|
||||
legacyData: LegacyData | null = null;
|
||||
classicData: ClassicConfig | null = null;
|
||||
@@ -269,6 +271,11 @@ export class EngineCache extends CachingFileSource {
|
||||
return this;
|
||||
}
|
||||
|
||||
async getDependencyGraph() {
|
||||
this.dependencyGraph ??= getDependencies(this, { lazyMapChunks: true });
|
||||
return this.dependencyGraph;
|
||||
}
|
||||
|
||||
async getGameFile(type: keyof LegacyData & keyof typeof cacheMajors, id: number) {
|
||||
if (this.legacyData) {
|
||||
return this.legacyData[type][id];
|
||||
|
||||
2
src/cache/openrs2loader.ts
vendored
2
src/cache/openrs2loader.ts
vendored
@@ -214,7 +214,7 @@ export class Openrs2CacheSource extends cache.DirectCacheFileSource {
|
||||
if (this.fscache && typeof crc != "undefined" && crc != 0) {//TODO fix places that use a magic 0 crc
|
||||
cachedfile = await this.fscache.getFile(major, minor, crc);
|
||||
} else {
|
||||
console.log("uncachable", major, minor, crc);
|
||||
// console.log("uncachable", major, minor, crc);
|
||||
}
|
||||
let rawfile = cachedfile ?? await this.downloadFile(major, minor);
|
||||
if (this.fscache && !cachedfile && typeof crc != "undefined" && crc != 0) {
|
||||
|
||||
@@ -183,7 +183,6 @@ export function mapsquareLocDependencies(grid: TileGrid, deps: DependencyGraph,
|
||||
}
|
||||
outlocgroups.push(outgroup);
|
||||
for (let loc of group) {
|
||||
let first = loc[0];
|
||||
v0.set(0, 0, 0);
|
||||
v1.set(0, 0, 0);
|
||||
for (let mesh of loc) {
|
||||
@@ -194,7 +193,7 @@ export function mapsquareLocDependencies(grid: TileGrid, deps: DependencyGraph,
|
||||
v1.max(v2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//8 vertices, one for each bounding box corner
|
||||
boxAttribute.setXYZ(0, v0.x, v0.y, v0.z);
|
||||
boxAttribute.setXYZ(1, v0.x, v0.y, v1.z);
|
||||
@@ -204,7 +203,8 @@ export function mapsquareLocDependencies(grid: TileGrid, deps: DependencyGraph,
|
||||
boxAttribute.setXYZ(5, v1.x, v0.y, v1.z);
|
||||
boxAttribute.setXYZ(6, v1.x, v1.y, v0.z);
|
||||
boxAttribute.setXYZ(7, v1.x, v1.y, v1.z);
|
||||
|
||||
|
||||
let first = loc[0];
|
||||
let trans = transformVertexPositions(boxAttribute, first.morph, grid, first.maxy, rect.x * tiledimensions * rs2ChunkSize, rect.z * tiledimensions * rs2ChunkSize);
|
||||
// trans.newpos.applyMatrix4(trans.matrix);
|
||||
let bounds = [...trans.newpos.array as Float32Array].map(v => v | 0);
|
||||
@@ -335,8 +335,7 @@ export function compareLocDependencies(chunka: ChunkLocDependencies[], chunkb: C
|
||||
return vertsets;
|
||||
}
|
||||
|
||||
export async function mapdiffmesh(scene: ThreejsSceneCache, points: number[][]) {
|
||||
let col = [255, 0, 0] as [number, number, number];
|
||||
export async function mapdiffmesh(scene: ThreejsSceneCache, points: number[][], col: [number, number, number] = [255, 0, 0]) {
|
||||
let tri = (model: ModelBuilder, verts: number[], a: number, b: number, c: number) => model.mat(-1).addTriangle(col,
|
||||
verts.slice(a * 3, a * 3 + 3) as any,
|
||||
verts.slice(b * 3, b * 3 + 3) as any,
|
||||
|
||||
@@ -7,7 +7,7 @@ import { cacheMajors } from "../constants";
|
||||
import { parse } from "../opdecoder";
|
||||
import { canvasToImageFile, flipImage, pixelsToImageFile } from "../imgutils";
|
||||
import { EngineCache, ThreejsSceneCache } from "../3d/modeltothree";
|
||||
import { crc32addInt, DependencyGraph, getDependencies } from "../scripts/dependencies";
|
||||
import { crc32addInt, DependencyGraph } from "../scripts/dependencies";
|
||||
import { ScriptOutput } from "../scriptrunner";
|
||||
import { CallbackPromise, delay, stringToFileRange, trickleTasks } from "../utils";
|
||||
import { drawCollision } from "./collisionimage";
|
||||
@@ -227,7 +227,8 @@ export async function runMapRender(output: ScriptOutput, filesource: CacheFileSo
|
||||
if (areas.length == 1) {
|
||||
deparea = { x: areas[0].x - 2, z: areas[0].z - 2, xsize: areas[0].xsize + 2, zsize: areas[0].zsize + 2 };
|
||||
}
|
||||
var deps = await getDependencies(engine, { area: deparea });
|
||||
var deps = await engine.getDependencyGraph();
|
||||
await deps.preloadChunkDependencies({ area: deparea });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
progress.updateProp("deps", "starting dependency graph");
|
||||
@@ -523,7 +524,7 @@ class SimpleHasher {
|
||||
let exists = false;
|
||||
for (let z = rect.z; z < rect.z + rect.zsize; z++) {
|
||||
for (let x = rect.x; x < rect.x + rect.xsize; x++) {
|
||||
exists ||= this.depstracker.deps.hasEntry(this.depstracker.deps.makeDeptName("mapsquare", x + z * worldStride));
|
||||
exists ||= this.depstracker.deps.hasEntry("mapsquare", x + z * worldStride);
|
||||
}
|
||||
}
|
||||
return exists;
|
||||
|
||||
@@ -14,7 +14,7 @@ const depids = arrayEnum(["material", "model", "item", "loc", "mapsquare", "sequ
|
||||
const depidmap = Object.fromEntries(depids.map((q, i) => [q, i]));
|
||||
export type DepTypes = typeof depids[number];
|
||||
|
||||
type DepArgs = { area?: MapRect, modelMaterials?: boolean } | undefined;
|
||||
type DepArgs = { area?: MapRect } | undefined;
|
||||
type DepCallback = (holdertype: DepTypes, holderId: number, deptType: DepTypes, depId: number) => void;
|
||||
type HashCallback = (depType: DepTypes, depId: number, hash: number, version: number) => void;
|
||||
type DepCollector = (cache: EngineCache, addDep: DepCallback, addHash: HashCallback, args: DepArgs) => Promise<void>;
|
||||
@@ -35,6 +35,25 @@ const mapsquareDeps: DepCollector = async (cache, addDep, addHash) => {
|
||||
}
|
||||
}
|
||||
|
||||
function chunkDeps(data: ChunkData, addDep: DepCallback, addHash: HashCallback) {
|
||||
let squareindex = data.mapsquarex + data.mapsquarez * worldStride;
|
||||
addHash("mapsquare", squareindex, data.chunkfilehash, data.chunkfileversion);
|
||||
for (let loc of data.rawlocs) {
|
||||
addDep("loc", loc.id, "mapsquare", squareindex);
|
||||
}
|
||||
|
||||
//batch these before adding for performance
|
||||
let overlays = new Set<number>();
|
||||
let underlays = new Set<number>();
|
||||
for (let tile of data.tiles) {
|
||||
if (tile.overlay != null) { overlays.add(tile.overlay); }
|
||||
if (tile.underlay != null) { underlays.add(tile.underlay); }
|
||||
}
|
||||
//set iterators are same as insertion order according to the spec
|
||||
overlays.forEach(id => addDep("overlay", id, "mapsquare", squareindex));
|
||||
underlays.forEach(id => addDep("overlay", id, "mapsquare", squareindex));
|
||||
}
|
||||
|
||||
const mapsquareDeps2: DepCollector = async (cache, addDep, addHash, args) => {
|
||||
await trickleTasksTwoStep(20, function* () {
|
||||
let rect = args?.area ?? { x: 0, z: 0, xsize: 100, zsize: 200 };
|
||||
@@ -45,22 +64,7 @@ const mapsquareDeps2: DepCollector = async (cache, addDep, addHash, args) => {
|
||||
}
|
||||
}, data => {
|
||||
if (!data) { return; }
|
||||
let squareindex = data.mapsquarex + data.mapsquarez * worldStride;
|
||||
addHash("mapsquare", squareindex, data.chunkfilehash, data.chunkfileversion);
|
||||
for (let loc of data.rawlocs) {
|
||||
addDep("loc", loc.id, "mapsquare", squareindex);
|
||||
}
|
||||
|
||||
//batch these before adding for performance
|
||||
let overlays = new Set<number>();
|
||||
let underlays = new Set<number>();
|
||||
for (let tile of data.tiles) {
|
||||
if (tile.overlay != null) { overlays.add(tile.overlay); }
|
||||
if (tile.underlay != null) { underlays.add(tile.underlay); }
|
||||
}
|
||||
|
||||
overlays.forEach(id => addDep("overlay", id, "mapsquare", squareindex));
|
||||
underlays.forEach(id => addDep("overlay", id, "mapsquare", squareindex));
|
||||
chunkDeps(data, addDep, addHash);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -297,20 +301,20 @@ const modelDeps: DepCollector = async (cache, addDep, addHash, opts) => {
|
||||
if (!modelindex) { continue; }
|
||||
addHash("model", modelindex.minor, modelindex.crc, modelindex.version);
|
||||
|
||||
if (opts?.modelMaterials) {
|
||||
let file = await cache.getFile(modelindex.major, modelindex.minor, modelindex.crc);
|
||||
let model = parse.models.read(file, cache);
|
||||
for (let mesh of model.meshes) {
|
||||
if (mesh.materialArgument != 0) {
|
||||
addDep("material", mesh.materialArgument - 1, "model", modelindex.minor);
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (opts?.modelMaterials) {
|
||||
// let file = await cache.getFile(modelindex.major, modelindex.minor, modelindex.crc);
|
||||
// let model = parse.models.read(file, cache);
|
||||
// for (let mesh of model.meshes) {
|
||||
// if (mesh.materialArgument != 0) {
|
||||
// addDep("material", mesh.materialArgument - 1, "model", modelindex.minor);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
export type DependencyGraph = (typeof getDependencies) extends ((...args: any[]) => Promise<infer T>) ? T : never;
|
||||
export async function getDependencies(cache: EngineCache, args?: DepArgs) {
|
||||
export async function getDependencies(cache: EngineCache, args?: {}) {
|
||||
let dependentsMap = new Map<string, string[]>();
|
||||
let dependencyMap = new Map<string, string[]>();
|
||||
let hashes = new Map<string, number>();
|
||||
@@ -341,8 +345,21 @@ export async function getDependencies(cache: EngineCache, args?: DepArgs) {
|
||||
|
||||
globalThis.dependentsMap = dependentsMap;
|
||||
|
||||
|
||||
let runDependencyGroup = async (run: DepCollector, args) => {
|
||||
try {
|
||||
console.log(`starting ${run.name}`);
|
||||
let t = Date.now();
|
||||
await run(cache, addDep, addHash, args);
|
||||
console.log(`finished ${run.name}, duration ${((Date.now() - t) / 1000).toFixed(1)}`);
|
||||
} catch (e) {
|
||||
debugger;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
let runs: DepCollector[] = [
|
||||
mapsquareDeps2,
|
||||
// mapsquareDeps2,
|
||||
locationDeps,
|
||||
itemDeps,
|
||||
animgroupDeps,
|
||||
@@ -357,16 +374,12 @@ export async function getDependencies(cache: EngineCache, args?: DepArgs) {
|
||||
// framesetDeps,
|
||||
];
|
||||
|
||||
try {
|
||||
for (let run of runs) {
|
||||
console.log(`starting ${run.name}`);
|
||||
let t = Date.now();
|
||||
await run(cache, addDep, addHash, args);
|
||||
console.log(`finished ${run.name}, duration ${((Date.now() - t) / 1000).toFixed(1)}`);
|
||||
}
|
||||
} catch (e) {
|
||||
debugger;
|
||||
throw e;
|
||||
for (let run of runs) {
|
||||
await runDependencyGroup(run, args);
|
||||
}
|
||||
|
||||
let preloadChunkDependencies = (args?: DepArgs) => {
|
||||
return runDependencyGroup(mapsquareDeps2, args);
|
||||
}
|
||||
|
||||
let makeDeptName = (deptType: DepTypes, id: number) => {
|
||||
@@ -404,12 +417,19 @@ export async function getDependencies(cache: EngineCache, args?: DepArgs) {
|
||||
return crc;
|
||||
}
|
||||
|
||||
let hasEntry = (depname: string) => {
|
||||
return hashes.has(depname);
|
||||
let hasEntry = (deptType: DepTypes, depId: number) => {
|
||||
return hashes.has(makeDeptName(deptType, depId));
|
||||
}
|
||||
|
||||
return { dependencyMap, dependentsMap, maxVersion, cascadeDependencies, makeDeptName, hashDependencies, hasEntry };
|
||||
let insertMapChunk = (data: ChunkData) => {
|
||||
chunkDeps(data, addDep, addHash);
|
||||
let squareindex = data.mapsquarex + data.mapsquarez * worldStride;
|
||||
return makeDeptName("mapsquare", squareindex);
|
||||
}
|
||||
|
||||
return { dependencyMap, dependentsMap, maxVersion, cascadeDependencies, makeDeptName, hashDependencies, hasEntry, insertMapChunk, preloadChunkDependencies };
|
||||
}
|
||||
|
||||
//TODO remove
|
||||
globalThis.getdeps = getDependencies;
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import { MaterialData } from '../3d/jmat';
|
||||
import { extractCacheFiles } from '../scripts/extractfiles';
|
||||
import { debugProcTexture } from '../3d/proceduraltexture';
|
||||
import { MapRenderDatabaseBacked } from '../map/backends';
|
||||
import { compareFloorDependencies, compareLocDependencies, mapdiffmesh, mapsquareFloorDependencies, mapsquareLocDependencies } from '../map/chunksummary';
|
||||
|
||||
type LookupMode = "model" | "item" | "npc" | "object" | "material" | "map" | "avatar" | "spotanim" | "scenario" | "scripts";
|
||||
|
||||
@@ -1589,8 +1590,20 @@ function SceneSpotAnim(p: LookupModeProps) {
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
type DiffMesh = {
|
||||
a: ThreejsSceneCache,
|
||||
b: ThreejsSceneCache,
|
||||
info: any,
|
||||
floora: number,
|
||||
floorb: number,
|
||||
visible: boolean,
|
||||
floormesh: ThreeJsSceneElementSource,
|
||||
locsmesh: ThreeJsSceneElementSource,
|
||||
remove: () => void
|
||||
}
|
||||
|
||||
type SceneMapState = {
|
||||
chunkgroups: { rect: MapRect, models: Map<ThreejsSceneCache, RSMapChunk> }[],
|
||||
chunkgroups: { rect: MapRect, models: Map<ThreejsSceneCache, RSMapChunk>, diffs: DiffMesh[] }[],
|
||||
center: { x: number, z: number },
|
||||
toggles: Record<string, boolean>,
|
||||
selectionData: any,
|
||||
@@ -1621,6 +1634,71 @@ export class SceneMapModel extends React.Component<LookupModeProps, SceneMapStat
|
||||
showModal({ title: "Map view" }, <Map2dView chunks={this.state.chunkgroups.map(q => q.models.get(this.props.ctx!.sceneCache)!).filter(q => q)} gridsize={512} mapscenes={true} />);
|
||||
}
|
||||
|
||||
async diffCaches(floora = 3, floorb = 3) {
|
||||
let group = this.state.chunkgroups[0];
|
||||
if (!this.props.ctx || !group) {
|
||||
return;
|
||||
}
|
||||
let arr = [...group.models.entries()];
|
||||
if (arr.length != 1 && arr.length != 2) {
|
||||
console.log("//TODO can currenly only diff with 1 or 2 caches loaded");
|
||||
return;
|
||||
}
|
||||
let [entrya, entryb] = (arr.length == 1 ? [arr[0], arr[0]] : arr);
|
||||
let chunka = await entrya[1].chunkdata;
|
||||
let chunkb = await entryb[1].chunkdata;
|
||||
let cachea = entrya[0];
|
||||
let cacheb = entryb[0];
|
||||
|
||||
let depsa = await cachea.engine.getDependencyGraph();
|
||||
depsa.insertMapChunk(chunka.chunks[0]);
|
||||
let depsb = await cacheb.engine.getDependencyGraph();
|
||||
depsb.insertMapChunk(chunkb.chunks[0]);
|
||||
|
||||
let floordepsa = (chunka.chunks.length == 0 ? [] : mapsquareFloorDependencies(chunka.grid, depsa, chunka.chunks[0]));
|
||||
let locdepsa = mapsquareLocDependencies(chunka.grid, depsa, chunka.modeldata, chunka.rect);
|
||||
let floordepsb = (chunkb.chunks.length == 0 ? [] : mapsquareFloorDependencies(chunkb.grid, depsb, chunkb.chunks[0]));
|
||||
let locdepsb = mapsquareLocDependencies(chunkb.grid, depsb, chunkb.modeldata, chunkb.rect);
|
||||
|
||||
let floordifs = compareFloorDependencies(floordepsa, floordepsb, floora, floorb);
|
||||
let locdifs = compareLocDependencies(locdepsa, locdepsb, floora, floorb);
|
||||
|
||||
let floordifmesh = await mapdiffmesh(globalThis.sceneCache, floordifs, [255, 0, 0]);
|
||||
let locdifmesh = await mapdiffmesh(globalThis.sceneCache, locdifs, [0, 255, 0]);
|
||||
|
||||
let diffgroup: DiffMesh = {
|
||||
a: cachea,
|
||||
b: cacheb,
|
||||
info: {
|
||||
floordepsa,
|
||||
floordepsb,
|
||||
locdepsa,
|
||||
locdepsb
|
||||
},
|
||||
floora,
|
||||
floorb,
|
||||
visible: true,
|
||||
floormesh: {
|
||||
getSceneElements() { return { modelnode: (!diffgroup.visible ? undefined : floordifmesh) } }
|
||||
},
|
||||
locsmesh: {
|
||||
getSceneElements() { return { modelnode: (!diffgroup.visible ? undefined : locdifmesh) } }
|
||||
},
|
||||
remove: () => {
|
||||
group.diffs = group.diffs.filter(q => q != diffgroup);
|
||||
this.props.ctx?.renderer.removeSceneElement(diffgroup.floormesh);
|
||||
this.props.ctx?.renderer.removeSceneElement(diffgroup.locsmesh);
|
||||
this.forceUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
this.props.ctx.renderer.addSceneElement(diffgroup.floormesh);
|
||||
this.props.ctx.renderer.addSceneElement(diffgroup.locsmesh);
|
||||
|
||||
group.diffs.push(diffgroup);
|
||||
this.forceUpdate();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
async meshSelected(e: ThreeJsRendererEvents["select"]) {
|
||||
this.selectCleanup.forEach(q => q());
|
||||
@@ -1714,7 +1792,7 @@ export class SceneMapModel extends React.Component<LookupModeProps, SceneMapStat
|
||||
let newstate: Partial<SceneMapState> = {};
|
||||
newstate.center = center;
|
||||
if (!group) {
|
||||
group = { rect, models: new Map() };
|
||||
group = { rect, models: new Map(), diffs: [] };
|
||||
newstate.chunkgroups = [...prevstate.chunkgroups, group];
|
||||
}
|
||||
group.models.set(sceneCache, chunk);
|
||||
@@ -1896,6 +1974,21 @@ export class SceneMapModel extends React.Component<LookupModeProps, SceneMapStat
|
||||
</div>
|
||||
))}
|
||||
{this.state.versions.length > 1 && <input type="button" className="sub-btn" value="Toggle" onClick={this.toggleCache} />}
|
||||
{this.state.chunkgroups.flatMap((group, groupi) => group.diffs.map((diff, i) => {
|
||||
let metaa = diff.a.engine.getCacheMeta();
|
||||
let metab = diff.a == diff.b ? metaa : diff.b.engine.getCacheMeta();
|
||||
return (
|
||||
<div key={groupi + "-" + i} style={{ clear: "both" }}>
|
||||
<label title={diff.a == diff.b ? metaa.descr : `cache a:${metaa.descr}\n\n${metab.descr}`}>
|
||||
<input type="checkbox" checked={diff.visible} onChange={e => { diff.visible = e.currentTarget.checked; this.props.ctx?.renderer.sceneElementsChanged(); this.forceUpdate(); }} />
|
||||
{diff.a.engine.getBuildNr()}, floor: {diff.floora}
|
||||
-
|
||||
{diff.b.engine.getBuildNr()}, floor: {diff.floorb}
|
||||
</label>
|
||||
<input type="button" className="sub-btn" onClick={diff.remove} style={{ float: "right" }} value="x" />
|
||||
</div>
|
||||
)
|
||||
}))}
|
||||
<JsonDisplay obj={this.state.selectionData} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user