reckfellENGINE

Entity3D: position, rotation and parent scale

Practice local/world space, −Z forward and correct normal transformation.

On this page

Run the hierarchy

Create a separate 1.0.0 C# level with an Entity3D root. Add TransformRig3D.cs and attach it. Use a 640×420 window/viewport, build and press F6. The script creates a camera, floor and parent at (1, 0, 0), rotated π/4 about Y and scaled (1.5, 1, 0.6). The blue cube is at its local point (1, 1, 0).

An Entity3D hierarchy with local axes, a child cube and world projection

TransformRig3D.cs
C#
using System;
using Reckfell;

public partial class TransformRig3D : Entity3D
{
    public Entity3D Pivot { get; private set; } = null!;
    public MeshInstance3D Marker { get; private set; } = null!;
    public Plane GroundPlane => new(Vector3.Up, 0);
    public Vector3 ProjectedPoint => GroundPlane.Project(Marker.GlobalPosition);

    private static MeshInstance3D Box(Vector3 size, Color color, Vector3 position)
    {
        return new MeshInstance3D
        {
            Mesh = new BoxMesh { Size = size }, Position = position,
            MaterialOverride = new StandardMaterial3D
            {
                AlbedoColor = color, ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded
            }
        };
    }

    public override void _Ready()
    {
        AddChild(new WorldEnvironment { Environment = new Reckfell.Environment
        {
            BackgroundMode = Reckfell.Environment.BGMode.Color,
            BackgroundColor = new Color(0.035f, 0.04f, 0.05f)
        }});
        AddChild(Box(new Vector3(8, 0.05f, 6), new Color(0.10f, 0.11f, 0.13f), new Vector3(0, -0.025f, 0)));
        Pivot = new Entity3D
        {
            Position = new Vector3(1, 0, 0), Rotation = new Vector3(0, Mathf.Pi / 4, 0),
            Scale = new Vector3(1.5f, 1, 0.6f)
        };
        AddChild(Pivot);
        Pivot.AddChild(Box(new Vector3(1.6f, 0.045f, 0.045f), new Color(0.25f, 0.46f, 1), new Vector3(0.8f, 0, 0)));
        Pivot.AddChild(Box(new Vector3(0.045f, 1.5f, 0.045f), Colors.White, new Vector3(0, 0.75f, 0)));
        Pivot.AddChild(Box(new Vector3(0.045f, 0.045f, 1.5f), new Color(0.45f, 0.48f, 0.55f), new Vector3(0, 0, 0.75f)));
        Marker = Box(new Vector3(0.2f, 0.2f, 0.2f), new Color(0.25f, 0.46f, 1), new Vector3(1, 1, 0));
        Pivot.AddChild(Marker);
        AddChild(Box(new Vector3(0.25f, 0.035f, 0.25f), Colors.White, ProjectedPoint + Vector3.Up * 0.025f));
        var camera = new Camera3D { Position = new Vector3(5, 4.5f, 7), Current = true, Fov = 48 };
        AddChild(camera); camera.LookAt(new Vector3(0.7f, 0.4f, 0), Vector3.Up);
        var canvas = new CanvasLayer(); AddChild(canvas);
        bool ru = TranslationServer.GetLocale().StartsWith("ru");
        var title = new Label { Position = new Vector2(24, 20), Text = ru
            ? "Локальная точка и мировая плоскость" : "A local point and a world-space plane" };
        title.AddThemeFontSizeOverride("font_size", 22); canvas.AddChild(title);
        canvas.AddChild(new Label { Position = new Vector2(24, 58), Text = ru
            ? "Синий куб: local (1,1,0). Белая метка: проекция на Y = 0."
            : "Blue cube: local (1,1,0). White marker: projection onto Y = 0." });
        ReckfellApi.Print("TRANSFORM local=" + Pivot.ToLocal(Marker.GlobalPosition));
    }

    public static bool TryTransformNormal(Basis basis, Vector3 normal, out Vector3 result)
    {
        result = Vector3.Zero;
        if (!basis.IsFinite() || !normal.IsFinite() || normal.LengthSquared() < 0.000001f ||
            MathF.Abs(basis.Determinant()) < 0.000001f) return false;
        Vector3 transformed = basis.Inverse().Transposed() * normal;
        if (!transformed.IsFinite() || transformed.LengthSquared() < 0.000001f) return false;
        result = transformed.Normalized();
        return true;
    }
}

TRANSFORM local should be near (1, 1, 0): ToLocal converts the cube's world position back into Pivot space. Parent rotation/scale changes the child's GlobalPosition without changing its Position.

Choose a space

Property/operationSpace
Position, Rotation, Scale, TransformRelative to the parent Entity3D
GlobalPosition, GlobalTransformWorld
ToGlobal(localPoint)This Entity3D's local point to world
ToLocal(worldPoint)World point to this Entity3D's local space

For aiming, use target.GlobalPosition − actor.GlobalPosition, or convert both points into one local space first. Vector3.Forward is (0, 0, −1). A standard −Z-facing object's world forward comes from −GlobalBasis.Column2; scale affects its magnitude, so normalize it when a direction is needed. Imported models may use another front axis: that is a model/orientation choice, not a different world convention.

Rotation without ambiguity

Rotation stores Euler radians; RotationDegrees uses degrees. Euler order affects results, and rotations around different axes do not commute. Adding three angles is not a general substitute for composing 3D rotations. Use unit Quaternions and Slerp for arbitrary orientation transitions; for aiming, check that the target differs from the position and up is not parallel to direction.

Transform3D struct methods and Entity3D methods do not always perform the same operation. Transform3D.Rotated rotates the whole transform, including Origin around the external origin; RotatedLocal rotates in the local basis and preserves Origin. Do not substitute them for an orientation edit without checking the intended pivot. See matrix composition for multiplication order.

Nonuniform scale

This example scales axes differently. Point inversion needs AffineInverse; a surface normal needs inverse-transposed Basis and renormalization. TryTransformNormal checks its chosen bounds and demonstrates that case; choose degeneracy tolerances for your task's scale.

Zero scale makes a direction noninvertible. Orthonormalized removes scale/skew rather than repairing a model for free. Rotated nonuniform-scale hierarchies may produce shear; position, Euler and Scale may not preserve that entire matrix losslessly. Separate clean rotation from scaling for complex animation and inspect the resulting hierarchy.

This is a geometry demo. It does not establish physics-collider correctness under arbitrary scale, network replication or every model-import workflow.

Reckfell Engine documentationBack to top ↑
QUICK LINKSGetting startedEntity hierarchyC# & .NET 10Scenes and prefabs
Reckfell guides and full API K
reckfellENGINE
RECKFELL ACCOUNT

Sign in

Sign in to your Reckfell account.

One account for the entire Reckfell platform.