|
@@ -6,6 +6,8 @@ import { Scene } from "../scene";
|
|
|
import { EngineStore } from "../Engines/engineStore";
|
|
|
import { Mesh } from "../Meshes/mesh";
|
|
|
import { MorphTarget } from "./morphTarget";
|
|
|
+import { RawTexture } from "../Materials/Textures/rawTexture";
|
|
|
+import { Constants } from "../Engines/constants";
|
|
|
/**
|
|
|
* This class is used to deform meshes using morphing between different targets
|
|
|
* @see https://doc.babylonjs.com/how_to/how_to_use_morphtargets
|
|
@@ -23,6 +25,10 @@ export class MorphTargetManager {
|
|
|
private _vertexCount = 0;
|
|
|
private _uniqueId = 0;
|
|
|
private _tempInfluences = new Array<number>();
|
|
|
+ private _canUseTextureForTargets = false;
|
|
|
+
|
|
|
+ /** @hidden */
|
|
|
+ public _targetStoreTextures: Array<RawTexture> = [];
|
|
|
|
|
|
/**
|
|
|
* Gets or sets a boolean indicating if normals must be morphed
|
|
@@ -54,6 +60,9 @@ export class MorphTargetManager {
|
|
|
this._scene.morphTargetManagers.push(this);
|
|
|
|
|
|
this._uniqueId = this._scene.getUniqueId();
|
|
|
+
|
|
|
+ const engineCaps = this._scene.getEngine().getCaps();
|
|
|
+ this._canUseTextureForTargets = engineCaps.textureFloat && engineCaps.maxVertexTextureImageUnits > 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -113,6 +122,26 @@ export class MorphTargetManager {
|
|
|
return this._influences;
|
|
|
}
|
|
|
|
|
|
+ private _useTextureToStoreTargets = true;
|
|
|
+ /**
|
|
|
+ * Gets or sets a boolean indicating that targets should be stored as a texture instead of using vertex attributes (default is true).
|
|
|
+ * Please note that this option is not available if the hardware does not support it
|
|
|
+ */
|
|
|
+ public get useTextureToStoreTargets(): boolean {
|
|
|
+ return this._useTextureToStoreTargets;
|
|
|
+ }
|
|
|
+
|
|
|
+ public set useTextureToStoreTargets(value: boolean) {
|
|
|
+ this._useTextureToStoreTargets = value;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Gets a boolean indicating that the targets are stored into a texture (instead of as attributes)
|
|
|
+ */
|
|
|
+ public get isUsingTextureForTargets() {
|
|
|
+ return this.useTextureToStoreTargets && this._canUseTextureForTargets;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Gets the active target at specified index. An active target is a target with an influence > 0
|
|
|
* @param index defines the index to check
|
|
@@ -248,6 +277,84 @@ export class MorphTargetManager {
|
|
|
if (!this._scene) {
|
|
|
return;
|
|
|
}
|
|
|
+
|
|
|
+ if (this.isUsingTextureForTargets) {
|
|
|
+ if (!this._vertexCount) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ let textureWidth = this._vertexCount * 3;
|
|
|
+
|
|
|
+ if (this._supportsNormals) {
|
|
|
+ textureWidth *= 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this._supportsTangents) {
|
|
|
+ textureWidth += this._vertexCount * 4;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this._supportsUVs) {
|
|
|
+ textureWidth += this._vertexCount * 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (var index = 0; index < this._targets.length; index++) {
|
|
|
+ let target = this._targets[index];
|
|
|
+
|
|
|
+ if (!this._targetStoreTextures[index] || this._targetStoreTextures[index].getSize().width !== textureWidth) {
|
|
|
+ if (this._targetStoreTextures[index]) {
|
|
|
+ this._targetStoreTextures[index].dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+ let data = new Float32Array(textureWidth);
|
|
|
+
|
|
|
+ let offset = 0;
|
|
|
+ const positions = target.getPositions();
|
|
|
+ const normals = target.getNormals();
|
|
|
+ const uvs = target.getUVs();
|
|
|
+ const tangents = target.getTangents();
|
|
|
+
|
|
|
+ if (!positions) {
|
|
|
+ Logger.Error("Invalid morph target. Target must have positions.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (var vertex = 0; vertex < this._vertexCount; vertex++) {
|
|
|
+ data[offset] = positions[vertex * 3];
|
|
|
+ data[offset + 1] = positions[vertex * 3 + 1];
|
|
|
+ data[offset + 2] = positions[vertex * 3 + 2];
|
|
|
+
|
|
|
+ offset += 3;
|
|
|
+
|
|
|
+ if (normals) {
|
|
|
+ data[offset] = normals[vertex * 3];
|
|
|
+ data[offset + 1] = normals[vertex * 3 + 1];
|
|
|
+ data[offset + 2] = normals[vertex * 3 + 2];
|
|
|
+ offset += 3;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (uvs) {
|
|
|
+ data[offset] = uvs[vertex * 2];
|
|
|
+ data[offset + 1] = uvs[vertex * 2 + 1];
|
|
|
+ offset += 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (tangents) {
|
|
|
+ data[offset] = tangents[vertex * 4];
|
|
|
+ data[offset + 1] = tangents[vertex * 4 + 1];
|
|
|
+ data[offset + 2] = tangents[vertex * 4 + 2];
|
|
|
+ data[offset + 3] = tangents[vertex * 4 + 3];
|
|
|
+ offset += 4;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this._targetStoreTextures[index]= RawTexture.CreateRGBATexture(data, textureWidth, 1,
|
|
|
+ this._scene, false, false, Constants.TEXTURE_NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_FLOAT);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
// Flag meshes as dirty to resync with the active targets
|
|
|
for (var mesh of this._scene.meshes) {
|
|
|
if ((<any>mesh).morphTargetManager === this) {
|