浏览代码

RendererFX: add spatial renderer FX

lviguier 3 月之前
父节点
当前提交
a3ce87ceac
共有 2 个文件被更改,包括 321 次插入0 次删除
  1. 1 0
      hrt/prefab/RenderProps.hx
  2. 320 0
      hrt/prefab/rfx/SpatialRendererFX.hx

+ 1 - 0
hrt/prefab/RenderProps.hx

@@ -160,6 +160,7 @@ class RenderProps extends Object3D {
 	override function getHideProps() : hide.prefab.HideProps {
 		return { icon : "sun-o", name : "RenderProps", allowChildren : function(p) {
 			return Prefab.isOfType(p,hrt.prefab.rfx.RendererFX)
+				|| Prefab.isOfType(p,hrt.prefab.rfx.SpatialRendererFX)
 				|| Prefab.isOfType(p,Light)
 				|| Prefab.isOfType(p,hrt.prefab.l3d.Environment);
 		}};

+ 320 - 0
hrt/prefab/rfx/SpatialRendererFX.hx

@@ -0,0 +1,320 @@
+package hrt.prefab.rfx;
+
+class SPRFXObject extends h3d.scene.Object {
+	public var sprfx : SpatialRendererFX;
+	var renderer : h3d.scene.Renderer;
+
+	public function new(sprfx : SpatialRendererFX, ?parent : h3d.scene.Object) {
+		super(parent);
+		this.sprfx = sprfx;
+	}
+
+	override function sync(ctx : h3d.scene.RenderContext) {
+		super.sync(ctx);
+
+		if (renderer == null) {
+			this.renderer = ctx.scene.renderer;
+			this.renderer.effects.push(@:privateAccess sprfx.instance);
+		}
+	}
+
+	override function onRemove() {
+		super.onRemove();
+		this.renderer.effects.remove(sprfx);
+	}
+}
+
+typedef SPRFXDebug = {
+	var color : Int;
+	var mesh : h3d.scene.Mesh;
+}
+
+enum SPRFXShape {
+	Sphere(radius : Float);
+	Box(width : Float, height: Float);
+}
+
+class SpatialRendererFX extends Object3D implements h3d.impl.RendererFX {
+	@:c public var innerShape : SPRFXShape;
+	@:c public var outerShape : SPRFXShape;
+
+	@:s var enableInEditor = true;
+
+	var cam : h3d.Camera;
+
+	// Debug
+	@:s var debug : Bool = false;
+	var innerShapeDebug = { color : 0xFFFF00FF, mesh : null };
+	var outerShapeDebug = { color : 0xFF00EEFF, mesh : null };
+
+	var instance : SpatialRendererFX;
+
+	public function new(parent:Prefab, contextShared: ContextShared) {
+		super(parent, contextShared);
+		this.innerShape = Sphere(1);
+		this.outerShape = Sphere(1);
+	}
+
+	override function load(data: Dynamic) {
+		super.load(data);
+
+		function loadShape(shape : Dynamic) : SPRFXShape {
+			if (shape == null)
+				return Sphere(1);
+
+			return switch (shape.shape) {
+				case 0:
+					return Sphere(shape.radius);
+				case 1:
+					return Box(shape.width, shape.height);
+				default:
+					throw "not implemented";
+			};
+		}
+
+		this.innerShape = loadShape(data.innerShape);
+		this.outerShape = loadShape(data.outerShape);
+	}
+
+	override function copy(data: Dynamic) : Void {
+		super.copy(data);
+
+		var s : SpatialRendererFX = cast data;
+		this.load(s.save());
+	}
+
+	override function save() : Dynamic {
+		var obj = super.save();
+
+		function saveShape(shape : SPRFXShape) : Dynamic {
+			if (shape == null)
+				return { shape: 0, radius: 1 };
+
+			return switch (shape) {
+				case Sphere(radius):
+					{ shape: 0, radius: radius };
+				case Box(width, height):
+					{ shape: 1, width: width, height: height };
+				default:
+					throw { shape: 0, radius: 1 };
+			};
+		}
+
+		obj.innerShape = saveShape(this.innerShape);
+		obj.outerShape = saveShape(this.outerShape);
+		return obj;
+	}
+
+
+	public function start( r : h3d.scene.Renderer ) {
+	}
+
+	public function begin( r : h3d.scene.Renderer, step : h3d.impl.RendererFX.Step ) {
+	}
+
+	public function end( r : h3d.scene.Renderer, step : h3d.impl.RendererFX.Step ) {
+	}
+
+	inline function checkEnabled() {
+		return enabled #if editor && enableInEditor && !inGameOnly #end;
+	}
+
+	override function make( ?sh:hrt.prefab.Prefab.ContextMake ) : Prefab {
+		instance = cast this.clone();
+		// unlink this.props and instance.props for ScreenShaderGraph
+		// because props is cloned by ref
+		instance.props = {};
+
+		if (!shouldBeInstanciated())
+			return this;
+
+		makeInstance();
+		for (c in children)
+			makeChild(c);
+		postMakeInstance();
+		updateInstance();
+
+		return this;
+	}
+
+	override function makeObject(parent3d: h3d.scene.Object) {
+		var o = new SPRFXObject(this, parent3d);
+		return o;
+	}
+
+	override function updateInstance(?propName : String) {
+		if (instance != null) {
+			if (propName != null && propName != "props") {
+				Reflect.setField(instance, propName, Reflect.field(this, propName));
+				return;
+			}
+
+			for (f in Reflect.fields(this)) {
+				if (f != "props")
+					Reflect.setField(instance, f, Reflect.field(this, f));
+			}
+		}
+
+		function createPrim(shape : SPRFXShape) : h3d.prim.Primitive {
+			switch(shape) {
+				case Sphere(radius):
+					var prim = new h3d.prim.Sphere(radius, 64, 64);
+					prim.addNormals();
+					prim.addUVs();
+					return prim;
+				case Box(width, height):
+					var prim = new h3d.prim.Cube(width);
+					prim.addNormals();
+					prim.addUVs();
+					return prim;
+			}
+		}
+
+		function applyDebug(sprDebug : SPRFXDebug, shape : SPRFXShape) {
+			if (sprDebug != null) {
+				sprDebug.mesh.remove();
+				sprDebug.mesh = null;
+			}
+
+			if (!debug) return;
+
+			sprDebug.mesh = new h3d.scene.Mesh(createPrim(shape), local3d);
+			sprDebug.mesh.name = "SpatialRendererFXDebug";
+			sprDebug.mesh.material.mainPass.depth(true, LessEqual);
+			var s = new h3d.shader.AlphaMult();
+			s.alpha = 0.3;
+			sprDebug.mesh.material.mainPass.addShader(s);
+			sprDebug.mesh.material.blendMode = Alpha;
+			sprDebug.mesh.material.mainPass.setPassName("overlay");
+			sprDebug.mesh.ignoreParentTransform = false;
+			var c = hrt.impl.ColorSpace.Color.fromInt(sprDebug.color);
+			hrt.impl.ColorSpace.iRGBtofRGB(c, sprDebug.mesh.material.color);
+		}
+
+		applyDebug(innerShapeDebug, this.innerShape);
+		applyDebug(outerShapeDebug, this.outerShape);
+	}
+
+	override function dispose() {
+		if (this.instance != null) {
+			var scene = this.instance.shared.root3d?.getScene();
+
+			if(scene != null)
+				scene.renderer.effects.remove(this.instance);
+
+			var i = this.instance;
+			this.instance = null;
+			i.dispose();
+		}
+
+		super.dispose();
+	}
+
+	#if editor
+	override function getHideProps() : hide.prefab.HideProps {
+		return { name : Type.getClassName(Type.getClass(this)).split(".").pop(), icon : "plus-circle" };
+	}
+
+	override function edit(ctx:hide.prefab.EditContext) {
+		var e = new hide.Element('
+		<div class="group" name="Spatial Renderer FX">
+			<dl>
+				<dt>Debug</dt><dd><input type="checkbox" field="debug"/></dd>
+				<dt>Shape</dt><dd><select id="shape-sel"></select></dd>
+				<div id="params">
+				</div>
+			</dl>
+		</div>
+		');
+
+		var shapeSel = e.find("#shape-sel");
+		for (idx => el in Type.getEnumConstructs(SPRFXShape))
+			shapeSel.append(new hide.Element('<option value="$el" ${ Type.enumIndex(this.innerShape) == idx ? 'selected' : ''}>$el</option>'));
+		shapeSel.on("change", function(e) {
+			var prevInner = this.innerShape;
+			var prevOuter = this.outerShape;
+
+			switch (shapeSel.val()) {
+				case "Sphere":
+					this.innerShape = Sphere(10);
+					this.outerShape = Sphere(15);
+				case "Box":
+					this.innerShape = Box(10, 10);
+					this.outerShape = Box(15, 15);
+				default:
+			}
+
+			var newInner = this.innerShape;
+			var newOuter = this.outerShape;
+
+			ctx.properties.undo.change(Custom(function(undo) {
+				this.innerShape = undo ? prevInner : newInner;
+				this.outerShape = undo ? prevOuter : newOuter;
+				ctx.rebuildProperties();
+			}));
+
+			ctx.rebuildProperties();
+		});
+
+		var paramsEl = e.find("#params");
+		var param : hide.Element = null;
+		function onChange() {
+			var prevInner = this.innerShape;
+			var prevOuter = this.outerShape;
+
+			switch (this.innerShape) {
+				case Sphere(_):
+					this.innerShape = Sphere(Std.parseFloat(param.find('#innerRadius').val()));
+					this.outerShape = Sphere(Std.parseFloat(param.find('#outerRadius').val()));
+				case Box(_):
+			}
+
+			var newInner = this.innerShape;
+			var newOuter = this.outerShape;
+
+			ctx.properties.undo.change(Custom(function(undo) {
+				this.innerShape = undo ? prevInner : newInner;
+				this.outerShape = undo ? prevOuter : newOuter;
+				ctx.rebuildProperties();
+				this.updateInstance();
+			}));
+
+			this.updateInstance();
+		}
+		switch ([this.innerShape, this.outerShape]) {
+			case [Sphere(r1), Sphere(r2)]:
+				param = new hide.Element('<div>
+					<dt>Inner radius</dt><dd><input type="number" id="innerRadius"/></dd>
+					<dt>Outer Radius</dt><dd><input type="number" id="outerRadius"/></dd>
+				</div>');
+
+				function setup(e : hide.Element, value : Dynamic) {
+					e.val(value);
+					e.on("change", () -> onChange());
+				}
+				setup(param.find('#innerRadius'), r1);
+				setup(param.find('#outerRadius'), r2);
+
+			default:
+				param = new hide.Element('<div><dt></dt><dd><p>Not supported</p></dd></div>');
+		}
+		paramsEl.append(param);
+		ctx.properties.add(e, this);
+	}
+	#end
+
+	public function getFactor() : Float {
+		if (cam == null)
+			cam = local3d.getScene().camera;
+		var distance = (local3d.getAbsPos().getPosition() - cam.pos).length();
+
+		switch ([innerShape, outerShape]) {
+			case [Sphere(r1), Sphere(r2)]:
+				if (distance < r1) return 1;
+				if (distance > r2) return 0;
+				return 1 - hxd.Math.clamp((distance - r1) / (r2 - r1), 0, 1);
+			default:
+				return 0.;
+		}
+	}
+}