Jump to content

Recommended Posts

2 minutes ago, karl said:

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

Cool. I'll check that out when I'm back at work tomorrow. : ]

Share this post


Link to post
Share on other sites

Sorry, I was off work longer than I knew I was going to be. Anyway, I've got that project opened up. It didn't have the probuilder stuff with it so I imported it. After that I now have " The type or namespace name `Math' does not exist in the namespace `ProBuilder2'. Are you missing an assembly reference?" coming from Pipe.cs on line 5:

using ProBuilder2.Math;

If I comment that out, the next problem is "The name `pb_Shape_Generator' does not exist in the current context" coming from PipeSpawner.cs at line 238:

pb_Object pb = pb_Shape_Generator.CubeGenerator(Vector3.one * pipeSize);

I imagine this is due to deprecation? How might I sort this out?

Share this post


Link to post
Share on other sites
1 hour ago, karl said:

Yeah, these things have moved around quite a bit since that was written. Math was moved into Core, and I think the shape gen class is now just pb_ShapeGenerator.

Cheers, that sorted that out. Looking at code now.

Share this post


Link to post
Share on other sites

Alright, so is this what's going on with the pipes?

Untitled.thumb.png.a1e898aef6380dfd35adf724978dd03c.png

If so, is this not just an automated version of the example that comes with probuilder that I started with?

Share this post


Link to post
Share on other sites

Yes, precisely. It seems like you've got the parts mostly correct, now it's just a matter of making sure that the pb_Object mesh state is set after you do the modifications. So looking at this as an example you can parse out when the appropriate times to call Extrude, ToMesh, and Refresh.

Share this post


Link to post
Share on other sites
19 hours ago, karl said:

Yes, precisely. It seems like you've got the parts mostly correct, now it's just a matter of making sure that the pb_Object mesh state is set after you do the modifications. So looking at this as an example you can parse out when the appropriate times to call Extrude, ToMesh, and Refresh.

I've added a couple more ToMesh() and Refesh()es so that each time I Extrude(), SetVertices(), ReverseIndices(), and TranslateVertices() I call those two methods immediately after. Seems to have been redundant though because my result is the same as before :(

    protected void CommenceModifications()
    {
        Vector3 facePos = FaceCenter(_currentSelection.Face, _currentSelection.PB); //get the center point of selected face
        Vector3 toWidget = (_widget.localPosition - facePos).normalized; //get direction from face center to widget
        bool behind = Mathf.Approximately(Vector3.Dot(toWidget, _localNormal), -1.0f); //determine if widget is in front or behind face

        if (_canExtrude) //may we extrude?
        {
            //make it so
            _currentSelection.PB.Extrude(new pb_Face[] { _currentSelection.Face },
                                         ExtrudeMethod.IndividualFaces,
                                         !_specifiedFaceSelect ? Mathf.Epsilon : 0.0f); //epsilon if not custom face, otherwise 0
            _currentSelection.PB.ToMesh();
            _currentSelection.PB.Refresh();

            if (_specifiedFaceSelect) //is this a user defined face?
            {
                //move their plane back to be flush with the actual face they want to extrude from
                _customExtruderPlane.gameObject.transform.position -=
                                                                _customExtruderPlane.gameObject.transform.forward * 0.1f;

                //extrude again with 0 distance
                _currentSelection.PB.Extrude(new pb_Face[] { _currentSelection.Face },
                                         ExtrudeMethod.IndividualFaces,
                                         0.0f);
                _currentSelection.PB.ToMesh();
                _currentSelection.PB.Refresh();

                Vector3[] verts = _currentSelection.PB.vertices; //grab all our verts
                int[] indices = _currentSelection.Face.indices /*distinctIndices*/; //grab the indices of the current face
                Vector3[] toVerts = _customExtruderPlane.vertices; //grab the verts of the user defined plane

                //place markers to represent the postions of the user defined plane verts
                for (int iMarker = 0; iMarker < _faceScalerMarkers.Length; iMarker++)
                {
                    //child marker to user defined plane
                    _faceScalerMarkers[iMarker].parent = _customExtruderPlane.gameObject.transform;
                    //place marker at user defined plane's iMarker vertice position
                    _faceScalerMarkers[iMarker].localPosition = toVerts[iMarker];
                    _faceScalerMarkers[iMarker].parent = transform; //child marker to us
                }

                //set current face vertices to the position of the closest marker (to retain wind order)
                for (int iVert = 0; iVert < indices.Length; iVert++)
                {
                    verts[indices[iVert]] = ClosestScalerMarkerPos(verts[indices[iVert]]);
                }
                _currentSelection.PB.SetVertices(verts); //apply new vertice positions
                _currentSelection.PB.ToMesh();
                _currentSelection.PB.Refresh();
                _currentSelection.Face.ReverseIndices(); //reverse the indices so we can see the face (for some reason backwards)
                _currentSelection.PB.ToMesh();
                _currentSelection.PB.Refresh();

                Destroy(_customExtruderPlane.gameObject); //destroy user defined plane

                for (int iMarker = 0; iMarker < _faceScalerMarkers.Length; iMarker++) //unchild markers
                {
                    _faceScalerMarkers[iMarker].parent = null;
                }
            }

            _currentSelection.PB.ToMesh(); //apply changes
            _currentSelection.PB.Refresh(); //..       
            _canExtrude = false; //no more extuding for now
        }

        if (!_specifiedFaceSelect) //if this was a not a user defined face, translate the verts based on widget pos
        {
            _currentSelection.PB.TranslateVertices(
                    _currentSelection.Face.distinctIndices,
                    _localNormal.normalized * (Vector3.Distance(_widget.localPosition, facePos)) * (behind ? -1.0f : 1.0f)
                                                  );
            _currentSelection.PB.ToMesh();
            _currentSelection.PB.Refresh();
        }
        else //otherwise
        {
            _specifiedFaceSelect = false; //we're done with the user defined face
            _destroyPreview = true; //destroy the preview plane
            _waitAFrame = true; //we should probably wait a frame before we do anything else
        }

        // Refresh will update the Collision mesh volume, face UVs as applicatble, and normal information.
        _currentSelection.PB.Refresh();

        // this creates the selected face preview
        RefreshSelectedFacePreview();
    }

 

