packages/miew/src/io/exporters/fbx/FBXModel.js

Summary

Maintainability
D
1 day
Test Coverage
import { Vector4 } from 'three'

const FBX_POS_SIZE = 3
const FBX_NORM_SIZE = 3
const FBX_COL_SIZE = 4

function copyFbxPoint3(src, srcIdx, dst, dstIdx) {
  dst[dstIdx] = src[srcIdx]
  dst[dstIdx + 1] = src[srcIdx + 1]
  dst[dstIdx + 2] = src[srcIdx + 2]
}

function copyFbxPoint4(src, srcIdx, dst, dstIdx, value) {
  dst[dstIdx] = src[srcIdx]
  dst[dstIdx + 1] = src[srcIdx + 1]
  dst[dstIdx + 2] = src[srcIdx + 2]
  dst[dstIdx + 3] = value
}

const vector4 = new Vector4()
function copyTransformedPoint3(src, srcIdx, dst, dstIdx, opts) {
  vector4.set(src[srcIdx], src[srcIdx + 1], src[srcIdx + 2], opts.w)
  vector4.applyMatrix4(opts.matrix)
  dst[dstIdx] = vector4.x
  dst[dstIdx + 1] = vector4.y
  dst[dstIdx + 2] = vector4.z
}

function setSubArray(src, dst, count, copyFunctor, functorOpts) {
  if (
    (dst.array.length - dst.start) / dst.stride < count ||
    (src.array.length - src.start) / src.stride < count
  ) {
    return // we've got no space
  }
  if (src.stride === dst.stride) {
    // stride is the same
    dst.array.set(src.array, dst.start)
  } else {
    let idx = dst.start
    let arridx = src.start
    for (let i = 0; i < count; ++i, idx += dst.stride, arridx += src.stride) {
      copyFunctor(src.array, arridx, dst.array, idx, functorOpts)
    }
  }
}

export default class FBXModel {
  constructor() {
    this.positions = null
    this.normals = null
    this.colors = null
    this.indices = null
    this.lastPos = 0
    this.lastNorm = 0
    this.lastCol = 0
    this.lastIdx = 0
  }

  init(vertsCount, indsCount) {
    this.positions = new Float32Array(vertsCount * FBX_POS_SIZE)
    this.normals = new Float32Array(vertsCount * FBX_NORM_SIZE)
    this.colors = new Float32Array(vertsCount * FBX_COL_SIZE)
    this.indices = new Int32Array(indsCount)
  }

  setPositions(array, start, count, stride) {
    const src = {
      array,
      start,
      stride
    }
    const dst = {
      array: this.positions,
      start: this.lastPos,
      stride: FBX_POS_SIZE
    }
    setSubArray(src, dst, count, copyFbxPoint3)
    this.lastPos += count * FBX_POS_SIZE
  }

  setTransformedPositions(array, start, count, stride, matrix) {
    let idx = this.lastPos
    let arrIdx = start
    const opts = { matrix, w: 1 }
    for (let i = 0; i < count; ++i, arrIdx += stride, idx += FBX_POS_SIZE) {
      copyTransformedPoint3(array, arrIdx, this.positions, idx, opts)
    }
    this.lastPos += count * FBX_POS_SIZE
  }

  setNormals(array, start, count, stride) {
    const src = {
      array,
      start,
      stride
    }
    const dst = {
      array: this.normals,
      start: this.lastNorm,
      stride: FBX_NORM_SIZE
    }
    setSubArray(src, dst, count, copyFbxPoint3)
    this.lastNorm += count * FBX_NORM_SIZE
  }

  setTransformedNormals(array, start, count, stride, matrix) {
    let idx = this.lastNorm
    let arrIdx = start
    const opts = { matrix, w: 0 }
    for (let i = 0; i < count; ++i, arrIdx += stride, idx += FBX_NORM_SIZE) {
      copyTransformedPoint3(array, arrIdx, this.normals, idx, opts)
    }
    this.lastNorm += count * FBX_NORM_SIZE
  }

  setColors(array, start, count, stride) {
    const src = {
      array,
      start,
      stride
    }
    const dst = {
      array: this.colors,
      start: this.lastCol,
      stride: FBX_COL_SIZE
    }
    setSubArray(src, dst, count, copyFbxPoint4, 1)
    this.lastCol += count * FBX_COL_SIZE
  }

  setIndices(array, start, count) {
    this.indices.set(array, this.lastIdx)
    this.lastIdx += count
  }

  setShiftedIndices(array, count, shift) {
    const shifted = array.map((x) => x + shift)
    this.setIndices(shifted, 0, count)
  }

  getVerticesNumber() {
    return this.lastPos / FBX_POS_SIZE
  }

  addInstance(matrix, geo) {
    // add indices at first to take old number of vertices for shift
    const currentCount = this.getVerticesNumber()
    this.setShiftedIndices(geo.indices, geo.indices.length, currentCount)
    // simply write vertices at empty space
    const size = geo.itemSize
    this.setTransformedPositions(
      geo.positions,
      0,
      geo.vertsCount,
      size.position,
      matrix
    )
    this.setTransformedNormals(
      geo.normals,
      0,
      geo.vertsCount,
      size.normal,
      matrix
    )
    this.setColors(geo.colors, 0, geo.vertsCount, size.color)
  }
}