added simple demoreel and ps2 skeleton coded by claude fable 5

This commit is contained in:
aap
2026-08-09 01:39:54 +02:00
parent 92851aa1ed
commit 0a7eb48ce3
18 changed files with 1482 additions and 9 deletions
+16
View File
@@ -0,0 +1,16 @@
add_executable(demoreel WIN32
demoreel.h
main.cpp
proctex.cpp
scene_knot.cpp
scene_particles.cpp
scene_tunnel.cpp
)
target_link_libraries(demoreel
PRIVATE
librw::skeleton
librw::librw
)
librw_platform_target(demoreel)
+62
View File
@@ -0,0 +1,62 @@
# PS2 build (SCE toolchain). PC builds are generated by premake.
# Build librw for ps2 first: premake5 gmake; make -C build librw config=debug_ps2
CC=ee-gcc
CXX=ee-g++
TARGET=demoreel
LIBRW := ../..
OBJDIR := obj/ps2
SRC := main.cpp proctex.cpp scene_tunnel.cpp scene_particles.cpp scene_knot.cpp
SKELSRC := skeleton.cpp ps2.cpp
SCELIBDIR := /usr/local/sce/ee/lib
CFLAGS := -Os -DRW_PS2 -fno-common -fno-exceptions
LIBS = $(LIBRW)/lib/ps2/Debug/librw.a \
$(SCELIBDIR)/libgraph.a \
$(SCELIBDIR)/libdma.a \
$(SCELIBDIR)/libmc.a \
$(SCELIBDIR)/libpc.a \
$(SCELIBDIR)/libpad.a \
$(SCELIBDIR)/libcdvd.a \
$(SCELIBDIR)/libscf.a
GCCLIB := /usr/local/sce/ee/gcc/lib/gcc-lib/ee/3.2-ee-030926
CRT_BEGIN := $(OBJDIR)/crt0.o $(GCCLIB)/crti.o $(GCCLIB)/crtbegin.o
CRT_END := $(GCCLIB)/crtend.o $(GCCLIB)/crtn.o
OBJ := $(addprefix $(OBJDIR)/,$(SRC:.cpp=.o) $(SKELSRC:.cpp=.o))
DEP := $(OBJ:.o=.d)
INC := -I. \
-I$(LIBRW) \
-I$(LIBRW)/skeleton \
-I/usr/local/sce/common/include \
-I/usr/local/sce/ee/include
$(TARGET).elf: $(OBJDIR)/crt0.o $(OBJ)
$(CXX) -o $@ $(CRT_BEGIN) $(OBJ) $(LIBS) $(CRT_END) -T $(SCELIBDIR)/app.cmd -L$(SCELIBDIR) -lm -nostartfiles -Wl,--gc-sections
run: $(TARGET).elf
dsedb -r run $(TARGET).elf
$(OBJDIR)/crt0.o:
@mkdir -p $(@D)
$(CC) -c -xassembler-with-cpp -o $@ $(SCELIBDIR)/crt0.s
$(OBJDIR)/%.o: %.cpp
@mkdir -p $(@D)
$(CXX) $(CFLAGS) $(INC) -MMD -c $< -o $@
$(OBJDIR)/%.o: $(LIBRW)/skeleton/%.cpp
@mkdir -p $(@D)
$(CXX) $(CFLAGS) $(INC) -MMD -c $< -o $@
clean:
rm -rf $(OBJDIR) $(TARGET).elf
-include $(DEP)
+39
View File
@@ -0,0 +1,39 @@
#ifndef DEMOREEL_H
#define DEMOREEL_H
#include <rw.h>
#include <skeleton.h>
// A demo scene. Plain function pointers and static data only;
// scene code is meant to stay compilable for PS2 (old gcc, no STL).
struct DemoScene
{
const char *name;
void (*init)(void);
void (*term)(void);
void (*update)(float dt);
void (*render)(void);
};
struct SceneGlobals
{
rw::World *world;
rw::Camera *camera;
};
extern SceneGlobals Scene;
extern float DemoTime; // seconds since scene start
// main.cpp
void LookAt(rw::Frame *frame, rw::V3d pos, rw::V3d target);
rw::RGBA HSV(float h, float s, float v, rw::uint8 alpha);
// proctex.cpp
rw::Texture *MakeGlowTexture(void);
rw::Texture *MakeGridTexture(void);
rw::Texture *MakeEnvTexture(void);
extern DemoScene TunnelScene;
extern DemoScene ParticleScene;
extern DemoScene KnotScene;
#endif
+301
View File
@@ -0,0 +1,301 @@
#include <assert.h>
#include <math.h>
#include "demoreel.h"
rw::EngineOpenParams engineOpenParams;
rw::RGBA ForegroundColor = { 200, 200, 200, 255 };
rw::RGBA BackgroundColor = { 0, 0, 0, 0 };
SceneGlobals Scene;
float DemoTime;
float TimeDelta;
static DemoScene *scenes[] = {
&TunnelScene,
&ParticleScene,
&KnotScene,
};
static int numScenes = sizeof(scenes)/sizeof(scenes[0]);
static int curScene = -1;
static int nextScene = 0;
void
LookAt(rw::Frame *frame, rw::V3d pos, rw::V3d target)
{
static rw::V3d worldup = { 0.0f, 0.0f, 1.0f };
rw::Matrix m;
rw::V3d at, right, up;
at = rw::normalize(rw::sub(target, pos));
right = rw::cross(worldup, at);
if(rw::length(right) < 0.001f)
right.x = 1.0f, right.y = right.z = 0.0f;
right = rw::normalize(right);
up = rw::cross(at, right);
m.setIdentity();
m.right = right;
m.up = up;
m.at = at;
m.pos = pos;
m.optimize();
frame->transform(&m, rw::COMBINEREPLACE);
}
rw::RGBA
HSV(float h, float s, float v, rw::uint8 alpha)
{
rw::RGBA col;
float r, g, b;
float f, p, q, t;
int i;
h = h - floorf(h);
h *= 6.0f;
i = (int)h;
f = h - i;
p = v*(1.0f - s);
q = v*(1.0f - s*f);
t = v*(1.0f - s*(1.0f - f));
switch(i){
default:
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5: r = v; g = p; b = q; break;
}
col.red = (rw::uint8)(r*255.0f);
col.green = (rw::uint8)(g*255.0f);
col.blue = (rw::uint8)(b*255.0f);
col.alpha = alpha;
return col;
}
static void
SwitchScene(int n)
{
while(n < 0) n += numScenes;
n %= numScenes;
if(n == curScene)
return;
if(curScene >= 0)
scenes[curScene]->term();
curScene = n;
DemoTime = 0.0f;
scenes[curScene]->init();
}
rw::World*
CreateWorld(void)
{
rw::BBox bb;
bb.inf.x = bb.inf.y = bb.inf.z = -1000.0f;
bb.sup.x = bb.sup.y = bb.sup.z = 1000.0f;
return rw::World::create(&bb);
}
rw::Camera*
CreateCamera(rw::World *world)
{
rw::Camera *camera;
camera = sk::CameraCreate(sk::globals.width, sk::globals.height, 1);
assert(camera);
camera->setNearPlane(0.1f);
camera->setFarPlane(300.0f);
camera->setFOV(70.0f, (float)sk::globals.width/sk::globals.height);
world->addCamera(camera);
return camera;
}
void
Initialize(void)
{
sk::globals.windowtitle = "librw demo reel";
sk::globals.width = 1280;
sk::globals.height = 800;
sk::globals.quit = 0;
}
bool
Initialize3D(void)
{
if(!sk::InitRW())
return false;
Scene.world = CreateWorld();
Scene.camera = CreateCamera(Scene.world);
#ifndef RW_PS2
ImGui_ImplRW_Init();
ImGui::StyleColorsClassic();
#endif
SwitchScene(0);
return true;
}
void
Terminate3D(void)
{
if(curScene >= 0){
scenes[curScene]->term();
curScene = -1;
}
if(Scene.camera){
Scene.world->removeCamera(Scene.camera);
Scene.camera->destroy();
Scene.camera = nil;
}
if(Scene.world){
Scene.world->destroy();
Scene.world = nil;
}
sk::TerminateRW();
}
bool
attachPlugins(void)
{
rw::ps2::registerPDSPlugin(40);
rw::ps2::registerPluginPDSPipes();
rw::registerMeshPlugin();
rw::registerNativeDataPlugin();
rw::registerAtomicRightsPlugin();
rw::registerMaterialRightsPlugin();
rw::xbox::registerVertexFormatPlugin();
rw::registerSkinPlugin();
rw::registerUserDataPlugin();
rw::registerHAnimPlugin();
rw::registerMatFXPlugin();
rw::registerUVAnimPlugin();
rw::ps2::registerADCPlugin();
return true;
}
#ifndef RW_PS2
void
Gui(void)
{
static bool showWindow = true;
int i;
ImGui::Begin("Demo reel", &showWindow);
for(i = 0; i < numScenes; i++)
if(ImGui::RadioButton(scenes[i]->name, curScene == i))
nextScene = i;
ImGui::NewLine();
ImGui::Text("%.1f fps", 1.0f/TimeDelta);
ImGui::End();
}
#endif
void
Render(void)
{
Scene.camera->clear(&BackgroundColor, rw::Camera::CLEARIMAGE|rw::Camera::CLEARZ);
Scene.camera->beginUpdate();
#ifndef RW_PS2
ImGui_ImplRW_NewFrame(TimeDelta);
#endif
scenes[curScene]->render();
#ifndef RW_PS2
Gui();
ImGui::EndFrame();
ImGui::Render();
ImGui_ImplRW_RenderDrawLists(ImGui::GetDrawData());
#endif
Scene.camera->endUpdate();
Scene.camera->showRaster(0);
}
void
Idle(float timeDelta)
{
TimeDelta = timeDelta;
if(TimeDelta <= 0.0f) TimeDelta = 1.0f/60.0f;
if(TimeDelta > 0.1f) TimeDelta = 0.1f;
SwitchScene(nextScene);
nextScene = curScene;
DemoTime += TimeDelta;
scenes[curScene]->update(TimeDelta);
Render();
}
void
KeyDown(int key)
{
switch(key){
case sk::KEY_ESC:
sk::globals.quit = 1;
break;
case sk::KEY_LEFT:
nextScene = curScene-1;
break;
case sk::KEY_RIGHT:
case ' ':
nextScene = curScene+1;
break;
default:
if(key >= '1' && key < '1'+numScenes)
nextScene = key-'1';
break;
}
}
sk::EventStatus
AppEventHandler(sk::Event e, void *param)
{
using namespace sk;
Rect *r;
#ifndef RW_PS2
ImGuiEventHandler(e, param);
#endif
switch(e){
case INITIALIZE:
Initialize();
return EVENTPROCESSED;
case RWINITIALIZE:
return Initialize3D() ? EVENTPROCESSED : EVENTERROR;
case RWTERMINATE:
Terminate3D();
return EVENTPROCESSED;
case PLUGINATTACH:
return attachPlugins() ? EVENTPROCESSED : EVENTERROR;
case KEYDOWN:
KeyDown(*(int*)param);
return EVENTPROCESSED;
case RESIZE:
r = (Rect*)param;
if(r->w == 0) r->w = 1;
if(r->h == 0) r->h = 1;
sk::globals.width = r->w;
sk::globals.height = r->h;
if(Scene.camera)
sk::CameraSize(Scene.camera, r, 0.5f, 4.0f/3.0f);
break;
case IDLE:
Idle(*(float*)param);
return EVENTPROCESSED;
}
return sk::EVENTNOTPROCESSED;
}
+137
View File
@@ -0,0 +1,137 @@
#include <math.h>
#include "demoreel.h"
// Procedural textures so the demos need no asset files at all.
using namespace rw;
static Texture*
imageToTexture(Image *img)
{
Raster *ras = Raster::createFromImage(img);
img->destroy();
if(ras == nil)
return nil;
Texture *tex = Texture::create(ras);
tex->setFilter(Texture::LINEAR);
tex->setAddressU(Texture::WRAP);
tex->setAddressV(Texture::WRAP);
return tex;
}
// soft radial blob for additive particles
Texture*
MakeGlowTexture(void)
{
const int SZ = 64;
Image *img = Image::create(SZ, SZ, 32);
img->allocate();
int x, y;
for(y = 0; y < SZ; y++){
uint8 *line = img->pixels + y*img->stride;
for(x = 0; x < SZ; x++){
float dx = (x + 0.5f - SZ/2) / (SZ/2);
float dy = (y + 0.5f - SZ/2) / (SZ/2);
float r = sqrtf(dx*dx + dy*dy);
float v = 1.0f - r;
if(v < 0.0f) v = 0.0f;
v = v*v*(3.0f - 2.0f*v); // smoothstep
v = v*v; // sharpen the core
uint8 c = (uint8)(v*255.0f);
line[x*4+0] = c;
line[x*4+1] = c;
line[x*4+2] = c;
line[x*4+3] = c;
}
}
return imageToTexture(img);
}
// glowing neon grid on dark ground
Texture*
MakeGridTexture(void)
{
const int SZ = 128;
const int CELL = 32;
Image *img = Image::create(SZ, SZ, 32);
img->allocate();
int x, y;
for(y = 0; y < SZ; y++){
uint8 *line = img->pixels + y*img->stride;
for(x = 0; x < SZ; x++){
int mx = x % CELL; if(mx > CELL/2) mx = CELL-mx;
int my = y % CELL; if(my > CELL/2) my = CELL-my;
int d = mx < my ? mx : my;
float v = 1.0f - d/6.0f;
if(v < 0.0f) v = 0.0f;
v = v*v;
// dark blue ground, cyan-white lines
float r = 0.02f + v*0.75f;
float g = 0.03f + v*0.95f;
float b = 0.10f + v*0.90f;
line[x*4+0] = (uint8)(r*255.0f);
line[x*4+1] = (uint8)(g*255.0f);
line[x*4+2] = (uint8)(b*255.0f);
line[x*4+3] = 255;
}
}
return imageToTexture(img);
}
// fake sky-ground gradient with highlight streaks, for env mapping
Texture*
MakeEnvTexture(void)
{
const int SZ = 128;
Image *img = Image::create(SZ, SZ, 32);
img->allocate();
int x, y;
for(y = 0; y < SZ; y++){
uint8 *line = img->pixels + y*img->stride;
float fy = (float)y/SZ;
for(x = 0; x < SZ; x++){
float fx = (float)x/SZ;
float r, g, b;
if(fy < 0.5f){
// sky: bright at horizon
float t = fy*2.0f;
r = 0.05f + t*0.55f;
g = 0.15f + t*0.65f;
b = 0.35f + t*0.65f;
}else{
// ground: dark, fading down
float t = (fy-0.5f)*2.0f;
r = 0.45f - t*0.40f;
g = 0.35f - t*0.32f;
b = 0.30f - t*0.28f;
}
// horizon band
float h = fabsf(fy - 0.5f);
float band = 1.0f - h*8.0f;
if(band > 0.0f){
band = band*band;
r += band*0.5f;
g += band*0.5f;
b += band*0.4f;
}
// vertical highlight streaks in the sky
float streak = sinf(fx*3.14159f*6.0f);
streak = streak*streak*streak*streak;
if(fy < 0.5f)
r += streak*0.15f, g += streak*0.15f, b += streak*0.1f;
if(r > 1.0f) r = 1.0f;
if(g > 1.0f) g = 1.0f;
if(b > 1.0f) b = 1.0f;
line[x*4+0] = (uint8)(r*255.0f);
line[x*4+1] = (uint8)(g*255.0f);
line[x*4+2] = (uint8)(b*255.0f);
line[x*4+3] = 255;
}
}
return imageToTexture(img);
}
+221
View File
@@ -0,0 +1,221 @@
#include <math.h>
#include <assert.h>
#include "demoreel.h"
// Retained mode: a procedural torus knot with env-mapped chrome,
// lit by two colored directional lights. Exercises Geometry
// creation, materials, MatFX and the world/light path.
using namespace rw;
#define KNOT_P 2
#define KNOT_Q 3
#define SEGS 160
#define SIDES 12
#define NUMVERTS ((SEGS+1)*(SIDES+1))
#define NUMTRIS (SEGS*SIDES*2)
static Atomic *knotAtomic;
static Frame *knotFrame;
static Light *ambient;
static Light *keyLight;
static Light *fillLight;
static Texture *envTex;
static Texture *gridTex;
static V3d
knotCenter(float t)
{
V3d p;
float r = 6.0f + 2.5f*cosf(KNOT_Q*t);
p.x = r*cosf(KNOT_P*t);
p.y = r*sinf(KNOT_P*t);
p.z = 2.5f*sinf(KNOT_Q*t);
return p;
}
static Geometry*
CreateKnotGeometry(void)
{
Geometry *geo = Geometry::create(NUMVERTS, NUMTRIS,
Geometry::POSITIONS | Geometry::NORMALS |
Geometry::LIGHT | Geometry::TEXTURED);
assert(geo);
MorphTarget *mt = &geo->morphTargets[0];
V3d *verts = mt->vertices;
V3d *norms = mt->normals;
TexCoords *uv = geo->texCoords[0];
float tuberad = 1.1f;
int i, j, v;
v = 0;
for(i = 0; i <= SEGS; i++){
float t = (float)i/SEGS*2.0f*3.14159265f;
V3d c = knotCenter(t);
V3d tan = normalize(sub(knotCenter(t+0.01f), knotCenter(t-0.01f)));
static V3d worldup = { 0.0f, 0.0f, 1.0f };
V3d n = normalize(cross(worldup, tan));
V3d b = cross(tan, n);
for(j = 0; j <= SIDES; j++){
float a = (float)j/SIDES*2.0f*3.14159265f;
V3d ring = add(scale(n, cosf(a)), scale(b, sinf(a)));
verts[v] = add(c, scale(ring, tuberad));
norms[v] = ring;
uv[v].u = (float)i/SEGS*24.0f;
uv[v].v = (float)j/SIDES;
v++;
}
}
Triangle *tri = geo->triangles;
for(i = 0; i < SEGS; i++)
for(j = 0; j < SIDES; j++){
int r0 = i*(SIDES+1) + j;
int r1 = (i+1)*(SIDES+1) + j;
tri->v[0] = r0; tri->v[1] = r0+1; tri->v[2] = r1;
tri->matId = 0;
tri++;
tri->v[0] = r0+1; tri->v[1] = r1+1; tri->v[2] = r1;
tri->matId = 0;
tri++;
}
Material *mat = Material::create();
gridTex = MakeGridTexture();
if(gridTex)
mat->setTexture(gridTex);
envTex = MakeEnvTexture();
MatFX::setEffects(mat, MatFX::ENVMAP);
MatFX *mfx = MatFX::get(mat);
if(envTex)
mfx->setEnvTexture(envTex);
mfx->setEnvFrame(Scene.camera->getFrame());
mfx->setEnvCoefficient(0.6f);
geo->matList.appendMaterial(mat);
mat->destroy(); // list holds a ref now
geo->calculateBoundingSphere();
// strips are the well-trodden path on PS2 (and a test for the tristripper)
geo->flags |= Geometry::TRISTRIP;
geo->buildMeshes();
return geo;
}
static Light*
MakeDirLight(float r, float g, float b, float yaw, float pitch)
{
static V3d Xaxis = { 1.0f, 0.0f, 0.0f };
static V3d Zaxis = { 0.0f, 0.0f, 1.0f };
Light *light = Light::create(Light::DIRECTIONAL);
light->setColor(r, g, b);
Frame *f = Frame::create();
f->rotate(&Xaxis, pitch, COMBINEREPLACE);
f->rotate(&Zaxis, yaw, COMBINEPOSTCONCAT);
light->setFrame(f);
Scene.world->addLight(light);
return light;
}
static void
KnotInit(void)
{
Geometry *geo = CreateKnotGeometry();
knotFrame = Frame::create();
knotAtomic = Atomic::create();
knotAtomic->setGeometry(geo, 0);
geo->destroy(); // atomic holds a ref now
knotAtomic->setFrame(knotFrame);
MatFX::enableEffects(knotAtomic);
Scene.world->addAtomic(knotAtomic);
ambient = Light::create(Light::AMBIENT);
ambient->setColor(0.15f, 0.15f, 0.2f);
Scene.world->addLight(ambient);
keyLight = MakeDirLight(1.0f, 0.85f, 0.6f, 30.0f, 120.0f);
fillLight = MakeDirLight(0.3f, 0.4f, 0.9f, 200.0f, 60.0f);
}
static void
KnotTerm(void)
{
if(knotAtomic){
Scene.world->removeAtomic(knotAtomic);
knotAtomic->destroy();
knotAtomic = nil;
}
if(knotFrame){
knotFrame->destroy();
knotFrame = nil;
}
if(ambient){
Scene.world->removeLight(ambient);
ambient->destroy();
ambient = nil;
}
if(keyLight){
Scene.world->removeLight(keyLight);
Frame *f = keyLight->getFrame();
keyLight->setFrame(nil);
f->destroy();
keyLight->destroy();
keyLight = nil;
}
if(fillLight){
Scene.world->removeLight(fillLight);
Frame *f = fillLight->getFrame();
fillLight->setFrame(nil);
f->destroy();
fillLight->destroy();
fillLight = nil;
}
if(envTex){
envTex->destroy();
envTex = nil;
}
if(gridTex){
gridTex->destroy();
gridTex = nil;
}
}
static void
KnotUpdate(float dt)
{
static V3d Xaxis = { 1.0f, 0.0f, 0.0f };
static V3d Zaxis = { 0.0f, 0.0f, 1.0f };
knotFrame->rotate(&Zaxis, dt*20.0f, COMBINEPOSTCONCAT);
knotFrame->rotate(&Xaxis, dt*8.0f, COMBINEPRECONCAT);
float ca = DemoTime*0.1f;
V3d campos;
campos.x = 24.0f*cosf(ca);
campos.y = 24.0f*sinf(ca);
campos.z = 8.0f*sinf(DemoTime*0.23f);
V3d origin = { 0.0f, 0.0f, 0.0f };
LookAt(Scene.camera->getFrame(), campos, origin);
}
static void
KnotRender(void)
{
SetRenderState(ZTESTENABLE, 1);
SetRenderState(ZWRITEENABLE, 1);
SetRenderState(CULLMODE, CULLBACK);
knotAtomic->render();
}
DemoScene KnotScene = {
"Torus knot",
KnotInit,
KnotTerm,
KnotUpdate,
KnotRender
};
+165
View File
@@ -0,0 +1,165 @@
#include <math.h>
#include "demoreel.h"
// Additive-blended particle vortex, camera-facing quads via im3d.
// The kind of thing the GS fillrate was made for.
using namespace rw;
#ifdef RW_PS2
#define NUMPARTS 600
#else
#define NUMPARTS 1200
#endif
struct Particle
{
float angle;
float radius;
float height;
float size;
float phase;
};
static Particle parts[NUMPARTS];
static RWDEVICE::Im3DVertex partVerts[NUMPARTS*4];
static uint16 partIndices[NUMPARTS*6];
static Texture *glowTex;
static Matrix identMat;
// cheap deterministic pseudo-random, no libc rand()
static float
frand(int n)
{
n = (n<<13) ^ n;
n = n*(n*n*15731 + 789221) + 1376312589;
return ((n & 0x7fffffff) / (float)0x7fffffff);
}
static void
ParticleInit(void)
{
int i;
glowTex = MakeGlowTexture();
identMat.setIdentity();
for(i = 0; i < NUMPARTS; i++){
Particle *p = &parts[i];
float u = (float)i/NUMPARTS;
p->radius = 4.0f + 30.0f*powf(u, 0.7f);
p->angle = i*2.3999632f + frand(i)*0.6f; // golden angle spiral
p->height = (frand(i*3+1) - 0.5f)*10.0f*expf(-p->radius/18.0f);
p->size = 0.5f + frand(i*7+2)*1.1f;
p->phase = frand(i*13+3)*6.28f;
}
for(i = 0; i < NUMPARTS; i++){
partIndices[i*6+0] = i*4+0;
partIndices[i*6+1] = i*4+1;
partIndices[i*6+2] = i*4+2;
partIndices[i*6+3] = i*4+0;
partIndices[i*6+4] = i*4+2;
partIndices[i*6+5] = i*4+3;
}
}
static void
ParticleTerm(void)
{
if(glowTex){
glowTex->destroy();
glowTex = nil;
}
}
static void
ParticleUpdate(float dt)
{
int i;
// differential rotation: inner particles spin faster
for(i = 0; i < NUMPARTS; i++){
Particle *p = &parts[i];
p->angle += dt*16.0f/(p->radius + 4.0f);
}
// slow orbiting camera
float ca = DemoTime*0.12f;
V3d campos;
campos.x = 55.0f*cosf(ca);
campos.y = 55.0f*sinf(ca);
campos.z = 14.0f + 10.0f*sinf(DemoTime*0.2f);
V3d origin = { 0.0f, 0.0f, 0.0f };
LookAt(Scene.camera->getFrame(), campos, origin);
// billboard the quads using the camera axes
Matrix *cm = Scene.camera->getFrame()->getLTM();
V3d right = cm->right;
V3d up = cm->up;
for(i = 0; i < NUMPARTS; i++){
Particle *p = &parts[i];
V3d pos;
pos.x = p->radius*cosf(p->angle);
pos.y = p->radius*sinf(p->angle);
pos.z = p->height + 0.8f*sinf(DemoTime*2.0f + p->phase);
float tw = 0.7f + 0.3f*sinf(DemoTime*5.0f + p->phase);
float sz = p->size*tw;
RGBA col = HSV(DemoTime*0.02f + p->radius*0.012f, 0.8f, tw, 255);
RWDEVICE::Im3DVertex *v = &partVerts[i*4];
V3d r = scale(right, sz);
V3d u = scale(up, sz);
V3d pu = add(pos, u);
V3d pd = sub(pos, u);
V3d c;
c = sub(pu, r);
v[0].setX(c.x); v[0].setY(c.y); v[0].setZ(c.z);
v[0].setU(0.0f); v[0].setV(0.0f);
c = add(pu, r);
v[1].setX(c.x); v[1].setY(c.y); v[1].setZ(c.z);
v[1].setU(1.0f); v[1].setV(0.0f);
c = add(pd, r);
v[2].setX(c.x); v[2].setY(c.y); v[2].setZ(c.z);
v[2].setU(1.0f); v[2].setV(1.0f);
c = sub(pd, r);
v[3].setX(c.x); v[3].setY(c.y); v[3].setZ(c.z);
v[3].setU(0.0f); v[3].setV(1.0f);
int j;
for(j = 0; j < 4; j++)
v[j].setColor(col.red, col.green, col.blue, col.alpha);
}
}
static void
ParticleRender(void)
{
SetRenderState(ZTESTENABLE, 1);
SetRenderState(ZWRITEENABLE, 0);
SetRenderState(SRCBLEND, BLENDONE);
SetRenderState(DESTBLEND, BLENDONE);
SetRenderState(CULLMODE, CULLNONE);
SetRenderState(TEXTUREFILTER, Texture::LINEAR);
SetRenderStatePtr(TEXTURERASTER, glowTex ? glowTex->raster : nil);
im3d::Transform(partVerts, NUMPARTS*4, &identMat, im3d::VERTEXUV);
im3d::RenderIndexedPrimitive(PRIMTYPETRILIST, partIndices, NUMPARTS*6);
im3d::End();
SetRenderState(ZWRITEENABLE, 1);
SetRenderState(SRCBLEND, BLENDSRCALPHA);
SetRenderState(DESTBLEND, BLENDINVSRCALPHA);
}
DemoScene ParticleScene = {
"Particle vortex",
ParticleInit,
ParticleTerm,
ParticleUpdate,
ParticleRender
};
+155
View File
@@ -0,0 +1,155 @@
#include <math.h>
#include "demoreel.h"
// Classic endless tunnel: fly along a wobbling closed loop,
// vertices regenerated on the CPU every frame, im3d rendering.
using namespace rw;
#define SIDES 16
#define RINGS 48
#define NUMVERTS ((SIDES+1)*(RINGS+1))
#define NUMINDICES (SIDES*RINGS*6)
#define LOOPRADIUS 60.0f
static RWDEVICE::Im3DVertex tunnelVerts[NUMVERTS];
static uint16 tunnelIndices[NUMINDICES];
static Texture *gridTex;
static Matrix identMat;
static float pathPos;
// closed loop in space with some vertical wobble
static V3d
tunnelPath(float s)
{
V3d p;
p.x = LOOPRADIUS*cosf(s) + 8.0f*cosf(3.0f*s);
p.y = LOOPRADIUS*sinf(s) + 8.0f*sinf(2.0f*s);
p.z = 14.0f*sinf(2.0f*s) + 5.0f*sinf(5.0f*s);
return p;
}
static float
tunnelRadius(float s)
{
return 6.0f + 1.5f*sinf(5.0f*s + DemoTime*2.0f);
}
static void
pathFrame(float s, V3d *pos, V3d *t, V3d *n, V3d *b)
{
static V3d worldup = { 0.0f, 0.0f, 1.0f };
*pos = tunnelPath(s);
*t = normalize(sub(tunnelPath(s+0.01f), *pos));
*n = normalize(cross(worldup, *t));
*b = cross(*t, *n);
}
static void
TunnelInit(void)
{
int i, j, v;
gridTex = MakeGridTexture();
identMat.setIdentity();
pathPos = 0.0f;
v = 0;
for(i = 0; i < RINGS; i++)
for(j = 0; j < SIDES; j++){
int r0 = i*(SIDES+1) + j;
int r1 = (i+1)*(SIDES+1) + j;
tunnelIndices[v++] = r0;
tunnelIndices[v++] = r1;
tunnelIndices[v++] = r0+1;
tunnelIndices[v++] = r0+1;
tunnelIndices[v++] = r1;
tunnelIndices[v++] = r1+1;
}
}
static void
TunnelTerm(void)
{
if(gridTex){
gridTex->destroy();
gridTex = nil;
}
}
static void
TunnelUpdate(float dt)
{
pathPos += dt*0.30f;
// camera rides the tunnel, slightly off center
V3d pos, t, n, b;
pathFrame(pathPos, &pos, &t, &n, &b);
V3d target = tunnelPath(pathPos + 0.15f);
pos = add(pos, scale(b, -1.5f*sinf(DemoTime*0.7f)));
pos = add(pos, scale(n, 1.5f*cosf(DemoTime*0.5f)));
LookAt(Scene.camera->getFrame(), pos, target);
// rebuild the tube
float ds = 0.028f;
float hue = DemoTime*0.03f;
// V texcoords must stay small: the GS wraps texel
// coordinates beyond ~1024, so shift out the integer part
float vscale = 4.0f;
float vbase = floorf(pathPos*vscale);
int i, j, v;
v = 0;
for(i = 0; i <= RINGS; i++){
float s = pathPos - ds + i*ds;
V3d c, tt, nn, bb;
pathFrame(s, &c, &tt, &nn, &bb);
float rad = tunnelRadius(s);
// brightness: fade out towards the far end, pulses running along
float fade = 1.0f - (float)i/RINGS;
fade = fade*fade;
float pulse = 0.55f + 0.45f*sinf(s*18.0f - DemoTime*6.0f);
float bright = fade*(0.35f + 0.65f*pulse);
RGBA col = HSV(hue + s*0.02f, 0.55f, bright, 255);
float twist = DemoTime*0.4f;
for(j = 0; j <= SIDES; j++){
float a = (float)j/SIDES*2.0f*3.14159265f + twist;
V3d p = add(c, add(scale(nn, rad*cosf(a)), scale(bb, rad*sinf(a))));
RWDEVICE::Im3DVertex *vert = &tunnelVerts[v++];
vert->setX(p.x);
vert->setY(p.y);
vert->setZ(p.z);
vert->setU((float)j/SIDES*4.0f);
vert->setV(s*vscale - vbase);
vert->setColor(col.red, col.green, col.blue, col.alpha);
}
}
}
static void
TunnelRender(void)
{
SetRenderState(ZTESTENABLE, 1);
SetRenderState(ZWRITEENABLE, 1);
SetRenderState(SRCBLEND, BLENDSRCALPHA);
SetRenderState(DESTBLEND, BLENDINVSRCALPHA);
SetRenderState(CULLMODE, CULLNONE);
SetRenderState(TEXTUREFILTER, Texture::LINEAR);
SetRenderStatePtr(TEXTURERASTER, gridTex ? gridTex->raster : nil);
im3d::Transform(tunnelVerts, NUMVERTS, &identMat, im3d::VERTEXUV|im3d::ALLOPAQUE);
im3d::RenderIndexedPrimitive(PRIMTYPETRILIST, tunnelIndices, NUMINDICES);
im3d::End();
}
DemoScene TunnelScene = {
"Tunnel",
TunnelInit,
TunnelTerm,
TunnelUpdate,
TunnelRender
};