Entity picking without physics on the rotated cube doesn't work

Hi ,
I have more than 100 cubes in my scene
I have to be able to select each one with Mouse
But adding rigidbody to this number of cubes and using raycast to select them , reduced the fps on lower devices
I want to use this project, to do this without rigidbody and physics

But when I rotate the cubes, this code again takes into account the cube that did not rotate

I even changed the code using this solution

BoundingBoxShape.prototype.initialize = function () {
    // Create the AABB and move the position of the box by the offset
    this.aabb = new pc.BoundingBox(this.entity.getPosition(), this.halfExtents);
};

BoundingBoxShape.prototype.postInitialize = function () {


    this.app.fire("shapepicker:add", this.entity, this.aabb);
};

BoundingBoxShape.prototype.postUpdate = function (dt) {
    this.aabb.center.copy(this.entity.getPosition());
    this.aabb.halfExtents.copy(this.halfExtents);

    this.aabb = this.entity.render.meshInstances[0].aabb;

};

But it does not work properly when rotating the cubes

pc.BoundingBox is an axis aligned bounding box so it’s not designed to be rotated. You would want pc.OrientedBox instead where you updated the world transform every frame: OrientedBox | PlayCanvas API Reference

So it be something like this (untested):

var transform = new pc.Mat4().setTRS(this.entity.getPosition(), this.entity.getRotation(), pc.Vec3.ONE);
var extents = this.entity.getLocalScale().clone().mulScalar(0.5);

this.box = new pc.OrientedBox(transform, extents);

// Then update it every frame
this.box.worldTransform.setTRS(this.entity.getPosition(), this.entity.getRotation(), pc.Vec3.ONE);

2 Likes

It seems that it is not possible to use setTRS directly on the worldTransform field of OrientedBox, I received strange behavior during raycasts.
After searching in the documentation and engine code, it turned out that certain logic is performed only in setter of worldTransform (~also in constructor)

Therefore, to update the transform for logic, you need to do one of these:

  1. Create a new Mat4() every time.

  2. Use the _modelTransform and _worldTransform directrly.

  3. Just reassign the worldTransform to yourself (this is better than manually changing private fields in case some logic is added to the engine).

Did I miss anything important?

My code for option 3 is below

var BoundingOrientedBoxShape = pc.createScript('boundingOrientedBoxShape');

BoundingOrientedBoxShape.attributes.add("dynamic", {type: "boolean"});
BoundingOrientedBoxShape.attributes.add("halfExtents", {type: "vec3"});
BoundingOrientedBoxShape.attributes.add("positionOffset", {type: "vec3"});
BoundingOrientedBoxShape.attributes.add("eulerRotation", {type: "vec3"});

BoundingOrientedBoxShape.prototype.initialize = function() {
    this.entity.boundingShape = new pc.OrientedBox();
    this.entity.boundingShape.halfExtents.copy(this.halfExtents);
    this.updateShapeData();
    
    this.on('enable', function() {this.updateShapeData(null)});
    if(this.dynamic){ 
        this.app.on("main:update", this.updateShapeData, this);
        this.on('destroy', function() {
            this.app.off("main:update", this.updateShapeData, this);
        });
    }
};

BoundingOrientedBoxShape.prototype.updateShapeData = function(dt) {  
    this.entity.boundingShape.worldTransform.setTRS(
        this.entity.getPosition().clone()
            .add(this.entity.right.clone().mulScalar(this.positionOffset.x))
            .add(this.entity.up.clone().mulScalar(this.positionOffset.y))
            .add(this.entity.forward.clone().mulScalar(this.positionOffset.z)),
        this.entity.getRotation().clone().mul(new pc.Quat().setFromEulerAngles(this.eulerRotation)),
        pc.Vec3.ONE);
    this.entity.boundingShape.worldTransform = this.entity.boundingShape.worldTransform;
};

There should be no problem using .setTRS() on an OrientedBox. The method that Yaustar suggested should work fine. I would only add that you don’t need to update the matrix every frame. It can be done just once, during the raycast, before doing the intersection test.

You are probably getting an error because the this.pickableShapes includes a BoundingSphere, which doesn’t have a worldTransform property.

I described the problem and three ways to solve it, if you call a worldTranform change without assigning it - the _modelTransform will not be updated, which manifested to me itself as a strange behavior during raycasts.

To prevent this from happening, I reassign the transform to itself so that the setter mentioned above is executed.

//after changing this.entity.boundingShape.worldTransform (in my case by setTRS)
this.entity.boundingShape.worldTransform = this.entity.boundingShape.worldTransform;

By the way, I don’t update all objects every frame, I added such an option for dynamic objects (check for obstacles collision with the player) and update other objects (that do not change their position every frame) when I need it by my method or on “enable” event (ground platforms).

1 Like