Graphic Drawing
Orillusion provides the @orillusion/graphic extension package, mainly used for drawing points, lines, surfaces, and volumes that change in real time. Using specific methods, it creates a dynamic mesh that is uniformly managed and integrated into the engine's rendering pipeline, offering high performance and ease of use.
Currently, three modules are provided to create high-performance graphic data:
Graphic3D: Provides basic line drawing capabilities, commonly used for drawing auxiliary lines.Graphic3DMeshRenderer: Batch creates a set ofMeshclones within a single renderer, allowing you to freely define and adjust each clone'sTransform,Texture, andMaterialto compose graphics and animations with a high degree of freedom.Shape3DRenderer: Creates complex customShape3Dobjects, such asEllipseShape3D,RoundRectShape3D,CircleShape3D, etc. ForShape3Dobjects with continuous drawing capabilities, such asPath2DShape3DandPath3DShape3D, the design references the CanvasPath API design, allowing developers to draw on and reuse the development practices they are already familiar with for graphic drawing work.
Installation
Like the engine itself, the graphic plugin can be introduced using NPM and CDN links:
1. Installing via NPM Package
npm install @orillusion/core --save
npm install @orillusion/graphic --saveimport { Engine3D } from "@orillusion/core"
import { Graphic3D, Shape3D } from "@orillusion/graphic"2. Introducing via CDN Link
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 quickly creating 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() {
let engine = 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, engine.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;
engine.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
Using Graphic3DMesh.draw(), you can quickly create a Graphic3DMeshRenderer instance. This object can be viewed as a collection of multiple cloned Geometry objects. For each object in this collection, you can set its position and texture, and combine them to achieve the desired visual effect.
Parameter Overview
| Parameter | Description |
|---|---|
| scene | Created in the specified Scene3D |
| geo | Specifies the mesh data source |
| texture | Texture list (indexed by subscript) |
| count | Specifies the maximum number of clone collections a renderer can support (choosing an appropriate value will improve performance) |
TIP
For geo, generally inputting a simple PlaneGeometry as the model source is sufficient, using different textures to express different appearances. In theory, you can pass in any model source to create with. For example, passing in a BoxGeometry model produces a graphic composed of many cubes, enabling the creation of pixel-art scenes or simulating voxel rendering.
Modifying
Transform: To modify the rotation, scale, or position of a unit at a specific index.
Get theobject3Dsbelonging to theGraphic3DMeshRenderer, use the array index to obtain the correspondingObject3D, and modifying theTransformof thatObject3Dwill synchronize to the target unit.Modifying
Texture: Call the functionsetTextureID, specifying the texture index (textureIndex) to modify for the unit at a specific index. The texture is obtained from the texture passed in theGraphic3DMeshRendererinitialization parameters.Modifying
Material: TheGraphic3DMeshRendererclass exposes a series of APIs named similarly tosetTextureID. The first parameter specifies the target unit to set, and the second parameter sets the relevant property. Developers can use such APIs to modify the content of the graphics, such as Color, UV, Emissive, and other data.
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 texture list
let textureArray = [];
textureArray.push(await engine.res.loadTexture("path/to/texture.png") as BitmapTexture2D);
let bitmapTexture2DArray = new BitmapTexture2DArray(textureArray[0].width, textureArray[0].height, textureArray.length);
bitmapTexture2DArray.setTextures(textureArray);
// Use Plane as the mesh clone data source
let geometry = new PlaneGeometry(1, 1, 1, 1, Vector3.Z_AXIS);
// In the current scene, using plane as the clone data source, create a Graphic3DMeshRenderer instance that supports up to 100 clones.
let mr:Graphic3DMeshRenderer = Graphic3DMesh.draw(scene, geometry, bitmapTexture2DArray, 100);
// Modify material properties
mr.material.blendMode = BlendMode.ADD;
mr.material.transparent = true;
mr.material.depthWriteEnabled = false;
mr.material.useBillboard = true;
// Get the Object3D corresponding to each clone unit, and modifying the Transform property of that Object3D will synchronously modify the Transform of the target clone.
// Placing the same operation in the engine's main update function modifies 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;
private engine: Engine3D;
graphicMeshRenderer: Graphic3DMeshRenderer;
constructor() {}
async run() {
Matrix4.maxCount = 500000;
Matrix4.allocCount = 500000;
this.engine = await Engine3D.init({beforeRender: ()=> this.update()});
this.engine.setting.render.debug = true;
this.engine.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, this.engine.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;
this.engine.startRenderView(this.view);
await this.initScene();
}
async initScene() {
let texts:any[] = [];
texts.push((await this.engine.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.multiplyScalar(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();For more
Graphic3DAPI usage, please refer to the GraphicMesh example code.
Shape3D Renderer
Using Shape3DMaker, create a Shape3DRenderer renderer, which can hold and maintain a Shape3D dataset. Each Shape3D is a predefined variety of shapes, such as EllipseShape3D, RoundRectShape3D, CircleShape3D, etc. Among them, Path2DShape3D and Path3DShape3D have a richer API that can help you combine and draw complex graphics.
| Parameter | Description |
|---|---|
| name | Name, used to identify the Shape3DRenderer |
| scene | Specifies which scene to put the Shape3DRenderer into |
| textureList | Texture list, indexed by index |
| maxNodeCount | Specifies the maximum number of Shape3D objects the renderer supports |
| triangleEachNode | Specifies the average number of triangles each Shape3D has |
The renderer is designed based on the API of
CanvasPath, allowing developers to reuse and draw on the development practices they are already familiar with for 3D drawing work. The 2D drawing part of the renderer refers to drawing points, lines, and surfaces in theXZplane. At the same time, each unit can still be independently controlled viaTransform. To draw graphics in 3D space, you need to usePath3DShape3Dto begin drawing graphics that incorporate Y-axis elevation data.
Basic Properties
The engine has many built-in basic shapes, all inheriting from the Shape3D class, which mainly contain the following properties:
| 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 of the texture used for the filled area |
| shapeOrder | Sets the layering of each Shape (to eliminate z-fighting; each Shape3DRenderer can define the maximum z-fighting range, and based on this range and the number of Shape3D objects, the offset each Shape3D has is derived) |
| points3D | A reserved collection of key points passed in externally |
| isClosed | Whether the shape is closed at its start and end |
| fill | Whether the shape is filled |
| lineWidth | The width of the drawn line |
| 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 UV movement speed of the fill area texture; zw correspond to the UV movement speed of the texture data when drawing lines |
Built-in Shapes
Similar to the CanvasPath API, the engine currently provides the following subclasses/derived classes of Shape3D:
| Shape | Description |
|---|---|
| CircleShape3D | Circle, arc |
| CurveShape3D | Bezier curve controlled by 2 anchor points |
| EllipseShape3D | Ellipse |
| LineShape3D | Polyline |
| Path2DShape3D | Draws a line path on the XZ plane |
| Path3DShape3D | Draws a line path in 3D space |
| QuadraticCurveShape3D | Bezier curve controlled by 1 anchor point |
| RoundRectShape3D | Rectangle, rounded rectangle |
Built-in Methods
Through an instance of Shape3DMaker, we can call the following methods to obtain the corresponding specific shapes:
| Method | Shape Type |
|---|---|
| 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 data, and the shape will be unfolded in the XZ plane.
In addition, we can also add, delete, and modify Shape3D via the Shape3DRenderer:
| Method | Description |
|---|---|
| createShape | Specify the type of Shape3D and create a Shape3D instance in the renderer |
| removeShape | Delete a Shape3D instance |
| getShapeObject3D | Get the corresponding Object3D through the shapeIndex property of a Shape3D instance. Used for subsequently modifying the Transform |
Example
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 texture list
let textureArray = [];
textureArray.push(await engine.res.loadTexture("path/to/texture.png") as BitmapTexture2D);
let bitmapTexture2DArray = new BitmapTexture2DArray(textureArray[0].width, textureArray[0].height, textureArray.length);
bitmapTexture2DArray.setTextures(textureArray);
// In the current scene, create a Shape3DRenderer instance
maker = Shape3DMaker.makeRenderer(`path`, bitmapTexture2DArray, scene);
maker.renderer.material.doubleSide = true;
// Create a Circle based on the XZ plane, with a radius of 5 and a center of (0, 0)
let circle:CircleShape3D = maker.arc(5, 0, 0);
circle.lineWidth = 1; // Line width is 1
circle.segment = 16; // This arc will be fit using 16 line segments
circle.fill = true; // Set whether to fill
circle.line = true; // Set whether to draw the outline
circle.uvSpeed = new Vector4(0, 0, 0, Math.random() - 0.5).multiplyScalar(0.005); // Set UV scroll speed
circle.fillColor = Color.randomRGB(); // Set fill color additive
circle.lineColor = Color.randomRGB(); // Set line outline color additive
circle.startAngle = 30; // Set arc start angle
circle.endAngle = 240; // Set arc end angle
// Placing the control script for circle in the engine's main loop drives the animation effectThe above code demonstrates drawing an independent circle/arc by creating an instance of
CircleShape3D. You can also obtain it 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;
engine: Engine3D;
async run() {
Matrix4.maxCount = 10000;
Matrix4.allocCount = 10000;
this.engine = await Engine3D.init({ beforeRender: () => this.update() });
this.engine.setting.render.debug = true;
this.engine.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, this.engine.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;
this.engine.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 this.engine.res.loadTexture('https://cdn.orillusion.com/textures/128/vein_0013.png')) as BitmapTexture2D);
textureArray.push((await this.engine.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();For more
Shape3DAPI usage, please refer to the Shape3D example code.

