Decals and angle

Hello!

I have a problem. I try to create a decal for mesh from the plane. And it is pretty simple until we will not try to rotate decal corresponding to mesh rotation. I attach the playground here:

It is without setting of angle:
Снимок экрана от 2024-07-26 18-13-14
It is if we set an angle:
Снимок экрана от 2024-07-26 18-14-26
And it is what I expect:
expect

So basically I just want to rotate the decal to the same angle with the mesh that i used to read data about normal and position.

What I tried to do is convert world angles to local space of the mesh normal (I quess decal rotating in terms of Y of its normal). But I understand that somewhere I make a mistake. Could you please help me if you know how to do it or know what’s wrong with my code

Your PG is a little complicated. What are you trying to do with decalInfoSource? Your variables localXAxis appear to be world-space coordinates. CreateDecal uses world space, so your use of world-space coordinates, though named “local*Axis,” seem correct. When I run with decal parameter “angle” as 0, it is different than your first image, appearing 180 degrees rotated.

Edit: actually, for me the decal is reversed when angle is 0, from your images.

1 Like

I was just looking as well and had the same observations. I think something in your local PG where you got the screenshots must be different from the PG link you shared.

Is the issue just the direction of rotation? The rotation will be inverted if you flip the y axis, which you seem to be doing. Also, is any transformation needed for the angle of rotation? The angle you are looking for is the same regardless of whether you are in local or world space, right?

1 Like

I think OP is trying to match the decal position and orientation to that of decalImageSource. My thought is to verify expected orientation of decal with no rotations applied to either decalInfoSource or the decal.

My guess is that the error is somewhere in the multiple meshes and world/local transformations.

1 Like

Yes, local axis are not actually local. It is the axis of the mesh in world coordinates (At least that’s what I intended :smiley:). So it is local axis of mesh in WorldSpace. What I try to do is assign a proper angle to rotate the decal corresponds to the source mesh. But I don’t know whats wrong with my solution. I quess I cant work with euler angles as with simple vectors when make transformation of coordinates. But not sure…Anyway what i want is just to rotate created decal corresponding to the source mesh

RE inverted Y Axis. It is because I use right handled system in original system. So I represent on PG as close as possible. And in rhs if you will not invert the normal decal will be painted from the another side of surface. I’m not sure if it is bug or expected behaviour

Im taking a guess at what is wanted …

you can change these …

 let planeZAngle = BABYLON.Angle.FromDegrees(20).radians()
 let planeXAngle = BABYLON.Angle.FromDegrees(20).radians()

the decal will be correct , it uses the normal , and the decal angle is negative planeZAngle …

EDIT:

take note , i did use this as well :

decalTx.uScale = -1

@shaderbytes
Thank you!
It looks like working solution. But what if I already have the rotation of source mesh in space. How can I get this planeZ and planeX angles. Should I just use absoluteRotationQuaternion.toEulerAngles().x and z?

try it and see what happens :wink:

( ps… I only used these two angles , but you could use y angle as well. x and y angles will determine/affect the normal direction of the decal , only z angle determines the rotation of the decal , well in this situation )

@shaderbytes
I don’t understand how it should works. I try to do smth like that using GPT for help. The function seems to be proper. But It still doesnt work properly in some cases but seems to should(

    protected getRotationAnglesInTermsOfAxis(axis: Vector3, quaternion: Quaternion): { x: number, y: number, z: number } {
        axis.normalize();

        const rotationMatrix = new Matrix();
        quaternion.toRotationMatrix(rotationMatrix);

        const rotation = Vector3.Zero();
        const rotMatQuaternion = Quaternion.Identity();
        rotationMatrix.decompose(undefined,rotMatQuaternion);
        rotation.copyFrom(rotMatQuaternion.toEulerAngles());

        const axisInLocalX = Vector3.TransformCoordinates(axis, Matrix.RotationX(rotation.x));
        const axisInLocalY = Vector3.TransformCoordinates(axis, Matrix.RotationY(rotation.y));
        const axisInLocalZ = Vector3.TransformCoordinates(axis, Matrix.RotationZ(rotation.z));

        const angleX = Vector3.Dot(axisInLocalX, Axis.X) * rotation.x;
        const angleY = Vector3.Dot(axisInLocalY, Axis.Y) * rotation.y;
        const angleZ = Vector3.Dot(axisInLocalZ, Axis.Z) * rotation.z;

        return {
            x: angleX,
            y: angleY,
            z: angleZ
        };
    }

I update playground using this method:

But some of decals still dont match the sources properly. I quess that problem in convertion of quaternion to euler angles. I think that I pretty close to solution. But can’t understand how make it works) What do you think about that?

