Skip to content

Commit

Permalink
Add lattice support for NGE (#15690)
Browse files Browse the repository at this point in the history
* Fix #15678

* Fixed!

* no need
  • Loading branch information
deltakosh authored Oct 10, 2024
1 parent 1a27c46 commit 4746b2a
Show file tree
Hide file tree
Showing 11 changed files with 305 additions and 14 deletions.
227 changes: 227 additions & 0 deletions packages/dev/core/src/Meshes/Node/Blocks/Set/latticeBlock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import { NodeGeometryBlock } from "../../nodeGeometryBlock";
import type { NodeGeometryConnectionPoint } from "../../nodeGeometryBlockConnectionPoint";
import { RegisterClass } from "../../../../Misc/typeStore";
import { NodeGeometryBlockConnectionPointTypes } from "../../Enums/nodeGeometryConnectionPointTypes";
import type { NodeGeometryBuildState } from "../../nodeGeometryBuildState";
import type { INodeGeometryExecutionContext } from "../../Interfaces/nodeGeometryExecutionContext";
import type { VertexData } from "../../../mesh.vertexData";
import { Vector3 } from "../../../../Maths/math.vector";
import { PropertyTypeForEdition, editableInPropertyPage } from "../../../../Decorators/nodeDecorator";
import { Lattice } from "core/Meshes/lattice";
import { extractMinAndMax } from "core/Maths/math.functions";

/**
* Block used to apply Lattice on geometry
*/
export class LatticeBlock extends NodeGeometryBlock implements INodeGeometryExecutionContext {
private _vertexData: VertexData;
private _currentIndexX: number;
private _currentIndexY: number;
private _currentIndexZ: number;
private _lattice: Lattice;
private _indexVector3 = new Vector3();
private _currentControl = new Vector3();

/**
* Gets or sets a boolean indicating that this block can evaluate context
* Build performance is improved when this value is set to false as the system will cache values instead of reevaluating everything per context change
*/
@editableInPropertyPage("Evaluate context", PropertyTypeForEdition.Boolean, "ADVANCED", { notifiers: { rebuild: true } })
public evaluateContext = true;

/**
* Resolution on x axis
*/
@editableInPropertyPage("resolutionX", PropertyTypeForEdition.Int, "ADVANCED", { notifiers: { rebuild: true }, min: 1, max: 10 })
public resolutionX = 3;

/**
* Resolution on y axis
*/
@editableInPropertyPage("resolutionY", PropertyTypeForEdition.Int, "ADVANCED", { notifiers: { rebuild: true }, min: 1, max: 10 })
public resolutionY = 3;

/**
* Resolution on z axis
*/
@editableInPropertyPage("resolutionZ", PropertyTypeForEdition.Int, "ADVANCED", { notifiers: { rebuild: true }, min: 1, max: 10 })
public resolutionZ = 3;

/**
* Create a new LatticeBlock
* @param name defines the block name
*/
public constructor(name: string) {
super(name);

this.registerInput("geometry", NodeGeometryBlockConnectionPointTypes.Geometry);
this.registerInput("controls", NodeGeometryBlockConnectionPointTypes.Vector3);

this.registerOutput("output", NodeGeometryBlockConnectionPointTypes.Geometry);
}

/**
* Gets the current index in the current flow
* @returns the current index
*/
public getExecutionIndex(): number {
return this._currentIndexX + this.resolutionX * (this._currentIndexY + this.resolutionY * this._currentIndexZ);
}

/**
* Gets the current loop index in the current flow
* @returns the current loop index
*/
public getExecutionLoopIndex(): number {
return this.getExecutionIndex();
}

/**
* Gets the current face index in the current flow
* @returns the current face index
*/
public getExecutionFaceIndex(): number {
return 0;
}

/**
* Gets the current class name
* @returns the class name
*/
public override getClassName() {
return "LatticeBlock";
}

/**
* Gets the geometry input component
*/
public get geometry(): NodeGeometryConnectionPoint {
return this._inputs[0];
}

/**
* Gets the controls input component
*/
public get controls(): NodeGeometryConnectionPoint {
return this._inputs[1];
}

/**
* Gets the geometry output component
*/
public get output(): NodeGeometryConnectionPoint {
return this._outputs[0];
}

/**
* Gets the value associated with a contextual positions
* In this case it will be the current position in the lattice
* @returns the current position in the lattice
*/
public getOverridePositionsContextualValue() {
return this._indexVector3;
}

/**
* Gets the value associated with a contextual normals
* In this case it will be the current control point being processed
* @returns the current control point being processed
*/
public getOverrideNormalsContextualValue() {
return this._currentControl;
}

protected override _buildBlock(state: NodeGeometryBuildState) {
const func = (state: NodeGeometryBuildState) => {
state.pushExecutionContext(this);

this._vertexData = this.geometry.getConnectedValue(state);

if (this._vertexData) {
this._vertexData = this._vertexData.clone(); // Preserve source data
}

if (!this._vertexData || !this._vertexData.positions) {
state.restoreExecutionContext();
this.output._storedValue = null;
return;
}
const positions = this._vertexData.positions;
const boundingInfo = extractMinAndMax(positions!, 0, positions!.length / 3);

// Building the lattice
this._lattice = new Lattice({
resolutionX: this.resolutionX,
resolutionY: this.resolutionY,
resolutionZ: this.resolutionZ,
size: boundingInfo.maximum.subtract(boundingInfo.minimum),
});

for (this._currentIndexX = 0; this._currentIndexX < this.resolutionX; this._currentIndexX++) {
for (this._currentIndexY = 0; this._currentIndexY < this.resolutionY; this._currentIndexY++) {
for (this._currentIndexZ = 0; this._currentIndexZ < this.resolutionZ; this._currentIndexZ++) {
this._indexVector3.set(this._currentIndexX, this._currentIndexY, this._currentIndexZ);
const latticeControl = this._lattice.data[this._currentIndexX][this._currentIndexY][this._currentIndexZ];
this._currentControl.copyFrom(latticeControl);
const tempVector3 = this.controls.getConnectedValue(state) as Vector3;

if (tempVector3) {
latticeControl.set(tempVector3.x, tempVector3.y, tempVector3.z);
}
}
}
}

// Apply lattice
this._lattice.deform(positions);

// Storage
state.restoreExecutionContext();
return this._vertexData;
};

if (this.evaluateContext) {
this.output._storedFunction = func;
} else {
this.output._storedFunction = null;
this.output._storedValue = func(state);
}
}

protected override _dumpPropertiesCode() {
let codeString = super._dumpPropertiesCode() + `${this._codeVariableName}.evaluateContext = ${this.evaluateContext ? "true" : "false"};\n`;

codeString += `${this._codeVariableName}.resolutionX = ${this.resolutionX};\n`;
codeString += `${this._codeVariableName}.resolutionY = ${this.resolutionY};\n`;
codeString += `${this._codeVariableName}.resolutionZ = ${this.resolutionZ};\n`;
return codeString;
}

/**
* Serializes this block in a JSON representation
* @returns the serialized block object
*/
public override serialize(): any {
const serializationObject = super.serialize();

serializationObject.evaluateContext = this.evaluateContext;
serializationObject.resolutionX = this.resolutionX;
serializationObject.resolutionY = this.resolutionY;
serializationObject.resolutionZ = this.resolutionZ;

return serializationObject;
}

public override _deserialize(serializationObject: any) {
super._deserialize(serializationObject);

if (serializationObject.evaluateContext !== undefined) {
this.evaluateContext = serializationObject.evaluateContext;
this.resolutionX = serializationObject.resolutionX;
this.resolutionY = serializationObject.resolutionY;
this.resolutionZ = serializationObject.resolutionZ;
}
}
}

RegisterClass("BABYLON.LatticeBlock", LatticeBlock);
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ export class GeometryInputBlock extends NodeGeometryBlock {
switch (value) {
case NodeGeometryContextualSources.Positions:
case NodeGeometryContextualSources.Normals:
case NodeGeometryContextualSources.LatticeID:
case NodeGeometryContextualSources.LatticeControl:
this._type = NodeGeometryBlockConnectionPointTypes.Vector3;
break;
case NodeGeometryContextualSources.Colors:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ export enum NodeGeometryContextualSources {
LoopID = 0x000f,
/** InstanceID */
InstanceID = 0x0010,
/** LatticeID */
LatticeID = 0x0011,
/** LatticeControl */
LatticeControl = 0x0012,
}
1 change: 1 addition & 0 deletions packages/dev/core/src/Meshes/Node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export * from "./Blocks/computeNormalsBlock";
export * from "./Blocks/vectorConverterBlock";
export * from "./Blocks/normalizeVectorBlock";
export * from "./Blocks/Set/setMaterialIDBlock";
export * from "./Blocks/Set/latticeBlock";
export * from "./Blocks/geometryTrigonometryBlock";
export * from "./Blocks/geometryTransformBlock";
export * from "./Blocks/Matrices/rotationXBlock";
Expand Down
12 changes: 12 additions & 0 deletions packages/dev/core/src/Meshes/Node/nodeGeometryBuildState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,18 @@ export class NodeGeometryBuildState {
}
return this.geometryContext.metadata.collectionId || 0;
}
case NodeGeometryContextualSources.LatticeID: {
if (this.executionContext.getOverridePositionsContextualValue) {
return this.executionContext.getOverridePositionsContextualValue();
}
return Vector3.Zero();
}
case NodeGeometryContextualSources.LatticeControl: {
if (this.executionContext.getOverrideNormalsContextualValue) {
return this.executionContext.getOverrideNormalsContextualValue();
}
return Vector3.Zero();
}
}

return null;
Expand Down
2 changes: 1 addition & 1 deletion packages/dev/core/src/Meshes/lattice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export class Lattice {
* Creates a new Lattice
* @param options options for creating
*/
public constructor(options: Partial<ILatticeOptions>) {
public constructor(options?: Partial<ILatticeOptions>) {
const localOptions: ILatticeOptions = {
resolutionX: 3,
resolutionY: 3,
Expand Down
13 changes: 13 additions & 0 deletions packages/tools/nodeGeometryEditor/src/blockTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,16 @@ import { GeometryReplaceColorBlock } from "core/Meshes/Node/Blocks/geometryRepla
import { GeometryRotate2dBlock } from "core/Meshes/Node/Blocks/geometryRotate2dBlock";
import { GeometryLengthBlock } from "core/Meshes/Node/Blocks/geometryLengthBlock";
import { GeometryInterceptorBlock } from "core/Meshes/Node/Blocks/geometryInterceptorBlock";
import { LatticeBlock } from "core/Meshes/Node/Blocks/Set/latticeBlock";

/**
* Static class for BlockTools
*/
export class BlockTools {
public static GetBlockFromString(data: string) {
switch (data) {
case "LatticeBlock":
return new LatticeBlock("Lattice");
case "InterceptorBlock":
return new GeometryInterceptorBlock("Interceptor");
case "Rotate2dBlock":
Expand Down Expand Up @@ -315,6 +318,16 @@ export class BlockTools {
block.contextualValue = NodeGeometryContextualSources.FaceID;
return block;
}
case "LatticeIDBlock": {
const block = new GeometryInputBlock("Lattice ID");
block.contextualValue = NodeGeometryContextualSources.LatticeID;
return block;
}
case "LatticeControlBlock": {
const block = new GeometryInputBlock("Lattice Control");
block.contextualValue = NodeGeometryContextualSources.LatticeControl;
return block;
}
case "AddBlock": {
const block = new MathBlock("Add");
block.operation = MathBlockOperations.Add;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
InstanceIDBlock: "Contextual value representing the current instance index (within an instantiate block)",
GeometryIDBlock: "Contextual value representing the identifier of the current active geometry",
CollectionIDBlock: "Contextual value representing the collection ID associated with the current active geometry",
LatticeIDBlock: "Contextual value representing the current lattice ID ie. the coordinate of the lattice control point inside the lattice",
LatticeControlBlock: "Contextual value representing the current lattice control point",
PosterizeBlock: "Reduces the number of values in each channel to the number in the corresponding channel of steps",
ReplaceColorBlock: "Outputs the replacement color if the distance between value and reference is less than distance, else outputs the value color",
EqualBlock: "Conditional block set to Equal",
Expand Down Expand Up @@ -143,6 +145,7 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
Rotate2dBlock: "Rotates UV coordinates around the W axis.",
LengthBlock: "Outputs the length of an input vector",
InterceptorBlock: "Block used to trigger an observable when traversed",
LatticeBlock: "Block used to apply Lattice on geometry",
};

private _customFrameList: { [key: string]: string };
Expand Down Expand Up @@ -243,6 +246,8 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
"InstanceIDBlock",
"GeometryIDBlock",
"CollectionIDBlock",
"LatticeIDBlock",
"LatticeControlBlock",
],
Logical: ["EqualBlock", "NotEqualBlock", "LessThanBlock", "LessOrEqualBlock", "GreaterThanBlock", "GreaterOrEqualBlock", "XorBlock", "OrBlock", "AndBlock"],
Math__Standard: [
Expand Down Expand Up @@ -317,6 +322,7 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
"ComputeNormalsBlock",
"OptimizeBlock",
"MappingBlock",
"LatticeBlock",
],
Noises: ["RandomBlock", "NoiseBlock"],
Textures: ["TextureBlock", "TextureFetchBlock"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ export class InputDisplayManager implements IDisplayManager {
case NodeGeometryContextualSources.Colors:
value = "Colors";
break;
case NodeGeometryContextualSources.LatticeID:
value = "LatticeID";
break;
case NodeGeometryContextualSources.LatticeControl:
value = "LatticeControl";
break;
}
} else {
switch (inputBlock.type) {
Expand Down
Loading

0 comments on commit 4746b2a

Please sign in to comment.