Instance rotation?

I’m experimenting with adding instance rotation in i3dm tiles, but it’s unclear to me how it works.

In https://bertt.github.io/cesium_3dtiles_samples/samples/instanced/index_north.html I’ve added 1 i3dm tile (seat_north.i3dm) with 1 instance (a seat, ecef location 1214947.2, -4736379, 4081540.8) pointing to the north and no NORMAL_UP and NORMAL_RIGHT defined. I’m using EAST_NORTH_UP = true otherwise the seat is wrongly rotated. Result looks ok.

Now I want to rotate the seat horizontally pointing eastwards. As a first test I’ve added NORMAL_UP (0, 1, 0) and NORMAL_RIGHT (1, 0, 0) to the i3dm tile (seat_east.i3dm). Now the seat is rotated strangely, I was expecting the same result as the seat pointing north (because no rotation with these normals). Result see https://bertt.github.io/cesium_3dtiles_samples/samples/instanced/index_east.html

So my question is: How do I calculate the NORMAL_UP and NORMAL_RIGHT vector so the seat is rotating horizontally to the east?

Bonusquestion: Why is the color of the seat different everytime I load Cesium??

Chair facing north I assume the seat normals would be (in respects to the local ENU frame)
normal_dir: (0,1,0) pointing north
normal_right: (1,0,0) pointing east
normal_up: (0,0,1) pointing up

Chair facing east should be:
normal_dir: (1,0,0) pointing east
normal_right: (0,-1,0) pointing south
normal_up: (0,0,1) pointing up (unchanged)

If needed these vectors could be transformed to the global Earth frame.

The way you set it probably had the chair’s front lying on the ground
NORMAL_UP (0, 1, 0) up pointing north
NORMAL_RIGHT (1, 0, 0) right pointing east

Hi, ok thanks, if I use your suggestion it does not rotate well:

normal_right: (0,-1,0) pointing south
normal_up: (0,0,1) pointing up (unchanged)

“If needed these vectors could be transformed to the global Earth frame.” Interesting, how can I do that? Do i need to provide the normal_right and normal_up in i3dm in local or global earth frame?

This is a bit long, I’m sure there’s a Cesium matrix function that can do these rotation transforms, just not sure which function name as I’ve been using my math library lately. So trying it ‘long hand’ by just using Cartesian3 math. This is untested code, so let me know if it works.

EDIT: what was I thinking, Cesium Cartesian3s are not stored in arrays like matrices, rather objects with x,y,z properties…Ignore this code, updated code posted later on this thread.

//get ENU vectors in terms of Earth Fixed
GD_transform = Cesium.Transforms.eastNorthUpToFixedFrame(position, viewer.scene.globe.ellipsoid, new Cesium.Matrix4());
GD_E=new Cesium.Cartesian3(GD_transform[0],GD_transform[1],GD_transform[2]);
GD_N=new Cesium.Cartesian3(GD_transform[4],GD_transform[5],GD_transform[6]);
GD_U=new Cesium.Cartesian3(GD_transform[8],GD_transform[9],GD_transform[10]);

//get chair vectors in terms of ENU frame
lNorth = new Cesium.Cartesian3(1,0,0);
lEast = new Cesium.Cartesian3(0,-1,0);

//vector declarations
let c1=[];var c2=[];let c3=[];let temp=[];
let EFnorth=[]; let EFeast=[];

//get EFnorth from lNorth
Cesium.Cartesian3.multiplyByScalar(GD_E, lNorth.x, c1);
Cesium.Cartesian3.multiplyByScalar(GD_N, lNorth.y, c2);
Cesium.Cartesian3.multiplyByScalar(GD_U, lNorth.z, c3);
Cesium.Cartesian3.add(c1, c2, temp); //c1+c2 result to temp
Cesium.Cartesian3.add(temp, c3, EFnorth); //temp+c3 result to EFnorth

//get EFeast from lEast
Cesium.Cartesian3.multiplyByScalar(GD_E, lEast.x, c1);
Cesium.Cartesian3.multiplyByScalar(GD_N, lEast.y, c2);
Cesium.Cartesian3.multiplyByScalar(GD_U, lEast.z, c3);
Cesium.Cartesian3.add(c1, c2, temp); //c1+c2 result to temp
Cesium.Cartesian3.add(temp, c3, EFeast); //temp+c3 result to EFnorth

