mirror of
https://github.com/skillbert/rsmv.git
synced 2025-12-23 21:47:48 -05:00
rewrite some map hashing logic
This commit is contained in:
@@ -21,6 +21,7 @@ import { CanvasImage } from "../imgutils";
|
|||||||
import { minimapFloorMaterial, minimapWaterMaterial } from "../rs3shaders";
|
import { minimapFloorMaterial, minimapWaterMaterial } from "../rs3shaders";
|
||||||
import { mapsquare_tiles_nxt } from "../../generated/mapsquare_tiles_nxt";
|
import { mapsquare_tiles_nxt } from "../../generated/mapsquare_tiles_nxt";
|
||||||
import { crc32addInt } from "../libs/crc32util";
|
import { crc32addInt } from "../libs/crc32util";
|
||||||
|
import { generateFloorHashBoxes, generateLocationHashBoxes } from "../map/chunksummary";
|
||||||
|
|
||||||
|
|
||||||
export const tiledimensions = 512;
|
export const tiledimensions = 512;
|
||||||
@@ -912,7 +913,7 @@ export class TileGrid implements TileGridSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ParsemapOpts = { padfloor?: boolean, invisibleLayers?: boolean, collision?: boolean, map2d?: boolean, minimap?: boolean, skybox?: boolean, mask?: MapRect[] };
|
export type ParsemapOpts = { padfloor?: boolean, invisibleLayers?: boolean, collision?: boolean, map2d?: boolean, minimap?: boolean, hashboxes?: boolean, skybox?: boolean, mask?: MapRect[] };
|
||||||
|
|
||||||
export async function getMapsquareData(engine: EngineCache, chunkx: number, chunkz: number) {
|
export async function getMapsquareData(engine: EngineCache, chunkx: number, chunkz: number) {
|
||||||
let squareSize = (engine.classicData ? classicChunkSize : rs2ChunkSize);
|
let squareSize = (engine.classicData ? classicChunkSize : rs2ChunkSize);
|
||||||
@@ -1165,7 +1166,6 @@ export type RSMapChunkData = {
|
|||||||
grid: TileGrid,
|
grid: TileGrid,
|
||||||
chunk: ChunkData | null,
|
chunk: ChunkData | null,
|
||||||
chunkSize: number,
|
chunkSize: number,
|
||||||
groups: Set<string>,
|
|
||||||
sky: { skybox: Object3D, fogColor: number[], skyboxModelid: number } | null,
|
sky: { skybox: Object3D, fogColor: number[], skyboxModelid: number } | null,
|
||||||
modeldata: Map<WorldLocation, PlacedMesh[]>,
|
modeldata: Map<WorldLocation, PlacedMesh[]>,
|
||||||
chunkroot: THREE.Group,
|
chunkroot: THREE.Group,
|
||||||
@@ -1211,6 +1211,12 @@ export async function renderMapSquare(cache: ThreejsSceneCache, parsedsquare: Re
|
|||||||
let rawboxes = mapsquareCollisionToThree(grid, chunk, level, true);
|
let rawboxes = mapsquareCollisionToThree(grid, chunk, level, true);
|
||||||
if (rawboxes) { chunkroot.add(rawboxes); }
|
if (rawboxes) { chunkroot.add(rawboxes); }
|
||||||
}
|
}
|
||||||
|
if (opts.hashboxes) {
|
||||||
|
for (let level = 0; level < squareLevels; level++) {
|
||||||
|
chunkroot.add(await generateLocationHashBoxes(cache, locmeshes.byLogical, grid, chunk.mapsquarex, chunk.mapsquarez, level));
|
||||||
|
chunkroot.add(await generateFloorHashBoxes(cache, grid, chunk, level));
|
||||||
|
}
|
||||||
|
}
|
||||||
modeldata = locmeshes.byLogical;
|
modeldata = locmeshes.byLogical;
|
||||||
} else {
|
} else {
|
||||||
modeldata = new Map();
|
modeldata = new Map();
|
||||||
@@ -1218,11 +1224,7 @@ export async function renderMapSquare(cache: ThreejsSceneCache, parsedsquare: Re
|
|||||||
let sky = (chunk && opts?.skybox ? await mapsquareSkybox(cache, chunk) : null);
|
let sky = (chunk && opts?.skybox ? await mapsquareSkybox(cache, chunk) : null);
|
||||||
let chunkSize = (cache.engine.classicData ? classicChunkSize : rs2ChunkSize);
|
let chunkSize = (cache.engine.classicData ? classicChunkSize : rs2ChunkSize);
|
||||||
|
|
||||||
let groups = new Set<string>();
|
|
||||||
chunkroot?.traverse(node => {
|
chunkroot?.traverse(node => {
|
||||||
if (node.userData.modelgroup) {
|
|
||||||
groups.add(node.userData.modelgroup);
|
|
||||||
}
|
|
||||||
if (node instanceof THREE.Mesh) {
|
if (node instanceof THREE.Mesh) {
|
||||||
let parent: THREE.Object3D | null = node;
|
let parent: THREE.Object3D | null = node;
|
||||||
let iswireframe = false;
|
let iswireframe = false;
|
||||||
@@ -1239,7 +1241,7 @@ export async function renderMapSquare(cache: ThreejsSceneCache, parsedsquare: Re
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { chunkx, chunkz, grid, chunk, groups, sky, modeldata, chunkroot, chunkSize, locRenders };
|
return { chunkx, chunkz, grid, chunk, sky, modeldata, chunkroot, chunkSize, locRenders };
|
||||||
}
|
}
|
||||||
|
|
||||||
type SimpleTexturePackerAlloc = { u: number, v: number, usize: number, vsize: number, x: number, y: number, repeatWidth: number, repeatHeight: number, totalpixels: number, img: CanvasImage }
|
type SimpleTexturePackerAlloc = { u: number, v: number, usize: number, vsize: number, x: number, y: number, repeatWidth: number, repeatHeight: number, totalpixels: number, img: CanvasImage }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Box2, BufferAttribute, Group, Matrix4, Vector2, Vector3 } from "three";
|
import { Box2, BufferAttribute, Group, Matrix4, Vector2, Vector3 } from "three";
|
||||||
import { objects } from "../../generated/objects";
|
import { objects } from "../../generated/objects";
|
||||||
import { ChunkData, getTileHeight, MapRect, ModelExtrasLocation, PlacedMesh, PlacedMeshBase, rs2ChunkSize, tiledimensions, TileGrid, transformVertexPositions, WorldLocation } from "../3d/mapsquare";
|
import { ChunkData, getTileHeight, MapRect, ModelExtras, ModelExtrasLocation, PlacedMesh, PlacedMeshBase, PlacedModel, rs2ChunkSize, tiledimensions, TileGrid, tileshapes, transformVertexPositions, WorldLocation } from "../3d/mapsquare";
|
||||||
import { ob3ModelToThree, ThreejsSceneCache } from "../3d/modeltothree";
|
import { ob3ModelToThree, ThreejsSceneCache } from "../3d/modeltothree";
|
||||||
import { ModelBuilder } from "../3d/modelutils";
|
import { ModelBuilder } from "../3d/modelutils";
|
||||||
import { DependencyGraph } from "../scripts/dependencies";
|
import { DependencyGraph } from "../scripts/dependencies";
|
||||||
@@ -9,6 +9,7 @@ import { CacheFileSource } from "../cache";
|
|||||||
import { RenderedMapMeta } from ".";
|
import { RenderedMapMeta } from ".";
|
||||||
import { crc32addInt } from "../libs/crc32util";
|
import { crc32addInt } from "../libs/crc32util";
|
||||||
import type { RSMapChunk } from "../3d/modelnodes";
|
import type { RSMapChunk } from "../3d/modelnodes";
|
||||||
|
import { getOrInsert } from "../utils";
|
||||||
|
|
||||||
export function getLocImageHash(grid: TileGrid, info: WorldLocation) {
|
export function getLocImageHash(grid: TileGrid, info: WorldLocation) {
|
||||||
let loc = info.location;
|
let loc = info.location;
|
||||||
@@ -29,8 +30,7 @@ export function getLocImageHash(grid: TileGrid, info: WorldLocation) {
|
|||||||
}
|
}
|
||||||
let baseheight = getTileHeight(grid, info.x, info.z, info.plane);
|
let baseheight = getTileHeight(grid, info.x, info.z, info.plane);
|
||||||
|
|
||||||
let hash = modelPlacementHash(info, false);
|
let hash = modelPlacementHash(info);
|
||||||
hash = crc32addInt(info.resolvedlocid, hash);
|
|
||||||
for (let height of subgrid) {
|
for (let height of subgrid) {
|
||||||
hash = crc32addInt(height - baseheight, hash);
|
hash = crc32addInt(height - baseheight, hash);
|
||||||
}
|
}
|
||||||
@@ -150,34 +150,39 @@ export function mapsquareFloorDependencies(grid: TileGrid, deps: DependencyGraph
|
|||||||
const groupsize = 2;
|
const groupsize = 2;
|
||||||
for (let x = 0; x < chunk.tilerect.xsize; x += groupsize) {
|
for (let x = 0; x < chunk.tilerect.xsize; x += groupsize) {
|
||||||
for (let z = 0; z < chunk.tilerect.zsize; z += groupsize) {
|
for (let z = 0; z < chunk.tilerect.zsize; z += groupsize) {
|
||||||
let tilehashes = new Array(grid.levels).fill(0);
|
let tilehashes = new Array<number>(grid.levels).fill(0);
|
||||||
let maxy = 0;
|
let maxy = 0;
|
||||||
//can't use Set here since we need determinisitic order
|
//can't use Set here since we need determinisitic order
|
||||||
let overlays: number[] = [];
|
let overlays: number[] = [];
|
||||||
let underlays: number[] = [];
|
let underlays: number[] = [];
|
||||||
for (let dx = 0; dx < groupsize; dx++) {
|
for (let level = 0; level < grid.levels; level++) {
|
||||||
for (let dz = 0; dz < groupsize; dz++) {
|
let minlevel = level;
|
||||||
for (let level = 0; level < grid.levels; level++) {
|
for (let dx = 0; dx < groupsize; dx++) {
|
||||||
|
for (let dz = 0; dz < groupsize; dz++) {
|
||||||
|
let tilehash = 0;
|
||||||
let tile = grid.getTile(chunk.tilerect.x + x + dx, chunk.tilerect.z + z + dz, level);
|
let tile = grid.getTile(chunk.tilerect.x + x + dx, chunk.tilerect.z + z + dz, level);
|
||||||
// if (!tile) { throw new Error("missing tile"); }
|
|
||||||
if (!tile || (!tile.underlayVisible && !tile.overlayVisible)) { continue; }
|
if (!tile || (!tile.underlayVisible && !tile.overlayVisible)) { continue; }
|
||||||
let tilehash = tilehashes[tile.effectiveVisualLevel];
|
minlevel = Math.min(minlevel, tile.effectiveVisualLevel);
|
||||||
|
|
||||||
let rawtile = tile.debug_raw;
|
let rawtile = tile.debug_raw;
|
||||||
//TODO make a nxt branch here
|
//TODO make a nxt branch here
|
||||||
if (!rawtile) { throw new Error("can't calculate chunkhash since rawtile isn't set"); }
|
if (!rawtile) { throw new Error("can't calculate chunkhash since rawtile isn't set"); }
|
||||||
tilehash = crc32addInt(rawtile.flags, tilehash);//TODO get rid of this one
|
|
||||||
tilehash = crc32addInt(rawtile.height ?? -1, tilehash);
|
tilehash = crc32addInt(rawtile.height ?? -1, tilehash);
|
||||||
tilehash = crc32addInt(rawtile.overlay ?? -1, tilehash);
|
tilehash = crc32addInt(rawtile.overlay ?? -1, tilehash);
|
||||||
tilehash = crc32addInt(rawtile.settings ?? -1, tilehash);
|
tilehash = crc32addInt(rawtile.settings ?? -1, tilehash);
|
||||||
tilehash = crc32addInt(rawtile.shape ?? -1, tilehash);
|
tilehash = crc32addInt(rawtile.shape ?? -1, tilehash);
|
||||||
tilehash = crc32addInt(rawtile.underlay ?? -1, tilehash);
|
tilehash = crc32addInt(rawtile.underlay ?? -1, tilehash);
|
||||||
|
|
||||||
|
tilehash = crc32addInt(tile.effectiveVisualLevel, tilehash);
|
||||||
|
|
||||||
if (rawtile.overlay != null && overlays.indexOf(rawtile.overlay) == -1) { overlays.push(rawtile.overlay); }
|
if (rawtile.overlay != null && overlays.indexOf(rawtile.overlay) == -1) { overlays.push(rawtile.overlay); }
|
||||||
if (rawtile.underlay != null && underlays.indexOf(rawtile.underlay) == -1) { underlays.push(rawtile.underlay); }
|
if (rawtile.underlay != null && underlays.indexOf(rawtile.underlay) == -1) { underlays.push(rawtile.underlay); }
|
||||||
|
|
||||||
maxy = Math.max(maxy, tile.y, tile.y01, tile.y10, tile.y11);
|
maxy = Math.max(maxy, tile.y, tile.y01, tile.y10, tile.y11);
|
||||||
tilehashes[tile.effectiveVisualLevel] = tilehash;
|
|
||||||
|
for (let i = tile.effectiveVisualLevel; i < grid.levels; i++) {
|
||||||
|
tilehashes[i] = crc32addInt(tilehash, tilehashes[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,17 +212,10 @@ export function mapsquareLocDependencies(grid: TileGrid, deps: DependencyGraph,
|
|||||||
const v1 = new Vector3();
|
const v1 = new Vector3();
|
||||||
const v2 = new Vector3();
|
const v2 = new Vector3();
|
||||||
|
|
||||||
let locgroups = new Map<number, PlacedMeshBase<ModelExtrasLocation>[][]>();
|
let locgroups = new Map<number, WorldLocation[]>();
|
||||||
for (let models of locs.values()) {
|
for (let loc of locs.keys()) {
|
||||||
let first = models[0];//TODO why only index 0, i forgot
|
let group = getOrInsert(locgroups, loc.locid, () => []);
|
||||||
if (first.extras.modeltype == "location") {
|
group.push(loc);
|
||||||
let group = locgroups.get(first.extras.locationid);
|
|
||||||
if (!group) {
|
|
||||||
group = [];
|
|
||||||
locgroups.set(first.extras.locationid, group);
|
|
||||||
}
|
|
||||||
group.push(models as PlacedMeshBase<ModelExtrasLocation>[]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let outlocgroups: ChunkLocDependencies[] = [];
|
let outlocgroups: ChunkLocDependencies[] = [];
|
||||||
@@ -231,9 +229,10 @@ export function mapsquareLocDependencies(grid: TileGrid, deps: DependencyGraph,
|
|||||||
}
|
}
|
||||||
outlocgroups.push(outgroup);
|
outlocgroups.push(outgroup);
|
||||||
for (let loc of group) {
|
for (let loc of group) {
|
||||||
|
let models = locs.get(loc)!;
|
||||||
v0.set(0, 0, 0);
|
v0.set(0, 0, 0);
|
||||||
v1.set(0, 0, 0);
|
v1.set(0, 0, 0);
|
||||||
for (let mesh of loc) {
|
for (let mesh of models) {
|
||||||
let posattr = mesh.model.attributes.pos;
|
let posattr = mesh.model.attributes.pos;
|
||||||
for (let i = 0; i < posattr.count; i++) {
|
for (let i = 0; i < posattr.count; i++) {
|
||||||
v2.set(posattr.getX(i), posattr.getY(i), posattr.getZ(i));
|
v2.set(posattr.getX(i), posattr.getY(i), posattr.getZ(i));
|
||||||
@@ -252,18 +251,18 @@ export function mapsquareLocDependencies(grid: TileGrid, deps: DependencyGraph,
|
|||||||
boxAttribute.setXYZ(6, v1.x, v1.y, v0.z);
|
boxAttribute.setXYZ(6, v1.x, v1.y, v0.z);
|
||||||
boxAttribute.setXYZ(7, v1.x, v1.y, v1.z);
|
boxAttribute.setXYZ(7, v1.x, v1.y, v1.z);
|
||||||
|
|
||||||
let first = loc[0];
|
let first = models[0];
|
||||||
let trans = transformVertexPositions(boxAttribute, first.morph, grid, first.maxy, chunkx * rs2ChunkSize * tiledimensions, chunkz * rs2ChunkSize * tiledimensions);
|
let trans = transformVertexPositions(boxAttribute, first.morph, grid, first.maxy, chunkx * rs2ChunkSize * tiledimensions, chunkz * rs2ChunkSize * tiledimensions);
|
||||||
let bounds = [...trans.array as Float32Array].map(v => v | 0);
|
let bounds = [...trans.array as Float32Array].map(v => v | 0);
|
||||||
outgroup.instances.push({
|
outgroup.instances.push({
|
||||||
plane: first.extras.locationInstance.plane,
|
plane: loc.plane,
|
||||||
x: first.extras.locationInstance.x,
|
x: loc.x,
|
||||||
z: first.extras.locationInstance.z,
|
z: loc.z,
|
||||||
rotation: first.extras.locationInstance.rotation,
|
rotation: loc.rotation,
|
||||||
type: first.extras.locationInstance.type,
|
type: loc.type,
|
||||||
|
|
||||||
visualLevel: first.extras.locationInstance.visualLevel,
|
visualLevel: loc.visualLevel,
|
||||||
placementhash: modelPlacementHash(first.extras.locationInstance),
|
placementhash: modelPlacementHash(loc),
|
||||||
bounds: bounds
|
bounds: bounds
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -273,26 +272,27 @@ export function mapsquareLocDependencies(grid: TileGrid, deps: DependencyGraph,
|
|||||||
return outlocgroups;
|
return outlocgroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function tileSetVertices(tile: ChunkTileDependencies) {
|
||||||
|
let x0 = tile.x * tiledimensions;
|
||||||
|
let x1 = x0 + tile.xzsize * tiledimensions;
|
||||||
|
let z0 = tile.z * tiledimensions;
|
||||||
|
let z1 = z0 + tile.xzsize * tiledimensions;
|
||||||
|
let y0 = 0;
|
||||||
|
let y1 = tile.maxy;
|
||||||
|
return [
|
||||||
|
x0, y0, z0,
|
||||||
|
x0, y0, z1,
|
||||||
|
x0, y1, z0,
|
||||||
|
x0, y1, z1,
|
||||||
|
x1, y0, z0,
|
||||||
|
x1, y0, z1,
|
||||||
|
x1, y1, z0,
|
||||||
|
x1, y1, z1,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
export function compareFloorDependencies(tilesa: ChunkTileDependencies[], tilesb: ChunkTileDependencies[], levela: number, levelb: number) {
|
export function compareFloorDependencies(tilesa: ChunkTileDependencies[], tilesb: ChunkTileDependencies[], levela: number, levelb: number) {
|
||||||
let vertsets: number[][] = [];
|
let vertsets: number[][] = [];
|
||||||
let addtile = (tile: ChunkTileDependencies) => {
|
|
||||||
let x0 = tile.x * tiledimensions;
|
|
||||||
let x1 = x0 + tile.xzsize * tiledimensions;
|
|
||||||
let z0 = tile.z * tiledimensions;
|
|
||||||
let z1 = z0 + tile.xzsize * tiledimensions;
|
|
||||||
let y0 = 0;
|
|
||||||
let y1 = tile.maxy;
|
|
||||||
vertsets.push([
|
|
||||||
x0, y0, z0,
|
|
||||||
x0, y0, z1,
|
|
||||||
x0, y1, z0,
|
|
||||||
x0, y1, z1,
|
|
||||||
x1, y0, z0,
|
|
||||||
x1, y0, z1,
|
|
||||||
x1, y1, z0,
|
|
||||||
x1, y1, z1,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
for (let i = 0; i < tilesa.length; i++) {
|
for (let i = 0; i < tilesa.length; i++) {
|
||||||
let tilea = tilesa[i];
|
let tilea = tilesa[i];
|
||||||
let tileb = tilesb[i];
|
let tileb = tilesb[i];
|
||||||
@@ -302,18 +302,12 @@ export function compareFloorDependencies(tilesa: ChunkTileDependencies[], tilesb
|
|||||||
mismatch = true;
|
mismatch = true;
|
||||||
} else if (tilea.tilehashes.length <= maxfloor || tileb.tilehashes.length <= maxfloor) {
|
} else if (tilea.tilehashes.length <= maxfloor || tileb.tilehashes.length <= maxfloor) {
|
||||||
mismatch = true;
|
mismatch = true;
|
||||||
} else {
|
} else if (tilea.tilehashes[levela] != tileb.tilehashes[levelb]) {
|
||||||
for (let level = 0; level <= maxfloor; level++) {
|
mismatch = true
|
||||||
let hasha = level <= levela ? tilea.tilehashes[level] : 0;
|
|
||||||
let hashb = level <= levelb ? tileb.tilehashes[level] : 0;
|
|
||||||
if (hasha != hashb) {
|
|
||||||
mismatch = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (mismatch) {
|
if (mismatch) {
|
||||||
addtile(tilea);
|
vertsets.push(tileSetVertices(tilea));
|
||||||
addtile(tileb);
|
vertsets.push(tileSetVertices(tileb));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return vertsets;
|
return vertsets;
|
||||||
@@ -323,8 +317,9 @@ export function compareLocDependencies(chunka: ChunkLocDependencies[], chunkb: C
|
|||||||
let vertsets: number[][] = [];
|
let vertsets: number[][] = [];
|
||||||
let iloca = 0, ilocb = 0;
|
let iloca = 0, ilocb = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
let loca = chunka[iloca];
|
//explicit bounds check because reading past end is really bad for performance apparently
|
||||||
let locb = chunkb[ilocb];
|
let loca = (iloca < chunka.length ? chunka[iloca] : undefined);
|
||||||
|
let locb = (ilocb < chunkb.length ? chunkb[ilocb] : undefined);
|
||||||
|
|
||||||
if (!loca && !locb) { break }
|
if (!loca && !locb) { break }
|
||||||
else if (loca && locb && loca.id == locb.id) {
|
else if (loca && locb && loca.id == locb.id) {
|
||||||
@@ -371,7 +366,7 @@ export function compareLocDependencies(chunka: ChunkLocDependencies[], chunkb: C
|
|||||||
ilocb++;
|
ilocb++;
|
||||||
} else if (!loca || locb && locb.id < loca.id) {
|
} else if (!loca || locb && locb.id < loca.id) {
|
||||||
//locb inserted
|
//locb inserted
|
||||||
vertsets.push(...locb.instances.map(q => q.bounds));
|
vertsets.push(...locb!.instances.map(q => q.bounds));
|
||||||
ilocb++;
|
ilocb++;
|
||||||
} else if (!locb || loca && loca.id < locb.id) {
|
} else if (!locb || loca && loca.id < locb.id) {
|
||||||
//locb inserted
|
//locb inserted
|
||||||
@@ -389,8 +384,8 @@ export async function mapdiffmesh(scene: ThreejsSceneCache, points: number[][],
|
|||||||
verts.slice(c * 3, c * 3 + 3) as any
|
verts.slice(c * 3, c * 3 + 3) as any
|
||||||
);
|
);
|
||||||
let models = new Group();
|
let models = new Group();
|
||||||
models.position.x -= tiledimensions * rs2ChunkSize / 2;
|
models.matrixAutoUpdate = false;
|
||||||
models.position.z -= tiledimensions * rs2ChunkSize / 2;
|
models.updateMatrix();
|
||||||
for (let group of points) {
|
for (let group of points) {
|
||||||
let model = new ModelBuilder();
|
let model = new ModelBuilder();
|
||||||
//double-sided box through each vertex
|
//double-sided box through each vertex
|
||||||
@@ -423,6 +418,54 @@ type KMeansBucket = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export async function generateLocationHashBoxes(scene: ThreejsSceneCache, locs: Map<WorldLocation, PlacedMesh[]>, grid: TileGrid, chunkx: number, chunkz: number, level: number) {
|
||||||
|
let deps = await scene.engine.getDependencyGraph();
|
||||||
|
await deps.preloadChunkDependencies({ area: { x: chunkx, z: chunkz, xsize: 1, zsize: 1 } });
|
||||||
|
let locdeps = mapsquareLocDependencies(grid, deps, locs, chunkx, chunkz);
|
||||||
|
|
||||||
|
let group = new Group();
|
||||||
|
for (let loc of locdeps) {
|
||||||
|
for (let inst of loc.instances) {
|
||||||
|
if (inst.visualLevel != level) { continue; }
|
||||||
|
let totalhash = loc.dependencyhash ^ inst.placementhash;
|
||||||
|
let color = [(totalhash >> 16) & 0xff, (totalhash >> 8) & 0xff, (totalhash >> 0) & 0xff] as [number, number, number];
|
||||||
|
group.add(await mapdiffmesh(scene, [inst.bounds], color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group.userData = {
|
||||||
|
modeltype: "overlay",
|
||||||
|
isclickable: false,
|
||||||
|
modelgroup: "hashbox_objects" + level,
|
||||||
|
level
|
||||||
|
} satisfies ModelExtras;
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function generateFloorHashBoxes(scene: ThreejsSceneCache, grid: TileGrid, chunk: ChunkData, level: number) {
|
||||||
|
let deps = await scene.engine.getDependencyGraph();
|
||||||
|
await deps.preloadChunkDependencies({ area: { x: chunk.mapsquarex, z: chunk.mapsquarez, xsize: 1, zsize: 1 } });
|
||||||
|
let floordeps = mapsquareFloorDependencies(grid, deps, chunk);
|
||||||
|
let group = new Group();
|
||||||
|
for (let dep of floordeps) {
|
||||||
|
let totalhash = 0;
|
||||||
|
totalhash = crc32addInt(dep.dephash, totalhash);
|
||||||
|
totalhash = crc32addInt(dep.tilehashes[level], totalhash);
|
||||||
|
let color = [(totalhash >> 16) & 0xff, (totalhash >> 8) & 0xff, (totalhash >> 0) & 0xff] as [number, number, number];
|
||||||
|
let verts = tileSetVertices(dep);
|
||||||
|
group.add(await mapdiffmesh(scene, [verts], color));
|
||||||
|
}
|
||||||
|
group.userData = {
|
||||||
|
modeltype: "overlay",
|
||||||
|
isclickable: false,
|
||||||
|
modelgroup: "hashbox_floor" + level,
|
||||||
|
level
|
||||||
|
} satisfies ModelExtras
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* this class is wayyy overkill for what is currently used
|
* this class is wayyy overkill for what is currently used
|
||||||
*/
|
*/
|
||||||
@@ -647,64 +690,39 @@ function rendermeans(rects: Box2[], buckets: KMeansBucket[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO remove
|
globalThis.test = async (chunka: RSMapChunk, levela: number, levelb = 0, chunkb = chunka) => {
|
||||||
globalThis.lochash = mapsquareLocDependencies;
|
let depsa = await chunka.cache.engine.getDependencyGraph();
|
||||||
globalThis.floorhash = mapsquareFloorDependencies;
|
let depsb = await chunkb.cache.engine.getDependencyGraph();
|
||||||
globalThis.comparehash = compareLocDependencies;
|
await depsa.preloadChunkDependencies({ area: { x: chunka.chunkx, z: chunka.chunkz, xsize: 1, zsize: 1 } });
|
||||||
globalThis.diffmodel = mapdiffmesh;
|
await depsb.preloadChunkDependencies({ area: { x: chunkb.chunkx, z: chunkb.chunkz, xsize: 1, zsize: 1 } });
|
||||||
globalThis.test = async (low: number, high: number) => {
|
await chunka.chunkdata;
|
||||||
globalThis.deps ??= await globalThis.getdeps(globalThis.engine, { area: { x: 49, z: 49, xsize: 3, zsize: 3 } });
|
await chunkb.chunkdata;
|
||||||
|
if (!chunka.loaded || !chunkb.loaded) { return; }
|
||||||
|
|
||||||
let chunk: RSMapChunk = globalThis.chunk;
|
let locsa = mapsquareLocDependencies(chunka.loaded.grid, depsa, chunka.loaded.modeldata, chunka.chunkx, chunka.chunkz);
|
||||||
if (!chunk.loaded) { return; }
|
let locsb = mapsquareLocDependencies(chunkb.loaded.grid, depsb, chunkb.loaded.modeldata, chunkb.chunkx, chunkb.chunkz);
|
||||||
let locdeps = mapsquareLocDependencies(chunk.loaded.grid, globalThis.deps, chunk.loaded.modeldata, chunk.loaded.chunkx, chunk.loaded.chunkz);
|
|
||||||
let locdifs = compareLocDependencies(locdeps, locdeps, low, high);
|
|
||||||
let locdifmesh = await mapdiffmesh(globalThis.sceneCache, locdifs);
|
|
||||||
globalThis.render.modelnode.add(locdifmesh);
|
|
||||||
|
|
||||||
let floordeps = mapsquareFloorDependencies(globalThis.chunk.loaded.grid, globalThis.deps, globalThis.chunk.loaded.chunks[0]);
|
let cmplocs = compareLocDependencies(locsa, locsb, levela, levelb);
|
||||||
let floordifs = compareFloorDependencies(floordeps, floordeps, low, high);
|
let cmplocsmesh = await mapdiffmesh(chunka.cache, cmplocs);
|
||||||
let floordifmesh = await mapdiffmesh(globalThis.sceneCache, floordifs);
|
chunka.rootnode.children[0].add(cmplocsmesh);
|
||||||
globalThis.render.modelnode.add(floordifmesh);
|
cmplocsmesh.userData = { modeltype: "overlay", isclickable: false, modelgroup: `cmplocs_${levela}_${levelb}`, level: levela } satisfies ModelExtras;
|
||||||
|
|
||||||
let frame = () => {
|
let floora = mapsquareFloorDependencies(chunka.loaded.grid, depsa, chunka.loaded.chunk!);
|
||||||
let floorproj = globalThis.render.camera.projectionMatrix.clone().multiply(globalThis.render.camera.matrixWorldInverse).multiply(floordifmesh.matrixWorld);
|
let floorb = mapsquareFloorDependencies(chunkb.loaded.grid, depsb, chunkb.loaded.chunk!);
|
||||||
let locproj = globalThis.render.camera.projectionMatrix.clone().multiply(globalThis.render.camera.matrixWorldInverse).multiply(locdifmesh.matrixWorld);
|
|
||||||
|
|
||||||
let grid = new ImageDiffGrid();
|
let cmpfloor = compareFloorDependencies(floora, floorb, levela, levelb);
|
||||||
grid.addPolygons(floorproj, floordifs);
|
let cmpfloormesh = await mapdiffmesh(chunka.cache, cmpfloor);
|
||||||
grid.addPolygons(locproj, locdifs);
|
chunka.rootnode.children[0].add(cmpfloormesh);
|
||||||
// let { grid, gridsize, buckets } = calculateDiffArea(locproj, locdifs);
|
cmpfloormesh.userData = { modeltype: "overlay", isclickable: false, modelgroup: `cmpfloor_${levela}_${levelb}`, level: levela } satisfies ModelExtras;
|
||||||
let res = grid.calculateDiffArea(512, 512);
|
|
||||||
//TODO remove
|
|
||||||
let rects: Box2[] = [];
|
|
||||||
for (let i = 0; i < grid.grid.length; i++) {
|
|
||||||
if (grid.grid[i]) {
|
|
||||||
let x = i % grid.gridsize;
|
|
||||||
let y = Math.floor(i / grid.gridsize);
|
|
||||||
rects.push(new Box2(new Vector2(
|
|
||||||
-1 + 2 * x / grid.gridsize,
|
|
||||||
-1 + 2 * y / grid.gridsize
|
|
||||||
), new Vector2(
|
|
||||||
-1 + 2 * (x + 1) / grid.gridsize,
|
|
||||||
-1 + 2 * (y + 1) / grid.gridsize
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rendermeans(rects, res.buckets);
|
chunka.emit("changed", undefined);
|
||||||
requestAnimationFrame(frame);
|
|
||||||
}
|
return chunka;
|
||||||
frame();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function modelPlacementHash(loc: WorldLocation, includeposition = true) {
|
function modelPlacementHash(loc: WorldLocation) {
|
||||||
let hash = 0;
|
let hash = 0;
|
||||||
if (includeposition) {
|
hash = crc32addInt(loc.resolvedlocid, hash);
|
||||||
hash = crc32addInt(loc.x, hash);
|
|
||||||
hash = crc32addInt(loc.z, hash);
|
|
||||||
hash = crc32addInt(loc.plane, hash);
|
|
||||||
}
|
|
||||||
hash = crc32addInt(loc.rotation, hash);
|
hash = crc32addInt(loc.rotation, hash);
|
||||||
hash = crc32addInt(loc.type, hash);
|
hash = crc32addInt(loc.type, hash);
|
||||||
if (loc.placement) {
|
if (loc.placement) {
|
||||||
|
|||||||
@@ -138,6 +138,9 @@ async function mipCanvas(render: MapRender, files: (UniqueMapFile | null)[], for
|
|||||||
if (!f) { return null; }
|
if (!f) { return null; }
|
||||||
let res = await render.getFileResponse(f.name);
|
let res = await render.getFileResponse(f.name);
|
||||||
let mimetype = res.headers.get("content-type");
|
let mimetype = res.headers.get("content-type");
|
||||||
|
let hashheader = res.headers.get("x-amz-meta-mapfile-hash");
|
||||||
|
if (typeof hashheader == "string" && +hashheader != f.hash) { throw new Error("hash mismatch while creating mip file"); }
|
||||||
|
|
||||||
let outx = (i % 2) * subtilesize;
|
let outx = (i % 2) * subtilesize;
|
||||||
let outy = Math.floor(i / 2) * subtilesize;
|
let outy = Math.floor(i / 2) * subtilesize;
|
||||||
if (avgfilter) {
|
if (avgfilter) {
|
||||||
|
|||||||
@@ -641,6 +641,7 @@ function bufferParser(args: unknown[], parent: ChunkParentCallback, typedef: Typ
|
|||||||
let len = lengthtype.read(state);
|
let len = lengthtype.read(state);
|
||||||
let bytelen = len * vectorLength * type.constr.BYTES_PER_ELEMENT;
|
let bytelen = len * vectorLength * type.constr.BYTES_PER_ELEMENT;
|
||||||
let backing = new ArrayBuffer(bytelen);
|
let backing = new ArrayBuffer(bytelen);
|
||||||
|
if (state.scan + bytelen > state.endoffset) { throw new Error("trying to read outside buffer bounds"); }
|
||||||
let bytes = Buffer.from(backing);
|
let bytes = Buffer.from(backing);
|
||||||
bytes.set(state.buffer.subarray(state.scan, state.scan + bytelen));
|
bytes.set(state.buffer.subarray(state.scan, state.scan + bytelen));
|
||||||
state.scan += bytelen;
|
state.scan += bytelen;
|
||||||
|
|||||||
@@ -627,6 +627,7 @@ export const cacheFileJsonModes = constrainedMap<JsonBasedFile>()({
|
|||||||
environments: { parser: parse.environments, lookup: singleMinorIndex(cacheMajors.config, cacheConfigPages.environments) },
|
environments: { parser: parse.environments, lookup: singleMinorIndex(cacheMajors.config, cacheConfigPages.environments) },
|
||||||
animgroupconfigs: { parser: parse.animgroupConfigs, lookup: singleMinorIndex(cacheMajors.config, cacheConfigPages.animgroups) },
|
animgroupconfigs: { parser: parse.animgroupConfigs, lookup: singleMinorIndex(cacheMajors.config, cacheConfigPages.animgroups) },
|
||||||
maplabels: { parser: parse.maplabels, lookup: singleMinorIndex(cacheMajors.config, cacheConfigPages.maplabels) },
|
maplabels: { parser: parse.maplabels, lookup: singleMinorIndex(cacheMajors.config, cacheConfigPages.maplabels) },
|
||||||
|
mapzones: { parser: parse.mapZones, lookup: singleMinorIndex(cacheMajors.worldmap, 0) },
|
||||||
cutscenes: { parser: parse.cutscenes, lookup: noArchiveIndex(cacheMajors.cutscenes) },
|
cutscenes: { parser: parse.cutscenes, lookup: noArchiveIndex(cacheMajors.cutscenes) },
|
||||||
|
|
||||||
particles0: { parser: parse.particles_0, lookup: singleMinorIndex(cacheMajors.particles, 0) },
|
particles0: { parser: parse.particles_0, lookup: singleMinorIndex(cacheMajors.particles, 0) },
|
||||||
|
|||||||
@@ -346,10 +346,10 @@ function spriteCss(spritedata: interfaces["spritedata"] & {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function spritePromise(ctx: UiRenderContext, spriteid: number) {
|
async function spritePromise(ctx: UiRenderContext, spriteid: number) {
|
||||||
let actualid = spriteid & 0xffffff;
|
|
||||||
let flags = spriteid >> 24;
|
|
||||||
let imgcss = "none";
|
let imgcss = "none";
|
||||||
if (actualid != -1) {
|
if (spriteid != -1) {
|
||||||
|
let actualid = spriteid & 0xffffff;
|
||||||
|
let flags = spriteid >> 24;
|
||||||
if (flags != 0) { console.log("sprite flags", flags); }
|
if (flags != 0) { console.log("sprite flags", flags); }
|
||||||
let spritebuf = await ctx.source.getFileById(cacheMajors.sprites, actualid);
|
let spritebuf = await ctx.source.getFileById(cacheMajors.sprites, actualid);
|
||||||
let img = expandSprite(parseSprite(spritebuf)[0]);
|
let img = expandSprite(parseSprite(spritebuf)[0]);
|
||||||
|
|||||||
@@ -23,9 +23,17 @@ import { drawTexture } from "../imgutils";
|
|||||||
import { RsUIViewer } from "./rsuiviewer";
|
import { RsUIViewer } from "./rsuiviewer";
|
||||||
import { ClientScriptViewer } from "./cs2viewer";
|
import { ClientScriptViewer } from "./cs2viewer";
|
||||||
|
|
||||||
//work around typescript being weird when compiling for browser
|
//see if we have access to a valid electron import
|
||||||
const electron = require("electron/renderer") as typeof import("electron/renderer");
|
let electron: typeof import("electron/renderer") | null = (() => {
|
||||||
const hasElectrion = !!electron.ipcRenderer;
|
try {
|
||||||
|
let electron = require("electron/renderer");
|
||||||
|
//some enviroments polyfill an empty mock object, this also catches when electron is imported from a main process and exports only a string
|
||||||
|
if (electron?.ipcRenderer) {
|
||||||
|
return electron;
|
||||||
|
}
|
||||||
|
} catch (e) { }
|
||||||
|
return null;
|
||||||
|
})();
|
||||||
|
|
||||||
export type SavedCacheSource = {
|
export type SavedCacheSource = {
|
||||||
type: string
|
type: string
|
||||||
@@ -57,7 +65,7 @@ export async function downloadBlob(name: string, blob: Blob) {
|
|||||||
|
|
||||||
/**@deprecated requires a service worker and is pretty sketchy, also no actual streaming output file sources atm */
|
/**@deprecated requires a service worker and is pretty sketchy, also no actual streaming output file sources atm */
|
||||||
export async function downloadStream(name: string, stream: ReadableStream) {
|
export async function downloadStream(name: string, stream: ReadableStream) {
|
||||||
if (!hasElectrion) {
|
if (!electron) {
|
||||||
let url = new URL(`download_${Math.random() * 10000 | 0}_${name}`, document.location.href).href;
|
let url = new URL(`download_${Math.random() * 10000 | 0}_${name}`, document.location.href).href;
|
||||||
let sw = await navigator.serviceWorker.ready;
|
let sw = await navigator.serviceWorker.ready;
|
||||||
if (!sw.active) { throw new Error("no service worker"); }
|
if (!sw.active) { throw new Error("no service worker"); }
|
||||||
@@ -208,7 +216,7 @@ export class CacheSelector extends React.Component<{ onOpen: (c: SavedCacheSourc
|
|||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
async clickOpenNative() {
|
async clickOpenNative() {
|
||||||
if (!hasElectrion) { return; }
|
if (!electron) { return; }
|
||||||
let dir: import("electron").OpenDialogReturnValue = await electron.ipcRenderer.invoke("openfolder", path.resolve(process.env.ProgramData!, "jagex/runescape"));
|
let dir: import("electron").OpenDialogReturnValue = await electron.ipcRenderer.invoke("openfolder", path.resolve(process.env.ProgramData!, "jagex/runescape"));
|
||||||
if (!dir.canceled) {
|
if (!dir.canceled) {
|
||||||
this.props.onOpen({ type: "autofs", location: dir.filePaths[0], writable: !!globalThis.writecache });//TODO propper ui for this
|
this.props.onOpen({ type: "autofs", location: dir.filePaths[0], writable: !!globalThis.writecache });//TODO propper ui for this
|
||||||
@@ -278,14 +286,14 @@ export class CacheSelector extends React.Component<{ onOpen: (c: SavedCacheSourc
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{hasElectrion && (
|
{electron && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<h2>Native local RS3 cache</h2>
|
<h2>Native local RS3 cache</h2>
|
||||||
<p>Only works when running in electron</p>
|
<p>Only works when running in electron</p>
|
||||||
<input type="button" className="sub-btn" onClick={this.clickOpenNative} value="Open native cache" />
|
<input type="button" className="sub-btn" onClick={this.clickOpenNative} value="Open native cache" />
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
{hasElectrion && (
|
{electron && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<h2>Jagex Servers</h2>
|
<h2>Jagex Servers</h2>
|
||||||
<p>Download directly from content servers. Only works when running in electron</p>
|
<p>Download directly from content servers. Only works when running in electron</p>
|
||||||
@@ -413,7 +421,7 @@ export async function openSavedCache(source: SavedCacheSource, remember: boolean
|
|||||||
if (source.type == "openrs2") {
|
if (source.type == "openrs2") {
|
||||||
cache = await Openrs2CacheSource.fromId(+source.cachename);
|
cache = await Openrs2CacheSource.fromId(+source.cachename);
|
||||||
}
|
}
|
||||||
if (hasElectrion && source.type == "autofs") {
|
if (electron && source.type == "autofs") {
|
||||||
let fs = new CLIScriptFS(source.location);
|
let fs = new CLIScriptFS(source.location);
|
||||||
cache = await selectFsCache(fs, { writable: source.writable });
|
cache = await selectFsCache(fs, { writable: source.writable });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1631,7 +1631,8 @@ type SceneMapState = {
|
|||||||
center: { x: number, z: number },
|
center: { x: number, z: number },
|
||||||
toggles: Record<string, boolean>,
|
toggles: Record<string, boolean>,
|
||||||
selectionData: any,
|
selectionData: any,
|
||||||
versions: { cache: ThreejsSceneCache, visible: boolean }[]
|
versions: { cache: ThreejsSceneCache, visible: boolean }[],
|
||||||
|
extramodels: boolean
|
||||||
};
|
};
|
||||||
export class SceneMapModel extends React.Component<LookupModeProps, SceneMapState> {
|
export class SceneMapModel extends React.Component<LookupModeProps, SceneMapState> {
|
||||||
selectCleanup: (() => void)[] = [];
|
selectCleanup: (() => void)[] = [];
|
||||||
@@ -1642,7 +1643,8 @@ export class SceneMapModel extends React.Component<LookupModeProps, SceneMapStat
|
|||||||
center: { x: 0, z: 0 },
|
center: { x: 0, z: 0 },
|
||||||
toggles: Object.create(null),
|
toggles: Object.create(null),
|
||||||
selectionData: undefined,
|
selectionData: undefined,
|
||||||
versions: []
|
versions: [],
|
||||||
|
extramodels: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1782,11 +1784,17 @@ export class SceneMapModel extends React.Component<LookupModeProps, SceneMapStat
|
|||||||
const renderer = this.props.ctx?.renderer;
|
const renderer = this.props.ctx?.renderer;
|
||||||
if (!sceneCache || !renderer) { return; }
|
if (!sceneCache || !renderer) { return; }
|
||||||
|
|
||||||
let chunk = RSMapChunk.create(sceneCache, chunkx, chunkz, { skybox: true });
|
let chunk = RSMapChunk.create(sceneCache, chunkx, chunkz, { skybox: true, map2d: this.state.extramodels, hashboxes: this.state.extramodels, minimap: this.state.extramodels });
|
||||||
chunk.on("changed", () => {
|
chunk.on("changed", () => {
|
||||||
let toggles = this.state.toggles;
|
let toggles = this.state.toggles;
|
||||||
let changed = false;
|
let changed = false;
|
||||||
[...chunk.loaded!.groups].sort((a, b) => a.localeCompare(b)).forEach(q => {
|
let groups = new Set<string>();
|
||||||
|
chunk.rootnode.traverse(node => {
|
||||||
|
if (node.userData.modelgroup) {
|
||||||
|
groups.add(node.userData.modelgroup);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
[...groups].sort((a, b) => a.localeCompare(b)).forEach(q => {
|
||||||
if (typeof toggles[q] != "boolean") {
|
if (typeof toggles[q] != "boolean") {
|
||||||
toggles[q] = !!q.match(/^(floor|objects)\d+/);
|
toggles[q] = !!q.match(/^(floor|objects)\d+/);
|
||||||
// toggles[q] = !!q.match(/^mini_(floor|objects)0/);
|
// toggles[q] = !!q.match(/^mini_(floor|objects)0/);
|
||||||
@@ -1952,6 +1960,7 @@ export class SceneMapModel extends React.Component<LookupModeProps, SceneMapStat
|
|||||||
{this.state.chunkgroups.length == 0 && (
|
{this.state.chunkgroups.length == 0 && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<StringInput onChange={this.onSubmit} initialid={initid} />
|
<StringInput onChange={this.onSubmit} initialid={initid} />
|
||||||
|
<label><input type="checkbox" checked={this.state.extramodels} onChange={e => this.setState({ extramodels: e.currentTarget.checked })} />Load extra modes</label>
|
||||||
<p>Input format: x,z[,xsize=1,[zsize=xsize]]</p>
|
<p>Input format: x,z[,xsize=1,[zsize=xsize]]</p>
|
||||||
<p>Coordinates are in so-called mapsquare coordinates, each mapsquare is 64x64 tiles in size. The entire RuneScape map is laid out in one plane and is 100x200 mapsquares in size.</p>
|
<p>Coordinates are in so-called mapsquare coordinates, each mapsquare is 64x64 tiles in size. The entire RuneScape map is laid out in one plane and is 100x200 mapsquares in size.</p>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
@@ -8,8 +8,16 @@ import VR360Viewer from "../libs/vr360viewer";
|
|||||||
import { CLIScriptFS, ScriptFS, ScriptOutput, ScriptState } from "../scriptrunner";
|
import { CLIScriptFS, ScriptFS, ScriptOutput, ScriptState } from "../scriptrunner";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
//work around typescript being weird when compiling for browser
|
//see if we have access to a valid electron import
|
||||||
const electron = require("electron/renderer");
|
let electron: typeof import("electron/renderer") | null = (() => {
|
||||||
|
try {
|
||||||
|
let electron = require("electron/renderer");
|
||||||
|
if (electron?.ipcRenderer) {
|
||||||
|
return electron;
|
||||||
|
}
|
||||||
|
} catch (e) { }
|
||||||
|
return null;
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
export type UIScriptFile = { name: string, kind: "file", data: Buffer | string }
|
export type UIScriptFile = { name: string, kind: "file", data: Buffer | string }
|
||||||
@@ -519,7 +527,7 @@ export function UIScriptFiles(p: { fs?: UIScriptFS | null, ctx: UIContext }) {
|
|||||||
let clicksave = async () => {
|
let clicksave = async () => {
|
||||||
if (!p.fs) { return; }
|
if (!p.fs) { return; }
|
||||||
let subfs: ScriptFS;
|
let subfs: ScriptFS;
|
||||||
if (!!electron.ipcRenderer) {
|
if (electron) {
|
||||||
let dir: Electron.OpenDialogReturnValue = await electron.ipcRenderer.invoke("openfolder", path.resolve(process.env.HOME!, "downloads"));
|
let dir: Electron.OpenDialogReturnValue = await electron.ipcRenderer.invoke("openfolder", path.resolve(process.env.HOME!, "downloads"));
|
||||||
if (dir.canceled || !dir.filePaths[0]) { return; }
|
if (dir.canceled || !dir.filePaths[0]) { return; }
|
||||||
subfs = new CLIScriptFS(dir.filePaths[0]);
|
subfs = new CLIScriptFS(dir.filePaths[0]);
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ module.exports = {
|
|||||||
"sharp": { commonjs: "sharp" },
|
"sharp": { commonjs: "sharp" },
|
||||||
"zlib": { commonjs: "zlib" },
|
"zlib": { commonjs: "zlib" },
|
||||||
"lzma": { commonjs: "lzma" },
|
"lzma": { commonjs: "lzma" },
|
||||||
"cmd-ts": { commonjs: "cmd-ts" },
|
|
||||||
"comment-json": { commonjs: "comment-json" },
|
"comment-json": { commonjs: "comment-json" },
|
||||||
"gl": { commonjs: "gl" },
|
"gl": { commonjs: "gl" },
|
||||||
"canvas": { commonjs: "canvas" }
|
"canvas": { commonjs: "canvas" }
|
||||||
|
|||||||
Reference in New Issue
Block a user