[N-World Contents] [Book Contents] [Prev] [Next] [Index]

You'll often want to extract the data stored in the N·World database for use in other applications. In this chapter, you'll learn how to:


Exporting Geometry

Geometry is the data which defines the locations of an objects vertices, faces, and edges with respect to its local coordinate system.

Determining Element Order

In many cases, you'll want to list geometry data in one of two orders

Each element in a body is assigned a sequential serial number when it is instantiated. Unfortunately, there is no easy way to compress these serial numbers. However, you can use 3D:WITH-COMPRESSED-VERTEX-INDICES within your loop forms to assign each element a temporary index, which can then be used to sort the output of your loops. For example:

(with-compressed-vertex-indices (body) ;; set the indices 

		(loop for vertex being the vertex-reverse-elements
of-body body

				for i from 0 

				do (setf (vidx vertex) i))

      ;;

      ;; your code here

      ;;

      )

The 3D:VIDX function assigns a temporary index number of value i to each vertex. Because the vertex-reverse-elements form was used instead of the vertex-ring-elements form, the vertices will be printed in first-added-first order.


Avoiding Printed Representations

The functions and loop macro forms we've used up until now return printed representations of object, bodies, and elements, e.g.

#<POLYHEDRON 8>

These printed representations serve only as labels for geometry items. In fact, there is no way to write a file using printed representations, and then read that file back into N·World. Therefore, we have to find a way of binding the coordinate information we're after to the indices of the elements we extract without using printed representations. This can be accomplished by binding the coordinates of each vertex to an ftriplet. To learn more about ftriplets, see "Ftriplets," on page 12-7


Formatting Output to a File

Now that we know how to obtain the geometry of a body (and its individual elements), we'll need to output that information to some useful device, such as the screen, the printer, or a file. The with-open-file macro offers a flexible way of writing formatted output to a file.

(with-open-file var-for-stream

                      :direction :output

                      :if-does-not-exist :create

                      :if-exists :new-version)

This function opens and closes the file, performing all necessary housekeeping functions automatically. All you need to do is direct your output to the file, usually with the format function.

Given an element of a polyhedron, coordinates are obtained with 3D:LOCUS-COORDINATES. In the following examples, we'll extract coordinates and bind them to a variable. Then, we'll print these values with the index for each vertex assigned by vidx. The resulting file will not contain printed representations or sharp characters, and will be much easier to read should we choose to do at some point in the future.

Given that ? is bound to a body, the following example writes the results of two do-element-loops to a file.

(let ((count 0))

  (3d:with-compressed-vertex-indices (?)

  (with-open-file (vs "~/junk/format-test.h" :direction :output

					   :if-does-not-exist :create

					   :if-exists :new-version)

    (loop for f being the face-reverse-elements of-body ? do

       (format vs "face ~A~%" (incf count))

       (loop for v being the component-vertices of-face f do

         (ftriplet-values-bind (x y z) (locus-coordinates v)

           (format vs "vertex ~S ~S ~S ~S~%" (vidx v) x y z)))))))

A Sample Exporter

Now, we'll define a sample exporter function which combines all of the aspects of exporting geometry we've introduced in this section, including:

Note. This function is an example only! It is a simple example of how to export geometry data, and is likely to require additional modification to suit your particular needs.

This function accepts an argument which specifies a pathname for the file it will generate:

(in-package "3d")

(defun sample-exporter (outpath &optional (object (choose-object t)))

  (with-open-file (stream outpath

                   :direction :output

                   :if-does-not-exist :create

                   :if-exists :new-version)

  

    (loop for tobj being the terminal-objects of-object object

              as body = (obod tobj)

              when (polyhedron-p body)

              do (format stream "~&Object: ~S~2%" (sp:get-name tobj))

              (with-compressed-vertex-indices (body)

                ;; Creating the compressed-vertex-indices

                ;; We have to loop through a ll vertices,

                ;; and setf vidx once per vertex

                (loop for v being the vertex-reverse-elements of-polyhedron body

                          for v-count from 1

                          do (setf (vidx v) v-count))

                ;; Now we can loop through faces and write out vertices

                ;; through the compressed-vertex-indices table

                (loop for f being the face-reverse-elements of-polyhedron body

                          for face-idx from 1

                          do

                          (format stream "face ~D~%" face-idx)

                          (loop for v being the component-vertices of-face f

                                    do 

                                    (ftriplet-values-bind (x y z) (locus-coordinates v)

                                      (format stream "vertex ~3D ~10F ~10F ~10F~%"

                                              (vidx v) x y z)))

                          (format stream "~%"))

                ))

    )

  )

