⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 particle.cpp

📁 wowmodelview魔兽世界的模型查看工具。下了看看吧
💻 CPP
字号:
#include "particle.h"
#include "util.h"

#define MAX_PARTICLES 10000

Vec4D fromARGB(uint32 color)
{
	const float a = ((color & 0xFF000000) >> 24) / 255.0f;
	const float r = ((color & 0x00FF0000) >> 16) / 255.0f;
	const float g = ((color & 0x0000FF00) >>  8) / 255.0f;
	const float b = ((color & 0x000000FF)      ) / 255.0f;
    return Vec4D(r,g,b,a);
}

template<class T>
T lifeRamp(float life, float mid, const T &a, const T &b, const T &c)
{
	if (life<=mid) 
		return interpolate<T>(life / mid,a,b);
	else 
		return interpolate<T>((life-mid) / (1.0f-mid),b,c);
}


void ParticleSystem::init(MPQFile &f, ModelParticleEmitterDef &mta, int *globals)
{
	speed.init	 (mta.params[0], f, globals);
	variation.init(mta.params[1], f, globals);
	spread.init	 (mta.params[2], f, globals);
	lat.init	 (mta.params[3], f, globals);
	gravity.init (mta.params[4], f, globals);
	lifespan.init(mta.params[5], f, globals);
	rate.init	 (mta.params[6], f, globals);
	areal.init	 (mta.params[7], f, globals);
	areaw.init	 (mta.params[8], f, globals);
	grav2.init	 (mta.params[9], f, globals);
	enabled.init (mta.en,        f, globals);

	
	for (size_t i=0; i<3; i++) {
		colors[i] = fromARGB(mta.p.colors[i]);
		sizes[i] = mta.p.sizes[i];// * mta.p.scales[i];
	}
	mid = mta.p.mid;
	slowdown = mta.p.slowdown;
	rotation = mta.p.rotation;
	//pos = fixCoordSystem(mta.pos);
	pos = fixCoordSystem(mta.pos);
	texture = model->textures[mta.texture];
	blend = mta.blend;
	rows = mta.rows;
	cols = mta.cols;
	type = mta.s1;
	//order = mta.s2;
	order = mta.s1>0 ? -1 : 0;
	parent = model->bones + mta.bone;
    
	switch (mta.type) {
	case 1:
		emitter = new PlaneParticleEmitter(this);
		break;
	case 2:
		emitter = new SphereParticleEmitter(this);
		break;
	}

	//transform = mta.flags & 1024;
	
	billboard = !(mta.flags & 4096);

	manim = mtime = 0;
	rem = 0;

	tofs = frand();

	// init tiles
	for (int i=0; i<rows*cols; i++) {
		TexCoordSet tc;
		initTile(tc.tc,i);
		tiles.push_back(tc);
	}
	
}

void ParticleSystem::initTile(Vec2D *tc, int num)
{
	Vec2D otc[4];
	Vec2D a,b;
	int x = num % cols;
	int y = num / cols;
	a.x = x * (1.0f / cols);
	b.x = (x+1) * (1.0f / cols);
	a.y = y * (1.0f / rows);
	b.y = (y+1) * (1.0f / rows);

	otc[0] = a;
	otc[2] = b;
	otc[1].x = b.x;
	otc[1].y = a.y;
	otc[3].x = a.x;
	otc[3].y = b.y;

	for (int i=0; i<4; i++) {
		tc[(i+4-order) & 3] = otc[i];
	}
}


void ParticleSystem::update(float dt)
{
	float grav = gravity.getValue(manim, mtime);

	// spawn new particles
	if (emitter) {
		float frate = rate.getValue(manim, mtime);
		float flife = 1.0f;
		//flife = lifespan.getValue(manim, mtime);

		float ftospawn = (dt * frate / flife) + rem;
		if (ftospawn < 1.0f) {
			rem = ftospawn;
			if (rem<0) 
				rem = 0;
		} else {
			int tospawn = (int)ftospawn;
			rem = ftospawn - (float)tospawn;
			//rem = 0;
			for (int i=0; i<tospawn; i++) {
				bool en = enabled.getValue(manim, mtime)!=0;

				if (en) {
					Particle p = emitter->newParticle(manim, mtime);
					// sanity check:
					if (particles.size() < MAX_PARTICLES) particles.push_back(p);
				}
			}
		}
	}

	float mspeed = 1.0f;

	for (ParticleList::iterator it = particles.begin(); it != particles.end(); ) {
		Particle &p = *it;
		p.speed += p.down * grav * dt;

		if (slowdown>0) {
			mspeed = expf(-1.0f * slowdown * p.life);
		}
		p.pos += p.speed * mspeed * dt;
		
		p.life += dt;
		float rlife = p.life / p.maxlife;
		// calculate size and color based on lifetime
		p.size = lifeRamp<float>(rlife, mid, sizes[0], sizes[1], sizes[2]);
		p.color = lifeRamp<Vec4D>(rlife, mid, colors[0], colors[1], colors[2]);

		// kill off old particles
		if (rlife >= 1.0f) particles.erase(it++);
		else ++it;
	}
}

