Texture Garden

Texture Garden's Texture Generation Language

This file documents version 1.12 of the texture generation language
This is distributed with Texture Garden version 1.12.

1.00 Introduction
1.10 The format of this file
2.00 Writing textures
2.10 Syntactic considerations
3.00 Command summary
4.00 Command details

1.00 Introduction

The Language:

When editing texture generation programs, some syntactical rules need to be borne in mind. The language is a compromise between three design criteria:

  • That it should be readable by a human;
  • That it should be editable by the computer;
  • That it should go like the clappers.

Compactness was not considered to be particularly important. Compactness and bandwith issues will be addressed by a bytecode version of the language.

Characteristically commands are verbose. It is recommended that typing in commands using longhand is to be avoided and that cut and paste facilities are employed wherever possible.

When loaded, commands are translated into an internal format and the original textfile is discarded. Loaded files may contain command names in any case, truncated names, commands optionally abbreviated with the . character, confused and confusing punctuation and mismatched brackets. When the file is saved, it is cleaned up, reformatted and expanded into a standard format. Unless you know what you are doing, keeping copies of the original files is recommended, as What The Computer Thinks You Are Talking About may initially differ from What You Are Trying To Say.

Error checking is currently implemented mainly at the parsing stage. If the program you feed the computer contains errors, a message will tell you which line they are found on. These messages should be helpful in the process of tracking down mistakes. Runtime error messages are also possible, but should occur rarely in practise.

If your version of this program is unregistered, then you will probably experience difficulties in changing existing textures or getting your own textures to be displayed. Please refer to the Register document for details of why this occurs.

Resources:

There are currently several main resources available for use in generating textures. The texture generation program sees a virtual machine with a variety of storage media and one primary output channel.

There are three one dimensional buffers of &400 16-bit values which may be manipulated. One two dimensional buffer is provided, with dimensions controlled by the size of the sprite to be generated. A buffer for storing colourmaps exists and there is space reserved for the manipulation of &400 variables. These contain unspecified values on entry to the routine.

Texture generation programs are entered when a texture is required. Their execution begins at their first line.

1.10 The format of this file

In the command summary which follows, each command is named and then it is followed by a string in brackets indicating what kind of parameters it takes. If there are no brackets then the command takes no parameters. In the second part of the document, descriptions of the operation of each of the commands are also given.

Comments are considered to be anything after a | character. Comments in If...Then...Else lines are treated using the same conventions as in Obey files, i.e. If 1 = 2 THEN |ECHO Equal! ELSE ECHO Not_Equal! would print Not_Equal! in an Obey file, despite the earlier |.

2.00 Writing textures

Conventions:

All textures should begin with a definition of their primary palette enclosed by the commands StartColourDefinition and EndColourDefinition.

Textures terminate when they reach the End command. It is conventional to place subroutine definitions after the End so they are separate from the main program. The mutator may rely on this in the future.

2.10 Syntactic considerations

Essentially, the syntax of the language is simple.

Everything falls into one of four categories.

These four categories are: Comments, Commands, Functions and Parameters.

Comments are simple: they are considered to be anything after a | character.

Commands come in three types: Conditional, Ordinary and Branch commands.

Conditionals have the form: If <Condition> Then <Command> Else <Command>. The Until <Condition> command also comes into this category.

Ordinary commands normally appear at the start of each line. They may have zero or more parameters. If they have parameters then these are enclosed in brackets.

Branch related commands come in several types. Define <DefinitionMarker> sets a named mark at a point in the file, Call <DefinitionMarker> calls the relevant Define and continues until a Return is encountered. Goto <DefinitionMarker> simply continues execution from the relevant Define.

Functions may be given instead of parameters. They should never appear at the start of a line. Functions are defined over the domain (&0000-&FFFF). They generally range over (&0000-&FFFF). That they have the same range as their domains means that it is convenient to feed the results of functions to other functions as parameters.

Parameters may cover the range from &0000 to &FFFF. They may be given as signed or unsigned, decimal or hexadecimal (with a preceding &) integers. Where a parameter is required, this may be given as a constant numerical value or as a function. Variables may be used and are implemented as simple functions.

Textures are not especially sensitive to punctuation mistakes, spaces, or other idiosyncrasies. If brackets are mismatched, the program guesses where they should be and inserts them. This is useful when editing textures in a text editor.

3.00 Command summary

Index

01
Functions
02 Types
03 Constants
04 Variables
05 Section of Functions of X and Y
06 Miscellaneous
07 Conditional
08 Mutation
09 Animation
10 Graphical output
11 Fourier Transform related
12 Variable Setting
13 Dithering
14 Processing
15 Linear segments
16 Shifting
17 Rotation
18 Loops
19 Buffer manipulation
20 Waves and Distortions
21 Colour related
22 Mirroring, Flipping and Shearing
23 Blobbing
24 Branching
25 Light sources and bump maps

Main List

01 Functions

02 Types
03 Constants
04 Variables
05 Section of Functions of X and Y
06 Miscellaneous

07 Conditional

08 Mutation
09 Animation
10 Graphical output
11 Fourier Transform related
12 Variable Setting
13 Dithering
14 Processing
15 Linear segments
16 Shifting
17 Rotation
18 Loops
19 Buffer manipulation
20 Waves and Distortions
21 Colour related
22 Mirroring, Flipping and Shearing
23 Blobbing
24 Branching
25 Light sources and bump maps

4.00 Command details

01 Section of Functions

Combine(Type,A,B) * partly deprecated *

This function combines the values of A and B according to the rule described by the Type given. This type may be expressed as one of the following:

SimpleAddition, UnboundedAddition, CeilingAddition, HalvingAddition, SimpleSubtraction, UnboundedSubtraction, FloorSubtraction, HalvingSubtraction, Maximise, Minimise, Overwrite, PositiveOverwrite, Preserve, Multiplication or ScaledMultiplication.

These are described in the section on types below. Alternatively, a number or expression may be given. Please refer to the types section which contains technical details on this.

As all the above Types now have their own functions devoted to them, Combine(Type,A,B) is described as partly deprecated, as it merely calls these functions.

When used with different parameters (i.e. a number or an expression), from those in the above list, its use is still legitimate. Please refer to the Types section which contains technical details on this.

Add(A,B)

Equivalent and preferred to the Combine function using combination type UnboundedAddition.

Returns (A + B) And &FFFF.

Subtract(A,B)

