Hello,
I know there are several resources on this but I am not able to get a curved plane from a set of Vector3 points. This is the screenshot of the output.
The ends of red and green line segments represents the boundaries of plane. and I want to make a plane connecting all those points. I can even move those points and add new points. So I want my plane size to be flexible too. Can someone help me out here?
Hi!
The first thought about it is that you have to re-create your geometry when you move or add a point.
I thought so, but isn’t there any way to add points without recreating the geometry? And can you tell me how to make a plane from the set of those points? I have tried using ShapeGeometry but I can create only 2D curved plane which acts as a projection to the figure on the grid.
.setDrawRange ( start : Integer, count : Integer ) : null
Set the .drawRange property. For non-indexed BufferGeometry, count is the number of vertices to render. For indexed BufferGeometry, count is the number of indices to render.
https://threejs.org/docs/index.html#api/en/core/BufferGeometry
This allows you to define a BufferGeometry with maximum size, but only draws the existing part. This is how I realized the construction of frames.
gLine.setDrawRange ( 0, 0 );
later then
sceneB.add( markers[ markerCount ] );
gLine.setDrawRange ( 0, markerCount + 1 );
The markers can also be moved. And then also the BufferGeometry (setDynamic).
.addAttribute( ‘position’, new THREE.BufferAttribute( g.points, 3 ).setDynamic( true ) );
Addendum:
I’m not quite clear:
Do only the white points define the geometry, or are all outer points of the red or green lines given?
It looks like all lines are parallel to x-z plane?
If only the white points are given, how is the curve ( midline) between them created?
Thanks for the answer. It worked out and now I have a dynamically changing geometry and about the question you asked.
Well I have created a catmullrom curve connecting the white points and all the red and green lines are the normals to the curve parallel to x-z plane.
Now I got to use the end points of the green and red line segments to make a plane connecting the points and make it into something like a road. So I want to know how to make a plane using those vertices where red indicating the right side of road and green indicating the left side of road.
If you have the coordinates of the points, it’s no problem. I’ll take a look at it in the evening. Such a road fits well into my addon Addon to create special / extended geometries
Yeah sure if it is doable by today it would be most helpful and I hope you got my question right.
I could use my approach from the
https://discourse.threejs.org/t/addon-produces-almost-infinite-many-time-varying-geometries-with-functions/262 .
https://github.com/hofk/THREEf.js/tree/master/THREEf_90
Totally simplify. An initial approach.
A motorway with four lanes is also possible.
Now you only have to generate the matching coordinates. Here is just a primitive example.
<!DOCTYPE html>
<!-- @author hofk -->
<head>
<title> road </title>
<meta charset="utf-8" />
</head>
<body>
</body>
<script src="../js/three.min.101.js"></script>
<script src="../js/OrbitControls.js"></script>
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 0.1, 100 );
camera.position.set( 10, 4, 10 );
var renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0xdddddd, 1 );
var container = document.createElement( 'div' );
document.body.appendChild( container );
container.appendChild( renderer.domElement );
var controls = new THREE.OrbitControls( camera, renderer.domElement );
var ls = 100; // length segments
var ws = 2; // width segments, tracks
var lss = ls + 1;
var wss = ws + 1;
var faceCount = ls * ws * 2;
var vertexCount = lss * wss;
var g = new THREE.BufferGeometry( );
g.faceIndices = new Uint32Array( faceCount * 3 );
g.vertices = new Float32Array( vertexCount * 3 );
//g.normals = new Float32Array( vertexCount * 3 );
//g.uvs = new Float32Array( vertexCount * 2 );
g.setIndex( new THREE.BufferAttribute( g.faceIndices, 1 ) );
g.addAttribute( 'position', new THREE.BufferAttribute( g.vertices, 3 ).setDynamic( true ) );
//g.addAttribute( 'normal', new THREE.BufferAttribute( g.normals, 3 ).setDynamic( true ) );
//g.addAttribute( 'uv', new THREE.BufferAttribute( g.uvs, 2 ) );
var idxCount = 0;
for ( var j = 0; j < ls; j ++ ) {
for ( var i = 0; i < ws; i ++ ) {
// 2 faces / segment, 3 vertex indices
a = wss * j + i;
b1 = wss * ( j + 1 ) + i; // right-bottom
c1 = wss * ( j + 1 ) + 1 + i;
b2 = wss * ( j + 1 ) + 1 + i; // left-top
c2 = wss * j + 1 + i;
g.faceIndices[ idxCount ] = a; // right-bottom
g.faceIndices[ idxCount + 1 ] = b1;
g.faceIndices[ idxCount + 2 ] = c1;
g.faceIndices[ idxCount + 3 ] = a; // left-top
g.faceIndices[ idxCount + 4 ] = b2,
g.faceIndices[ idxCount + 5 ] = c2;
idxCount += 6;
}
}
// write groups for multi material
/*
//Customize for different colored tracks.
for ( var f = 0, p = 0; f < faceCount; f ++, p += 3 ) {
g.addGroup( p, 3, 1 );
}
*/
var x, y, z;
var vIdx = 0; // vertex index
var posIdx; // position index
for ( var j = 0; j < lss; j ++ ) { // length
for ( var i = 0; i < wss; i ++ ) { // width
// calculate here the coordinates according to your wishes
x = j / 10;
y = Math.sin( Math.PI * j / 100 );
z = i - 1;
xyzSet();
vIdx ++;
}
}
g.attributes.position.needsUpdate = true;
//g.attributes.normal.needsUpdate = true;
var material = new THREE.MeshBasicMaterial( { color: 0xff0000, side: THREE.DoubleSide, wireframe: true } );
var mesh = new THREE.Mesh( g, material );
scene.add( mesh );
animate();
//............................
// set vertex position
function xyzSet() {
posIdx = vIdx * 3;
g.vertices[ posIdx ] = x;
g.vertices[ posIdx + 1 ] = y;
g.vertices[ posIdx + 2 ] = z;
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
controls.update();
}
</script>
</html>
The sketch shows the scheme of vertices and faces for 3 tracks and a length of 3.
Here you can also see the bottom and top of the Addon.
It’s not a road, but it’s a nice tape.
// calculate here the coordinates according to your wishes
tangent = curve.getTangent( j / 100 ); // 100 length segments
x = points[ j ].x + tangent.x;
y = points[ j ].y + 1;
z = points[ j ].z + tangent.z + i / 2;
Now there is a four-lane street in colored wireframe.
<!DOCTYPE html>
<!-- @author hofk -->
<head>
<title> FourLaneRoad </title>
<meta charset="utf-8" />
</head>
<body>
</body>
<script src="../js/three.min.101.js"></script>
<script src="../js/OrbitControls.js"></script>
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 0.1, 100 );
camera.position.set( -1, 14, 24 );
var renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0x000000, 1 );
var container = document.createElement( 'div' );
document.body.appendChild( container );
container.appendChild( renderer.domElement );
var controls = new THREE.OrbitControls( camera, renderer.domElement );
var gridHelper = new THREE.GridHelper( 100, 100 );
scene.add( gridHelper );
var ls = 200; // length segments
var ws = 4; // width segments, tracks
var lss = ls + 1;
var wss = ws + 1;
var faceCount = ls * ws * 2;
var vertexCount = lss * wss;
var g = new THREE.BufferGeometry( );
g.faceIndices = new Uint32Array( faceCount * 3 );
g.vertices = new Float32Array( vertexCount * 3 );
//g.normals = new Float32Array( vertexCount * 3 );
//g.uvs = new Float32Array( vertexCount * 2 );
g.setIndex( new THREE.BufferAttribute( g.faceIndices, 1 ) );
g.addAttribute( 'position', new THREE.BufferAttribute( g.vertices, 3 ).setDynamic( true ) );
//g.addAttribute( 'normal', new THREE.BufferAttribute( g.normals, 3 ).setDynamic( true ) );
//g.addAttribute( 'uv', new THREE.BufferAttribute( g.uvs, 2 ) );
var idxCount = 0;
for ( var j = 0; j < ls; j ++ ) {
for ( var i = 0; i < ws; i ++ ) {
// 2 faces / segment, 3 vertex indices
a = wss * j + i;
b1 = wss * ( j + 1 ) + i; // right-bottom
c1 = wss * ( j + 1 ) + 1 + i;
b2 = wss * ( j + 1 ) + 1 + i; // left-top
c2 = wss * j + 1 + i;
g.faceIndices[ idxCount ] = a; // right-bottom
g.faceIndices[ idxCount + 1 ] = b1;
g.faceIndices[ idxCount + 2 ] = c1;
g.faceIndices[ idxCount + 3 ] = a; // left-top
g.faceIndices[ idxCount + 4 ] = b2,
g.faceIndices[ idxCount + 5 ] = c2;
g.addGroup( idxCount, 6, i ); // write groups for multi material
idxCount += 6;
}
}
var curve = new THREE.CatmullRomCurve3( [
new THREE.Vector3( -25, 0, -25 ),
new THREE.Vector3( -4, 2, -9 ),
new THREE.Vector3( 4, 1, -6 ),
new THREE.Vector3( 6, 0, 0 ),
new THREE.Vector3( -3, 1, 1 ),
new THREE.Vector3( -11, 0, 6 ),
new THREE.Vector3( -12, 1, 1 ),
new THREE.Vector3( -7, 1, -3 ),
new THREE.Vector3( 7, 8, -9 ),
new THREE.Vector3( 13, 2, -12 ),
] );
var points = curve.getPoints( ls );
var curveGeometry = new THREE.BufferGeometry().setFromPoints( points );
var tangent;
var normal = new THREE.Vector3( 0, 0, 0 );
var binormal = new THREE.Vector3( 0, 1, 0 );
var x, y, z;
var vIdx = 0; // vertex index
var posIdx; // position index
for ( var j = 0; j < lss; j ++ ) { // length
for ( var i = 0; i < wss; i ++ ) { // width
// calculate here the coordinates according to your wishes
tangent = curve.getTangent( j / ls ); // .. / length segments
normal.cross( tangent, binormal );
binormal.cross( normal, tangent ); // new binormal
normal.normalize().multiplyScalar( 0.25 );
x = points[ j ].x + ( i - ws / 2 ) * normal.x;
y = points[ j ].y;
z = points[ j ].z + ( i - ws / 2 ) * normal.z;
xyzSet();
vIdx ++;
}
}
g.attributes.position.needsUpdate = true;
//g.attributes.normal.needsUpdate = true;
var material = [
new THREE.MeshBasicMaterial( { color: 0x00ff00, side: THREE.DoubleSide, wireframe: true } ),
new THREE.MeshBasicMaterial( { color: 0xff0000, side: THREE.DoubleSide, wireframe: true } ),
new THREE.MeshBasicMaterial( { color: 0x0000ff, side: THREE.DoubleSide, wireframe: true } ),
new THREE.MeshBasicMaterial( { color: 0xff00ff, side: THREE.DoubleSide, wireframe: true } ),
];
var mesh = new THREE.Mesh( g, material );
scene.add( mesh );
var curveMaterial = new THREE.LineBasicMaterial( { color : 0xffffff } );
var curveLine = new THREE.Line( curveGeometry, curveMaterial );
scene.add( curveLine );
animate();
//............................
// set vertex position
function xyzSet() {
posIdx = vIdx * 3;
g.vertices[ posIdx ] = x;
g.vertices[ posIdx + 1 ] = y;
g.vertices[ posIdx + 2 ] = z;
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
controls.update();
}
</script>
</html>
Thank you very much for such a great explanation, it really helped me a lot. I got a road now but I am struggling with creating a line(full line and dashed) in between having some width. setting a scale doesn’t work and line width doesn’t work. Do I have to use a similar plane making technique with shorter width?
@hofk
This is cool! Would be greate to have it amongst official examples. Time to time, people on SO ask about such thing
How about this?
With black and white material.
var ls = 200; // length segments
var ws = 5; // width segments, tracks, streaks, stripes
…
var d = [ -0.6, -0.58, -0.01, 0.01, 0.58, 0.6 ];
for ( var j = 0; j < lss; j ++ ) { // length
for ( var i = 0; i < wss; i ++ ) { // width
// calculate here the coordinates according to your wishes
tangent = curve.getTangent( j / ls ); // .. / length segments
normal.cross( tangent, binormal );
binormal.cross( normal, tangent ); // new binormal
normal.normalize();
x = points[ j ].x + d[ i ] * normal.x;
y = points[ j ].y;
z = points[ j ].z + d[ i ] * normal.z;
xyzSet();
vIdx ++;
}
}
Just another option.
use
var ls = 200; // length segments
var ws = 3; // width segments
enable
g.uvs = new Float32Array( vertexCount * 2 );
g.addAttribute( 'uv', new THREE.BufferAttribute( g.uvs, 2 ) );
calculate
var uvIdxCount = 0;
for ( var j = 0; j < lss; j ++ ) {
for ( var i = 0; i < wss; i ++ ) {
g.uvs[ uvIdxCount ] = j / ls;
g.uvs[ uvIdxCount + 1 ] = i / ws;
uvIdxCount += 2;
}
}
use
var d = [ -0.52, -0.5, 0.5, 0.52 ];
use
normal.normalize();
x = points[ j ].x + d[ i ] * normal.x;
y = points[ j ].y;
z = points[ j ].z + d[ i ] * normal.z;
material
tex = new THREE.TextureLoader().load( 'RoadMarking.png' );
tex.wrapS = THREE.RepeatWrapping;
tex.wrapT = THREE.RepeatWrapping;
tex.repeat.set( 100, 1 );
var material = [
new THREE.MeshBasicMaterial( { color: 0xffffff, side: THREE.DoubleSide} ),
new THREE.MeshBasicMaterial( { map: tex, side: THREE.DoubleSide} ),
new THREE.MeshBasicMaterial( { color: 0xffffff, side: THREE.DoubleSide} ),
];
RoadMarking.png painted fast, I am not an artist
result
Really appreciate the help but I have done all those … I should be dynamically able to change the width of those lane markings and using texture is not an option. I was able to make a pure line with the curved plane with small width but don’t know how to make a dashed line with changable width.
By the way, I really can’t thank you enough for helping me this much and getting me this far.
You can take a separate strip for the centerline and a simple texture.
Now you can make the stripes dynamic by changing the values in
d = [ -0.62, -0.6, -0.02, 0.02, 0.6, 0.62]; ( initial).
You can see how this works in my addons, for example.
But there’s another problem. If the dots are not equidistant, there are different line lengths white.
var curve = new THREE.CatmullRomCurve3( [
new THREE.Vector3( -25, 0.2, -25 ),
new THREE.Vector3( -24, 0.2, -24 ),
new THREE.Vector3( -4, 2, -9 ),
One must extend the calculation, e.g. for the uv’s use the possibilities of CatmullRomCurve3 / Curve.
.getLength .getSpacedPoints
etc.
This is gonna be a nice racetrack , I think.
I need to take a closer look at this.
addendum: I looked. It’s quite simple.
The uv calculation shifted backwards and changed:
var len = curve.getLength( );
var lenList = curve.getLengths ( ls );
var uvIdxCount = 0;
for ( var j = 0; j < lss; j ++ ) {
for ( var i = 0; i < wss; i ++ ) {
//g.uvs[ uvIdxCount ] = j / ls;
//g.uvs[ uvIdxCount + 1 ] = i / ws;
g.uvs[ uvIdxCount ] = lenList[ j ] / len;
g.uvs[ uvIdxCount + 1 ] = i / ws;
uvIdxCount += 2;
}
}
There’s a bug fix.
Also in the addon THREEg.
Since only the components x and z of the normal are used, but the y component is not always 0, it resulted in different road widths.
It’s better this way.
normal.y = 0;
normal.normalize();
lxz = Math.sqrt( normal.x * normal.x + normal.z * normal.z );
nx = normal.x / lxz;
nz = normal.z / lxz;
for ( var i = 0; i < g.wss; i ++ ) { // width
x = g.points[ j ].x + g.td[ i ] * nx;
y = g.points[ j ].y;
z = g.points[ j ].z + g.td[ i ] * nz;
The road upstairs is just an area. The normal is horizontal.
If you do it vertically, you get a wall.
But also only one surface.
That’s why I combined both things and got a wall or a street with a substructure.
I’ll add it soon on Github: GitHub - hofk/THREEg.js: three.js addon to create special or extended geometries. The addon generates indexed or non indexed BufferGeometries. soon is now
Run around the wall.
Hi @hofk
Great work! If I want to create a road intersection, how can I do that? Could you give me some clues? Thanks
This is very similar for streets and walls, the street just has no / hardly any height.
There is just one topic where this is discussed. There are several possibilities.
Wall building in a level-editor see page 15