nodeMaterialBlockConnectionPoint.ts 17 KB

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