API

Making A Frame

Using Frame Maker

var frame = frameMaker("frame", options, scene);

optionvaluedefault value
path
(Vector3[]) array of Vector3 points forming outer edge of the frame, z must be 0, REQUIRED
 
profile
(Vector3[]) array of Vector3 points forming the frame profile, z must be 0, REQUIRED
 

The profile, or cross section, of the frame has to be a consecutive sequence of points in the XoY plane that give the corners of the profile. The left-most point or points follow the path and form the outer edge of the frame. The following profile is a square with a rebated corner.

import { Vector3 } from "@babylonjs/core/Maths/math.vector";
var profilePoints = [
new Vector3(-15, 15, 0),
new Vector3(-15, -15, 0),
new Vector3(15, -15, 0),
new Vector3(15, 10, 0),
new Vector3(10, 10, 0),
new Vector3(10, 15, 0)
];

The path defines the outer edge of the frame and is also given as points in the XoY plane. The following example produces a rectangular frame.

import { Vector3 } from "@babylonjs/core/Maths/math.vector";
path = [
new Vector3(-150, -100, 0),
new Vector3(150, -100, 0),
new Vector3(150, 100, 0),
new Vector3(-150, 100, 0)
];

The direction of the path (clockwise or counter-clockwise) determines the orientation of the profile around the frame. If you need to flip the profile, reverse the path array before passing it to frameMaker.

path.reverse();

Playground Examples

Window With Rebate Triangular Frame with L Shape Profile Oval Frame

Code

import { Vector3 } from "@babylonjs/core/Maths/math.vector";
import { Mesh } from "@babylonjs/core/Meshes/mesh";
import { MeshBuilder } from "@babylonjs/core/Meshes/meshBuilder";
var frameMaker = function (name, options, scene) {
var path = options.path;
var profile = options.profile;
var originX = Number.MAX_VALUE;
for (let m = 0; m < profile.length; m++) {
originX = Math.min(originX, profile[m].x);
}
var innerData = [];
var outerData = [];
var angle = 0;
var extrusion = 0;
var width = 0;
var cornerProfile = [];
var nbPoints = path.length;
var line = Vector3.Zero();
var nextLine = Vector3.Zero();
path[1].subtractToRef(path[0], line);
path[2].subtractToRef(path[1], nextLine);
for (let p = 0; p < nbPoints; p++) {
angle = Math.PI - Math.acos(Vector3.Dot(line, nextLine) / (line.length() * nextLine.length()));
direction = Vector3.Cross(line, nextLine).normalize().z;
lineNormal = new Vector3(line.y, -1 * line.x, 0).normalize();
line.normalize();
extrusionLength = line.length();
cornerProfile[(p + 1) % nbPoints] = [];
//local profile
for (m = 0; m < profile.length; m++) {
width = profile[m].x - originX;
cornerProfile[(p + 1) % nbPoints].push(path[(p + 1) % nbPoints].subtract(lineNormal.scale(width)).subtract(line.scale(direction * width / Math.tan(angle / 2))));
}
line = nextLine.clone();
path[(p + 3) % nbPoints].subtractToRef(path[(p + 2) % nbPoints], nextLine);
}
var frame = [];
var extrusionPaths = [];
for (let p = 0; p < nbPoints; p++) {
extrusionPaths = [];
for (let m = 0; m < profile.length; m++) {
extrusionPaths[m] = [];
extrusionPaths[m].push(new Vector3(cornerProfile[p][m].x, cornerProfile[p][m].y, profile[m].y));
extrusionPaths[m].push(new Vector3(cornerProfile[(p + 1) % nbPoints][m].x, cornerProfile[(p + 1) % nbPoints][m].y, profile[m].y));
}
frame[p] = MeshBuilder.CreateRibbon("frameLeft", { pathArray: extrusionPaths, sideOrientation: Mesh.DOUBLESIDE, updatable: true, closeArray: true }, scene);
}
return Mesh.MergeMeshes(frame, true).convertToFlatShadedMesh();
};