Angular 6+Autodesk Viewer:模型元素复制与热力图实现求助
Angular 6 + Autodesk Viewer: Custom BufferGeometry & Cloned Element Heatmaps
Hey there! I’ve built similar Angular + Autodesk Viewer integrations for IFC models, so let’s break down your two requirements with Angular-specific code examples that fit your setup.
1. Adding Custom BufferGeometry (with Color, Texture, or Heatmap)
First up, you need to make sure the Autodesk Viewer and your IFC model are fully loaded before modifying the scene—this is critical in Angular since we’re dealing with async initialization workflows.
Step-by-Step Implementation
- Wait for Viewer & Model Ready: Hook into the Viewer’s
onDocumentLoadSuccessevent to ensure your IFC model is fully loaded and ready for modifications. - Build Geometry & Material: Choose a Three.js material (Viewer uses Three.js under the hood) based on whether you want solid colors, textures, or a heatmap effect.
- Align & Add to Scene: Use the Viewer’s internal scene to add your custom mesh, and handle coordinate conversion to match the IFC model’s space.
Angular Component Code Example
import { Component, OnInit, OnDestroy } from '@angular/core'; declare var Autodesk: any; @Component({ selector: 'app-viewer', templateUrl: './viewer.component.html', styleUrls: ['./viewer.component.css'] }) export class ViewerComponent implements OnInit, OnDestroy { viewer: Autodesk.Viewing.GuiViewer3D; private customMesh: THREE.Mesh | null = null; ngOnInit(): void { this.initializeViewer(); } ngOnDestroy(): void { // Clean up to avoid memory leaks if (this.customMesh) { this.viewer.impl.scene.remove(this.customMesh); this.customMesh.geometry.dispose(); this.customMesh.material.dispose(); } this.viewer?.finish(); } private initializeViewer(): void { const container = document.getElementById('viewer-container'); this.viewer = new Autodesk.Viewing.GuiViewer3D(container); const options = { env: 'AutodeskProduction', api: 'derivativeV2', getAccessToken: (onTokenReady: any) => { // Replace with your actual token logic const token = 'YOUR_ACCESS_TOKEN'; onTokenReady(token, 3600); } }; Autodesk.Viewing.Initializer(options, () => { this.viewer.start(); this.loadIFCModel('YOUR_IFC_DOCUMENT_URN'); }); } private loadIFCModel(urn: string): void { Autodesk.Viewing.Document.load( `urn:${urn}`, (doc: any) => { const viewables = doc.getRoot().getDefaultGeometry(); this.viewer.loadDocumentNode(doc, viewables).then(() => { // Model is loaded—add custom BufferGeometry now this.addCustomBufferGeometry(); }); }, (error: any) => { console.error('Error loading IFC model:', error); } ); } private addCustomBufferGeometry(): void { const THREE = (window as any).THREE; // 1. Create a sample BufferGeometry (e.g., a flat plane) const geometry = new THREE.PlaneBufferGeometry(10, 10); // Option A: Solid color material // const material = new THREE.MeshPhongMaterial({ color: 0x2196F3, side: THREE.DoubleSide }); // Option B: Texture material // const textureLoader = new THREE.TextureLoader(); // const texture = textureLoader.load('assets/your-texture.png'); // const material = new THREE.MeshPhongMaterial({ map: texture, side: THREE.DoubleSide }); // Option C: Heatmap using ShaderMaterial const heatmapMaterial = new THREE.ShaderMaterial({ uniforms: { heatRange: { value: new THREE.Vector2(0, 10) }, colorStart: { value: new THREE.Color(0x00FF00) }, colorEnd: { value: new THREE.Color(0xFF0000) } }, vertexShader: ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` uniform vec2 heatRange; uniform vec3 colorStart; uniform vec3 colorEnd; varying vec2 vUv; void main() { // Simulate heat value based on UV coordinates float heatValue = mix(heatRange.x, heatRange.y, vUv.y); float normalized = (heatValue - heatRange.x) / (heatRange.y - heatRange.x); vec3 finalColor = mix(colorStart, colorEnd, normalized); gl_FragColor = vec4(finalColor, 1.0); } `, side: THREE.DoubleSide }); // 2. Position mesh to align with IFC model const mesh = new THREE.Mesh(geometry, heatmapMaterial); const modelTransform = this.viewer.model.getModelToViewerTransform(); mesh.applyMatrix4(modelTransform); mesh.position.set(0, 0, 5); // Adjust to your model's space mesh.rotation.x = -Math.PI / 2; // Rotate plane to lie flat // 3. Add to Viewer's scene and trigger render this.viewer.impl.scene.add(mesh); this.customMesh = mesh; this.viewer.impl.invalidate(true); } }
2. Clone Selected Model Element & Apply Heatmap
To clone a clicked IFC element and add a heatmap effect, you’ll need to listen for selection events, extract the element’s geometry, clone it, and apply a custom heatmap material.
Step-by-Step Implementation
- Listen for Selection Events: Bind to the
Autodesk.Viewing.SELECTION_CHANGED_EVENTto detect when a user clicks an IFC element. - Extract & Clone Geometry: Use the Viewer’s fragment list to get the selected element’s geometry, then clone it to avoid modifying the original model.
- Add Heatmap & Offset: Apply a semi-transparent heatmap material and offset the cloned mesh slightly to distinguish it from the original.
Angular Component Code Example (Add to the same component)
private clonedMesh: THREE.Mesh | null = null; private initializeViewer(): void { // ... existing initialization code ... Autodesk.Viewing.Initializer(options, () => { this.viewer.start(); this.loadIFCModel('YOUR_IFC_DOCUMENT_URN'); // Add selection listener after Viewer starts this.viewer.addEventListener(Autodesk.Viewing.SELECTION_CHANGED_EVENT, this.onSelectionChanged.bind(this)); }); } private onSelectionChanged(event: any): void { const selectedDbIds = event.dbIdArray; // Clean up previous cloned mesh if nothing is selected if (selectedDbIds.length === 0) { if (this.clonedMesh) { this.viewer.impl.scene.remove(this.clonedMesh); this.clonedMesh.geometry.dispose(); this.clonedMesh.material.dispose(); this.clonedMesh = null; } return; } const dbId = selectedDbIds[0]; const model = this.viewer.model; const fragmentList = model.getFragmentList(); // Get fragment ID for the selected element model.getFragmentList().getFragments(dbId, (fragIds: number[]) => { const fragId = fragIds[0]; const originalGeometry = fragmentList.getGeometry(fragId); const clonedGeometry = originalGeometry.clone(); // Clone to avoid affecting original const THREE = (window as any).THREE; // Create semi-transparent heatmap material const heatmapMaterial = new THREE.ShaderMaterial({ uniforms: { maxDistance: { value: 5 }, hotColor: { value: new THREE.Color(0xFF4444) }, coolColor: { value: new THREE.Color(0x4444FF) } }, vertexShader: ` varying vec3 vWorldPos; void main() { vWorldPos = (modelMatrix * vec4(position, 1.0)).xyz; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` uniform float maxDistance; uniform vec3 hotColor; uniform vec3 coolColor; varying vec3 vWorldPos; void main() { // Heat based on distance from mesh center vec3 center = vec3(0,0,0); // Adjust to element's center if needed float distance = length(vWorldPos - center); float normalized = clamp(distance / maxDistance, 0.0, 1.0); vec3 finalColor = mix(coolColor, hotColor, normalized); gl_FragColor = vec4(finalColor, 0.7); // Semi-transparent } `, transparent: true }); // Create cloned mesh and apply original transform this.clonedMesh = new THREE.Mesh(clonedGeometry, heatmapMaterial); const fragTransform = new THREE.Matrix4(); fragmentList.getWorldMatrix(fragId, fragTransform); this.clonedMesh.applyMatrix4(fragTransform); // Offset slightly to avoid overlapping with original this.clonedMesh.position.add(new THREE.Vector3(0.3, 0.3, 0.3)); // Add to scene and render this.viewer.impl.scene.add(this.clonedMesh); this.viewer.impl.invalidate(true); }); } // Update ngOnDestroy to remove selection listener ngOnDestroy(): void { this.viewer?.removeEventListener(Autodesk.Viewing.SELECTION_CHANGED_EVENT, this.onSelectionChanged.bind(this)); // ... existing cleanup code ... }
Angular-Specific Tips
- Three.js Access: The Viewer includes Three.js, so no need to install it separately—access it via
(window as any).THREE. - Async Safety: Always wait for the Viewer and model to fully load before modifying the scene (Angular’s async lifecycle hooks help here).
- Memory Management: Dispose geometries/materials and remove event listeners in
ngOnDestroyto prevent memory leaks in your Angular app. - Coordinate Alignment: Use
model.getModelToViewerTransform()or fragment world matrices to ensure custom meshes match the IFC model’s coordinate space.
内容的提问来源于stack exchange,提问作者GiEGi
相关产品推荐
相关产品推荐

