Drawing a polygon in 3d space (and extruding it)

Hello.

I’m new to Babylon (and, in a way, to 3d in general…) and I need to do something that appear to be way more complicated than I thought… I have two needs :

  • Draw a polygon from a set of points
  • Extrude that polygon

As an input, I have a set of points in 3d (x, y and z) defining the outline of the polygon and the “depth” of the extrusion. Each point can be anywhere in x/y/z, all aligned on a “plane” (that plane being rotated/moved in space by unknown values beforehand).

I thought it would be relatively easy to create this polygon “in the air” and extrude it in the directions of the polygon’s normals, but I can’t find out how to do it.

As an example, this is a (simple) possible input for my use case. Note that I can have more than 3 points :

var polygon = {
    "points":[
        [0, 0, 0], 
        [0, 10, 10], 
        [10, 0, 10]
    ],
    "extrude":5
}

As I understand it, Babylon can only create 2d polygons. I thought of “triangulating” my points from their final 3d position to a 2d plane (and then rotate this “plane” so the points go back to their original position), but I’m not quite sure how to do that and I fear that rounding errors might move my points a bit… Is there a better way? Can someone push me in the right direction?

I think a visualization could be really helpful here to communicate what you are looking for. Maybe you could draw a quick sketch and share it? It would be great to show us the points in 3D space, and then the polygon that you want to build from them, and then the final extruded result.

There are different ways to generate polygons from a set of vertices, such as Triangle fans and Triangle strips. These are basically different interpretations of what the input data means. You’ll have to pick a strategy for connecting together the points to form your polygon. (As it turns out Babylon actually has an enumeration of these strategies, used in the glTF file loader).

The other question is, in what way do you want to extrude the polygon? You’ll have to pick a vector to extrude it along.

If what you want is to make a quad on which all three points lie, you’ll never need more than three points, as any 3D plane can be defined by any set of three points. However, you would need to define the center point, as well as the width and height of the plane. You could take the average of your three points, define that to be the center, and then calculate the width, height, and rotation needed to encapsulate all three. If that’s what you want to do, let me know, and we can give the math a try together :sweat_smile:

I’m not quite sure how I can actually sketch 3d points clearly. I tried and I was unable to understand what I did myself. I tried doing a simple 3d visualization using the playground to help visualize it (I also added a “floor” just to help understanding the orientation)

