Building 3D Infrastructure Monitoring Systems with Three.js

System Architecture

This system implements real-time 3D visualization for bridge and tunnel infrastructure monitoring. The architecture follows a layered approach where field devices transmit data through gateways to a central platform, which then commmunicates with the 3D frontend.

The data flow uses REST APIs for standard queries and WebSocket connections for real-time alerts and high-frequency data like stress measurements.

Performance Optimization Strategies

Dynamic Model Loading

For scenes requiring extensive model populations, implement threshold-based loading and unloading. When the camera approaches a monitored section, models within that zone load on-demand. Once the camera moves beyond the unloading threshold, these models are released from memory. This approach maintains smooth performance even with thousands of device models.

Cloning for Reusable Models

When deploying multiple instances of identical device types, use the clone() method rather than loading new assets. This significantly reduces memory consumption and improves load times.

Model File Optimization

During the modeling phase, consolidate vertices and faces where geometrically possible. Merge materials across similar models. After export, apply file compression to reduce asset sizes.

Device Monitoring Implementation

Supproted Device Types

The system supports numerous infrastructure device categories:

const DeviceRegistry = {
    WIND_SOCK: "windIndicator",
    WEATHER_STATION: "weatherMeter",
    FIXED_CAMERA: "fixedCamera",
    PTZ_CAMERA: "remoteCamera",
    SPEED_CAMERA: "enforcementCamera",
    VEHICLE_DETECTOR: "microwaveDetector",
    GANTRIES_SIGN: "gantrySign",
    CANTILEVER_SIGN: "cantileverSign",
    ZONE_CONTROLLER: "zoneController",
    VARIABLE_SIGNAL: "trafficSignal",
    LANE_INDICATOR: "laneSignal",
    VENTILATION_UNIT: "ventFan",
    BROADCAST_SYSTEM: "ipSpeaker",
    EMERGENCY_PHONE: "emergencyStation",
    FIRE_ALARM: "smokeDetector",
    VARIABLE_MESSAGE: "changeableSign",
    SPEED_LIMIT_SIGN: "speedLimitSign",
    AIR_QUALITY_SENSOR: "covisMonitor",
    ILLUMINANCE_SENSOR: "lightMeter",
    INCIDENT_DETECTOR: "trafficAnalyzer"
};

Device Instance Configuration

Each device instance requires positioning, scaling, and rotation parameters:

{
    visible: true,
    deviceId: "unit_001",
    category: "VENTILATION_UNIT",
    identifier: "fan_01",
    coordinates: { x: -950, y: 55, z: -70 },
    scale: { x: 0.05, y: 0.05, z: 0.05 },
    rotation: { x: 0, y: Math.PI/2, z: 0 }
}

Adding or Updating Devices Dynamically

DeviceManager.prototype.addOrUpdateDevice = function(category, identifier, coordinates, scale, rotation, visible, deviceId, callback) {
    const existing = this.deviceConfigs.find(cfg => cfg.category === category && cfg.identifier === identifier);
    
    if (existing) {
        Object.assign(existing, { coordinates, scale, rotation, visible, deviceId });
    }
    
    const sceneObject = SceneUtils.locate(`dev_${category}_${identifier}`);
    
    if (sceneObject) {
        if (coordinates) {
            sceneObject.position.set(coordinates.x, coordinates.y, coordinates.z);
        }
        if (scale) {
            sceneObject.scale.set(scale.x, scale.y, scale.z);
        }
        if (rotation) {
            sceneObject.rotation.set(rotation.x, rotation.y, rotation.z);
        }
        sceneObject.visible = visible;
    } else {
        this.deviceConfigs.push({
            visible,
            deviceId,
            category,
            identifier,
            coordinates,
            scale,
            rotation
        });
        
        const modelDef = this.buildModelDefinition(category, identifier, coordinates, scale, rotation, visible);
        SceneUtils.load([modelDef], { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 0 }, true, true, function() {
            if (callback) callback();
        });
    }
};

Camera Navigation to Devices