The function sample-exporter requires one argument, outpath. This is the path to which it writes the geometry of an object or objects. The object argument is optional. You can either specify an object, or by default the function will call choose-objects so that the user can select an object.

In the first loop macro, body is bound to the body of each terminal object. After the first loop, with-compressed-vertex-indices compresses the vertex indices of body.

In the second loop, a counter, face-idx, is initialized at 1. This counter serves as a label for each face.

In the third loop, the ftriplet-values-bind command binds the x, y, and z coordinates of each vertex to the variables x, y, and z, respectively.

Finally, these values are written to the file. After you've compiled this function, you can evaluate it. Don't forget to provide an output path as an argument!

USER(2): (sample-exporter "~/objects/thisobject.file")

This function will print the geometry of any object you can select to a file, no matter how many inferior objects that object may contain.

Obviously, there are many shortcomings in this sample function. You may choose to improve it in one of the following ways:


Exporting UV Coordinates

Note. This chapter discusses only polyhedra and shading attribute domains, such as GL and Render. These are the only combinations for which UV coordinates are defined.

This section describes how to retrieve UV coordinates for vertices given a domain, a bdi, a mapper type, and an attributed element. Attributed elements can be either polyhedra, face parts, or faces. The N·World interface for obtaining UV coordinates includes three macros:

Macros				with-surface-context

				with-mapping-setup

				ensuring-face-display-cache-for-mapping

You can find source code for these macros and the methods which follow in /usr/local/ngc/demo/src/uv-example.lisp

The with-surface-context macro binds a variable to a surface context, given a bdi and an attribute domain. This relies on the unique relationship between a bdi-domain pair and a surface context. In other words, given a surface context, you can find the unique bdi-domain pair which defines that context.

The with-mapping-setup macro binds a variable to a mapper, given a mapper type, an attributed element and a surface context. The attributed element may be, in the case of a polyhedron, the body, a face part, or a face. This macro uses the transformations of the object and a mapper object to compute the UV projection. Therefore, any transformation of either the object or the mapper object inside of the body of this macro will invalidate the setup.

The ensuring-face-display-cache-for-mapping macro binds a variable to the face-display-cache for a given face with respect to a given mapper. The with-mapping-setup macro, by computing the UV projection, prepares the face-display-cache to hold the updated UV coordinates for the face. We extract UV coordinates from the display cache.

Following is the source code for these macros. You must compile and load these macros before you can use the example methods we'll present later in this section, because they are not currently included in N·World 3.0. They can be found in /usr/local/ngc/demo/src/uv-example.lisp

(in-package :geometry)

(defmacro WITH-SURFACE-CONTEXT ((context-var bdi domain) &body body)

  \Q(let ((,context-var (get-surface-context ,bdi (attr:find-domain ,domain))))

     ,@body)

  )

(defmacro WITH-MAPPING-SETUP ((mapper-var attributed-element context

		&optional (mapper-type :texture-map-mapper) errorp) &body body)

	(let		((matrix (gensym))

			(tmm    (gensym))

        			(bdi    (gensym)))

    

(let* ( (,bdi        (get-context-bdi ,context))

		 (,matrix     (get-current-transformation ,bdi))

		 (,tmm        (and ,context

		 				(attr:derived-attribute-value ,attributed-element ,mapper-type 
,context)))

		(,mapper-var (ra:setup-mapper-cache ,tmm ,bdi ,matrix))

		)

       (cond (,mapper-var

              (setup-gl-texture-wrap ,attributed-element ,context ,mapper-var)

              ,@body)

             				(,errorp

				(error "No ~S mapper for element ~S in context ~S" ,mapper-type

                      ,attributed-element ,context)))

       ))

  )

