Chapter 02Torus Knots
Using the CreateTorusKnot()
method of the MeshBuilder
class, we can make torus knots.
let torusKnot = BABYLON.MeshBuilder.CreateTorusKnot("name", {options}, scene);
The first argument of CreateTorusKnot()
is a String that we can use to retrieve the entire torus knot mesh and data about the torus knot later. The second argument is an object that specifies various properties of the torus knot. The last argument is a reference to the scene in which the torus knot will be inserted into.
<script> var canvas = document.getElementById("renderCanvas"); var engine = new BABYLON.Engine(canvas, true); function createScene(canvas, engine){ let scene = new BABYLON.Scene(engine); scene.clearColor = new BABYLON.Color3(1, 1, 1); let camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 4, Math.PI / 4, 8, BABYLON.Vector3.Zero(), scene); camera.attachControl(canvas, true); let light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0), scene); let mat = new BABYLON.StandardMaterial("mat", scene); mat.diffuseColor = new BABYLON.Color3(0, 1, 0); let torusKnot = BABYLON.MeshBuilder.CreateTorusKnot("torusKnot", {}, scene); torusKnot.material = mat; return scene; }; var scene = createScene(canvas, engine); engine.runRenderLoop(function(){ scene.render(); }); window.addEventListener("resize", function(){ engine.resize(); }); </script> <canvas id="renderCanvas"></canvas>
Properties
Radius
The radius
property can be used to specify the length of the entire knot. The default value is 2.
<script> var canvas1 = document.getElementById("renderCanvas1"); var engine1 = new BABYLON.Engine(canvas1, true); function createScene1(canvas, engine){ let scene = new BABYLON.Scene(engine); scene.clearColor = new BABYLON.Color3(1, 1, 1); let camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 4, Math.PI / 4, 8, BABYLON.Vector3.Zero(), scene); camera.attachControl(canvas, true); let light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0), scene); let mat = new BABYLON.StandardMaterial("mat", scene); mat.diffuseColor = new BABYLON.Color3(0, 1, 0); let knot = BABYLON.MeshBuilder.CreateTorusKnot("knot", {radius: 1}, scene); knot.material = mat; return scene; }; var scene1 = createScene1(canvas1, engine1); engine1.runRenderLoop(function(){ scene1.render(); }); window.addEventListener("resize", function(){ engine1.resize(); }); </script> <canvas id="renderCanvas1"></canvas>
Tube
The tube
property allows us to specify the thickness of the tube which makes up the torus knot. The default value is 0.5.
<script> var canvas2 = document.getElementById("renderCanvas2"); var engine2 = new BABYLON.Engine(canvas2, true); function createScene2(canvas, engine){ let scene = new BABYLON.Scene(engine); scene.clearColor = new BABYLON.Color3(1, 1, 1); let camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 4, Math.PI / 4, 8, BABYLON.Vector3.Zero(), scene); camera.attachControl(canvas, true); let light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0), scene); let mat = new BABYLON.StandardMaterial("mat", scene); mat.diffuseColor = new BABYLON.Color3(0, 1, 0); let knot = BABYLON.MeshBuilder.CreateTorusKnot("knot", {tube: 0.25}, scene); knot.material = mat; return scene; }; var scene2 = createScene2(canvas2, engine2); engine2.runRenderLoop(function(){ scene2.render(); }); window.addEventListener("resize", function(){ engine2.resize(); }); </script> <canvas id="renderCanvas2"></canvas>
RadialSegment
The radialSegment
property specifies how many segments make up the entire tube. The more segments we have, the smoother the knot will be. The default value is 32.
<script> var canvas3 = document.getElementById("renderCanvas3"); var engine3 = new BABYLON.Engine(canvas3, true); function createScene3(canvas, engine){ let scene = new BABYLON.Scene(engine); scene.clearColor = new BABYLON.Color3(1, 1, 1); let camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 4, Math.PI / 4, 8, BABYLON.Vector3.Zero(), scene); camera.attachControl(canvas, true); let light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0), scene); let mat = new BABYLON.StandardMaterial("mat", scene); mat.diffuseColor = new BABYLON.Color3(0, 1, 0); mat.wireframe = true; let knot = BABYLON.MeshBuilder.CreateTorusKnot("knot", {radialSegments: 16}, scene); knot.material = mat; return scene; }; var scene3 = createScene3(canvas3, engine3); engine3.runRenderLoop(function(){ scene3.render(); }); window.addEventListener("resize", function(){ engine3.resize(); }); </script> <canvas id="renderCanvas3"></canvas>
TubularSegment
The tubularSegment
property specifies how many segments make up each radial segment along the tube. The default value is 32.
<script> var canvas4 = document.getElementById("renderCanvas4"); var engine4 = new BABYLON.Engine(canvas4, true); function createScene4(canvas, engine){ let scene = new BABYLON.Scene(engine); scene.clearColor = new BABYLON.Color3(1, 1, 1); let camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 4, Math.PI / 4, 8, BABYLON.Vector3.Zero(), scene); camera.attachControl(canvas, true); let light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0), scene); let mat = new BABYLON.StandardMaterial("mat", scene); mat.diffuseColor = new BABYLON.Color3(0, 1, 0); mat.wireframe = true; let knot = BABYLON.MeshBuilder.CreateTorusKnot("knot", {radialSegments: 16, tubularSegments: 5}, scene); knot.material = mat; return scene; }; var scene4 = createScene4(canvas4, engine4); engine4.runRenderLoop(function(){ scene4.render(); }); window.addEventListener("resize", function(){ engine4.resize(); }); </script> <canvas id="renderCanvas4"></canvas>
P
The p
property allows us to specify the amount of windings the torus knot has along the z-axis.
<script> var canvas5 = document.getElementById("renderCanvas5"); var engine5 = new BABYLON.Engine(canvas5, true); function createLocalAxes(size, scene) { let [local_axisX, local_axisY, local_axisZ] = createAxis(size, scene); let origin = new BABYLON.TransformNode("origin"); local_axisX.parent = origin; local_axisY.parent = origin; local_axisZ.parent = origin; return origin; } function createAxis(size, scene) { let makeTextPlane = function(text, color, size, camera, billboardMode) { let dynamicTexture = new BABYLON.DynamicTexture("DynamicTexture", 50, scene, true); dynamicTexture.hasAlpha = true; dynamicTexture.drawText(text, 5, 40, "bold 36px Arial", color , "transparent", true); let plane = new BABYLON.MeshBuilder.CreatePlane("TextPlane", {size: size, updatable: true}, scene); plane.material = new BABYLON.StandardMaterial("TextPlaneMaterial", scene); plane.material.specularColor = new BABYLON.Color3(0, 0, 0); plane.material.diffuseTexture = dynamicTexture; plane.billboardMode = BABYLON.Mesh.BILLBOARDMODE_ALL; return plane; }; let axisX = BABYLON.Mesh.CreateLines("axisX", [ new BABYLON.Vector3.Zero(), new BABYLON.Vector3(size, 0, 0), new BABYLON.Vector3(size * 0.95, 0.05 * size, 0), new BABYLON.Vector3(size, 0, 0), new BABYLON.Vector3(size * 0.95, -0.05 * size, 0) ], scene); axisX.color = new BABYLON.Color3(1, 0, 0); let xChar = makeTextPlane("X", "red", size / 8); xChar.position = new BABYLON.Vector3(0.9 * size, -0.05 * size, 0); xChar.parent = axisX; let axisY = BABYLON.Mesh.CreateLines("axisY", [ new BABYLON.Vector3.Zero(), new BABYLON.Vector3(0, size, 0), new BABYLON.Vector3( -0.05 * size, size * 0.95, 0), new BABYLON.Vector3(0, size, 0), new BABYLON.Vector3( 0.05 * size, size * 0.95, 0) ], scene); axisY.color = new BABYLON.Color3(0, 1, 0); let yChar = makeTextPlane("Y", "green", size / 8); yChar.position = new BABYLON.Vector3(0, 0.9 * size, -0.05 * size); yChar.parent = axisY; let axisZ = BABYLON.Mesh.CreateLines("axisZ", [ new BABYLON.Vector3.Zero(), new BABYLON.Vector3(0, 0, size), new BABYLON.Vector3( 0 , -0.05 * size, size * 0.95), new BABYLON.Vector3(0, 0, size), new BABYLON.Vector3( 0, 0.05 * size, size * 0.95) ], scene); axisZ.color = new BABYLON.Color3(0, 0, 1); let zChar = makeTextPlane("Z", "blue", size / 8); zChar.position = new BABYLON.Vector3(0, 0.05 * size, 0.9 * size); zChar.parent = axisZ; return [axisX, axisY, axisZ]; } function createScene5(canvas, engine){ let scene = new BABYLON.Scene(engine); scene.clearColor = new BABYLON.Color3(1, 1, 1); let camera = new BABYLON.ArcRotateCamera("Camera", -Math.PI / 2, Math.PI / 2, 30, BABYLON.Vector3.Zero(), scene); camera.attachControl(canvas, true); let light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0), scene); let mat = new BABYLON.StandardMaterial("mat", scene); mat.diffuseColor = new BABYLON.Color3(0, 1, 0); mat.wireframe = true; let knot1 = BABYLON.MeshBuilder.CreateTorusKnot("knot1", {p: 1, q: 1}, scene); knot1.material = mat; knot1.position = new BABYLON.Vector3(-16, 0, 0); let axis_origin1 = createLocalAxes(5, scene); axis_origin1.parent = knot1; let knot2 = BABYLON.MeshBuilder.CreateTorusKnot("knot2", {p: 2, q: 1}, scene); knot2.material = mat; knot2.position = new BABYLON.Vector3(-8, 0, 0); let axis_origin2 = createLocalAxes(5, scene); axis_origin2.parent = knot2; let knot3 = BABYLON.MeshBuilder.CreateTorusKnot("knot3", {p: 3, q: 1}, scene); knot3.material = mat; knot3.position = new BABYLON.Vector3(0, 0, 0); let axis_origin3 = createLocalAxes(5, scene); axis_origin3.parent = knot3; let knot4 = BABYLON.MeshBuilder.CreateTorusKnot("knot4", {p: 4, q: 1}, scene); knot4.material = mat; knot4.position = new BABYLON.Vector3(8, 0, 0); let axis_origin4 = createLocalAxes(5, scene); axis_origin4.parent = knot4; let knot5 = BABYLON.MeshBuilder.CreateTorusKnot("knot5", {p: 5, q: 1}, scene); knot5.material = mat; knot5.position = new BABYLON.Vector3(16, 0, 0); let axis_origin5 = createLocalAxes(5, scene); axis_origin5.parent = knot5; return scene; }; var scene5 = createScene5(canvas5, engine5); engine5.runRenderLoop(function(){ scene5.render(); }); window.addEventListener("resize", function(){ engine5.resize(); }); </script> <canvas id="renderCanvas5"></canvas>
Q
The q
property allows us to specify the amount of windings the torus knot has on the xy-plane.
<script> var canvas6 = document.getElementById("renderCanvas6"); var engine6 = new BABYLON.Engine(canvas6, true); function createLocalAxes(size, scene) { let [local_axisX, local_axisY, local_axisZ] = createAxis(size, scene); let origin = new BABYLON.TransformNode("origin"); local_axisX.parent = origin; local_axisY.parent = origin; local_axisZ.parent = origin; return origin; } function createAxis(size, scene) { let makeTextPlane = function(text, color, size, camera, billboardMode) { let dynamicTexture = new BABYLON.DynamicTexture("DynamicTexture", 50, scene, true); dynamicTexture.hasAlpha = true; dynamicTexture.drawText(text, 5, 40, "bold 36px Arial", color , "transparent", true); let plane = new BABYLON.MeshBuilder.CreatePlane("TextPlane", {size: size, updatable: true}, scene); plane.material = new BABYLON.StandardMaterial("TextPlaneMaterial", scene); plane.material.specularColor = new BABYLON.Color3(0, 0, 0); plane.material.diffuseTexture = dynamicTexture; plane.billboardMode = BABYLON.Mesh.BILLBOARDMODE_ALL; return plane; }; let axisX = BABYLON.Mesh.CreateLines("axisX", [ new BABYLON.Vector3.Zero(), new BABYLON.Vector3(size, 0, 0), new BABYLON.Vector3(size * 0.95, 0.05 * size, 0), new BABYLON.Vector3(size, 0, 0), new BABYLON.Vector3(size * 0.95, -0.05 * size, 0) ], scene); axisX.color = new BABYLON.Color3(1, 0, 0); let xChar = makeTextPlane("X", "red", size / 8); xChar.position = new BABYLON.Vector3(0.9 * size, -0.05 * size, 0); xChar.parent = axisX; let axisY = BABYLON.Mesh.CreateLines("axisY", [ new BABYLON.Vector3.Zero(), new BABYLON.Vector3(0, size, 0), new BABYLON.Vector3( -0.05 * size, size * 0.95, 0), new BABYLON.Vector3(0, size, 0), new BABYLON.Vector3( 0.05 * size, size * 0.95, 0) ], scene); axisY.color = new BABYLON.Color3(0, 1, 0); let yChar = makeTextPlane("Y", "green", size / 8); yChar.position = new BABYLON.Vector3(0, 0.9 * size, -0.05 * size); yChar.parent = axisY; let axisZ = BABYLON.Mesh.CreateLines("axisZ", [ new BABYLON.Vector3.Zero(), new BABYLON.Vector3(0, 0, size), new BABYLON.Vector3( 0 , -0.05 * size, size * 0.95), new BABYLON.Vector3(0, 0, size), new BABYLON.Vector3( 0, 0.05 * size, size * 0.95) ], scene); axisZ.color = new BABYLON.Color3(0, 0, 1); let zChar = makeTextPlane("Z", "blue", size / 8); zChar.position = new BABYLON.Vector3(0, 0.05 * size, 0.9 * size); zChar.parent = axisZ; return [axisX, axisY, axisZ]; } function createScene6(canvas, engine){ let scene = new BABYLON.Scene(engine); scene.clearColor = new BABYLON.Color3(1, 1, 1); let camera = new BABYLON.ArcRotateCamera("Camera", -Math.PI / 2, Math.PI / 2, 30, BABYLON.Vector3.Zero(), scene); camera.attachControl(canvas, true); let light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0), scene); let mat = new BABYLON.StandardMaterial("mat", scene); mat.diffuseColor = new BABYLON.Color3(0, 1, 0); mat.wireframe = true; let knot1 = BABYLON.MeshBuilder.CreateTorusKnot("knot1", {p: 1, q: 1}, scene); knot1.material = mat; knot1.position = new BABYLON.Vector3(-16, 0, 0); let axis_origin1 = createLocalAxes(5, scene); axis_origin1.parent = knot1; let knot2 = BABYLON.MeshBuilder.CreateTorusKnot("knot2", {p: 1, q: 2}, scene); knot2.material = mat; knot2.position = new BABYLON.Vector3(-8, 0, 0); let axis_origin2 = createLocalAxes(5, scene); axis_origin2.parent = knot2; let knot3 = BABYLON.MeshBuilder.CreateTorusKnot("knot3", {p: 1, q: 3}, scene); knot3.material = mat; knot3.position = new BABYLON.Vector3(0, 0, 0); let axis_origin3 = createLocalAxes(5, scene); axis_origin3.parent = knot3; let knot4 = BABYLON.MeshBuilder.CreateTorusKnot("knot4", {p: 1, q: 4}, scene); knot4.material = mat; knot4.position = new BABYLON.Vector3(8, 0, 0); let axis_origin4 = createLocalAxes(5, scene); axis_origin4.parent = knot4; let knot5 = BABYLON.MeshBuilder.CreateTorusKnot("knot5", {p: 1, q: 5}, scene); knot5.material = mat; knot5.position = new BABYLON.Vector3(16, 0, 0); let axis_origin5 = createLocalAxes(5, scene); axis_origin5.parent = knot5; return scene; }; var scene6 = createScene6(canvas6, engine6); engine6.runRenderLoop(function(){ scene6.render(); }); window.addEventListener("resize", function(){ engine6.resize(); }); </script> <canvas id="renderCanvas6"></canvas>
Updatable
If we want the torus knot to be able to have its internal geometry changed after creation, we can set the Boolean parameter updatable
equal true
. The default value is false
.
SideOrientation
The sideOrientation
property allows us to make our torus knot double-sided. The default value is FRONTSIDE
.
The different values for the sideOrientation
property are:
FrontUVs and BackUVs
If the torus knot is double-sided, we can choose what parts of a texture we crop and stick on the front and back sides of our mesh with the frontUVs
and backUVs
properties, respectively. The default value for both is Vector4(0, 0, 1, 1)
, which is the entire texture.
<script> var canvas7 = document.getElementById("renderCanvas7"); var engine7 = new BABYLON.Engine(canvas7, true); function createScene7(canvas, engine){ let scene = new BABYLON.Scene(engine); scene.clearColor = new BABYLON.Color3(1, 1, 1); let camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 4, Math.PI / 4, 8, BABYLON.Vector3.Zero(), scene); camera.attachControl(canvas, true); let light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0), scene); let front = new BABYLON.Vector4(0, 0, 1, 1); let back = new BABYLON.Vector4(0, 0, 1, 1); let mat = new BABYLON.StandardMaterial("mat", scene); mat.diffuseTexture = new BABYLON.Texture("https://www.babylonjs-playground.com/textures/fur.jpg", scene); mat.diffuseColor = new BABYLON.Color3(0, 1, 0); let torusKnot = BABYLON.MeshBuilder.CreateTorusKnot("torusKnot", {frontUVs: front, backUVs: back}, scene); torusKnot.material = mat; return scene; }; var scene7 = createScene7(canvas7, engine7); engine7.runRenderLoop(function(){ scene7.render(); }); window.addEventListener("resize", function(){ engine7.resize(); }); </script> <canvas id="renderCanvas7"></canvas>