Imagine Texture Format

name: texture.doc

                               TEXTURE FORMAT
                       Imagine 3.0 Texture File Format

                         Rev 1.0  05-22-94 S.Kirvan
                Copyright 1994 Impulse Inc., Mpls, MN 55444

        All information contained herein is subject to change without
        notice.  Knowledge is dangerous - use at your own risk.  No
        warranties are made or implied, and no liability or responsibility
        is assumed.     Good luck.


This text will attempt to describe the construction of Imagine 3.0's
run-time loadable texture modules.  The idea behind this text is basically,
"teach by example."  By this, I mean that I have actually included and
(perhaps overly) documented a texture module that does a basic, seen-it-
way-too-many-times-in-raytraces, checkerboard (or checkerbored) texture.
The code included is in C and all text in this document is in comment form.
This document, of course, doesn't attempt to explain how Imagine 3.0's
textures do what they do, but is instead, a basic example for understanding

Unfortunately, Impulse does not have the support staff available to answer
technical questions about the information included in this document.
Hopefully, this will be basic enough to be a reasonable starting point, and
at the same time, will have enough depth to leave you with a sense of
direction towards more complex subjects.

Again, Good luck.


The primary communications between the texture code and the renderer are
handled through a "patch" structure.  The patch is a collection of
attributes and other information that are relevent to a particular spot
where a camera ray has hit an object.  This spot is refered to as the "hit
point."  The information passed to the texture code by the renderer (via.
the patch) can be modified to change surface attributes and create
textures.  Information commonly modified by texture code is the color,
transparency, reflect, and surface normals.

The functions, fwork() and iwork(), below, are the guts of the texture
code.  One of these functions, fwork() for floating point and PC versions,
and iwork() for Amiga integer version, is called from the renderer each
time a ray hits an object with this texture on it.  The patch structure
gets passed to the work function along with information about the texture
axis and the position of the hit point.  Using this information, the patch
may be modified, changing the object's attributes.  The texture code doesn't
get any information about the "world," or about the geometry (points/edges
/faces) of the object that was hit.

The entry module for the texture code is texture_init().  texture_init()
sets up a structure (ttable{}) which is passed Back to imagine.
texture_init() also checks for software version compatability.  The
structure passed Back to imagine has information so that imagine can find
the work() function, and the interface (the texture requester) data.
Imagine's rendering code uses this pointer to the work() function when it
calls the texture(s).

The section of texture.c (below), called "INITIALIZATIONS,"  sets up the
data for the user interface to the textures.  This section has all the
information for the text, defaults, and texture axis data that can be
modified by the user.

Imagine's texture hook is intentionally as streamlined as possible - there
is no error checking done within Imagine - it's all up to you.  If objects
start looking bright, dark, or translucent in ways you aren't expecting,
it's probably because some value has gone out of range and Imagine has
forced the errant value through the renderer.  Doing this may look cool, but
it could have unpredictable side effects so we recomend against doing it.

Color gun values (for color, reflect, and filter) are set by the user in a
range from 0 to 255.  Within the patch, the associated color gun values are
stored as floats (FRACTS) and must be normalized to range from 0.0 to 1.0.

The surface normal within the patch must also be normalized (ie. The length
of the normal vector must equal 1.0).  When doing bumpy textures (like
DinoSkin), you modify the surface normal to fool the renderer into putting
shading and specular highlights in areas that normally wouldn't get them.
As well as making sure the surface normal is normalized, you must also be
sure that modifications to the surface are made relative to the local
texture axis - not doing this will lead to bumps that have illogical
highlights and won't animate properly.  The way to see if the bumps are
being applied properly is as follows:  Create an animation with the texture
in question on an object - have the camera and a light source associated to
the object (so they all move in tandem). Put all these objects in black
space, and rotate the main object throughout the animation.  There should
be no change in the look of the object through the length of the animation
since the scene is being viewed and lit relative to the object itself.  If
the texture isn't doing the bumps properly, the specular highlights will
crawl on the bumps.  The texture, Rainbow.itx, which was include with
Imaigne 3.0 is a big help in debugging and getting bumpy textures to work

In the patch, some of the data is read-only.  This data is supplied as
information that can be used by the textures but modifying the data will
have no effect.

When a texture is used repeatedly in the same image, it is only loaded into
memory once - The important thing about this is that texture's can have
static data space, but this data space will be shared by every instance of
the texture.

*********   COMPILING DETAILS   *********

The SAS/C Compiler Ver 6.5 command line is as follows:
        sc nostartup code=far data=far math=68881 nostackcheck link
                                                to Texture.itx texture.c
(all typed on one line...)

On the PC, the texture code has to be linked as a Phar Lap 32-bit ".REX"
file (relocatable executable).  Impulse's textures were compiled using the
Metaware High-C compiler, and linked using Pharlap's linker.
        hc386 -Hoff=Protection -c texture.c
        386link texture.obj -relexe Texture.itx