//get EFup (the chair is upright)
let EFup=GD_U;

EDIT:
Actually since you already know EFup, just find either EFnorth or EFeast, then use cross to find the 3rd
Cesium.Cartesian3.cross(left, right, result)

1 Like

I’ve tried your code with position = new Cesium.Cartesian3(1214947.2, -4736379, 4081540.8).

Result:
EFup: x: 0.19022214020877842, y: -0.7415665061164088, z: 0.6433464497303876
EFeast: x: 0.1598520208967612, y: -0.6231709121869502, z: -0.765575173068789

When using these values in the i3dm for NORMAL_UP and NORMAL_RIGHT (is this ok?), it looks like the chair is fallen on the back :frowning:

back

Looks like we’re getting close with this…

Note: the actual usecase is rotating horizontally to an angle (angle between 0 and 360, 0 == north), so not just rotating east.

Perhaps the model’s up vector is not through the back support but rather through the seat. If that’s the case maybe try:
lEast = new Cesium.Cartesian3(0,a,b); //unknown, but make sure it’s orthogonal to the other 2
lNorth = new Cesium.Cartesian3(0,a,b); //unknown, but make sure it’s orthogonal to the other 2
lUp = new Cesium.Cartesian3(1,0,0); //model’s up vector facing east

some combinations to try for a and b (some of these might break the right hand rule)
a=1;b=0;
a=0;b=1;
a=-1;b=0;
a=0;b=-1;

I made a transform function to save code lines. Earlier I mistakenly declared Cartesian3 as arrays, when they actually have xyz object properties, I suppose you already made the correction? I altered the code accordingly

function vectorTransform(lv, tm, ov)
{
	//lv local vector, tm transform matrix, ov output vector
	let GD_E=new Cesium.Cartesian3(tm[0],tm[1],tm[2]);
	let GD_N=new Cesium.Cartesian3(tm[4],tm[5],tm[6]);
	let GD_U=new Cesium.Cartesian3(tm[8],tm[9],tm[10]);

	let c1= new Cesium.Cartesian3();let c2= new Cesium.Cartesian3();
	let c3= new Cesium.Cartesian3();let temp= new Cesium.Cartesian3();

	Cesium.Cartesian3.multiplyByScalar(GD_E, lv.x, c1);
	Cesium.Cartesian3.multiplyByScalar(GD_N, lv.y, c2);
	Cesium.Cartesian3.multiplyByScalar(GD_U, lv.z, c3);
	Cesium.Cartesian3.add(c1, c2, temp); //c1+c2 result to temp
	Cesium.Cartesian3.add(temp, c3, ov); //temp+c3 result to ov
} 

//vector declarations
let EFnorth= new Cesium.Cartesian3();
let EFeast= new Cesium.Cartesian3();
let EFup= new Cesium.Cartesian3();

//local vectors (un-comment the combo to try)
y1=1; z1=0; y2=0; z2=1;
//y1=0 ;z1=-1; y2=-1; z2=0;
//y1=-1 ;z1=0; y2=0; z2=-1;
//y1=0 ;z1=1; y2=1; z2=0;
let lEast = new Cesium.Cartesian3(0,y1,z1); //unknown, but make sure it's orthogonal to the other 2
let lNorth = new Cesium.Cartesian3(0,y2,z2); //unknown, but make sure it's orthogonal to the other 2
let lUp = new Cesium.Cartesian3(1,0,0); //model's up vector facing east
/* old code segment (what was I thinking)
a=1;b=0;
//a=0;b=1;
//a=-1;b=0;
//a=0;b=-1;
let lEast = new Cesium.Cartesian3(0,a,b); //unknown, but make sure it's orthogonal to the other 2
let lNorth = new Cesium.Cartesian3(0,a,b); //unknown, but make sure it's orthogonal to the other 2
let lUp = new Cesium.Cartesian3(1,0,0); //model's up vector facing east
*/

GD_transform = Cesium.Transforms.eastNorthUpToFixedFrame(position, viewer.scene.globe.ellipsoid, new Cesium.Matrix4());

vectorTransform(lEast,GD_transform,EFeast);
vectorTransform(lNorth,GD_transform,EFnorth);
vectorTransform(lUp,GD_transform,EFUp);

In Blender the seat.glb looks like:

I’ve tried your last code with position = new Cesium.Cartesian3(1214947.2, -4736379, 4081540.8), the third option (a=-1;b=0) gives good result :slight_smile:

yeah

normals used:

normal_up (from EFup): {x: 0.9686397001928082, y: 0.24846957803801, z: 0}
normal_right (from EFeast): {x: 0.1598520208967612, y: -0.6231709121869502, z: -0.765575173068789}

Thanks for your help! Next week I’ll try to get all the rotations (seat angles between 0-360) working well.

Looks like you’ve caught another error of mine. Instead of a,b I meant something like this (updated the above code already):
(0,y1,z1)
(0,y2,z2)
(1,0,0)
with combos like
y1=1; z1=0; y2=0; z2=1;
y1=0 ;z1=-1; y2=-1; z2=0;
y1=-1 ;z1=0; y2=0; z2=-1;
y1=0 ;z1=1; y2=1; z2=0;

With a=-1 b=0 that would mean
let lEast = new Cesium.Cartesian3(0,-1,0);
let lNorth = new Cesium.Cartesian3(0,-1,0);

Which have should have failed as they are not orthogonal, unless you just used one of them in which case it should be fine. Did you just use the east or north by chance? I’m wondering if the backrest is the right or forward vector, as it would appear that the up vector is the seat (Blender y to Cesium up)

I’m glad that it is working! Can rotate vectors about other vectors to get non-cardinal rotations.

yes in the working version I used only EFup (for normal_up in i3dm tile) and EFeast (for normal_right in i3dm tile) and not EFnorth. I suppose EFup and EFeast are orthogonal.

Ah OK. If orthogonal their dot product would be zero (or very close to) which they are.

So for an upright chair facing east:
lEast (0,-1,0) right side of chair facing south
lUp (1,0,0) seat of chair facing east

then this should be the case:
lNorth (0,0,-1) chair legs facing down

I assume you’d want to rotate it around it’s vertical axis (LNorth in this case.) This should be doable just by using sin and cos.

let deg=0; //in degrees (east is 0 degrees, range: -180 to +180)
let rad=deg*Math.PI/180; //radians
let lUp=(Math.Cos(rad),Math.Sin(rad),0);
let lEast=(Math.Cos(rad-Math.PI/2),Math.Sin(rad-Math.PI/2),0);

then convert local to global

1 Like

FYI: got it working now for seats in a stadium (note with random seats subset) :slight_smile:

Awesome! Can set the direction of the seats as well? They seem to all be facing roughly the same direction in the image.

yes they are all rotated to an angle (seat facing the field)

1 Like

Hi @Hyper_Sonic , a related question: this time I want to rotate a b3dm model (lets say facing east again), I think I have to change the transform values in tileset.json. At the moment I only use the GD_transform values in the transform to place the model correctly. Should I compute normal_up and normal_right again to rotate the model? How do I put it in the transform?

I think I’d need more information. The only json file I see is the one referenced in your link at the top of the thread bertt.github.io/cesium_3dtiles_samples/samples/instanced/tileset_north.json
The only thing there that looked remotely like vectors was region, but I discovered it was not:
https://docs.opengeospatial.org/cs/18-053r2/18-053r2.html#32
[west, south, east, north, minimum height, maximum height]
So the first 4 are lon/lat values in radians, and the last 2 are heights in meters.

Without further data, I’d say just try simple local orientation vectors again then transform to the local ENU transform and see what it takes to orient upright facing east again.

ah yes, I’m trying to rotate the building.b3dm model in https://bertt.github.io/cesium_3dtiles_samples/samples/b3dm/index.html
tileset.json: https://bertt.github.io/cesium_3dtiles_samples/samples/b3dm/tileset.json

If I modify the boundingVolume.box elements, only the white debug boxes are moved/rotated, but not the model :frowning:

So I think I need to change the transform array somehow. The transform now contains GD_transform of lon=-75.61 lat = 40.04 . As alternative I’m considering to rotate the glTF model before putting it in the b3dm, but I hope there is a better way…

I’ve tried messing with it using the console, but I can’t seem alter rotations though. I typed this into the console

var cr=viewer.scene.camera.right;
var cd=viewer.scene.camera.direction;
var cu=viewer.scene.camera.up;
var cp=viewer.scene.camera.position;

I then tried both of the following with to no avail (move house to camera)

