static ERROR CMDInit(OBJECTPTR, struct KernelBase *);
static ERROR CMDExpunge(void);
#define VER_FOREST 1.0
EXPORT struct ModHeader ModHeader = {
MODULE_HEADER_V1, CMDInit, NULL, NULL, CMDExpunge,
JMP_DEFAULT, 0, CPU_PC, VER_FOREST, VER_KERNEL,
"Rocklyte Systems, Guillaume Ramey, Pascal Pensa",
"All rights reserved.",
"July 2001",
"Forest"
};
static struct KernelBase *KernelBase = NULL;
static struct RenderBase *RenderBase = NULL;
static OBJECTPTR RenderModule = NULL;
static OBJECTPTR ForestClass = NULL;
/****************************************************************************/
#define SINF(n) ((float)sin(n))
#define COSF(n) ((float)cos(n))
#define FABSF(n) ((float)fabs((double)(n)))
/*** Degree to radian ***/
#define DEGTORAD(x) (((float)(x)) * 3.1416 / 180.0)
#define RANGE_RAND(min,max) ((min) + RandomNumber((max) - (min)))
/*****************************************************************************
** Forest class definition.
*/
struct Forest {
OBJECT_HEADER
LONG MaxElements; /* Total number of trees */
LONG MinHeight; /* Tree minimum height */
LONG MaxHeight; /* Tree maximum height */
LONG MinAngle; /* Angle */
LONG MaxAngle; /* Angle */
LONG Conformity; /* Max random angle from default */
LONG IterationLevel; /* Tree iteration */
/*** Storage area ***/
OBJECTID DrawableID;
OBJECTID TimerID;
struct Bitmap *Bitmap;
WORD BitmapWidth;
WORD BitmapHeight;
LONG TreeCount;
LONG Time;
};
static ERROR SET_Conformity(struct Forest *, LONG *);
static ERROR SET_IterationLevel(struct Forest *, LONG *);
static ERROR SET_MaxAngle(struct Forest *, LONG *);
static ERROR SET_MaxElements(struct Forest *, LONG *);
static ERROR SET_MaxHeight(struct Forest *, LONG *);
static ERROR SET_MinAngle(struct Forest *, LONG *);
static ERROR SET_MinHeight(struct Forest *, LONG *);
static struct FieldArray ForestFields[] = {
{ "MaxElements", 0, FDF_LONG|FDF_RW, 0, 0, SET_MaxElements },
{ "MinHeight", 0, FDF_LONG|FDF_RW, 0, 0, SET_MinHeight },
{ "MaxHeight", 0, FDF_LONG|FDF_RW, 0, 0, SET_MaxHeight },
{ "MinAngle", 0, FDF_LONG|FDF_RW, 0, 0, SET_MinAngle },
{ "MaxAngle", 0, FDF_LONG|FDF_RW, 0, 0, SET_MaxAngle },
{ "Conformity", 0, FDF_LONG|FDF_RW, 0, 0, SET_Conformity },
{ "IterationLevel", 0, FDF_LONG|FDF_RW, 0, 0, SET_IterationLevel },
END_FIELD
};
static ERROR FOREST_ActionNotify(struct Forest *, struct acActionNotify *);
static ERROR FOREST_Free(struct Forest *, APTR);
static ERROR FOREST_Init(struct Forest *, APTR);
static ERROR FOREST_NewObject(struct Forest *, APTR);
static ERROR FOREST_Timer(struct Forest *, struct acTimer *);
static struct ActionArray ForestActions[] = {
{ AC_ActionNotify, FOREST_ActionNotify },
{ AC_Free, FOREST_Free },
{ AC_Init, FOREST_Init },
{ AC_NewObject, FOREST_NewObject },
{ AC_Timer, FOREST_Timer },
{ NULL, NULL }
};
static void DrawTree(struct Forest *, struct Bitmap *, WORD, WORD,
LONG, FLOAT, FLOAT, struct RGB, LONG);
/*****************************************************************************
** Command: Init()
*/
static ERROR CMDInit(OBJECTPTR argModule, struct KernelBase *argKernelBase)
{
KernelBase = argKernelBase;
if (CreateObject(ID_MODULE, NULL, &RenderModule, NULL,
FID_Name|TSTRING, "render",
FID_Version|TFLOAT, MODVERSION_RENDER,
TAGEND) IS ERR_Okay) {
if (GetField(RenderModule, FID_ModBase, FT_POINTER, &RenderBase) != ERR_Okay) {
return(ObjectError(argModule, ERH_InitModule, ERR_GetField));
}
}
else return(ObjectError(argModule, ERH_InitModule, ERR_CreateObject));
return(CreateObject(ID_CLASS, NULL, (OBJECTPTR *)&ForestClass, NULL,
FID_BaseClassID|TLONG, ID_FOREST,
FID_Version|TFLOAT, VER_FOREST,
FID_Name|TSTRING, "Forest",
FID_Category|TLONG, CCF_EFFECT,
FID_Actions|TPTR, ForestActions,
FID_Fields|TPTR, ForestFields,
FID_Size|TLONG, sizeof(struct Forest),
TAGEND));
}
/*****************************************************************************
** Command: Expunge()
*/
static ERROR CMDExpunge(void)
{
if (RenderModule) { Action(AC_Free, RenderModule, NULL); RenderModule = NULL; }
if (ForestClass) { Action(AC_Free, ForestClass, NULL); ForestClass = NULL; }
return(ERR_Okay);
}
/*****************************************************************************
** Forest: ActionNotify
*/
static ERROR FOREST_ActionNotify(struct Forest *Self, struct acActionNotify *NotifyArgs)
{
if ((NotifyArgs->ActionID IS AC_Redimension) IS (NotifyArgs->Error IS ERR_Okay)) {
struct Bitmap *bitmap;
WORD width, height;
if (LockDrawableBitmap(Self->DrawableID, &bitmap, NULL) IS ERR_Okay) {
width = bitmap->ClipRight - bitmap->ClipLeft;
height = bitmap->ClipBottom - bitmap->ClipTop;
if (width > Self->BitmapWidth) {
ActionTags(MT_DrawRectangle, bitmap, Self->BitmapWidth, 0,
width - Self->BitmapWidth, Self->BitmapHeight, 0x000000, TRUE);
}
if (height > Self->BitmapHeight) {
ActionTags(MT_DrawRectangle, bitmap, 0, Self->BitmapHeight, width,
height - Self->BitmapHeight, 0x000000, TRUE);
}
Self->BitmapWidth = width;
Self->BitmapHeight = height;
UnlockDrawableBitmap(Self->DrawableID, bitmap);
}
return(ERR_Okay);
}
else return(ERR_Failed);
}
/*****************************************************************************
** Forest: Free
*/
static ERROR FOREST_Free(struct Forest *Self, APTR Void)
{
UnsubscribeTimer(Self->Head.UniqueID);
return(ERR_Okay);
}
/*****************************************************************************
** Forest: Init
*/
static ERROR FOREST_Init(struct Forest *Self, APTR Void)
{
OBJECTPTR drawable;
/*** Find the Drawable object that we are associated with ***/
if (!Self->DrawableID) {
OBJECTID OwnerID;
OwnerID = GetOwner(Self);
while ((OwnerID) AND (GetClassID(OwnerID) != ID_RENDER)) {
OwnerID = GetOwnerID(OwnerID);
}
if (!OwnerID) return(ObjectError(Self, ERH_Init, ERR_UnsupportedOwner));
else Self->DrawableID = OwnerID;
}
/*** Validate fields ***/
if (Self->MaxElements < 5) Self->MaxElements = 5;
if (AccessObject(Self->DrawableID, 3000, &drawable) IS ERR_Okay) {
struct Bitmap *bitmap;
SubscribeActionTags(drawable, Self, AC_Redimension, TAGEND);
if (LockDrawableBitmap(Self->DrawableID, &bitmap, NULL) IS ERR_Okay) {
Action(AC_Clear, bitmap, NULL);
Self->BitmapWidth = bitmap->ClipRight - bitmap->ClipLeft;
Self->BitmapHeight = bitmap->ClipBottom - bitmap->ClipTop;
UnlockDrawableBitmap(Self->DrawableID, bitmap);
}
ReleaseObject(drawable);
}
/*** Initialise the Timer ***/
return(SubscribeTimer(Self->Head.UniqueID, 40));
}
/*****************************************************************************
** Forest: NewObject
*/
static ERROR FOREST_NewObject(struct Forest *Self, APTR Void)
{
Self->MaxElements = 50;
Self->MinHeight = 20;
Self->MaxHeight = 40;
Self->MinAngle = 15;
Self->MaxAngle = 35;
Self->Conformity = 15;
Self->IterationLevel = 1;
return(ERR_Okay);
}
/*****************************************************************************
** Forest: Timer
*/
static ERROR FOREST_Timer(struct Forest *Self, struct acTimer *Args)
{
struct Bitmap *bitmap;
struct RGB rgb;
WORD width, height, x, y, x2, y2, len;
FLOAT a, as;
if (LockDrawableBitmap(Self->DrawableID, &bitmap, NULL) != ERR_Okay) return(ERR_LockFailed);
width = bitmap->ClipRight - bitmap->ClipLeft;
height = bitmap->ClipBottom - bitmap->ClipTop;
Self->Time += Args->TimeElapsed;
if (Self->TreeCount < Self->MaxElements) {
x = RANGE_RAND(0, width);
y = RANGE_RAND(0, height + Self->MaxHeight);
a = -3.1416 / 2.0 + DEGTORAD(RandomNumber(2 * Self->Conformity) - Self->Conformity);
as = DEGTORAD(RANGE_RAND(Self->MinAngle, Self->MaxAngle));
len = ((RANGE_RAND(Self->MinHeight, Self->MaxHeight) * (width / 20)) / 50) + 2;
rgb.Red = RandomNumber(180);
rgb.Green = RandomNumber(180);
rgb.Blue = RandomNumber(180);
x2 = x + (WORD)(COSF(a) * ((FLOAT)len));
y2 = y + (WORD)(SINF(a) * ((FLOAT)len));
ActionTags(MT_DrawRGBLine, bitmap, x, y, x2, y2, rgb.Red, rgb.Green, rgb.Blue);
ActionTags(MT_DrawRGBLine, bitmap, x+1, y, x2+1, y2, rgb.Red, rgb.Green, rgb.Blue);
DrawTree(Self, bitmap, x2, y2, (len * 90) / 100, a, as, rgb, 1);
Self->TreeCount++;
DelayMsg(MT_Expose, Self->DrawableID, NULL);
}
/*** If the timer has reached its maximal count, reset the forest and start again ***/
if (Self->Time >= 10000) {
Action(AC_Clear, bitmap, NULL);
Self->Time = 0;
Self->TreeCount = 0;
}
UnlockDrawableBitmap(Self->DrawableID, bitmap);
return(ERR_Okay);
}
/****************************************************************************
** Internal: DrawTree()
** Short: Draws a single forest tree.
*/
void DrawTree(struct Forest *Self, struct Bitmap *Bitmap, WORD x, WORD y,
LONG len, FLOAT a, FLOAT as, struct RGB rgb, LONG level)
{
WORD x1, y1, x2, y2, redspeed, greenspeed, bluespeed;
FLOAT a1, a2;
redspeed = (255 - rgb.Red) / (Self->IterationLevel + 12);
greenspeed = (255 - rgb.Green) / (Self->IterationLevel + 12);
bluespeed = (255 - rgb.Blue) / (Self->IterationLevel + 12);
/* left */
a1 = a + as + DEGTORAD(RandomNumber(2 * Self->Conformity) - Self->Conformity);
x1 = x + (short)(COSF(a1) * ((FLOAT)len));
y1 = y + (short)(SINF(a1) * ((FLOAT)len));
/* right */
a2 = a - as + DEGTORAD(RandomNumber(2 * Self->Conformity) - Self->Conformity);
x2 = x + (short)(COSF(a2) * ((FLOAT)len));
y2 = y + (short)(SINF(a2) * ((FLOAT)len));
ActionTags(MT_DrawRGBLine, Bitmap, x, y, x1, y1, rgb.Red, rgb.Green, rgb.Blue);
ActionTags(MT_DrawRGBLine, Bitmap, x, y, x2, y2, rgb.Red, rgb.Green, rgb.Blue);
if (level < 2) {
ActionTags(MT_DrawRGBLine, Bitmap, x+1, y, x1+1, y1, rgb.Red, rgb.Green, rgb.Blue);
ActionTags(MT_DrawRGBLine, Bitmap, x+1, y, x2+1, y2, rgb.Red, rgb.Green, rgb.Blue);
}
rgb.Red = (rgb.Red + redspeed) % 255;
rgb.Green = (rgb.Green + greenspeed) % 255;
rgb.Blue = (rgb.Blue + bluespeed) % 255;
len = (len * 900) / 1000;
if (level < (9+Self->IterationLevel)) {
DrawTree(Self, Bitmap, x1, y1, len, a1, as, rgb, level + 1);
DrawTree(Self, Bitmap, x2, y2, len, a2, as, rgb, level + 1);
}
}
/*****************************************************************************
###FIELD###
Name: Conformity
Short: Defines the level of conformity used to generate each tree.
Type: LONG
Status: Read/Write
###DESCRIPTION###
Changing this field allows you to alter the conformity of the generated
trees. Low values will generate many trees that conform to a similar shape,
while high values will generate more variants. The field cannot be set to
a value greater than 30.
###END###
*****************************************************************************/
static ERROR SET_Conformity(struct Forest *Self, LONG *Value)
{
if (*Value < 0) Self->Conformity = 0;
else if (*Value > 30) Self->Conformity = 30;
else Self->Conformity = *Value;
return(ERR_Okay);
}
/*****************************************************************************
###FIELD###
Name: IterationLevel
Short: Determines the amount of branches generated for each tree.
Type: LONG
Status: Read/Write
###DESCRIPTION###
The iteration level defines how many 'branches' each tree will have. The
higher the value, the more the fractals will look like genuine trees. The
minimum value is 0 and the maximum value is 5. Note that the processor time
required to draw each tree will increase when more branches need to be
drawn.
###END###
*****************************************************************************/
static ERROR SET_IterationLevel(struct Forest *Self, LONG *Value)
{
if (*Value < 0) Self->IterationLevel = 0;
else if (*Value > 5) Self->IterationLevel = 5;
else Self->IterationLevel = *Value;
return(ERR_Okay);
}
/*****************************************************************************
###FIELD###
Name: MaxElements
Short: The total number of trees drawn by the generator is defined by this field.
Type: LONG
Status: Read/Init
###DESCRIPTION###
You may specify the exact amount of trees to be drawn by the fractal
generator by setting the MaxElements field. It is recommended that you use
a setting between 50 and 100 for a reasonable effect.
###END###
*****************************************************************************/
static ERROR SET_MaxElements(struct Forest *Self, LONG *Value)
{
if (*Value > 100) Self->MaxElements = 100;
else if (*Value < 1) Self->MaxElements = 1;
else Self->MaxElements = *Value;
return(ERR_Okay);
}
/*****************************************************************************
###FIELD###
Name: MinHeight
Short: The minimum height allowed for each tree.
Type: LONG
Status: Read/Write
###DESCRIPTION###
The minimum height of each tree is defined in this field. The height is
calculated proportionatly from this value, so the setting is not pixel
relative. The minimum possible height is 3, and the value cannot be greater
than that of the MaxHeight field.
###SEE ALSO###
Field: MaxHeight
###END###
*****************************************************************************/
static ERROR SET_MinHeight(struct Forest *Self, LONG *Value)
{
if (*Value < 5) Self->MinHeight = 5;
else if (*Value >= Self->MaxHeight) Self->MinHeight = Self->MaxHeight - 3;
else Self->MinHeight = *Value;
return(ERR_Okay);
}
/*****************************************************************************
###FIELD###
Name: MaxHeight
Short: The maximum height allowed for each tree.
Type: LONG
Status: Read/Write
###DESCRIPTION###
The maximum height of each tree is defined in this field. The height is
calculated proportionatly from this value, so the setting is not pixel
relative. The maximum possible height is 100, and the value cannot be less
than that of the MinHeight field.
###SEE ALSO###
Field: MinHeight
###END###
*****************************************************************************/
static ERROR SET_MaxHeight(struct Forest *Self, LONG *Value)
{
if (*Value > 100) Self->MaxHeight = 100;
else if (*Value < Self->MinHeight) Self->MaxHeight = Self->MinHeight + 3;
else Self->MaxHeight = *Value;
return(ERR_Okay);
}
/*****************************************************************************
###FIELD###
Name: MinAngle
Short: Defines the minimum possible angle in each branch' differential.
Type: LONG
Status: Read/Write
###DESCRIPTION###
Manipulating the MinAngle and MaxAngle fields allows you to control the
breadth of the trees. Setting low MinAngle and MaxAngle values will create
thin-looking trees, higher values will create broader looking trees.
The MaxAngle field cannot be set to a number greater than 50 and the
MinAngle cannot be set to a value less than three.
###SEE ALSO###
Field: MaxAngle
###END###
*****************************************************************************/
static ERROR SET_MinAngle(struct Forest *Self, LONG *Value)
{
if (*Value < 3) Self->MinAngle = 3;
else if (*Value >= Self->MaxAngle) Self->MinAngle = Self->MaxAngle - 3;
else Self->MinAngle = *Value;
return(ERR_Okay);
}
/*****************************************************************************
###FIELD###
Name: MaxAngle
Short: Defines the maximum possible angle in each branch' differential.
Type: LONG
Status: Read/Write
###DESCRIPTION###
Manipulating the MinAngle and MaxAngle fields allows you to control the
breadth of the trees. Setting low MinAngle and MaxAngle values will create
thin-looking trees, higher values will create broader looking trees.
The MaxAngle field cannot be set to a number greater than 50 and the
MinAngle cannot be set to a value less than three.
###SEE ALSO###
Field: MinAngle
###END###
*****************************************************************************/
static ERROR SET_MaxAngle(struct Forest *Self, LONG *Value)
{
if (*Value < Self->MinAngle) Self->MaxAngle = Self->MinAngle + 3;
else if (*Value > 50) Self->MaxAngle = 50;
else Self->MaxAngle = *Value;
return(ERR_Okay);
}