//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "game/debris.h" #include "dgl/dgl.h" #include "core/bitStream.h" #include "math/mathUtils.h" #include "console/consoleTypes.h" #include "console/consoleObject.h" #include "sim/netConnection.h" #include "sceneGraph/sceneState.h" #include "sceneGraph/sceneGraph.h" #include "sceneGraph/detailManager.h" #include "ts/tsShapeInstance.h" #include "ts/tsPartInstance.h" #include "game/fx/particleEngine.h" #include "game/fx/explosion.h" const U32 csmStaticCollisionMask = TerrainObjectType | InteriorObjectType; const U32 csmDynamicCollisionMask = StaticShapeObjectType; //************************************************************************** // Debris Data //************************************************************************** IMPLEMENT_CO_DATABLOCK_V1(DebrisData); //-------------------------------------------------------------------------- // Constructor //-------------------------------------------------------------------------- DebrisData::DebrisData() { dMemset( emitterList, 0, sizeof( emitterList ) ); dMemset( emitterIDList, 0, sizeof( emitterIDList ) ); explosion = NULL; explosionId = 0; velocity = 0.0; velocityVariance = 0.0; elasticity = 0.3; friction = 0.2; numBounces = 0; bounceVariance = 0; minSpinSpeed = maxSpinSpeed = 0.0; render2D = false; staticOnMaxBounce = false; explodeOnMaxBounce = false; snapOnMaxBounce = false; lifetime = 3.0; lifetimeVariance = 0.0; minSpinSpeed = 0.0; maxSpinSpeed = 0.0; textureName = NULL; mTypeMask |= DebrisObjectType; shapeName = NULL; fade = true; useRadiusMass = false; baseRadius = 1.0; gravModifier = 1.0; terminalVelocity = 0.0; ignoreWater = true; } //-------------------------------------------------------------------------- // Initialize - Check data //-------------------------------------------------------------------------- bool DebrisData::onAdd() { if(!Parent::onAdd()) return false; for( int i=0; i velocity ) { Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: velocityVariance invalid", getName()); velocityVariance = velocity; } if( friction < -10.0 || friction > 10.0 ) { Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: friction invalid", getName()); friction = 0.2; } if( elasticity < -10.0 || elasticity > 10.0 ) { Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: elasticity invalid", getName()); elasticity = 0.2; } if( lifetime < 0.0 || lifetime > 1000.0 ) { Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetime invalid", getName()); lifetime = 3.0; } if( lifetimeVariance < 0.0 || lifetimeVariance > lifetime ) { Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetimeVariance invalid", getName()); lifetimeVariance = 0.0; } if( numBounces < 0 || numBounces > 10000 ) { Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: numBounces invalid", getName()); numBounces = 3; } if( bounceVariance < 0 || bounceVariance > numBounces ) { Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: bounceVariance invalid", getName()); bounceVariance = 0; } if( minSpinSpeed < -10000.0 || minSpinSpeed > 10000.0 || minSpinSpeed > maxSpinSpeed ) { Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: minSpinSpeed invalid", getName()); minSpinSpeed = maxSpinSpeed - 1.0; } if( maxSpinSpeed < -10000.0 || maxSpinSpeed > 10000.0 ) { Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: maxSpinSpeed invalid", getName()); maxSpinSpeed = 0.0; } return true; } //-------------------------------------------------------------------------- // Preload //-------------------------------------------------------------------------- bool DebrisData::preload(bool server, char errorBuffer[256]) { if (Parent::preload(server, errorBuffer) == false) return false; if( server ) return true; if( shapeName && shapeName[0] != '\0' && !bool(shape) ) { shape = ResourceManager->load(shapeName); if( bool(shape) == false ) { dSprintf(errorBuffer, sizeof(errorBuffer), "DebrisData::load: Couldn't load shape \"%s\"", shapeName); return false; } else { TSShapeInstance* pDummy = new TSShapeInstance(shape, !server); delete pDummy; } } return true; } //-------------------------------------------------------------------------- // Initialize console fields (static) //-------------------------------------------------------------------------- IMPLEMENT_CONSOLETYPE(DebrisData) IMPLEMENT_SETDATATYPE(DebrisData) IMPLEMENT_GETDATATYPE(DebrisData) void DebrisData::initPersistFields() { Parent::initPersistFields(); addGroup("Display"); addField("texture", TypeFilename, Offset(textureName, DebrisData)); addField("shapeFile", TypeFilename, Offset(shapeName, DebrisData)); addField("render2D", TypeBool, Offset(render2D, DebrisData)); endGroup("Display"); addGroup("Datablocks"); addField("emitters", TypeParticleEmitterDataPtr, Offset(emitterList, DebrisData), DDC_NUM_EMITTERS); addField("explosion", TypeExplosionDataPtr, Offset(explosion, DebrisData)); endGroup("Datablocks"); addGroup("Physical Properties"); addField("elasticity", TypeF32, Offset(elasticity, DebrisData)); addField("friction", TypeF32, Offset(friction, DebrisData)); addField("numBounces", TypeS32, Offset(numBounces, DebrisData)); addField("bounceVariance", TypeS32, Offset(bounceVariance, DebrisData)); addField("minSpinSpeed", TypeF32, Offset(minSpinSpeed, DebrisData)); addField("maxSpinSpeed", TypeF32, Offset(maxSpinSpeed, DebrisData)); addField("gravModifier", TypeF32, Offset(gravModifier, DebrisData)); addField("terminalVelocity", TypeF32, Offset(terminalVelocity, DebrisData)); addField("velocity", TypeF32, Offset(velocity, DebrisData)); addField("velocityVariance", TypeF32, Offset(velocityVariance, DebrisData)); addField("lifetime", TypeF32, Offset(lifetime, DebrisData)); addField("lifetimeVariance", TypeF32, Offset(lifetimeVariance, DebrisData)); addField("useRadiusMass", TypeBool, Offset(useRadiusMass, DebrisData)); addField("baseRadius", TypeF32, Offset(baseRadius, DebrisData)); endGroup("Physical Properties"); addGroup("Behavior"); addField("explodeOnMaxBounce", TypeBool, Offset(explodeOnMaxBounce, DebrisData)); addField("staticOnMaxBounce", TypeBool, Offset(staticOnMaxBounce, DebrisData)); addField("snapOnMaxBounce", TypeBool, Offset(snapOnMaxBounce, DebrisData)); addField("fade", TypeBool, Offset(fade, DebrisData)); addField("ignoreWater", TypeBool, Offset(ignoreWater, DebrisData)); endGroup("Behavior"); } //-------------------------------------------------------------------------- // Pack data //-------------------------------------------------------------------------- void DebrisData::packData(BitStream* stream) { Parent::packData(stream); stream->write(elasticity); stream->write(friction); stream->write(numBounces); stream->write(bounceVariance); stream->write(minSpinSpeed); stream->write(maxSpinSpeed); stream->write(render2D); stream->write(explodeOnMaxBounce); stream->write(staticOnMaxBounce); stream->write(snapOnMaxBounce); stream->write(lifetime); stream->write(lifetimeVariance); stream->write(minSpinSpeed); stream->write(maxSpinSpeed); stream->write(velocity); stream->write(velocityVariance); stream->write(fade); stream->write(useRadiusMass); stream->write(baseRadius); stream->write(gravModifier); stream->write(terminalVelocity); stream->write(ignoreWater); stream->writeString( textureName ); stream->writeString( shapeName ); for( int i=0; iwriteFlag( emitterList[i] != NULL ) ) { stream->writeRangedU32( emitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); } } if( stream->writeFlag( explosion ) ) { stream->writeRangedU32(packed? SimObjectId(explosion): explosion->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast); } } //-------------------------------------------------------------------------- // Unpack data //-------------------------------------------------------------------------- void DebrisData::unpackData(BitStream* stream) { Parent::unpackData(stream); stream->read(&elasticity); stream->read(&friction); stream->read(&numBounces); stream->read(&bounceVariance); stream->read(&minSpinSpeed); stream->read(&maxSpinSpeed); stream->read(&render2D); stream->read(&explodeOnMaxBounce); stream->read(&staticOnMaxBounce); stream->read(&snapOnMaxBounce); stream->read(&lifetime); stream->read(&lifetimeVariance); stream->read(&minSpinSpeed); stream->read(&maxSpinSpeed); stream->read(&velocity); stream->read(&velocityVariance); stream->read(&fade); stream->read(&useRadiusMass); stream->read(&baseRadius); stream->read(&gravModifier); stream->read(&terminalVelocity); stream->read(&ignoreWater); textureName = stream->readSTString(); shapeName = stream->readSTString(); for( int i=0; ireadFlag() ) { emitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); } } if(stream->readFlag()) { explosionId = (S32)stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); } else { explosionId = 0; } } //************************************************************************** // Debris //************************************************************************** IMPLEMENT_CO_NETOBJECT_V1(Debris); //---------------------------------------------------------------------------- // Initialize debris piece //---------------------------------------------------------------------------- ConsoleMethod( Debris, init, bool, 4, 4, "(Point3F position, Point3F velocity)" "Set this piece of debris at the given position with the given velocity.") { Point3F pos; dSscanf( argv[2], "%g %g %g", &pos.x, &pos.y, &pos.z ); Point3F vel; dSscanf( argv[3], "%g %g %g", &vel.x, &vel.y, &vel.z ); object->init( pos, vel ); return true; } //---------------------------------------------------------------------------- // Constructor //---------------------------------------------------------------------------- Debris::Debris() { mTypeMask |= DebrisObjectType; mVelocity = Point3F( 0.0, 0.0, 4.0 ); mLifetime = gRandGen.randF( 1.0, 10.0 ); mLastPos = getPosition(); mNumBounces = gRandGen.randI( 0, 1 ); mSize = 2.0; mElapsedTime = 0.0; mShape = NULL; mPart = NULL; mXRotSpeed = 0.0; mZRotSpeed = 0.0; mInitialTrans.identity(); mRadius = 0.2; mStatic = false; dMemset( mEmitterList, 0, sizeof( mEmitterList ) ); } //---------------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------------- Debris::~Debris() { if( mShape ) { delete mShape; mShape = NULL; } if( mPart ) { delete mPart; mPart = NULL; } } //---------------------------------------------------------------------------- // Init class (static) //---------------------------------------------------------------------------- void Debris::initPersistFields() { addGroup("Misc"); addField("lifetime", TypeF32, Offset(mLifetime, Debris)); endGroup("Misc"); } //---------------------------------------------------------------------------- // Init //---------------------------------------------------------------------------- void Debris::init( const Point3F &position, const Point3F &velocity ) { setPosition( position ); setVelocity( velocity ); } //---------------------------------------------------------------------------- // On new data block //---------------------------------------------------------------------------- bool Debris::onNewDataBlock( GameBaseData* dptr ) { mDataBlock = dynamic_cast< DebrisData* >( dptr ); if( !mDataBlock || !Parent::onNewDataBlock( dptr ) ) return false; scriptOnNewDataBlock(); return true; } //---------------------------------------------------------------------------- // On Add //---------------------------------------------------------------------------- bool Debris::onAdd() { // first check if we have a server connection, if we dont then this is on the server // and we should exit, then check if the parent fails to add the object NetConnection* conn = NetConnection::getConnectionToServer(); if(!conn || !Parent::onAdd()) return false; F32 lifeVar = (mDataBlock->lifetimeVariance * 2.0f * gRandGen.randF(-1.0,1.0)) - mDataBlock->lifetimeVariance; mLifetime = mDataBlock->lifetime + lifeVar; // create emitters for( int i=0; iemitterList[i] != NULL ) { ParticleEmitter * pEmitter = new ParticleEmitter; pEmitter->onNewDataBlock( mDataBlock->emitterList[i] ); if( !pEmitter->registerObject() ) { Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() ); delete pEmitter; pEmitter = NULL; } mEmitterList[i] = pEmitter; } } // set particle sizes based on debris size F32 sizeList[ParticleEngine::PC_SIZE_KEYS]; if( mEmitterList[0] ) { sizeList[0] = mSize * 0.5; sizeList[1] = mSize; sizeList[2] = mSize * 1.5; mEmitterList[0]->setSizes( sizeList ); } if( mEmitterList[1] ) { sizeList[0] = 0.0; sizeList[1] = mSize * 0.5; sizeList[2] = mSize; mEmitterList[1]->setSizes( sizeList ); } S32 bounceVar = gRandGen.randI( -mDataBlock->bounceVariance, mDataBlock->bounceVariance ); mNumBounces = mDataBlock->numBounces + bounceVar; F32 xRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed ); F32 zRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed ); zRotSpeed *= gRandGen.randF( 0.1, 0.5 ); mRotAngles.set( xRotSpeed, 0.0, zRotSpeed ); mElasticity = mDataBlock->elasticity; mFriction = mDataBlock->friction; // Setup our bounding box if( mDataBlock->shape ) { mObjBox = mDataBlock->shape->bounds; } else { mObjBox = Box3F(Point3F(-1, -1, -1), Point3F(1, 1, 1)); } if( mDataBlock->shape ) { mShape = new TSShapeInstance( mDataBlock->shape, true); } if( mPart ) { // use half radius becuase we want debris to stick in ground mRadius = mPart->getRadius() * 0.5; mObjBox = mPart->getBounds(); } resetWorldBox(); mInitialTrans = getTransform(); if( mDataBlock->velocity != 0.0 ) { F32 velocity = mDataBlock->velocity + gRandGen.randF( -mDataBlock->velocityVariance, mDataBlock->velocityVariance ); mVelocity.normalizeSafe(); mVelocity *= velocity; } // mass calculations if( mDataBlock->useRadiusMass ) { if( mRadius < mDataBlock->baseRadius ) mRadius = mDataBlock->baseRadius; // linear falloff F32 multFactor = mDataBlock->baseRadius / mRadius; mElasticity *= multFactor; mFriction *= multFactor; mRotAngles *= multFactor; } // tell engine the debris exists gClientContainer.addObject(this); gClientSceneGraph->addObjectToScene(this); removeFromProcessList(); gClientProcessList.addObject(this); conn->addObject(this); return true; } //---------------------------------------------------------------------------- // On Remove //---------------------------------------------------------------------------- void Debris::onRemove() { for( int i=0; ideleteWhenEmpty(); mEmitterList[i] = NULL; } } if( mPart ) { TSShapeInstance *ss = mPart->getSourceShapeInstance(); if( ss ) { ss->decDebrisRefCount(); if( ss->getDebrisRefCount() == 0 ) { delete ss; } } } mSceneManager->removeObjectFromScene(this); getContainer()->removeObject(this); Parent::onRemove(); } //---------------------------------------------------------------------------- // Process tick //---------------------------------------------------------------------------- void Debris::processTick(const Move*) { // Update real position info. setTransform(getRenderTransform()); if (mLifetime <= 0.0) deleteObject(); } //---------------------------------------------------------------------------- // Advance Time //---------------------------------------------------------------------------- void Debris::advanceTime( F32 dt ) { mElapsedTime += dt; mLifetime -= dt; if( mLifetime <= 0.0 ) { mLifetime = 0.0; return; } mLastPos = getRenderPosition(); if( !mStatic ) { rotate( dt ); Point3F nextPos = getRenderPosition(); computeNewState( nextPos, mVelocity, dt ); if( bounce( nextPos, dt ) ) { --mNumBounces; if( mNumBounces <= 0 ) { if( mDataBlock->explodeOnMaxBounce ) { explode(); mLifetime = 0.0; } if( mDataBlock->snapOnMaxBounce ) { // orient debris so it's flat MatrixF stat = getRenderTransform(); Point3F dir; stat.getColumn( 1, &dir ); dir.z = 0.0; MatrixF newTrans = MathUtils::createOrientFromDir( dir ); // hack for shell casings to get them above ground. Need something better - bramage newTrans.setPosition( getPosition() + Point3F( 0.0, 0.0, 0.10 ) ); setRenderTransform( newTrans ); } if( mDataBlock->staticOnMaxBounce ) { mStatic = true; } } } else { MatrixF tmp = getRenderTransform(); tmp.setPosition( nextPos ); setRenderTransform(tmp); } } updateEmitters( (Point3F&)*getRenderPosition(), mVelocity, (U32)(dt * 1000.0)); } //---------------------------------------------------------------------------- // Rotate debris //---------------------------------------------------------------------------- void Debris::rotate( F32 dt ) { MatrixF curTrans = getRenderTransform(); curTrans.setPosition( Point3F(0.0, 0.0, 0.0) ); Point3F curAngles = mRotAngles * dt * M_PI/180.0; MatrixF rotMatrix( EulerF( curAngles.x, curAngles.y, curAngles.z ) ); curTrans.mul( rotMatrix ); curTrans.setPosition( getRenderPosition() ); setRenderTransform( curTrans ); } //---------------------------------------------------------------------------- // Bounce the debris - returns true if debris bounces //---------------------------------------------------------------------------- bool Debris::bounce( const Point3F &nextPos, F32 dt ) { Point3F curPos = getRenderPosition(); Point3F dir = nextPos - curPos; if( dir.magnitudeSafe() == 0.0 ) return false; dir.normalizeSafe(); Point3F extent = nextPos + dir * mRadius; F32 totalDist = Point3F( extent - curPos ).magnitudeSafe(); F32 moveDist = Point3F( nextPos - curPos ).magnitudeSafe(); F32 movePercent = (moveDist / totalDist); RayInfo rayInfo; U32 collisionMask = csmStaticCollisionMask; if( !mDataBlock->ignoreWater ) collisionMask |= WaterObjectType; if( getContainer()->castRay( curPos, extent, collisionMask, &rayInfo ) ) { Point3F reflection = mVelocity - rayInfo.normal * (mDot( mVelocity, rayInfo.normal ) * 2.0); mVelocity = reflection; Point3F tangent = reflection - rayInfo.normal * mDot( reflection, rayInfo.normal ); mVelocity -= tangent * mFriction; Point3F velDir = mVelocity; velDir.normalizeSafe(); mVelocity *= mElasticity; Point3F bouncePos = curPos + dir * rayInfo.t * movePercent; bouncePos += mVelocity * dt; MatrixF tmp = getRenderTransform(); tmp.setPosition( bouncePos ); setRenderTransform(tmp); mRotAngles *= mElasticity; return true; } return false; } //---------------------------------------------------------------------------- // Explode //---------------------------------------------------------------------------- void Debris::explode() { if( !mDataBlock->explosion ) return; const Point3F explosionPos = getRenderPosition(); Explosion* pExplosion = new Explosion; pExplosion->onNewDataBlock(mDataBlock->explosion); MatrixF trans( true ); trans.setPosition( explosionPos ); pExplosion->setTransform( trans ); pExplosion->setInitialState( explosionPos, VectorF(0,0,1), 1); if (!pExplosion->registerObject()) delete pExplosion; } //---------------------------------------------------------------------------- // Compute state of debris as if it hasn't collided with anything //---------------------------------------------------------------------------- void Debris::computeNewState( Point3F &newPos, Point3F &newVel, F32 dt ) { // apply gravity Point3F force = Point3F(0, 0, -9.81 * mDataBlock->gravModifier ); if( mDataBlock->terminalVelocity > 0.0001 && newVel.magnitudeSafe() > mDataBlock->terminalVelocity ) { newVel.normalizeSafe(); newVel *= mDataBlock->terminalVelocity; } else { newVel += force * dt; } newPos += newVel * dt; } //---------------------------------------------------------------------------- // Update emitters //---------------------------------------------------------------------------- void Debris::updateEmitters( Point3F &pos, Point3F &vel, U32 ms ) { Point3F axis = -vel; Point3F lastPos = mLastPos; if( axis.magnitudeSafe() == 0.0 ) axis = Point3F( 0.0, 0.0, 1.0 ); axis.normalizeSafe(); for( int i=0; iemitParticles( lastPos, pos, axis, vel, ms ); } } } //---------------------------------------------------------------------------- // Render debris //---------------------------------------------------------------------------- bool Debris::prepRenderImage(SceneState* state, const U32 stateKey, const U32 /*startZone*/, const bool /*modifyBaseState*/) { if (isLastState(state, stateKey)) return false; setLastState(state, stateKey); // This should be sufficient for most objects that don't manage zones, and // don't need to return a specialized RenderImage... if( state->isObjectRendered(this) && (mPart || mShape) ) { Point3F cameraOffset; mObjToWorld.getColumn(3,&cameraOffset); cameraOffset -= state->getCameraPosition(); F32 dist = cameraOffset.len(); F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z)); if( mShape ) { DetailManager::selectPotentialDetails(mShape,dist,invScale); if( mShape->getCurrentDetail() < 0 ) { return false; } } if( mPart ) { DetailManager::selectPotentialDetails(mPart,dist,invScale); } SceneRenderImage* image = new SceneRenderImage; image->obj = this; image->isTranslucent = true; image->sortType = SceneRenderImage::Point; state->setImageRefPoint(this, image); state->insertRenderImage(image); } return false; } //---------------------------------------------------------------------------- // Render Object //---------------------------------------------------------------------------- void Debris::renderObject(SceneState* state, SceneRenderImage* ) { AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry"); RectI viewport; glMatrixMode(GL_PROJECTION); glPushMatrix(); dglGetViewport(&viewport); // Uncomment this if this is a "simple" (non-zone managing) object state->setupObjectProjection(this); F32 alpha = 1.0; if( mDataBlock->fade && mLifetime < 1.0 ) alpha = mLifetime; if( (mShape && DetailManager::selectCurrentDetail(mShape)) || (mPart && DetailManager::selectCurrentDetail(mPart)) ) { glMatrixMode(GL_MODELVIEW); glPushMatrix(); dglMultMatrix(&mObjToWorld); glScalef(mObjScale.x, mObjScale.y, mObjScale.z); setTransform(getRenderTransform()); mLightingInfo.mDirty = true; gClientSceneGraph->getLightManager()->sgSetupLights(this); Point3F cameraOffset; mObjToWorld.getColumn(3,&cameraOffset); cameraOffset -= state->getCameraPosition(); F32 fogAmount = state->getHazeAndFog(cameraOffset.len(),cameraOffset.z); if( mShape ) { TSMesh::setOverrideFade( alpha ); mShape->setupFog(fogAmount, state->getFogColor()); mShape->render(); TSMesh::setOverrideFade( 1.0 ); } else { if (mPart->getCurrentObjectDetail() != -1) { TSShapeInstance *parent = mPart->getSourceShapeInstance(); parent->setupFog(fogAmount, state->getFogColor()); TSMesh::setOverrideFade( alpha ); mPart->render(); TSMesh::setOverrideFade( 1.0 ); } } gClientSceneGraph->getLightManager()->sgResetLights(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); } render2D(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); dglSetViewport(viewport); dglSetCanonicalState(); AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit"); } //---------------------------------------------------------------------------- // Render 2D debris //---------------------------------------------------------------------------- void Debris::render2D() { if( !mDataBlock->render2D ) return; glBindTexture( GL_TEXTURE_2D, mDataBlock->texture.getGLName() ); glColor4f( 1.0, 1.0, 1.0, 1.0 ); glDisable(GL_CULL_FACE); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glEnable(GL_TEXTURE_2D); dglDrawBillboard( getPosition(), 0.1, 0.0 ); glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } //---------------------------------------------------------------------------- // Set size //---------------------------------------------------------------------------- void Debris::setSize( F32 size ) { mSize = size; }