Equivalent and preferred to the Combine function using combination type UnboundedSubtraction.

Returns (A - B) And &FFFF.

CeilingAdd(A,B)

Equivalent (and preferred to) the Combine function using combination type CeilingAddition.

IF A + B > &FFFF THEN returns &FFFF ELSE returns (A + B).

FloorSubtract(A,B)

Equivalent (and preferred to) the Combine function using combination type FloorSubtraction.

IF B > A THEN returns 0 ELSE returns (A - B).

SimpleAdd(A,B)

Equivalent (and preferred to) the Combine function using combination type SimpleAddition.

IF A + B < &10000 THEN returns (A + B) ELSE returns &20000 - (A + B).

SimpleSubtract(A,B)

Equivalent (and preferred to) the Combine function using combination type SimpleSubtraction.

IF A - B > 0 THEN returns (A - B) ELSE returns (B - A).

HalvingAdd(A,B)

Equivalent (and preferred to) the Combine function using combination type HalvingAddition.

Returns (A + B) DIV 2.

HalvingSubtract(A,B)

Equivalent (and preferred to) the Combine function using combination type HalvingSubtraction.

Returns ((A - B) DIV 2) &FFFF (A and B are considered unsigned).

Max(A,B)

Equivalent and preferred to the Combine function using combination type Maximise.

Returns the greater of A and B (A and B are considered unsigned).

Min(A,B)

Equivalent and preferred to the Combine function using combination typeMinimise.

Returns the greater of A and B (A and B are considered unsigned).

Multiply(A,B)

Equivalent and preferred to the Combine function using combination type Multiplication.

Returns A * B And &FFFF.

ScaledMultiply(A,B)

Equivalent and preferred to the Combine function using combination type ScaledMultiplication.

Returns ((A * B) DIV &10000) And &FFFF.

ScaledSignedMultiply(A,B)

Function returning the value of (A * B) << 16, treating A and B as signed integers.

SignedMultiply(A,B)

Function returning the value of A * B, treating A and B as signed integers.

PartlyScaledSignedMultiply(A,B)

Function returning the value of (A * B) << 8, treating A and B as signed integers.

PartlyScaledMultiply(A,B)

Function returning the value of (A * B) << 8.

Divide(Numerator,Denominator)

Function returning the value of Integer(Numerator / Denominator)

Eor(A,B)

Function returning the value of A Exclusive_Or B.

And(A,B)

Function returning the value of A And B (bitwise AND).

Or(A,B)

Function returning the value of A Or B (bitwise OR).

Absolute(A)

IF A < &8000 THEN returns A ELSE returns A Exclusive_Or &FFFF.

LogicalShiftLeft(A,Exponent)

Function returning the value of A * 2^Exponent (A << Exponent).

LogicalShiftRight(A,Exponent)

Function returning the value of Integer(A / 2^Exponent) (A >> Exponent).

ArithmeticShiftLeft(A,Exponent)

Function returning the value of A * 2^Exponent (A << Exponent).

ArithmeticShiftRight(A,Exponent)

Function returning the value of A / 2^Exponent treating A as a signed integer.

Variable(Variable_Number)

Returns the value of the <Variable_Number>th system variable.

Random(And_value,Eor_value)

Function returning the value of a 16-bit system random number generator ANDed with <And_value> and then EORed with <Eor_value>.

Sin(Theta)

Returns ((Sin of <Theta>) + 0.5) * &8000.

Cos(Theta)

Returns ((Sin of <Theta>) + 0.5) * &8000.

SignedSin(Theta)

Returns (Sin of <Theta>) * &8000.

SignedCos(Theta)

Returns (Sin of <Theta>) * &8000.

SquareRoot(Value)

Returns the square root of Value.

Minus(Value)

Minus(Value) = -Value. Unless Value = &8000 : Minus(&8000) = &8000.

02 Section of Types

Types govern how two parameters are combined with one another. Precicely which two parameters it refers to depends on the function using it. Often, a process will perform some calculation on the value of a function at a point and then recombine this with the original value at that point before using its result.

In the following description of the common Types, A and B are used to describe the two values to be combined.

SimpleAddition

IF A + B < &10000 THEN returns (A + B) ELSE returns &20000 - (A + B).

This produces an effect like a ball bouncing off a ceiling (ahem).

UnboundedAddition

Returns (A + B) And &FFFF.

CeilingAddition

IF A + B < &10000 THEN returns (A + B) ELSE returns &FFFF.

HalvingAddition

Returns (A + B) DIV 2 (A and B are considered unsigned).

SimpleSubtraction

IF A - B > 0 THEN returns (A - B) ELSE returns (B - A).

This produces an effect like a ball bouncing off a floor.

UnboundedSubtraction

Returns (A - B) And &FFFF.

FloorSubtraction

IF A - B > 0 THEN returns (A - B) ELSE returns 0.

HalvingSubtraction

Returns (A - B) DIV 2 (A and B are considered unsigned).

Maximise

Returns the greater of A and B (A and B are considered unsigned).

Minimise

Returns the lesser of A and B (A and B are considered unsigned).

Overwrite

Returns B.

PositiveOverwrite

Returns (B EOR &8000).

Preserve

Returns A.

Multiplication

Returns A * B And &FFFF.

ScaledMultiplication

Returns ((A * B) DIV &10000) And &FFFF.

Zeroise

Returns 0.

03 Section of Constants

True

Returns &FFFF.

False

Returns 0.

Zero

Returns 0.

Ninety

Returns &4000.

OneHundredAndEighty

Returns &8000.

TwoHundredandSeventy

Returns &C000. These commands are useful in connection with Rotate. They are not affected by the mutation engine.

FloydSteinberg

Returns &FEBE (from FloydstEinBErg). Used with the Dithering command.

RGB

Returns 0. Usually used as a description of a colour model.

HSV

Returns 1. Usually used as a description of a colour model.

CIE

Returns 2. Usually used as a description of a colour model.

Gouraud

Returns 0. Usually used as a lightsource type. For historical reasons,the misspelling Gourad is also supported.

Phong

Returns 1. Usually used as a lightsource type.

Specular

Returns 2. Usually used as a lightsource type.

SpecularPhong * deprecated *

Returns 3. Usually used as a lightsource type. Texture authors should now use Add(Specular,Phong) in preference to this.

North (also N)

Returns &0.

South (also S)

Returns &1.

West (also W)

Returns &2.

East (also E)

Returns &3.

NorthWest (also NW)

