/***************************************************************************** This source code is made available to you under the terms of the Open Development License. This Package should be accompanied by a file that contains the associated licensing terms. If you are unable to find this file, please refer to its location on the Internet at www.rocklyte.com/license.html. ****************************************************************************** ###CLASS### Name: Starfield Version: 1.0 Status: Stable Date: February 2001 ID: ID_STARFIELD Category: Effect Author: Rocklyte Systems Copyright: Rocklyte Systems (c) 2000-2001. All rights reserved. Short: This class creates a real-time animated starfield. Keywords: starfield, star, starburst, zoom, zooming ###DESCRIPTION### <p>The Starfield class displays a zooming starfield, a classic effect often used in older graphics demonstrations. When creating a Starfield object, you must make sure that it is contained by a graphical object area (e.g. that provided by a Render object). Another graphical object should also be used to define the background underneath the Starfield. Here is an example:</p> <pre> &lt;render width="300" height="200"/&gt; &lt;box colour="#000000"/&gt; &lt;starfield maxelements="1000"/&gt; &lt;/render&gt; </pre> <p>Once initialised, the Starfield will run automatically until it is freed from the system. The amount of processor cycles used by the Starfield is directly proportional to the amount of requested stars, as specified by the MaxElements field. Generally, 500 to 1000 stars is sufficient for an adequate effect and minimal processor usage.</p> ###END### *****************************************************************************/ #include <pandora/main.h> #include <pandora/system/all.h> #include <pandora/graphics/bitmap.h> #include <pandora/graphics/render.h> #include <math.h> static ERROR CMDInit(OBJECTPTR, struct KernelBase *); static ERROR CMDExpunge(void); #define VER_STARFIELD 1.0 EXPORT struct ModHeader ModHeader = { MODULE_HEADER_V1, CMDInit, NULL, NULL, CMDExpunge, JMP_DEFAULT, 0, CPU_PC, VER_STARFIELD, VER_KERNEL, "Rocklyte Systems", "Rocklyte Systems (c) 2000-2001. All rights reserved.", "February 2001", "Starfield" }; static struct KernelBase *KernelBase = 0; static OBJECTPTR StarfieldClass = 0; /***************************************************************************** ** Starfield class. */ struct Starfield { OBJECT_HEADER OBJECTID DrawableID; /* */ LONG MaxElements; /* Maximum amount of stars to use */ LONG XOffset; /* Horizontal offset of the starfield center */ LONG YOffset; /* Vertical offset of the starfield center */ PRIVATE_FIELDS OBJECTID TimerID; struct Star *Stars; MEMORYID StarsMID; LONG DrawableWidth; LONG DrawableHeight; }; struct Star { LONG XCoord; LONG YCoord; LONG XSpeed; LONG YSpeed; WORD TrailX; WORD TrailY; }; static struct FieldArray StarfieldFields[] = { { "Drawable", 0, FDF_OBJECTID|FDF_RI, ID_RENDER, NULL, NULL }, { "MaxElements", 0, FDF_LONG|FDF_RI, 0, NULL, NULL }, { "XOffset", 0, FDF_LONG|FDF_RW, 0, NULL, NULL }, { "YOffset", 0, FDF_LONG|FDF_RW, 0, NULL, NULL }, END_FIELD }; static ERROR STARFIELD_ActionNotify(struct Starfield *, struct acActionNotify *); static ERROR STARFIELD_Free(struct Starfield *, APTR); static ERROR STARFIELD_ReleaseObject(struct Starfield *, APTR); static ERROR STARFIELD_NewObject(struct Starfield *, APTR); static ERROR STARFIELD_Init(struct Starfield *, APTR); static ERROR STARFIELD_Move(struct Starfield *, struct acMove *); static ERROR STARFIELD_Scroll(struct Starfield *, struct acScroll *); static ERROR STARFIELD_Timer(struct Starfield *, struct acTimer *); static struct ActionArray StarfieldActions[] = { { AC_ActionNotify, STARFIELD_ActionNotify }, { AC_Free, STARFIELD_Free }, { AC_ReleaseObject, STARFIELD_ReleaseObject }, { AC_Init, STARFIELD_Init }, { AC_Move, STARFIELD_Move }, { AC_NewObject, STARFIELD_NewObject }, { AC_Scroll, STARFIELD_Scroll }, { AC_Timer, STARFIELD_Timer }, { NULL, NULL } }; /***************************************************************************** ** Command: Init() */ ERROR CMDInit(OBJECTPTR argModule, struct KernelBase *argKernelBase) { KernelBase = argKernelBase; return(CreateObject(ID_CLASS, NULL, (OBJECTPTR *)&StarfieldClass, NULL, FID_BaseClassID|TLONG, ID_STARFIELD, FID_Version|TFLOAT, VER_STARFIELD, FID_Name|TSTRING, "Starfield", FID_Category|TLONG, CCF_EFFECT, FID_Actions|TPTR, StarfieldActions, FID_Fields|TPTR, StarfieldFields, FID_Size|TLONG, sizeof(struct Starfield), TAGEND)); } /***************************************************************************** ** Command: Expunge() */ ERROR CMDExpunge(void) { if (StarfieldClass) { Action(AC_Free, StarfieldClass, NULL); StarfieldClass = NULL; } return(ERR_Okay); } /***************************************************************************** ** Function: GenerateStar() */ static void GenerateStar(struct Star *Star) { #define MAXSPEED 18 #define MINSPEED 2 Star->XCoord = 0; Star->YCoord = 0; Star->XSpeed = 0; Star->YSpeed = 0; while ((Star->XSpeed > -MINSPEED) AND (Star->XSpeed < +MINSPEED) AND (Star->YSpeed > -MINSPEED) AND (Star->YSpeed < +MINSPEED)) { Star->XSpeed = (RandomNumber(MAXSPEED) - (MAXSPEED>>1) + (((FLOAT)RandomNumber(10000))/10000)) * 0xffff; Star->YSpeed = (RandomNumber(MAXSPEED) - (MAXSPEED>>1) + (((FLOAT)RandomNumber(10000))/10000)) * 0xffff; } } /***************************************************************************** ** Starfield: ActionNotify */ static ERROR STARFIELD_ActionNotify(struct Starfield *Self, struct acActionNotify *NotifyArgs) { if ((NotifyArgs->ActionID IS AC_Draw) IS (NotifyArgs->Error IS ERR_Okay)) { struct RGB rgb; struct Bitmap *bitmap; OBJECTPTR Drawable; WORD offsetx, offsety, XQuadrant, YQuadrant, x, y; LONG ColourX, ColourY, i; if (!Self->Stars) { if (AccessMemory(Self->StarsMID, MEM_READWRITE, (void **)&Self->Stars) != ERR_Okay) { return(ObjectError(Self, ERH_Draw, ERR_AccessMemory)); } } 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_GetField)); } GetFields(Drawable, FID_Width|TLONG, &Self->DrawableWidth, FID_Height|TLONG, &Self->DrawableHeight, TAGEND); XQuadrant = Self->DrawableWidth>>1; YQuadrant = Self->DrawableHeight>>1; offsetx = Self->XOffset + (Self->DrawableWidth>>1); offsety = Self->YOffset + (Self->DrawableHeight>>1); if ((XQuadrant > 0) AND (YQuadrant > 0)) { /* Must be > 0 to prevent division by zero */ /*** Update the starfield ***/ for (i=0; i < Self->MaxElements; i++) { ColourX = Self->Stars[i].XCoord>>16; if (ColourX < 0) ColourX = -ColourX; ColourX = (ColourX * (256-32) / XQuadrant) + 32; ColourY = Self->Stars[i].YCoord>>16; if (ColourY < 0) ColourY = -ColourY; ColourY = (ColourY * (256-32) / YQuadrant) + 32; if (ColourY > ColourX) ColourX = ColourY; rgb.Red = ColourX; rgb.Green = ColourX; rgb.Blue = ColourX; x = offsetx + (WORD)(Self->Stars[i].XCoord>>16); y = offsety + (WORD)(Self->Stars[i].YCoord>>16); if ((x >= bitmap->ClipLeft) AND (x < bitmap->ClipRight) AND (y >= bitmap->ClipTop) AND (y < bitmap->ClipBottom)) { bitmap->DrawUCRPixel(bitmap, bitmap->XOffset + x, bitmap->YOffset + y, &rgb); } } } ReleaseObject(Drawable); } return(ERR_Okay); } /***************************************************************************** ** Starfield: Free */ static ERROR STARFIELD_Free(struct Starfield *Self, APTR Void) { UnsubscribeTimer(Self->Head.UniqueID); if (Self->DrawableID) { OBJECTPTR Drawable; if (AccessObject(Self->DrawableID, 2000, &Drawable) IS ERR_Okay) { UnsubscribeAction(Drawable, NULL, GetUniqueID(Self)); ReleaseObject(Drawable); } } if (Self->Stars) { ReleaseMemory(Self->Stars); Self->Stars = NULL; } if (Self->StarsMID) { FreeMemoryID(Self->StarsMID); Self->StarsMID = NULL; } return(ERR_Okay); } /***************************************************************************** ** Starfield: ReleaseObject */ static ERROR STARFIELD_ReleaseObject(struct Starfield *Self, APTR Void) { if (Self->Stars) { ReleaseMemory(Self->Stars); Self->Stars = NULL; } return(ERR_Okay); } /***************************************************************************** ** Starfield: Init */ static ERROR STARFIELD_Init(struct Starfield *Self, APTR Void) { OBJECTPTR Drawable; LONG i; if (Self->MaxElements > 10000) Self->MaxElements = 10000; /*** 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; } /*** Calculate the starfield ***/ if (AllocMemory(sizeof(struct Star) * Self->MaxElements, Self->Head.MemFlags|MEM_NOCLEAR, (APTR *)&Self->Stars, &Self->StarsMID) IS ERR_Okay) { for (i=0; i < Self->MaxElements; i++) { GenerateStar(&Self->Stars[i]); } } else return(ObjectError(Self, ERH_Init, ERR_Memory)); /*** Action subscriptions ***/ if (AccessObject(Self->DrawableID, 200, &Drawable) IS ERR_Okay) { SubscribeAction(Drawable, AC_Draw, Self); ReleaseObject(Drawable); } /*** Initialise the Timer ***/ return(SubscribeTimer(Self->Head.UniqueID, 20)); } /***************************************************************************** ** Action: Move */ static ERROR STARFIELD_Move(struct Starfield *Self, struct acMove *Args) { Self->XOffset += Args->XChange; Self->YOffset += Args->YChange; return(ERR_Okay); } /***************************************************************************** ** Starfield: NewObject */ static ERROR STARFIELD_NewObject(struct Starfield *Self, APTR Args) { Self->MaxElements = 500; return(ERR_Okay); } /***************************************************************************** ** Action: Scroll */ static ERROR STARFIELD_Scroll(struct Starfield *Self, struct acScroll *Args) { Self->XOffset += Args->XChange; Self->YOffset += Args->YChange; return(ERR_Okay); } /***************************************************************************** ** Action: Timer */ static ERROR STARFIELD_Timer(struct Starfield *Self, struct acTimer *Args) { LONG XCoord, YCoord, i, offsetx, offsety; if (!Self->Stars) { if (AccessMemory(Self->StarsMID, MEM_READWRITE, (void **)&Self->Stars) != ERR_Okay) { return(ObjectError(Self, ERH_Draw, ERR_AccessMemory)); } } offsetx = Self->XOffset + (Self->DrawableWidth>>1); offsety = Self->YOffset + (Self->DrawableHeight>>1); for (i=0; i < Self->MaxElements; i++) { XCoord = offsetx + (WORD)(Self->Stars[i].XCoord>>16); YCoord = offsety + (WORD)(Self->Stars[i].YCoord>>16); if ((XCoord >= 0) AND (XCoord < Self->DrawableWidth) AND (YCoord >= 0) AND (YCoord < Self->DrawableHeight)) { Self->Stars[i].TrailX = XCoord; Self->Stars[i].TrailY = YCoord; Self->Stars[i].XCoord += Self->Stars[i].XSpeed; Self->Stars[i].YCoord += Self->Stars[i].YSpeed; } else GenerateStar(&Self->Stars[i]); } DelayMsg(AC_Draw, Self->DrawableID, NULL); return(ERR_Okay); } /***************************************************************************** ###FIELD### Name: MaxElements Short: The total number of stars is defined by this field. Type: LONG Status: Read/Init ###DESCRIPTION### <p>You may specify the exact amount of stars to be used in the Starfield by setting the MaxElements field. For a reasonable effect, a minimum amount of 500 stars is recommended. The value should be no higher than 5000.</p> <p>If you do not set this field, the default number of stars will be used.</p> ###END### *****************************************************************************/ /***************************************************************************** ###FIELD### Name: XOffset Short: The horizontal location of the Starfield's center. Type: LONG Status: Read/Write ###DESCRIPTION### <p>By default this field is set to zero, which puts the horizontal location of the Starfield at the center of its container. You can manipulate the horizontal center of the Starfield by writing directly to this field. Negative values will move the Starfield to the left, positive values will move it to the right.</p> ###END### *****************************************************************************/ /***************************************************************************** ###FIELD### Name: YOffset Short: The vertical location of the Starfield's center. Type: LONG Status: Read/Write ###DESCRIPTION### <p>By default this field is set to zero, which puts the vertical location of the Starfield at the center of its container. You can manipulate the vertical center of the Starfield by writing directly to this field. Negative values will move the Starfield up, positive values will move it down.</p> ###END### *****************************************************************************/