You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 onDocumentLoadSuccess event 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_EVENT to 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 ngOnDestroy to 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:28:48