Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UV2 morph support and load UV and UV2 morph targets from glTF #15602

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ export class MorphTargetsBlock extends NodeMaterialBlock {
NodeMaterialBlockConnectionPointTypes.Color4 | NodeMaterialBlockConnectionPointTypes.Vector4 | NodeMaterialBlockConnectionPointTypes.Vector3
);
this.registerInput("uv", NodeMaterialBlockConnectionPointTypes.Vector2);
this.registerInput("uv2", NodeMaterialBlockConnectionPointTypes.Vector2);
this.registerOutput("positionOutput", NodeMaterialBlockConnectionPointTypes.Vector3);
this.registerOutput("normalOutput", NodeMaterialBlockConnectionPointTypes.Vector3);
this.registerOutput("tangentOutput", NodeMaterialBlockConnectionPointTypes.Vector4);
this.registerOutput("uvOutput", NodeMaterialBlockConnectionPointTypes.Vector2);
this.registerOutput("uv2Output", NodeMaterialBlockConnectionPointTypes.Vector2);
}

/**
Expand Down Expand Up @@ -70,12 +72,19 @@ export class MorphTargetsBlock extends NodeMaterialBlock {
}

/**
* Gets the tangent input component
* Gets the uv input component
*/
public get uv(): NodeMaterialConnectionPoint {
return this._inputs[3];
}

/**
* Gets the uv2 input component
*/
public get uv2(): NodeMaterialConnectionPoint {
return this._inputs[4];
}

/**
* Gets the position output component
*/
Expand All @@ -98,12 +107,19 @@ export class MorphTargetsBlock extends NodeMaterialBlock {
}

/**
* Gets the tangent output component
* Gets the uv output component
*/
public get uvOutput(): NodeMaterialConnectionPoint {
return this._outputs[3];
}

/**
* Gets the uv2 output component
*/
public get uv2Output(): NodeMaterialConnectionPoint {
return this._outputs[4];
}

public override initialize(state: NodeMaterialBuildState) {
state._excludeVariableName("morphTargetInfluences");

Expand Down Expand Up @@ -170,6 +186,15 @@ export class MorphTargetsBlock extends NodeMaterialBlock {
}
uvInput.output.connectTo(this.uv);
}
if (!this.uv2.isConnected) {
let uv2Input = material.getInputBlockByPredicate((b) => b.isAttribute && b.name === "uv2" && additionalFilteringInfo(b));

if (!uv2Input) {
uv2Input = new InputBlock("uv2");
uv2Input.setAsAttribute("uv2");
}
uv2Input.output.connectTo(this.uv2);
}
}

public override prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
Expand Down Expand Up @@ -208,17 +233,20 @@ export class MorphTargetsBlock extends NodeMaterialBlock {
const normal = this.normal;
const tangent = this.tangent;
const uv = this.uv;
const uv2 = this.uv2;
const positionOutput = this.positionOutput;
const normalOutput = this.normalOutput;
const tangentOutput = this.tangentOutput;
const uvOutput = this.uvOutput;
const uv2Output = this.uv2Output;
const state = vertexShaderState;
const repeatCount = defines.NUM_MORPH_INFLUENCERS as number;

const manager = (<Mesh>mesh).morphTargetManager;
const hasNormals = manager && manager.supportsNormals && defines["NORMAL"];
const hasTangents = manager && manager.supportsTangents && defines["TANGENT"];
const hasUVs = manager && manager.supportsUVs && defines["UV1"];
const hasUV2s = manager && manager.supportsUV2s && defines["UV2"];

let injectionCode = "";

Expand Down Expand Up @@ -260,6 +288,13 @@ export class MorphTargetsBlock extends NodeMaterialBlock {
} else {
injectionCode += `${tangentOutput.associatedVariableName}.w = 1.;\n`;
}
injectionCode += `vertexID += 1.0;\n`;
injectionCode += `#endif\n`;
}

if (hasUV2s) {
injectionCode += `#ifdef MORPHTARGETS_UV2\n`;
injectionCode += `${uv2Output.associatedVariableName} += (readVector3FromRawSampler(i, vertexID).xy - ${uv2.associatedVariableName}) * morphTargetInfluences[i];\n`;
injectionCode += `#endif\n`;
}

Expand Down Expand Up @@ -291,6 +326,12 @@ export class MorphTargetsBlock extends NodeMaterialBlock {
}
injectionCode += `#endif\n`;
}

