TexGraph.hx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. package hrt.texgraph;
  2. typedef Edge = {
  3. ?inputNodeId : Int,
  4. ?nameInput : String, // Fallback if name has changed
  5. ?inputId : Int,
  6. ?outputNodeId : Int,
  7. ?nameOutput : String, // Fallback if name has changed
  8. ?outputId : Int,
  9. };
  10. typedef Connection = {
  11. from : TexNode,
  12. outputId : Int,
  13. };
  14. class TexGraph extends hrt.prefab.Prefab {
  15. public static var CURRENT_NODE_ID = 0;
  16. public var cachedOutputs : Map<Int, Array<h3d.mat.Texture>> = [];
  17. public var nodes : Map<Int, TexNode> = [];
  18. // Base parameters that can be overrided by nodes
  19. @:s public var outputHeight = 256;
  20. @:s public var outputWidth = 256;
  21. @:s public var outputFormat = hxd.PixelFormat.RGBA;
  22. override function save() {
  23. var json = super.save();
  24. Reflect.setField(json, "graph", saveToDynamic());
  25. return json;
  26. }
  27. override function load(json : Dynamic) : Void {
  28. super.load(json);
  29. nodes = [];
  30. CURRENT_NODE_ID = 0;
  31. var graphJson = Reflect.getProperty(json, "graph");
  32. var nodesJson : Array<Dynamic> = Reflect.getProperty(graphJson, "nodes");
  33. for (n in nodesJson) {
  34. var node = TexNode.createFromDynamic(n, this);
  35. this.nodes.set(node.id, node);
  36. CURRENT_NODE_ID = hxd.Math.imax(CURRENT_NODE_ID, node.id+1);
  37. }
  38. var edgesJson : Array<Dynamic> = Reflect.getProperty(graphJson, "edges");
  39. for (e in edgesJson) {
  40. addEdge(e);
  41. }
  42. }
  43. override function copy(other: hrt.prefab.Prefab) : Void {
  44. throw "Texture graph is not meant to be put in a prefab tree. Use a dynamic shader that references this shadergraph instead";
  45. }
  46. public function saveToDynamic() : Dynamic {
  47. var edgesJson : Array<Edge> = [];
  48. for (n in nodes) {
  49. for (inputId => connection in n.connections) {
  50. if (connection == null) continue;
  51. var outputId = connection.outputId;
  52. edgesJson.push({ outputNodeId: connection.from.id, nameOutput: connection.from.getOutputs()[outputId].name, inputNodeId: n.id, nameInput: n.getInputs()[inputId].name, inputId: inputId, outputId: outputId });
  53. }
  54. }
  55. var json = {
  56. nodes: [
  57. for (n in nodes) n.serializeToDynamic(),
  58. ],
  59. edges: edgesJson
  60. };
  61. return json;
  62. }
  63. public function saveToText() : String {
  64. return haxe.Json.stringify(save(), "\t");
  65. }
  66. public function addEdge(edge : Edge) {
  67. var node = this.nodes.get(edge.inputNodeId);
  68. var output = this.nodes.get(edge.outputNodeId);
  69. var inputs = node.getInputs();
  70. var outputs = output.getOutputs();
  71. var outputId = edge.outputId;
  72. var inputId = edge.inputId;
  73. {
  74. // Check if there is an output with that id and if it has the same name
  75. // else try to find the id of another output with the same name
  76. var output = outputs[outputId];
  77. if (output == null || output.name != edge.nameOutput) {
  78. for (id => o in outputs) {
  79. if (o.name == edge.nameOutput) {
  80. outputId = id;
  81. break;
  82. }
  83. }
  84. };
  85. }
  86. {
  87. var input = inputs[inputId];
  88. if (input == null || input.name != edge.nameInput) {
  89. for (id => i in inputs) {
  90. if (i.name == edge.nameInput) {
  91. inputId = id;
  92. break;
  93. }
  94. }
  95. }
  96. }
  97. node.connections[inputId] = { from: output, outputId: outputId };
  98. return true;
  99. }
  100. public function removeEdge(idNode : Int, inputId : Int, update : Bool = true) {
  101. var node = this.nodes.get(idNode);
  102. if (node.connections[inputId] == null) return;
  103. node.connections[inputId] = null;
  104. }
  105. public function hasCycle() {
  106. var visited : Array<Bool> = [for (n in nodes) false];
  107. var recStack : Array<Bool> = [for (n in nodes) false];
  108. function hasCycleUtil(current : Int, visited : Array<Bool>, recStack: Array<Bool>) {
  109. if (recStack[current])
  110. return true;
  111. if (visited[current])
  112. return false;
  113. visited[current] = true;
  114. recStack[current] = true;
  115. var children: Array<Int> = [];
  116. for (c in nodes[current].connections) {
  117. for (idx => n in nodes) {
  118. if (n == c.from)
  119. children.push(idx);
  120. }
  121. }
  122. for (idx in children) {
  123. if (hasCycleUtil(idx, visited, recStack))
  124. return true;
  125. }
  126. recStack[current] = false;
  127. return false;
  128. }
  129. for (idx => n in nodes)
  130. if (hasCycleUtil(idx, visited, recStack))
  131. return true;
  132. return false;
  133. }
  134. public function canAddEdge(edge : Edge) {
  135. var node = this.nodes.get(edge.inputNodeId);
  136. var output = this.nodes.get(edge.outputNodeId);
  137. var inputs = node.getInputs();
  138. var outputs = output.getOutputs();
  139. // Temp add edge to check for cycle, remove it after
  140. var prev = node.connections[edge.inputId];
  141. node.connections[edge.inputId] = {from: output, outputId: edge.outputId};
  142. var res = hasCycle();
  143. node.connections[edge.inputId] = prev;
  144. if(res)
  145. return false;
  146. return true;
  147. }
  148. public function addNode(texNode : TexNode) {
  149. this.nodes.set(texNode.id, texNode);
  150. }
  151. public function removeNode(idNode : Int) {
  152. this.nodes.remove(idNode);
  153. }
  154. public function getOutputNodes() : Array<hrt.texgraph.nodes.TexOutput> {
  155. var outputNodes : Array<hrt.texgraph.nodes.TexOutput> = [];
  156. for (n in nodes) {
  157. if (Std.downcast(n, hrt.texgraph.nodes.TexOutput) != null)
  158. outputNodes.push(cast n);
  159. }
  160. return outputNodes;
  161. }
  162. public function generate() : Map<String, h3d.mat.Texture> {
  163. var engine = h3d.Engine.getCurrent();
  164. if (engine == null)
  165. return null;
  166. cachedOutputs = [];
  167. function generateNode(node : TexNode) {
  168. for (c in node.connections) {
  169. if (c != null && cachedOutputs.get(c.from.id) == null)
  170. generateNode(c.from);
  171. }
  172. // Apply parameters before generation
  173. node.outputFormat = outputFormat;
  174. node.outputHeight = outputHeight;
  175. node.outputWidth = outputWidth;
  176. for (f in Reflect.fields(node.overrides))
  177. Reflect.setField(node, f, Reflect.field(node.overrides, f));
  178. var outputs = node.apply(cachedOutputs);
  179. cachedOutputs.set(node.id, outputs);
  180. }
  181. for (n in nodes) {
  182. if (cachedOutputs.get(n.id) != null)
  183. continue;
  184. generateNode(n);
  185. }
  186. var outputs : Map<String, h3d.mat.Texture> = [];
  187. for (o in getOutputNodes())
  188. outputs.set(o.label, cachedOutputs.get(o.id)[0]);
  189. return outputs;
  190. }
  191. static var _ = hrt.prefab.Prefab.register("texgraph", TexGraph, "texgraph");
  192. }