DeviceManager.prototype.navigateToDevice = function(category, identifier, callback) {
    const target = SceneUtils.locate(`dev_${category}_${identifier}`);
    if (target) {
        animateCameraTo(target, function() {
            if (callback) callback();
        });
    }
    return target;
};

Device State Management

Signal Light Control

SignalController.prototype.setSignalState = function(deviceType, deviceName, state) {
    const target = SceneUtils.locate(`dev_${deviceType}_${deviceName}`);
    if (!target) return;
    
    switch (deviceType) {
        case "TRAFFIC_SIGNAL":
            this.updateFourWaySignal(target, state);
            break;
        case "DUAL_SIGNAL":
            this.updateDualSignal(target, state);
            break;
        case "TRIPLE_SIGNAL":
            this.updateTripleSignal(target, state);
            break;
        case "SINGLE_SIGNAL":
            this.updateSingleSignal(target, state);
            break;
    }
};

SignalController.prototype.updateSingleSignal = function(obj, state) {
    const imageMap = {
        1: "redlight.png",
        2: "yellowlight.png",
        3: "golight.png",
        4: "nolight.png",
        5: "leftArrow.png",
        6: "rightArrow.png"
    };
    
    const stateNum = parseInt(state);
    if (imageMap[stateNum]) {
        MaterialUtils.applyTexture(obj, 0, imageMap[stateNum]);
    }
};

SignalController.prototype.updateDualSignal = function(obj, state) {
    const states = String(state).split('');
    const mappings = [
        { child: obj.children[4], index: 1 },
        { child: obj.children[3], index: 1 }
    ];
    
    mappings.forEach((mapping, idx) => {
        const stateVal = states[idx];
        this.applySignalState(mapping.child, stateVal, mapping.index);
    });
};

Lane Indicator Control

LaneIndicatorController.prototype.setLaneState = function(obj, state) {
    const stateMap = {
        1: { front: "go.png", back: "stop.png" },
        2: { front: "stop.png", back: "go.png" },
        3: { front: "stop.png", back: "stop.png" }
    };
    
    const config = stateMap[parseInt(state)];
    if (config) {
        MaterialUtils.applyTexture(obj, 0, config.front);
        MaterialUtils.applyTexture(obj, 1, config.back);
    }
};

Variable Message Signs

MessageSignController.prototype.setSignContent = function(obj, content) {
    if (content && content.length > 0) {
        const numericContent = parseInt(content);
        if (numericContent > 100) {
            obj.children[0].refreshDisplay(numericContent);
            obj.children[0].position.z = -5;
            obj.children[0].matrixAutoUpdate = true;
        } else {
            obj.children[0].refreshDisplay(numericContent);
            obj.children[0].position.z = -30;
            obj.children[0].matrixAutoUpdate = true;
        }
    } else {
        obj.children[0].refreshDisplay("");
    }
};

Ventilation Unit Animation

FanController.prototype.setFanState = function(obj, state) {
    if (obj.animationTimer) {
        clearInterval(obj.animationTimer);
    }
    
    switch (parseInt(state)) {
        case 1: // Stopped
            obj.children[0].rotation.z = 0;
            obj.children[1].rotation.z = 0;
            obj.children[0].matrixAutoUpdate = false;
            obj.children[1].matrixAutoUpdate = false;
            break;
        case 2: // Forward rotation
            obj.children[0].matrixAutoUpdate = true;
            obj.children[1].matrixAutoUpdate = true;
            obj.animationTimer = setInterval(() => {
                obj.children[0].rotation.z += 0.5;
                obj.children[1].rotation.z += 0.5;
            }, 50);
            break;
        case 3: // Reverse rotation
            obj.children[0].matrixAutoUpdate = true;
            obj.children[1].matrixAutoUpdate = true;
            obj.animationTimer = setInterval(() => {
                obj.children[0].rotation.z -= 0.5;
                obj.children[1].rotation.z -= 0.5;
            }, 50);
            break;
    }
};

Wind Indicator Direction