(defmacro ENSURING-FACE-DISPLAY-CACHE-FOR-MAPPING ((display-cache-var face mapper)

                                                   &body body)

  \Q(when ,mapper

     (let ((,display-cache-var (ensure-face-display-cache ,face ,mapper nil)))

       (if ,display-cache-var

           (progn ,@body)

         (error "No Face-display-cache for face ~S." ,face)))) 

  )

Getting the UVs of a Vertex

The method GET-UV-COORDS returns UV coordinates for any vertex, given the vertex and a neighboring face, a bdi, a mapper type, and an attribute domain.

Some of the steps taken by these macros do not need to be recomputed for each vertex in a given face. For example, the surface context need only be identified once per body. The mapper setup must be executed once per body and once for each attributed face part. The display cache must be recomputed for every face.

(in-package :geometry)

(defmethod GET-UV-COORDS ((self vertex) (face face) bdi

                          &key

                          (mapper-type :texture-map-mapper)

                          domain        ; :GL :SEGA :PSX :ARKRENDER or NIL

                                        ; (the current domain)

                          )

  

  (with-surface-context (context bdi domain)

    (with-mapping-setup (mapper face context mapper-type)

      (ensuring-face-display-cache-for-mapping (display-cache face mapper)

        (let ((uvcoords (getf (sf-texcoords display-cache) mapper)))

          (declare (type (simple-vector *) uvcoords))

          (loop for i from 0 below (length uvcoords) 

                    for v being the ccw-component-vertices of-face face

                    when (eq v self)

                    do (return (svref uvcoords i)))

          ))))

  )

The interface which is documented here provides the UV coordinates for a single vertex with respect to a single face. Therefore, each of these macros is executed for
each vertex in the GET-UV-COORDS method. In the next section, we'll present some examples which show how to access UV coordinates more efficiently in specific contexts.

Examples and Templates

The following three methods are designed to return UV coordinates for a face, face part, and a polyhedron.

Becuase UV coordinates are only availabe for face parts and polyhedra, the method specialized on a face must find either an attributed part which the face is a member of, or the polyhedron of which the face is a component. Then the appropriate face part or polyhedra method is called.

(in-package :geometry)

(defmethod MY-WRITE-UV-COORDS-TO-STREAM ((self face) 

                                         bdi 

                                         stream 

                                         &key

                                         (mapper-type :texture-map-mapper)

                                         domain

                                         )

;;Given a face, we have to find either the last-attributed-face-part of which the face is a member, or failing that, the polyhedron of the face

  (let ((parts (or (find-parts-with-me self)

                   (find-body self))))

    (format t "~% Parts: ~a" parts)

    (if (consp parts)

        (loop for part in parts

            do

              (format t "~%Part: ~a" part)

              (my-write-uv-coords-to-stream part bdi stream :mapper-type mapper-type
:domain domain))

        (my-write-uv-coords-to-stream parts bdi stream :mapper-type mapper-type 
:domain domain)

            )))

The second example method returns UV coordinates for a face-part:

(in-package :geometry)

(defmethod MY-WRITE-UV-COORDS-TO-STREAM ((self part)

						bdi
stream

						&key

						(mapper-type :texture-map-mapper)

						domain 

						)

(with-surface-context (context bdi domain)

    (with-mapping-setup (mapper self context mapper-type nil)

      ;; Check that the last attributed part for every face

      ;; is the part we are writing out.

      (loop for face in (group-components self)

          when (eq self (last-attributed-part face context))

          do 

            (ensuring-face-display-cache-for-mapping (display-cache face mapper)

              (let ((uvcoords (getf (sf-texcoords display-cache) mapper)))

                (declare (type (simple-vector *) uvcoords))

                (loop for i from 0 below (length uvcoords) 

                          for v being the ccw-component-vertices of-face face

                          as uv = (svref uvcoords i)

                          do

                      (format stream "~%Face: ~a" face)

                      (format stream "~%Vert: ~a UV: ~a" v uv)

;;                      [.....YOUR WRITE CODE HERE....]

                          )

                )))

      ))

  )

