2D object in 3D space (by Vertices)

Hi everyone!
I have a problem:
How to draw 2D, flat object given by Vertices in 3D Space?
I have an array with 3D Points, and I want to draw line from Points[0] to Points[1], from Points[1] to Points[2] etc…
Now I have a following solution:
var geometry = new THREE.BufferGeometry();

var vertices = faceToTriangles( VerticesArray );  // my function

var uvs = new Float32Array([
	0.0, 0.0,
	1.0, 0.0,
	1.0, 1.0,
	
	0.0, 0.0,
	1.0, 1.0,
	0.0, 1.0
]);


geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
var material = new THREE.MeshLambertMaterial( {color: 'red' } );
material.side = THREE.DoubleSide;
var mesh = new THREE.Mesh( geometry, material );

function faceToTriangles( VerticesArray )
{
	var Triangles = new Float32Array( (VerticesArray.length-2)*9 );
	
	var i = 0;
	for($v=1; $v < Face.Vertices3D.length-1; $v++)
	{
		Triangles[i++] = parseFloat(Face.Vertices3D[0].x);
		Triangles[i++] = parseFloat(Face.Vertices3D[0].y);
		Triangles[i++] = parseFloat(Face.Vertices3D[0].z);
		
		Triangles[i++] = parseFloat(Face.Vertices3D[$v].x);
		Triangles[i++] = parseFloat(Face.Vertices3D[$v].y);
		Triangles[i++] = parseFloat(Face.Vertices3D[$v].z);

		
		Triangles[i++] = parseFloat(Face.Vertices3D[$v+1].x);
		Triangles[i++] = parseFloat(Face.Vertices3D[$v+1].y);
		Triangles[i++] = parseFloat(Face.Vertices3D[$v+1].z);
	}

	return Triangles;
}

I wrote it some time ago, and it’s works in most of cases.
It makes a triangles from an objects. But in some cases, function faceToTriangles() works incorretly. It generates triangles going beyond the Object.

How make it correctly?
How to display 2D flat object (given by an array of vertices) in 3D space?
Maybe without converting to triangles?

(sorry for my english)

Hi!
Does it mean, that all those points are coplanar and they form a contour (which you want to fill with triangles)?

Yes, all of them are coplanar, and next elements of an array form a contour. I want to display it (fill with some color).

One more question: order of points is clockwise or counter-clockwise?
Or it can be either, kind of points of one contour is in clockwise, another contour is in counter-clockwise, and so on.

One time clockwise, another time counter-clockwise.
But always order of points in array is orders of points in object. That means:
1. Edge: Points[0] -> Points[1]
2. Edge: Points[1] -> Points[2]
3. Edge: Points[2] -> Points[3]

n. Edge: Points[n] -> Points[0]

Could you provide a list of points, so I could check my idea with something real?
Also, do you want to have those figures visible from both sides, front and back?

Yeah, sure: (JSON format)
{"Vertices3D":[{"x":10,"y":10,"z":1},{"x":9.421052631578952,"y":11.736842105263158,"z":6.789473684210525},{"x":5,"y":12.142857142857142,"z":7.7142857142857135},{"x":5.285714285714286,"y":13,"z":10.628571428571426},{"x":-1,"y":13,"z":10},{"x":0,"y":10,"z":0}]}

Yes, but I think that I can do it with:
material.side = THREE.DoubleSide;

I’ve answered on Stackoverflow :slight_smile:

2 Likes

Thanks. It was my question on stackoverflow :wink:

Yeah, I see :slight_smile:
Just in case: Edit fiddle - JSFiddle - Code Playground

2 Likes

As always an elegant solution. :+1:t2:

I once tested some variants.
20180510-1827-2942

It seems that the method also works with non-complanar points.
Only uv’s missing


Addendum:

It seems that non-complanar points also work. Only uv’s missing!

1 Like

@hofk
Thanks for testing :beers:

Kind of a related topic:

I was sure that it won’t last so simple, and there was evolving:

2 Likes

Hi,
I have following code now:

function addObject3D( Object )
{
	var AllFaces = new THREE.Group();
	for(var f=0; f < Object.Faces.length; f++)
	{
		var points = [];
		Object.Faces[f].Vertices3D.forEach(r => {
			points.push(new THREE.Vector3(r.x, r.y, r.z));
		});

		var tri = new THREE.Triangle(points[2], points[1], points[0]);
		var normal = new THREE.Vector3();
		tri.getNormal(normal);

		var baseNormal = new THREE.Vector3(0, 0, 1);
		var quaternion = new THREE.Quaternion().setFromUnitVectors(normal, baseNormal);

		var tempPoints = [];
		points.forEach(p => {
			tempPoints.push(p.clone().applyQuaternion(quaternion));
		})

		var shape = new THREE.Shape(tempPoints);
		var shapeGeom = new THREE.ShapeGeometry(shape);

		var Texture = new THREE.TextureLoader().load( 'graphic/3D/wood2.jpg' );
		var Material = new THREE.MeshBasicMaterial( { map: Texture } );
		Material.side = THREE.DoubleSide;

		var mesh = new THREE.Mesh( shapeGeom, Material ) ;

		mesh.geometry.vertices = points;

		AllFaces.add(mesh);
	}
	
	return AllFaces;
}

Everything is ok, when I use THREE.MeshBasicMaterial, but when i try to use a texture - the object has a solid color instead the texture.
How to use the texture? :slight_smile:

1 Like

Possibly, there’s something wrong with UV coordinates.

Also I think threejs is incompatible with php :slight_smile:

My mistake.
There is no <?php line :smiley:

@prisoner849 Tinkering a bit with your code, I have found one issue.

In this example, I draw a polygon in the X-Y (horizontal) plane, and then project the points into a tilted plane so that all points’ Z coordinates are non-negative. The resulting face/plane is not drawn as expected (i.e.: the contour “jumps” between non-contiguous points in the perimeter):

https://jsfiddle.net/Peque/0tj2vceg

Screenshot from 2020-01-22 21-58-57

The expected result should be an L-shaped polygon with just 6 segments between each contiguous pair of vertices and with all the area within the projected polygon filled.

Screenshot from 2020-01-22 21-59-51

Any ideas on what is happening or how could I fix that?

Might be a problem of accuracy :thinking:

2020-01-22_21.09.38
I cleaned up the code a bit.

<head>
  <title> planetest </title>
  <meta charset="utf-8" />
</head>

<body> </body>
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

<script>
var renderer, controls, scene, camera;

init();

function init() {

    // Scene
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xb0b0b0);

    // Camera
    camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 1000);
    camera.position.set(0, 0, 400);
    camera.up.set(0, 0, 1);

    // Light
    var ambientLight = new THREE.AmbientLight(0xcccccc, 0.2);
    scene.add(ambientLight);

    // Helpers
    var helpers = new THREE.Group();
    var grid = new THREE.GridHelper(200, 10);
    grid.rotation.x = Math.PI / 2;
    var axis = THREE.AxisHelper(100);
    helpers.add(grid);
    helpers.add(axis);
    scene.add(helpers);

    // Renderer
    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // Controls
    controls = new THREE.OrbitControls(camera, renderer.domElement);

    // Event listeners
    controls.addEventListener("change", render, false);

    // Draw projected mesh
    var points = [
	
        new THREE.Vector2(-20, 0),
        new THREE.Vector2(-20, -20),
        new THREE.Vector2(20, -20),
        new THREE.Vector2(20, 20),
        new THREE.Vector2(0, 20),
		
		//new THREE.Vector2(0, 0),
        new THREE.Vector2(-0.00000000001, 0.00000000001),
		
        new THREE.Vector2(-20, 0),
	 
    ];
	
    var plane = new THREE.Plane().setFromNormalAndCoplanarPoint(
	
        new THREE.Vector3(0.7071067811865475, 0, 0.7071067811865475),
        new THREE.Vector3(20, 0, 0)
		
    );
 
    var mesh = projectPoints( points, plane );
    scene.add(mesh);

    // Render
    render();
}

function projectPoints( points, plane ) {
    var projectedPoints = [];
    const upDirection = new THREE.Vector3(0, 0, 1);
    points.forEach(p => {
        const r = new THREE.Ray(new THREE.Vector3(p.x, p.y, 0.0), upDirection);
        let intersection = new THREE.Vector3();
        r.intersectPlane(plane, intersection);
        projectedPoints.push(new THREE.Vector3(p.x, p.y, intersection.z));
    });
 
	var shape = new THREE.Shape(projectedPoints );
    var shapeGeom = new THREE.ShapeGeometry(shape);
    var mesh = new THREE.Mesh(
        shapeGeom,
        new THREE.MeshBasicMaterial({
            color: 0x0000ff,
            side: THREE.DoubleSide,
            wireframe: false,
            transparent: true,
            opacity: 0.3,
        })
    );
    console.log(projectedPoints);
    mesh.geometry.vertices = projectedPoints;
    return mesh;
	
}

function render() {

    renderer.render(scene, camera);
	
}
</script>
</html>
1 Like

As an option, try this approach without quaternions:
https://jsfiddle.net/prisoner849/gsLz4vmk/

1 Like