Enrico Gullotti

earnest wonderer

earnest wonderer

Practical Space Mapping for interaction

Space mapping is the process to establish a correspondence between elements from distinct spaces. Computer graphic systems like 3ds Max render a virtual three-dimensional space as seen through a camera on a two-dimensional space, represented by the screen monitor. There is therefore a relationship between 3D space points and 2D screen pixels. Every space is defined by an orthogonal axis system following the right-hand rule.

[*] in MaxScript language "View Coordinate System" is called "Screen Coordinate System". Mouse space isn't considered a coordinate system at all. It is confusing, therefore in this article more suggestive names have been chosen. It doesn't affects programming functionality, but only the way to name variables and figure out their meaning.

Space Mapping: WorldCS \(\to\) ScreenCS The most basic mapping, allows to express in ScreenCS the coordinates of a point in the scene expressed in WorldCS. Identity matrix (Matrix3 1) represents the World transform matrix.

Space Mapping: WorldCS \(\to\) ViewCS \(\to\) ScreenCS Create a point helper in the scene and place wherever needed, it provides the WorldCS position to express in other coordinate systems. The three steps show how to get point helper coordinates relative to ViewCS in system units, and in ScreenCS in pixels.

gw.setTransform sets the active viewport's graphics window transformation matrix to the specified matrix3 value, and updates the modeling coordinates to normalized projection coordinates matrix. Identity matrix (Matrix3 1) represents the World transform matrix.

In the step from ViewCS to ScreenCS the Z axis value is discarded as being not meaningful. The ceil rounding of X and Y got from gw.transPoint gives the most accurate results.

Space Mapping: ScreenCS \(\to\) ViewCS \(\to\) WorldCS The passage from a 2D to a 3D system requires Z axis value in addition to X and Y axis values provided. Mapping a ScreenCS to ViewCS can be pictured as projecting the mouse pointer on a virtual plane parallel to the current view plane. The Z axis value defines the projection plane distance from ViewCS origin.

To manage this process the Z axis value is seldom defined by hand, more often there's something in the scene needing to be manipulated. Create a point helper in the scene and use it as a reference, the virtual projection plane will pass through it. The distance between View and reference point positions gives the Z axis value that must be corrected by the cosine of the angle between View axis and the direction from View to the point helper.

Space Mapping: WorldCS \(\to\) GridCS \(\to\) ScreenCS Create a point helper in the scene and place where needed, it provides the WorldCS position to express in other coordinate systems. Create a grid helper, place and orient it as needed. If the purpose is to get the screen coordinates of the starting point, grid helper transformations aren't relevant and provide the same results in any case, otherwise set them properly. The three steps show how to get point helper coordinates relative to GridCS in system units, and in ScreenCS in pixels.

In the step from GridCS to ScreenCS the Z axis value is discarded as being not meaningful. The ceil rounding of X and Y got from gw.transPoint gives the most accurate results.

Space Mapping: ScreenCS \(\to\) GridCS \(\to\)WorldCS GridCS offers a greater flexibility than ViewCS, since a grid helper can be placed anywhere in the scene and oriented in any direction. For this discussion let's assume to replicate the same path walked with View, where grid helper replicates the virtual projection plane, oriented like the view plane, with its origin in a desired position in space.

Space Mapping: LocalCS \(\leftrightarrow\) WorldCS When working with sub-objects, for example vertices in an editable poly object, their coordinates are often expressed in the node LocalCS. It is required to express them in WorldCS before every other transformation.

A vector can be transformed from one space to another with above rules by using the proper rotation matrix.

Space mapping is the process to establish a correspondence between elements from distinct spaces. Computer graphic systems like 3ds Max render a virtual three-dimensional space as seen through a camera on a two-dimensional space, represented by the screen monitor. There is therefore a relationship between 3D space points and 2D screen pixels. Every space is defined by an orthogonal axis system following the right-hand rule.

Representation of a standard 3ds Max scene. View is shown as a standard Camera to support the explanation. The Screen frame representation is fictional to help figuring how the system works. |

**3D World Coordinate System [WorldCS]**

The world space coordinate system. The origin is the center of the scene, X and Y axis lay on the virtual ground plane while Z axis points up.

**3D Local Coordinate System [LocalCS]**

The object's local coordinate system is specific for every object in the scene and is represented by the default object pivot.

**3D View* Coordinate System [ViewCS]**