The important things to "get" about trying to compile texture code are as
    - don't link in startup code
    - everything is 32 bit - ie. far pointers for both data and code
    - don't allow the compiler to put in any stack checking code


/*                                                            */
/*   Texture.c - Imagine 3.0 checkerboard texture example     */
/*                                                            */

/*********   CONSATANTS   *********/

// constants to be used in infoflags[]
#define TXTF_RED    1
#define TXTF_GRN    2
#define TXTF_BLU    4
#define TXTF_SCL    8

// version number constant - if not correct, texture won't load
#define TXT_VERS    0x49545854
//#define TXT_VERS    0x54585449        // PC version

#define NULL (0L)

/*********   DATA TYPES   *********/

typedef char                BYTE;
typedef unsigned char       UBYTE;
typedef short               WORD;
typedef unsigned short      UWORD;
typedef long                LONG;
typedef void *              APTR;
typedef float               FLOAT;

// FRACT can be typedef'ed as a float without harm since this example is
// for the floating point version only.  See TDDD.DOC for an explanation
// of the FRACT data type.

typedef float               FRACT;

typedef struct _vector {
    FRACT   X;
    FRACT   Y;
    FRACT   Z;

typedef struct _tform   {
    VECTOR  r;      // texture axes position
    VECTOR  a;      // texture's X axis alignment
    VECTOR  b;      // texture's Y axis alignment
    VECTOR  c;      // texture's Z axis alignment
    VECTOR  s;      // length of each axis
}   TFORM;

// The ttable{} structure is used as a communication link between
// Imagine and the run-time loadable texture modules.  This structure is
// initialized and passed Back to imagine by the entry module
// (texture_init()).

typedef struct ttable {
    LONG    id;             // version number identifier
    void    (*init)();      // reserved - curently unused
    void    (*cleanup)();   // reserved - curently unused
    void    (*work)();      // hook to the texture algorithm (the "guts")
    BYTE    **infotext;     // pointer to text fields for requester
    UBYTE   *infoflags;     // pointer to data field flags for requester
    APTR    params;         // pointer to data values for requester
    APTR    tform;          // pointer to texture axis geometry info

// The PATCH structure contains all the (ray) hit point info.
// This structure is passed to a texture function each time a
// ray hits the object with a texture on it.

typedef struct _patch {
    VECTOR  ptc_pos;    // global hit point - read only
    VECTOR  ptc_nor;    // surface normal (must be normalized)
    FRACT   ptc_col[3]; // surface color (R,G,B) at hit point
    FRACT   ptc_ref[3]; // surface color (R,G,B) at hit point
    FRACT   ptc_tra[3]; // surface color (R,G,B) at hit point
    FRACT   ptc_spc[3]; // surface color (R,G,B) at hit point - read only
    UWORD   ptc_shp;    // copy of SHAP flags - read only - see TDDD.DOC
    UWORD   ptc_shd;    // flag - obj can shadow itself - read only
    FRACT   ptc_pc0;
    FRACT   ptc_pc1;
    VECTOR  *ptc_ray;   // position/exit direction of camera ray - read only
    FRACT   raydist;
    FRACT   foglen;     // surface foglength
}   PATCH;

// In the patch structure, ptc_ray[0] is the (camera) ray's base point
// position, and ptc_ray[1] is ray's (normalized) direction vector

/*********   PROTOTYPES   *********/

TTABLE *texture_init (LONG);
void fwork (FRACT *, PATCH *, VECTOR *, FRACT *);
void iwork (FRACT *, PATCH *, VECTOR *, FRACT *);

/*********   INITIALIZATIONS   *********/

// infotext[][] is an array of strings that contain the texture name and
// all the text fields that appear in the texture requester.  The
// texture name file (infotext[0]) is not currently used, but we
// suggest supplying a name it in case it eventually is used.

BYTE    *infotext[17] = {
    "Tutorial Texture",
    "Color Red",
    "Color Green",
    "Color Blue",
    "", "", "", "",
    "", "", "", "",
    "", "", "", "",

// infoflags[] is an array of TXTF_ (texture flags) for the data
// fields of the texture requester.  The the TXTF flags are as
// follows:
//      Bit 0 - alter the red gun in the requester color chip
//      Bit 1 - alter the green gun in the requester color chip
//      Bit 2 - alter the blue gun in the requester color chip
//      Bit 3 - auto-scale this data field when object is scaled
//      Bits 4 thru 7 - reserved

UBYTE   infoflags[16] = {
    0, 0, 0, 0,
    0, 0, 0, 0,
    0, 0, 0, 0,

// fparams[] is an array of floats that are the initial defaults
// in the texture requester.  The values in this array can be
// modified by the user and are passed to the texture code by
// the floating point version of Imagine's renderer.

FLOAT   fparams[16] = {
    255.0, 255.0, 0.0, 0.0,
    0.0, 0.0, 0.0, 0.0,
    0.0, 0.0, 0.0, 0.0,
    0.0, 0.0, 0.0, 0.0,

// iparams[] is an array of FRACT that are the initial defaults
// in the texture requester.  The values in this array can be
// modified by the user and are passed to the texture code by
// the integer version (amiga only) of Imagine's renderer.  To
// use FRACT with math functions requires a little creative
// slight-of-hand with math macros and type conversions - doing
// this is beyond the scope of this example, so the integer version
// of this example texture will be stubbed out.  These are treated
// as fixed point, FRACTs, stored as longs.

LONG    iparams[16] = {
    0L, 0L, 0L, 0L,
    0L, 0L, 0L, 0L,
    0L, 0L, 0L, 0L,
    0L, 0L, 0L, 0L,

// ftform[] is an array of floats that represent the initial TFORM data
// (size and position) of the texture axis.  The values in this array can
// be modified by the user (via edit axis) and are passed to the texture
// code by the floating point version of Imagine's renderer.

FLOAT   ftform[15] = {
    0.0, 0.0, 0.0,      // position vector
    1.0, 0.0, 0.0,      // X axis alignment vector
    0.0, 1.0, 0.0,      // Y axis alignment vector
    0.0, 0.0, 1.0,      // Z axis alignment vector
    10.0, 10.0, 10.0,   // size of each axis

// itform[] is an array of FRACT that represent the initial TFORM data
// (size and position) of the texture axis.  The values in this array can
// be modified by the user (via edit axis) and are passed to the texture
// code by the integer version of Imagine's renderer.  Again, this example
// is for floating point systems only.  The FRACT data type is explained
// in TDDD.DOC and the integer version of this texture example will be
// stubbed out.  These are treated as fixed point, FRACTs, stored as longs.

LONG    itform[15] = {
    0L, 0L, 0L,
    0L, 0L, 0L,
    0L, 0L, 0L,
    0L, 0L, 0L,
    10*0x10000L, 10*0x10000L, 10*0x10000L,

// this ttable{} structure is defined earlier and initialized here.

TTABLE ttable = {
    TXT_VERS,       /* version identifier - this contstant must be used */

/*********   FUNCTIONS   *********/

// This is the texture_init() module for the textures.  This is the entry
// module through which Imagine loads and calls the texture code.
// texture_init() is called with a version number and a flag representing
// whether this texture is being called from a floating point version or an
// integer version of Imagine.  On the Amiga, these two (WORD) arguments
// are packed into and passed as a single LONG which, in turn, is parsed
// into the two arguments and interpreted within texture_init(). If all
// goes well, texture_init() then initializes the ttable{} structure
// (defined above) with the proper info and returns a pointer, for the
// ttable{} structure, Back to Imagine.

TTABLE *texture_init (LONG arg0)
    // "TTABLE *texture_init (int vers, int fp)" on PC

    WORD    vers, fp;

    vers = arg0 >> 16;      // parse out the two arguments
    fp = arg0 & 0xffff;

    if(vers != 0x60)        // look for wrong version number
        return 0L;

    if (fp) {      // called from the floating point version = fwork;
        ttable.params = (APTR)fparams;
        ttable.tform = (APTR)ftform;
    } else {      // called from the (Amiga only) integer version = iwork;
        ttable.params = (APTR)iparams;
        ttable.tform = (APTR)itform;
    return &ttable;

void fwork(params, pt, v, t)    // this is the floating point work function.
FRACT *params;          // pointer to user definable numbers in requester
PATCH *pt;              // pointer to patch structure - defined above
VECTOR *v;              // hit position  -  relative to texture axis
FRACT *t;               // info about texture axis (TFORM array - 15 floats)
    FLOAT X, Y, Z;

    X = v->X / t[12];  // note that the texture axis is used for scaling
    Y = v->Y / t[13];
    Z = v->Z / t[14];

    if (X < 0.0) X -= 1.0; // this eliminates duplication across the axes
    if (Y < 0.0) Y -= 1.0; // as the chex go from the posative to the
    if (Z < 0.0) Z -= 1.0; // negative regions

    if (((int)X + (int)Y + (int)Z) % 2) { // check for even numbers
        pt->ptc_col[0] = params[0] * .00392157; // use the params from
        pt->ptc_col[1] = params[1] * .00392157; // the requester on
        pt->ptc_col[2] = params[2] * .00392157; // these chex.
    } else {
        pt->ptc_col[0] = 0.0;   // force the others to be blue
        pt->ptc_col[1] = 0.0;
        pt->ptc_col[2] = 0.5;


void iwork(params, pt, v, t)   // This is the integer version work function.
FRACT *params;
PATCH *pt;
    ;   // stub...

Last Update: June 10, 1995
Back to Ian's HomePage