gord0 2 Report post Posted March 13, 2018 Hi there, I'm on a project that is a sandbox instructors' tool for various airspace coordination scenarios (basically don't shoot down your own air support 101). The instructor can drag many things into the scene (various aircraft/artillery, marker gizmos, etc). One of the things they want to be able to do is mark "zones" in both the air and on the ground. Right now I've just put in some regular unity cubes with a 2 sided shader with transparency so they can see through them but also still see them when the camera is inside them. The user would just scale them as needed. The problem is they're still just scaled cubes. What they would like is the ability to make "corridors". So I back-engineered your example scene for extruding faces of a cube at runtime and the user can now resize the "zones" by extruding faces.... ....As it is right now I can only extrude entire faces, but I'm unable to branch off as is depicted in the image below. I'm not fully familiar with all the geometry terminology but I have gotten this far, would anyone be able to point me in the right direction or have examples of what I've described? I've been able to do it in the editor, but I need to know how to do it via C# at runtime. Any help is much appreciated PS I'm running Unity 5.6.2f1 and I don't have the liberty to change versions. Share this post Link to post Share on other sites
karl 321 Report post Posted March 14, 2018 It looks like so far you've just translated faces along their normal. To create branches, you need to extrude the face prior to translating. So using the RuntimeEdit example, you'd add currentSelection.pb.Extrude(new pb_Face[] { currentSelection.face }, ExtrudeMethod.IndividualFaces, Mathf.Epsilon); prior to translating the vertices. That creates new segments that you can then branch off however you'd like. 1 gord0 reacted to this Share this post Link to post Share on other sites
gord0 2 Report post Posted March 14, 2018 19 minutes ago, karl said: It looks like so far you've just translated faces along their normal. To create branches, you need to extrude the face prior to translating. So using the RuntimeEdit example, you'd add currentSelection.pb.Extrude(new pb_Face[] { currentSelection.face }, ExtrudeMethod.IndividualFaces, Mathf.Epsilon); prior to translating the vertices. That creates new segments that you can then branch off however you'd like. Cheers. It's a snow day for me so I won't be able to try this until tomorrow...unless that's a snow day too. I'll let you know how it works out. : ] 1 karl reacted to this Share this post Link to post Share on other sites
gord0 2 Report post Posted March 15, 2018 20 hours ago, karl said: It looks like so far you've just translated faces along their normal. To create branches, you need to extrude the face prior to translating. So using the RuntimeEdit example, you'd add currentSelection.pb.Extrude(new pb_Face[] { currentSelection.face }, ExtrudeMethod.IndividualFaces, Mathf.Epsilon); prior to translating the vertices. That creates new segments that you can then branch off however you'd like. There's no .Extrude method for pb_Object. At least not a public one. Share this post Link to post Share on other sites
karl 321 Report post Posted March 15, 2018 You'll need to include the ProBuilder2.MeshOperations namespace. 1 gord0 reacted to this Share this post Link to post Share on other sites
gord0 2 Report post Posted March 15, 2018 38 minutes ago, karl said: You'll need to include the ProBuilder2.MeshOperations namespace. That sorted out the syntax error, but I think I need to change when things are happening because I'm creating waaaay too much geometry. Also I think there's a texturing issue. In game view it appears as though I'm just pulling the face right off the cube but scene view shows that I am indeed doing what I wanted to, but way too many times. Here's the bit of code where it's happening: if (_preview!=null && Input.GetMouseButton(0)) { UpdateNodeRestrictions(); //check if behind face, if so use a nagative multiplier below Vector3 facePos = FaceCenter(_currentSelection.Face, _currentSelection.PB); Vector3 toWidget = (_widget.localPosition - facePos).normalized; bool behind = Mathf.Approximately(Vector3.Dot(toWidget, _localNormal), -1.0f); _currentSelection.PB.Extrude( new pb_Face[] { _currentSelection.Face }, ExtrudeMethod.IndividualFaces, Mathf.Epsilon); _currentSelection.PB.TranslateVertices( _currentSelection.Face.distinctIndices, _localNormal.normalized * (Vector3.Distance(_widget.localPosition, facePos)) * (behind ? -1.0f : 1.0f) ); // Refresh will update the Collision mesh volume, face UVs as applicatble, and normal information. _currentSelection.PB.Refresh(); // this create the selected face preview RefreshSelectedFacePreview(); } Share this post Link to post Share on other sites
karl 321 Report post Posted March 15, 2018 You're almost there, it's just missing a few parts. In psuedo-code, it should look like this: if (mouseDrag) { if (!wasDragging) { pb.Extrude(faces) pb.ToMesh(); pb.Refresh(); wasDragging = true; } pb.TranslateVertices(...); pb.Refresh(); } 1 gord0 reacted to this Share this post Link to post Share on other sites
gord0 2 Report post Posted March 15, 2018 24 minutes ago, karl said: You're almost there, it's just missing a few parts. In psuedo-code, it should look like this: if (mouseDrag) { if (!wasDragging) { pb.Extrude(faces) pb.ToMesh(); pb.Refresh(); wasDragging = true; } pb.TranslateVertices(...); pb.Refresh(); } W0000! Before you had replied I had sorted out the bool thing so that it only does it once per drag, but I didn't know that ToMesh() was needed. I take it that method is like a "commit changes" sort of thing? I went a little nuts testing but it works : ] Just one more thing though. Is there a way I can do this with my own rectangle (just like the preview plane) that isn't sized from the values of the face. I just size it myself and then use it like a cookie cutter to extrude with? As it is now, you kind of have to play a bit to get a face to be the right size to make a "corridor". Also the end user isn't going to have any comprehension of what's under the hood. Share this post Link to post Share on other sites
karl 321 Report post Posted March 16, 2018 Yes, ToMesh is what applies the mesh data stored in pb_Object to the actual mesh. Most mesh modification calls require ToMesh after (TranslateVertices is actually one of a small few that does not). 18 hours ago, gord0 said: Is there a way I can do this with my own rectangle (just like the preview plane) that isn't sized from the values of the face I don't follow, could you explain this further? Share this post Link to post Share on other sites
gord0 2 Report post Posted March 16, 2018 28 minutes ago, karl said: Yes, ToMesh is what applies the mesh data stored in pb_Object to the actual mesh. Most mesh modification calls require ToMesh after (TranslateVertices is actually one of a small few that does not). I don't follow, could you explain this further? In the picture below. Let the blue represent a face. Let the red represent the preview plane. Instead of having it sized to match the face, have it sized by the user in some way so they can just extrude whatever size of rectangle from wherever they place the preview plane. Programmatically we would be adding the necessary geometry for it to work. Is that doable with probuilder? Share this post Link to post Share on other sites
karl 321 Report post Posted March 16, 2018 I see. Yes, you could do an extrusion and scale to get the sizing, then a second extrude to pull the new section out. Share this post Link to post Share on other sites
gord0 2 Report post Posted March 16, 2018 1 hour ago, karl said: I see. Yes, you could do an extrusion and scale to get the sizing, then a second extrude to pull the new section out. So which overload of Extrude() would work? Would we not need to add vertices to the face to create the smaller face within the original face? I made another diagram using regular cubes to illustrate what I'm trying to do. Where the red plane is sized and placed by the user. Share this post Link to post Share on other sites
karl 321 Report post Posted March 16, 2018 You just extrude with no distance, then scale the face inward, then extrude again. Here's a gif showing what I mean. 1 gord0 reacted to this Share this post Link to post Share on other sites
gord0 2 Report post Posted March 16, 2018 19 minutes ago, karl said: You just extrude with no distance, then scale the face inward, then extrude again. Here's a gif showing what I mean. Ahhh. Nice. So what I'm going to do now is work on a way for the user to just draw the preview plane on a face (perhaps by holding a kb key and dragging the mouse). Then I can do as you've explained above. Instead of sizing the preview plane to match the face, I'll scale/position the face to match the preview plane. : ] 1 karl reacted to this Share this post Link to post Share on other sites
gord0 2 Report post Posted March 19, 2018 Alright, I've hit a wall that I can't see the other side of. The following is the code I'm using to allow the user to draw a plane on a pbObject. It gets called every frame if the user is pressing the right inputs. protected void DrawCustomPlane() { if (Input.GetMouseButtonUp(0)) //they let go of the button, lets get outta here { _customExtruding = false; return; } if (!Input.GetKey(KeyCode.LeftControl)) { return; } //they're not holding left control, so let's chill if (_customExtruderPlane==null) //if our plane is null { _ray = _cam.ScreenPointToRay(Input.mousePosition); //let's fire a ray to see if we can fix that if (Physics.Raycast(_ray, out _hit, Mathf.Infinity, _lm)) //if we hit something { if (_hit.collider.gameObject.GetComponent<pb_Object>()==null) { return; } //forget it, not pb object Vector3[] verts = new Vector3[4] //we'll start our 4 vertices at the hit point { new Vector3(_hit.point.x, _hit.point.y, _hit.point.z), new Vector3(_hit.point.x, _hit.point.y, _hit.point.z), new Vector3(_hit.point.x, _hit.point.y, _hit.point.z), new Vector3(_hit.point.x, _hit.point.y, _hit.point.z) }; int[] indices = new int[verts.Length]; //define our indices for (int iVert=0; iVert<verts.Length; iVert++) { indices[iVert] = iVert; } //.. //now we can make the plane _customExtruderPlane = pb_Object.CreateInstanceWithVerticesFaces(verts, new pb_Face[] { new pb_Face(indices) }); _customExtruderPlane.SetFaceMaterial(_customExtruderPlane.faces, PreviewMaterial); _customExtruderPlane.ToMesh(); _customExtruderPlane.Refresh(); //move it just a little in front of whatever the ray hit _customExtruderPlane.gameObject.transform.position += _hit.normal * 0.1f; } } if (_customExtruderPlane==null) { return; } //i guess we missed else //lets move our vertices according to mouse movement { /*i'm assuming the following is what each vertice is, i could very well be wrong, we'll find out soon enough verts[0] -- upper left corner verts[1] -- upper right corner verts[2] -- lower right corner verts[3] -- lower left corner */ Vector3[] verts = _customExtruderPlane.vertices; verts[1].x += Input.GetAxis("Mouse X") * CustomExtrudeDragSpeed * Time.deltaTime; verts[2].x = verts[1].x; verts[2].y += Input.GetAxis("Mouse Y") * CustomExtrudeDragSpeed * Time.deltaTime; verts[3].y = verts[2].y; _customExtruderPlane.SetVertices(verts); _customExtruderPlane.ToMesh(); _customExtruderPlane.Refresh(); } } I get an index out of range error at this line: _customExtruderPlane = pb_Object.CreateInstanceWithVerticesFaces(verts, new pb_Face[] { new pb_Face(indices) }); Quote IndexOutOfRangeException: Array index is out of range. ProBuilder2.Common.pb_Face.CacheEdges () ProBuilder2.Common.pb_Face.RebuildCaches () ProBuilder2.Common.pb_Face.SetIndices (System.Int32[] i) ProBuilder2.Common.pb_Face..ctor (System.Int32[] i) PBCorridor.DrawCustomPlane () (at Assets/Scripts/DraggableScripts/Shapes/PBCorridor.cs:166) PBCorridor.MainLogic () (at Assets/Scripts/DraggableScripts/Shapes/PBCorridor.cs:235) I can't dig into the PB scripts to see why. Would you be able to shine some light? Cheers Share this post Link to post Share on other sites
gord0 2 Report post Posted March 20, 2018 I've tracked down your git repo, but even in that the code I need to look at is inside a .dll . : ( Share this post Link to post Share on other sites
gord0 2 Report post Posted March 20, 2018 Figured it out, it needs 6 vertices and I was giving it 4. It's creating the plane now, but I need to sort out placement. It's not appearing where it should be. I figure it's a local vs world thing. Share this post Link to post Share on other sites
gord0 2 Report post Posted March 20, 2018 So, how does one go about scaling a pb_Face via code? Share this post Link to post Share on other sites
karl 321 Report post Posted March 23, 2018 If you've not already found it, you may find our repository of example code helpful. https://github.com/Unity-Technologies/ProBuilder-API-Examples Share this post Link to post Share on other sites
gord0 2 Report post Posted March 26, 2018 On 23/03/2018 at 6:48 PM, karl said: If you've not already found it, you may find our repository of example code helpful. https://github.com/Unity-Technologies/ProBuilder-API-Examples Just got back in from the weekend. I see you've added more stuff to the repo since I last visited a week or two ago. I've made some progress on my side but still stuck with some things. I'll take a look on the repo and report back later. Share this post Link to post Share on other sites
gord0 2 Report post Posted March 26, 2018 Alright, so nothing in there really helps (that I can tell). So right now I've got it doing almost what I want but I'm experiencing geometry glitches. I've stripped out as much of my project as I could while keeping code in question functioning and placed it in a zip which I've attached to this post (CorridorTest3.7z). The code that's doing the work in question is in PBCorridor.cs . The scene is PBCorridorTest3 . You'll need to be quite careful as I've only put in super basic functionality and it's easy to break things. First you'll want to drag out a corridor: It should look like this, don't click anywhere just yet: Now move the cursor to somewhere on the face that is facing the camera. Hold left control and left click and drag the mouse to draw a rectangle on the face (might want to have left control down before the left click): When you let go, the rectangle will disappear (at least for now it does, later I'll have it auto-select). Click roughly near the center of the rectangle you drew. A single arrow should appear with your rectangle highlighted: Now you'll want to move the camera to see better (camera controls are same as scene view (right click--rotate, middle mouse down--pan/strafe, scroll wheel--zoom). Then left click on arrow and pull it: Now you're thinking, so you've got it. What's the problem? Well if you try this on any other face (including a second time on the face you already used) the geometry gets messed up causing faces to no longer be visible. Also, in the screen shot above, it actually has one tiny problem too (the face on the back side that is not in view of the camera is invisible). This is the point where I no longer know what to do. Any help would be much appreciated. : ] Share this post Link to post Share on other sites
gord0 2 Report post Posted March 27, 2018 I'm super stuck. Don't know how to sort this out. Does probuilder have the ability to "merge" meshes? So I could just merge the user drawn plane (pb_Object) into the target object (pb_Object) so that I don't have to do all this manual jiggering around? Or do you know another way to achieve the desired result? Share this post Link to post Share on other sites
karl 321 Report post Posted March 28, 2018 Can you condense the code to the specific problem area? It would be much easier for me to help pick out the issue. Yes, you can merge meshes using the MeshOps namespace. 1 gord0 reacted to this Share this post Link to post Share on other sites
gord0 2 Report post Posted March 28, 2018 3 minutes ago, karl said: Can you condense the code to the specific problem area? It would be much easier for me to help pick out the issue. Yes, you can merge meshes using the MeshOps namespace. I'm not at work today, but the problem is most likely in CommenceModifications(). The reason I asked about a merging functionality is that it could probably take a huge chunk of the work out of my hands. If I were to place a plane on a face of a cube, said plane is smaller dimensions than the face, and merge would that plane become a face on the cube? Share this post Link to post Share on other sites
karl 321 Report post Posted March 28, 2018 1 hour ago, gord0 said: The reason I asked about a merging functionality is that it could probably take a huge chunk of the work out of my hands. If I were to place a plane on a face of a cube, said plane is smaller dimensions than the face, and merge would that plane become a face on the cube? Yes, but you'd end up with two faces sandwiched next to each other. If that works for you, it would be fine to use use that approach. I also just remembered that I did a very similar thing for an article a few years ago. I can't find the article, but the example code is here. https://github.com/karl-/pipedreams 1 gord0 reacted to this Share this post Link to post Share on other sites