Added signs attaching
+Added an attachSigns function that will attach signs near signals to these signals so in the future their icons can be rendered without overlapping +Added Vector3.distanceSqExcludeY, .toLocal and .negate *Fixed SceneryObject not returning valid render bounds
This commit is contained in:
parent
05bacafb67
commit
2d655676c9
98
src/model/attach-signs.js
Normal file
98
src/model/attach-signs.js
Normal file
@ -0,0 +1,98 @@
|
||||
import AngleHelper from "../helpers/angleHelper";
|
||||
import Constants from "../helpers/constants";
|
||||
import SceneryParserLog from "./scenery-parser-log";
|
||||
import Vector3 from "./vector3";
|
||||
|
||||
function getGridSize(scenery) {
|
||||
const sceneryBounds = scenery.getBounds();
|
||||
const gridSizeX = Math.floor((sceneryBounds.maxX - sceneryBounds.minX) / Constants.parser.attachSignsGridSize) + 1;
|
||||
const gridSizeZ = Math.floor((sceneryBounds.maxZ - sceneryBounds.minZ) / Constants.parser.attachSignsGridSize) + 1;
|
||||
|
||||
return { x: gridSizeX, z: gridSizeZ };
|
||||
}
|
||||
|
||||
function getGridCellPositions(pos, scenery, gridSize) {
|
||||
const sceneryBounds = scenery.getBounds();
|
||||
const gx = Math.floor((pos.x - sceneryBounds.minX) / Constants.parser.attachSignsGridSize);
|
||||
const gz = Math.floor((pos.z - sceneryBounds.minZ) / Constants.parser.attachSignsGridSize);
|
||||
|
||||
if(gx < 0 || gx >= gridSize.x || gz < 0 || gz >= gridSize.z) {
|
||||
SceneryParserLog.warn('attachSignsOutOfBounds', `TrackObject at position (${pos.x}, ${pos.z}) is out of bounds of the scenery grid. Grid size: ${gridSize.x}x${gridSize.z}, position: (${gx}, ${gz})`);
|
||||
return [];
|
||||
}
|
||||
|
||||
const positions = [{ x: gx, z: gz }];
|
||||
|
||||
const inCellX = pos.x - (sceneryBounds.minX + gx * Constants.parser.attachSignsGridSize);
|
||||
const inCellZ = pos.z - (sceneryBounds.minZ + gz * Constants.parser.attachSignsGridSize);
|
||||
|
||||
const isLeftEdge = inCellX <= Constants.parser.attachSignsMaxDistanceZ && gx > 0;
|
||||
const isRightEdge = inCellX >= Constants.parser.attachSignsGridSize - Constants.parser.attachSignsMaxDistanceZ && gx < gridSize.x - 1;
|
||||
const isTopEdge = inCellZ <= Constants.parser.attachSignsMaxDistanceZ && gz > 0;
|
||||
const isBottomEdge = inCellZ >= Constants.parser.attachSignsGridSize - Constants.parser.attachSignsMaxDistanceZ && gz < gridSize.z - 1;
|
||||
|
||||
if(isLeftEdge) positions.push({ x: gx - 1, z: gz });
|
||||
if(isRightEdge) positions.push({ x: gx + 1, z: gz });
|
||||
if(isTopEdge) positions.push({ x: gx, z: gz - 1 });
|
||||
if(isBottomEdge) positions.push({ x: gx, z: gz + 1 });
|
||||
if(isLeftEdge && isTopEdge) positions.push({ x: gx - 1, z: gz - 1 });
|
||||
if(isLeftEdge && isBottomEdge) positions.push({ x: gx - 1, z: gz + 1 });
|
||||
if(isRightEdge && isTopEdge) positions.push({ x: gx + 1, z: gz - 1 });
|
||||
if(isRightEdge && isBottomEdge) positions.push({ x: gx + 1, z: gz + 1 });
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
export function attachSigns(scenery) {
|
||||
const maxDistSq = Constants.parser.attachSignsMaxDistanceZ ** 2 + Constants.parser.attachSignsMaxDistanceX ** 2;
|
||||
const trackObjectsArr = Object.values(scenery.objects['track-objects'] || {});
|
||||
const gridSize = getGridSize(scenery);
|
||||
const posGrid = new Array(gridSize.x)
|
||||
.fill(new Array(gridSize.z)
|
||||
.fill([]));
|
||||
|
||||
for(const trackObject of trackObjectsArr) {
|
||||
if(trackObject.type !== 'Signal') continue;
|
||||
|
||||
const positions = getGridCellPositions(trackObject.pos, scenery, gridSize);
|
||||
for(const pos of positions) {
|
||||
posGrid[pos.x][pos.z].push(trackObject);
|
||||
}
|
||||
}
|
||||
|
||||
for(const trackObject of trackObjectsArr) {
|
||||
if(trackObject.type !== 'Sign') continue;
|
||||
|
||||
const positions = getGridCellPositions(trackObject.pos, scenery, gridSize);
|
||||
const signals = positions.flatMap(pos => posGrid[pos.x][pos.z]);
|
||||
|
||||
let closestSignal = null;
|
||||
let closestDistanceSq = Infinity;
|
||||
|
||||
for(const signal of signals) {
|
||||
if(Constants.parser.attachSignsNeedsSameTrack && signal.track_id !== trackObject.track_id) continue; // skip signals on different tracks
|
||||
|
||||
const distanceSq = signal.pos.distanceSqExcludeY(trackObject.pos);
|
||||
if(distanceSq > maxDistSq) continue; // skip too far signals
|
||||
|
||||
const signalAngleRad = AngleHelper.degToRad(signal.rot.y);
|
||||
const localPos = trackObject.pos.toLocal(signal.pos, new Vector3(0, signalAngleRad, 0));
|
||||
if(Math.abs(localPos.z) > Constants.parser.attachSignsMaxDistanceZ) continue; // skip signals that are too far in front or behind
|
||||
if(Math.abs(localPos.x) > Constants.parser.attachSignsMaxDistanceX) continue; // skip signals that are too far to the side
|
||||
|
||||
if(distanceSq < closestDistanceSq) {
|
||||
closestDistanceSq = distanceSq;
|
||||
closestSignal = signal;
|
||||
}
|
||||
}
|
||||
|
||||
if(!closestSignal) continue;
|
||||
|
||||
if(Constants.parser.logAttachedSigns) {
|
||||
console.log(`Attaching sign ${trackObject.name} (${trackObject.id}) to signal ${closestSignal.name} (${closestSignal.id}) at distance ${Math.sqrt(closestDistanceSq).toFixed(2)} m`);
|
||||
}
|
||||
|
||||
trackObject.attached_to = closestSignal;
|
||||
closestSignal.attached_signs.push(trackObject);
|
||||
}
|
||||
}
|
||||
@ -21,6 +21,6 @@ export default class SceneryObject {
|
||||
}
|
||||
|
||||
getRenderBounds() {
|
||||
return this.pos;
|
||||
return [this.pos];
|
||||
}
|
||||
}
|
||||
@ -18,6 +18,7 @@ import ElectrificationResolver from './electrification-resolver';
|
||||
import NEVP from './track-objects/nevp';
|
||||
import Derailer from './track-objects/derailer';
|
||||
import SpawnPoint from './track-objects/spawn-point';
|
||||
import { attachSigns } from './attach-signs';
|
||||
|
||||
/*
|
||||
TODO: add support for WorldRotation and WorldTranslation
|
||||
@ -45,6 +46,7 @@ export default class SceneryParser {
|
||||
|
||||
if(Constants.parser.resolveElectrification) ElectrificationResolver.resolveScenery(scenery);
|
||||
if(Constants.parser.runTracksConnectionTest) tracksConnectionTest(scenery);
|
||||
if(Constants.parser.attachSigns) attachSigns(scenery);
|
||||
if(Constants.parser.logSceneryAfterFinished) console.log(scenery);
|
||||
|
||||
return scenery;
|
||||
|
||||
@ -7,6 +7,7 @@ export default class Sign extends TrackObject {
|
||||
type = "Sign";
|
||||
data;
|
||||
def;
|
||||
attached_to = null;
|
||||
|
||||
constructor(id, prefab_name, pos, rot, track_id, name, data) {
|
||||
super(id, prefab_name, pos, rot, track_id, name);
|
||||
|
||||
@ -11,6 +11,7 @@ export default class Signal extends TrackObject {
|
||||
signal_elements;
|
||||
type = "Signal";
|
||||
applied = false;
|
||||
attached_signs = [];
|
||||
|
||||
constructor(id, prefab_name, pos, rot, track_id, name, is_spawn, spawn_info, signal_name, signal_elements) {
|
||||
super(id, prefab_name, pos, rot, track_id, name);
|
||||
|
||||
@ -114,6 +114,12 @@ export default class Vector3 {
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
|
||||
distanceSqExcludeY(other) {
|
||||
const dx = this.x - other.x;
|
||||
const dz = this.z - other.z;
|
||||
return dx * dx + dz * dz;
|
||||
}
|
||||
|
||||
distance(other) {
|
||||
return Math.sqrt(this.distanceSq(other));
|
||||
}
|
||||
@ -122,6 +128,15 @@ export default class Vector3 {
|
||||
return Math.abs(this.x - other.x) + Math.abs(this.y - other.y) + Math.abs(this.z - other.z);
|
||||
}
|
||||
|
||||
toLocal(parentPos, parentRot = Vector3.zero()) {
|
||||
const localPos = this.sub(parentPos);
|
||||
return localPos.rotate(parentRot.negate());
|
||||
}
|
||||
|
||||
negate() {
|
||||
return new Vector3(-this.x, -this.y, -this.z);
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new Vector3(this.x, this.y, this.z);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user