Returns &4.

SouthWest (also SW)

Returns &5.

NorthEast (also NE)

Returns &6.

SouthEast (also SE)

Returns &7.

On

Returns &FFFF.

Off

Returns &0.

04 Section of Variables

X

Y

Z

Return X, Y, and Z in appropriate situations. In OneDimensional commands, Y is always 0. Z should not be used, except in DefineSolidBlock commands.

Size

Returns the size at which the texture is being generated. This should not normally be referred to.

LogSize

Returns the size at which the texture is being generated. This should not normally be referred to.

LogBitsPerPixel

Returns the log in base 2 of the number of bits-per-pixel in the screen mode in which the texture is being generated. This may be usefully referred to when choosing dithering patterns for the texture.

AnimationFrameNumber

Returns the number of the animation frame currently being generated. This is only of interest if the texture is to be animated.

AnimationType(Frequency)

Returns the value of the corresponding value to Frequency in the buffer controlling the type of animation to be used. This buffer can be thought of as containing 1024 signed 4-bit values, controlling the way in which various frequencires in the texture are animated. This should only normally be referred to if the texture is to be animated. If the texture is not being animated and the Use animation type option is not on then it returns 0.

Registered

Returns 0 if the user has the Freeware demonstration version of Texture Garden: some non-zero value otherwise.

05 Section of Functions of X and Y

These functions are functions of X and Y as well as of their parameters. They are mainly used for describing filters by using a command like CreateTwoDimensionalFilter. Filters are usually used to filter random noise prior to this result then being inverse-fourier-transform'd. The descriptions of the functions describe what they look like when viewed in the frequency domain. Descriptions in three dimensions use the convention of describing the value of the function at a position (X,Y) as its height at that point.

Noise(Intensity,MaximumFrequency)

The plan view of this function looks like a circle about the origin. In three dimensions it looks like a cylindrical tin can of height H = Intensity.

PinkNoise(Intensity,MaximumFrequency)

The plan view of this function looks like a circle about the origin. It fades towards the edges and may be visualised in three dimensions as a cone or witch's hat of height H = Intensity.

QuickNoise(Intensity,MaximumFrequency)

The plan view of this function looks like a square about the origin. In three dimensions it looks like a square office block of height H = Intensity.

BandpassNoise(Intensity,MinimumFrequency,MaximumFrequency)

The plan view of this function looks like a circle about the origin with its centre hollowed out. In three dimensions it looks like a doughnut of height H = Intensity with sharp corners.

BandpassQuickNoise(Intensity,MinimumFrequency,MaximumFrequency)

The plan view of this function looks like a square about the origin with its centre hollowed out. In three dimensions it looks like a square office block with a square central courtyard.

FractalNoise(MaximumIntensity,FractalDimension)

This function looks like a mountain whose sides follow the function H = FractalDimension/(RxR) where R is the distance from the origin. The mountain is truncated at H = MaximumIntensity.

ShiftedNoise(Intensity,MaximumFrequency,X,Y)

The plan view of this function looks like a circle centred at (X,Y). In three dimensions it looks like a cylindrical tin can of height H = Intensity.

ShiftedPinkNoise(Intensity,MaximumFrequency,X,Y)

The plan view of this function looks like a circle centred at (X,Y). It fades towards the edges and may be visualised in three dimensions as a cone or witch's hat of height H = Intensity.

ShiftedQuickNoise(Intensity,MaximumFrequency,X,Y)

The plan view of this function looks like a square centred at (X,Y). In three dimensions it looks like a square office block of height H = Intensity.

ShiftedFractalNoise(Intensity,MaximumFrequency,X,Y)

This function looks like a mountain whose sides follow the function H = FractalDimension/(RxR) where R is the distance from the point (X,Y). The mountain is truncated at H = MaximumIntensity.

ShiftedSymmetricNoise(Intensity,MaximumFrequency,X,Y)

The plan view of this function looks like four circles centred at (X,Y), (-X,Y), (X,-Y) and (-X,-Y). In three dimensions it looks like four cylindrical tin cans of height H = Intensity.

ShiftedSymmetricPinkNoise(Intensity,MaximumFrequency,X,Y)

The plan view of this function looks like four circles centred at (X,Y), (-X,Y), (X,-Y) and (-X,-Y). They fade towards their edges and may be visualised in three dimensions as four cones or witch's hats of height H = Intensity.

ShiftedSymmetricQuickNoise(Intensity,MaximumFrequency,X,Y)

The plan view of this function looks like four squares centred at (X,Y), (-X,Y), (X,-Y) and (-X,-Y). In three dimensions they look like four square office blocks of height H = Intensity.

ShiftedSymmetricFractalNoise(MaximumIntensity,FractalDimension,X,Y)

This function looks like four mountains whose sides follow the function H = FractalDimension/(RxR) where R is the distance from the point (X,Y). The mountains are symmetrical about the lines X = 0 and Y = 0. The mountains are truncated at H = MaximumIntensity.

TwoDimensionalPoint(X,Y)

Returns the value or height at the point (X,Y). This command and the three that follow it employ an anti-aliising technique to smooth sharp edges.

OneDimensionalPoint(X)

Returns the value or height at the point (X) in the main one-dimensional buffer.

OneDimensionalPointOne(X) * deprecated *

Returns the value or height at the point (X) in the first one-dimensional buffer.

OneDimensionalPointTwo(X) * deprecated *

Returns the value or height at the point (X) in the second one-dimensional buffer.

QuickTwoDimensionalPoint(X,Y)

QuickOneDimensionalPoint(X)

QuickOneDimensionalPointOne(X) * deprecated *

QuickOneDimensionalPointTwo(X) * deprecated *

These four commands are identical to the four above them, except for the fact that they do not employ any anti-aliasing techniques.

TwoDPoint(Buffer,X,Y)

Returns the value or height at the point (X) in the specified two dimensional buffer.

OneDPoint(Buffer,X)

Returns the value or height at the point (X) in the specified one dimensional buffer.

QuickTwoDPoint(Buffer,X,Y)

QuickOneDPoint(Buffer,X)

These two commands are identical to the two above them, except they do not use any anti-aliasing techniques.

06 Miscellaneous commands

Percentage(Percentage)

Displays the percentage of the texture completed using the hourglass.

The Percentage parameter's domain may be considered as being either from &0 to &63 (99) or from &80 to &63FF (100 * 256 - 1). Using Percentage(&FFFF) turns off the percentage display.

