nodeMaterialBlockConnectionPoint.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. import { NodeMaterialBlockConnectionPointTypes } from "./Enums/nodeMaterialBlockConnectionPointTypes.js";
  2. import { NodeMaterialBlockTargets } from "./Enums/nodeMaterialBlockTargets.js";
  3. import { Observable } from "../../Misc/observable.js";
  4. /**
  5. * Enum used to define the compatibility state between two connection points
  6. */
  7. export var NodeMaterialConnectionPointCompatibilityStates;
  8. (function (NodeMaterialConnectionPointCompatibilityStates) {
  9. /** Points are compatibles */
  10. NodeMaterialConnectionPointCompatibilityStates[NodeMaterialConnectionPointCompatibilityStates["Compatible"] = 0] = "Compatible";
  11. /** Points are incompatible because of their types */
  12. NodeMaterialConnectionPointCompatibilityStates[NodeMaterialConnectionPointCompatibilityStates["TypeIncompatible"] = 1] = "TypeIncompatible";
  13. /** Points are incompatible because of their targets (vertex vs fragment) */
  14. NodeMaterialConnectionPointCompatibilityStates[NodeMaterialConnectionPointCompatibilityStates["TargetIncompatible"] = 2] = "TargetIncompatible";
  15. /** Points are incompatible because they are in the same hierarchy **/
  16. NodeMaterialConnectionPointCompatibilityStates[NodeMaterialConnectionPointCompatibilityStates["HierarchyIssue"] = 3] = "HierarchyIssue";
  17. })(NodeMaterialConnectionPointCompatibilityStates || (NodeMaterialConnectionPointCompatibilityStates = {}));
  18. /**
  19. * Defines the direction of a connection point
  20. */
  21. export var NodeMaterialConnectionPointDirection;
  22. (function (NodeMaterialConnectionPointDirection) {
  23. /** Input */
  24. NodeMaterialConnectionPointDirection[NodeMaterialConnectionPointDirection["Input"] = 0] = "Input";
  25. /** Output */
  26. NodeMaterialConnectionPointDirection[NodeMaterialConnectionPointDirection["Output"] = 1] = "Output";
  27. })(NodeMaterialConnectionPointDirection || (NodeMaterialConnectionPointDirection = {}));
  28. /**
  29. * Defines a connection point for a block
  30. */
  31. export class NodeMaterialConnectionPoint {
  32. /**
  33. * Checks if two types are equivalent
  34. * @param type1 type 1 to check
  35. * @param type2 type 2 to check
  36. * @returns true if both types are equivalent, else false
  37. */
  38. static AreEquivalentTypes(type1, type2) {
  39. switch (type1) {
  40. case NodeMaterialBlockConnectionPointTypes.Vector3: {
  41. if (type2 === NodeMaterialBlockConnectionPointTypes.Color3) {
  42. return true;
  43. }
  44. break;
  45. }
  46. case NodeMaterialBlockConnectionPointTypes.Vector4: {
  47. if (type2 === NodeMaterialBlockConnectionPointTypes.Color4) {
  48. return true;
  49. }
  50. break;
  51. }
  52. case NodeMaterialBlockConnectionPointTypes.Color3: {
  53. if (type2 === NodeMaterialBlockConnectionPointTypes.Vector3) {
  54. return true;
  55. }
  56. break;
  57. }
  58. case NodeMaterialBlockConnectionPointTypes.Color4: {
  59. if (type2 === NodeMaterialBlockConnectionPointTypes.Vector4) {
  60. return true;
  61. }
  62. break;
  63. }
  64. }
  65. return false;
  66. }
  67. /** Gets the direction of the point */
  68. get direction() {
  69. return this._direction;
  70. }
  71. /**
  72. * Gets or sets the associated variable name in the shader
  73. */
  74. get associatedVariableName() {
  75. if (this._ownerBlock.isInput) {
  76. return this._ownerBlock.associatedVariableName;
  77. }
  78. if ((!this._enforceAssociatedVariableName || !this._associatedVariableName) && this._connectedPoint) {
  79. return this._connectedPoint.associatedVariableName;
  80. }
  81. return this._associatedVariableName;
  82. }
  83. set associatedVariableName(value) {
  84. this._associatedVariableName = value;
  85. }
  86. /** Get the inner type (ie AutoDetect for instance instead of the inferred one) */
  87. get innerType() {
  88. if (this._linkedConnectionSource && this._linkedConnectionSource.isConnected) {
  89. return this.type;
  90. }
  91. return this._type;
  92. }
  93. /**
  94. * Gets or sets the connection point type (default is float)
  95. */
  96. get type() {
  97. if (this._type === NodeMaterialBlockConnectionPointTypes.AutoDetect) {
  98. if (this._ownerBlock.isInput) {
  99. return this._ownerBlock.type;
  100. }
  101. if (this._connectedPoint) {
  102. return this._connectedPoint.type;
  103. }
  104. if (this._linkedConnectionSource && this._linkedConnectionSource.isConnected) {
  105. return this._linkedConnectionSource.type;
  106. }
  107. }
  108. if (this._type === NodeMaterialBlockConnectionPointTypes.BasedOnInput) {
  109. if (this._typeConnectionSource) {
  110. if (!this._typeConnectionSource.isConnected && this._defaultConnectionPointType) {
  111. return this._defaultConnectionPointType;
  112. }
  113. return this._typeConnectionSource.type;
  114. }
  115. else if (this._defaultConnectionPointType) {
  116. return this._defaultConnectionPointType;
  117. }
  118. }
  119. return this._type;
  120. }
  121. set type(value) {
  122. this._type = value;
  123. }
  124. /** Gets or sets the target of that connection point */
  125. get target() {
  126. if (!this._prioritizeVertex || !this._ownerBlock) {
  127. return this._target;
  128. }
  129. if (this._target !== NodeMaterialBlockTargets.VertexAndFragment) {
  130. return this._target;
  131. }
  132. if (this._ownerBlock.target === NodeMaterialBlockTargets.Fragment) {
  133. return NodeMaterialBlockTargets.Fragment;
  134. }
  135. return NodeMaterialBlockTargets.Vertex;
  136. }
  137. set target(value) {
  138. this._target = value;
  139. }
  140. /**
  141. * Gets a boolean indicating that the current point is connected to another NodeMaterialBlock
  142. */
  143. get isConnected() {
  144. return this.connectedPoint !== null || this.hasEndpoints;
  145. }
  146. /**
  147. * Gets a boolean indicating that the current point is connected to an input block
  148. */
  149. get isConnectedToInputBlock() {
  150. return this.connectedPoint !== null && this.connectedPoint.ownerBlock.isInput;
  151. }
  152. /**
  153. * Gets a the connected input block (if any)
  154. */
  155. get connectInputBlock() {
  156. if (!this.isConnectedToInputBlock) {
  157. return null;
  158. }
  159. return this.connectedPoint.ownerBlock;
  160. }
  161. /** Get the other side of the connection (if any) */
  162. get connectedPoint() {
  163. return this._connectedPoint;
  164. }
  165. /** Get the block that owns this connection point */
  166. get ownerBlock() {
  167. return this._ownerBlock;
  168. }
  169. /** Get the block connected on the other side of this connection (if any) */
  170. get sourceBlock() {
  171. if (!this._connectedPoint) {
  172. return null;
  173. }
  174. return this._connectedPoint.ownerBlock;
  175. }
  176. /** Get the block connected on the endpoints of this connection (if any) */
  177. get connectedBlocks() {
  178. if (this._endpoints.length === 0) {
  179. return [];
  180. }
  181. return this._endpoints.map((e) => e.ownerBlock);
  182. }
  183. /** Gets the list of connected endpoints */
  184. get endpoints() {
  185. return this._endpoints;
  186. }
  187. /** Gets a boolean indicating if that output point is connected to at least one input */
  188. get hasEndpoints() {
  189. return this._endpoints && this._endpoints.length > 0;
  190. }
  191. /** Gets a boolean indicating that this connection has a path to the vertex output*/
  192. get isDirectlyConnectedToVertexOutput() {
  193. if (!this.hasEndpoints) {
  194. return false;
  195. }
  196. for (const endpoint of this._endpoints) {
  197. if (endpoint.ownerBlock.target === NodeMaterialBlockTargets.Vertex) {
  198. return true;
  199. }
  200. if (endpoint.ownerBlock.target === NodeMaterialBlockTargets.Neutral || endpoint.ownerBlock.target === NodeMaterialBlockTargets.VertexAndFragment) {
  201. if (endpoint.ownerBlock.outputs.some((o) => o.isDirectlyConnectedToVertexOutput)) {
  202. return true;
  203. }
  204. }
  205. }
  206. return false;
  207. }
  208. /** Gets a boolean indicating that this connection will be used in the vertex shader */
  209. get isConnectedInVertexShader() {
  210. if (this.target === NodeMaterialBlockTargets.Vertex) {
  211. return true;
  212. }
  213. if (!this.hasEndpoints) {
  214. return false;
  215. }
  216. for (const endpoint of this._endpoints) {
  217. if (endpoint.ownerBlock.target === NodeMaterialBlockTargets.Vertex) {
  218. return true;
  219. }
  220. if (endpoint.target === NodeMaterialBlockTargets.Vertex) {
  221. return true;
  222. }
  223. if (endpoint.ownerBlock.target === NodeMaterialBlockTargets.Neutral || endpoint.ownerBlock.target === NodeMaterialBlockTargets.VertexAndFragment) {
  224. if (endpoint.ownerBlock.outputs.some((o) => o.isConnectedInVertexShader)) {
  225. return true;
  226. }
  227. }
  228. }
  229. return false;
  230. }
  231. /** Gets a boolean indicating that this connection will be used in the fragment shader */
  232. get isConnectedInFragmentShader() {
  233. if (this.target === NodeMaterialBlockTargets.Fragment) {
  234. return true;
  235. }
  236. if (!this.hasEndpoints) {
  237. return false;
  238. }
  239. for (const endpoint of this._endpoints) {
  240. if (endpoint.ownerBlock.target === NodeMaterialBlockTargets.Fragment) {
  241. return true;
  242. }
  243. if (endpoint.ownerBlock.target === NodeMaterialBlockTargets.Neutral || endpoint.ownerBlock.target === NodeMaterialBlockTargets.VertexAndFragment) {
  244. if (endpoint.ownerBlock.isConnectedInFragmentShader()) {
  245. return true;
  246. }
  247. }
  248. }
  249. return false;
  250. }
  251. /**
  252. * Creates a block suitable to be used as an input for this input point.
  253. * If null is returned, a block based on the point type will be created.
  254. * @returns The returned string parameter is the name of the output point of NodeMaterialBlock (first parameter of the returned array) that can be connected to the input
  255. */
  256. createCustomInputBlock() {
  257. return null;
  258. }
  259. /**
  260. * Creates a new connection point
  261. * @param name defines the connection point name
  262. * @param ownerBlock defines the block hosting this connection point
  263. * @param direction defines the direction of the connection point
  264. */
  265. constructor(name, ownerBlock, direction) {
  266. /** @internal */
  267. this._connectedPoint = null;
  268. this._endpoints = new Array();
  269. /** @internal */
  270. this._typeConnectionSource = null;
  271. /** @internal */
  272. this._defaultConnectionPointType = null;
  273. /** @internal */
  274. this._linkedConnectionSource = null;
  275. /** @internal */
  276. this._acceptedConnectionPointType = null;
  277. this._type = NodeMaterialBlockConnectionPointTypes.Float;
  278. /** @internal */
  279. this._enforceAssociatedVariableName = false;
  280. /** Indicates that this connection point needs dual validation before being connected to another point */
  281. this.needDualDirectionValidation = false;
  282. /**
  283. * Gets or sets the additional types supported by this connection point
  284. */
  285. this.acceptedConnectionPointTypes = [];
  286. /**
  287. * Gets or sets the additional types excluded by this connection point
  288. */
  289. this.excludedConnectionPointTypes = [];
  290. /**
  291. * Observable triggered when this point is connected
  292. */
  293. this.onConnectionObservable = new Observable();
  294. /**
  295. * Observable triggered when this point is disconnected
  296. */
  297. this.onDisconnectionObservable = new Observable();
  298. /**
  299. * Gets or sets a boolean indicating that this connection point is exposed on a frame
  300. */
  301. this.isExposedOnFrame = false;
  302. /**
  303. * Gets or sets number indicating the position that the port is exposed to on a frame
  304. */
  305. this.exposedPortPosition = -1;
  306. /** @internal */
  307. this._prioritizeVertex = false;
  308. this._target = NodeMaterialBlockTargets.VertexAndFragment;
  309. this._ownerBlock = ownerBlock;
  310. this.name = name;
  311. this._direction = direction;
  312. }
  313. /**
  314. * Gets the current class name e.g. "NodeMaterialConnectionPoint"
  315. * @returns the class name
  316. */
  317. getClassName() {
  318. return "NodeMaterialConnectionPoint";
  319. }
  320. /**
  321. * Gets a boolean indicating if the current point can be connected to another point
  322. * @param connectionPoint defines the other connection point
  323. * @returns a boolean
  324. */
  325. canConnectTo(connectionPoint) {
  326. return this.checkCompatibilityState(connectionPoint) === NodeMaterialConnectionPointCompatibilityStates.Compatible;
  327. }
  328. /**
  329. * Gets a number indicating if the current point can be connected to another point
  330. * @param connectionPoint defines the other connection point
  331. * @returns a number defining the compatibility state
  332. */
  333. checkCompatibilityState(connectionPoint) {
  334. const ownerBlock = this._ownerBlock;
  335. const otherBlock = connectionPoint.ownerBlock;
  336. if (ownerBlock.target === NodeMaterialBlockTargets.Fragment) {
  337. // Let's check we are not going reverse
  338. if (otherBlock.target === NodeMaterialBlockTargets.Vertex) {
  339. return NodeMaterialConnectionPointCompatibilityStates.TargetIncompatible;
  340. }
  341. for (const output of otherBlock.outputs) {
  342. if (output.ownerBlock.target != NodeMaterialBlockTargets.Neutral && output.isConnectedInVertexShader) {
  343. return NodeMaterialConnectionPointCompatibilityStates.TargetIncompatible;
  344. }
  345. }
  346. }
  347. if (this.type !== connectionPoint.type && connectionPoint.innerType !== NodeMaterialBlockConnectionPointTypes.AutoDetect) {
  348. // Equivalents
  349. if (NodeMaterialConnectionPoint.AreEquivalentTypes(this.type, connectionPoint.type)) {
  350. return NodeMaterialConnectionPointCompatibilityStates.Compatible;
  351. }
  352. // Accepted types
  353. if ((connectionPoint.acceptedConnectionPointTypes && connectionPoint.acceptedConnectionPointTypes.indexOf(this.type) !== -1) ||
  354. (connectionPoint._acceptedConnectionPointType && NodeMaterialConnectionPoint.AreEquivalentTypes(connectionPoint._acceptedConnectionPointType.type, this.type))) {
  355. return NodeMaterialConnectionPointCompatibilityStates.Compatible;
  356. }
  357. else {
  358. return NodeMaterialConnectionPointCompatibilityStates.TypeIncompatible;
  359. }
  360. }
  361. // Excluded
  362. if (connectionPoint.excludedConnectionPointTypes && connectionPoint.excludedConnectionPointTypes.indexOf(this.type) !== -1) {
  363. return NodeMaterialConnectionPointCompatibilityStates.TypeIncompatible;
  364. }
  365. // Check hierarchy
  366. let targetBlock = otherBlock;
  367. let sourceBlock = ownerBlock;
  368. if (this.direction === NodeMaterialConnectionPointDirection.Input) {
  369. targetBlock = ownerBlock;
  370. sourceBlock = otherBlock;
  371. }
  372. if (targetBlock.isAnAncestorOf(sourceBlock)) {
  373. return NodeMaterialConnectionPointCompatibilityStates.HierarchyIssue;
  374. }
  375. return NodeMaterialConnectionPointCompatibilityStates.Compatible;
  376. }
  377. /**
  378. * Connect this point to another connection point
  379. * @param connectionPoint defines the other connection point
  380. * @param ignoreConstraints defines if the system will ignore connection type constraints (default is false)
  381. * @returns the current connection point
  382. */
  383. connectTo(connectionPoint, ignoreConstraints = false) {
  384. if (!ignoreConstraints && !this.canConnectTo(connectionPoint)) {
  385. // eslint-disable-next-line no-throw-literal
  386. throw "Cannot connect these two connectors.";
  387. }
  388. this._endpoints.push(connectionPoint);
  389. connectionPoint._connectedPoint = this;
  390. this._enforceAssociatedVariableName = false;
  391. this.onConnectionObservable.notifyObservers(connectionPoint);
  392. connectionPoint.onConnectionObservable.notifyObservers(this);
  393. return this;
  394. }
  395. /**
  396. * Disconnect this point from one of his endpoint
  397. * @param endpoint defines the other connection point
  398. * @returns the current connection point
  399. */
  400. disconnectFrom(endpoint) {
  401. const index = this._endpoints.indexOf(endpoint);
  402. if (index === -1) {
  403. return this;
  404. }
  405. this._endpoints.splice(index, 1);
  406. endpoint._connectedPoint = null;
  407. this._enforceAssociatedVariableName = false;
  408. endpoint._enforceAssociatedVariableName = false;
  409. this.onDisconnectionObservable.notifyObservers(endpoint);
  410. endpoint.onDisconnectionObservable.notifyObservers(this);
  411. return this;
  412. }
  413. /**
  414. * Fill the list of excluded connection point types with all types other than those passed in the parameter
  415. * @param mask Types (ORed values of NodeMaterialBlockConnectionPointTypes) that are allowed, and thus will not be pushed to the excluded list
  416. */
  417. addExcludedConnectionPointFromAllowedTypes(mask) {
  418. let bitmask = 1;
  419. while (bitmask < NodeMaterialBlockConnectionPointTypes.All) {
  420. if (!(mask & bitmask)) {
  421. this.excludedConnectionPointTypes.push(bitmask);
  422. }
  423. bitmask = bitmask << 1;
  424. }
  425. }
  426. /**
  427. * Serializes this point in a JSON representation
  428. * @param isInput defines if the connection point is an input (default is true)
  429. * @returns the serialized point object
  430. */
  431. serialize(isInput = true) {
  432. const serializationObject = {};
  433. serializationObject.name = this.name;
  434. serializationObject.displayName = this.displayName;
  435. if (isInput && this.connectedPoint) {
  436. serializationObject.inputName = this.name;
  437. serializationObject.targetBlockId = this.connectedPoint.ownerBlock.uniqueId;
  438. serializationObject.targetConnectionName = this.connectedPoint.name;
  439. serializationObject.isExposedOnFrame = true;
  440. serializationObject.exposedPortPosition = this.exposedPortPosition;
  441. }
  442. if (this.isExposedOnFrame || this.exposedPortPosition >= 0) {
  443. serializationObject.isExposedOnFrame = true;
  444. serializationObject.exposedPortPosition = this.exposedPortPosition;
  445. }
  446. return serializationObject;
  447. }
  448. /**
  449. * Release resources
  450. */
  451. dispose() {
  452. this.onConnectionObservable.clear();
  453. this.onDisconnectionObservable.clear();
  454. }
  455. }
  456. //# sourceMappingURL=nodeMaterialBlockConnectionPoint.js.map