mirror of
https://github.com/processing/processing4.git
synced 2026-02-03 05:39:18 +01:00
160 lines
5.0 KiB
Plaintext
160 lines
5.0 KiB
Plaintext
// Rocket, by Andres Colubri.
|
|
// This example shows the OBJ load functionality and the use of
|
|
// the PShape3D class to create a particle system.
|
|
// Rocket model from http://keleb.free.fr/codecorner/models-en.htm
|
|
|
|
PShape3D rocket;
|
|
PShape3D particles;
|
|
int[] lifetimes;
|
|
|
|
int numParticlesTotal = 1200;
|
|
int numParticlesPerFrame = 10;
|
|
int particleLifetime;
|
|
|
|
float time;
|
|
float timeInc = 0.03;
|
|
|
|
PVector axis, pos, vel;
|
|
|
|
void setup() {
|
|
size(800, 600, P3D);
|
|
|
|
rocket = (PShape3D)loadShape("rocket.obj");
|
|
// Adjusting the size, orientation and position of the object.
|
|
// The bulk scale, rotate and translate operations done on the
|
|
// shape are permanent.
|
|
rocket.scaleVertices(0.2);
|
|
rocket.rotateVerticesX(-PI);
|
|
rocket.translateVertices(-5, 25, 0);
|
|
|
|
// The particle system is stored in a PShape3D object set to
|
|
// POINT_SPRITES mode
|
|
PShape3D.Parameters params = PShape3D.newParameters(POINT_SPRITES, STREAM);
|
|
particles = (PShape3D)createShape(numParticlesTotal, params);
|
|
particleLifetime = numParticlesTotal / numParticlesPerFrame;
|
|
lifetimes = new int[numParticlesTotal];
|
|
|
|
// Loading and setting the sprite image.
|
|
PImage sprite = loadImage("smoke.png");
|
|
particles.setTexture(sprite);
|
|
// The default maximum sprite size is determined by the graphics
|
|
// hardware (it usually ranges between 32 and 128 pixels).
|
|
println("Maximum sprite size: " + particles.getMaxSpriteSize());
|
|
// The maximum size can be set with setMaxSpriteSize(), but will be
|
|
// capped by the maximum size supported by the hardware.
|
|
particles.setMaxSpriteSize(32);
|
|
// The actual sprite size depends on the distance d from the sprite
|
|
// to the camera as follows:
|
|
// s = smax / (1 + c * d * d) (quadratic dependence on d)
|
|
// or
|
|
// s = smax / (1 + c * d) (linear dependence on d)
|
|
// where smax is the maximum sprite size and c an adjustable constant.
|
|
// In the next call, the constant is adjusted so that the actual sprite
|
|
// size is 10 when the distance is 400. A quadratic dependence on d is used.
|
|
particles.setSpriteSize(10, 400, QUADRATIC);
|
|
// PShape3D objects automatically update their bounding boxes, but we don't
|
|
// want this for the particle system object.
|
|
particles.autoBounds(false);
|
|
particles.setColor(color(0)); // Making sure that all particles start as black.
|
|
|
|
// Initialzing particles with negative lifetimes so they are added
|
|
// progresively into the scene during the first frames of the program
|
|
int t = -1;
|
|
for (int i = 0; i < numParticlesTotal; i++) {
|
|
if (i % numParticlesPerFrame == 0) {
|
|
t++;
|
|
}
|
|
lifetimes[i] = -t;
|
|
}
|
|
|
|
pos = new PVector(0, 0, 0);
|
|
vel = new PVector(0, 0, 0);
|
|
|
|
// The rocket object is originally aligned to the Y axis. We
|
|
// use this vector to calculate the rotation needed to make
|
|
// the rocket aligned to the velocity vector
|
|
axis = new PVector(0, 1, 0);
|
|
}
|
|
|
|
void draw() {
|
|
background(0);
|
|
|
|
updatePosition();
|
|
updateParticles();
|
|
|
|
ambient(250, 250, 250);
|
|
pointLight(255, 255, 255, 500, height/2, 400);
|
|
|
|
pushMatrix();
|
|
translate(pos.x, pos.y, pos.z);
|
|
|
|
PVector rotAxis = axis.cross(vel);
|
|
float angle = PVector.angleBetween(axis, vel);
|
|
rotate(angle, rotAxis.x, rotAxis.y, rotAxis.z);
|
|
|
|
rotateY(2 * time);
|
|
|
|
shape(rocket);
|
|
popMatrix();
|
|
|
|
// The particles are not lit.
|
|
noLights();
|
|
|
|
// Writing to the depth mask is disabled to avoid rendering artifacts due
|
|
// to the fact that the particles are transparent but not depth sorted
|
|
hint(DISABLE_DEPTH_MASK);
|
|
shape(particles);
|
|
hint(ENABLE_DEPTH_MASK);
|
|
|
|
time += timeInc;
|
|
}
|
|
|
|
void updatePosition() {
|
|
pos.x = width/2 + 150 * cos(time);
|
|
pos.y = 50 + height/2 + 200 * cos(2 * time);
|
|
pos.z = 150 + 200 * sin(time);
|
|
|
|
vel.x = 150 * sin(time);
|
|
vel.y = 400 * sin(2 * time);
|
|
vel.z = -150 * cos(time);
|
|
}
|
|
|
|
void updateParticles() {
|
|
// Respawn dead particles
|
|
particles.loadVertices();
|
|
for (int i = 0; i < numParticlesTotal; i++) {
|
|
if (lifetimes[i] == 0) {
|
|
particles.vertices[3 * i + 0] = random(pos.x - 8, pos.x + 8);
|
|
particles.vertices[3 * i + 1] = random(pos.y - 8, pos.y + 8);
|
|
particles.vertices[3 * i + 2] = random(pos.z - 8, pos.z + 8);
|
|
}
|
|
}
|
|
particles.updateVertices();
|
|
|
|
// Update colors and lifetimes of particles
|
|
particles.loadColors();
|
|
for (int i = 0; i < numParticlesTotal; i++) {
|
|
if (0 <= lifetimes[i]) {
|
|
// Interpolating between alpha 1 to 0:
|
|
float a = 1.0 - float(lifetimes[i]) / particleLifetime;
|
|
|
|
// Interpolating from orange to white during the first
|
|
// quarter of the particle's life (the color shoud be specified
|
|
// in normalized RGBA components)
|
|
float f = min(float(lifetimes[i]) / (0.25 * particleLifetime), 1);
|
|
particles.colors[4 * i + 0] = (1 - f) * 0.98 + f;
|
|
particles.colors[4 * i + 1] = (1 - f) * 0.75 + f;
|
|
particles.colors[4 * i + 2] = (1 - f) * 0.26 + f;
|
|
particles.colors[4 * i + 3] = a;
|
|
|
|
}
|
|
lifetimes[i]++;
|
|
if (lifetimes[i] == particleLifetime) {
|
|
lifetimes[i] = 0;
|
|
}
|
|
}
|
|
particles.updateColors();
|
|
}
|
|
|
|
|