nodeGeometryBuildState.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. import { NodeGeometryContextualSources } from "./Enums/nodeGeometryContextualSources.js";
  2. import { Matrix, Vector2, Vector3, Vector4 } from "../../Maths/math.vector.js";
  3. import { NodeGeometryBlockConnectionPointTypes } from "./Enums/nodeGeometryConnectionPointTypes.js";
  4. /**
  5. * Class used to store node based geometry build state
  6. */
  7. export class NodeGeometryBuildState {
  8. constructor() {
  9. this._rotationMatrix = new Matrix();
  10. this._scalingMatrix = new Matrix();
  11. this._positionMatrix = new Matrix();
  12. this._scalingRotationMatrix = new Matrix();
  13. this._transformMatrix = new Matrix();
  14. this._tempVector3 = new Vector3();
  15. /** Gets or sets the list of non connected mandatory inputs */
  16. this.notConnectedNonOptionalInputs = [];
  17. /** Gets or sets the list of non contextual inputs having no contextudal data */
  18. this.noContextualData = [];
  19. /** Gets or sets the vertex data */
  20. this.vertexData = null;
  21. this._geometryContext = null;
  22. this._executionContext = null;
  23. this._instancingContext = null;
  24. this._geometryContextStack = [];
  25. this._executionContextStack = [];
  26. this._instancingContextStack = [];
  27. }
  28. /** Gets or sets the geometry context */
  29. get geometryContext() {
  30. return this._geometryContext;
  31. }
  32. /** Gets or sets the execution context */
  33. get executionContext() {
  34. return this._executionContext;
  35. }
  36. /** Gets or sets the instancing context */
  37. get instancingContext() {
  38. return this._instancingContext;
  39. }
  40. /**
  41. * Push the new active geometry context
  42. * @param geometryContext defines the geometry context
  43. */
  44. pushGeometryContext(geometryContext) {
  45. this._geometryContext = geometryContext;
  46. this._geometryContextStack.push(this._geometryContext);
  47. }
  48. /**
  49. * Push the new active execution context
  50. * @param executionContext defines the execution context
  51. */
  52. pushExecutionContext(executionContext) {
  53. this._executionContext = executionContext;
  54. this._executionContextStack.push(this._executionContext);
  55. }
  56. /**
  57. * Push the new active instancing context
  58. * @param instancingContext defines the instancing context
  59. */
  60. pushInstancingContext(instancingContext) {
  61. this._instancingContext = instancingContext;
  62. this._instancingContextStack.push(this._instancingContext);
  63. }
  64. /**
  65. * Remove current geometry context and restore the previous one
  66. */
  67. restoreGeometryContext() {
  68. this._geometryContextStack.pop();
  69. this._geometryContext = this._geometryContextStack.length > 0 ? this._geometryContextStack[this._geometryContextStack.length - 1] : null;
  70. }
  71. /**
  72. * Remove current execution context and restore the previous one
  73. */
  74. restoreExecutionContext() {
  75. this._executionContextStack.pop();
  76. this._executionContext = this._executionContextStack.length > 0 ? this._executionContextStack[this._executionContextStack.length - 1] : null;
  77. }
  78. /**
  79. * Remove current isntancing context and restore the previous one
  80. */
  81. restoreInstancingContext() {
  82. this._instancingContextStack.pop();
  83. this._instancingContext = this._instancingContextStack.length > 0 ? this._instancingContextStack[this._instancingContextStack.length - 1] : null;
  84. }
  85. /**
  86. * Gets the value associated with a contextual source
  87. * @param source Source of the contextual value
  88. * @param skipWarning Do not store the warning for reporting if true
  89. * @returns the value associated with the source
  90. */
  91. getContextualValue(source, skipWarning = false) {
  92. if (!this.executionContext) {
  93. if (!skipWarning) {
  94. this.noContextualData.push(source);
  95. }
  96. return null;
  97. }
  98. const index = this.executionContext.getExecutionIndex();
  99. switch (source) {
  100. case NodeGeometryContextualSources.Positions:
  101. if (this.executionContext.getOverridePositionsContextualValue) {
  102. return this.executionContext.getOverridePositionsContextualValue();
  103. }
  104. if (!this.geometryContext || !this.geometryContext.positions) {
  105. return Vector3.Zero();
  106. }
  107. return Vector3.FromArray(this.geometryContext.positions, index * 3);
  108. case NodeGeometryContextualSources.Normals:
  109. if (this.executionContext.getOverrideNormalsContextualValue) {
  110. return this.executionContext.getOverrideNormalsContextualValue();
  111. }
  112. if (!this.geometryContext || !this.geometryContext.normals) {
  113. return Vector3.Zero();
  114. }
  115. return Vector3.FromArray(this.geometryContext.normals, index * 3);
  116. case NodeGeometryContextualSources.Colors:
  117. if (!this.geometryContext || !this.geometryContext.colors) {
  118. return Vector4.Zero();
  119. }
  120. return Vector4.FromArray(this.geometryContext.colors, index * 4);
  121. case NodeGeometryContextualSources.Tangents:
  122. if (!this.geometryContext || !this.geometryContext.tangents) {
  123. return Vector4.Zero();
  124. }
  125. return Vector4.FromArray(this.geometryContext.tangents, index * 4);
  126. case NodeGeometryContextualSources.UV:
  127. if (this.executionContext.getOverrideUVs1ContextualValue) {
  128. return this.executionContext.getOverrideUVs1ContextualValue();
  129. }
  130. if (!this.geometryContext || !this.geometryContext.uvs) {
  131. return Vector2.Zero();
  132. }
  133. return Vector2.FromArray(this.geometryContext.uvs, index * 2);
  134. case NodeGeometryContextualSources.UV2:
  135. if (!this.geometryContext || !this.geometryContext.uvs2) {
  136. return Vector2.Zero();
  137. }
  138. return Vector2.FromArray(this.geometryContext.uvs2, index * 2);
  139. case NodeGeometryContextualSources.UV3:
  140. if (!this.geometryContext || !this.geometryContext.uvs3) {
  141. return Vector2.Zero();
  142. }
  143. return Vector2.FromArray(this.geometryContext.uvs3, index * 2);
  144. case NodeGeometryContextualSources.UV4:
  145. if (!this.geometryContext || !this.geometryContext.uvs4) {
  146. return Vector2.Zero();
  147. }
  148. return Vector2.FromArray(this.geometryContext.uvs4, index * 2);
  149. case NodeGeometryContextualSources.UV5:
  150. if (!this.geometryContext || !this.geometryContext.uvs5) {
  151. return Vector2.Zero();
  152. }
  153. return Vector2.FromArray(this.geometryContext.uvs5, index * 2);
  154. case NodeGeometryContextualSources.UV6:
  155. if (!this.geometryContext || !this.geometryContext.uvs6) {
  156. return Vector2.Zero();
  157. }
  158. return Vector2.FromArray(this.geometryContext.uvs6, index * 2);
  159. case NodeGeometryContextualSources.VertexID:
  160. return index;
  161. case NodeGeometryContextualSources.FaceID:
  162. return this.executionContext.getExecutionFaceIndex();
  163. case NodeGeometryContextualSources.LoopID:
  164. return this.executionContext.getExecutionLoopIndex();
  165. case NodeGeometryContextualSources.InstanceID:
  166. return this.instancingContext ? this.instancingContext.getInstanceIndex() : 0;
  167. case NodeGeometryContextualSources.GeometryID:
  168. return !this.geometryContext ? 0 : this.geometryContext.uniqueId;
  169. case NodeGeometryContextualSources.CollectionID: {
  170. if (!this.geometryContext || !this.geometryContext.metadata) {
  171. return 0;
  172. }
  173. return this.geometryContext.metadata.collectionId || 0;
  174. }
  175. }
  176. return null;
  177. }
  178. /**
  179. * Adapt a value to a target type
  180. * @param source defines the value to adapt
  181. * @param targetType defines the target type
  182. * @returns the adapted value
  183. */
  184. adapt(source, targetType) {
  185. const value = source.getConnectedValue(this) || 0;
  186. if (source.type === targetType) {
  187. return value;
  188. }
  189. switch (targetType) {
  190. case NodeGeometryBlockConnectionPointTypes.Vector2:
  191. return new Vector2(value, value);
  192. case NodeGeometryBlockConnectionPointTypes.Vector3:
  193. return new Vector3(value, value, value);
  194. case NodeGeometryBlockConnectionPointTypes.Vector4:
  195. return new Vector4(value, value, value, value);
  196. }
  197. return null;
  198. }
  199. /**
  200. * Adapt an input value to a target type
  201. * @param source defines the value to adapt
  202. * @param targetType defines the target type
  203. * @param defaultValue defines the default value to use if not connected
  204. * @returns the adapted value
  205. */
  206. adaptInput(source, targetType, defaultValue) {
  207. if (!source.isConnected) {
  208. return source.value || defaultValue;
  209. }
  210. const value = source.getConnectedValue(this);
  211. if (source._connectedPoint?.type === targetType) {
  212. return value;
  213. }
  214. switch (targetType) {
  215. case NodeGeometryBlockConnectionPointTypes.Vector2:
  216. return new Vector2(value, value);
  217. case NodeGeometryBlockConnectionPointTypes.Vector3:
  218. return new Vector3(value, value, value);
  219. case NodeGeometryBlockConnectionPointTypes.Vector4:
  220. return new Vector4(value, value, value, value);
  221. }
  222. return null;
  223. }
  224. /**
  225. * Emits console errors and exceptions if there is a failing check
  226. */
  227. emitErrors() {
  228. let errorMessage = "";
  229. for (const notConnectedInput of this.notConnectedNonOptionalInputs) {
  230. errorMessage += `input ${notConnectedInput.name} from block ${notConnectedInput.ownerBlock.name}[${notConnectedInput.ownerBlock.getClassName()}] is not connected and is not optional.\n`;
  231. }
  232. for (const source of this.noContextualData) {
  233. errorMessage += `Contextual input ${NodeGeometryContextualSources[source]} has no context to pull data from (must be connected to a setXXX block or a instantiateXXX block).\n`;
  234. }
  235. if (errorMessage) {
  236. // eslint-disable-next-line no-throw-literal
  237. throw "Build of NodeGeometry failed:\n" + errorMessage;
  238. }
  239. }
  240. /** @internal */
  241. _instantiate(clone, currentPosition, rotation, scaling, additionalVertexData) {
  242. // Transform
  243. Matrix.ScalingToRef(scaling.x, scaling.y, scaling.z, this._scalingMatrix);
  244. Matrix.RotationYawPitchRollToRef(rotation.y, rotation.x, rotation.z, this._rotationMatrix);
  245. Matrix.TranslationToRef(currentPosition.x, currentPosition.y, currentPosition.z, this._positionMatrix);
  246. this._scalingMatrix.multiplyToRef(this._rotationMatrix, this._scalingRotationMatrix);
  247. this._scalingRotationMatrix.multiplyToRef(this._positionMatrix, this._transformMatrix);
  248. for (let clonePositionIndex = 0; clonePositionIndex < clone.positions.length; clonePositionIndex += 3) {
  249. this._tempVector3.fromArray(clone.positions, clonePositionIndex);
  250. Vector3.TransformCoordinatesToRef(this._tempVector3, this._transformMatrix, this._tempVector3);
  251. this._tempVector3.toArray(clone.positions, clonePositionIndex);
  252. if (clone.normals) {
  253. this._tempVector3.fromArray(clone.normals, clonePositionIndex);
  254. Vector3.TransformNormalToRef(this._tempVector3, this._scalingRotationMatrix, this._tempVector3);
  255. this._tempVector3.toArray(clone.normals, clonePositionIndex);
  256. }
  257. }
  258. additionalVertexData.push(clone);
  259. }
  260. /** @internal */
  261. _instantiateWithMatrix(clone, transform, additionalVertexData) {
  262. for (let clonePositionIndex = 0; clonePositionIndex < clone.positions.length; clonePositionIndex += 3) {
  263. this._tempVector3.fromArray(clone.positions, clonePositionIndex);
  264. Vector3.TransformCoordinatesToRef(this._tempVector3, transform, this._tempVector3);
  265. this._tempVector3.toArray(clone.positions, clonePositionIndex);
  266. if (clone.normals) {
  267. this._tempVector3.fromArray(clone.normals, clonePositionIndex);
  268. Vector3.TransformNormalToRef(this._tempVector3, transform, this._tempVector3);
  269. this._tempVector3.toArray(clone.normals, clonePositionIndex);
  270. }
  271. }
  272. additionalVertexData.push(clone);
  273. }
  274. /** @internal */
  275. _instantiateWithPositionAndMatrix(clone, currentPosition, transform, additionalVertexData) {
  276. Matrix.TranslationToRef(currentPosition.x, currentPosition.y, currentPosition.z, this._positionMatrix);
  277. transform.multiplyToRef(this._positionMatrix, this._transformMatrix);
  278. for (let clonePositionIndex = 0; clonePositionIndex < clone.positions.length; clonePositionIndex += 3) {
  279. this._tempVector3.fromArray(clone.positions, clonePositionIndex);
  280. Vector3.TransformCoordinatesToRef(this._tempVector3, this._transformMatrix, this._tempVector3);
  281. this._tempVector3.toArray(clone.positions, clonePositionIndex);
  282. if (clone.normals) {
  283. this._tempVector3.fromArray(clone.normals, clonePositionIndex);
  284. Vector3.TransformNormalToRef(this._tempVector3, this._transformMatrix, this._tempVector3);
  285. this._tempVector3.toArray(clone.normals, clonePositionIndex);
  286. }
  287. }
  288. additionalVertexData.push(clone);
  289. }
  290. }
  291. //# sourceMappingURL=nodeGeometryBuildState.js.map