home *** CD-ROM | disk | FTP | other *** search
- #define __MILETOS_PARTICLE_EMITTER_CPP__
-
- //
- // Libmiletos
- //
- // Copyright (C) Lauris Kaplinski 2007
- //
-
- #include <malloc.h>
- #include <stdlib.h>
-
- #include <elea/line.h>
-
- #include <sehle/engine.h>
- #include <sehle/graph.h>
- #include <sehle/light.h>
- #include <sehle/renderable.h>
- #include <sehle/commonmaterials.h>
-
- #include "xml/base.h"
- #include "sphere.h"
-
- #include "particleemitter.h"
-
- namespace Miletos {
-
- // ParticleEmitter
-
- ParticleEmitter::ParticleEmitter (void)
- : Item(0), radius(0.25f), numlights(0), lights(NULL), vertices(NULL), nindices(0), indices(NULL),
- cutoffDistance(2), constantAttenuation(1), linearAttenuation(0), quadraticAttenuation(64),
- maxparticles(100), numparticles(0), coords(NULL), velocities(NULL), ages(NULL), colors(NULL),
- lasttime(0), lastcreation(0)
- {
- static u32 ival[] = { 0, 1, 2, 0, 2, 3, 1, 0, 4, 1, 4, 5, 2, 1, 5, 2, 5, 6, 3, 2, 6, 3, 6, 7, 0, 3, 7, 0, 7, 4, 5, 4, 7, 5, 7, 6 };
- nindices = 12 * 3;
- if (!indices) indices = (u32 *) malloc (nindices * sizeof (u32));
- memcpy (indices, ival, nindices * sizeof (u32));
-
- coords = (Elea::Vector3f *) malloc (maxparticles * sizeof (Elea::Vector3f));
- memset (coords, 0, maxparticles * sizeof (Elea::Vector3f));
- velocities = (Elea::Vector3f *) malloc (maxparticles * sizeof (Elea::Vector3f));
- memset (velocities, 0, maxparticles * sizeof (Elea::Vector3f));
- ages = (float *) malloc (maxparticles * sizeof (float));
- memset (ages, 0, maxparticles * sizeof (float));
- colors = (Elea::Color4f *) malloc (maxparticles * sizeof (Elea::Color4f));
- memset (colors, 0, maxparticles * sizeof (Elea::Color4f));
- }
-
- ParticleEmitter::~ParticleEmitter (void)
- {
- if (vertices) free (vertices);
- if (indices) free (indices);
- if (coords) free (coords);
- if (velocities) free (velocities);
- if (ages) free (ages);
- if (colors) free (colors);
- }
-
- static Object *
- particleemitter_factory (void)
- {
- return new ParticleEmitter();
- }
-
- const Object::Type *
- ParticleEmitter::objectType (void)
- {
- return type ();
- }
-
- const Object::Type *
- ParticleEmitter::type (void)
- {
- static Type *mytype = NULL;
- if (!mytype) mytype = new Type(Item::type (), "ParticleEmitter", "particleEmitter", particleemitter_factory, 0, NULL);
- return mytype;
- }
-
- void
- ParticleEmitter::build (Thera::Node *pnode, Document *doc, BuildCtx *ctx)
- {
- Item::build (pnode, doc, ctx);
-
- document->addTimer (this);
- }
-
- void
- ParticleEmitter::release (void)
- {
- document->removeTimer (this);
-
- Item::release ();
- }
-
- void
- ParticleEmitter::set (const char *attrid, const char *val)
- {
- Item::set (attrid, val);
- }
-
- void
- ParticleEmitter::write (const char *attrid)
- {
- Item::write (attrid);
- }
-
- void
- ParticleEmitter::update (UpdateCtx *ctx, unsigned int flags)
- {
- Elea::Cuboid3f q(-radius, -radius, -radius, radius, radius, radius);
- bbox.set (ctx->i2w.transform (q));
-
- Item::update (ctx, flags);
-
- updateVolume ();
- if (lights) updateLights ();
- }
-
- Sehle::Renderable *
- ParticleEmitter::show (Sehle::Graph *graph, Sehle::u32 contextmask)
- {
- // Create Sehle light block
- lights = graph->newPointLights (maxparticles, 0);
- numlights = maxparticles;
- updateLights ();
-
- // Create mesh
- Sehle::StaticMesh *mesh = new Sehle::StaticMesh(graph, contextmask);
-
- int nvertices;
- int nindices;
- Sphere::generateMesh (NULL, 0, NULL, 0, NULL, 0, NULL, radius, 3, 1, nvertices, nindices);
- if (!mesh->vbuffer) mesh->vbuffer = mesh->graph->engine->getVertexBuffer (NULL);
- mesh->vbuffer->setUp (nvertices, 8);
- mesh->vbuffer->setOffset (Sehle::VertexBuffer::COORDINATES, 0);
- mesh->vbuffer->setOffset (Sehle::VertexBuffer::NORMALS, 3);
- mesh->vbuffer->setOffset (Sehle::VertexBuffer::TEXCOORDS, 6);
- Sehle::f32 *attributes = mesh->vbuffer->map (Sehle::VertexBuffer::WRITE);
- if (!mesh->ibuffer) mesh->ibuffer = mesh->graph->engine->getIndexBuffer (NULL);
- mesh->ibuffer->resize (nindices);
- Sehle::u32 *indices = mesh->ibuffer->map (Sehle::IndexBuffer::WRITE);
- Sphere::generateMesh (attributes, mesh->vbuffer->stride * sizeof (Sehle::f32),
- attributes + 3, mesh->vbuffer->stride * sizeof (Sehle::f32),
- attributes + 6, mesh->vbuffer->stride * sizeof (Sehle::f32),
- indices, radius, 3, 1, nvertices, nindices);
- mesh->vbuffer->unMap ();
- mesh->ibuffer->unMap ();
-
- mesh->resizeMaterials (1);
- // mesh->setMaterial (0, WireMaterial::newWireMaterial (engine, NULL));
- mesh->setMaterial (0, Sehle::ColorMaterial::newColorMaterial (mesh->graph->engine, NULL));
- mesh->resizeFragments (1);
- mesh->frags[0].first = 0;
- mesh->frags[0].nindices = nindices;
- mesh->frags[0].matidx = 0;
-
- return mesh;
- }
-
- void
- ParticleEmitter::hide (Sehle::RenderableGroup *pgroup)
- {
- numlights = 0;
- graph->deletePointLights ((Sehle::PointLight *) lights);
- lights = NULL;
-
- Item::hide (pgroup);
- }
-
- Item *
- ParticleEmitter::trace (const Elea::Line3f *wray, unsigned int mask, float *distance)
- {
- Elea::Cuboid3f bbox(-radius, -radius, -radius, radius, radius, radius);
- Elea::Line3f rl = _w2i.transform (*wray);
-
- // i2w.transformInPlace (bbox);
- float p0, p1;
- if (bbox.getIntersection (rl, p0, p1) && (p0 > 0)) {
- *distance = p0;
- return this;
- }
-
- return NULL;
- }
-
- void
- ParticleEmitter::updateLights (void)
- {
- if (!graph) return;
- if (numlights != maxparticles) {
- if (numlights) {
- graph->deletePointLights ((Sehle::PointLight *) lights);
- lights = NULL;
- }
- if (maxparticles) {
- lights = graph->newPointLights (maxparticles, 0);
- }
- numlights = maxparticles;
- }
- for (u32 i = 0; i < numparticles; i++) {
- Sehle::PointLight *point = (Sehle::PointLight *) &lights[i];
- point->highlightmask = Miletos::Item::OPAQUE | TRANSPARENT;
- point->hasshadow = 0;
- point->hasdensity = 0;
- point->ambient = Elea::Color4fBlack;
- float intensity = pow (1 / (1 + exp (ages[i] - 8)), 8);
- point->diffuse = Elea::Color4f(intensity * colors[i][0], intensity * colors[i][1], intensity * colors[i][2], 1);
- point->direct = Elea::Color4fBlack;
- point->vertices = vertices;
- point->nindices = nindices;
- point->indices = indices;
- // fixme: Use parameter for bound/free particles
- point->l2w = Elea::Matrix4x4f::translation (coords[i]);
- point->constantAttenuation = constantAttenuation;
- point->linearAttenuation = linearAttenuation;
- point->quadraticAttenuation = quadraticAttenuation;
- }
- }
-
- void
- ParticleEmitter::updateVolume (void)
- {
- float maxdist = cutoffDistance;
- // maxdist = 20;
- if ((quadraticAttenuation >= 0) && ((linearAttenuation > 0) || (quadraticAttenuation > 0))) {
- // Find distance where I1 == 0.001
- // constAtt + linAtt * d + quadAtt * d * d = 1000
- double a = quadraticAttenuation;
- double b = linearAttenuation;
- double c = constantAttenuation - 1000;
- double q = b * b - 4 * a * c;
- if (q >= 0) {
- double distance = (-b + sqrt (q)) / (2 * a);
- if (distance < maxdist) maxdist = (float) distance;
- }
- }
- float l = maxdist;
-
- if (!vertices) vertices = (Elea::Vector3f *) malloc (8 * sizeof (Elea::Vector3f));
- vertices[0].set (-l, -l, -l);
- vertices[1].set (l, -l, -l);
- vertices[2].set (l, l, -l);
- vertices[3].set (-l, l, -l);
- vertices[4].set (-l, -l, l);
- vertices[5].set (l, -l, l);
- vertices[6].set (l, l, l);
- vertices[7].set (-l, l, l);
- }
-
- void
- ParticleEmitter::start (double time)
- {
- lasttime = time;
- numlights = 0;
- updateLights ();
- }
-
- void
- ParticleEmitter::timeStep (double time)
- {
- // fixme:
- if (!lasttime) {
- lasttime = time;
- lastcreation = time;
- return;
- }
- u32 i = 0;
- float dtime = (float) (time - lasttime);
- while (i < numparticles) {
- if ((ages[i] + dtime) > 10) {
- // Retire
- coords[i] = coords[numparticles - 1];
- velocities[i] = velocities[numparticles - 1];
- ages[i] = ages[numparticles - 1];
- numparticles -= 1;
- } else {
- coords[i] = coords[i] + dtime * velocities[i];
- ages[i] += dtime;
- i += 1;
- }
- }
- float dcreation = (float) (time - lastcreation);
- for (float c = 0.2f; c < dcreation; c += 0.2f) {
- if (numparticles >= maxparticles) break;
- coords[numparticles] = _i2w.getTranslation ();
- float x = (float) ((double) rand () / RAND_MAX) - 0.5f;
- float y = (float) ((double) rand () / RAND_MAX) - 0.5f;
- // float z = (float) ((double) rand () / RAND_MAX);
- x = x * 4;
- y = y * 4;
- velocities[numparticles] = Elea::Vector3f(x, y, 0);
- ages[numparticles] = 0;
- float r = (float) ((double) rand () / RAND_MAX);
- float g = (float) ((double) rand () / RAND_MAX);
- float b = (float) ((double) rand () / RAND_MAX);
- float max = r;
- if (g > max) max = g;
- if (b > max) max = b;
- r = r * 10 / max;
- g = g * 10 / max;
- b = b * 10 / max;
- colors[numparticles] = Elea::Color4f(r, g, b, 1);
-
- numparticles += 1;
- lastcreation = time;
- }
- lasttime = time;
- updateLights ();
- }
-
- void
- ParticleEmitter::stop (double time)
- {
- numlights = 0;
- updateLights ();
- }
-
- } // Namespace Miletos
-