Make progress on particle effects

This commit is contained in:
James Lambert 2023-09-12 21:43:55 -06:00
parent 7499075c65
commit cfb98555c5
8 changed files with 230 additions and 3 deletions

View file

@ -131,6 +131,9 @@ portal_pak_dir/materials/signage/indicator_lights/indicator_lights_corner_floor_
portal_pak_dir/materials/signage/indicator_lights/indicator_lights_floor_on.png: portal_pak_dir/materials/signage/indicator_lights/indicator_lights_floor.vtf
$(VTF2PNG) -f 2 $< $@
portal_pak_dir/materials/effects/portal_1_particle_orange.png: portal_pak_dir/materials/effects/portal_1_particle.vtf
$(VTF2PNG) -f 2 $< $@
convert_all_png: $(ALL_PNG_IMAGES)
portal_pak_dir/%_copy_0.png: portal_pak_dir/%.png

View file

@ -0,0 +1 @@
-resize 16x16

View file

@ -0,0 +1,168 @@
#include "splash_particle_effect.h"
#include "../math/mathf.h"
#include "../util/time.h"
#include "../math/vector2.h"
#include "../physics/config.h"
#include "../scene/dynamic_scene.h"
#include "../defs.h"
void splashParticleEffectBuildVtx(Vtx* vtx, struct SplashParticle* particle, int index) {
int posIndex = index >> 1;
int widthSign = index & 0x1;
struct Vector3 finalPos;
if (widthSign) {
vector3Add(&particle->position[posIndex], &particle->widthOffset, &finalPos);
} else {
vector3Sub(&particle->position[posIndex], &particle->widthOffset, &finalPos);
}
vtx->v.ob[0] = (short)(finalPos.x * SCENE_SCALE);
vtx->v.ob[1] = (short)(finalPos.y * SCENE_SCALE);
vtx->v.ob[2] = (short)(finalPos.z * SCENE_SCALE);
vtx->v.flag = 0;
vtx->v.tc[0] = posIndex ? 0 : (32 << 5);
vtx->v.tc[1] = widthSign ? 0 : (32 << 5);
vtx->v.cn[0] = 255;
vtx->v.cn[1] = 255;
vtx->v.cn[2] = 255;
vtx->v.cn[3] = 255;
}
void splashParticleEffectRender(void* data, struct DynamicRenderDataList* renderList, struct RenderState* renderState) {
struct SplashParticleEffect* effect = (struct SplashParticleEffect*)data;
Vtx* vertices = renderStateRequestVertices(renderState, effect->def->particleCount * 4);
Vtx* curr = vertices;
for (int i = 0; i < effect->def->particleCount; ++i) {
splashParticleEffectBuildVtx(&curr[0], &effect->particles[i], 0);
splashParticleEffectBuildVtx(&curr[1], &effect->particles[i], 1);
splashParticleEffectBuildVtx(&curr[2], &effect->particles[i], 2);
splashParticleEffectBuildVtx(&curr[3], &effect->particles[i], 3);
curr += 4;
}
Gfx* displayList = renderStateAllocateDLChunk(renderState, effect->def->particleCount + (effect->def->particleCount >> 5) + 1);
Gfx* dl = displayList;
for (int i = 0; i < effect->def->particleCount; ++i) {
int relativeVertex = i & 0x1f;
if (relativeVertex == 0) {
int verticesLeft = (effect->def->particleCount - i) << 2;
if (verticesLeft > 32) {
verticesLeft = 32;
}
gSPVertex(dl++, &vertices[i << 2], verticesLeft, 0);
}
gSP2Triangles(
dl++,
relativeVertex,
relativeVertex + 1,
relativeVertex + 2,
0,
relativeVertex + 2,
relativeVertex + 1,
relativeVertex + 3,
0
);
}
gSPEndDisplayList(dl++);
dynamicRenderListAddData(renderList, displayList, NULL, effect->def->materialIndex, &effect->startPosition, NULL);
}
void splashParticleEffectInit(struct SplashParticleEffect* effect) {
effect->def = NULL;
effect->dynamicId = INVALID_DYNAMIC_OBJECT;
}
void splashParticleEffectPlay(struct SplashParticleEffect* effect, struct SplashParticleDefinition* definiton, struct Vector3* origin, struct Vector3* normal) {
if (effect->dynamicId != INVALID_DYNAMIC_OBJECT) {
dynamicSceneRemove(effect->dynamicId);
}
effect->def = definiton;
effect->time = 0.0f;
struct Vector3 right;
struct Vector3 up;
vector3Perp(normal, &right);
vector3Normalize(&right, &right);
vector3Cross(normal, &right, &up);
for (int i = 0; i < effect->def->particleCount; ++i) {
struct SplashParticle* particle = &effect->particles[i];
struct Vector3 initialVelocity;
struct Vector2 tangentDir;
vector2RandomUnitCircle(&tangentDir);
float tangentMag = randomInRangef(definiton->minTangentVelocity, definiton->maxTangentVelocity);
float normalMag = randomInRangef(definiton->minNormalVelocity, definiton->maxNormalVelocity);
vector3Scale(normal, &initialVelocity, normalMag);
vector3AddScaled(&initialVelocity, &right, tangentDir.x * tangentMag, &initialVelocity);
vector3AddScaled(&initialVelocity, &up, tangentDir.y * tangentMag, &initialVelocity);
particle->position[1] = *origin;
vector3AddScaled(origin, &initialVelocity, definiton->particleTailDelay, &particle->position[0]);
vector3Cross(&initialVelocity, &gUp, &particle->widthOffset);
float widthMag = vector3MagSqrd(&particle->widthOffset);
if (widthMag < 0.00001f) {
vector3Scale(&gRight, &particle->widthOffset, definiton->particleHalfWidth);
particle->widthOffset = gRight;
} else {
vector3Scale(&particle->widthOffset, &particle->widthOffset, definiton->particleHalfWidth / sqrtf(widthMag));
}
}
effect->startPosition = *origin;
effect->dynamicId = dynamicSceneAdd(effect, splashParticleEffectRender, &effect->startPosition, 10.0f);
}
void splashParticleEffectUpdate(struct SplashParticleEffect* effect) {
if (!effect->def) {
return;
}
for (int i = 0; i < effect->def->particleCount; ++i) {
struct SplashParticle* particle = &effect->particles[i];
vector3AddScaled(&particle->position[0], &particle->velocity, FIXED_DELTA_TIME, &particle->position[0]);
vector3AddScaled(&particle->position[1], &particle->velocity, FIXED_DELTA_TIME, &particle->position[1]);
// this line simulates tracking the yvelocity of the tail separate
// without needing to track that velocity separately
// tailYVelocity = yVelocity - effect->def->particleTailDelay * GRAVITY_CONSTANT
// tailPos.y = tailPos.y + tailYVelocity * FIXED_DELTA_TIME
// tailPos.y = tailPos.y + (yVelocity - effect->def->particleTailDelay * GRAVITY_CONSTANT) * FIXED_DELTA_TIME
// tailPos.y = tailPos.y + yVelocity * FIXED_DELTA_TIME - effect->def->particleTailDelay * GRAVITY_CONSTANT * FIXED_DELTA_TIME
particle->position[1].y -= effect->def->particleTailDelay * (GRAVITY_CONSTANT * FIXED_DELTA_TIME);
particle->velocity.y += FIXED_DELTA_TIME * GRAVITY_CONSTANT;
}
effect->time += FIXED_DELTA_TIME;
if (effect->time >= effect->def->particleLifetime) {
effect->def = NULL;
dynamicSceneRemove(effect->dynamicId);
effect->dynamicId = INVALID_DYNAMIC_OBJECT;
}
}

