3D Graphic
Orillusion provides an extension package @orillusion/graphic for rendering dynamically changing points, lines, surfaces, and volumes. This package allows for the creation of dynamic meshes using specific methods, which are efficiently managed and integrated into the engine's rendering pipeline. It is both high-performance and user-friendly.
Three main modules are available for creating high-performance graphic data:
Graphic3D: Provides basic line drawing capabilities, commonly used for drawing auxiliary lines. Graphic3DMesh Renderer: Allows batch creation of Mesh clones in a single renderer, with the ability to freely adjust each clone's Transform, Texture, and Material, offering high flexibility in creating graphics and animations. Shape3D Renderer: Creates complex custom Shape3D objects, such as EllipseShape3D, RoundRectShape3D, CircleShape3D, etc. For Shape3D objects with continuous drawing capabilities, such as Path2DShape3D and Path3DShape3D, the design references the CanvasPath API, allowing developers to use familiar methods for graphic rendering.
Installation
Like the engine itself, the graphic plugins can be introduced using NPM or CDN links:
1. Installing via NPM Packages
npm install @orillusion/core --save
npm install @orillusion/graphic --saveimport { Engine3D } from "@orillusion/core"
import { Graphic3D, Shape3D } from "@orillusion/graphic"2. Installing via CDN links
It is recommended to use the ESModule build version
<script type="module">
import { Engine3D } from "https://unpkg.com/@orillusion/core/dist/orillusion.es.js"
import { Graphic3D, Shape3D } from "https://unpkg.com/@orillusion/graphic/dist/graphic.es.js"
</script>Or, load the UMD build version using <script>, accessing the Shape3D module from the global Orillusion variable:
<script src="https://unpkg.com/@orillusion/core/orillusion.umd.js"></script>
<script src="https://unpkg.com/@orillusion/graphic/dist/graphic.umd.js"></script>
<script>
const { Engine3D, Graphic } = Orillusion
const { Graphic3D, Shape3D } = Graphic
</script>Graphic3D
Create a Graphic3D object to uniformly draw graphics in the scene. Currently, three APIs are provided for quick creation of different line combinations: drawLines, drawBox, and drawCircle.
Basic Methods
import {Graphic3D} from '@orillusion/graphic'
// ...
// Create a Graphic3D object
let graphic3D = new Graphic3D();
// Add to the scene
scene.addChild(graphic3D);
// Use graphic3D to uniformly draw lines
// line - (uid, [start1, end1, start2, end2, ...], color)
graphic3D.drawLines('line', [new Vector3(0, 0, 0), new Vector3(0, 10, 0)], new Color(1, 0, 0));
// box - (uid, center, size, color)
graphic3D.drawBox('box', new Vector3(-5, -5, -5), new Vector3(5, 5, 5), new Color(0, 1, 0));
// circle - (uid, center, radius, segments, up, color)
graphic3D.drawCircle('circle', new Vector3(-15, -5, -5), 5, 15, Vector3.X_AXIS, new Color(0, 0, 1));import { Object3D, Scene3D, Engine3D, Vector3, Color, AnimationCurve, Keyframe, View3D, AtmosphericComponent, CameraUtil, HoverCameraController, DirectLight, KelvinUtil, MeshRenderer, BoxGeometry, LitMaterial } from '@orillusion/core';
import { Graphic3D, Graphic3DLineRenderer } from '@orillusion/graphic';
import { Stats } from '@orillusion/stats';
import * as dat from 'dat.gui';
class GraphicLine {
scene: Scene3D;
view: View3D;
graphic3D: Graphic3D;
async run() {
await Engine3D.init();
// init Scene3D
this.scene = new Scene3D();
this.scene.exposure = 1;
this.scene.addComponent(Stats);
// init sky
let atmosphericSky: AtmosphericComponent;
atmosphericSky = this.scene.addComponent(AtmosphericComponent);
atmosphericSky.exposure = 1.0;
// init Camera3D
let camera = CameraUtil.createCamera3DObject(this.scene);
camera.perspective(60, Engine3D.aspect, 1, 5000);
// init Camera Controller
let hoverCtrl = camera.object3D.addComponent(HoverCameraController);
hoverCtrl.setCamera(-30, -15, 100);
// init View3D
let view = new View3D();
view.scene = this.scene;
view.camera = camera;
// add a Graphic3D
this.graphic3D = new Graphic3D();
this.scene.addChild(this.graphic3D);
// create direction light
let lightObj3D = new Object3D();
lightObj3D.x = 0;
lightObj3D.y = 30;
lightObj3D.z = -40;
lightObj3D.rotationX = 20;
lightObj3D.rotationY = 160;
lightObj3D.rotationZ = 0;
let light = lightObj3D.addComponent(DirectLight);
light.lightColor = KelvinUtil.color_temperature_to_rgb(5355);
light.intensity = 30;
this.scene.addChild(light.object3D);
// relative light to sky
atmosphericSky.relativeTransform = light.transform;
Engine3D.startRenderView(view);
this.view = view;
await this.initScene();
}
async initScene() {
this.graphic3D.drawLines('line1', [Vector3.ZERO, new Vector3(0, 10, 0)], new Color().hexToRGB(Color.RED));
let animCurve = new AnimationCurve();
animCurve.addKeyFrame(new Keyframe(0, 0.5));
animCurve.addKeyFrame(new Keyframe(0.15, -0.2));
animCurve.addKeyFrame(new Keyframe(0.22, 0.4));
animCurve.addKeyFrame(new Keyframe(0.34, 0.2));
animCurve.addKeyFrame(new Keyframe(0.65, -0.2));
animCurve.addKeyFrame(new Keyframe(1, 0.9));
let lines: Vector3[] = [];
for (let i = 0; i < 100; i++) {
let y = animCurve.getValue(i / (100 - 1)) * 10;
lines.push(new Vector3(i, y, 0));
}
this.graphic3D.drawLines('line2', lines, new Color().hexToRGB(Color.RED));
this.graphic3D.drawBox('box1', new Vector3(-5, -5, -5), new Vector3(5, 5, 5), new Color().hexToRGB(Color.GREEN));
this.graphic3D.drawCircle('Circle1', new Vector3(-15, -5, -5), 5, 15, Vector3.X_AXIS, new Color().hexToRGB(Color.GREEN));
this.graphic3D.drawCircle('Circle2', new Vector3(-15, -5, -5), 5, 15, Vector3.Y_AXIS, new Color().hexToRGB(Color.GREEN));
this.graphic3D.drawCircle('Circle3', new Vector3(-15, -5, -5), 5, 15, Vector3.Z_AXIS, new Color().hexToRGB(Color.GREEN));
{
let obj = new Object3D();
let mr = obj.addComponent(MeshRenderer);
mr.geometry = new BoxGeometry(5, 5, 5);
mr.material = new LitMaterial();
this.scene.addChild(obj);
}
let gui = new dat.GUI();
let btn = {'depthTest': true}
gui.add(btn, 'depthTest').onChange(v=>{
this.graphic3D.getComponents(Graphic3DLineRenderer).forEach(mr=>{
mr.materials[0].depthCompare = v ? 'less' : 'always'
})
})
}
}
new GraphicLine().run();Graphic3DMesh Renderer
By Graphic3DMesh.draw(), we can create an instance of Graphic3DMeshRenderer, This object can be viewed as a collection of multiple cloned Geometry objects. For each object in this collection, you can set the position and texture to achieve the desired visual effect.
| Parameter | Description |
|---|---|
| scene | which scene to add the renderer |
| geo | which geometry to clone |
| texture | the index of a given texture array |
| count | maximum number of clones in the collection that a renderer can support (choosing an appropriate value will improve performance) |
TIP
The geo parameter typically uses a simple PlaneGeometry as the model source, with different textures applied to create various appearances. In theory, you can use any model source to create diverse effects. For example, by using a BoxGeometry model, you can create graphics composed of many cubes, enabling the creation of pixel art-style scenes or simulating voxel rendering.
Modifying
Transform: To modify the rotation, scale, or position of a specific unit at a given index, access theobject3Dsbelonging to theGraphic3DMeshRenderer. Use the index to obtain the correspondingObject3Dand adjust itsTransform. This change will be synchronized with the target unit.Modifying
Texture: Call the functionsetTextureIDto specify and modify the texture index for a particular unit at a given index. The textures are sourced from the textures array provided in the initialization parameters of theGraphic3DMeshRenderer.Modifying
Material: TheGraphic3DMeshRendererclass provides a series of APIs with names similar tosetTextureID. The first parameter specifies the target unit, while the second parameter sets the relevant properties. Developers can use these APIs to modify various aspects of the graphics, such asColor,UV,Emissiveproperties, and more.
Example
import { Object3D, Scene3D, Engine3D, BitmapTexture2DArray, BitmapTexture2D, PlaneGeometry, Vector3, Matrix4, Time, BlendMode, Color, ColorUtil } from "@orillusion/core";
import { Graphic3D, Graphic3DMesh, Graphic3DMeshRenderer } from '@orillusion/graphic';
// Load textures
let textureArray = [];
textureArray.push(await Engine3D.res.loadTexture("path/to/texture.png") as BitmapTexture2D);
let bitmapTexture2DArray = new BitmapTexture2DArray(textureArray[0].width, textureArray[0].height, textureArray.length);
bitmapTexture2DArray.setTextures(textureArray);
// take a plane as the clone sorce
let geometry = new PlaneGeometry(1, 1, 1, 1, Vector3.Z_AXIS);
// Create a Graphic3DMeshRenderer instance with maxium 100 clones
let mr:Graphic3DMeshRenderer = Graphic3DMesh.draw(scene, geometry, bitmapTexture2DArray, 100);
// set material properties
mr.material.blendMode = BlendMode.ADD;
mr.material.transparent = true;
mr.material.depthWriteEnabled = false;
mr.material.useBillboard = true;
// Get the corresponding object3Ds and modify the Transform property of that Object3D to synchronously update the Transform of the target clone
// By placing the same operation in the main update function of the engine, you can modify it every frame to drive the animation effect
let parts = mr.object3Ds;
for (let i = 0; i < 100; i++) {
const element = parts[i];
// set texture index from textureArray
mr.setTextureID(i, 0);
// update transform
element.transform.x = 1;
element.transform.scaleX = 1;
element.transform.rotationX = 0;
// ...
}import { Object3D, Scene3D, Engine3D, AtmosphericComponent, CameraUtil, HoverCameraController, View3D, UnLitTexArrayMaterial, BitmapTexture2DArray, BitmapTexture2D, PlaneGeometry, Vector3, Matrix4, Time, BlendMode, Color } from '@orillusion/core';
import { Stats } from '@orillusion/stats';
import { Graphic3DMesh, Graphic3DMeshRenderer } from '@orillusion/graphic';
class Sample_GraphicMesh {
private scene: Scene3D;
private parts: Object3D[];
private width: number;
private height: number;
private cafe: number = 47;
private view: View3D;
graphicMeshRenderer: Graphic3DMeshRenderer;
constructor() {}
async run() {
Matrix4.maxCount = 500000;
Matrix4.allocCount = 500000;
await Engine3D.init({beforeRender: ()=> this.update()});
Engine3D.setting.render.debug = true;
Engine3D.setting.shadow.shadowBound = 5;
this.scene = new Scene3D();
this.scene.addComponent(Stats);
let sky = this.scene.addComponent(AtmosphericComponent);
sky.enable = false;
let camera = CameraUtil.createCamera3DObject(this.scene);
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
camera.object3D.addComponent(HoverCameraController).setCamera(30, 0, 120);
this.view = new View3D();
this.view.scene = this.scene;
this.view.camera = camera;
Engine3D.startRenderView(this.view);
await this.initScene();
}
async initScene() {
let texts:any[] = [];
texts.push((await Engine3D.res.loadTexture('https://cdn.orillusion.com/textures/128/star_0008.png')) as BitmapTexture2D);
let bitmapTexture2DArray = new BitmapTexture2DArray(texts[0].width, texts[0].height, texts.length);
bitmapTexture2DArray.setTextures(texts);
let mat = new UnLitTexArrayMaterial();
mat.baseMap = bitmapTexture2DArray;
mat.name = 'LitMaterial';
{
this.width = 15;
this.height = 15;
let geometry = new PlaneGeometry(1, 1, 1, 1, Vector3.Z_AXIS);
this.graphicMeshRenderer = Graphic3DMesh.draw(this.scene, geometry, bitmapTexture2DArray, this.width * this.height);
this.parts = this.graphicMeshRenderer.object3Ds;
this.graphicMeshRenderer.material.blendMode = BlendMode.ADD;
this.graphicMeshRenderer.material.transparent = true;
this.graphicMeshRenderer.material.depthWriteEnabled = false;
this.graphicMeshRenderer.material.useBillboard = true;
for (let i = 0; i < this.width * this.height; i++) {
const element = this.parts[i];
this.graphicMeshRenderer.setTextureID(i, 0);
element.transform.scaleX = 5.5;
element.transform.scaleY = 5.5;
element.transform.scaleZ = 5.5;
}
}
}
update(){
if (this.parts) {
let len = this.parts.length;
for (let i = 0; i < len; i++) {
const element = this.parts[i];
let tmp = this.sphericalFibonacci(i, len);
tmp.scaleBy(Math.sin(i + Time.frame * 0.01) * this.cafe);
element.transform.localPosition = tmp;
}
}
}
public madfrac(A: number, B: number): number {
return A * B - Math.floor(A * B);
}
public sphericalFibonacci(i: number, n: number): Vector3 {
const PHI = Math.sqrt(5.0) * 0.5 + 0.5;
let phi = 2.0 * Math.PI * this.madfrac(i, PHI - 1);
let cosTheta = 1.0 - (2.0 * i + 1.0) * (1.0 / n);
let sinTheta = Math.sqrt(Math.max(Math.min(1.0 - cosTheta * cosTheta, 1.0), 0.0));
return new Vector3(Math.cos(phi) * sinTheta, Math.sin(phi) * sinTheta, cosTheta);
}
}
new Sample_GraphicMesh().run();See more
Graphic3DAPI usage in Graphic3D
Shape3D Renderer
By Shape3DMaker, we can create a Shape3DRenderer renderer,which can hold and maintain a dataset of Shape3D objects. Each Shape3D corresponds to a variety of predefined shapes, such as EllipseShape3D、RoundRectShape3D、CircleShape3D and so on. Additionally, Path2DShape3D and Path3DShape3D offer a more extensive API that can assist you in combining and drawing complex graphics.
| Parameter | Description |
|---|---|
| name | name to identify Shape3DRenderer |
| scene | which scene to add the renderer |
| textureList | the index of a given texture array |
| maxNodeCount | maximum number of nodes in the collection that a renderer can support |
| triangleEachNode | how many triangles to draw for each node |
The renderer is designed based on the API of
CanvasPath, allowing developers to continue using familiar development practices while working with theOrillusionengine for 3D graphics rendering. The 2D drawing section of the renderer refers to drawing points, lines, and surfaces in the XZ plane. Each unit can still be independently controlled viaTransform. For drawing shapes in 3D space, you need to usePath3DShape3Dto begin drawing graphics that incorporate Y-axis elevation data.
Properties
The following table provides a brief summary and description of the properties of Shape3D.
| Property | Description |
|---|---|
| lineColor | The color additive when drawing lines |
| fillColor | The color additive when drawing filled areas |
| lineTextureID | Sets the texture used when drawing lines |
| fillTextureID | Sets the texture used when filling areas |
| fillRotation | Sets the rotation angle for textures used in filled areas |
| shapeOrder | Sets the layering of each shape (to eliminate z-fighting; each Shape3DRenderer can define the maximum range for z-fighting, and based on this range and the number of Shape3D instances, calculates the offset for each Shape3D) |
| points3D | A placeholder for externally provided key points collection |
| isClosed | Indicates whether the shape is closed (starts and ends at the same point) |
| fill | Indicates whether the shape is filled |
| lineWidth | The width of the line when drawn |
| lineUVRect | UV data: xy correspond to the offset of the line texture, and zw correspond to the scaling of the texture data |
| fillUVRect | UV data: xy correspond to the offset of the fill area texture, and zw correspond to the scaling of the texture data |
| uvSpeed | UV data: xy correspond to the movement speed of the UVs for the fill area texture; zw corresponds to the movement speed of the UVs for the line texture. |
Shapes
Like the CanvasPath API, we have provided some subclasses of Shape3D for users to draw specific shapes:
| Shape | Description |
|---|---|
| CircleShape3D | Circle and arc shapes |
| CurveShape3D | Bezier curve controlled by two anchor points |
| EllipseShape3D | Elliptical shapes |
| LineShape3D | Polyline shapes |
| Path2DShape3D | Draws line paths on the XZ plane |
| Path3DShape3D | Draws line paths in 3D space |
| QuadraticCurveShape3D | Bezier curve controlled by one anchor point |
| RoundRectShape3D | Rectangle and rounded rectangle shapes |
Methods
All instances from Shape3DMaker could create shapes by the following methods:
| Method | Shape |
|---|---|
| ellipse | EllipseShape3D |
| arc | CircleShape3D |
| line | LineShape3D |
| quadraticCurve | QuadraticCurveShape3D |
| curve | CurveShape3D |
| path2D | Path2DShape3D |
| path3D | Path3DShape3D |
| rect | RoundRectShape3D |
| roundRect | RoundRectShape3D |
TIP
All 2D shapes, e.g. Path2D, will ignore the y-axis elevation data, and will be drawn in the XZ plane.
Additionally, we could create/delete Shape3D from Shape3DRenderer:
| Methods | Description |
|---|---|
| createShape | create a new Shape3D instance |
| removeShape | delete a Shape3D instance |
| getShapeObject3D | get the Object3D instance of a shape3D |
Usage
import { Object3D, Scene3D, Engine3D, BitmapTexture2DArray, BitmapTexture2D, PlaneGeometry, Vector3, Matrix4, Time, BlendMode, Color,ColorUtil } from "@orillusion/core";
import { CircleShape3D, EllipseShape3D, Shape3DMaker, Shape3D } from "@orillusion/graphic";
// load textures
let textureArray = [];
textureArray.push(await Engine3D.res.loadTexture("path/to/texture.png") as BitmapTexture2D);
let bitmapTexture2DArray = new BitmapTexture2DArray(textureArray[0].width, textureArray[0].height, textureArray.length);
bitmapTexture2DArray.setTextures(textureArray);
// create a Shape3DRenderer in the scene with a texture array
maker = Shape3DMaker.makeRenderer(`path`, bitmapTexture2DArray, scene);
maker.renderer.material.doubleSide = true;
// create a Circle shape with radius 5 at center (0, 0)
let circle:CircleShape3D = maker.arc(5, 0, 0);
circle.lineWidth = 1; // width of line
circle.segment = 16; // the segment of circle
circle.fill = true; // if fill the circle
circle.line = true; // if draw a line border
circle.uvSpeed = new Vector4(0, 0, 0, Math.random() - 0.5).multiplyScalar(0.005); // set UV speed
circle.fillColor = Color.randomRGB(); // set a fill color
circle.lineColor = Color.randomRGB(); // set a border color
circle.startAngle = 30; // set a start angle
circle.endAngle = 240; // set the end angle
// we could tween a animation of the circle by updating properties in the main loopThe above code demonstrates how to draw an independent circle/arc by creating an instance of
CircleShape3D. You can also achieve this by creating a genericPath2DShape3Dinstance and then calling itsarc()function.
import { Object3D, Scene3D, Engine3D, AtmosphericComponent, CameraUtil, HoverCameraController, View3D, DirectLight, KelvinUtil, BitmapTexture2DArray, BitmapTexture2D, Matrix4, Color, LineJoin, Vector4, Object3DUtil, AxisObject } from '@orillusion/core';
import { Stats } from '@orillusion/stats';
import { Shape3DMaker, Shape3D, CircleArcType, CircleShape3D } from '@orillusion/graphic';
import * as dat from 'dat.gui';
/**
* This example shows how to use Shape2D to draw various different paths on xz plane.
*
* @export
* @class Sample_Shape3DPath2D
*/
class Sample_Shape3DPath2D {
lightObj3D: Object3D;
scene: Scene3D;
view: View3D;
async run() {
Matrix4.maxCount = 10000;
Matrix4.allocCount = 10000;
await Engine3D.init({ beforeRender: () => this.update() });
Engine3D.setting.render.debug = true;
Engine3D.setting.shadow.shadowBound = 5;
this.scene = new Scene3D();
this.scene.addComponent(Stats);
let sky = this.scene.addComponent(AtmosphericComponent);
let camera = CameraUtil.createCamera3DObject(this.scene);
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
camera.object3D.addComponent(HoverCameraController).setCamera(0, -60, 60);
this.view = new View3D();
this.view.scene = this.scene;
this.view.camera = camera;
Engine3D.startRenderView(this.view);
await this.initScene();
this.scene.addChild(new AxisObject(10, 0.1));
sky.relativeTransform = this.lightObj3D.transform;
}
async initScene() {
{
/******** light *******/
this.lightObj3D = new Object3D();
this.lightObj3D.rotationX = 21;
this.lightObj3D.rotationY = 108;
this.lightObj3D.rotationZ = 10;
let directLight = this.lightObj3D.addComponent(DirectLight);
directLight.lightColor = KelvinUtil.color_temperature_to_rgb(5355);
directLight.castShadow = false;
directLight.intensity = 20;
this.scene.addChild(this.lightObj3D);
await this.addNode();
}
{
let floor = Object3DUtil.GetSingleCube(100, 0.1, 100, 0.2, 0.2, 0.2);
floor.y = -0.2;
this.scene.addChild(floor);
}
}
private maker: Shape3DMaker;
private async addNode() {
let textureArray:any[] = [];
textureArray.push((await Engine3D.res.loadTexture('https://cdn.orillusion.com/textures/128/vein_0013.png')) as BitmapTexture2D);
textureArray.push((await Engine3D.res.loadTexture('https://cdn.orillusion.com/textures/128/vein_0014.png')) as BitmapTexture2D);
let bitmapTexture2DArray = new BitmapTexture2DArray(textureArray[0].width, textureArray[0].height, textureArray.length);
bitmapTexture2DArray.setTextures(textureArray);
this.maker = Shape3DMaker.makeRenderer(`path`, bitmapTexture2DArray, this.scene);
this.maker.renderer.material.doubleSide = true;
this.createPath();
}
private createPath(): Shape3D {
let circle = this.maker.arc(20, 0, 360, undefined);
circle.lineWidth = 2;
circle.segment = 40;
circle.fill = true;
circle.line = true;
circle.isClosed = false;
circle.lineUVRect.z = 0.5;
circle.lineUVRect.w = 0.5;
circle.fillUVRect.z = 0.1;
circle.fillUVRect.w = 0.1;
circle.fillTextureID = 0;
circle.lineTextureID = 1;
circle.lineColor = Color.random();
circle.uvSpeed = new Vector4(0, 0, 0, Math.random() - 0.5).multiplyScalar(0.005);
const GUIHelp = new dat.GUI();
this.renderCircle(GUIHelp, circle, 5, false);
return circle;
}
update() {}
renderCircle(GUIHelp: dat.GUI, shape: CircleShape3D, maxSize: number, open: boolean = true, name?: string) {
name ||= 'Circle3D_' + shape.shapeIndex;
GUIHelp.addFolder(name);
GUIHelp.add(shape, 'radius', 0, maxSize, 0.1);
GUIHelp.add(shape, 'segment', 0, 100, 1);
GUIHelp.add(shape, 'startAngle', 0, 360, 1);
GUIHelp.add(shape, 'endAngle', 0, 360, 1);
let arcType = {};
arcType['sector'] = CircleArcType.Sector;
arcType['moon'] = CircleArcType.Moon;
GUIHelp.add({ arcType: shape.arcType }, 'arcType', arcType).onChange((v) => {
shape.arcType = Number.parseInt(v);
});
this.renderCommonShape3D(GUIHelp, shape, maxSize);
}
renderCommonShape3D(GUIHelp: dat.GUI, shape: Shape3D, maxSize: number, uvMin: number = 0.01, uvMax: number = 1.0) {
GUIHelp.add(shape, 'line');
GUIHelp.add(shape, 'fill');
GUIHelp.add(shape, 'isClosed');
GUIHelp.add(shape, 'lineWidth', 0, maxSize, 0.01);
GUIHelp.add(shape, 'fillRotation', -Math.PI, Math.PI, 0.01);
this.renderVec4(GUIHelp, 'FillUVRect.', shape, 'fillUVRect', 0, 10, 0.01);
this.renderVec4(GUIHelp, 'LineUVRect.', shape, 'lineUVRect', 0, 10, 0.01);
this.renderVec4(GUIHelp, 'UVSpeed.', shape, 'uvSpeed', -0.01, 0.01, 0.0001);
GUIHelp.add(shape, 'lineTextureID', 0, 1, 1);
GUIHelp.add(shape, 'fillTextureID', 0, 1, 1);
}
renderVec4(GUIHelp: dat.GUI, label: string, target: Object, key: string, min: number, max: number, step: number = 0.01) {
let components = ['x', 'y', 'z', 'w'];
let data = {} as any;
let vec4: Vector4 = target[key];
for (let component of components) {
data[label + component] = vec4[component];
GUIHelp.add(data, label + component, min, max, step).onChange((v) => {
vec4[component] = v;
target[key] = vec4;
});
}
}
}
new Sample_Shape3DPath2D().run();See more
Shape3DAPI usage in Shape3D