AddToPercentage(DeltaPercentage)

Adds the value of DeltaPercentage to the current hourglass percentage.

Beep

Bell

These produce a short beep using an equivalent to VDU 7.

UnknownCommand

This is not a command that should ever appear in a file, bit is inserted whenever a command is not recognised. If a texture does not form, and saving the texture as a textfile contains this command, then probably some mistake has been make in the typing of new commands.

End

This command should always be included at the end of the texture file and before any subroutines are declared. It should always be present, by convention.

Checksum(Value)

This command is included as a marketing device for the program. It enables distribution of a version of the program that can read and display textures, and allow their saving as bitmaps, while not allowing unregistered users to edit the programs that generate these textures.

In unregistered versions of the program, textures which do not contain the correct value in their checksum or contain no checksum at all are rejected by the program and not displayed. This limits the extent to which unregistered users may manipulate their textures.

In registered versions, an additional module is supplied which will enable the program (and all future upgrades of it) to read textures independently of the checksum.

A site license or Super-user version will also display whether the checksum is correct, and will write texture files with correct checksums, enabling people other than the author to distribute textures readable by the freeware distributed version of the program.

Error(Value)

This is provided for debugging purposes. When it is executed, texture generation is terminated and control is returned to the user. The Value is reported to the user.

Report(Value)

This is provided for debugging purposes. When it is executed, the value of Value is sent via WIMP messages to a program such as my own !Zephyr, which keeps a scrolling window of text for the purposes of debugging WIMP applications. Message number &804C0 is used.

ErrorText <string>

When executed, texture generation is terminated and control is returned to the user with the <string> reported as an error message.

ReportText <string>

When executed, <string> is sent via WIMP message to a WIMP debugging program such as my own Zephyr. Message number &804C0 is used with a block containing the raw text to be sent.

UnknownCommand

This is for internal use only and should not be used.

07 Conditional commands

If <Condition> Then <Command> Else <Command>...

This command is unusual in that it does not require brackets for its parameters. The syntax is identical to the way if statements are used in Obey files. The commands may optionally contain further If statements. Comments are treated as they would be in Obey files, i.e. If 1 = 2 THEN |ECHO Equal! ELSE ECHO Not_Equal! would print Not_Equal! in an Obey file, despite the earlier |.

Digression beginning...

If ... Then ... If ... Then ... Else constructs currently treat the Else as belonging to BOTH Ifs. One can't help thinking that If ... Then ... If ... Then ... Else ... Else should make sense - i.e. the Else should belong to the nearest If. Unless the Ifs are nested in this manner one of them cannot have an Else without the other one getting it too. However, BASIC does not agree (and that's BBC BASIC, the official Englishman's dialect). No doubt the Germans would have implemented it as they are used to recursively peeling verbs from their sentences and correlating them with earlier nouns. Still, the sentence The boy, the girl, the man, kissed, hit, ran is reputed to be good English, though no-one seems quite to be able to get their head 'round it. It is possible to invert the first condition so that the If comes after the Else instead of after the Then (unless the command required there is another If statement).

Obey files don't seem to like their Ifs nested either (programmers must find it easier to just look for the next Else than to count the intermediate Ifs), and so neither does Texture Garden (which tries to follow the Obey file syntax, so its files can be displayed in Obey file syntax-colouring modes in text editors).

...digression end.

As <Condition>s you cannot use <, =, >= != etc.
A list of conditions that are acceptable follows:

IsLessThan(A,B)

IsGreaterThan(A,B)

IsLessThanOrEqualTo(A,B)

IsGreaterThanOrEqualTo(A,B)

SignedIsLessThan(A,B)

SignedIsGreaterThan(A,B)

SignedIsLessThanOrEqualTo(A,B)

SignedIsGreaterThanOrEqualTo(A,B)

IsEqualTo(A,B)

IsNotEqualTo(A,B)

These functions are fairly self explanatory; hopefully a single example will suffice:

IsLessThan(A,B) returns TRUE (&FFFF or -1) if A is less than B, otherwise it returns FALSE (0).

08 Mutation Commands

StopMutating

This command effectively stops the system from mutating until the next StartMutating command. This has the effect of stopping any intervening commands from being affected by any mutations or sexual recombination that may occur. This may be of use when stopping mutations affecting parts of the texture whose structural integrity is important.

StartMutating

Reverses the effect of the last StopMutating command.

Mutating(On|Off)

This is equivalent to (and replaces) the above two commands.

09 Animation Commands

StopAnimating

This command effectively stops the system variable AnimationFrameNumber from affecting the texture until the next StartAnimating command. This has the effect of stopping any intervening commands from being affected by any animation that is occuring. This may be of use when stopping palettes which are declared using Fourier transforms from being animated causing the palette to change then this is considered undesirable.

StartAnimating

Reverses the effect of the last StopAnimating command.

Animating(On|Off)

This is equivalent to (and replaces) the above two commands. See also:

SetAnimationFrameNumber, AnimationFrameNumber and AnimationType(Frequency).

10 Graphical output commands

MakeSprite

Creates a sprite in the current mode using the current colourmap.

AddToSprite

Adds to an existing sprite using the current colourmap. The existing sprite is written to only at the points where the value at (X,Y) is non-zero.

MakeVirtualSprite(Type_R,Type_G,Type_B)

This makes a 24-bit colour virtual sprite.

This is useful when creating images by using multiple layers of texture.

Type_R, Type_G and Type_B are the combination-types to be used when adding to an existing sprite for red, green and blue components respectively.

The virtual sprite should not be assumed to be blank when it is created and so the first occurrence of this command should normally use Overwrite,Overwrite,Overwrite as its parameters.

MakeSpriteFromVirtualSprite

This command makes a RISC OS sprite from the 24-bit virtual sprite created by a previous MakeVirtualSprite command.

FloydSteinberg dithering may be applied, but will only produce noticable effects if the texture is rendered in 16, 256 or 32K modes. In 16M modes, FloydSteinberg dithering is not required.

The MakeSpriteFromVirtualSprite command usually leaves the virtual sprite unaffected, but if FloydSteinberg dithering is being used, it is altered to the dithered version of the sprite in the selected mode.

One point to note is that dithering should always be set either to Zero or to FloydSteinberg before this command is executed. As the dithering command is affected by the mutator this should not be as a raw number, but as one of the strings mentioned above.

This is for compatibility with other possible future dithering routines.

