Autodesk forge 无法在Autodesk Forge Viewer中显示文本精灵

Autodesk forge 无法在Autodesk Forge Viewer中显示文本精灵,autodesk-forge,autodesk-viewer,Autodesk Forge,Autodesk Viewer,我无法在Autodesk Forge Viewer中显示文本精灵 我创建了一个示例项目,在加载的模型下显示一个罗盘玫瑰。虽然罗盘的度数显示正常,但不显示基数(N、S、E、W)的文本 下面的TextSprite.ts文件已从转换为Typescript。我注释掉了THREE.LinearFilter的用法,因为它似乎不在Forge viewer中 // Converted to Typescript from https://github.com/vasturiano/three-spritete

我无法在Autodesk Forge Viewer中显示文本精灵

我创建了一个示例项目,在加载的模型下显示一个罗盘玫瑰。虽然罗盘的度数显示正常,但不显示基数(N、S、E、W)的文本

下面的TextSprite.ts文件已从转换为Typescript。我注释掉了THREE.LinearFilter的用法,因为它似乎不在Forge viewer中

// Converted to Typescript from https://github.com/vasturiano/three-spritetext.
//
// Commented out usage to THREE.LinearFilter as that doesn't seem to be present in the
// Forge viewer.

export default class TextSprite extends THREE.Sprite {
  _text: string;
  _textHeight: number;
  _color: string;
  _backgroundColor: string;
  _padding: number;
  _borderWidth: number;
  _borderColor: string;
  _strokeWidth: number;
  _strokeColor: string;
  _fontFace: string;
  _fontSize: number;
  _fontWeight: string;
  _canvas: HTMLCanvasElement;
  _texture: THREE.Texture;

  constructor(text = '', textHeight = 10, color = 'rgba(255, 255, 255, 1)') {
    super(new THREE.SpriteMaterial({ map: new THREE.Texture() }));

    this._text = `${text}`;
    this._textHeight = textHeight;
    this._color = color;
    this._backgroundColor = ''; // no background color

    this._padding = 0;
    this._borderWidth = 0;
    this._borderColor = 'blue';

    this._strokeWidth = 0;
    this._strokeColor = 'green';

    this._fontFace = 'Arial';
    this._fontSize = 90; // defines text resolution
    this._fontWeight = 'normal';

    this._canvas = document.createElement('canvas');

    if (this.material instanceof THREE.SpriteMaterial) {
      this._texture = this.material.map;
    }
    // this._texture.minFilter = three.LinearFilter;

    this._genCanvas();
  }

  get text() { return this._text; }
  set text(text) { this._text = text; this._genCanvas(); }
  get textHeight() { return this._textHeight; }
  set textHeight(textHeight) { this._textHeight = textHeight; this._genCanvas(); }
  get color() { return this._color; }
  set color(color) { this._color = color; this._genCanvas(); }
  get backgroundColor() { return this._backgroundColor; }
  set backgroundColor(color) { this._backgroundColor = color; this._genCanvas(); }
  get padding() { return this._padding; }
  set padding(padding) { this._padding = padding; this._genCanvas(); }
  get borderWidth() { return this._borderWidth; }
  set borderWidth(borderWidth) { this._borderWidth = borderWidth; this._genCanvas(); }
  get borderColor() { return this._borderColor; }
  set borderColor(borderColor) { this._borderColor = borderColor; this._genCanvas(); }
  get fontFace() { return this._fontFace; }
  set fontFace(fontFace) { this._fontFace = fontFace; this._genCanvas(); }
  get fontSize() { return this._fontSize; }
  set fontSize(fontSize) { this._fontSize = fontSize; this._genCanvas(); }
  get fontWeight() { return this._fontWeight; }
  set fontWeight(fontWeight) { this._fontWeight = fontWeight; this._genCanvas(); }
  get strokeWidth() { return this._strokeWidth; }
  set strokeWidth(strokeWidth) { this._strokeWidth = strokeWidth; this._genCanvas(); }
  get strokeColor() { return this._strokeColor; }
  set strokeColor(strokeColor) { this._strokeColor = strokeColor; this._genCanvas(); }

