static ERROR CMDInit(OBJECTPTR, struct KernelBase *);
static ERROR CMDExpunge(void);
#define VER_ROTOPLEX 1.0
EXPORT struct ModHeader ModHeader = {
MODULE_HEADER_V1, CMDInit, NULL, NULL, CMDExpunge,
JMP_DEFAULT, 0, CPU_PC, VER_ROTOPLEX, VER_KERNEL,
"Rocklyte Systems",
"Rocklyte Systems (c) 1999-2003. All rights reserved.",
"April 2003",
"Rotoplex"
};
static struct KernelBase *KernelBase = 0;
static OBJECTPTR RotoplexClass = 0;
static FLOAT *sine = 0; /* Pointer to our sine table */
static FLOAT *cosine = 0; /* Pointer to our cosine table */
/*****************************************************************************
** Rotoplex class.
*/
#define DEFAULT_WIDTH 100
#define DEFAULT_HEIGHT 100
#define DEFAULT_DOTS 1000
#define MORPH_SPEED 100 /* Low values give fast morphing, higher values slower */
struct Rotoplex {
OBJECT_HEADER /* */
OBJECTID DrawableID; /* The graphical object that contains the Rotoplex graphic */
LONG MaxElements; /* Total amount of dots in use */
FLOAT AngleX; /* X angle */
FLOAT AngleY; /* Y angle */
FLOAT AngleZ; /* Z angle */
FLOAT XSpeed; /* X Speed */
FLOAT YSpeed; /* Y Speed */
FLOAT ZSpeed; /* Z Speed */
LONG Shape; /* Predefined object number */
FLOAT XCoord; /* Current X coordinate of the object */
FLOAT YCoord; /* Current Y coordinate of the object */
FLOAT ZCoord; /* Current Z coordinate of the object */
OBJECTID DScroll; /* Depth scrollbar */
OBJECTID HScroll; /* Horizontal scrollbar */
OBJECTID VScroll; /* Vertical scrollbar */
/*** Field store ***/
struct DotPixel *Object; /* Pointer to 3D dot structure */
struct DotPixel *Morph;
MEMORYID ObjectMID;
OBJECTID TimerID; /* Timer Event */
LONG Morphing;
LONG MorphPos;
FLOAT fAngleX;
FLOAT fAngleY;
FLOAT fAngleZ;
FLOAT HPosition;
FLOAT VPosition;
LONG MorphCount;
};
struct DotPixel {
FLOAT X,Y,Z;
};
/*** Rotoplex->Shape ***/
#define RP_ENNEPER 1
#define RP_HELIX 2
#define RP_SPHERE 3
#define RP_CUBE 4
#define RP_START 1
#define RP_END 5
static struct FieldDef RotoplexShape[] = {
{ "ENNEPER", RP_ENNEPER }, { "HELIX", RP_HELIX },
{ "SPHERE", RP_SPHERE }, { "CUBE", RP_CUBE },
{ NULL, NULL }
};
/*****************************************************************************
** These structures are only used when a program sets the Object field.
*/
struct DotObject {
LONG TotalPixels;
struct UserPixels *Pixels;
};
struct UserPixels {
FLOAT X, Y, Z;
};
static ERROR SET_DScroll(struct Rotoplex *, OBJECTID *);
static ERROR SET_HScroll(struct Rotoplex *, OBJECTID *);
static ERROR SET_Object(struct Rotoplex *, struct DotObject *);
static ERROR SET_VScroll(struct Rotoplex *, OBJECTID *);
static struct FieldArray RotoplexFields[] = {
{ "Drawable", 0, FDF_OBJECTID|FDF_RW, 0, NULL, NULL },
{ "MaxElements", 0, FDF_LONG|FDF_RI, 0, NULL, NULL },
{ "AngleX", 0, FDF_FLOAT|FDF_R, 0, NULL, NULL },
{ "AngleY", 0, FDF_FLOAT|FDF_R, 0, NULL, NULL },
{ "AngleZ", 0, FDF_FLOAT|FDF_R, 0, NULL, NULL },
{ "XSpeed", 0, FDF_FLOAT|FDF_RW, 0, NULL, NULL },
{ "YSpeed", 0, FDF_FLOAT|FDF_RW, 0, NULL, NULL },
{ "ZSpeed", 0, FDF_FLOAT|FDF_RW, 0, NULL, NULL },
{ "Shape", 0, FDF_LONG|FDF_LOOKUP|FDF_RI,(LONG)&RotoplexShape, NULL, NULL },
{ "XCoord", 0, FDF_FLOAT|FDF_RW, 0, NULL, NULL },
{ "YCoord", 0, FDF_FLOAT|FDF_RW, 0, NULL, NULL },
{ "ZCoord", 0, FDF_FLOAT|FDF_RW, 0, NULL, NULL },
{ "DScroll", 0, FDF_OBJECTID|FDF_RW, 0, NULL, SET_DScroll },
{ "HScroll", 0, FDF_OBJECTID|FDF_RW, 0, NULL, SET_HScroll },
{ "VScroll", 0, FDF_OBJECTID|FDF_RW, 0, NULL, SET_VScroll },
/*** Virtual fields ***/
{ "Object", 0, FDF_POINTER|FDF_RI, 0, NULL, SET_Object },
END_FIELD
};
static ERROR ROTOPLEX_ActionNotify(struct Rotoplex *, struct acActionNotify *);
static ERROR ROTOPLEX_Free(struct Rotoplex *, APTR);
static ERROR ROTOPLEX_ReleaseObject(struct Rotoplex *, APTR);
static ERROR ROTOPLEX_Init(struct Rotoplex *, APTR);
static ERROR ROTOPLEX_Move(struct Rotoplex *, struct acMove *);
static ERROR ROTOPLEX_NewOwner(struct Rotoplex *, struct acNewOwner *);
static ERROR ROTOPLEX_NewObject(struct Rotoplex *, APTR);
static ERROR ROTOPLEX_ScrollToPoint(struct Rotoplex *, struct acScrollToPoint *);
static ERROR ROTOPLEX_Timer(struct Rotoplex *, struct acTimer *);
static struct ActionArray RotoplexActions[] = {
{ AC_ActionNotify, ROTOPLEX_ActionNotify },
{ AC_Free, ROTOPLEX_Free },
{ AC_ReleaseObject, ROTOPLEX_ReleaseObject },
{ AC_Init, ROTOPLEX_Init },
{ AC_Move, ROTOPLEX_Move },
{ AC_NewOwner, ROTOPLEX_NewOwner },
{ AC_NewObject, ROTOPLEX_NewObject },
{ AC_ScrollToPoint, ROTOPLEX_ScrollToPoint },
{ AC_Timer, ROTOPLEX_Timer },
{ NULL, NULL }
};
static void CalculateObject(struct Rotoplex *, struct DotPixel *, LONG, LONG, LONG, LONG);
/*****************************************************************************
** Command: Init()
*/
ERROR CMDInit(OBJECTPTR argModule, struct KernelBase *argKernelBase)
{
LONG i;
FLOAT angle;
KernelBase = argKernelBase;
if (AllocMemory(sizeof(FLOAT) * 360, MEM_DATA|MEM_NOCLEAR, (APTR *)&sine, NULL) IS ERR_Okay) {
if (AllocMemory(sizeof(FLOAT) * 360, MEM_DATA|MEM_NOCLEAR, (APTR *)&cosine, NULL) IS ERR_Okay) {
for (i=0; i < 360; i++) {
angle = (i * 3.141592654) / 180.0;
cosine[i] = cos(angle);
sine[i] = sin(angle);
}
}
else return(ObjectError(argModule, ERH_InitModule, ERR_Memory));
}
else return(ObjectError(argModule, ERH_InitModule, ERR_Memory));
return(CreateObject(ID_CLASS, NULL, (OBJECTPTR *)&RotoplexClass, NULL,
FID_BaseClassID|TLONG, ID_ROTOPLEX,
FID_Version|TFLOAT, VER_ROTOPLEX,
FID_Name|TSTRING, "Rotoplex",
FID_Category|TLONG, CCF_EFFECT,
FID_Actions|TPTR, RotoplexActions,
FID_Fields|TPTR, RotoplexFields,
FID_Size|TLONG, sizeof(struct Rotoplex),
TAGEND));
}
/*****************************************************************************
** Command: Expunge()
*/
ERROR CMDExpunge(void)
{
if (sine) { FreeMemory(sine); sine = NULL; }
if (cosine) { FreeMemory(cosine); cosine = NULL; }
if (RotoplexClass) { Action(AC_Free, RotoplexClass, NULL); RotoplexClass = NULL; }
return(ERR_Okay);
}
/*****************************************************************************
** Rotoplex: ActionNotify
*/
static ERROR ROTOPLEX_ActionNotify(struct Rotoplex *Self, struct acActionNotify *NotifyArgs)
{
if ((NotifyArgs->ActionID IS AC_Draw) AND (NotifyArgs->Error IS ERR_Okay)) {
struct mtDrawRGBPixel pixel;
struct Bitmap *bitmap;
OBJECTPTR drawable;
FLOAT temp, x2, y2, z2;
LONG width, height, offsetx, offsety, colour, i;
LONG (*QDrawRGBPixel)(struct Bitmap *, struct mtDrawRGBPixel *);
if (!Self->Object) {
if (AccessMemory(Self->ObjectMID, MEM_READWRITE, (void **)&Self->Object) != ERR_Okay) {
return(ObjectError(Self, ERH_ActionNotify, ERR_AccessMemory));
}
Self->Morph = Self->Object + Self->MaxElements;
}
if (AccessObject(Self->DrawableID, 0, &drawable) != ERR_Okay) {
return(ObjectError(Self, ERH_Draw, ERR_ExclusiveDenied));
}
if ((GetField(drawable, FID_Bitmap, FT_POINTER, &bitmap) != ERR_Okay) OR (!bitmap)) {
ReleaseObject(drawable);
return(ObjectError(Self, ERH_Draw, ERR_FieldNotSet));
}
if (ObtainMethod(bitmap, MT_DrawRGBPixel, (void **)&QDrawRGBPixel) != ERR_Okay) return(ERR_Failed);
GetFields(drawable, FID_Width|TLONG, &width,
FID_Height|TLONG, &height,
TAGEND);
offsetx = Self->XCoord + (width / 2);
offsety = Self->YCoord + (height / 2);
if (Self->ZCoord < 0) Self->ZCoord = 0.0;
if (Self->ZCoord > 100) Self->ZCoord = 100.0;
for (i = 0; i < Self->MaxElements; i++) {
x2 = Self->Object[i].X;
y2 = Self->Object[i].Y;
z2 = Self->Object[i].Z;
/* Rotate the X axis */
temp = z2;
z2 = z2 * cosine[(WORD)Self->fAngleX] - y2 * sine[(WORD)Self->fAngleX];
y2 = y2 * cosine[(WORD)Self->fAngleX] + temp * sine[(WORD)Self->fAngleX];
/* Rotate the Y axis */
temp = z2;
z2 = z2 * cosine[(WORD)Self->fAngleY] - x2 * sine[(WORD)Self->fAngleY];
x2 = x2 * cosine[(WORD)Self->fAngleY] + temp * sine[(WORD)Self->fAngleY];
/* Calculate colour based on new Z position (-1 < Z < 1) */
colour = (((z2 + 1) / 1) * 255) / 2;
if (colour < 0) colour = 0;
if (colour > 255) colour = 255;
pixel.Red = colour;
pixel.Green = colour;
pixel.Blue = colour;
/* Finally scale the (x,y) coordinates to enlarge or shrink the object */
pixel.XCoord = (LONG)(x2 * (Self->ZCoord)) + offsetx;
pixel.YCoord = (LONG)(y2 * (Self->ZCoord)) + offsety;
QDrawRGBPixel(bitmap, &pixel);
}
ReleaseObject(drawable);
}
return(ERR_Okay);
}
/*****************************************************************************
** Rotoplex: Free
*/
static ERROR ROTOPLEX_Free(struct Rotoplex *Self, APTR Void)
{
OBJECTPTR drawable;
UnsubscribeTimer(Self->Head.UniqueID);
/*** Remove all subscriptions to our drawable container ***/
if (Self->DrawableID) {
if (AccessObject(Self->DrawableID, 5000, &drawable) IS ERR_Okay) {
UnsubscribeAction(drawable, NULL, GetUniqueID(Self));
ReleaseObject(drawable);
}
}
if (Self->Object) {
ReleaseMemory(Self->Object);
Self->Object = NULL;
Self->Morph = NULL;
}
if (Self->ObjectMID) { FreeMemoryID(Self->ObjectMID); Self->ObjectMID = NULL; }
return(ERR_Okay);
}
/*****************************************************************************
** Rotoplex: Init
*/
static ERROR ROTOPLEX_Init(struct Rotoplex *Self, APTR Void)
{
OBJECTPTR drawable;
WORD nextshape;
if (!Self->DrawableID) return(ObjectError(Self, ERH_Init, ERR_UnsupportedOwner));
if (!Self->MaxElements) Self->MaxElements = DEFAULT_DOTS;
if (!Self->Shape) Self->Shape = RandomNumber(RP_END-1)+1;;
/*** Calculate the 3D object ***/
if (!Self->Object) {
if (AllocMemory(sizeof(struct DotPixel) * Self->MaxElements * 2, Self->Head.MemFlags,
(APTR *)&Self->Object, &Self->ObjectMID) IS ERR_Okay) {
Self->Morph = Self->Object + Self->MaxElements;
CalculateObject(Self, Self->Object, Self->Shape, 0, Self->MaxElements, FALSE);
}
else return(ObjectError(Self, ERH_Init, ERR_AllocMemory));
}
nextshape = Self->Shape;
while (nextshape IS Self->Shape) nextshape = RandomNumber(RP_END-1)+1;
Self->Shape = nextshape;
Self->fAngleX = Self->AngleX;
Self->fAngleY = Self->AngleY;
Self->fAngleZ = Self->AngleZ;
Self->Morphing = FALSE;
Self->MorphPos = 0;
/*** Subscribe to our container's Resize action ***/
if (AccessObject(Self->DrawableID, 5000, &drawable) IS ERR_Okay) {
SubscribeAction(drawable, AC_Draw, Self);
ReleaseObject(drawable);
}
/*** Initialise the Timer ***/
return(SubscribeTimer(Self->Head.UniqueID, 20));
}
/*****************************************************************************
** Rotoplex: Move
*/
static ERROR ROTOPLEX_Move(struct Rotoplex *Self, struct acMove *Args)
{
if (Args) {
Self->XCoord += Args->XChange;
Self->YCoord += Args->YChange;
Self->ZCoord += Args->ZChange;
return(ERR_Okay);
}
else return(ERR_Args);
}
/*****************************************************************************
** Rotoplex: NewObject
*/
static ERROR ROTOPLEX_NewObject(struct Rotoplex *Self, APTR Args)
{
Self->ZCoord = 50.0;
Self->MaxElements = DEFAULT_DOTS;
Self->Shape = RandomNumber(RP_END-1)+1;;
Self->XSpeed = 4.0;
Self->YSpeed = 4.0;
Self->ZSpeed = 4.0;
return(ERR_Okay);
}
/*****************************************************************************
** Rotoplex: NewOwner
*/
static ERROR ROTOPLEX_NewOwner(struct Rotoplex *Self, struct acNewOwner *Args)
{
OBJECTID owner;
if (!(Self->Head.ObjectFlags & NF_INITIALISED)) {
owner = Args->NewOwnerID;
while ((owner) AND (GetClassID(owner) != ID_RENDER)) {
owner = GetOwnerID(owner);
}
if (owner) Self->DrawableID = owner;
}
return(ERR_Okay);
}
/*****************************************************************************
** Rotoplex: ReleaseObject
*/
static ERROR ROTOPLEX_ReleaseObject(struct Rotoplex *Self, APTR Void)
{
if (Self->Object) {
ReleaseMemory(Self->Object);
Self->Object = NULL;
Self->Morph = NULL;
}
return(ERR_Okay);
}
/*****************************************************************************
** Action: Scroll
*/
static ERROR ROTOPLEX_ScrollToPoint(struct Rotoplex *Self, struct acScrollToPoint *Args)
{
if (Args->Flags & STF_X) Self->XCoord = Args->XCoord;
if (Args->Flags & STF_Y) Self->YCoord = Args->YCoord;
if (Args->Flags & STF_Z) Self->ZCoord = Args->ZCoord;
if (Self->ZCoord < 0) Self->ZCoord = 0.0;
else if (Self->ZCoord > 100) Self->ZCoord = 100.0;
return(ERR_Okay);
}
/*****************************************************************************
** Action: Timer
*/
static ERROR ROTOPLEX_Timer(struct Rotoplex *Self, struct acTimer *Args)
{
LONG CalcPixels, newshape, i;
/*** Gain access to the 3D object definition if we have not already ***/
if (!Self->Object) {
if (AccessMemory(Self->ObjectMID, MEM_READWRITE, (void **)&Self->Object) != ERR_Okay) {
return(ObjectError(Self, ERH_ActionNotify, ERR_AccessMemory));
}
Self->Morph = Self->Object + Self->MaxElements;
}
/* Calculate the next object that we are going to morph into. Note that
** we calculate the next object in stages rather than all-at-once, as
** it will suck up too much processor time otherwise.
*/
if (Self->Morphing IS FALSE) {
CalcPixels = Self->MaxElements/100;
CalculateObject(Self, Self->Morph, Self->Shape, Self->MorphPos, Self->MorphPos + CalcPixels, TRUE);
Self->MorphPos += CalcPixels;
if (Self->MorphPos >= Self->MaxElements) {
/*** If we have calculated all the elements, reset the morphing process ***/
Self->Morphing = TRUE;
Self->MorphPos = 0;
Self->MorphCount = 0;
newshape = Self->Shape;
while (newshape IS Self->Shape) newshape = RandomNumber(RP_END-1)+1;
Self->Shape = newshape;
}
}
/*** Spin the object on each angle ***/
Self->fAngleX += Self->XSpeed;
if (Self->fAngleX >= 360) Self->fAngleX = 0.0;
Self->fAngleY += Self->YSpeed;
if (Self->fAngleY >= 360) Self->fAngleY = 0.0;
Self->fAngleZ += Self->ZSpeed;
if (Self->fAngleZ >= 360) Self->fAngleZ = 0.0;
/*** Morph the object into the next shape ***/
if (Self->Morphing IS TRUE) {
for (i = 0; i < Self->MaxElements; i++) {
Self->Object[i].X += Self->Morph[i].X;
Self->Object[i].Y += Self->Morph[i].Y;
Self->Object[i].Z += Self->Morph[i].Z;
}
Self->MorphCount++;
if (Self->MorphCount >= MORPH_SPEED) Self->Morphing = FALSE;
}
DelayMsg(AC_Draw, Self->DrawableID, NULL);
return(ERR_Okay);
}
/*****************************************************************************
** Function: CalculateObject()
**
** The purpose of this function is to calculate the 3D coordinates of our
** objects. The coordinates must be from -1.0 to +1.0, so some scaling takes
** place for some of the calculated objects.
*/
void CalculateObject(struct Rotoplex *Self, struct DotPixel *Dots, LONG Shape, LONG Start, LONG End, LONG Distance)
{
FLOAT u, v, max;
LONG i, longtitude, latitude;
#define PI 3.141592654
if (End > Self->MaxElements) End = Self->MaxElements;
if (Shape IS RP_HELIX) {
for (i=Start; i < End; i++) {
u = ((FLOAT)RandomNumber(12566 - 1) + 1) / 1000; /* 0 < u < (4 * PI) */
v = ((FLOAT)RandomNumber(6283 - 1) + 1) / 1000; /* 0 < v < (2 * PI) */
Dots[i].X = cos(u) * (2 + cos(v));
Dots[i].Y = sin(u) * (2 + cos(v));
Dots[i].Z = (u - (2 * PI)) + sin(v);
}
/*** Scale down to values of -1.0 to +1.0 ***/
max = ((2 * PI) + sin(2*PI));
for (i=Start; i < End; i++) {
Dots[i].X = Dots[i].X / max;
Dots[i].Y = Dots[i].Y / max;
Dots[i].Z = Dots[i].Z / max;
}
}
else if (Shape IS RP_ENNEPER) {
for (i=Start; i < End; i++) {
u = ((FLOAT)RandomNumber(28000 - 1) + 1) / 10000 - 1.4; /* -1.4 < u < 1.4 */
v = ((FLOAT)RandomNumber(28000 - 1) + 1) / 10000 - 1.4; /* -1.4 < v < 1.4 */
Dots[i].X = u * (1 + (v * v)) - (u * u * u);
Dots[i].Y = v * (1 + (u * u)) - (v * v * v);
Dots[i].Z = (u * u) - (v * v);
}
/*** Scale down to values of -1.0 to +1.0 ***/
for (i=Start; i < End; i++) {
Dots[i].X = Dots[i].X / 1.96;
Dots[i].Y = Dots[i].Y / 1.96;
Dots[i].Z = Dots[i].Z / 1.96;
}
}
else if (Shape IS RP_CUBE) {
for (i=Start; i < End; i++) {
#define CUBE_MAX 200000
Dots[i].X = 0.0; Dots[i].Y = 0.0; Dots[i].Z = 0.0;
while ((Dots[i].X < 0.95) AND (Dots[i].X > -0.95) AND
(Dots[i].Y < 0.95) AND (Dots[i].Y > -0.95) AND
(Dots[i].Z < 0.95) AND (Dots[i].Z > -0.95)) {
Dots[i].X = ((FLOAT)(RandomNumber(20000)-10000)) * 0.0001;
Dots[i].Y = ((FLOAT)(RandomNumber(20000)-10000)) * 0.0001;
Dots[i].Z = ((FLOAT)(RandomNumber(20000)-10000)) * 0.0001;
}
}
}
else if (Shape IS RP_SPHERE) {
for (i=Start; i < End; i++) {
longtitude = RandomNumber(180);
latitude = RandomNumber(10);
Dots[i].X = sin(longtitude) * cos(latitude);
Dots[i].Y = sin(longtitude) * sin(latitude);
Dots[i].Z = cos(longtitude);
}
}
/*** Convert coordinates to morphing distance values if requested ***/
if (Distance IS TRUE) {
for (i=Start; i < End; i++) {
Dots[i].X = ((Dots[i].X - Self->Object[i].X) / MORPH_SPEED);
Dots[i].Y = ((Dots[i].Y - Self->Object[i].Y) / MORPH_SPEED);
Dots[i].Z = ((Dots[i].Z - Self->Object[i].Z) / MORPH_SPEED);
}
}
}
/*****************************************************************************
###FIELD###
Name: AngleX
Short: Defines the angle of rotation on the X axis.
Type: FLOAT
Status: Read/Init
###DESCRIPTION###
The AngleX field defines the current rotation of the 3D object on the X
axis. When the field is set to 0, the object will appear in its default
non-rotated state on the X axis.
###SEE ALSO###
Fields: AngleY, AngleZ, XSpeed, YSpeed, ZSpeed
###END###
*****************************************************************************/
/*****************************************************************************
###FIELD###
Name: AngleY
Short: Defines the angle of rotation on the Y axis.
Type: FLOAT
Status: Read/Init
###DESCRIPTION###
The AngleY field defines the current rotation of the 3D object on the Y
axis. When the field is set to 0, the object will appear in its default
non-rotated state on the Y axis.
###SEE ALSO###
Fields: AngleX, AngleZ, XSpeed, YSpeed, ZSpeed
###END###
*****************************************************************************/
/*****************************************************************************
###FIELD###
Name: AngleZ
Short: Defines the angle of rotation on the Z axis.
Type: FLOAT
Status: Read/Init
###DESCRIPTION###
The AngleZ field defines the current rotation of the 3D object on the Z
axis. When the field is set to 0, the object will appear in its default
non-rotated state on the Z axis.
###SEE ALSO###
Fields: AngleX, AngleY, XSpeed, YSpeed, ZSpeed
###END###
*****************************************************************************/
/*****************************************************************************
###FIELD###
Name: DScroll
Short: Reference to a scrollbar for depth based movement.
Type: OBJECTID
Status: Read/Write
###DESCRIPTION###
If you want to attach a positioning scrollbar to the Z coordinate of a
Rotoplex object, set this field to an object belonging to the Scroll class.
So long as the scroll object is set up to provide full scrollbar
functionality, the user will be able to manipulate the position of the Rotoplex
object along the Z axis.
###SEE ALSO###
Class: Scroll
Fields: HScroll, VScroll
###END###
*****************************************************************************/
static ERROR SET_DScroll(struct Rotoplex *Self, OBJECTID *ScrollID)
{
struct mtUpdateScroll scroll;
OBJECTPTR scrollbar;
if (GetClassID(*ScrollID) IS ID_SCROLL) {
if (AccessObject(*ScrollID, 5000, &scrollbar) IS ERR_Okay) {
SetFields(scrollbar, FID_Object|TLONG, Self->Head.UniqueID,
FID_Axis|TLONG, AXIS_Z,
TAGEND);
Self->DScroll = *ScrollID;
scroll.ViewSize = 20;
scroll.PageSize = 100;
scroll.Position = Self->ZCoord;
scroll.Unit = 0;
Action(MT_UpdateScroll, scrollbar, &scroll);
ReleaseObject(scrollbar);
return(ERR_Okay);
}
else return(ObjectError(Self, ERH_SetField, ERR_AccessObject));
}
else {
DPrintF("@SetField:","[Rotoplex] Attempt to set the DScroll field with an invalid object.");
return(ERR_Failed);
}
}
/*****************************************************************************
###FIELD###
Name: HScroll
Short: Reference to a scrollbar for horizontal movement.
Type: OBJECTID
Status: Read/Write
###DESCRIPTION###
If you want to attach a positioning scrollbar to the X coordinate of a
Rotoplex object, set this field to an object belonging to the Scroll class.
So long as the Scroll object is set up to provide full scrollbar
functionality, the user will be able to manipulate the position of the Rotoplex
object along the horizontal axis.
###SEE ALSO###
Class: Scroll
Fields: DScroll, VScroll
###END###
*****************************************************************************/
static ERROR SET_HScroll(struct Rotoplex *Self, OBJECTID *Scroll)
{
struct mtUpdateScroll scroll;
OBJECTPTR scrollbar;
if (GetClassID(*Scroll) IS ID_SCROLL) {
if (AccessObject(*Scroll, 5000, &scrollbar) IS ERR_Okay) {
SetField(scrollbar, FID_Object, FT_LONG, &Self->Head.UniqueID);
ReleaseObject(scrollbar);
Self->HScroll = *Scroll;
Self->HPosition = 500.0;
scroll.PageSize = 1000;
scroll.Position = Self->HPosition;
scroll.ViewSize = 100;
scroll.Unit = 0;
return(ActionMsg(MT_UpdateScroll, Self->HScroll, &scroll));
}
else return(ObjectError(Self, ERH_SetField, ERR_ExclusiveDenied));
}
else {
DPrintF("@SetField:","[Rotoplex] Attempt to set the HScroll field with an invalid object.");
return(ERR_Failed);
}
}
/*****************************************************************************
###FIELD###
Name: MaxElements
Short: The total number of dots used to make up the object.
Type: LONG
Status: Read/Init
###DESCRIPTION###
You can set the total amount of dots that the Rotoplex object should
use by setting this field. If you do not set this field, the Rotoplex
object will set a default value here before creating the 3D pixel
array.
###END###
*****************************************************************************/
/*****************************************************************************
###FIELD###
Name: Object
Short: Allows custom objects to be created.
Type: struct DotObject *
Status: Read/Init
###DESCRIPTION###
This field points to an array of 3D dots that specify the makeup of the
rotating object. Writing to the field is useful if you wish to create your
own 3-Dimensional object rather than use a pre-generated one.
The DotObject structure is defined as follows:
struct Pixels {
LONG TotalPixels;
struct DotPixel *Pixels;
};
Keep in mind that you will also need to set the TotalDots field so that
the Rotoplex class knows how large the array is.
###END###
*****************************************************************************/
static ERROR SET_Object(struct Rotoplex *Self, struct DotObject *Pixels)
{
LONG i;
if (!Pixels) return(ERR_Failed);
if (Pixels->TotalPixels < 1) return(ERR_BadData);
if (Self->ObjectMID) {
if (Self->Object) {
ReleaseMemory(Self->Object);
Self->Object = NULL;
}
FreeMemoryID(Self->ObjectMID);
Self->ObjectMID = NULL;
}
if (AllocMemory(sizeof(struct DotPixel) * Pixels->TotalPixels * 2, Self->Head.MemFlags, (APTR *)&Self->Object, &Self->ObjectMID) IS ERR_Okay) {
Self->Morph = Self->Object + Pixels->TotalPixels;
Self->MaxElements = Pixels->TotalPixels;
for (i=0; i < Pixels->TotalPixels; i++) {
Self->Object[i].X = Pixels->Pixels[i].X;
Self->Object[i].Y = Pixels->Pixels[i].Y;
Self->Object[i].Z = Pixels->Pixels[i].Z;
}
return(ERR_Okay);
}
else return(ObjectError(Self, ERH_Init, ERR_Memory));
}
/*****************************************************************************
###FIELD###
Name: Shape
Short: A pre-calculated object can be selected by writing to this field.
Type: LONG
Status: Read/Init
###DESCRIPTION###
You can define what 3D object you want the Rotoplex to use by selecting
one of the available types as listed here:
RP_ENNEPER
RP_HELIX
RP_SPHERE
RP_CUBE
If the field is not set, an object type will be randomly chosen from
the available list on initialisation.
###END###
*****************************************************************************/
/*****************************************************************************
###FIELD###
Names: XSpeed
Short: Determines the speed of rotation along the X axis.
Type: FLOAT
Status: Read/Init
###DESCRIPTION###
You can set the speed of the object's rotation along the X axis by
writing to this field. The acceptable range for the value is between
-1000 and +1000, but typically you will get the best results by using
values between -5 and +5.
###END###
*****************************************************************************/
/*****************************************************************************
###FIELD###
Names: YSpeed
Short: Determines the speed of rotation along the Y axis.
Type: FLOAT
Status: Read/Init
###DESCRIPTION###
You can set the speed of the object's rotation along the Y axis by
writing to this field. The acceptable range for the value is between
-1000 and +1000, but typically you will get the best results by using
values between -5 and +5.
###END###
*****************************************************************************/
/*****************************************************************************
###FIELD###
Names: ZSpeed
Short: Determines the speed of rotation along the Z axis.
Type: FLOAT
Status: Read/Init
###DESCRIPTION###
You can set the speed of the object's rotation along the Z axis by
writing to this field. The acceptable range for the value is between
-1000 and +1000, but typically you will get the best results by using
values between -5 and +5.
###END###
*****************************************************************************/
/*****************************************************************************
###FIELD###
Name: VScroll
Short: Reference to a scrollbar for vertical movement.
Type: OBJECTID
Status: Read/Write
###DESCRIPTION###
If you want to attach a positioning scrollbar to the Y coordinate of a
Rotoplex object, set this field to an object belonging to the Scroll class.
So long as the Scroll object is set up to provide full scrollbar
functionality, the user will be able to manipulate the position of the Rotoplex
object along the vertical axis.
###END###
*****************************************************************************/
static ERROR SET_VScroll(struct Rotoplex *Self, OBJECTID *Scroll)
{
struct mtUpdateScroll scroll;
OBJECTPTR scrollbar;
if (GetClassID(*Scroll) IS ID_SCROLL) {
if (AccessObject(*Scroll, 5000, &scrollbar) IS ERR_Okay) {
SetField(scrollbar, FID_Object, FT_LONG, &Self->Head.UniqueID);
ReleaseObject(scrollbar);
Self->VScroll = *Scroll;
Self->VPosition = 500.0;
scroll.PageSize = 1000;
scroll.Position = Self->VPosition;
scroll.ViewSize = 100;
scroll.Unit = 0;
return(ActionMsg(MT_UpdateScroll, Self->VScroll, &scroll));
}
else return(ObjectError(Self, ERH_SetField, ERR_ExclusiveDenied));
}
else {
DPrintF("@SetField:","[Rotoplex] Attempt to set the VScroll field with an invalid object.");
return(ERR_Failed);
}
}
/*****************************************************************************
###FIELD###
Name: XCoord
Short: Defines the horizontal coordinate of the Rotoplex object.
Type: FLOAT
Status: Read/Write
###DESCRIPTION###
The horizontal position of the Rotoplex object can be altered by writing to
this field. The new position will be reflected on the next drawing cycle,
which will typically be within the next 1/25th of a second.
###END###
*****************************************************************************/
/*****************************************************************************
###FIELD###
Name: YCoord
Short: Defines the vertical coordinate of the Rotoplex object.
Type: FLOAT
Status: Read/Write
###DESCRIPTION###
The vertical position of the Rotoplex object can be altered by writing to
this field. The new position will be reflected on the next drawing cycle,
which will typically be within the next 1/25th of a second.
###END###
*****************************************************************************/
/*****************************************************************************
###FIELD###
Name: ZCoord
Short: Defines the depth-based coordinate of the Rotoplex object.
Type: FLOAT
Status: Read/Write
###DESCRIPTION###
The Z coordinate of the Rotoplex object can be altered by writing to this
field. The range is limited between -100 and +100. 0 is considered to be the
center of the Z plane, while -100 is the furthest possible point away from
the field of view. A value of +100 is the closest possible point to the
beginning of the field of view.
###END###
*****************************************************************************/