if (hasUV2s) {
injectionCode += `#ifdef MORPHTARGETS_UV2\n`;
injectionCode += `${uv2Output.associatedVariableName}.xy += (uv2_${index} - ${uv2.associatedVariableName}.xy) * morphTargetInfluences[${index}];\n`;
injectionCode += `#endif\n`;
}
}
}
injectionCode += `#endif\n`;
Expand All @@ -312,6 +353,10 @@ export class MorphTargetsBlock extends NodeMaterialBlock {
if (hasUVs) {
state.attributes.push(VertexBuffer.UVKind + "_" + index);
}

if (hasUV2s) {
state.attributes.push(VertexBuffer.UV2Kind + "_" + index);
}
}
}
}
Expand All @@ -333,10 +378,12 @@ export class MorphTargetsBlock extends NodeMaterialBlock {
const normal = this.normal;
const tangent = this.tangent;
const uv = this.uv;
const uv2 = this.uv2;
const positionOutput = this.positionOutput;
const normalOutput = this.normalOutput;
const tangentOutput = this.tangentOutput;
const uvOutput = this.uvOutput;
const uv2Output = this.uv2Output;
const comments = `//${this.name}`;

state.uniforms.push("morphTargetInfluences");
Expand Down Expand Up @@ -366,6 +413,11 @@ export class MorphTargetsBlock extends NodeMaterialBlock {
state.compilationString += `#else\n`;
state.compilationString += `${state._declareOutput(uvOutput)} = vec2(0., 0.);\n`;
state.compilationString += `#endif\n`;
state.compilationString += `#ifdef UV2\n`;
state.compilationString += `${state._declareOutput(uv2Output)} = ${uv2.associatedVariableName};\n`;
state.compilationString += `#else\n`;
state.compilationString += `${state._declareOutput(uv2Output)} = vec2(0., 0.);\n`;
state.compilationString += `#endif\n`;

// Repeatable content
this._repeatableContentAnchor = state._repeatableContentAnchor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ export class MeshAttributeExistsBlock extends NodeMaterialBlock {
case "uvOutput":
this.attributeType = MeshAttributeExistsBlockTypes.UV1;
break;
case "uv2Output":
this.attributeType = MeshAttributeExistsBlockTypes.UV2;
break;
}
}
});
Expand Down
2 changes: 2 additions & 0 deletions packages/dev/core/src/Materials/Node/nodeMaterial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ export class NodeMaterialDefines extends MaterialDefines implements IImageProces
public MORPHTARGETS_TANGENT = false;
/** Morph target uv */
public MORPHTARGETS_UV = false;
/** Morph target uv2 */
public MORPHTARGETS_UV2 = false;
/** Number of morph influencers */
public NUM_MORPH_INFLUENCERS = 0;
/** Using a texture to store morph target data */
Expand Down
1 change: 1 addition & 0 deletions packages/dev/core/src/Materials/PBR/pbrBaseMaterial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ export class PBRMaterialDefines extends MaterialDefines implements IImageProcess
public MORPHTARGETS_NORMAL = false;
public MORPHTARGETS_TANGENT = false;
public MORPHTARGETS_UV = false;
public MORPHTARGETS_UV2 = false;
public NUM_MORPH_INFLUENCERS = 0;
public MORPHTARGETS_TEXTURE = false;