TruncateSpriteVertically(Bottom,Top)

Truncates the top and bottom edges from the sprite. * #

TruncateSpriteHorizontally(Left,Right)

Truncates the left- and right-hand edges from the sprite. * #

ResizeSprite(XFactor,YFactor)

Performs TruncateSpriteHorizontally(0,XFactor) and TruncateSpriteVertically(0,YFactor). This is the method used by Texture Garden's front-end to resize textures.

* Note that with these commands, the sprite may not subsequently be added to. These operations may stop the resulting sprite from tessellating.

# These commands should only be used when resizing the texture using the front-end is not necessary, as they will stop textures from being resized.

11 Fourier Transform related commands

CreateTwoDimensionalFilter(Filter_Description)

This function is a function of X and Y as well as of its parameter Filter_Description. It generates a field of values in the main two dimensional working buffer. These form a Filter which is usually used to filter random noise with the result then being inverse-fourier-transform'd. The functions commonly used are described in a different section. Filters may be defined in other ways, if so desired, but this is the main recommended method.

TwoDimensionalTransform

This and the command that follow it perform a series of operations on the main two dimensional working buffer. First, the contents of the buffer are used as a filter to filter a spectrum of random white noise.

What noise is used and how the filtering is performed is affected by the following commands: Seed(Value), Phase(Value), NoiseToBeFiltered(Value), CreateCosineSymmetry, CreateSineSymmetry, CreateCosineArtefacts(Value), CreateSineArtefacts(Value), AbandonCosinePhase, and AbandonSinePhase.

Exactly how these commands affect the filtering of the noise is partially described elsewhere.

Next the resulting spectrum is exposed to an two-dimensional inverse fast fourier transform. This is an elaborate function that is capable of converting a signal from the frequency domain into the space/time domain. Readers are referred to signal processing textbooks for further details. Finally the resultant vectors are converted to a series of amplitudes by squaring, adding and then square-rooting their components.

SmoothTwoDimensionalTransform

This is identical to the above command, except the resulting values are centred about &8000, eliminating the spiky artefacts that occur near H = 0 when the plain TwoDimensionalTransform is used.

CreateOneDimensionalFilter(Filter_Description)

See above description of this function in two dimensions. The main one dimensional buffer is used. The filter is calculated only over the range where Y = 0.

CreateOneDFilter(BufferNumber,Filter_Description)

The filter is calculated by setting Y = 0 and Z = 0.

OneDimensionalTransform

See above description of this function in two dimensions.The main one dimensional buffer is used.

OneDTransform(BufferNumber)

As OneDimensionalTransform but uses buffer <BufferNumber>.

SmoothOneDimensionalTransform

See above description of this function in two dimensions. The main one dimensional buffer is used.

SmoothOneDTransform(BufferNumber)

As SmoothOneDimensionalTransform but uses buffer <BufferNumber>.

Invariance(On|Off)

This command is used to influence how filters are created. The idea behind it is to provide a single command which will allow textures to be generated so that when the texture is enlarged, more of the texture becomes visible. The option defaults to off.

This command is currently in an embryonic state and only affects the creation of filters for fourier transforms. It should not currently be used in the making of one-dimensional filters. It has an effect on these, but this is currently broken, and is subject to change in future versions of the program.

Invariance also affects the size of Ripples and Radials commands, scaling these in an appropriate manner. Note that to provide true scale invariance for these commands, more of them will need to be executed. A density command may be provided in the future to assist with this kind of operation.

12 Variable Setting commands

SetVariable(Variable_Number,Value)

This sets variable number (Variable_Number MOD &400) to be equal to <Value>.

SetAnimationFrameNumber(Value)

This sets the system variable AnimationFrameNumber to be equal to <Value>. This not normally be referred to. If the texture is to be animated manually using batch files, then it may be used in conjunction with the Use Animation type option.

Seed(Value)

Sets the seed to be used in the random number generators.

RestoreArtefactsFlags

Resets the AbandonCosinePhase, AbandonSinePhase, CreateCosineSymmetry and CreateSineSymmetry flags to off.

Sets the values of AbandonCosinePhase, AbandonSinePhase, CreateCosineArtefacts and CreateSineArtefacts to zero.

Phase(Value)

Sets the phase to be used in the filtering of the random white noise to <Value>. This phase affects all frequencys equally. Changing it smoothly produces similar effects to animating the texture using the Positive.Boring animation pattern.

NoiseToBeFiltered(Value)

Sets the amplitude of the random white noise used in the filtering to <Value>.

CreateCosineSymmetry

When filtering white noise, eliminate all sin components. This produces a pattern guaranteed to at its maximum at its centre, with a rotational symmetry of two.

CreateSineSymmetry

When filtering white noise, eliminate all cosine components. This produces a pattern guaranteed to be zero at its centre, with a rotational symmetry of two.

CreateCosineArtefacts(Value)

When filtering white noise, this eliminates all cosine components with an amplitude less than a certain value. Its effect is like CreateSineSymmetry but not as extreme.

CreateSineArtefacts(Value)

When filtering white noise, this eliminates all sine components with an amplitude less than a certain value. Its effect is like CreateCosineSymmetry but not as extreme.

AbandonCosinePhase

When filtering white noise, this eliminates the effect of the noise on the cosine components of the result. Instead of white noise, bright white light may be thought of as being used.

AbandonSinePhase

See AbandonCosinePhase substituting Sine for Cosine.

13 Dithering commands

The next three commands affect the way dithering is used in the generation of palettes and sprites.

Dithering0 affects the red component of palettes defined using RGB data, the hue component of palettes defined using HSV data and general (within-palette) dithering when making Sprites.

Dithering1 affects the green component of palettes defined using RGB data and the saturation component of palettes defined using HSV data.

Dithering2 affects the blue component of palettes defined using RGB data and the value component of palettes defined using HSV data.

Floyd-Steinberg dithering is available if virtual sprites are being used through the Dithering(FloydSteinberg) command. Note that ScaledDithering(FloydSteinberg) will not usually work.

Dithering(Value)

Sets Dithering0, Dithering1 and Dithering2 to be equal to <Value>.

DitheringOne(Value)

Sets Dithering1 to be equal to <Value>.

DitheringTwo(Value)

Sets Dithering2 to be equal to <Value>.

ScaledDithering(Value)

Sets Dithering0 to be equal to a value which depends on the colour depth of the mode being used. The precise Value given to Dithering0 varies as follows:

Depth Result

16M 0
32K Value * 3 / 8
256 Value / 2
16 Value
4 Value * 2
2 Value * 4

If the result is greater than &FFFF, then it is set to this ceiling.

14 Processing commands

OneDimensionalProcess(X1,X2,Function,Type)

The section of the main one dimensional buffer defined by (X1,X2) is processed according to Function and combined with its original self using combination-type Type. Function may usefully contain commands such as OneDimensionalPoint(X).

OneDProcess(BufferNumber,X1,X2,Function,Type)

Equivalent to the above command, though acting on the specified BufferNumber.

TwoDimensionalProcess(X1,Y1,X2,Y2,Function,Type)

The rectangle in the main two dimensional buffer defined by opposing corners at (X1,Y1) and (X2,Y2) is processed according to Function and combined with its original self using combination-type Type. Function may usefully contain commands such as TwoDimensionalPoint(X,Y).

OneDimensionalFilter(Left,Centre,Right,Type)

Each point in the one dimensional buffer is processed by adding the values of its two neighbours and itself (multiplied by one of three multipliers) and dividing by &100 before combining it with its original self using combination-type Type. The following diagram describes the position of the relevant multipliers.

 -------------
| L | C | R |
-------------

OneDFilter(BufferNumber,Left,Centre,Right,Type)

Equivalent to the above command, though acting on the specified BufferNumber.

TwoDimensionalFilter(Top_Left,Top_Centre,Top_Right,Middle_Left,Middle_Centre,Middle_Right,Bottom_Left,Bottom_Centre,Bottom_Right,Type)

Each point in the two dimensional buffer is processed by adding the values of its eight neighbours and itself (multiplied by a series of multipliers) and dividing by &100 before combining it with its original self using combination-type Type. The following diagram describes the position of the relevant multipliers.

 ----------------
| TL | TC | TR |
----------------
| ML | MC | MR |
----------------
| BL | BC | BR |
----------------

OneDimensionalContrast(Value)

Multiplies the entire contents of the main one dimensional buffer by <Value>, divides by &100 and then truncates any high bit spillage.

OneDContrast(BufferNumber,Value)

Equivalent to the above command, though acting on the specified BufferNumber.

TwoDimensionalContrast(Value)

Multiplies the entire contents of the main two dimensional buffer by <Value>, divides by &100 and then truncates any high bit spillage.

OneDimensionalFlood(Threshold,Multiplier,Value,Type)

If the contents of the main one dimensional buffer at any point is less than <Threshold>, this command multiplies it by <Multiplier>, divides by &100 and then combines it with <Value> using combination-type Type.

OneDFlood(BufferNumber,Threshold,Multiplier,Value,Type)

TwoDimensionalFlood(Threshold,Multiplier,Value,Type)

If the contents of the main one dimensional buffer at any point is less than <Threshold>, this command multiplies it by <Multiplier>, divides by &100 and then combines it with <Value> using combination-type Type.

OneDimensionalEqualization

This expands the range of the contents of the main one dimensional buffer until it covers the range &0000-&FFFF by using linear scaling.

OneDEqualization(BufferNumber)

Equivalent to the above command, though acting on the specified BufferNumber.

TwoDimensionalEqualization

This expands the range of the contents of the main two dimensional buffer until it covers the range &0000-&FFFF by using linear scaling.

One of the problems with this command (and the one above) emerges when textures using it are animated. Because the height of the highest mountain and the floor of the deepest valley (so to speak) determine the scaling factor to be used, this will be a different factor for different animation frames. An especially high mountain will produce a freak low gain value. This will have the effect of making the rest of the texture appear lower than it would normally be. Depending on the colouring scheme used, this can result in an unsightly flashing effect in the resultant animation. There is no simple fix for this, but truncating high mountains, and using a fixed gain contrast command are both possible.

GenerateOneDimensionalDust(Threshold,Function,Type)

Processes the contents of the main one dimensional buffer by comparing it to a bank of white noise and combining those values exceeded by some threshold () by the function (of X) Function using combination-type Type.

GenerateOneDDust(BufferNumber,Threshold,Function,Type)

Equivalent to the above command, though acting on the specified BufferNumber.

GenerateTwoDimensionalDust(Threshold,Function,Type)

Similar to above using the main two dimensional buffer.

TwoDimensionalInversion

Inverts (EORs with &FFFF) the main two dimensional buffer.

OneDimensionalInversion

Inverts (EORs with &FFFF) the main one dimensional buffer.

OneDInversion(BufferNumber)

Inverts (EORs with &FFFF) the contents of buffer BufferNumber.

TwoDimensionalClear

Clears the main two dimensional buffer with zeros.

OneDimensionalClear

Clears the main one dimensional buffer with zeros.

DefineSolidBlock(Block_Description)

This command defines a solid block of texture in three dimensions. Its single parameter, Block_Description is a function of X,Y, and Z. This function usually refers to the various buffers using the 'TwoDimensionalPoint commands.

Sculpture(Path_of_X,Path_of_Y,Path_of_Z)

This command sculpts a solid shape from the solid block of texture described in the last DefineSolidBlock command. The parameters, Path_of_X, Path_of_Y, Path_of_Z, define a mapping between X and Y and the three dimensional space. They are all functions of X and Y. The resulting shape is stored in the main two dimensional buffer.

Resize(XFactor,YFactor)

Resizes the texture by XFactor and YFactor.
Actually performs something similar to:
TwoDimensionalShift(&8000,&8000,Overwrite)
TwoDimensionalProcess(0,0,XFactor,YFactor,TwoDimensionalPoint(Combine(Multiplication,&40000 DIV XFactor,LogicalShiftRight(X,&6)),Combine(Multiplication,&40000 DIV YFactor,LogicalShiftRight(Y,&6))),Overwrite).
TwoDimensionalShift(&8000,&8000,Overwrite)

ResizeBumpMap(XFactor,YFactor)

This command is used in a similar context to the Resize(XFactor,YFactor) command. It is intended for use in resizing bump maps. The front end assumes that if the two dimensional map has been changed since the last virtual sprite was made, then this command has been inserted at the appropriate point.

If called with XFactor and YFactor both equal to &FFFF this command does no processing. Otherwise it performs the action of Resize(XFactor,YFactor).

TwoDFill(Buffer,Value)

The specified two-dimensional buffer is completely filled with the value <Value>. Memory is allocated if this has not been performed yet.

OneDFill(Buffer,Value)

