Skip to content
本页内容

WorldSpace

除了屏幕空间(ScreenSpace), GUI组件都可以在 WorldSpace 中渲染


Orillusion powered by WebGPU on Chrome/Edge 113+
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, Scene3D, Object3D, Camera3D, ForwardRenderJob, TextAnchor, UITextField, DirectLight, HoverCameraController, Color, GUIHelp, CameraUtil, webGPUContext, Vector3, CEvent, CEventDispatcher, WorldPanel, KelvinUtil, Time, clamp, UIImage, PointerEvent3D, UIInteractive, ImageType, LitMaterial, BoxGeometry, MeshRenderer } from "@orillusion/core";

var scene: Scene3D;
var hover;
async function demo() {
    // initializa engine
    await Engine3D.init({
        renderLoop: () => {
            renderUpdate();
        },
    });

    Engine3D.setting.shadow.shadowBound = 200;
    Engine3D.setting.shadow.shadowBias = 0.002;

    Engine3D.setting.shadow.autoUpdate = true;
    Engine3D.setting.shadow.updateFrameRate = 1;

    scene = new Scene3D();
    let camera = CameraUtil.createCamera3DObject(scene);
    camera.perspective(60, webGPUContext.aspect, 1, 5000.0);

    hover = camera.object3D.addComponent(HoverCameraController);
    hover.setCamera(45, -45, 200, new Vector3(0, 20, 0));

    let renderJob = new ForwardRenderJob(scene);

    Engine3D.startRender(renderJob);
    await initScene();

    let model = await Engine3D.res.loadGltf(
        "https://cdn.orillusion.com/gltfs/wukong/wukong.gltf"
    );
    let scale = 50;
    model.localScale = new Vector3(scale, scale, scale);
    scene.addChild(model);
    scene.addChild(GetSingleCube(400, 1, 400, 0.2, 0.2, 0.2));

    await Engine3D.res.loadFont("https://cdn.orillusion.com/fnt/0.fnt");
    await Engine3D.res.loadAtlas(
        "https://cdn.orillusion.com/atlas/Sheet_atlas.json"
    );

    makeUIPanelList();
}

var sampleUIPanelClick: CEvent = new CEvent("ClickUIPanel");
var sampleUIPanelDispatcher: CEventDispatcher = new CEventDispatcher();
var nodeList: _GUIPanelBinder[] = [];
var bindTarget3DRoot: Object3D;
var lightObj;
function makeUIPanelList(): void {
    let uiRoot = new Object3D();
    bindTarget3DRoot = new Object3D();
    bindTarget3DRoot.y = 50;
    scene.addChild(bindTarget3DRoot);
    Engine3D.renderJob.guiCanvas.addGUIChild(uiRoot);

    for (let i = 0; i < 50; i++) {
        //panel
        let panelRoot: Object3D = uiRoot.addChild(new Object3D()) as Object3D;
        panelRoot.addComponent(WorldPanel);

        //random position
        let angle = Math.PI * 2 * Math.random();
        let pos = new Vector3();
        pos.set(Math.sin(angle), Math.cos(angle), (Math.random() - 0.5) * 2);
        pos.multiplyScalar(50 * Math.sqrt(Math.random() + 0.25));

        let ball = new Object3D();
        ball.localPosition = pos;
        bindTarget3DRoot.addChild(ball);

        //binder
        let node = new _GUIPanelBinder(ball, panelRoot, i);
        nodeList.push(node);
    }
    sampleUIPanelDispatcher.addEventListener(
        sampleUIPanelClick.type, (e) => {
            let target = e.data as Object3D;
            let targetPos = Camera3D.mainCamera.worldToScreenPoint(
                target.transform.worldPosition
            );
            let orginPos = Camera3D.mainCamera.worldToScreenPoint(new Vector3());
            isSpeedAdd = targetPos.x > orginPos.x ? 1 : -1;
            speedAngle += 50;
        }, this
    );
}

function GetSingleCube(
    sizeX: number, sizeY: number, sizeZ: number, r: number, g: number, b: number
) {
    let mat = new LitMaterial();
    mat.baseColor = new Color(r, g, b, 1);

    let obj = new Object3D();
    let mr = obj.addComponent(MeshRenderer);
    mr.castGI = true;
    mr.geometry = new BoxGeometry(sizeX, sizeY, sizeZ);
    mr.material = mat;
    return obj;
}

async function initScene() {
    /******** light *******/
    {
        lightObj = new Object3D();
        lightObj.rotationX = 54;
        lightObj.rotationY = 100;
        lightObj.rotationZ = 0;
        let lc = lightObj.addComponent(DirectLight);
        lc.lightColor = KelvinUtil.color_temperature_to_rgb(5355);
        lc.castShadow = true;
        lc.intensity = 40;
        scene.addChild(lightObj);
    }
}

var speedAngle = 1;
var isSpeedAdd = 1;
function renderUpdate() {
    if (bindTarget3DRoot) {
        speedAngle -= 0.2;
        speedAngle = Math.max(speedAngle, 1);
        bindTarget3DRoot.rotationY += 0.01 * speedAngle * isSpeedAdd;

        for (let binder of nodeList) {
            binder.update(Time.delta);
        }
    }
}