This method is for accessing the UV coordinates for all the vertices of all the faces of a given face part.

The following method demonstrates how to use the first template in order to return a list of UV coordinates for all the vertices of a face. It's up to you to manage the resulting list.

You must write code which supplies these methods with the appropriate arguments. For example, you will have to derive the bdi from a terminal object using get-terminal-object and obdi.

The following example illustrates how to use these macros to extract the UV coordinates for an entire polyhedron, and write these coordinates to a stream.

(in-package :geometry)

(defmethod MY-WRITE-UV-COORDS-TO-STREAM ((self polyhedron)

                                     bdi

                                     stream 

                                     &key

                                     (mapper-type :texture-map-mapper)

                                     domain

                                     )

  (with-surface-context (body-context bdi domain)

    (with-mapping-setup (body-mapper self body-context mapper-type nil)

      ;; Dump UVs for faces not in parts

      (loop for face being the face-reverse-elements of-polyhedron self

          as parts = (find-parts-with-me face)

          unless parts

          do

            (format stream "~%Face: ~a" face)

            (ensuring-face-display-cache-for-mapping (display-cache face body-mapper)

                 (let ((uvcoords (getf (sf-texcoords display-cache) body-mapper)))

                   (declare (type (simple-vector *) uvcoords))

                   (loop for i from 0 below (length uvcoords) 

                       for v being the ccw-component-vertices of-face face

                       as uv = (svref uvcoords i)

                       do

                         (format stream "~%    Vert: ~a UV: ~a" v uv )

                         ;;  [...YOUR WRITE CODE HERE...]

                         ))))

      ;; Dump UVs for parts not attributed

      (loop for part in (parts-of-type \Qface (get-parts self))

          unless (attr:local-attributes part body-context T)

          do 

            (format stream "~%UA-Part: ~a" part)

            (loop for face in (group-components part)

                as parts = (find-parts-with-me face)

                as winning = (car parts)

                unless (or (last-attributed-part face body-context)

                           (not (eq winning part)))

                do 

                  (format stream "~%   Face: ~a" face)

                  (ensuring-face-display-cache-for-mapping

                   (display-cache face body-mapper)

                   (let ((uvcoords (getf (sf-texcoords display-cache) body-mapper)))

                     (declare (type (simple-vector *) uvcoords))

                     (loop for i from 0 below (length uvcoords) 

                         for v being the ccw-component-vertices of-face face

                         as uv = (svref uvcoords i)

                         do

                           (format stream "~%    Vert: ~a UV: ~a" v uv )

;;                      [ ...YOUR WRITE CODE HERE... ]

                           )))

                  (format t "~%   End Part: ~a " winning))

      )

    

    ;; Dump UVs for attributed parts, using last-attributed-part priority

      (loop for part in (parts-of-type \Qface (get-parts self))

          do 

            (format stream "~%  LA-Part: ~a " part)                  

            (with-mapping-setup (part-mapper part body-context mapper-type nil)

              (loop for face in (group-components part)

                  as pla = (last-attributed-part face body-context)

                  when (eq part pla)

                  do

                    (format stream "~%Face: ~a" face)

                    (ensuring-face-display-cache-for-mapping

                     (display-cache face part-mapper)

                     (let ((uvcoords (getf (sf-texcoords display-cache) body-mapper)))

                       (declare (type (simple-vector *) uvcoords))

                       (loop for i from 0 below (length uvcoords) 

                           for v being the ccw-component-vertices of-face face

                           as uv = (svref uvcoords i)

                           do

                           

                           (format stream "~%    Vert: ~a UV: ~a" v uv )

                   

;;                     [ ...YOUR WRITE CODE HERE... ]

                     ))))

    ))

    ))

)

The structure of the method will necessarily reflect the output format used. As a result, the methods you write may have a different structure than these examples depending upon your needs.

Seams and Edges