The specified two-dimensional buffer is completely filled with the value <Value>. Memory is allocated if this has not been performed yet.

15 Linear segment commands

LinearField(H1,H2)

Creates a linear field over the entire domain of the main one dimensional buffer. It ranges from H1 ay X = 0 to H2 at X = &FFFF. *

LinearSegment(X1,X2,H1,H2,Type)

Creates a linear field over the domain (X = X1 to X = X2) in the main one dimensional buffer. It ranges from Y = H1 at X = X1 to Y = H2 at X = X2. *

LinearFieldSegment(H1,H2,X1,X2,Type) * deprecated *

Creates a linear field over the domain (X = &FFFF - X2 to X = &FFFF - X1) in the main one dimensional buffer. It ranges from Y = H2 at X = &FFFF - X2 to Y = H1 at X = &FFFF - X1. The behaviour of the command when X1 is greater than X2 is always produces a repeatable result, but it is beyond the scope of this document to describe its exact effect. In particular, it should not be assumed that when X1 is greater than X2 this command affects this domain.

This ridiculous specification of the syntax of this command is a legacy from Texture Garden's developmental days. It has been retained to enable old textures to be generated correctly. The command LinearSegment(X1,X2,H1,H2,Type) should now be used preferentially. *

* Note that by virtue of its nature, the previous three commands may destroy the ability of textures using it to tessellate.

Rectangle(I,X1,Y1,X2,Y2,Type)

Simply creates a rectangle in the main two dimensional buffer. Of height <I> and opposing corners at (X1,Y1) and (X2,Y2), the rectangle is combined with the existing pattern using combination-type Type.

OneDLinearField(BufferNumber,Y1,Y2)

OneDLinearSegment(BufferNumber,X1,X2,H1,H2,Type)

These commands act like those above, only they work on the specified <BufferNumber>.

16 Shifting commands

OneDimensionalShift(Delta_X,Type)

The main one dimensional buffer is shifted by <Delta_X> and combined with its original self using combination-type Type.

TwoDimensionalShift(Delta_X,Delta_Y,Type)

The main two dimensional buffer is shifted by (<Delta_X>,<Delta_Y>) and combined with its original self using combination-type Type.

OneDShift(BufferNumber,Delta_X,Type)

The specified buffer is shifted by <Delta_X> and combined with itself using combination-type Type.

17 Rotation commands

QuickRotate(Theta,Type)

The main two dimensional buffer is rotated by <Theta> rounded to the nearest multiple of 90 degrees and combined with its original self using combination-type Type. Usually <Theta> is one of Zero, Ninety, OneHundredAndEigty and TwoHundredAndSeventy.

Rotate(Theta,Type)

The main two dimensional buffer is rotated by <Theta> rounded to the nearest multiple of 1/3 of a degree and combined with its original self using combination-type Type. *

SlowRotate(Theta,Type)

Same as Rotate but with sub-pixel anti-aliasing. *

RotateAbout(Theta,X,Y,Type)

Similar to Rotate, but the rotation is about an arbitrary point. This is not equivalent to a Shift, Rotate and Shift because of the location of the introduced discontinuities. * #

SlowRotateAbout(Theta,X,Y,Type)

Same as RotateAbout but with sub-pixel anti-aliasing. *

* Note that by virtue of the nature of its action, this command may destroy the ability of the texture to tessellate unless <Theta> is one of Zero, Ninety, OneHundredAndEighty and TwoHundredAndSeventy (in which case QuickRotate should be used). The discontinuities are introduced at what would normally turn into the edges of the sprite. However, further processing may alter their location. The command is also a bit quick and dirty in that no anti-aliasing techniques are used to smooth the rotation. If multiple rotations are used then this might become a problem, though it may also produce some interesting effects as the number of iterations reaches 100.

# Note that there is no QuickRotateAbout as this can be accomplished with a QuickRotate and a TwoDimensionalShift.

18 Loops

Repeat

Sets up a Repeat-type loop.

Until(Condition)

Loops to the matching Repeat until <Condition> is non-zero.

For(Number_of_times_to_loop)

Sets up a For-type loop to be executed <Number_of_times_to_loop> times.

Next

Loops to the matching For for the specified number of times.

19 Buffer manipulation commands

OneDimensionalStoreBufferOne

Stores the contents of the main one dimensional buffer in the first one dimensional buffer, overwriting its contents.

OneDimensionalStoreBufferTwo

Stores the contents of the main one dimensional buffer in the second one dimensional buffer, overwriting its contents.

OneDimensionalSwapBufferOne

Swaps the contents of the main one dimensional buffer with the contents of the first one dimensional buffer.

OneDimensionalSwapBufferTwo

Swaps the contents of the main one dimensional buffer with the contents of the second one dimensional buffer.

OneDimensionalMaskBufferOne(Type)

Combines the contents of the main one dimensional buffer with the contents of the first one dimensional buffer using combination-type Type.

TwoDimensionalStore

Stores the contents of the main two dimensional buffer in the first two dimensional buffer, overwriting its contents. *

TwoDimensionalSwap

Swaps the contents of the main two dimensional buffer with the contents of the first two dimensional buffer. *

TwoDimensionalMask(Type)

Combines the contents of the main two dimensional buffer with the contents of the first two dimensional buffer using combination-type Type. *

OneDStoreBuffers(DestinationBuffer)

Overwrites the contents of the specified <DestinationBuffer> with the contents of the main one dimensional buffer.

OneDCombineBuffers(DestinationBuffer,SourceBuffer,Type)

Combines the contents of the two specified buffers and stores the result in the <DestinationBuffer>. Combination-type Type is used.

OneDSwapBuffers(BufferNumber,BufferNumber)

Swaps the contents of the two specified buffers.

* Note that the memory used in manipulating the first two dimensional buffer is not allocated by default, and this will cause the process of texture generation to use more memory than normal. This is only important in if textures are to be generated in low-memory environments.

20 Wavey and Distorting commands

VerticalDistortion(Type)

Applies a series of vertical displacements to the contents of the main two dimensional buffer according to the contents of the main one dimensional buffer. The maximum displacement is twice the dimensions of the main two dimensional buffer. Combination-type Type is used.

HorizontalDistortion(Type)

See VerticalDistortion which is similar.

HorizontalWaveWarp(Type)