The viewport's coordinate system is associated to the currently active viewport. Every non orthographic view like Perspective, is like a hidden Camera view optimized for scene navigation. To create a visible copy of the View choose from menu Views > Create Camera From View, or press Ctrl+C. The ViewCS is the LocalCS of View. The origin is placed at the View position, X and Y axis lay on a plane parallel to the view plane and Z axis points opposite to the viewing direction.

**3D Grid Coordinate System [GridCS]**

The grid's coordinate system is associated to the currently active grid. The origin and axis are defined by the default grid pivot, X and Y axis lay on the grid plane while Z axis points toward the positive halfspace defined by the grid. If there's not user defined active grid helper in the scene, GridCS corresponds to WorldCS.

**2D Screen* Coordinate System [ScreenCS]**

The mouse position in viewport expressed in pixels. The origin is in the upper left corner of the currently active viewport. X axis points right and Y axis points down.

[*] in MaxScript language "View Coordinate System" is called "Screen Coordinate System". Mouse space isn't considered a coordinate system at all. It is confusing, therefore in this article more suggestive names have been chosen. It doesn't affects programming functionality, but only the way to name variables and figure out their meaning.

Space Mapping: WorldCS \(\to\) ScreenCS The most basic mapping, allows to express in ScreenCS the coordinates of a point in the scene expressed in WorldCS. Identity matrix (Matrix3 1) represents the World transform matrix.

-- Create a Point Helper hPoint = Point() -------------------------------------------------------------------------------- -- Space Mapping: WorldCS -> ScreenCS -------------------------------------------------------------------------------- -- Point coordinates in WorldCS (system units) p3PointWorldPos = hPoint.position -- WorldCS (system units) -> ScreenCS (pixels) gw.setTransform (Matrix3 1) p3Temp = gw.transPoint p3PointWorldPos p2PointScreenPos = [(ceil p3Temp).x, (ceil p3Temp).y]

Space Mapping: WorldCS \(\to\) ViewCS \(\to\) ScreenCS Create a point helper in the scene and place wherever needed, it provides the WorldCS position to express in other coordinate systems. The three steps show how to get point helper coordinates relative to ViewCS in system units, and in ScreenCS in pixels.

gw.setTransform sets the active viewport's graphics window transformation matrix to the specified matrix3 value, and updates the modeling coordinates to normalized projection coordinates matrix. Identity matrix (Matrix3 1) represents the World transform matrix.

In the step from ViewCS to ScreenCS the Z axis value is discarded as being not meaningful. The ceil rounding of X and Y got from gw.transPoint gives the most accurate results.

-- Create a Point Helper hPoint = Point() -------------------------------------------------------------------------------- -- Space Mapping: WorldCS -> ViewCS -> ScreenCS -------------------------------------------------------------------------------- -- Point coordinates in WorldCS (system units) p3PointWorldPos = hPoint.position -- WorldCS (system units) -> ViewCS (system units) p3PointViewPos = p3PointWorldPos * getViewTM() -- ViewCS (system units) -> ScreenCS (pixels) gw.setTransform (inverse (getViewTM())) p3Temp = gw.transPoint p3PointViewPos p2PointScreenPos = [(ceil p3Temp).x, (ceil p3Temp).y]

Space Mapping: ScreenCS \(\to\) ViewCS \(\to\) WorldCS The passage from a 2D to a 3D system requires Z axis value in addition to X and Y axis values provided. Mapping a ScreenCS to ViewCS can be pictured as projecting the mouse pointer on a virtual plane parallel to the current view plane. The Z axis value defines the projection plane distance from ViewCS origin.

To manage this process the Z axis value is seldom defined by hand, more often there's something in the scene needing to be manipulated. Create a point helper in the scene and use it as a reference, the virtual projection plane will pass through it. The distance between View and reference point positions gives the Z axis value that must be corrected by the cosine of the angle between View axis and the direction from View to the point helper.

