EduGraf Tutorial: Transformations and scene hierarchy
How to place objects in the scene
In this tutorial, we want to learn more about how to place objects in the scene.
All objects are initially defined in their own coordinate system. They are usually, due to best practice, placed in the center within the box in the interval [-1, +1]. Obviously, we do not want all objects at the origin. The basic transformations that are available are scale, rotate and translate. They are best applied in that order, because rotation and translation produce different results when applied in different order. Scale changes the size of the object, rotate changes its orientation in space and translate moves it to a different location.
Make the world go round
We continue the hello world tutorial. Override the virtual OnUpdateFrame() method of the base rendering class to update the graphic at a high enough frequency that is not perceptible to the eye. The code gets the current timestamp and calculates the change in the angle since the last timestamp. The velocity is chosen such that the sphere rotates around itself once every minute.
private long _lastUpdate = DateTime.Now.Ticks;
protected override void OnUpdateFrame(Window window)
{
const float velocity = MathF.PI / (30 * TimeSpan.TicksPerSecond);
long now = DateTime.Now.Ticks;
var deltaAngle = velocity * (now - _lastUpdate);
_lastUpdate = now;
_earth?.RotateY(deltaAngle);
base.OnUpdateFrame(window);
}
Actually, the rotation axis of our planet is tilted with respect to the orbital axis by 23.4 degrees. The method above defines the relative update in rotation since its previous call. The additional task of tilting the earth axis can be solved more elegantly by the following approach. Calculate the rotation as a function of absolute time and then set the object's Transform property with a matrix calculated by Matrix4.RotationY() instead of calling a rotation method. We will however not continue this.
Simulate the earth system
Let us add a moon to the earth. For that we need another sphere. We take it easy and just give it a gray fully diffuse material. Furthermore, we need to scale it to have the right proportions with respect to earth. The moon diameter is 3'470 km and earth is 12'700 km. Thus, we need to scale the moon by 0.273. Furthermore the moon is at distance 384'000 km from earth or 60 times the earth radius.
Now, we want to rotate the moon around the earth. Be be accurate, we let them both rotate around the common center of mass. The mass of the earth is 5.97 * 10^24 kg and of the moon 7.35 * 10^22 respectively. Thus, earth has roughly 81 times the mass of the moon. Their common center of mass is \(d_{Center} = d_{EarthMoon} / (81 + 1) = 60 / 82 = 0.73\) times the earth radius. In our simulation, we let the earth an the moon rotate around each other in 27.3 minutes.
With this "theoretical" preparation, we are ready to setup our scene and simulation. Add another method GetMoon(), that is pretty identical to GetEarth() without texture. Then change OnLoad() as follows.
const float distance = 60;
const float center = 0.73f;
_earth = GetEarth(graphic);
_earth.Translate(-center * Vector3.UnitX);
Scene.Add(_earth);
var moon = GetMoon(graphic);
moon.Scale(0.273f);
moon.Translate((distance - center) * Vector3.UnitX);
Scene.Add(moon);
What is missing is that we want them to rotate around each other. For that we group the earth and moon together into a little hierarchy as follows and rotate the system by _system?.RotateY(1f / 27.3 * deltaAngle);. The translation of earth and moon are now relative to the system and the rotation of the system affects both earth and moon as intended.
_system = Graphic.CreateGroup("system");
Scene.Add(_system);
...
_system.Add(_earth); // instead of Scene.Add(_earth);
...
_system.Add(moon);
Add sun light
Especially the moon looks quite boring. Adding sunlight gives the moon the characteristic silhuette. Since the sun is far away, ParallelLight, e.g., in direction -Z, is appropriate. Create an instance in OnLoad() and pass it to GetEarth() and GetMoon(). Change material and shading as follows. The same can be done for earth using ColorTextureMaterial.
var material = new UniformMaterial(new Color3(0.7f, 0.7f, 0.7f));
var shading = graphic.CreateShading("moon", material, sunlight);