Expand Down
2 changes: 2 additions & 0 deletions packages/dev/core/src/Materials/materialHelper.functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,7 @@ export function PrepareDefinesForMorphTargets(mesh: AbstractMesh, defines: any)
const manager = (<Mesh>mesh).morphTargetManager;
if (manager) {
defines["MORPHTARGETS_UV"] = manager.supportsUVs && defines["UV1"];
defines["MORPHTARGETS_UV2"] = manager.supportsUV2s && defines["UV2"];
defines["MORPHTARGETS_TANGENT"] = manager.supportsTangents && defines["TANGENT"];
defines["MORPHTARGETS_NORMAL"] = manager.supportsNormals && defines["NORMAL"];
defines["NUM_MORPH_INFLUENCERS"] = manager.numMaxInfluencers || manager.numInfluencers;
Expand All @@ -687,6 +688,7 @@ export function PrepareDefinesForMorphTargets(mesh: AbstractMesh, defines: any)
defines["MORPHTARGETS_TEXTURE"] = manager.isUsingTextureForTargets;
} else {
defines["MORPHTARGETS_UV"] = false;
defines["MORPHTARGETS_UV2"] = false;
defines["MORPHTARGETS_TANGENT"] = false;
defines["MORPHTARGETS_NORMAL"] = false;
defines["MORPHTARGETS"] = false;
Expand Down
4 changes: 4 additions & 0 deletions packages/dev/core/src/Materials/shaderMaterial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -770,12 +770,16 @@ export class ShaderMaterial extends PushMaterial {
const manager = mesh ? (<Mesh>mesh).morphTargetManager : null;
if (manager) {
const uv = manager.supportsUVs && defines.indexOf("#define UV1") !== -1;
const uv2 = manager.supportsUV2s && defines.indexOf("#define UV2") !== -1;
const tangent = manager.supportsTangents && defines.indexOf("#define TANGENT") !== -1;
const normal = manager.supportsNormals && defines.indexOf("#define NORMAL") !== -1;
numInfluencers = manager.numMaxInfluencers || manager.numInfluencers;
if (uv) {
defines.push("#define MORPHTARGETS_UV");
}
if (uv2) {
defines.push("#define MORPHTARGETS_UV2");
}
if (tangent) {
defines.push("#define MORPHTARGETS_TANGENT");
}
Expand Down
1 change: 1 addition & 0 deletions packages/dev/core/src/Materials/standardMaterial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ export class StandardMaterialDefines extends MaterialDefines implements IImagePr
public MORPHTARGETS_NORMAL = false;
public MORPHTARGETS_TANGENT = false;
public MORPHTARGETS_UV = false;
public MORPHTARGETS_UV2 = false;
public NUM_MORPH_INFLUENCERS = 0;
public MORPHTARGETS_TEXTURE = false;
public NONUNIFORMSCALING = false; // https://playground.babylonjs.com#V6DWIH
Expand Down
8 changes: 8 additions & 0 deletions packages/dev/core/src/Meshes/mesh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4003,6 +4003,11 @@ export class Mesh extends AbstractMesh implements IGetSetVerticesData {
if (uvs) {
this.geometry.setVerticesData(VertexBuffer.UVKind + "_" + index, uvs, false, 2);
}

const uv2s = morphTarget.getUV2s();
if (uv2s) {
this.geometry.setVerticesData(VertexBuffer.UV2Kind + "_" + index, uv2s, false, 2);
}
}
} else {
let index = 0;
Expand All @@ -4020,6 +4025,9 @@ export class Mesh extends AbstractMesh implements IGetSetVerticesData {
if (this.geometry.isVerticesDataPresent(VertexBuffer.UVKind + index)) {
this.geometry.removeVerticesData(VertexBuffer.UVKind + "_" + index);
}
if (this.geometry.isVerticesDataPresent(VertexBuffer.UV2Kind + index)) {
this.geometry.removeVerticesData(VertexBuffer.UV2Kind + "_" + index);
}
index++;
}
}
Expand Down
40 changes: 40 additions & 0 deletions packages/dev/core/src/Morph/morphTarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export class MorphTarget implements IAnimatable {
private _normals: Nullable<FloatArray> = null;
private _tangents: Nullable<FloatArray> = null;
private _uvs: Nullable<FloatArray> = null;
private _uv2s: Nullable<FloatArray> = null;
private _influence: number;
private _uniqueId = 0;

Expand Down Expand Up @@ -136,6 +137,13 @@ export class MorphTarget implements IAnimatable {
return !!this._uvs;
}

/**
* Gets a boolean defining if the target contains texture coordinates 2 data
*/
public get hasUV2s(): boolean {
return !!this._uv2s;
}

/**
* Affects position data to this target
* @param data defines the position data to use
Expand Down Expand Up @@ -224,6 +232,28 @@ export class MorphTarget implements IAnimatable {
return this._uvs;
}

/**
* Affects texture coordinates 2 data to this target
* @param data defines the texture coordinates 2 data to use
*/
public setUV2s(data: Nullable<FloatArray>) {
const hadUV2s = this.hasUV2s;

this._uv2s = data;

if (hadUV2s !== this.hasUV2s) {
this._onDataLayoutChanged.notifyObservers(undefined);
}
}

/**
* Gets the texture coordinates 2 data stored in this target
* @returns a FloatArray containing the texture coordinates 2 data (or null if not present)
*/
public getUV2s(): Nullable<FloatArray> {
return this._uv2s;
}

/**
* Clone the current target
* @returns a new MorphTarget
Expand All @@ -235,6 +265,7 @@ export class MorphTarget implements IAnimatable {
newOne._normals = this._normals;
newOne._tangents = this._tangents;
newOne._uvs = this._uvs;
newOne._uv2s = this._uv2s;

return newOne;
}
Expand Down Expand Up @@ -262,6 +293,9 @@ export class MorphTarget implements IAnimatable {
if (this.hasUVs) {
serializationObject.uvs = Array.prototype.slice.call(this.getUVs());
}
if (this.hasUV2s) {
serializationObject.uv2s = Array.prototype.slice.call(this.getUV2s());
}

// Animations
SerializationHelper.AppendSerializedAnimations(this, serializationObject);
Expand Down Expand Up @@ -302,6 +336,9 @@ export class MorphTarget implements IAnimatable {
if (serializationObject.uvs) {
result.setUVs(serializationObject.uvs);
}
if (serializationObject.uv2s) {
result.setUV2s(serializationObject.uv2s);
}

// Animations
if (serializationObject.animations) {
Expand Down Expand Up @@ -352,6 +389,9 @@ export class MorphTarget implements IAnimatable {
if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
result.setUVs(<FloatArray>mesh.getVerticesData(VertexBuffer.UVKind));
}
if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind)) {
result.setUV2s(<FloatArray>mesh.getVerticesData(VertexBuffer.UV2Kind));
}

return result;
}
Expand Down
Loading