浏览代码

MeshSpray prefab

Tom SPIRA 6 年之前
父节点
当前提交
e1d160378d
共有 5 个文件被更改,包括 317 次插入14 次删除
  1. 3 0
      bin/style.css
  2. 6 0
      bin/style.less
  3. 19 13
      hide/comp/SceneEditor.hx
  4. 1 1
      hide/view/l3d/Level3D.hx
  5. 288 0
      hrt/prefab/l3d/MeshSpray.hx

+ 3 - 0
bin/style.css

@@ -578,6 +578,9 @@ input[type=checkbox]:checked:after {
 .hide-properties .group > .content {
   margin-bottom: 5px;
 }
+.hide-properties .btn-list * {
+  margin-bottom: 5px;
+}
 .hide-properties .error {
   color: red;
 }

+ 6 - 0
bin/style.less

@@ -631,6 +631,12 @@ input[type=checkbox] {
 		margin-bottom : 5px;
 	}
 
+	.btn-list {
+		* {
+			margin-bottom: 5px;
+		}
+	}
+
 	.error {
 		color: red;
 	}

+ 19 - 13
hide/comp/SceneEditor.hx

@@ -951,22 +951,28 @@ class SceneEditor {
 		scene.init(ctx.local3d);
 	}
 
-	public function addObject(e : PrefabElement) {
-		makeInstance(e);
-		refresh(Partial, () -> selectObjects([e]));
+	public function addObject(elts : Array<PrefabElement>) {
+		for (e in elts) {
+			makeInstance(e);
+		}
+		refresh(Partial, () -> selectObjects(elts));
 		undo.change(Custom(function(undo) {
 			var fullRefresh = false;
 			if(undo) {
 				deselect();
-				if(!removeInstance(e))
-					fullRefresh = true;
-				e.parent.children.remove(e);
+				for (e in elts) {
+					if(!removeInstance(e))
+						fullRefresh = true;
+					e.parent.children.remove(e);
+				}
 				refresh(fullRefresh ? Full : Partial);
 			}
 			else {
-				e.parent.children.push(e);
-				makeInstance(e);
-				refresh(Partial, () -> selectObjects([e]));
+				for (e in elts) {
+					e.parent.children.push(e);
+					makeInstance(e);
+				}
+				refresh(Partial, () -> selectObjects(elts));
 			}
 		}));
 	}
@@ -1370,7 +1376,7 @@ class SceneEditor {
 			ref.rotationY = obj3d.rotationY;
 			ref.rotationZ = obj3d.rotationZ;
 		}
-		addObject(ref);
+		addObject([ref]);
 	}
 
 	function duplicate(thenMove: Bool) {
@@ -1680,10 +1686,10 @@ class SceneEditor {
 					ide.chooseFile(pmodel.inf.fileSource, function(path) {
 						if( path == null ) return;
 						var p = make(path);
-						addObject(p);
+						addObject([p]);
 					});
 				else
-					addObject(make());
+					addObject([make()]);
 			}
 		};
 	}
@@ -1698,7 +1704,7 @@ class SceneEditor {
 					var s = new hrt.prefab.Shader(parentElt);
 					s.source = path;
 					s.name = name;
-					addObject(s);
+					addObject([s]);
 				}
 			}
 		}

+ 1 - 1
hide/view/l3d/Level3D.hx

@@ -170,7 +170,7 @@ private class Level3DSceneEditor extends hide.comp.SceneEditor {
 			}
 
 			autoName(p);
-			haxe.Timer.delay(addObject.bind(p), 0);
+			haxe.Timer.delay(addObject.bind([p]), 0);
 		}
 
 		newItems = newItems.concat(super.getNewContextMenu(current, onMake));

+ 288 - 0
hrt/prefab/l3d/MeshSpray.hx