  _genCanvas() {
    const canvas = this._canvas;
    const ctx = canvas.getContext('2d');

    const border = [this.borderWidth, this.borderWidth]; // x,y border
    const relBorder = border.map(b => b * this.fontSize * 0.1); // border in canvas units

    const padding = [this.padding, this.padding]; // x,y padding
    const relPadding = padding.map(p => p * this.fontSize * 0.1); // padding in canvas units

    const lines = this.text.split('\n');
    const font = `${this.fontWeight} ${this.fontSize}px ${this.fontFace}`;

    ctx.font = font; // measure canvas with appropriate font
    const innerWidth = Math.max(...lines.map(line => ctx.measureText(line).width));
    const innerHeight = this.fontSize * lines.length;
    canvas.width = innerWidth + relBorder[0] * 2 + relPadding[0] * 2;
    canvas.height = innerHeight + relBorder[1] * 2 + relPadding[1] * 2;

    // paint border
    if (this.borderWidth) {
      ctx.strokeStyle = this.borderColor;

      if (relBorder[0]) {
        ctx.lineWidth = relBorder[0] * 2;
        ctx.beginPath();
        ctx.moveTo(0, 0);
        ctx.lineTo(0, canvas.height);
        ctx.moveTo(canvas.width, 0);
        ctx.lineTo(canvas.width, canvas.height);
        ctx.stroke();
      }

      if (relBorder[1]) {
        ctx.lineWidth = relBorder[1] * 2;
        ctx.beginPath();
        ctx.moveTo(relBorder[0], 0);
        ctx.lineTo(canvas.width - relBorder[0], 0);
        ctx.moveTo(relBorder[0], canvas.height);
        ctx.lineTo(canvas.width - relBorder[0], canvas.height);
        ctx.stroke();
      }
    }

    ctx.translate(relBorder[0], relBorder[1]);

    // paint background
    if (this.backgroundColor) {
      ctx.fillStyle = this.backgroundColor;
      ctx.fillRect(0, 0, canvas.width - relBorder[0] * 2, canvas.height - relBorder[1] * 2);
    }

    ctx.translate(relPadding[0], relPadding[1]);

    // paint text
    ctx.font = font; // Set font again after canvas is resized, as context properties are reset
    ctx.fillStyle = this.color;
    ctx.textBaseline = 'bottom';

    const drawTextStroke = this.strokeWidth > 0;
    if (drawTextStroke) {
      ctx.lineWidth = this.strokeWidth * this.fontSize / 10;
      ctx.strokeStyle = this.strokeColor;
    }

    lines.forEach((line, index) => {
      const lineX = (innerWidth - ctx.measureText(line).width) / 2;
      const lineY = (index + 1) * this.fontSize;

      drawTextStroke && ctx.strokeText(line, lineX, lineY);
      ctx.fillText(line, lineX, lineY);
    });

    // Inject canvas into sprite
    this._texture.image = canvas;
    this._texture.needsUpdate = true;

    const yScale = this.textHeight * lines.length + border[1] * 2 + padding[1] * 2;
    this.scale.set(yScale * canvas.width / canvas.height, yScale, 0);
  }

  // clone(recursive?: boolean): this {
  //   return new SpriteText(this.text, this.textHeight, this.color).copy(this);
  // }

  copy(source: this, recursive?: boolean): this {
    super.copy(source, recursive);

    this.color = source.color;
    this.backgroundColor = source.backgroundColor;
    this.padding = source.padding;
    this.borderWidth = source.borderWidth;
    this.borderColor = source.borderColor;
    this.fontFace = source.fontFace;
    this.fontSize = source.fontSize;
    this.fontWeight = source.fontWeight;
    this.strokeWidth = source.strokeWidth;
    this.strokeColor = source.strokeColor;

    return this;
  }
}
在CompassRose.ts中,将创建文本精灵,将其翻译并旋转到适当的位置,然后将其添加到THREE.Group中

import TextSprite from './TextSprite';

const markerLength = 1;
const fifthMarkerLength = 5;
const tenthMarkerLength = 10;
const ordinalMarkerLength = 15;

const color = 0x444444;
const fontSize = 4;
const halfFontSize = fontSize / 2;
const radius = 100;

const cardinalOuter = radius + 10;


export default class CompassRose {
    build(): THREE.Group {
        const group = new THREE.Group();

        const material = new THREE.LineBasicMaterial({
            color: color
        });

        for (let degree = 0; degree < 360; degree++) {
            const lineParent = new THREE.Group();
            const lineAngle = (Math.PI * degree) / 180;

            lineParent.rotateX(lineAngle);

            const length = this.getMarkerLength(degree);
            const line = this.buildLine(radius, length, material);

            lineParent.add(line);
            group.add(lineParent);
        }

        let sprite = this.createHeadingTextSprite("N")
            .translateX(cardinalOuter - fontSize)
            .translateY(halfFontSize)
            .rotateZ(-Math.PI / 2);
        group.add(sprite);

        sprite = this.createHeadingTextSprite("S")
            .translateX(-cardinalOuter)
            .translateY(halfFontSize)
            .rotateZ(-Math.PI / 2);
        group.add(sprite);

        sprite = this.createHeadingTextSprite("W")
            .translateY(cardinalOuter)
            .translateX(-halfFontSize)
            .rotateZ(-Math.PI / 2);
        group.add(sprite);

        sprite = this.createHeadingTextSprite("E")
            .translateY(-cardinalOuter + fontSize)
            .translateX(-halfFontSize)
            .rotateZ(-Math.PI / 2);
        group.add(sprite);

        return group;
    }

    private getMarkerLength(degree: number) {
        let length: number;

        if (degree % 90 === 0) {
            length = ordinalMarkerLength;
        } else if (degree % 10 === 0) {
            length = tenthMarkerLength;
        } else if (degree % 5 === 0) {
            length = fifthMarkerLength;
        } else {
            length = markerLength;
        }

        return length;
    }