tileset.modelMatrix={0: cr.x, 1: cr.y, 2: cr.z, 3: 0, 4: cd.x, 5: cd.y, 6: cd.z, 7: 0, 8: cu.x, 9: cu.y, 10: cu.z, 11: 0, 12: cp.x, 13: cp.y, 14: cp.z, 15: 1};
tileset._modelMatrix={0: cr.x, 1: cr.y, 2: cr.z, 3: 0, 4: cd.x, 5: cd.y, 6: cd.z, 7: 0, 8: cu.x, 9: cu.y, 10: cu.z, 11: 0, 12: cp.x, 13: cp.y, 14: cp.z, 15: 1};

Odd thing is you can move it around a bit, this moves it up a bit:

tileset.modelMatrix={0: 1, 1: 0, 2: 0, 3: 0, 4: 0, 5: 1, 6: 0, 7: 0, 8: 0, 9: 0, 10: 1, 11: 0, 12: 0, 13: 0, 14: 20, 15: 1};

Then this moves it up and right a bit:

tileset.modelMatrix={0: 1, 1: 0, 2: 0, 3: 0, 4: 0, 5: 1, 6: 0, 7: 0, 8: 0, 9: 0, 10: 1, 11: 0, 12: 40, 13: 0, 14: 20, 15: 1};

I’ve tried different rotations, but the house wouldn’t appear

//rotate 90deg CW around z
tileset.modelMatrix={0: 0, 1: -1, 2: 0, 3: 0, 4: 1, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 1, 11: 0, 12: 0, 13: 0, 14: 0, 15: 1};

//rotate 90deg CCW around z
tileset.modelMatrix={0: 0, 1: 1, 2: 0, 3: 0, 4: -1, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 1, 11: 0, 12: 0, 13: 0, 14: 20, 15: 1};

That transform in the JSON is entered to _clippingPlanesOriginMatrix
Not sure if that’s causing the house to disappear on rotations.

I’ve even tried

tileset.root.children[0]._tileset.modelMatrix={0: cr.x, 1: cr.y, 2: cr.z, 3: 0, 4: cd.x, 5: cd.y, 6: cd.z, 7: 0, 8: cu.x, 9: cu.y, 10: cu.z, 11: 0, 12: cp.x, 13: cp.y, 14: cp.z, 15: 1};
tileset.root.children[0]._tileset._modelMatrix={0: cr.x, 1: cr.y, 2: cr.z, 3: 0, 4: cd.x, 5: cd.y, 6: cd.z, 7: 0, 8: cu.x, 9: cu.y, 10: cu.z, 11: 0, 12: cp.x, 13: cp.y, 14: cp.z, 15: 1};

That transform in the JSON file. I believe that is the local ENU transform for that location. Yes, it is, If I place the camera near the house and type this in the console (EDIT: reading back on your previous post I should have known!)

Cesium.Transforms.eastNorthUpToFixedFrame(viewer.scene.camera.position, viewer.scene.globe.ellipsoid, new Cesium.Matrix4());

I get that transform. So the modelMatrix position apparently is transformed by that transform. Hmm, now how to rotate it. My rotation attempts should have worked, where I tried to rotate 90deg in either direction. Those new rotation vectors should have been transformed just like the position vector was. In any event you should see the house, even if at a funny orientation.

Try replacing the transform in the json with this. I took the one that’s there now and rotated it 90deg around it’s up vector. Possibly it’ll rotate the house 90deg, who knows. (corrected, first time I rotated 90 radians!)

[
0.5719022525624771,
0.6163388209036162,
0.541344904867667,
0,
-0.7979626193589172,
0.2649554064190438,
0.5413449162659866,
0,
0.19021961055045852,
-0.7415693387513873,
0.6433439241941505,
0,
1214931,
-4736397,
4081525,
1
]

Ok thanks, I see the house rotating indeed :slight_smile:

What formula did you use for this? For the up vector I’ve calculated:

(lon, lat, alt) = -75.6132, 40.0416, 0
ecef = 1214931, -4736397, 4081525
transform = 0.9686, 0.2484, 0, 0, -0.1598, 0.6231, 0.765,0,0.1902, -0.7415, 0.6433, 0, 1214931, -4736397, 4081525, 1
east = 0.9686, 0.2484, 0
up = -0.1598, 0.6231, 0.7655