Share this post


Link to post
Share on other sites

Alright I'm getting better results using a different approach. The new problem is, what is this diagonal plain on the inside of the "corridor"? I've used a 2 sided transparent material so you can see (it seems your attachment system is broken so I'm using external links):

https://pasteboard.co/HfQ4Xja.png

https://pasteboard.co/HfQ5Ahq.png

 

This is how I'm achieving it:

protected void CommenceModifications()
    {
        Vector3 facePos = FaceCenter(_currentSelection.Face, _currentSelection.PB); //get the center point of selected face
        Vector3 toWidget = (_widget.localPosition - facePos).normalized; //get direction from face center to widget
        bool behind = Mathf.Approximately(Vector3.Dot(toWidget, _localNormal), -1.0f); //determine if widget is in front or behind face

        if (_canExtrude) //may we extrude?
        {
            //make it so
            _currentSelection.PB.Extrude(new pb_Face[] { _currentSelection.Face },
                                         ExtrudeMethod.IndividualFaces,
                                         !_specifiedFaceSelect ? Mathf.Epsilon : 0.0f); //epsilon if not custom face, otherwise 0
            _currentSelection.PB.ToMesh();
            _currentSelection.PB.Refresh();

            if (_specifiedFaceSelect) //is this a user defined face?
            {
                //move their plane back to be flush with the actual face they want to extrude from
                _customExtruderPlane.gameObject.transform.position -=
                                                            _customExtruderPlane.gameObject.transform.forward * 0.1f;

                Transform t = _customExtruderPlane.gameObject.transform;
                if (Mathf.Approximately(t.forward.z, 1.0f) || Mathf.Approximately(t.forward.x, 1.0f) ||
                    Mathf.Approximately(t.forward.x, -1.0f) || Mathf.Approximately(t.forward.y, 1.0f) ||
                    Mathf.Approximately(t.forward.y, -1.0f))
                {
                    _customExtruderPlane.faces[0].ReverseIndices();
                }
                t.parent = _currentSelection.PB.gameObject.transform;

                Vector3[] offSetVerts = _customExtruderPlane.vertices;
                float xDist = Vector3.Distance(offSetVerts[0], offSetVerts[2]);
                float yDist = Vector3.Distance(offSetVerts[0], offSetVerts[1]);
                for (int iVert=0; iVert<offSetVerts.Length; iVert++)
                {
                    offSetVerts[iVert] += t.localPosition;
                    if (Mathf.Approximately(t.forward.z, -1.0f)) { offSetVerts[iVert].x += xDist; }
                    
                    /*if (iVert==5) //touching: 0, 2, 5
                    {         //not touching: 1, 3, 4
                        Transform go = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform;
                        go.parent = _currentSelection.PB.gameObject.transform;
                        go.localPosition = offSetVerts[iVert];
                    }*/
                    if ((iVert!=0 && iVert!=1 && iVert!=3) &&
                             (Mathf.Approximately(t.forward.x, 1.0f) || Mathf.Approximately(t.forward.x, -1.0f)))
                    {
                        offSetVerts[iVert].x = offSetVerts[0].x;
                        offSetVerts[iVert].z += xDist * (Mathf.Approximately(t.forward.x, -1.0f) ? -1.0f : 1.0f);
                    }
                    if ((iVert!=0 && iVert!=2 && iVert!=5) &&
                        (Mathf.Approximately(t.forward.y, 1.0f) ||Mathf.Approximately(t.forward.y, -1.0f)))
                    {
                        offSetVerts[iVert].y = offSetVerts[0].y;
                        offSetVerts[iVert].z += yDist * (Mathf.Approximately(t.forward.y, 1.0f) ? -1.0f : 1.0f);
                    }
                }
                t.parent = null;

                pb_Face pbf=_currentSelection.PB.AppendFace(offSetVerts,
                                                            new Color[1] { Color.white },
                                                            _customExtruderPlane.uv,
                                                            _customExtruderPlane.faces[0]);

                _manualFaceSelect = true;
                _manualFaceSelectPBO = _currentSelection.PB;
                _manualFaceSelectFace = pbf;

                /*Debug.LogWarning("_customExtruderPlane.gameObject.transform.forward: " + 
                                  _customExtruderPlane.gameObject.transform.forward);*/

                if (_customExtruderPlane != null) { Destroy(_customExtruderPlane.gameObject); }
            }

            _currentSelection.PB.ToMesh(); //apply changes
            _currentSelection.PB.Refresh(); //..       
            _canExtrude = false; //no more extuding for now
        }

        if (!_specifiedFaceSelect) //if this was a not a user defined face, translate the verts based on widget pos
        {
            _currentSelection.PB.TranslateVertices(
                    _currentSelection.Face.distinctIndices,
                    _localNormal.normalized * (Vector3.Distance(_widget.localPosition, facePos)) * (behind ? -1.0f : 1.0f)
                                                  );
            _currentSelection.PB.ToMesh();
            _currentSelection.PB.Refresh();
        }
        else //otherwise
        {
            _specifiedFaceSelect = false; //we're done with the user defined face
            _destroyPreview = true; //destroy the preview plane
            _waitAFrame = true; //we should probably wait a frame before we do anything else
        }

        // Refresh will update the Collision mesh volume, face UVs as applicatble, and normal information.
        _currentSelection.PB.Refresh();

        // this creates the selected face preview
        RefreshSelectedFacePreview();

Have you seen this kind of problem before?

Share this post


Link to post
Share on other sites

Yup, your only problem is the ExtrudeMethod being used. You want `ExtrudeMethod.FaceNormal`, not InvdividualFaces.

_currentSelection.PB.Extrude(new pb_Face[] { _currentSelection.Face },
                             ExtrudeMethod.FaceNormal,
                             !_specifiedFaceSelect ? Mathf.Epsilon : 0.0f); //epsilon if not custom face, otherwise 0

 

Share this post


Link to post
Share on other sites
11 minutes ago, karl said:

Yup, your only problem is the ExtrudeMethod being used. You want `ExtrudeMethod.FaceNormal`, not InvdividualFaces.


_currentSelection.PB.Extrude(new pb_Face[] { _currentSelection.Face },
                             ExtrudeMethod.FaceNormal,
                             !_specifiedFaceSelect ? Mathf.Epsilon : 0.0f); //epsilon if not custom face, otherwise 0

 

Ahhhh, I didn't even think to look there since I had been using that line as is for so long. Will give it a go in the morning!

Share this post


Link to post
Share on other sites
17 hours ago, karl said:

Yup, your only problem is the ExtrudeMethod being used. You want `ExtrudeMethod.FaceNormal`, not InvdividualFaces.


_currentSelection.PB.Extrude(new pb_Face[] { _currentSelection.Face },
                             ExtrudeMethod.FaceNormal,
                             !_specifiedFaceSelect ? Mathf.Epsilon : 0.0f); //epsilon if not custom face, otherwise 0

 

I was hopeful but...same issue. I also tried VertexNormal just see what would happen and it's also the same. All three methods seem to turn out the same in this scenario.

Share this post


Link to post
Share on other sites
59 minutes ago, karl said:

Okay, so the face that you're extruding from is two triangles then. What is the code that is creating the initial cube or face from which you extrude?

 protected void DrawCustomPlane()
    {
        if (Input.GetMouseButtonUp(0)) //they let go of the button, lets get outta here
        {
            _customExtruding = false;
            _meoc.RelinquishNonAxisMovement = false;
            _specifiedFaceSelect = true;
            CommenceModifications();
            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[6] //we'll start with all the vertices zero'd out
                {
                    Vector3.zero,
                    Vector3.zero,
                    Vector3.zero,
                    Vector3.zero,
                    Vector3.zero,
                    Vector3.zero
                };
                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 so we can see it proper
                _customExtruderPlane.gameObject.transform.position = _hit.point + (_hit.normal * 0.1f);
                _customExtruderPlane.gameObject.transform.forward = _hit.normal;
            }
        }

        if (_customExtruderPlane==null) { return; } //i guess we missed
        else //lets move our vertices according to mouse movement
        {
            /*
            verts[0] -- upper left corner       verts[0] -- upper left corner
            verts[1,3] -- lower left corner     verts[2,3] -- lower left corner
            verts[2,5] -- upper right corner    verts[1,4] -- upper right corner
            verts[4] -- lower right corner      verts[5] -- lower righ corner

            0   ---  2,5                        0   ---  1,4
            |        |                          |        |
            |        |                          |        |
            |        |                          |        |
            1,3 ---  4                          2,3 ---  5

            */

            _ray = _cam.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(_ray, out _hit, Mathf.Infinity, _lm)) //fire ray from the mouse
            {
                if (_hit.collider.gameObject.GetComponent<pb_Object>() == null) { return; } //forget it, not pb object

                _customPlaneMarker.position = _hit.point; //place our marker at the hit point
                _customPlaneMarker.parent = _customExtruderPlane.gameObject.transform; //child it to the plane

                Vector3[] verts = _customExtruderPlane.vertices; //grab verts from the plane

                verts[4] = _customPlaneMarker.localPosition; //place lower right corner at marker
                verts[4].z = verts[0].z; //copy z from upper right corner to lower left (remember plane is a little in front of hit)
                verts[2].x = verts[4].x; //make rest of verts form the rest of the rectangle based on 0 and 4
                verts[5].x = verts[4].x; //..
                verts[1].y = verts[4].y; //..
                verts[3].y = verts[4].y; //..

                //same as above but clockwise winding order
                //verts[5] = _customPlaneMarker.localPosition;
                //verts[5].z = verts[0].z;
                //verts[1].x = verts[5].x;
                //verts[4].x = verts[5].x;
                //verts[2].y = verts[5].y;
                //verts[3].y = verts[5].y;

                _customExtruderPlane.SetVertices(verts); //update the plane's vertices with our changes
                _customExtruderPlane.ToMesh(); //apply changes
                _customExtruderPlane.Refresh(); //..

                _customPlaneMarker.parent = null; //unchild marker from plane
            }
        }
    }