Typically, UV coordinate values lie between 0.0 and 1.0 (exclusive). However, a special case arises when a map wraps around, as when the right side of the map butts up against the left side. Unless the vertices of the object happen to fall exactly on the boundaries of the map, i.e., the U or V values are 0.0 or 1.0, the map seam will fall on the interior of a face, which means the U values of those vertices falling on the right side of the map will be less than 1.0, and for those falling on the left side of the map, more than 0.0.

Figure 8.1 Mapper seam falls across the highlighted face

Normally the U or V values are interpolated across the face. However, if our face spans a seam, we cannot interpolate naively from, say, .9 to .1, because that will take us "the long way" across the map and instead of seeing the seam, we will see the whole map compressed across the face. In these cases, our UV values must exceed the 0.0 to 1.0 range. Depending on where the face is situated with respect to the seam, our UV values might range either from -0.1 to 0.1 or from 0.9 to 1.1.

Note that one vertex can have different UV values that represent the same place on the texture map. For example, with respect to one face within a wrapping texture map, it may have a U value of 1.1, while with respect to another face using the same texture map, it may have a U value of 0.1.


Transformations

The basic geometry of an object is defined with respect to it's local axes. A cube, for example, consists of eight vertices which occupy definite locations with respect to these local axes. Modifying the geometry of this object (for example, moving one of these vertices) changes the relationship between these vertices and the local axes of the cube.

Transformations do not alter the relationship between the elements of an object and its local axes. Instead, transformations change the relationship between the objects local axes and another frame of reference, such as the global axes. For example, a rotation transformation results in a change in the appearance of the object with respect to the global coordinate system. However, no change occurs in the relationship of its vertices with its local axes. Figure 8.2 shows two rotated cubes:

Figure 8.2 Transformations vs. modifications.

Superficially, these cubes are similar in appearance. However, an examination of the local axes of each reveals that the cube on the left has been transformed. Each axis is still perpendicular to the faces of the cube. The cube on the right, on the other hand, has been modified. It's local axes are no longer perpendicular to the faces of the cube.

Transformation Matrices

A transformation matrix (TM) is a 4 x 4 array of numbers. These numbers refer to the orientations and relative scalings of the local axes of an object with respect to some frame of reference. Changes to these values result in the three types of rigid-body transformations; scaling, rotating, and shearing, and displacement.

Figure 8.3 shows a special type of transformation matrix, called an identity-matrix. An identity matrix is the matrix which is exactly parallel to that matrices frame of reference. When an object is first created, its transformation is by default an identity-matrix with respect to the global coordinate system.

Figure 8.3 Anatomy of a transformation matrix

The TM of the cube on the right is an identity-matrix. The rotation applied to it changed the relationship of its constituent points to its local axes, but did not change the relationship between the cubes local axes and the global frame of reference.

Now, consider the TM of the cube on the left in Figure 8.2:

		.8862      -.4633          .0000        .0000

		.4633        .8862          .0000        .0000

		.0000        .0000        1.0000        .0000

		.0000        .0000          .0000      1.0000

The values in this matrix represent vectors which describe the new orientation of each axis. For simplicities sake, the z axis has not been rotated, which means that the vectors in this matrix are two dimensional.

Unit Direction Vectors

The difference between a frame of reference orientation and the orientation of an objects local axes are described in a TM by a unit-direction vector. Essentially, vectors describe how to get from one point to another. Vectors have a direction and a magnitude. In three-dimensional space, a vector can be defined by three displacements, delta-x, delta-y, and delta-z. Figure 8.4. shows how delta-x and delta-y are derived.

Figure 8.4 2D Axis displacement vectors

Normalizing Vectors

The magnitude of the vector is defined as the square root of the sum of the squares of the displacements:

Unit-direction vectors are vectors normalized to 1. The magnitude of a transformation unit-direction vector is always equal to 1. For the X-axis unit-direction vectors in the example above

Unit direction vectors are expressed as ftriplets in N·World. To normalize an ftriplet to a given magnitude, use the 3D:NORMALIZE-FTRIPLET! macro.

(3D:NORMALIZE-FTRIPLET! triplet &OPTIONAL new-magnitude)

