Files
caravels-community-simulation/js/buildings.js
rafaeldpsilva adbbf6bf50 first commit
2025-12-10 12:32:12 +00:00

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;
}
}