Applies a shaped smear of waves to the contents of the main two dimensional buffer whose horizontal displacements vary according to the contents of the main one dimensional buffer. The exact form of the smear is controlled by the first one dimensional buffer. The maximum amplitude of the waves is twice the dimensions of the main two dimensional buffer. Combination-type Type is used.

VerticalWaveWarp(Type)

See HorizontalWaveWarp which is similar.

HorizontalWaves(Type)

Applies a smear of waves to the contents of the main two dimensional buffer. The exact form of the smear is controlled by the main one dimensional buffer. The maximum amplitude of the waves is twice the dimensions of the main two dimensional buffer. Combination-type Type is used.

VerticalWaves(Type)

See HorizontalWaves which is similar.

21 Colour related commands

StartColourDefinition

This should occur at the start of every palette definition. It is a marker used by the front end to manipulate texture texts.

EndColourDefinition

This should occur at the end of every palette definition. It is a marker used by the front end to manipulate texture texts.

SetColour(ColourModel,Component1,Component2,Component3)

Selects the current colour to the one specified by Component1, Component2 and Component3 as set out in the CreateColours(ColourModel) command (see below).

CreateColours(ColourModel)

This command sets up a field of 24 bit RGB values describing the palette which will be used in any MakeSprite, AddToSprite or MakeVirtualSprite commands. The ColourModel may be one of RGB, HSV or CIE. This command represents the preferred method of performing these actions.

The three one-dimensional buffers are used to supply the colour's components.

With the RGB ColourModel, buffer two contains the red component, buffer one contains the green component, and the main buffer containing the blue component.

With the HSV ColourModel, buffer two contains the red component, buffer one contains the green component, and the main buffer containing the blue component.

With the HSV ColourModel, buffer two contains the hue component, buffer one contains the saturation component, and the main buffer contains the value component.

With the HSV ColourModel, buffer two contains the X component, buffer one contains the Y component, and the main buffer contains the Z component.

CreateColoursUsingRGBData

Performs CreateColours(RGB).

CreateColoursUsingHSVData

Performs CreateColours(HSV).

CreateColoursUsingCIEData

Performs CreateColours(CIE).

22 Mirroring, Flipping and Shearing commands

Caution should be exercised when using the leading-diagonal and trailing-diagonal versions of the mirror and flip commands as they may prevent the resulting texture from tessellating correctly. The shear commands may also appear to produce different effects at different resolutions unless the absolute size of their parameter is kept quite small.

The following commands all combine the main two dimensional buffer with its mirror image in the specified axis before combining it with its original self using combination-type Type.

HorizontalMirror(Type)

VerticalMirror(Type)

LeadingDiagonalMirror(Type)

TrailingDiagonalMirror(Type)

The following Flip commands are similar to the Mirror commands. However, the result is no longer guaranteed to be symmetrical. They take fractionally longer to perform.

HorizontalFlip(Type)

VerticalFlip(Type)

LeadingDiagonalFlip(Type)

TrailingDiagonalFlip(Type)

Shears combine a sheared image of the main two dimensional buffer with its original self using combination-type Type. A shear of Size 1 produces a 45 degree shear; higher numbers may be thought of as representing the effect of repeated shears.

HorizontalShear(Size,Type)

VerticalShear(Size,Type)

23 Blobbing commands

Ripples(Intensity,Radius,X,Y,Type)

This produces a circular effect in the main two dimensional buffer. The effect is centred at (X,Y) and is of radius <Radius> and height <Intensity>. The effect is composed of concentric ripples modulated by the contents of the main one dimensional buffer.

Radials(Intensity,Radius,X,Y,Type,TypeTwo)

This produces a near-circular effect in the main two dimensional buffer. The effect is centred at (X,Y) and is of radius <Radius> and height <Intensity>. The effect is composed of distorted ripples combined with radial lines modulated by the contents of the one dimensional buffers. The main one dimensional buffer affects the distortion, the first buffer affects the ripples and the second affects the radial lines and their distribution. Type affects the way the radial lines are combined with the background and TypeTwo affects the way the ripples are combined with the resultant.

24 Branching

Define <DefinitionMarker>

Places a marker in the program and gives it a name. This name may contain any characters, but top-bit-set (> &7F), low values (< &20), ), | and , should not be used for compatibility with future versions of the program.

Goto <DefinitionMarker>

Jumps directly to the named marker.

Call <DefinitionMarker>

Calls a routine at the named marker, setting up a return address. Calls and Gotos may not currently be used as functions or as parameters to functions. They may be used as commands in conditional statements.

Return

Returns from the most recent Call.

25 Light sources and bump maps

DefineLightSource(LightModel,Theta,Phi)

Sets up a light source. This comes from angle Theta in the horizontal plane (&0 = East, &4000 = North, etc.) and elevation Phi (&0 = on the horizon, &FFFF = at the zenith).

The colour is defined as the current colour (i.e. the one last defined with a SetColour command).

If the lighting model requires it, the current Dithering will be used to define the specular constant.

The <LightModel> may currently be one of Gouraud, Phong, Specular. These may be combined e.g. Add(Specular,Phong).

If you use more than one light source, it is easy to saturate the resulting texture, which will appear to have uniform blobs of colour, usually white.

This may also happen when shining bright light souces from near the horizon. Because many bump maps will have tall narrow peaks, shining a light from the side will produce significantly more illumination than shining one from near the zenith.

To stop this from happening, either reduce the intensity of the lights being used, shine them more from overhead, use the PseudoExposure command, orreduce the bumpiness of the map being used.

Currently, only the first eight lightsources are recognised.

ShineLightOnVirtualSprite

This performs the computation using the main two dimensional buffer as the data for the bumpmap, and the previously defined light sources to specify the lighting.

The virtual sprite contains the data which is to be illuminated, and is used to store the result.

PseudoExposure(Value)

Sets the PseudoExposure variable. This controls how lightsources produce highlights. Setting Value to &1000 produces no highlights. Lower values produce dull, under-exposed images. Higher values cause the lightsources to wash out, usually to white or one of the six primaryish colours. NOTES:

1. In order to cooperate with future front ends to this feature, the DefineLightSource commands, the SetColour commands and the ShineLightOnVirtualSprite command should all be grouped together in one section, with a ResizeBumpMap at the beginning if appropriate.

2. The ResizeBumpMap should always be used if the bumpmap is altered since the last MakeVirtualSprite command. It should be placed after the bumpmap has been created, and directly before any SetColour commands. If this is not done, the texture may not resize properly.


tim@tt1.org | http://texturegarden.com/