You can specify a new magnitude for the resulting unit-direction vector, or accept the default value of 1.

Transformation Notation

Transformation notation includes information about the direction of a transformation and the frame of reference within which the transformation is defined. Thus, a transformation like this one:

can be notated like this:

, and the inverse of this transformation like :

. We can say:

Creating Scratch Matrices

Many operations which create or modify matrices require that a matrix already exist to hold their output. You can create a scratch matrix with the 3D:WITH-TEMP-TRANSFORM-MATRIX macro:

(3d:WITH-TEMP-TRANSFORM-MATRIX (matrix-name)

				(....Your code here....))

Transformations and Object Hierarchies

The local, or base-transformation of an object, is the transformation of that object with respect to its superior object. An objects current transformation is its transformation with respect to the global frame of reference. For any given object in a hierarchy, the current transformation can be obtained by recursively multiplying, or concatenating, the local transformations of all its superior objects.

If A is a top level object, B is A's inferior, and C is B's inferior, then:

Obtaining the Local Transformation

The local transformation of an object, which is that objects transformation with respect to its superior object, can be obtained with 3D:WITH-LOCAL-TRANSFORM-MATRIX

					(3d:WITH-LOCAL-TRANSFORM-MATRIX (var-for-matrix of-object)

						(...your code here))

Calculating the Current Transformation

The current transformation matrix for C is obtained by multiplying the local matrices of each of its superiors, according to the following equation:

The order in which the matrices are multiplied is significant. The commutative property does not apply to matrix multiplication.

In N·World, current transformations for any object can be obtained using the 3D:WITH-CURRENT-TRANSFORM-MATRIX macro:

	(3d:WITH-CURRENT-TRANSFORM-MATRIX ((matrix-name of-object &OPTIONAL wrt-thing))

			(....your code here....)))

Calculating Current Transformations With Respect to Other Objects

The current transformation describes the relationship between an objects local axes and the global origin. However, you'll often need to calculate the transformation of an object with respect to another object, which may itself be transformed with respect to the global origin. Figure 8.5 illustrates this scenario.

Figure 8.5 Transformation of an object with respect to another object.

In Figure 8.5, the transformation of E with respect to Cis equivalent to

To calculate this transformation:

1. Construct a vector diagram, which connects the two objects through the global origin.

The diagram reveals the path of matrix multiplication. The vector path
and

are oriented in exactly opposite directions, but the path and

are paralell. Thus, you must use the inverse-matrix of

when calculating the transformation. The correct equation for derving the transformation of C with respect to Eis revealed by the diagram to be:

The macro 3D:WITH-INVERSETRANSFORM-MATRIX returns the inverse of any matrix:

	(3D:WITH-INVERSE-TRANSFORM-MATRIX ((var-for-matrix of-object &OPTIONAL wrt-thing))

							(....Your code here...))

To obtain

you'd have to calculate current matrices for each object in the hierarchy, then evaluate the equation for the transformation. Fortunately, there's a shorthand for this calculation. You can calculate a current-matrix for an object with regard to another object by including the with-regard-to object as an argument to 3D:WITH-CURRENT-TRANSFORMATION-MATRIX.

(3d:WITH-CURRENT-TRANSFORM-MATRIX ((var-for-matrix of-object &OPTIONAL with-respect-to- object))

			(.....your code here....))

For example, to solve the above equation:

	(3d:WITH-CURRENT-TRANSFORM-MATRIX (matrix)(obj-C)(obj-E)

								(...your code here...)))

Note: All of these macros which return transform matrices can be calculated with respect to another object. Each optionally accepts a with-respect-to-object argument.


Additional Transformation Matrices

Body-Matrix

The body-matrix is a cache for the current transformation of an object. It is updated only at display time, so it can be out of date during the interval between the application of transformations and object redraw. The body-matrix incorporates all the transformations of its object's superiors. If its object has no superior, this slot is shared with the base-matrix. 3D:GET-BODY-MATRIX returns the body matrix, given an object:

(3D:GET-BODY-MATRIX of-object)

