wedge
In many applications, especially some campus applications. It is necessary to display the ground environment of the park, and the road surface is a part of the ground.
The usual practice is to build all relevant elements during modeling, and then import them into the display system for display.
However, in some cases, modeling may not be very convenient, so it is necessary for the 3D editor to directly perform simple road editing.
Pavement Object Extension
Simple pavements are expected to be generated by a path. We know that there are objects that generate pipelines through paths in threejs. Refer to the article WebGL pipeline network display (and TubeGeometry optimization) , and the cross-section of pipelines is a circle. The cross section of the road is expected to be a rectangle. Therefore, we can make a similar object PathRectGeometry following the idea of the pipeline, but when calculating the vertices, the cross section no longer uses a circle, but a rectangle. The code is as follows:
let points = [new Vec3(-width/2,-height/2,0),new Vec3(-width/2,height/2,0),
new Vec3(width/2,height/2,0),new Vec3(width/2,-height/2,0)]
if(!scope.clockwise) {
points = [new Vec3(-width/2,-height/2,0),new Vec3(width/2,-height/2,0),
new Vec3(width/2,height/2,0),new Vec3(-width/2,height/2,0)];
}
for( let j = 0;j <= points.length;j ++) {
let jj = j == points.length ? 0 : j;
let point = points[jj];
let radius = Math.hypot(point.x,point.y);
const sin = point.y / radius;
const cos = point.x / radius;
normal.x = ( cos * N.x + sin * B.x );
normal.y = ( cos * N.y + sin * B.y );
normal.z = ( cos * N.z + sin * B.z );
normal.normalize();
normals.push( 0,1,0 );
// vertex
vertex.x = P.x + radius * normal.x;
vertex.y = P.y + radius * normal.y;
vertex.z = P.z + radius * normal.z;
vertices.push( vertex.x, vertex.y, vertex.z );
}
The effect of creating an object through PathRectGeometry is shown in the following figure:
PavementEdit
Straight lines and Bezier curves are constructed by making points on the plane, and then a path is generated by constructing the lines, and the road effect can be generated through the path.
graph.getView().addEventListener("click", (event) => {
let now = new Date().getTime();
if (t != 0 && now - t < 500) {
return;
}
t = now;
if (path) {
let pos = graph.getPositionOnPlaneByEvent(event, plane);
constraintsHorizontalOrVertical(path, pos);
path.lineTo(pos.x, pos.y, pos.z);
tempPath = path.clone(tempPath);
tempRoad.geometry.path = tempPath;
}
})
The process is roughly as follows:
On the generated path, there will be many control points, and dragging the control points can modify the path twice:
generate connection
There will be a zebra crossing at the connection between the two roads. Click to generate a zebra crossing, and the zebra crossing can be automatically calculated by an algorithm.
// 找到road1 到road2的joint
function createJointShape(road1, road2) {
let path = road1.geometry.path;
let path2 = road2.geometry.path;
let lastPoint = path.points.at(-1);
let lastCurve = path.curves.at(-1);
let curves = path2.curves;
console.log(curves);
let minCurve, minDist = Infinity,
minPoint
for (let i = 0; i < curves.length; i++) {
let curve = curves[i];
if (curve.type == "LineCurve3") {
let {
dist,
point
} = findClosestPoint(lastPoint, curve.v1, curve.v2);
if (dist < minDist) {
minDist = dist;
minPoint = point;
minCurve = curve;
}
}
}
console.log(minCurve, minDist, minPoint);
let v1 = lastCurve.v1,
v2 = lastCurve.v2;
let tagent = new dt.Vec3().subVectors(v2, v1);
let up = new dt.Vec3(0, 1, 0);
let cross = new dt.Vec3().cross(up, tagent);
cross.normalize();
let halfRoadWidth = 50;
cross.multiplyScalar(halfRoadWidth);
let cross2 = cross.clone().multiplyScalar(3.0);
let p1 = lastPoint.clone().add(cross),
p2 = lastPoint.clone().sub(cross);
let sub = new dt.Vec3().subVectors(minPoint, lastPoint);
console.log(sub.length(), minDist, halfRoadWidth)
sub.setLength(minDist - halfRoadWidth);
let joinPoint = new dt.Vec3().addVectors(lastPoint, sub);
let halfSub = sub.clone().multiplyScalar(0.75);
let p3Center = p1.clone().add(halfSub);
let p4Center = p2.clone().add(halfSub);
let p3 = joinPoint.clone().add(cross2);
let p4 = joinPoint.clone().sub(cross2)
let newPath = new dt.ShapePath();
newPath.moveTo(p2.x, p2.z);
newPath.quadraticCurveTo(p4Center.x, p4Center.z, p4.x, p4.z);
newPath.lineTo(p3.x, p3.z);
newPath.quadraticCurveTo(p3Center.x, p3Center.z, p1.x, p1.z);
// newPath.closePath();
// let geo = new dt.PathTubeGeometry(newPath, 64, 2);
// let tube = new dt.Mesh(geo);
let shapePath = newPath;
const simpleShapes = shapePath.toShapes(true);
var texture = graph.loadTexture("./road/001.jpg", {
wrapT: dt.RepeatWrapping,
wrapS: dt.RepeatWrapping,
});
texture.repeat.set(1 / 100, 1 / 100);
texture.anisotropy = 16;
let m1 = new dt.BasicMaterial({
// flatShading:true,
map: texture,
// envMap:envMap,
// reflectivity:0.4,
color: 0xffffff,
toneMapped: false,
});
var geometry = new dt.ExtrudeGeometry(simpleShapes, {
depth: 1,
bevelEnabled: false,
vertical: true,
});
var mesh = new dt.Mesh(geometry, m1);
window.graph.getDataManager().add(mesh);
road1.add(mesh);
}
As shown below:
Epilogue
What this article shows is just a demo level to try. If you want to make a strong road editor system, you may have to consider a lot, such as multi-lane effects, heavier connection shapes, and so on. This will continue to strengthen related functions in subsequent products.
If you have good ideas, you are also welcome to communicate with me. Follow the official account "ITMan Biao Shu", you can add the author's WeChat to communicate, and receive more valuable articles in time.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。