Configurator.hx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. package hrt.prefab.rfx;
  2. import h3d.scene.Renderer;
  3. #if hscript
  4. private typedef ChangedVar = { obj : Dynamic, field : String, value : Dynamic, set : Bool };
  5. class ConfiguratorInterp extends hscript.Interp {
  6. public var allowChanges : Bool = false;
  7. var prevVars : Map<String,Array<ChangedVar>> = [];
  8. var allVars : Array<ChangedVar> = [];
  9. public function new() {
  10. super();
  11. }
  12. override function set( o : Dynamic, f : String, v : Dynamic ) : Dynamic {
  13. var prev = prevVars.get(f);
  14. if( prev == null ) {
  15. prev = [];
  16. prevVars.set(f, prev);
  17. }
  18. var found = null;
  19. for( v in prev ) {
  20. if( v.obj == o ) {
  21. found = v;
  22. break;
  23. }
  24. }
  25. if( found == null ) {
  26. #if editor
  27. if( !Reflect.hasField(o,f) ) {
  28. var c = Type.getClass(o);
  29. while( c != null ) {
  30. if( Type.getInstanceFields(c).indexOf(f) >= 0 )
  31. break;
  32. c = Type.getSuperClass(c);
  33. }
  34. if( c == null ) {
  35. var cl = Type.getClass(o);
  36. throw (cl == null ? ""+o : Type.getClassName(cl)) + " has no field "+f;
  37. }
  38. }
  39. #end
  40. found = { obj : o, field : f, value : null, set : false };
  41. if ( !allowChanges )
  42. allVars.push(found);
  43. prev.push(found);
  44. if( allVars.length > 200 ) throw "Vars are leaking";
  45. }
  46. if( !found.set ) {
  47. found.set = true;
  48. found.value = Reflect.getProperty(o, f);
  49. }
  50. Reflect.setProperty(o, f, v);
  51. return v;
  52. }
  53. public function restoreVars() {
  54. for( v in allVars ) {
  55. if( v.set ) {
  56. Reflect.setProperty(v.obj, v.field, v.value);
  57. v.set = false;
  58. }
  59. }
  60. }
  61. }
  62. #end
  63. class Configurator extends RendererFX {
  64. @:s public var vars : Array<{ name : String, defValue : Float }> = [];
  65. @:s var script : String = "";
  66. var values : Map<String, Float> = new Map();
  67. var prefabCache : Map<String, { r : Prefab }> = new Map();
  68. var particlesCache : Map<String, { v : h3d.scene.Object }> = new Map();
  69. #if hscript
  70. var interp : ConfiguratorInterp;
  71. var parsedExpr : hscript.Expr;
  72. #end
  73. #if editor
  74. var errorTarget : hide.Element;
  75. #end
  76. var rootPrefab : Prefab;
  77. public function new(?parent) {
  78. super(parent);
  79. type = "configurator";
  80. }
  81. public function set( name : String, value : Float ) {
  82. values.set(name,value);
  83. }
  84. function smoothValue( v : Float, easing : Float ) : Float {
  85. var bpow = Math.pow(v, 1 + easing);
  86. return bpow / (bpow + Math.pow(1 - v, easing + 1));
  87. }
  88. function getParts( r : Renderer, id : String) {
  89. var p = particlesCache.get(id);
  90. if (p != null)
  91. return p.v;
  92. var obj = r.ctx.scene.getObjectByName(id);
  93. if ( obj == null)
  94. throw "Missing object #"+id;
  95. #if !editor
  96. particlesCache.set(id, { v : obj });
  97. #end
  98. return obj;
  99. }
  100. function getPrefab( opt : Bool, id : String ) {
  101. var p = prefabCache.get(id);
  102. if( p != null )
  103. return p.r;
  104. var p = rootPrefab.getOpt(hrt.prefab.Prefab,id,true);
  105. if( p == null ) {
  106. if( opt ) return null;
  107. throw "Missing prefab #"+id;
  108. }
  109. #if !editor
  110. prefabCache.set(id, { r : p });
  111. #end
  112. return p;
  113. }
  114. #if hscript
  115. function allowChanges( v : Bool ) {
  116. interp.allowChanges = v;
  117. }
  118. #end
  119. function resetCache() {
  120. prefabCache = [];
  121. particlesCache = [];
  122. }
  123. override function makeInstance(ctx:Context):Context {
  124. for( v in vars )
  125. values.set(v.name, v.defValue);
  126. rootPrefab = this;
  127. var shared = ctx.shared;
  128. while( shared.parent != null ) {
  129. rootPrefab = shared.parent.prefab;
  130. shared = shared.parent.shared;
  131. }
  132. while( rootPrefab.parent != null )
  133. rootPrefab = rootPrefab.parent;
  134. resetCache();
  135. #if hscript
  136. interp = null;
  137. #end
  138. return super.makeInstance(ctx);
  139. }
  140. override function begin(r:h3d.scene.Renderer, step:h3d.impl.RendererFX.Step) {
  141. #if !hscript
  142. throw "Requires -lib hscript";
  143. #else
  144. if( step == MainDraw ) {
  145. var errorMessage = null;
  146. if( parsedExpr == null ) {
  147. var parser = new hscript.Parser();
  148. parsedExpr = try parser.parseString(script) catch( e : hscript.Expr.Error ) { errorMessage = hscript.Printer.errorToString(e); null; };
  149. }
  150. if( interp == null ) {
  151. interp = new ConfiguratorInterp();
  152. interp.variables.set("get", getPrefab.bind(false));
  153. interp.variables.set("getParts", getParts.bind(r));
  154. interp.variables.set("getOpt", getPrefab.bind(true));
  155. interp.variables.set("smooth", smoothValue);
  156. interp.variables.set("allowChanges", allowChanges);
  157. }
  158. for( k => v in values )
  159. interp.variables.set(k, v);
  160. if( errorMessage == null )
  161. try {
  162. interp.execute(parsedExpr);
  163. } catch( e : Dynamic ) {
  164. errorMessage = Std.string(e);
  165. }
  166. if( errorMessage != null ) {
  167. #if editor
  168. if( errorTarget != null ) errorTarget.text(errorMessage);
  169. #end
  170. } else {
  171. #if editor
  172. if( errorTarget != null ) errorTarget.html("&nbsp;");
  173. #end
  174. }
  175. }
  176. #end
  177. }
  178. #if hscript
  179. override function end(r:h3d.scene.Renderer, step:h3d.impl.RendererFX.Step) {
  180. if( step == Overlay )
  181. interp.restoreVars();
  182. }
  183. #end
  184. #if editor
  185. override function getHideProps() : HideProps {
  186. return { name : "Configurator", icon : "dashboard" };
  187. }
  188. override function edit( ectx : EditContext ) {
  189. var props = new hide.Element('
  190. <div>
  191. <div class="group" name="Variables">
  192. <dl id="vars">
  193. </dl>
  194. <dl>
  195. <dt></dt>
  196. <dd><input type="button" value="Add" id="addvar"/></dd>
  197. </dl>
  198. </div>
  199. <div class="group" name="Script">
  200. <div>
  201. <div class="error">&nbsp;</div>
  202. <div id="script" style="height:200px"></div>
  203. </div>
  204. </div>
  205. </div>
  206. ');
  207. errorTarget = props.find(".error");
  208. var evars = props.find("#vars");
  209. props.find("#addvar").click(function(_) {
  210. var name = ectx.ide.ask("Variable name");
  211. if( name == null ) return;
  212. ectx.makeChanges(this, function() vars.push({ name : name, defValue: 0 }));
  213. values.set(name, 0);
  214. ectx.rebuildProperties();
  215. });
  216. ectx.properties.add(props);
  217. for( v in vars ) {
  218. var ref = { v : values.get(v.name) };
  219. var def = new hide.Element('<div><dt>${v.name}</dt><dd><input type="range" min="0" max="1" field="v"/></dd></div>').appendTo(evars);
  220. ectx.properties.build(def, ref, function(_) {
  221. values.set(v.name, ref.v);
  222. });
  223. def.find("dt").contextmenu(function(e) {
  224. new hide.comp.ContextMenu([
  225. { label : "Set Default", click : () -> v.defValue = ref.v },
  226. { label : "Remove", click : () -> {
  227. vars.remove(v);
  228. values.remove(v.name);
  229. interp.variables.remove(v.name);
  230. ectx.rebuildProperties();
  231. }},
  232. ]);
  233. return false;
  234. });
  235. }
  236. var selt = props.find("#script");
  237. var editor = new hide.comp.ScriptEditor(this.script, selt, selt);
  238. editor.onSave = function() {
  239. script = editor.code;
  240. parsedExpr = null;
  241. interp = null;
  242. };
  243. }
  244. #end
  245. static var _ = Library.register("rfx.configurator", Configurator);
  246. }