export class _GUIPanelBinder {
    objUI: Object3D;
    panel: _GUIPanelPOI;
    private ball: Object3D;

    constructor(ball: Object3D, ui: Object3D, index: number) {
        this.ball = ball;
        this.objUI = ui;
        this.objUI.name = "panel " + index;
        this.objUI.scaleX = this.objUI.scaleY = this.objUI.scaleZ = 0.1;
        this.panel = new _GUIPanelPOI(this.objUI, index);
    }

    update(delta: number) {
        this.objUI.localPosition = this.ball.transform.worldPosition;
        this.panel.update(delta);
    }
}

class _GUIPanelPOI {
    private readonly alpha = 0.8;

    private objUI: Object3D;
    private index: number;
    private _originColor: Color = new Color();
    private _remainTime: number = -1;
    private _backImage: UIImage;
    private _outColor = new Color(0, 0.5, 0.75, this.alpha);

    constructor(obj, index: number) {
        this.objUI = obj;
        this.index = index;
        this.displayUIDetail();
    }

    update(delta: number): void {
        if (this._remainTime > 0) {
            this._remainTime -= delta;
            let progress = clamp(this._remainTime, 0, 500);
            progress = 1 - progress / 500;
            let color = this._backImage.color;
            color.r = this._originColor.r * progress + (1.0 - progress) * 0.2;
            color.g = this._originColor.g * progress + (1.0 - progress) * 0.2;
            color.b = this._originColor.b * progress + (1.0 - progress) * 0.2;
            this._backImage.color = color;
        }
        this.updateFrame();
    }

    private lastIndex: number = -1;
    private frame: number = Math.floor(Math.random() * 10000);
    private frameStart = 65; //65~77
    private frameCount = 13;
    private _icon: UIImage;

    private _frameSpeed = 0.05 + 0.1 * Math.random();

    updateFrame() {
        this.frame++;
        let newIndex = Math.floor(this.frame * this._frameSpeed) % this.frameCount;
        if (newIndex != this.lastIndex) {
            this.lastIndex = newIndex;
            let frameKey = (this.lastIndex + this.frameStart)
                .toString()
                .padStart(5, "0");
            this._icon.texture = Engine3D.res.getSubTexture(frameKey);
        }
    }

    private displayUIDetail(): void {
        let uiChild = this.objUI.addChild(new Object3D()) as Object3D;

        let r = Math.random() * 0.25 + 0.2;
        let b = Math.random() * 0.25 + 0.2;
        let g = Math.random() * 0.25 + 0.2;
        this._originColor.setTo(r, g, b, this.alpha);
        //back
        this._backImage = this.addImage(
            uiChild, " ", 200, 120, r, g, b, this.alpha
        );
        this._backImage.uiTransform.x = 100;
        this._backImage.uiTransform.y = -60;

        uiChild.addEventListener(
            PointerEvent3D.PICK_CLICK_GUI, () => {
                this._remainTime = 500;
                sampleUIPanelClick.data = this.objUI;
                sampleUIPanelDispatcher.dispatchEvent(sampleUIPanelClick);
            }, this
        );

        uiChild.addEventListener(
            PointerEvent3D.PICK_OVER_GUI, () => {
                this._backImage.color = this._outColor;
            }, this
        );

        uiChild.addEventListener(
            PointerEvent3D.PICK_OUT_GUI, () => {
                this._backImage.color = this._originColor;
            }, this
        );

        let button = uiChild.addComponent(UIInteractive);
        button.interactive = true;

        //icon
        {
            let iconNode = uiChild.addChild(new Object3D()) as Object3D;
            let icon = this.addImage(iconNode, "", 100, 100, 1, 1, 1);
            icon.uiTransform.x = 30;
            icon.uiTransform.y = -30;
            this._icon = icon;
            this.updateFrame();
        }

        //text
        {
            let textChild = this.objUI.addChild(new Object3D()) as Object3D;
            let text = textChild.addComponent(UITextField);
            text.uiTransform.resize(120, 60);
            text.uiTransform.x = 110;
            text.uiTransform.y = -48;
            text.alignment = TextAnchor.UpperLeft;
            text.text = "Orilussion";
            text.fontSize = 22;
            text.color = new Color(0.9, 0.9, 0.9, 1.0);
        }

        //text
        {
            let textChild = this.objUI.addChild(new Object3D()) as Object3D;
            let text = textChild.addComponent(UITextField);
            text.uiTransform.resize(120, 60);
            text.uiTransform.x = 110;
            text.uiTransform.y = -100;
            text.alignment = TextAnchor.UpperLeft;
            text.text = "Hello WebGPU,次时代三维引擎";
            text.fontSize = 18;
            text.color = new Color(0.8, 0.8, 0.8, 1.0);
        }
    }

    private addImage(
        obj: Object3D, texture: string, w: number, h: number, r: number, g: number, b: number, a: number = 1
    ): UIImage {
        let image = obj.addComponent(UIImage);
        image.texture = Engine3D.res.getSubTexture(texture);
        image.uiTransform.resize(w, h);
        image.imageType = ImageType.Sliced;
        image.color.setTo(r, g, b, a);
        return image;
    }
}

demo();