This is the code that allows the user to draw their own plane.

Share this post


Link to post
Share on other sites

The problem stems from how you're creating that initial face. The gist of it is that for the moment it assumes any triangle edge with unique vertex positions are perimeter edges. So when you extrude, ProBuilder thinks that the face you're extruding from is actually two faces. To fix this, just use 4 vertex positions instead of 6. Ex:

		/**
		 * Vertex positions for a quad are laid out like this. Note that positions 0 & 3 are reused.
		 *
		 * 2-----3
		 * |     |
		 * |     |
		 * 0-----1
		 *
		 */
		Vector3[] verts = new Vector3[4]
		{
			new Vector3(-1, -1f, 0f),
			new Vector3( 1, -1f, 0f),
			new Vector3(-1,  1f, 0f),
			new Vector3( 1,  1f, 0f),
		};

		/**
		 * Quad is two tris:
		 *
		 * 2←-------3
		 * |↖ ↘    ↑
		 * |  ↖ ↘  |
		 * ↓    ↖ ↘|
		 * 0-------→1
		 */
		var face = new pb_Face(new int[6]
		{
			0, 1, 2,
			1, 3, 2
		});

		face.material = pb_Constant.DefaultMaterial;
		var pb = pb_Object.CreateInstanceWithVerticesFaces(verts, new pb_Face[] { face });
		pb.ToMesh();
		pb.Refresh();

 