Init-matrix

When an object is frozen, its current transformation matrix is copied into the objects init-matrix slot. Initializing an objects transformation matrix returns the object to the transformation stored in the init-matrix slot. The slot-accessor function 3D:GET-INIT-MATRIX returns the init-matrix, given an object:

(3D:GET-INIT-MATRIX object)

This function will return NIL if an object has never been frozen.


Manipulating Transformations

The preceding sections described how to obtain any transformation matrix for any object with respect to any frame of reference. Having obtained your matrices, you'll probably want to do things to them. The following section includes brief descriptions of some of the more useful functions and macros which manipulate matrices.

Transforming Objects

To alter a transformation matrix:

1. Create a scratch matrix with 3D:WITH-TEMP-TRANSFORM-MATRIX:

(3D:WITH-TEMP-TRANSFORM-MATRIX var-for-matrix

You'll alter this temporary matrix, then concatenate it with the matrix you want to transform.

2. Alter the matrix

Modify the matrix with 3D:ALTER-3D-MATRIX:

					(3D:ALTER-3D-MATRIX matrix-to-alter &REST alteration-action-keywords)

Table 8.1 lists the keyword arguments which define the effect of 3D:ALTER-3D-MATRIX on the target matrix.

3. Apply the temporary matrix to the matrix of the object you want to transform with 3D:TRANSFORM.

	(3D:TRANSFORM object matrix)

3D:TRANSFORM operates on the current transformation of an object

Altering Matrices

Matrices are altered with 3D:ALTER-3D-MATRIX. This useful function accepts a wide variety of keyword arguments which are used to manipulate the values in a matrix. These arguments, summarized in Table 8.1, can be cumulative. In other words, you can specify rotation keywords, followed by translation keyword arguments. All of the modifications made by 3D:ALTER-3D-MATRIX are in relation to the superior matrix of the matrix being modified.

Table 8.1 Keyword arguments to 3D:ALTER-3D-MATRIX
Keyword Options Type Description
:INITIALIZE

boolean or matrix

If t, Initialize transformation matrix before altering.

:INVERT

boolean

If t, invert transformation matrix.

:COPY

matrix

Copy specified matrix to matrix

:COPY-INVERSE

matrix

Copy inverse of specified matrix to matrix

:MOVE

direction-vector

ftriplet

Modifies displacement values in matrix by values in ftriplet.

:UNMOVE

direction-vector

ftriplet

Apply displacement values in matrix by inverse of values in ftriplet

:MOVE-TO

position

ftriplet

Translate to a specifed position

:X-MOVE

float

Modify x-axis translation by specified amount.

:Y-MOVE

float

Modify y-axis translation by specified amount

:Z-MOVE

float

ces

Modify z-axis translation by specified amount.

:X-SHEAR

float

Produces a shear along x-axis of specified amount

:Y-SHEAR

float

Produces a shear along y-axis of specified amount

:Z-SHEAR

float

Produces a shear along z-axis of specified amount.

:ALIGN

vector

ftriplet

Align z-axis with specified direction

:UNALIGN

vector

ftriplet

Align specified direction with z-axis

:ROTATE

rotation

ftriplet

Apply rotations to matrix in yxz order

:X-ROT

float

degrees

Rotate around x-axis specified number of degrees.

:Y-ROT

float

degrees

Rotate around y-axis specified number of degrees

:Z-ROT

float

degrees

Rotate around z-axis specified number of degrees

:X-ROTR

float

radians

Rotate around x-axis specified number of radians

:Y-ROTR

float

radians

Rotate around y-axis specified number of radians

:Z-ROTR

float

radians

Rotate around z-axis specified number of radians.

:SHOW

boolean

If t, print out resulting transformation matrix

All displacements are with respect to the matrix frame of reference

Altering Matrices: Examples

Arguments to 3D:ALTER-3D-MATRIX are cumulative. You can rotate, then scale, then invert a matrix in a single operation.

Rotating

To apply rotations of 20, 30, and 40 degrees to the x, y, and z axes of an object,bound to obj:

(3D:USING-TRANSFORM-MATRIX (scratch)

		(3D:ALTER-3D-MATRIX scratch

				:x-rot 20.0

				:y-rot 30.0

				:z-rot 40.0)

(3D:TRANSFORM object scratch))

Decomposing Matrices

Transformation matrices describe the position, rotation, and scale of an objects local axes. You can extract the components for use in various applications.

Rotations

The orientation of an object can be expressed as a series of rotations around its local axes. There are three formats for expressing rotational data in N·World.

Obtaining Rotations

To decompose an objects transformation matrix into its constituent rotations you must supply a transformation matrix, a rotation order, and an ftriplet to store the result.

For bones, rotation orders can be obtained with 3D:GET-ROTATION-ORDER:

(3D:GET-ROTATION-ORDER bone)

Bone rotation orders can be returned as keywords with 3D:GET-ROTATION-ORDER-KEYWORD:

(3D:GET-ROTATION-ORDER-KEYWORD bone)

This function returns a keyword expressing the bones rotation order, such as :xyz, :zyx, etc.

3D:DECOMPOSE-3D-MATRIX-TO-ROTATIONS returns the rotations of an object with respect to a given rotation order.

			(3D:DECOMPOSE-3D-MATRIX-TO-ROTATIONS matrix target-ftriplet rot-order)

For example, to obtain the rotations of an object bound to ? with respect to the global origin:

			(3D:WITH-CURRENT-TRANSFORM-MATRIX (cur-tm ?)

			(3D:DECOMPOSE-3D-MATRIX-TO-ROTATIONS ctm (setf rots (make-ftriplet!)) :xyz))

returns an ftriplet containing the rotation values around the x, y, and z axes respectively:

#(-42.48432 14.224002 3.8966184)

Specifying a different rotation order, e.g. :zyx, results in a different result:

#(-43.825947 7.7521873 12.572399)

Obtaining Scalings, Shearings, and Translations

Scaling, shearing, and translation factors can be obtained with a single function, 3D:DECOMPOSE-3D-MATRIX:

(3D:DECOMPOSE-3D-MATRIX matrix scale-triplet shear-triplet rotation-triplet 
translation-triplet &OPTIONAL perspective-quadruplet rotation-order)

Obtaining Positions

A position is defined as a location in space relative to [the global origin? Any frame of reference?]. Positions are stored in ftriplets. You can obtain positions from any matrix with 3D:FILL-POSITION-FROM-MATRIX:

(3D:FILL-POSITION-FROM-MATRIX matrix var-for-position)

For example, given an object, bound to obj1, which has been transformed along the global x-axis:

				(WITH-CURRENT-TRANSFORM-MATRIX (ctm obj1)

					(3D:FILL-POSITION-FROM-MATRIX ctm (setf position (make-ftriplet!))))

Obtaining Up, Direction, and Position from Matrices

Several directions are defined by convention with respect to a matrices frame of reference:

Given any transformation matrix, you can obtain vector values in the form of ftriplets for up, and direction with 3D:FILL-VECTORS-FROM-MATRIX:

				(3D:FILL-VECTORS-FROM-MATRIX matrix up-ftriplet direction-ftriplet)

Positions are expressed in terms of offsets along the x, y, and z axes from some know point. 3D:FILL-POSITION-AND-VECTORS-FROM-MATRIX returns a position and vectors for up and direction, with respect to the matrix frame of reference:

(3D:FILL-POSITION-AND-VECTORS-FROM-MATRIX matrix 

		position-ftriplet up-ftriplet direction-ftriplet)

Converting Matrices to Quaternions

You can create a quaternion from any transformation matrix with 3D:FILL-QUATERNION-FROM-MATRIX:

	(3D:FILL-QUATERNION-FROM-MATRIX matrix quaternion &OPTIONAL
fixed-rotation-order)

This function decomposes a 3D matrix to rotations, then fills a quaternion with the rotational data.



[N-World Contents] [Book Contents] [Prev] [Next] [Index]

Another fine product from Nichimen documentation!

Copyright © 1996, Nichimen Graphics Corporation. All rights reserved.