Skip to content

Commit

Permalink
Add UV2 morph support and load UV and UV2 morph targets from glTF
Browse files Browse the repository at this point in the history
  • Loading branch information
chubei-urus committed Nov 7, 2024
1 parent 0f3934a commit 1c4778d
Show file tree
Hide file tree
Showing 23 changed files with 227 additions and 15 deletions.
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
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

0 comments on commit 1c4778d

Please sign in to comment.