PlayCanvas Gizmo API

Can anyone provide assistance with the Gizmo API reference? Specifically, I am looking for examples of how to integrate or call the API within my code, as well as how to apply transform handling types on the model if you do a simple basic project like applying the gizmo on the model through this API that would be very helpful for all of us. Thanks

Link:

This could be helpful: PlayCanvas Examples

1 Like

Thanks for the reply,I checked this example but I need to apply in playcanvas script like how can I do this .Thanks

Could you make a test build on playcanvas,it’s really helpful for me to proceed the work with your test build . Thanks

Thanks for sharing this example.Done it myself :slightly_smiling_face:

1 Like

I have a question related to the gizmo. I want to intersect like the gizmo if hit with the wall then it shouldn’t be dragged. Is there any custom code that I have to add or if you could help me, it would be appreciated. Thanks

 gizmo.on('transform:move', () => {

                 //  console.log(this._nodes[0]);
                   this._nodes[0]._children[0]._children[0].collision.on('contact', (result)=>{

                    console.log("its hitting",result);


                });

I’m trying to do like this but nothing is happening. Is there any way you can guide me or write code? It would be very helpful for me . Thanks

Here’s a super-simple example showing how to set up a translate gizmo in under 50 lines of code:

import * as pc from 'https://cdn.jsdelivr.net/npm/playcanvas/+esm';

// create an application
const canvas = document.getElementById('application');
const app = new pc.Application(canvas);
app.setCanvasResolution(pc.RESOLUTION_AUTO);
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
app.start();

// ensure canvas is resized when window changes size
const resize = () => app.resizeCanvas();
window.addEventListener('resize', resize);

// create a camera
const camera = new pc.Entity();
camera.addComponent('camera', {
    clearColor: new pc.Color(0.3, 0.3, 0.7)
});
camera.setEulerAngles(-45, 45, 0);
camera.translateLocal(0, 0, 5);
app.root.addChild(camera);

// create a light
const light = new pc.Entity();
light.addComponent('light');
light.setEulerAngles(45, 25, 0);
app.root.addChild(light);

// create a box
const box = new pc.Entity();
box.addComponent('model', {
    type: 'box'
});
app.root.addChild(box);

// create gizmo layer
const gizmoLayer = new pc.Layer({
    name: 'Gizmo',
    clearDepthBuffer: true,
    opaqueSortMode: pc.SORTMODE_NONE,
    transparentSortMode: pc.SORTMODE_NONE
});
const layers = app.scene.layers;
layers.push(gizmoLayer);
camera.camera.layers = camera.camera.layers.concat(gizmoLayer.id);

const gizmo = new pc.TranslateGizmo(app, camera.camera, gizmoLayer);
gizmo.size = 0.5;
gizmo.attach([box]);

1 Like

Thanks, Will for helping me out, I have done this part already, and it’s working. But I want like to intersect or maybe collide in runtime once I drag the model . This means if I can drag the model and on the left side it collides with the wall, then it should not overlap means not cross the wall . This part I need this Here is it code

var GizmoManager = pc.createScript('gizmoManager');

// Create a PlayCanvas script

// Attribute to reference the existing camera entity
GizmoManager.attributes.add('cameraEntity', { type: 'entity' });

// Initialize the script
GizmoManager.prototype.initialize = async function() {

    GizmoManager.Instance=this;

    const app = this.app;
    const canvas = app.graphicsDevice.canvas;
    const cameraEntity = this.cameraEntity;

    // Local state management
    const state = {
        gizmo: {
            type: 'translate',
            coordSpace: 'world'
        },
        camera: {
            proj: pc.PROJECTION_PERSPECTIVE,
            fov: 45
        }
    };

    // GizmoHandler class
    class GizmoHandler {
        _type = 'translate';
        _gizmos;
        _nodes = [];
        _ignorePicker = false;
        _skipSetFire = false;

        constructor(app, camera, layer) {
            this._gizmos = {
                translate: new pc.TranslateGizmo(app, camera, layer),
                rotate: new pc.RotateGizmo(app, camera, layer),
                scale: new pc.ScaleGizmo(app, camera, layer)
            };

            for (const type in this._gizmos) {
                const gizmo = this._gizmos[type];
                console.log(gizmo.on);
                gizmo.on(
                    'pointer:down',
                    (x, y, meshInstance) => {
                        this._ignorePicker = !!meshInstance;
                        //console.log(this._gizmos);

                    }
                );
                
                gizmo.on('pointer:up', () => {
                    this._ignorePicker = false;
                });

               

                
            }
        }
        get gizmo() {
            return this._gizmos[this._type];
        }

        get ignorePicker() {
            return this._ignorePicker;
        }

        get skipSetFire() {
            return this._skipSetFire;
        }

        _updateData(type) {
            const gizmo = this.gizmo;
            this._skipSetFire = true;
            state.gizmo = {
                type: type,
                size: gizmo.size,
                snapIncrement: gizmo.snapIncrement,
                xAxisColor: Object.values(gizmo.xAxisColor),
                yAxisColor: Object.values(gizmo.yAxisColor),
                zAxisColor: Object.values(gizmo.zAxisColor),
                colorAlpha: gizmo.colorAlpha,
                coordSpace: gizmo.coordSpace,
                axisLineTolerance: gizmo.axisLineTolerance,
                axisCenterTolerance: gizmo.axisCenterTolerance,
                ringTolerance: gizmo.ringTolerance,
                axisGap: gizmo.axisGap,
                axisLineThickness: gizmo.axisLineThickness,
                axisLineLength: gizmo.axisLineLength,
                axisArrowThickness: gizmo.axisArrowThickness,
                axisArrowLength: gizmo.axisArrowLength,
                axisBoxSize: gizmo.axisBoxSize,
                axisPlaneSize: gizmo.axisPlaneSize,
                axisPlaneGap: gizmo.axisPlaneGap,
                axisCenterSize: gizmo.axisCenterSize,
                xyzTubeRadius: gizmo.xyzTubeRadius,
                xyzRingRadius: gizmo.xyzRingRadius,
                faceTubeRadius: gizmo.faceTubeRadius,
                faceRingRadius: gizmo.faceRingRadius
            };
            this._skipSetFire = false;
        }

        add(node, clear = false) {
            if (clear) {
                this._nodes.length = 0;
            }
            if (this._nodes.indexOf(node) === -1) {
                this._nodes.push(node);
            }
            this.gizmo.attach(this._nodes);
        }

        clear() {
            this._nodes.length = 0;
            this.gizmo.detach();
        }

        switch(type) {
            this.gizmo.detach();
            this._type = type ?? 'translate';
            this.gizmo.attach(this._nodes);
            this._updateData(type);
        }

        destroy() {
            for (const type in this._gizmos) {
                this._gizmos[type].destroy();
            }
        }
    }

    const layers = app.scene.layers;
    const gizmoLayer = new pc.Layer({
        name: 'Gizmo',
        clearDepthBuffer: true,
        opaqueSortMode: pc.SORTMODE_NONE,
        transparentSortMode: pc.SORTMODE_NONE
    });
    layers.push(gizmoLayer);
    cameraEntity.camera.layers = cameraEntity.camera.layers.concat(gizmoLayer.id);

    const gizmoHandler = new GizmoHandler(app, cameraEntity.camera, gizmoLayer);
    gizmoHandler.switch('translate');
    // var hasnainBox = this.app.root.findByName("hasnainBox");
    // gizmoHandler.add(hasnainBox);
    window.focus();

    const setType = (value) => {
        state.gizmo.type = value;
        gizmoHandler.switch(value);
    };

    const keydown = (e) => {
        gizmoHandler.gizmo.snap = !!e.shiftKey;
        gizmoHandler.gizmo.uniform = !e.ctrlKey;
    };
    const keyup = (e) => {
        gizmoHandler.gizmo.snap = !!e.shiftKey;
        gizmoHandler.gizmo.uniform = !e.ctrlKey;
    };
    const keypress = (e) => {
        switch (e.key) {
            case 'x':
                state.gizmo.coordSpace = state.gizmo.coordSpace === 'world' ? 'local' : 'world';
                gizmoHandler.gizmo.coordSpace = state.gizmo.coordSpace;
                break;
            case '1':
                setType('translate');
                break;
            case '2':
                setType('rotate');
                break;
            case '3':
                setType('scale');
                break;
        }
    };
    window.addEventListener('keydown', keydown);
    window.addEventListener('keyup', keyup);
    window.addEventListener('keypress', keypress);

    const picker = new pc.Picker(app, canvas.clientWidth, canvas.clientHeight);
    const worldLayer = layers.getLayerByName('World');
    const pickerLayers = [worldLayer];

    const onPointerDown = (e) => {

        if (gizmoHandler.ignorePicker) {
            return;
        }

        if (picker) {
            picker.resize(canvas.clientWidth, canvas.clientHeight);
            picker.prepare(cameraEntity.camera, app.scene, pickerLayers);
        }

        const selection = picker.getSelection(e.clientX - 1, e.clientY - 1, 2, 2);
        if (!selection[0]) {
            gizmoHandler.clear();
            return;
        }
       // console.log("value of picker is ",selection[0].node);
      
        var nodeName = selection[0].node._parent._parent.name;
        var nodeNumber = parseInt(nodeName, 10);

        if (nodeNumber >= 1 && nodeNumber <= 18) {
            var selected = selection[0].node._parent._parent;
        }
        else
        {
            gizmoHandler.clear();
            return; 
        }      
        gizmoHandler.add(selected, !e.ctrlKey && !e.metaKey);
    };

 

    window.addEventListener('pointerdown', onPointerDown);

    app.on('destroy', () => {
        gizmoHandler.destroy();

        window.removeEventListener('keydown', keydown);
        window.removeEventListener('keyup', keyup);
        window.removeEventListener('keypress', keypress);
        window.removeEventListener('pointerdown', onPointerDown);
    });
};

I think @KPal is perhaps better placed to offer some advice here…

Hey @HasnainX so to do this you have to override the functionality of the transform-gizmo.js file. The function which sets the position is _setNodePositions. This updates the positions of the entities moved and then updates the gizmo based on the entities’ new position. I would recommend extending the TransformGizmo class to override this behavior

var GizmoManager = pc.createScript('gizmoManager');

GizmoManager.attributes.add('cameraEntity', { type: 'entity' });

GizmoManager.prototype.initialize = async function() {
   
    GizmoManager.Instance = this;

    const app = this.app;
    const canvas = app.graphicsDevice.canvas;
    const cameraEntity = this.cameraEntity;

    // Local state management
    this.state = {
        gizmo: {
            type: 'translate',
            coordSpace: 'world'
        },
        camera: {
            proj: pc.PROJECTION_PERSPECTIVE,
            fov: 45
        }
    };

    this.gizmoHandler = this.createGizmoHandler(app, cameraEntity);

    this.setupEvents();
    this.setupPicker();

    app.on('destroy', this.cleanupEvents.bind(this));
};

GizmoManager.prototype.createGizmoHandler = function(app, cameraEntity) {
    const layers = app.scene.layers;
    const gizmoLayer = new pc.Layer({
        name: 'Gizmo',
        clearDepthBuffer: true,
        opaqueSortMode: pc.SORTMODE_NONE,
        transparentSortMode: pc.SORTMODE_NONE
    });
    layers.push(gizmoLayer);
    cameraEntity.camera.layers = cameraEntity.camera.layers.concat(gizmoLayer.id);

    const gizmoHandler = new this.GizmoHandler(app, cameraEntity.camera, gizmoLayer);
    gizmoHandler.switch('translate');

    return gizmoHandler;
};

GizmoManager.prototype.setupEvents = function() {
    window.addEventListener('keydown', this.keydown.bind(this));
    window.addEventListener('keyup', this.keyup.bind(this));
    window.addEventListener('keypress', this.keypress.bind(this));
    window.addEventListener('pointerdown', this.onPointerDown.bind(this));
};

GizmoManager.prototype.cleanupEvents = function() {
    this.gizmoHandler.destroy();

    window.removeEventListener('keydown', this.keydown.bind(this));
    window.removeEventListener('keyup', this.keyup.bind(this));
    window.removeEventListener('keypress', this.keypress.bind(this));
    window.removeEventListener('pointerdown', this.onPointerDown.bind(this));
};

GizmoManager.prototype.setupPicker = function() {
    const app = this.app;
    const canvas = app.graphicsDevice.canvas;
    const picker = new pc.Picker(app, canvas.clientWidth, canvas.clientHeight);
    const worldLayer = app.scene.layers.getLayerByName('World');
    this.pickerLayers = [worldLayer];
    this.picker = picker;
};

GizmoManager.prototype.onPointerDown = function(e) {
    if (ButtonMainManager.Instance.EVENTTYPE == "uploadGIZMO") {
        if (this.gizmoHandler.ignorePicker) {
            return;
        }

        if (this.picker) {
            this.picker.resize(this.app.graphicsDevice.canvas.clientWidth, this.app.graphicsDevice.canvas.clientHeight);
            this.picker.prepare(this.cameraEntity.camera, this.app.scene, this.pickerLayers);
        }

        const selection = this.picker.getSelection(e.clientX - 1, e.clientY - 1, 2, 2);
        if (!selection[0]) {
            this.gizmoHandler.clear();
            return;
        }
        var nodeName = selection[0].node._parent._parent.name;
        var nodeNumber = parseInt(nodeName, 10);

        if (nodeNumber >= 1 && nodeNumber <= 18) {
            var selected = selection[0].node._parent._parent;
        } else {
            this.gizmoHandler.clear();
            return;
        }

        this.gizmoHandler.add(selected, !e.ctrlKey && !e.metaKey);
    }

};

GizmoManager.prototype.keydown = function(e) {
    this.gizmoHandler.gizmo.snap = !!e.shiftKey;
    this.gizmoHandler.gizmo.uniform = !e.ctrlKey;
};

GizmoManager.prototype.keyup = function(e) {
    this.gizmoHandler.gizmo.snap = !!e.shiftKey;
    this.gizmoHandler.gizmo.uniform = !e.ctrlKey;
};

GizmoManager.prototype.keypress = function(e) {
    switch (e.key) {
        case 'x':
            this.state.gizmo.coordSpace = this.state.gizmo.coordSpace === 'world' ? 'local' : 'world';
            this.gizmoHandler.gizmo.coordSpace = this.state.gizmo.coordSpace;
            break;
        case '1':
            this.setType('translate');
            break;
        case '2':
            this.setType('rotate');
            break;
        case '3':
            this.setType('scale');
            break;
    }
};

GizmoManager.prototype.setType = function(value) {
    this.state.gizmo.type = value;
    this.gizmoHandler.switch(value);
};

// GizmoHandler class
GizmoManager.prototype.GizmoHandler = class {
    _type = 'translate';
    _gizmos;
    _nodes = [];
    _ignorePicker = false;
    _skipSetFire = false;

    constructor(app, camera, layer) {
        this._gizmos = {
            translate: new pc.TranslateGizmo(app, camera, layer),
            rotate: new pc.RotateGizmo(app, camera, layer),
            scale: new pc.ScaleGizmo(app, camera, layer)
        };

        for (const type in this._gizmos) {
            const gizmo = this._gizmos[type];
            gizmo.on('pointer:down', (x, y, meshInstance) => {
                this._ignorePicker = !!meshInstance;
            });

            gizmo.on('pointer:up', () => {
                this._ignorePicker = false;
            });
        }
    }

    get gizmo() {
        return this._gizmos[this._type];
    }

    get ignorePicker() {
        return this._ignorePicker;
    }

    get skipSetFire() {
        return this._skipSetFire;
    }

    _updateData(type) {
        const gizmo = this.gizmo;
        this._skipSetFire = true;
        GizmoManager.Instance.state.gizmo = {
            type: type,
            size: gizmo.size,
            snapIncrement: gizmo.snapIncrement,
            xAxisColor: Object.values(gizmo.xAxisColor),
            yAxisColor: Object.values(gizmo.yAxisColor),
            zAxisColor: Object.values(gizmo.zAxisColor),
            colorAlpha: gizmo.colorAlpha,
            coordSpace: gizmo.coordSpace,
            axisLineTolerance: gizmo.axisLineTolerance,
            axisCenterTolerance: gizmo.axisCenterTolerance,
            ringTolerance: gizmo.ringTolerance,
            axisGap: gizmo.axisGap,
            axisLineThickness: gizmo.axisLineThickness,
            axisLineLength: gizmo.axisLineLength,
            axisArrowThickness: gizmo.axisArrowThickness,
            axisArrowLength: gizmo.axisArrowLength,
            axisBoxSize: gizmo.axisBoxSize,
            axisPlaneSize: gizmo.axisPlaneSize,
            axisPlaneGap: gizmo.axisPlaneGap,
            axisCenterSize: gizmo.axisCenterSize,
            xyzTubeRadius: gizmo.xyzTubeRadius,
            xyzRingRadius: gizmo.xyzRingRadius,
            faceTubeRadius: gizmo.faceTubeRadius,
            faceRingRadius: gizmo.faceRingRadius
        };
        this._skipSetFire = false;
    }

    add(node, clear = false) {
        if (clear) {
            this._nodes.length = 0;
        }
        if (this._nodes.indexOf(node) === -1) {
            this._nodes.push(node);
        }
        this.gizmo.attach(this._nodes);
    }

    clear() {
        this._nodes.length = 0;
        this.gizmo.detach();
    }

    switch(type) {
        this.gizmo.detach();
        this._type = type ?? 'translate';
        this.gizmo.attach(this._nodes);
        this._updateData(type);
    }

    destroy() {
        for (const type in this._gizmos) {
            this._gizmos[type].destroy();
        }
    }
};

for gizmo Part ,I’m using this code so how do we add those code which you told me :slight_smile:

So instead of using TranslateGizmo you would define your own class as such

const tmpV1 = new pc.Vec3();
const tmpV2 = new pc.Vec3();
const tmpQ1 = new pc.Quat();

class CollideTranslateGizmo extends pc.TranslateGizmo {
    /**
     * @param {Vec3} pointDelta - The delta to apply to the node positions.
     * @private
     */
    _setNodePositions(pointDelta) {
        for (let i = 0; i < this.nodes.length; i++) {
            const node = this.nodes[i];
            const pos = this._nodePositions.get(node);
            if (!pos) {
                continue;
            }
            if (this._coordSpace === pc.GIZMO_LOCAL) {
                tmpV1.copy(pointDelta);
                node.parent?.getWorldTransform().getScale(tmpV2);
                tmpV2.x = 1 / tmpV2.x;
                tmpV2.y = 1 / tmpV2.y;
                tmpV2.z = 1 / tmpV2.z;
                tmpQ1.copy(node.getLocalRotation()).transformVector(tmpV1, tmpV1);
                tmpV1.mul(tmpV2);

                node.setLocalPosition(pos.clone().add(tmpV1)); // Modify this line for local translation
            } else {

                node.setPosition(pos.clone().add(pointDelta)); // Modify this line for world translation
            }
        }

        this._updatePosition();
    }
}

And then in line 149 of your example code you would replace pc.TranslateGizmo with this new CollideTransformGizmo

Thanks for this . I will check this and let you know if I get some issue or fix :slightly_smiling_face:

2 Likes