Obviously, the shape can be anything and have any number of “corners”. The only thing we know for sure is that ALL of the points are on the same “plane” (but we don’t know how this plane is aligned…

I come from the easier 2d world. With a 2d canvas, we can do something like this to create a shape from its corners and then fill it. I thought I could do something similar (but in 3d, generating a mesh automatically as needed). Maybe I’m just not in the right mindset for this…

ctx.beginPath();
ctx.moveTo(0, 0);
ctx.moveTo(0, 10);
ctx.moveTo(10, 5);
ctx.closePath();
ctx.fill();

As for the extrusion, it “simply” needs to be perpendicular to the polygon (so we have 90 degree angle everywhere)

Checking the documentation, and playing with it, I thought I could draw my “flat polygon” in 3d and then use its normal as the extrusion vector. For multiple reasons, it doesn’t work this way (firstly, the CreatePolygon cannot create a polygon with non-zero Y)

I don’t actually need the quad on which all points lie. I just thought that might be helpful in a way (at least to visualize the problem). That being said, if there’s an easy way to get the rotations needed to place a “virtual quad” on all of our points, that should allow me to draw the initial polygon on the ground (projecting/rotating all the points to the ground), extrude it as needed toward the sky and then rotate it back at its final position.

(Technically, I don’t need the real center for anything. But I need to use the first point in the array as the “transform center” of the final shape. I don’t know if it helps in any way.)

For reference, here’s what I had in mind initially (but that doesn’t work)

	//Declare my vertices
	var shape = [ 
		new BABYLON.Vector3(0, 0, 0), 
        new BABYLON.Vector3(0, 10, 10), 
        new BABYLON.Vector3(10, 5, 10), 
    ];

    var extrusionDepth = 5;

    //Polygon options as needed
    var options = {
        shape:shape, 
        sideOrientation: BABYLON.Mesh.DOUBLESIDE 
    }

    //Generate polygon
    var polygon = BABYLON.MeshBuilder.CreatePolygon("polygon", options);

    //Get normal of a point
    //I think this shoud work because my points are on a weird plane, so any normal should be in the right direction.
    var normals = polygon.getVerticesData(BABYLON.VertexBuffer.NormalKind);
    var positions = polygon.getVerticesData(BABYLON.VertexBuffer.PositionKind);

    //Generate extrusion vector
    var extrusionVector = BABYLON.Vector3.FromArray(positions[0], 0).add(BABYLON.Vector3.FromArray(normals, 0).scaleInPlace(extrusionDepth));

Then, I hoped I would be able to extrude my current polygon doing something like this

polygon.extrude(extrusionVector)

I don’t think something like that is doable. I think I need to create a new shape (using ExtrudeShape?)

    var options = {
        shape: shape,
        path: extrusionVector,
        sideOrientation: BABYLON.Mesh.DOUBLESIDE,
        updatable: true
    }

    let extrudedPolygon = BABYLON.MeshBuilder.ExtrudeShape("ext", options);  

(I find it a bit “wasteful” to create a shape just to get its normals - there’s probably a better way. I guess I can minimize the impact of this by doing a small mesh (using only 3 of the points maybe) because they should all generate a normal in the same direction.)

Thanks for your help.

Hi @csuture0a and welcome to the community

There are two ways to achieve what you want, ExtrudeShape or ExtrudePolygon (three if you want to do your own custom build)

ExtrudeShape
Advantages - Polygon built in XY plane and extruded perpendicular to given direction
Disadvantages - Top and bottom caps will not be drawn correctly when the polygon is concave.

Example

ExtrudePolygon
Advantages - Polygon built in XZ plane can be concave or have holes inside and top and bottom drawn correctly.

Example

Disadvantages - Extrusion only in negative Y, polygon needs to be rotated to match given direction.

2 Likes

Hi @JohnK,

As I understand it, my main problem wasn’t about the extrusion. It is mainly about the fact that my shape is defined using points in 3 dimensions (x, y and z), which isn’t possible using Polygon / ExtrudePolygin / ExtrudeShape (which only work with shapes on the XZ plane)

@DarraghBurke I think I’m getting close to what I want (but I’m not quite sure yet). I found a (probably unoptimal way) to rotate my points back to the XZ plane and draw my shape there, on the ground. Now, it’s just a matter of rotating it back to its original position (so the corners match the spheres). I thought it would be easy (I still have all the rotations I used in polygon.rotations, so I thought I simply needed to apply them “backward”), but it doesn’t appear to work right now.

I updated the playground with that. The original points are the spheres (red is the first point, green is the second one and blue is the last one). The “rotated” points on the ground are the cubes. I then use this position to draw my shape – I can extrude it there easily (line 69/70), but I disabled that for now (It’s easier to check the final alignment with a flat polygon).

If anyone has an idea to help me place the polygon back to its final position and/or doing any of this “better,” I would be forever grateful to you!

Either way, thanks for your help :slight_smile:

2 Likes

Hello @csuture0a! Welcome dude! :slight_smile:

Check this out. Might help.

Not a full solution:
You have to correctly set the indices order. Set the backfaceCulling to true and check which faces disappears. Write an algorithm which generates the indices automatically. I am dead tired, sorry, you have to finish it, at least try to! :slight_smile:

The code is self-explanatory but if you have any questions do not hesitate to ask! AAAnd hopefully this is what you were looking for lol

R.

:vulcan_salute:

Edit: I’ve just checked the PG on my phone and it’s not showing the mesh. I’ll check this one too later.

It should look like this:

Actually I have a much simpler solution in my mind. It’s just popped out of nowhere. I’ll get back to you later. It’s 2:27 AM here. Going to sleep… :melting_face:

For what it’s worth, I advanced my previous idea to make it (almost) do what I needed (Rotating back the polygon at its original position after building it on the XZ plane).

I need to center the polygon’s depth on its origins (so it’s kind of extruded by half the desired depth both ways). I initially tried to offset half the extrusion by changing the Y value before applying the rotations to the polygon, but the rotations were done from the top corner instead of the middle of the extrusion as I need it (0,0,0). I’m pretty sure there’s a way to do the rotations around a set point, but for now, i rotated the polygon to its position (so the corners touch the spheres) and then translated the polygon by half the extrusion after moving it along a weird vector I calculated and rotated too many times…

So… yeah, right now, my code is working… but only if my polygon’s first point is at 0,0,0 - I thought I considered that in the code but apparently not.

I feel like I did it using multiple weird shortcuts - this is all a weird mix of clumsy math and awkward rotations… It’s more of a proof of concept than anything else. But at least, I can now explain (and show) exactly what I’m trying to accomplish.

I’m sure there is multiple better way to accomplish what I need, though…

@roland what you have there looks pretty promising. Unfortunately, I see nothing else than the spheres on the playground on both my phone and computer (tested on safari and chrome on both - maybe it’s some kind of issue with Apple’s OpenGL implementation?) I’ll take a closer look at it tomorrow - it’s also kind of late here too.

Thanks everyone :smiley:

Hi!
the windows implementation of WebGL is quite indulgent when dealing with incorrect number of indices and positions in a CustomMesh but Mac/Linux is not. I bet you get a lot of WebGL errors in your console. I was dealing with this before. Let me tweak the stuff a bit :slight_smile:

As I see it there are two sceneraios:

  1. You are generating the points for the polygon to extrude;
  2. You are importing the points for the polygon to extrude.

For 2. the solution is more complex. Unless you say that this is the situation I will stick to 1.

For 1., When generating the points I would want to make sure I generated them as coplanar points. I can only do this, for more than three points, when I know the plane to generate them onto. Since I know what the plane is I will know a normal vector to the plane. I will take this normal direction as my vertical Y axis and generate an X and Z axis in the plane. I would then generate my points using the plane X and Z axes. To do this I may as well view the plane as horizontal and construct the polygon from world X and Z coordinates, extrude the polygon and then rotate the extruded poygon in line with the normal direction so it is at the angle I need. Essentially this is the method I proposed in my two earlier playgrounds.

SInce three arbritary different non colinear points in 3D space define a plane I would use a ribbon to draw the extruded polygon, as below

1 Like

Hey!
@JohnK built a cool solution based on a custom Mesh as well. I like it :slight_smile:

The problem with my custom Mesh solution and @JohnK 's solution is that it has the world coordinates baked into the vertices itself.

I was thinking about a different solution. This way the resulting mesh has correctly positioned vertices in it’s local space.

Guys, waiting for your improvements!

:vulcan_salute:

EDIT: this is a lunch time production, please be lenient/patient (or how to say it correctly) with me :smiley:

I work on a Smart buildings project where I need to visualize the rooms drawn on a 2D floor plan in 3D. I have all coordinates in 2D so I didn’t have the issues you are/were facing. Here is a small helper function I use to find the center of a 2D polygon. I use to it correctly set the center of the 3D mesh. I am not the author, if I remember correctly, it’s a StackOverlflow production :smiley: Works well! Might be helpful to you as well.

export function getPolygonCentroid(pts: { x: number; y: number }[]) {
  if (pts.length === 1) {
    return { x: pts[0].x, y: pts[0].y }
  }

  const first = pts[0],
    last = pts[pts.length - 1]
  if (first.x != last.x || first.y != last.y) pts.push(first)
  let twicearea = 0,
    x = 0,
    y = 0
  const nPts = pts.length
  let p1: { x: number; y: number }
  let p2: { x: number; y: number }
  let f = 0
  for (let i = 0, j = nPts - 1; i < nPts; j = i++) {
    p1 = pts[i]
    p2 = pts[j]
    f = (p1.y - first.y) * (p2.x - first.x) - (p2.y - first.y) * (p1.x - first.x)
    twicearea += f
    x += (p1.x + p2.x - 2 * first.x) * f
    y += (p1.y + p2.y - 2 * first.y) * f
  }
  f = twicearea * 3
  return { x: x / f + first.x, y: y / f + first.y }
}