void ParticleSystem::setup(int anim, int time)
{
	manim = anim;
	mtime = time;

	/*
	if (transform) {
		// transform every particle by the parent trans matrix   - apparently this isn't needed
		Matrix m = parent->mat;
		for (ParticleList::iterator it = particles.begin(); it != particles.end(); ++it) {
			it->tpos = m * it->pos;
		}
	} else {
		for (ParticleList::iterator it = particles.begin(); it != particles.end(); ++it) {
			it->tpos = it->pos;
		}
	}
	*/
	
}

void ParticleSystem::draw()
{
	Vec3D bv0,bv1,bv2,bv3;

	// setup blend mode
	switch (blend) {
	case 0:
		glDisable(GL_BLEND);
		glDisable(GL_ALPHA_TEST);
		break;
	case 1:
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_COLOR, GL_ONE);
		glDisable(GL_ALPHA_TEST);
		break;
	case 2:
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glDisable(GL_ALPHA_TEST);
		break;
	case 3:
		glDisable(GL_BLEND);
		glEnable(GL_ALPHA_TEST);
		break;
	case 4:
		glEnable(GL_BLEND);
		glDisable(GL_ALPHA_TEST);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE);
		break;
	}
	
	//glDisable(GL_LIGHTING);
	//glDisable(GL_CULL_FACE);
	//glDepthMask(GL_FALSE);

	glBindTexture(GL_TEXTURE_2D, texture);

	Matrix mbb;	mbb.unit();
	if (billboard) {
		// get a billboard matrix
		Matrix mtrans;		glGetFloatv(GL_MODELVIEW_MATRIX, &(mtrans.m[0][0]));		mtrans.transpose();		mtrans.invert();		Vec3D camera = mtrans * Vec3D(0,0,0);		Vec3D look = (camera - pos).normalize();		Vec3D up = ((mtrans * Vec3D(0,1,0)) - camera).normalize();		Vec3D right = (up % look).normalize();		up = (look % right).normalize();		// calculate the billboard matrix		mbb.m[0][1] = right.x;		mbb.m[1][1] = right.y;		mbb.m[2][1] = right.z;		mbb.m[0][2] = up.x;		mbb.m[1][2] = up.y;		mbb.m[2][2] = up.z;		mbb.m[0][0] = look.x;		mbb.m[1][0] = look.y;		mbb.m[2][0] = look.z;	}	if (type==0 || type==2) {		// TODO: figure out type 2 (deeprun tram subway sign)		// - doesn't seem to be any different from 0 -_-		// regular particles		float f = 0.707106781f; // sqrt(2)/2		if (billboard) {			bv0 = mbb * Vec3D(0,-f,+f);			bv1 = mbb * Vec3D(0,+f,+f);			bv2 = mbb * Vec3D(0,+f,-f);			bv3 = mbb * Vec3D(0,-f,-f);		} else {			bv0 = Vec3D(-f,0,+f);			bv1 = Vec3D(+f,0,+f);			bv2 = Vec3D(+f,0,-f);			bv3 = Vec3D(-f,0,-f);		}		// TODO: per-particle rotation in a non-expensive way?? :|		glBegin(GL_QUADS);		for (ParticleList::iterator it = particles.begin(); it != particles.end(); ++it) {
			glColor4fv(it->color);

			glTexCoord2fv(tiles[it->tile].tc[0]);
			glVertex3fv(it->pos + bv0 * it->size);

			glTexCoord2fv(tiles[it->tile].tc[1]);
			glVertex3fv(it->pos + bv1 * it->size);

			glTexCoord2fv(tiles[it->tile].tc[2]);
			glVertex3fv(it->pos + bv2 * it->size);

			glTexCoord2fv(tiles[it->tile].tc[3]);
			glVertex3fv(it->pos + bv3 * it->size);
		}
		glEnd();
	} else if (type==1) {
		// particles from origin to position
		bv0 = mbb * Vec3D(0,-1.0f,0);
		bv1 = mbb * Vec3D(0,1.0f,0);

		glBegin(GL_QUADS);		for (ParticleList::iterator it = particles.begin(); it != particles.end(); ++it) {
			glColor4fv(it->color);

			glTexCoord2fv(tiles[it->tile].tc[0]);
			glVertex3fv(it->pos + bv0 * it->size);

			glTexCoord2fv(tiles[it->tile].tc[1]);
			glVertex3fv(it->pos + bv1 * it->size);

			glTexCoord2fv(tiles[it->tile].tc[2]);
			glVertex3fv(it->origin + bv1 * it->size);

			glTexCoord2fv(tiles[it->tile].tc[3]);
			glVertex3fv(it->origin + bv0 * it->size);
		}
		glEnd();
	}

	//glEnable(GL_LIGHTING);
	glEnable(GL_ALPHA_TEST);
	glDisable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	//glDepthMask(GL_TRUE);
	//glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
}