WindIndicatorController.prototype.setWindDirection = function(obj, state) {
    const parts = state.split("_");
    const angle = parseFloat(parts[1]);
    obj.rotation.y = angle;
};

Lighting Control

LightingController.prototype.setLightState = function(obj, brightness) {
    const opacityMap = {
        1: 0.6,  // Full brightness
        2: 0.3,  // Normal
        3: 0.1   // Dimmed
    };
    
    if (opacityMap[brightness]) {
        obj.children[2].visible = true;
        obj.children[2].material.opacity = opacityMap[brightness];
    }
};

Audio Device Control

AudioController.prototype.setAudioState = function(obj, state) {
    const isActive = parseInt(state) === 1;
    
    switch (obj.deviceType) {
        case "ipSpeaker":
            obj.children[0].visible = isActive;
            break;
        case "emergencyStation":
            obj.children[1].visible = isActive;
            break;
    }
};

Device Status Visualization

StatusOverlayManager.prototype.displayDeviceStatus = function(category, identifier, statusCode) {
    const target = SceneUtils.locate(`dev_${category}_${identifier}`);
    if (!target) return;
    
    const bounds = new THREE.Box3().setFromObject(target);
    const labelY = bounds && bounds.max ? bounds.max.y + 18 : target.position.y + 30;
    const labelId = `dev_${category}_${identifier}_statusMark`;
    
    const existing = SceneUtils.locate(labelId);
    if (existing) {
        SceneUtils.remove(labelId);
    }
    
    if (statusCode === 1 || statusCode === 2) {
        const iconConfig = {
            name: labelId,
            type: "sprite",
            dimensions: { x: 30, y: 30 },
            position: { x: target.position.x, y: labelY, z: target.position.z },
            image: statusCode === 1 ? "disabled_icon.png" : "alert_icon.png"
        };
        
        const marker = SceneBuilder.createSprite(iconConfig);
        marker.material.depthTest = false;
        marker.visible = target.visible;
        SceneBuilder.addToScene(marker);
    }
    
    const alertColors = {
        1: 0x333333,  // Disabled - gray
        2: 0xff0000,  // Fault - red
        3: 0x000000   // Normal - stop flashing
    };
    
    if (alertColors[statusCode]) {
        const duration = statusCode === 3 ? -1 : 0;
        AnimationUtils.pulseObject([target], `flash_${identifier}_`, alertColors[statusCode], duration, 200, 0);
    }
};

Interactive Callbacks

Single Click Handler

function onDeviceClick(modelObject, deviceInfo) {
    const popupContent = `<div class="device-details">Device: ${deviceInfo.deviceId}</div>`;
    
    UI.showTooltip(popupContent, '#tooltipAnchor', {
        closeButton: true,
        overlayOpacity: 0.1,
        dimensions: { width: "300px", height: "300px" },
        colorScheme: "highlight"
    });
}

Double Click Hendler

function onDeviceDoubleClick(modelObject, deviceInfo) {
    const popupContent = `<div class="device-details">Detailed view for ${modelObject.name}</div>`;
    
    UI.openModal({
        title: `${modelObject.name} Details [${deviceInfo.deviceId}]`,
        dimensions: ['500px', '500px'],
        content: popupContent,
        animation: 'slide'
    });
}

Object Selection

DeviceManager.prototype.handleObjectSelection = function(_obj, _face) {
    if (!_obj.visible) return;
    
    const deviceInfo = resolveDeviceInfo(_obj.name);
    console.log(deviceInfo);
    onDeviceDoubleClick(_obj, deviceInfo);
};

Unified Bridge-Tunnel Visualization

The integrated view combines multiple infrastructure elements within a single scene. For wide-area visualizations, use simplified main models to represent primary structures while maintaining overview monitoring capabilities.

Users can drill down into individual structures by double-clicking, transitioning to detailed models that showcase equipment components and monitoring capabilities specific to each section.

Tags: Three.js WebGL 3D Visualization Infrastructure Monitoring Bridge

Posted on Fri, 08 May 2026 06:57:08 +0000 by alarik149