    private buildLine(radius: number, length: number, material: THREE.Material) {
        const lineGeometry = new THREE.Geometry();
        lineGeometry.vertices.push(new THREE.Vector3(0, radius, 0));
        lineGeometry.vertices.push(new THREE.Vector3(0, radius - length, 0));
        return new THREE.Line(lineGeometry, material);
    }

    private createHeadingTextSprite(text: string): THREE.Sprite {
        return new TextSprite(text, 10, '#888888');
    }
}
从“/TextSprite”导入TextSprite;
常数markerLength=1;
常量第五个MarkerLength=5;
常数tenthMarkerLength=10;
常量序数MarkerLength=15;
常数颜色=0x4444;
常数fontSize=4;
const halfFontSize=fontSize/2;
常数半径=100;
常数=半径+10;
导出默认类CompassRose{
build():三个。组{
const group=new THREE.group();
常量材质=新的三线基本材质({
颜色:颜色
});
对于(让度=0;度<360;度++){
const lineParent=new THREE.Group();
常数lineAngle=(Math.PI*度)/180;
lineParent.rotateX(lineAngle);
常量长度=此.getMarkerLength(度);
常数线=该建筑线(半径、长度、材料);
lineParent.add(行);
添加组(lineParent);
}
让sprite=this.createHeadingTextSprite(“N”)
.translateX(基数外部-字体大小)
.translateY(半字体大小)
.rotateZ(-Math.PI/2);
添加(雪碧);
sprite=this.createHeadingTextSprite(“S”)
.translateX(-cardinalOuter)
.translateY(半字体大小)
.rotateZ(-Math.PI/2);
添加(雪碧);
sprite=this.createHeadingTextSprite(“W”)
.translateY(红衣主教)
.translateX(-halfFontSize)
.rotateZ(-Math.PI/2);
添加(雪碧);
sprite=this.createHeadingTextSprite(“E”)
.translateY(-cardinalOuter+fontSize)
.translateX(-halfFontSize)
.rotateZ(-Math.PI/2);
添加(雪碧);
返回组;
}
私人getMarkerLength(学位:编号){
让长度:数字;
如果(度%90==0){
长度=顺序标记长度;
}否则如果(学位%10==0){
长度=十分长;
}否则如果(学位%5==0){
长度=第五个MarkerLength;
}否则{
长度=标记长度;
}
返回长度;
}
专用建筑线(半径:编号,长度:编号,材质:三。材质){
const lineGeometry=新的三个.Geometry();
lineGeometry.vertices.push(新的3.Vector3(0,半径,0));
lineGeometry.vertices.push(新的3.Vector3(0,半径-长度,0));
返回新的三线(线几何体、材质);
}
私有createHeadingTextSprite(文本:字符串):3.Sprite{
返回新的TextSprite(文本,10'#8888888');
}
}

浏览器控制台窗口中未显示任何错误。

请注意,Forge Viewer基于Three.js版本R71。在代码中,您似乎在使用Three.js R103的类型定义,Three spritetext依赖项引用Three.js R86。恐怕是这些版本的差异导致了文本精灵呈现的无声失败

作为替代方案,我建议:

  • 使用(可在R71中获得)生成精简的3D文本
  • 向场景中添加简单贴图,并将刻度盘和标签添加为纹理
  • 考虑将拨号盘/标签作为另一个Forge模型(例如,从PDF或DWG文件)加载到查看器场景中

感谢您的快速响应!我刚刚更新了项目,删除了对Three.js R103的类型定义的依赖。没有对three spritetext的依赖,我已将代码转换为Typescript,并将源文件添加到项目中,请参阅。sprite由Three.js R71支持,所以我希望这个版本的文本sprite可以工作?是的,它可以工作,但是Three.js API变化非常快,所以即使R71版本确实支持Sprites,在R86版本之前(文本sprite代码似乎已经实现)可能已经发生了很多变化。在Forge Viewer中使用时,我们也遇到了类似的问题。所以底线是:我们不能正式支持针对不同版本的three.js编写的库。如果需要使这样的库与Forge Viewer兼容,我们需要分配一些时间来研究、调试和移植代码,就像我们对Potree所做的那样。我原以为会有更容易添加文本/标签的需求,有什么地方我可以提出要求吗?另外,如何加载字体以用于THREE.TextGeometry?如果我导入字体“``import FontJson from'../fonts/helvetiker_regular.typeface.json```然后创建它``const font=new THREE.font(FontJson);const options={font:font};const textGeometry=new THREE.textGeometry(文本,选项);const textMesh=new THREE.Mesh(textGeometry,material);`我得到一个运行时错误:TypeError:THREE.Font不是构造函数解决了这个问题,我必须为THREE.FontUtils添加一个类型声明,然后我可以在初始化时调用它,例如THREE.FontUtils.loadFace(FontJson);我已经更新了示例项目,文本现在显示:-)