Particle PlaneParticleEmitter::newParticle(int anim, int time)
{
    Particle p;
	// TODO: maybe evaluate these outside the spawn function, since they will be common for a given frame?
	float spr = sys->spread.getValue(anim, time);
	float w = sys->areal.getValue(anim, time) * spr;
	float l = sys->areaw.getValue(anim, time) * spr;
	float spd = sys->speed.getValue(anim, time);
	float var = sys->variation.getValue(anim, time);

	// what the butter!
	// The new halo of transcendence particle effect has no width nor height
	// but its suppose to look like a circular glowing halo
	// This would make sense under a SphereParticleEmitter.. but here!?

	if (l==0 && w==0 && spr==0 && var==1.0f) { // If all these values are zero,  assume its squared ?
		float t = randfloat(0,2*PI);
		Vec3D z = Vec3D(0.0f, sys->pos.y + 0.15f, sys->pos.z);  // Need to manually correct for the halo - why?

		p.pos = z + Vec3D(cos(t)/8, 0.0f, sin(t)/8);

		var /= 2; // since we're manually correcting all this just for the halo, may aswell do this aswell.

		// var isn't being used, which is set to 1.0f,  whats the importance of this?
		// why does this set of values differ from other particles

	} else {
		p.pos = sys->pos + Vec3D(randfloat(-l, l), 0.0f, randfloat(-w,w));
		
		if (sys->parent->parent == 0) // Needed for items that have particles attached via visualeffectsdb.
			p.pos = sys->parent->pivot * p.pos;
		else //if (sys->parent->parent != 0)
			p.pos = sys->parent->mat * p.pos;
	}

	Vec3D dir = Vec3D(0.0f, 1.0f, 0.0f);

	if (sys->parent->parent == 0) {
		dir = sys->model->bones[sys->parent->parent].mrot * sys->parent->mrot * dir;
		p.speed = dir.normalize() * spd * randfloat(0, var*2);
	} else {
		//dir = sys->parent->mrot * dir;
		p.speed = dir.normalize() * (spd / 2);
	}

	p.down = Vec3D(0.0f, -1.0f, 0.0f); /* dir * -1.0f; */

	p.life = 0;
	p.maxlife = sys->lifespan.getValue(anim, time);

	p.origin = p.pos;

	p.tile = randint(0, sys->rows*sys->cols-1);
	return p;
}

Particle SphereParticleEmitter::newParticle(int anim, int time)
{
    Particle p;
	float l = sys->areal.getValue(anim, time);
	float w = sys->areaw.getValue(anim, time);
	float spd = sys->speed.getValue(anim, time);
	float var = sys->variation.getValue(anim, time);
	float spr = sys->spread.getValue(anim, time);
	
	// TODO: fix shpere emitters to work properly

	// Spread should never be zero for sphere particles
	float t;
	if (spr == 0)
		t = randfloat(0,2*PI);
	else
		t = randfloat(-spr,spr);
	
	// Length should never technically be zero ?
	if (l==0)
		l = w;

	//Vec3D bdir(0,0,0);
	//Vec3D bdir(l*cosf(t), 0, w*sinf(t));
	//Vec3D bdir(w*cosf(t), l*sinf(t), 0);
	Vec3D bdir(w*cosf(t), 0.0f, l*sinf(t));

	/*
	float theta_range = sys->spread.getValue(anim, time);
	float theta = -0.5f* theta_range + randfloat(0, theta_range);
	Vec3D bdir(0, l*cosf(theta), w*sinf(theta));

	float phi_range = sys->lat.getValue(anim, time);
	float phi = randfloat(0, phi_range);
	rotate(0,0, &bdir.z, &bdir.x, phi);
	*/

	p.pos = sys->pos + bdir;
	p.pos = sys->parent->mat * p.pos;

	if (bdir.lengthSquared()==0) 
		p.speed = Vec3D(0.0f, 0.0f, 0.0f);
	else {
		//Vec3D dir = sys->parent->mrot * (bdir.normalize());
		Vec3D dir = bdir.normalize();
		p.speed = dir.normalize() * spd * (1.0f + randfloat(-var,var));   // ?
		//p.speed = dir.normalize() * spd * (1.0f + randfloat(-var, var)); 
	}

	//p.down = sys->parent->mrot * Vec3D(0,-1.0f,0);
	p.down = Vec3D(0,-1.0f,0);

	p.life = 0;
	p.maxlife = sys->lifespan.getValue(anim, time);

	p.origin = p.pos;

	p.tile = randint(0, sys->rows*sys->cols-1);
	return p;
}