@@ -0,0 +1,288 @@
+package hrt.prefab.l3d;
+
+import h3d.Vector;
+import hxd.Key as K;
+
+class MeshSpray extends Object3D {
+
+	#if editor
+
+	var meshes : Array<String> = [];
+	var sceneEditor : hide.comp.SceneEditor;
+
+	var density : Int = 10;
+	var radius : Float = 5.0;
+	var rotation : Float = 0.0;
+	var rotationOffset : Float = 10.0;
+
+	var sprayEnable : Bool = false;
+	var interactive : h2d.Interactive;
+	var gBrush : h3d.scene.Graphics;
+
+	var lastSpray : Float = 0;
+
+	override function save() {
+		var obj : Dynamic = super.save();
+		obj.meshes = meshes;
+		return obj;
+	}
+
+	override function load( obj : Dynamic ) {
+		super.load(obj);
+		if (obj.meshes != null)
+			meshes = obj.meshes;
+	}
+
+	override function getHideProps() : HideProps {
+		return { icon : "paint-brush", name : "MeshSpray" };
+	}
+
+	function getHMD( ctx : Context, meshPath : String ) : hxd.fmt.hmd.Library {
+		if( meshPath == null ) return null;
+		return @:privateAccess ctx.shared.cache.loadLibrary(hxd.res.Loader.currentInstance.load(meshPath).toModel());
+	}
+
+	function extractMeshName( path : String ) : String {
+		if( path == null ) return "None";
+		var childParts = path.split("/");
+		return childParts[childParts.length - 1].split(".")[0];
+	}
+
+	override function edit( ctx : EditContext ) {
+		sceneEditor = ctx.scene.editor;
+
+		var props = new hide.Element('<div class="group" name="Meshes"></div>');
+		var selectElement = new hide.Element('<select multiple size="6" style="width: 300px" ></select>').appendTo(props);
+		for (m in meshes) {
+			addMeshPath(m);
+			selectElement.append(new hide.Element('<option value="${m}">${extractMeshName(m)}</option>'));
+		}
+		var options = new hide.Element('<div class="btn-list" align="center" ></div>').appendTo(props);
+
+		var selectAllBtn = new hide.Element('<input type="button" value="Select all" />').appendTo(options);
+		var addBtn = new hide.Element('<input type="button" value="Add" >').appendTo(options);
+		var removeBtn = new hide.Element('<input type="button" value="Remove" />').appendTo(options);
+		var cleanBtn = new hide.Element('<input type="button" value="Remove all meshes" />').appendTo(options);
+		new hide.Element('<br /><b><i>Hold down SHIFT to remove meshes</i></b>').appendTo(options);
+
+		selectAllBtn.on("click", function() {
+			var options = selectElement.children().elements();
+			for (opt in options) {
+				opt.prop("selected", true);
+			}
+		});
+		addBtn.on("click", function () {
+			hide.Ide.inst.chooseFile(["fbx"], function(path) {
+				if (path != null && path.length > 0) {
+					addMeshPath(path);
+					selectElement.append(new hide.Element('<option value="${path}">${extractMeshName(path)}</option>'));
+				}
+			});
+		});
+		removeBtn.on("click", function () {
+			var options = selectElement.children().elements();
+			for (opt in options) {
+				if (opt.prop("selected")) {
+					removeMeshPath(opt.val());
+					opt.remove();
+				}
+			}
+		});
+		cleanBtn.on("click", function() {
+			if (hide.Ide.inst.confirm("Are you sure to remove all meshes for this MeshSpray ?")) {
+				sceneEditor.deleteElements(children.copy());
+				sceneEditor.selectObjects([this]);
+			}
+		});
+
+
+		ctx.properties.add(props, this, function(pname) {});
+
+		var optionsGroup = new hide.Element('<div class="group" name="Options"><dl></dl></div>');
+		optionsGroup.append(hide.comp.PropsEditor.makePropsList([
+				{ name: "density", t: PInt(1, 25), def: density },
+				{ name: "radius", t: PFloat(0, 50), def: radius },
+				{ name: "rotation", t: PFloat(0, 180), def: rotation },
+				{ name: "rotationOffset", t: PFloat(0, 30), def: rotationOffset }
+			]));
+		ctx.properties.add(optionsGroup, this, function(pname) {  });
+	}
+
+
+	override function setSelected( ctx : Context, b : Bool ) {
+		if( b ) {
+			var s2d = @:privateAccess ctx.local2d.getScene();
+			interactive = new h2d.Interactive(10000, 10000, s2d);
+			interactive.propagateEvents = true;
+			interactive.cancelEvents = false;
+
+			interactive.onWheel = function(e) {
+
+			};
+
+			interactive.onPush = function(e) {
+				e.propagate = false;
+				sprayEnable = true;
+
+			};
+
+			interactive.onRelease = function(e) {
+				e.propagate = false;
+				sprayEnable = false;
+			};
+
+			interactive.onMove = function(e) {
+				var worldPos = sceneEditor.screenToWorld(s2d.mouseX, s2d.mouseY);
+
+				drawCircle(ctx, worldPos, radius, 2, 16711680);
+
+				if( K.isDown( K.MOUSE_LEFT) ) {
+					e.propagate = false;
+
+					if (sprayEnable) {
+
+						if (lastSpray < Date.now().getTime() - 50) {
+							if( K.isDown( K.SHIFT) ) {
+								removeMeshesAround(ctx, worldPos);
+							} else {
+								addMeshesAround(ctx, worldPos);
+							}
+							lastSpray = Date.now().getTime();
+						}
+					}
+				}
+			};
+		}
+		else{
+			if( interactive != null ) interactive.remove();
+		}
+	}
+
+	function addMeshPath(path : String) {
+		if (meshes.indexOf(path) == -1)
+			meshes.push(path);
+	}
+
+	function removeMeshPath(path : String) {
+		meshes.remove(path);
+	}
+
+	function addMeshesAround(ctx : Context, point : h3d.col.Point) {
+		if (meshes.length == 0) {
+			throw "There is no meshes";
+		}
+		var nbMeshesInZone = 0;
+		var vecRelat = point.toVector();
+		var transform = this.getTransform().clone();
+		transform.invert();
+		vecRelat.transform3x4(transform);
+		var point2d = new h2d.col.Point(vecRelat.x, vecRelat.y);
+
+		var minDistanceBetweenMeshesSq = (radius * radius / density);
+
+		var currentPivots : Array<h2d.col.Point> = [];
+		inline function distance(x1 : Float, y1 : Float, x2 : Float, y2 : Float) return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
+		var fakeRadius = radius * radius + minDistanceBetweenMeshesSq;
+		for (child in children) {
+			var model = child.to(hrt.prefab.Object3D);
+			if (distance(point2d.x, point2d.y, model.x, model.y) < fakeRadius) {
+				nbMeshesInZone++;
+				currentPivots.push(new h2d.col.Point(model.x, model.y));
+			}
+		}
+		var nbMeshesToPlace = density - nbMeshesInZone;
+		if (nbMeshesToPlace > 0) {
+			var models : Array<hrt.prefab.Prefab> = [];
+
+			var random = new hxd.Rand(Std.random(0xFFFFFF));
+
+			while (nbMeshesToPlace-- > 0) {
+				var nbTry = 5;
+				var position : h3d.col.Point;
+				do {
+					var randomRadius = radius*Math.sqrt(random.rand());
+					var angle = random.rand() * 2*Math.PI;
+
+					position = new h3d.col.Point(point.x + randomRadius*Math.cos(angle), point.y + randomRadius*Math.sin(angle), 0);
+					var vecRelat = position.toVector();
+					vecRelat.transform3x4(transform);
+
+					var isNextTo = false;
+					for (cPivot in currentPivots) {
+						if (distance(vecRelat.x, vecRelat.y, cPivot.x, cPivot.y) <= minDistanceBetweenMeshesSq) {
+							isNextTo = true;
+							break;
+						}
+					}
+					if (!isNextTo) {
+						break;
+					}
+				} while (nbTry-- > 0);
+
+				var randRotationOffset = random.rand() * rotationOffset;
+				if (Std.random(2) == 0) {
+					randRotationOffset *= -1;
+				}
+				var rotationZ = ((rotation  + randRotationOffset) % 360)/360 * 2*Math.PI;
+
+				var model = new hrt.prefab.Model(this);
+				model.source = meshes[Std.random(meshes.length)];
+				model.name = extractMeshName(model.source);
+
+				var localMat = new h3d.Matrix();
+				localMat.initRotationZ(rotationZ);
+				position.z = sceneEditor.getZ(position.x, position.y);
+				localMat.setPosition(new Vector(position.x, position.y, position.z));
+				var invParent = getTransform().clone();
+				invParent.invert();
+				localMat.multiply(localMat, invParent);
+				model.setTransform(localMat);
+				models.push(model);
+				currentPivots.push(new h2d.col.Point(model.x, model.y));
+			}
+
+			sceneEditor.addObject(models);
+			sceneEditor.selectObjects([this]);
+		}
+	}
+
+	function removeMeshesAround(ctx : Context, point : h3d.col.Point) {
+		var vecRelat = point.toVector();
+		var transform = this.getTransform().clone();
+		transform.invert();
+		vecRelat.transform3x4(transform);
+		var point2d = new h2d.col.Point(vecRelat.x, vecRelat.y);
+
+		var childToRemove = [];
+		inline function distance(x1 : Float, y1 : Float, x2 : Float, y2 : Float) return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
+		var fakeRadius = radius * radius;
+		for (child in children) {
+			var model = child.to(hrt.prefab.Object3D);
+			if (distance(point2d.x, point2d.y, model.x, model.y) < fakeRadius) {
+				childToRemove.push(child);
+			}
+		}
+		sceneEditor.deleteElements(childToRemove);
+		sceneEditor.selectObjects([this]);
+	}
+
+	public function drawCircle(ctx : Context, origin: h3d.col.Point, radius: Float, thickness: Float, color) {
+		if (gBrush != null) gBrush.remove();
+		gBrush = new h3d.scene.Graphics(ctx.local3d);
+		gBrush.material.props = h3d.mat.MaterialSetup.current.getDefaults("fx");
+		gBrush.setPosition(origin.x, origin.y, sceneEditor.getZ(origin.x, origin.y) + 0.1);
+		gBrush.lineStyle(thickness, color, 1.0);
+		gBrush.moveTo(radius, 0, 0);
+		var nsegments = 32;
+		for(i in 0...nsegments) {
+			var theta = Math.PI * 2 * (i+1)/nsegments;
+			gBrush.lineTo(Math.cos(theta) * radius, Math.sin(theta) * radius, 0);
+		}
+
+		gBrush.lineStyle();
+	}
+
+	#end
+
+	static var _ = Library.register("MeshSpray", MeshSpray);
+}