Share this post


Link to post
Share on other sites
3 minutes ago, karl said:

The problem stems from how you're creating that initial face. The gist of it is that for the moment it assumes any triangle edge with unique vertex positions are perimeter edges. So when you extrude, ProBuilder thinks that the face you're extruding from is actually two faces. To fix this, just use 4 vertex positions instead of 6. Ex:


		/**
		 * Vertex positions for a quad are laid out like this. Note that positions 0 & 3 are reused.
		 *
		 * 2-----3
		 * |     |
		 * |     |
		 * 0-----1
		 *
		 */
		Vector3[] verts = new Vector3[4]
		{
			new Vector3(-1, -1f, 0f),
			new Vector3( 1, -1f, 0f),
			new Vector3(-1,  1f, 0f),
			new Vector3( 1,  1f, 0f),
		};

		/**
		 * Quad is two tris:
		 *
		 * 2←-------3
		 * |↖ ↘    ↑
		 * |  ↖ ↘  |
		 * ↓    ↖ ↘|
		 * 0-------→1
		 */
		var face = new pb_Face(new int[6]
		{
			0, 1, 2,
			1, 3, 2
		});

		face.material = pb_Constant.DefaultMaterial;
		var pb = pb_Object.CreateInstanceWithVerticesFaces(verts, new pb_Face[] { face });
		pb.ToMesh();
		pb.Refresh();

 