void RibbonEmitter::init(MPQFile &f, ModelRibbonEmitterDef &mta, int *globals)
{
	color.init(mta.color, f, globals);
	opacity.init(mta.opacity, f, globals);
	above.init(mta.above, f, globals);
	below.init(mta.below, f, globals);

	parent = model->bones + mta.bone;
	int *texlist = (int*)(f.getBuffer() + mta.ofsTextures);
	// just use the first texture for now; most models I've checked only had one
	texture = model->textures[texlist[0]];

	tpos = pos = fixCoordSystem(mta.pos);
	//tpos = pos = (parent->mat * fixCoordSystem(mta.pos));

	// TODO: figure out actual correct way to calculate length
	// in BFD, res is 60 and len is 0.6, the trails are very short (too long here)
	// in CoT, res and len are like 10 but the trails are supposed to be much longer (too short here)
	numsegs = (int)mta.res;
	seglen = mta.unk;
	//length = mta.res * seglen;
	length = mta.length;

	// create first segment
	RibbonSegment rs;
	rs.pos = tpos; //parent->mat * tpos;
	rs.len = ((parent->mat * pos) - tpos).length();
	segs.push_back(rs);
}

void RibbonEmitter::setup(int anim, int time)
{
	Vec3D ntpos = parent->mat * pos;
	Vec3D ntup = parent->mat * (pos + Vec3D(0,0,1));
	//Vec3D ntpos = pos;
	//Vec3D ntup = (pos * Vec3D(0,0,1));

	ntup -= ntpos;
	ntup.normalize();
	float dlen = (ntpos-tpos).length();

	manim = anim;
	mtime = time;

	// move first segment
	RibbonSegment &first = *segs.begin();
	if (first.len > seglen) {
		// add new segment
		first.back = (tpos-ntpos).normalize();
		first.len0 = first.len;
		RibbonSegment newseg;
		newseg.pos = ntpos;
		newseg.up = ntup;
		newseg.len = dlen;
		segs.push_front(newseg);
	} else {
		first.up = ntup;
		first.pos = ntpos;
		first.len += dlen;
	}

	// kill stuff from the end
	float l = 0;
	bool erasemode = false;
	
	for (std::list<RibbonSegment>::iterator it = segs.begin(); it != segs.end(); ++it) {
		if (!erasemode) {
			l += it->len;
			//if (l > length) {
			if (l > numsegs) {
				//it->len = l - length;
				//erasemode = true;
				segs.clear();
				break;
			}
			//++it;
		} else {
			segs.erase(it++);
		}
	}

	tpos = ntpos;
	tcolor = Vec4D(color.getValue(anim, time), opacity.getValue(anim, time));
	
	tabove = above.getValue(anim, time);
	tbelow = below.getValue(anim, time);
}

void RibbonEmitter::draw()
{
	/*
	// placeholders
	glDisable(GL_TEXTURE_2D);
	glDisable(GL_LIGHTING);
	glColor4f(1,1,1,1);
	glBegin(GL_TRIANGLES);
	glVertex3fv(tpos);
	glVertex3fv(tpos + Vec3D(1,1,0));
	glVertex3fv(tpos + Vec3D(-1,1,0));
	glEnd();
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_LIGHTING);
	*/

	glBindTexture(GL_TEXTURE_2D, texture);
	glEnable(GL_BLEND);
	//glDisable(GL_LIGHTING);
	glDisable(GL_ALPHA_TEST);
	//glDisable(GL_CULL_FACE);
	//glDepthMask(GL_FALSE);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
	glColor4fv(tcolor);

	glBegin(GL_QUAD_STRIP);
	std::list<RibbonSegment>::iterator it = segs.begin();
	++it;
	float l = 0;
	for (; it != segs.end(); ++it) {
        float u = l/length;

		glTexCoord2f(u,0);
		glVertex3fv(it->pos + tabove * it->up);
		glTexCoord2f(u,1);
		glVertex3fv(it->pos - tbelow * it->up);

		l += it->len;
	}
	
	if (segs.size() > 1) {
		// last segment...?
		--it;
		glTexCoord2f(1.0f, 0.0f);
		glVertex3fv(it->pos + tabove * it->up + (it->len/it->len0) * it->back);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3fv(it->pos - tbelow * it->up + (it->len/it->len0) * it->back);
	}
	
	glEnd();

	//glColor4f(1.0f,1.0f,1.0f,1.0f);

	//glEnable(GL_LIGHTING);
	glEnable(GL_ALPHA_TEST);
	glDisable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	//glDepthMask(GL_TRUE);
}





⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -