205 lines
8.8 KiB
JavaScript
205 lines
8.8 KiB
JavaScript
let buildings = [];
|
|
|
|
// --- Building Data---
|
|
const buildingTypes = {
|
|
house: {
|
|
geometry: new THREE.BoxGeometry(4, 6, 4),
|
|
material: new THREE.MeshLambertMaterial({ color: 0x87CEEB}),
|
|
description: "Residential House", consumption: "Low Energy, Water", generation: "None", iot: "Smart Thermostat, Smart Lights"
|
|
},
|
|
factory: {
|
|
geometry: (() => {
|
|
const shape = new THREE.Shape().moveTo(0, 0).lineTo(0, 6).lineTo(8, 6).lineTo(8, 8).lineTo(10, 8).lineTo(10, 0).lineTo(0, 0);
|
|
return new THREE.ExtrudeGeometry(shape, { depth: 12, bevelEnabled: false });
|
|
})(),
|
|
material: new THREE.MeshLambertMaterial({ color: 0xB22222}),
|
|
description: "Manufacturing Plant", consumption: "High Energy, Industrial Resources", generation: "Goods, Waste Heat", iot: "Process Sensors, Automated Controls"
|
|
},
|
|
office: {
|
|
geometry: new THREE.BoxGeometry(6, 10, 6),
|
|
material: new THREE.MeshLambertMaterial({ color: 0xFFFFFF }),
|
|
description: "Office Building", consumption: "Medium Energy, Data", generation: "Services, Data", iot: "BMS, Occupancy Sensors"
|
|
},
|
|
shop: {
|
|
geometry: new THREE.BoxGeometry(8, 4, 6),
|
|
material: new THREE.MeshLambertMaterial({ color: 0xd8d400 }),
|
|
description: "Retail Shop", consumption: "Medium Energy, Goods", generation: "Retail Sales", iot: "POS System, Smart Shelves"
|
|
}
|
|
};
|
|
|
|
function initBuildings() {
|
|
createBuilding('house', new THREE.Vector3(-9, 0, 24), 'H01', 'data/H01.csv', 'assets/models/building1.glb');
|
|
createBuilding('factory', new THREE.Vector3(48, 0, -49), 'H02', 'data/H02.csv', 'assets/models/factory.glb'); // ExtrudeGeometry origin is at bottom
|
|
createBuilding('office', new THREE.Vector3(-38, 0, -12), 'H03', 'data/H03.csv', 'assets/models/predio1.glb');
|
|
createBuilding('shop', new THREE.Vector3(-5, 0, -5), 'H04', 'data/H04.csv', 'assets/models/store1.glb', Math.PI / 2);
|
|
createBuilding('house', new THREE.Vector3(-5, 0, 40), 'H05', 'data/H05.csv', 'assets/models/building2.glb');
|
|
createBuilding('house', new THREE.Vector3(6, 0, 24), 'H06', 'data/H06.csv', 'assets/models/building1.glb');
|
|
createBuilding('house', new THREE.Vector3(-37, 0, 30), 'H07', 'data/H07.csv', 'assets/models/house1.glb');
|
|
createBuilding('house', new THREE.Vector3(-34, 0, 9), 'H08', 'data/H08.csv', 'assets/models/store4.glb', -Math.PI / 2);
|
|
createBuilding('house', new THREE.Vector3(19, 0, -20), 'H09', 'data/H09.csv', 'assets/models/store2.glb', Math.PI / 2);
|
|
createBuilding('house', new THREE.Vector3(36, 0, 6), 'H10', 'data/H10.csv', 'assets/models/store5.glb');
|
|
createBuilding('house', new THREE.Vector3(34, 0, -19), 'H11', 'data/H11.csv', 'assets/models/store3.glb', Math.PI);
|
|
createBuilding('house', new THREE.Vector3(-21, 0, -18), 'H12', 'data/H12.csv', 'assets/models/predio2.glb');
|
|
createBuilding('house', new THREE.Vector3(-5, 0, -20), 'H13', 'data/H13.csv', 'assets/models/predio3.glb');
|
|
createBuilding('house', new THREE.Vector3(-7, 0, -48), 'H14', 'data/H14.csv', 'assets/models/predio4.glb');
|
|
renderer.domElement.addEventListener('pointerdown', function(event) {
|
|
// Calculate mouse position in normalized device coordinates
|
|
const rect = renderer.domElement.getBoundingClientRect();
|
|
const mouse = new THREE.Vector2(
|
|
((event.clientX - rect.left) / rect.width) * 2 - 1,
|
|
-((event.clientY - rect.top) / rect.height) * 2 + 1
|
|
);
|
|
|
|
raycaster.setFromCamera(mouse, camera);
|
|
const intersects = raycaster.intersectObjects(interactableObjects);
|
|
|
|
if (intersects.length > 0) {
|
|
const mesh = intersects[0].object;
|
|
if (mesh.material && mesh.material.name && mesh.material.name.toLowerCase().includes("vetro")) {
|
|
enableWindowLight(mesh);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function enableWindowLight(windowMesh) {
|
|
// If light is already on, turn it off
|
|
if (windowMesh.userData.lightOn) {
|
|
if (windowMesh.userData.lightOn.parent) {
|
|
windowMesh.userData.lightOn.parent.remove(windowMesh.userData.lightOn);
|
|
}
|
|
windowMesh.userData.lightOn = null;
|
|
// Optionally, reset window glow
|
|
if (windowMesh.material) {
|
|
windowMesh.material.emissive = new THREE.Color(0x000000);
|
|
windowMesh.material.emissiveIntensity = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Create a warm point light just inside the window
|
|
const light = new THREE.PointLight(0xdbb108, 2, 20);
|
|
// Place the light slightly inside the window (adjust as needed)
|
|
const bbox = new THREE.Box3().setFromObject(windowMesh);
|
|
const center = bbox.getCenter(new THREE.Vector3());
|
|
light.position.copy(center);
|
|
// Optionally, offset the light a bit inward
|
|
light.position.y += 1;
|
|
|
|
// Attach the light to the window's parent group (the building)
|
|
if (windowMesh.parent) {
|
|
scene.add(light);
|
|
}
|
|
windowMesh.userData.lightOn = light;
|
|
if (windowMesh.material) {
|
|
windowMesh.material.color.set(0x66aaff)
|
|
windowMesh.material.emissive = new THREE.Color(0xebb104);
|
|
windowMesh.material.emissiveIntensity = 1;
|
|
}
|
|
}
|
|
|
|
// --- Building Creation Function ---
|
|
function createBuilding(type, position, id, csvPath, modelPath, rotation) {
|
|
const buildingData = buildingTypes[type];
|
|
if (!buildingData) { console.warn(`Building type "${type}" not found.`); return null; }
|
|
|
|
if (modelPath) {
|
|
const loader = new THREE.GLTFLoader();
|
|
loader.load(modelPath, function (gltf) {
|
|
const group = gltf.scene;
|
|
|
|
group.position.copy(position);
|
|
if (rotation){
|
|
group.rotation.y = rotation;
|
|
}
|
|
group.userData = { id, type, ...buildingData, csvPath };
|
|
group.traverse(function (child) {
|
|
if (child.isMesh) {
|
|
child.castShadow = true;
|
|
child.receiveShadow = true;
|
|
child.userData.parentGroup = group;
|
|
interactableObjects.push(child);
|
|
// Make windows less transparent
|
|
if (child.material && child.material.name && child.material.name.toLowerCase().includes("vetro")) {
|
|
child.material.thickness = 1;
|
|
child.material.opacity = 1; // or 1 for fully opaque
|
|
child.material.transparent = true; // or false if you want no transparency
|
|
child.material.transmission = 0.01;
|
|
}
|
|
}
|
|
});
|
|
buildings.push(group);
|
|
scene.add(group);
|
|
if (csvPath) loadBuildingCSV(group, csvPath);
|
|
}, undefined, function (error) {
|
|
console.error('Error loading model:', error);
|
|
});
|
|
} else {
|
|
const mesh = new THREE.Mesh(buildingData.geometry, buildingData.material);
|
|
mesh.position.copy(position);
|
|
mesh.castShadow = true;
|
|
mesh.receiveShadow = true;
|
|
mesh.userData = { id, type, ...buildingData, csvPath }; // Store csvPath
|
|
scene.add(mesh);
|
|
buildings.push(mesh);
|
|
interactableObjects.push(mesh);
|
|
if (csvPath) loadBuildingCSV(mesh, csvPath);
|
|
return mesh;
|
|
}
|
|
}
|
|
|
|
function loadBuildingCSV(building, csvPath) {
|
|
Papa.parse(csvPath, {
|
|
download: true,
|
|
header: true,
|
|
dynamicTyping: true,
|
|
complete: function (results) {
|
|
building.userData.energyData = results.data; // Array of rows
|
|
// Optionally, set initial values
|
|
if (results.data.length > 0) {
|
|
updateBuildingEnergy(building, 0); // Set to first row
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Helper to update building's current energy values by row index (e.g., time step)
|
|
function updateBuildingEnergy(building, rowIndex) {
|
|
const data = building.userData.energyData;
|
|
if (!data || !data[rowIndex]) return;
|
|
const row = data[rowIndex];
|
|
building.userData.generation = row.Generation;
|
|
building.userData.consumption = row.Consumption;
|
|
building.userData.devices = {
|
|
Time: row.Time,
|
|
AC1: row.AC1,
|
|
AC2: row.AC2,
|
|
AC3: row.AC3,
|
|
AC4: row.AC4,
|
|
WaterHeater: row['Water heater'],
|
|
TV: row.TV,
|
|
Microwave: row.Microwave,
|
|
Kettle: row.Kettle,
|
|
Lighting: row.Lighting,
|
|
Refrigerator: row.Refrigerator,
|
|
FlexibleLoad: row['Flexible Load'],
|
|
RigidLoad: row['Rigid Load']
|
|
};
|
|
}
|
|
|
|
function updateAllBuildingsForTimeStep(rowIndex) {
|
|
buildings.forEach(obj => {
|
|
if (obj.userData.energyData) {
|
|
updateBuildingEnergy(obj, rowIndex);
|
|
}
|
|
});
|
|
}
|
|
|
|
function updateBuildings() {
|
|
const rowIndex = getCurrentRowIndex();
|
|
|
|
if (rowIndex !== lastUpdateRowIndex) {
|
|
//updateAllBuildingsForTimeStep(rowIndex);
|
|
lastUpdateRowIndex = rowIndex;
|
|
}
|
|
} |