I defenetelly don’t clearly understand what I do) :smiling_face_with_tear:

As I understand to rotate decal to the proper angle I should do the following:

  1. Get source mesh orientation (localYAxisInWorldTerms) normal of the mesh in world coordinates
  2. Get the source mesh rotaion (using absolute rotation quaternion because I dont know what rotation source mesh will have in real case)
  3. Represent the rotation of the mesh in terms of localYAxisInWorldTerms
  4. Get the angle by Y axis
  5. Assign this angle to decal

I dont know on which steps I done mistakes (quess that at step 3)

I made a PG where decals assigns in real time to better understand what actually happens (warn it takes quite a lot performance). I see that on some point of rotation decals like inverts their rotation. I quess it is because of transforming of Quaternion to Euler angles

im not spending time on your chatGPT code PG , I thought you only had issues with rotating the decal , but didnt do the scene with projecting , so I updated my code to do projection and also used two decals.

Stop over complicating all you stuff :wink:

The projection is based on the normal. How far it projects is based on the size.z option .

https://doc.babylonjs.com/features/featuresDeepDive/mesh/decals

Take note my mesh planes are not at perfect tangent angles to the sphere. I just generally placed and rotated them , so some distortion will be visible.

Getting the best angle to the target mesh would pobably involve some ray casting

Thank you for reply!

I make like you made but it doesnt work for me for some reason. I don’t clearly understand what happend. I just used predefined rotation angles of mesh instead of defined plane angles like in your example but my normals looks to sides and projection works wrong(

If I use Y axis to project decals normals looks properly, but angle of decal applied wrong

I quess the problem with an normal appears because I use ground mesh. And by default it oriented differently with plane. But why angles are not applied properly in my case for pg:

And applied in your case I have no idea

something is not working here , I stripped down your earlier scene to just one mesh for projection , i kept it as a ground plane and I kept your values for rotation and position

the required angle of the decal is not any of the angles from the mesh rotation. If you hard code a value of ~0.7 for the decal it is about right , im not sure why its not working. We need a second pair of eyes on this.

something with those plane objects being ground planes … it must be because when using regular planes it is working ,

im not sure why the op needs ground objects , i know they have different orientation , so i just tried switching axis but it did work.

@sebavan , do you perhaps know who in the community knows the decal system well? or rather , why when using ground planes it doesnt work like it does with normal planes

I spoke with my colleague and we found very crutched way to do that. It is not good solution it is just “works”. For sure I prefer another way of doing it)))

it is the way) :sweat_smile:

            const worldMatrix = mesh.getWorldMatrix();
            const localXAxisInWorldTerms = Vector3.TransformNormal(new Vector3(1, 0, 0), worldMatrix);
            const localYAxisInWorldTerms = Vector3.TransformNormal(new Vector3(0, -1, 0), worldMatrix);
            
            let decalProbe = MeshBuilder.CreateDecal(
                "decalProbe",
                mesh.parent! as Mesh,
                {
                    position: mesh.getAbsolutePosition(),
                    normal: localYAxisInWorldTerms,
                    size: mesh.absoluteScaling,
                    angle:0
                }
            );
            const localYProbeAxis = Vector3.TransformNormal(new Vector3(0, 1, 0), decalProbe.getWorldMatrix());
            let angle = Vector3.GetAngleBetweenVectors(localXAxisInWorldTerms,localYProbeAxis,localYAxisInWorldTerms);
            decalProbe.dispose();
            //state for creating of final decal
            let state = {
                id          : mesh.id,
                position    : mesh.getAbsolutePosition(),
                scaling     : mesh.absoluteScaling,
                normal      : localYAxisInWorldTerms,
                angle       : angle%180+Math.PI/2
            };

Just create a probe decal read the local axis in world space and then found the angle between corresponding angles

@simon

haha that seems very convoluted :wink:

I mean , I did do a simple working solution using planes right? here it is again but using your updated texture :

the angle of the decal is set from the mesh rotation without any problems…

In real situation I just cant use planes( Because I use meshes from .glb file to assign decals. And I try to use the way you demonstrate but it doesnt work with ground mesh). And doesnt work with meshes that I use from .glb. But your example is defenetelly more attractive