Cheers, will try this out in the morning. : ]

Share this post


Link to post
Share on other sites
On ‎10‎/‎04‎/‎2018 at 4:29 PM, karl said:

The problem stems from how you're creating that initial face. The gist of it is that for the moment it assumes any triangle edge with unique vertex positions are perimeter edges. So when you extrude, ProBuilder thinks that the face you're extruding from is actually two faces. To fix this, just use 4 vertex positions instead of 6. Ex:


		/**
		 * Vertex positions for a quad are laid out like this. Note that positions 0 & 3 are reused.
		 *
		 * 2-----3
		 * |     |
		 * |     |
		 * 0-----1
		 *
		 */
		Vector3[] verts = new Vector3[4]
		{
			new Vector3(-1, -1f, 0f),
			new Vector3( 1, -1f, 0f),
			new Vector3(-1,  1f, 0f),
			new Vector3( 1,  1f, 0f),
		};

		/**
		 * Quad is two tris:
		 *
		 * 2←-------3
		 * |↖ ↘    ↑
		 * |  ↖ ↘  |
		 * ↓    ↖ ↘|
		 * 0-------→1
		 */
		var face = new pb_Face(new int[6]
		{
			0, 1, 2,
			1, 3, 2
		});

		face.material = pb_Constant.DefaultMaterial;
		var pb = pb_Object.CreateInstanceWithVerticesFaces(verts, new pb_Face[] { face });
		pb.ToMesh();
		pb.Refresh();

 

It is working! I still have to make some adjustments so that it works on all axis but 4 of the 6 are working. I'll let you know if I have any more questions/troubles. : ]

Share this post


Link to post
Share on other sites

One more thing. When using AppendFace() is there a way to make the face physically stay where it was before appending. It seems to always go to the origin of the pbo that I attach it to. I then have to move the vertices manually with offsets depending where I intended to attach it. Which was fine until I realized that if the pbo is rotated at all it throws everything out of whack.

Share this post


Link to post
Share on other sites

AppendFace will always assume vertices are in local coordinates. To account for rotation, you can use the functions "TransformPoint" and "InverseTransformPoint"  in the Transform class to translate from one object's local coordinates to another.

Share this post


Link to post
Share on other sites

Well of course because people love changing things, the whole input style has changed. The want to be able to just place nodes and have the "corridors" just connect the nodes. Which is fine, but it also means a lot of how I was doing things has changed. Have you ever had a face just disappear after an extrusion? In this case, not even the face I had extruded.

1.thumb.png.4244edd34724481f8cf07dba49aebbda.png

2.png.b75a675534aa801b75feb7be3a57fe84.png

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×