-- Create a Point Helper hPoint = Point() -- Point coordinates in WorldCS (system units) p3PointWorldPos = hPoint.position -- Get View position in WorldCS p3ViewWorldPos = (inverse(getViewTM())).row4 -- Get View direction in WorldCS p3ViewWorldDir = -(inverse(getViewTM())).row3 -- Get the angle between the View axis and the Point Helper fViewAngle = acos (dot (normalize (p3PointWorldPos - p3ViewWorldPos)) p3ViewWorldDir) -- Correct the distance between View and a Point Helper fViewDepth = (distance p3PointWorldPos p3ViewWorldPos) * (cos fViewAngle) -------------------------------------------------------------------------------- -- Space mapping: ScreenCS -> ViewCS -> WorldCS -------------------------------------------------------------------------------- -- Mouse coordinates in ScreenCS (pixels) p2MouseScreenPos = mouse.pos -- ScreenCS (pixels) -> ViewCS (system units) p3MouseViewPos = mapScreenToView p2MouseScreenPos (-fViewDepth) -- ViewCS (system units) -> WorldCS (system units) p3MouseWorldPos = p3MouseViewPos * (inverse (getViewTM()))

Space Mapping: WorldCS \(\to\) GridCS \(\to\) ScreenCS Create a point helper in the scene and place where needed, it provides the WorldCS position to express in other coordinate systems. Create a grid helper, place and orient it as needed. If the purpose is to get the screen coordinates of the starting point, grid helper transformations aren't relevant and provide the same results in any case, otherwise set them properly. The three steps show how to get point helper coordinates relative to GridCS in system units, and in ScreenCS in pixels.

In the step from GridCS to ScreenCS the Z axis value is discarded as being not meaningful. The ceil rounding of X and Y got from gw.transPoint gives the most accurate results.

-- Create a Point Helper hPoint = Point() -- Create a Grid Helper hGrid = Grid() -- Orient the Grid Helper like View hGrid.rotation = (getViewTM()).rotation -- Activate the new Grid Helper activeGrid = hGrid -- Activate the World Construction Plane activeGrid = undefined -------------------------------------------------------------------------------- -- Space mapping: WorldCS -> GridCS -> ScreenCS -------------------------------------------------------------------------------- -- Point coordinates in WorldCS (system units) p3PointWorldPos = hPoint.position -- WorldCS (system units) -> GridCS (system units) p3PointGridPos = p3PointWorldPos * (inverse hGrid.transform) -- WorldCS (system units) -> ScreenCS (pixels) gw.setTransform (hGrid.transform) p3Temp = gw.transPoint p3PointGridPos p2PointScreenPos = [(ceil p3Temp).x, (ceil p3Temp).y]

Space Mapping: ScreenCS \(\to\) GridCS \(\to\)WorldCS GridCS offers a greater flexibility than ViewCS, since a grid helper can be placed anywhere in the scene and oriented in any direction. For this discussion let's assume to replicate the same path walked with View, where grid helper replicates the virtual projection plane, oriented like the view plane, with its origin in a desired position in space.

-- Create a Point Helper hPoint = Point() -- Create a Grid Helper hGrid = Grid() -- Orient the Grid Helper like View hGrid.rotation = (getViewTM()).rotation -- or whatever needed -- Position the Grid Helper like the Point Helper hGrid.position = hPoint.position -- or whatever needed -- Activate the new Grid Helper activeGrid = hGrid -- Activate the World Construction Plane activeGrid = undefined -------------------------------------------------------------------------------- -- Space mapping: ScreenCS -> GridCS -> WorldCS -------------------------------------------------------------------------------- -- Mouse coordinates in ScreenCS (pixels) p2MouseScreenPos = mouse.pos -- ScreenCS (pixels) -> GridCS (system units) p3MouseGridPos = gw.getPointOnCP p2MouseScreenPos -- based on the currently active grid -- GridCS (system units) -> WorldCS (system units) p3MouseWorldPos = gw.mapCPToWorld p3MouseGridPos

p3MouseWorldPos = p3MouseGridPos * hGrid.transform

Space Mapping: LocalCS \(\leftrightarrow\) WorldCS When working with sub-objects, for example vertices in an editable poly object, their coordinates are often expressed in the node LocalCS. It is required to express them in WorldCS before every other transformation.

-- Object LocalCS -> WorldCS p3PointWorld = p3PointLocal * node.transform -- WorldCS -> Object LocalCS p3PointLocal = p3PointWorld * (inverse (node.transform))

-- Object LocalCS -> WorldCS p3VectorWorld = p3VectorLocal * (node.transform.rotationPart as Matrix3) -- WorldCS -> Object LocalCS p3VectorLocal = p3VectorWorld * (inverse (node.transform.rotationPart as Matrix3))