View file

@ -0,0 +1,38 @@
#ifndef __SPLASH_PARTICLE_EFFECT_H__
#define __SPLASH_PARTICLE_EFFECT_H__
#include "../math/vector3.h"
struct SplashParticleDefinition {
float particleLifetime;
float particleTailDelay;
float minNormalVelocity;
float maxNormalVelocity;
float minTangentVelocity;
float maxTangentVelocity;
short particleCount;
short materialIndex;
float particleHalfWidth;
};
struct SplashParticle {
struct Vector3 position[2];
struct Vector3 velocity;
struct Vector3 widthOffset;
};
#define MAX_SPLASH_PARTICLES 16
struct SplashParticleEffect {
struct SplashParticleDefinition* def;
struct SplashParticle particles[MAX_SPLASH_PARTICLES];
struct Vector3 startPosition;
float time;
short dynamicId;
};
void splashParticleEffectInit(struct SplashParticleEffect* effect);
void splashParticleEffectPlay(struct SplashParticleEffect* effect, struct SplashParticleDefinition* definition, struct Vector3* origin, struct Vector3* normal);
void splashParticleEffectUpdate(struct SplashParticleEffect* effect);
#endif

View file

@ -14,8 +14,8 @@ int randomInRange(int min, int maxPlusOne) {
return randomInt() * (maxPlusOne - min) / (MAX_INT_VALUE + 1) + min;
}
float randomInRangef(float min, float maxPlusOne) {
return randomInt() * (maxPlusOne - min) / (MAX_INT_VALUE + 1) + min;
float randomInRangef(float min, float max) {
return randomInt() * (max - min) * (1.0f / MAX_INT_VALUE) + min;
}
float fabsf(float input) {

View file

@ -4,7 +4,7 @@
int randomInt();
int randomInRange(int min, int maxPlusOne);
float randomInRangef(float min, float maxPlusOne);
float randomInRangef(float min, float max);
float mathfLerp(float from, float to, float t);
float mathfInvLerp(float from, float to, float value);

View file

@ -121,4 +121,19 @@ void vector2Max(struct Vector2* a, struct Vector2* b, struct Vector2* out) {
void vector2Lerp(struct Vector2* a, struct Vector2* b, float lerp, struct Vector2* out) {
out->x = (b->x - a->x) * lerp + a->x;
out->y = (b->y - a->y) * lerp + a->y;
}
void vector2RandomUnitCircle(struct Vector2* result) {
float xSqrd = randomInRangef(0.0f, 1.0f);
result->x = sqrtf(xSqrd);
result->y = sqrtf(1.0f - xSqrd);
if (randomInt() & 0x800) {
result->x = -result->x;
}
if (randomInt() & 0x800) {
result->y = -result->y;
}
}

View file

@ -30,4 +30,6 @@ void vector2Max(struct Vector2* a, struct Vector2* b, struct Vector2* out);
void vector2Lerp(struct Vector2* a, struct Vector2* b, float lerp, struct Vector2* out);
void vector2RandomUnitCircle(struct Vector2* result);
#endif