Towards turning materials into materials
by Kirk Longendyke · in Torque Game Engine Advanced · 07/13/2007 (4:55 am) · 23 replies
(this is gonna get a bit spammy. apologies)
First up, let me start out by making it verry clear: this is a nasty, filthy, hacky kludge of a work in progress, and isn't likely to see the inside of a recource for quite a while at this rate.
That being said, till there's an official writeup on how to do this type of thing, more than willing to see if this doesn't start the ball rolling on something better.
So, with the disclaimer out of the way, on to the meat of the endeavor:
Theres several things that the current material system is highly defective for in terms of asset interactivity, that we'll be looking to adress as this goes on, namely:
1- decal placement on a per-material basis
2- sounds likewise
3- general expolosive behavior(you'll see what i mean by that one shortly)
4- damage modification
5- elasticity/friction modification
First up, let me start out by making it verry clear: this is a nasty, filthy, hacky kludge of a work in progress, and isn't likely to see the inside of a recource for quite a while at this rate.
That being said, till there's an official writeup on how to do this type of thing, more than willing to see if this doesn't start the ball rolling on something better.
So, with the disclaimer out of the way, on to the meat of the endeavor:
Theres several things that the current material system is highly defective for in terms of asset interactivity, that we'll be looking to adress as this goes on, namely:
1- decal placement on a per-material basis
2- sounds likewise
3- general expolosive behavior(you'll see what i mean by that one shortly)
4- damage modification
5- elasticity/friction modification
#2
shapebase.h
shapebase.cc
(we'll be revisiting this file in a bit, but for now, let's just get to the point of grabbing faces)
07/13/2007 (4:56 am)
Shapebase Derivations:shapebase.h
//add #include "materials/materialList.h" #include "materials/matInstance.h" #include "materials/material.h" bool castRay( const Point3F& start, const Point3F& end, TriRayInfo* rayInfo ); //endadd
shapebase.cc
//add #include "materials/materialList.h" #include "materials/matInstance.h" //endadd
bool ShapeBase::castRay( const Point3F& start, const Point3F& end, TriRayInfo* rayInfo )
{
TSShapeInstance* si = this->getShapeInstance();
if ( !si || !si->getShape() || si->getShape()->details.empty() )
return false;
TSShape* tsShape = si->getShape();
Point3F tstart, tend;
MatrixF mat = this->getWorldTransform();
mat.mulP(start,&tstart);
mat.mulP(end,&tend);
si->setStatics(0);
rayInfo->distance = F32_MAX;
const TSDetail& detail = tsShape->details[0];
S32 ss = detail.subShapeNum;
S32 od = detail.objectDetailNum;
S32 first = tsShape->subShapeFirstObject[ss];
S32 last = tsShape->subShapeFirstObject[ss] + tsShape->subShapeNumObjects[ss];
for ( S32 m=first; m < last; m++ )
{
TSMesh* mesh = tsShape->meshes[ m ];
if ( !mesh || mesh->getMeshType() != TSMesh::StandardMeshType )
continue;
TriRayInfo info;
if ( mesh->castRayTri( 0, tstart, tend, &info ) && info.distance < rayInfo->distance )
{
*rayInfo = info;
// Ok... we got a hit... cast back into world
// space and return it.
MatrixF invmat( mat );
invmat.inverse();
invmat.mulP(rayInfo->point);
// Find the material for this surface.
MatInstance* matInst = tsShape->materialList->getMaterialInst( rayInfo->material );
if ( matInst && matInst->getMaterial() ) {
rayInfo->material = matInst->getMaterial()->getId();
} else {
rayInfo->material = 0;
}
}
}
si->clearStatics();
return rayInfo->distance < F32_MAX;
}(we'll be revisiting this file in a bit, but for now, let's just get to the point of grabbing faces)
#3
Nasty Hack Alert:
you really probably shouldn't use an array as we'll be setting up here, but for now, it's a quick, simple, and above all clear method for setting up a material functionality index
materials.h:
class Material : public SimObject
(you'll notice that says FX, not sound, not gfx, ect. this is because for right now, higher complications will just serve to overcomplicate matters. Besides, I've never had call to use more than 32 types of material on a level, when you get right down to it)
07/13/2007 (4:57 am)
So ok. at this point, you've got a way to grab the material from an object. time to make that mean something, by adding some meaningfull data:Nasty Hack Alert:
you really probably shouldn't use an array as we'll be setting up here, but for now, it's a quick, simple, and above all clear method for setting up a material functionality index
materials.h:
//add #define MAX_MAT_FX 32 //endadd
class Material : public SimObject
//add S32 mFXIndex; //endadd
(you'll notice that says FX, not sound, not gfx, ect. this is because for right now, higher complications will just serve to overcomplicate matters. Besides, I've never had call to use more than 32 types of material on a level, when you get right down to it)
#4
First up, we'll be hitting on a verry specific case, then move on more general one that's still giving me a few issues.
For our first game, were focusing mostly on a simple little racing style to knock the kinks out of the vehiclular collisions, and generally all arround polish things up as far as the engine allows. One of the under-rated things folks don't usually notice unless it's missing is things like tire tracks, so heres a setup for that:
07/13/2007 (4:57 am)
You. Yeah, you in the back rolling your eyes. Time to wake on up, because now were getting to the fun parts: actually doing something with that mess up top.First up, we'll be hitting on a verry specific case, then move on more general one that's still giving me a few issues.
For our first game, were focusing mostly on a simple little racing style to knock the kinks out of the vehiclular collisions, and generally all arround polish things up as far as the engine allows. One of the under-rated things folks don't usually notice unless it's missing is things like tire tracks, so heres a setup for that:
#5
wheeledvehicle.h
in
struct WheeledVehicleTire: public SimDataBlock
in
struct WheeledVehicleData: public VehicleData
wheeledvehicletire.cc/.cpp:
WheeledVehicleTire::WheeledVehicleTire()
bool WheeledVehicleTire::preload(bool server, char errorBuffer[256])
(place inside the if (shapeName && shapeName[0]) loop)
void WheeledVehicleTire::initPersistFields()
I'm going to take a moment to emphasise this one, as it's one of the more overlooked functions that the engine comes with stock:
moving right along:
void WheeledVehicleTire::unpackData(BitStream* stream)
WheeledVehicleData::WheeledVehicleData()
bool WheeledVehicleData::preload(bool server, char errorBuffer[256])
void WheeledVehicleData::initPersistFields()
void WheeledVehicleData::packData(BitStream* stream)
void WheeledVehicleData::unpackData(BitStream* stream)
bool WheeledVehicle::onNewDataBlock(GameBaseData* dptr)
alter
to
07/13/2007 (4:58 am)
Data storage:wheeledvehicle.h
in
struct WheeledVehicleTire: public SimDataBlock
//add DecalData *decalData[MAX_MAT_FX]; S32 decalID[MAX_MAT_FX]; //endadd
in
struct WheeledVehicleData: public VehicleData
//add ParticleEmitterData* tireEmitter[MAX_MAT_FX]; //endadd
wheeledvehicletire.cc/.cpp:
WheeledVehicleTire::WheeledVehicleTire()
//add
for (int i = 0; i<MAX_MAT_FX; i++)
{
decalData[i] = NULL;
decalID[i] = 0;
}
//endaddbool WheeledVehicleTire::preload(bool server, char errorBuffer[256])
(place inside the if (shapeName && shapeName[0]) loop)
//add
for (int i = 0; i<MAX_MAT_FX; i++)
{
if (!decalData[i] && decalID[i] != 0 )
if (!Sim::findObject(decalID[i], decalData[i]))
Con::errorf(ConsoleLogEntry::General, "WheeledVehicleTire::preload Invalid packet, bad datablockId(decalData)[%i]: 0x%x",i, decalID);
}
//endaddvoid WheeledVehicleTire::initPersistFields()
I'm going to take a moment to emphasise this one, as it's one of the more overlooked functions that the engine comes with stock:
//add
addField("decalData", TypeSimObjectPtr, Offset(decalData, WheeledVehicleTire), MAX_MAT_FX);
//endaddif you'll refference the shapeimmage source, you'll notice similar functinality for the various states things can be used with.moving right along:
void WheeledVehicleTire::packData(BitStream* stream)
//add
for (int i = 0; i<MAX_MAT_FX; i++)
{
if( stream->writeFlag(decalData[i]))
{
stream->writeRangedU32( decalData[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
}
//endaddvoid WheeledVehicleTire::unpackData(BitStream* stream)
//add
for (int i = 0; i<MAX_MAT_FX; i++)
{
if( stream->readFlag() )
{
decalID[i] = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
}
}
//endaddWheeledVehicleData::WheeledVehicleData()
//add for (int i=0; i<MAX_MAT_FX; i++) tireEmitter[i] = 0; //endadd
bool WheeledVehicleData::preload(bool server, char errorBuffer[256])
//add
for (int i=0; i<MAX_MAT_FX; i++)
if (tireEmitter[i])
Sim::findObject(SimObjectId(tireEmitter[i]),tireEmitter[i]);
//endaddvoid WheeledVehicleData::initPersistFields()
//add
addField("tireEmitter", TypeParticleEmitterDataPtr, Offset(tireEmitter, WheeledVehicleData), MAX_MAT_FX);
//endaddvoid WheeledVehicleData::packData(BitStream* stream)
//add for (int i=0; i<MAX_MAT_FX; i++) if (stream->writeFlag(tireEmitter[i])) stream->writeRangedU32(packed? SimObjectId(tireEmitter[i]): tireEmitter[i]->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast); //endadd
void WheeledVehicleData::unpackData(BitStream* stream)
//add for (int i=0; i<MAX_MAT_FX; i++) tireEmitter[i] = stream->readFlag()? (ParticleEmitterData*) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast): 0; //endadd
bool WheeledVehicle::onNewDataBlock(GameBaseData* dptr)
alter
// Each wheel get's it's own particle emitter
if (mDataBlock->tireEmitter) {
wheel->emitter = new ParticleEmitter;
wheel->emitter->onNewDataBlock(mDataBlock->tireEmitter);to
// Each wheel get's it's own particle emitter
if (mDataBlock->tireEmitter[0]) {
wheel->emitter = new ParticleEmitter;
wheel->emitter->onNewDataBlock(mDataBlock->tireEmitter[0]);
#6
void WheeledVehicle::updateWheelParticles(F32 dt)
alter
and be sure to place a disableCollision();/enableCollision(); pair at rhe begining and end of the function
07/13/2007 (4:58 am)
Aaaaaaaand finally:void WheeledVehicle::updateWheelParticles(F32 dt)
alter
if (speed > 1.0f)
{
MaterialPropertyMap* matMap = MaterialPropertyMap::get();
Point3F axis = vel;
axis.normalize();
// Only emit dust on the terrain
Wheel* wend = &mWheel[mDataBlock->wheelCount];
for (Wheel* wheel = mWheel; wheel < wend; wheel++)
{
// Is this wheel in contact with the ground?
if (wheel->tire && wheel->spring && !wheel->emitter.isNull() &&
wheel->surface.contact && wheel->surface.object &&
wheel->surface.object->getTypeMask() & TerrainObjectType)
{
TerrainBlock* tBlock = static_cast<TerrainBlock*>(wheel->surface.object);
S32 mapIndex = tBlock->mMPMIndex[0];
// Emit the dust, the density (time) is scaled by the
// the vehicles velocity.
wheel->emitter->emitParticles(wheel->surface.pos,true,
axis, vel, (U32)(3/*dt * (speed / mDataBlock->maxWheelSpeed) * 1000 * wheel->slip*/));
}
}toif (speed > 1.0f)
{
MaterialPropertyMap* matMap = MaterialPropertyMap::get();
Point3F axis = vel;
axis.normalize();
// Only emit dust on the terrain
Wheel* wend = &mWheel[mDataBlock->wheelCount];
for (Wheel* wheel = mWheel; wheel < wend; wheel++)
{
// Is this wheel in contact with the ground?
if (wheel->tire && wheel->spring && !wheel->emitter.isNull() &&
wheel->surface.contact && wheel->surface.object &&
wheel->surface.object->getTypeMask() & STATIC_COLLISION_MASK)
{
//similar to the extend wheels function, except we multiply the length *2
//to ensure were going to make a ray long enough to intersect the model were testing against
Point3F sp,vec;
currMatrix.mulP(wheel->data->pos,&sp);
currMatrix.mulV(VectorF(0,0,-wheel->spring->length),&vec);
F32 ts = wheel->tire->radius *2 / wheel->spring->length;
Point3F ep = sp + (vec * (1 + ts));
TriRayInfo rInfo;
if (mContainer->castRay(sp, ep, STATIC_COLLISION_MASK, &rInfo))
{
Material* matInst = static_cast<Material*>(Sim::findObject(rInfo.material));
if (matInst != NULL)
{
//Con::errorf("Tex: %s", matInst->getName());
if (this->mDataBlock->tireEmitter[matInst->mFXIndex] != NULL)
{
wheel->emitter->setDataBlock(this->mDataBlock->tireEmitter[matInst->mFXIndex]);
//Con::errorf("FX: %s", mDataBlock->tireEmitter[matInst->mFXIndex]->getName());
if (wheel->tire->decalData[matInst->mFXIndex] != NULL)
{
if(mSceneManager->getCurrentDecalManager())
{
mSceneManager->getCurrentDecalManager()->addDecal(wheel->surface.pos, vel, wheel->surface.normal, wheel->tire->decalData[matInst->mFXIndex]);
}
}
//else Con::errorf("Decals Still NULL");
}
}
//else Con::errorf("matInst Still Registers as (%i)",matInst);
}
TerrainBlock* tBlock = static_cast<TerrainBlock*>(wheel->surface.object);
S32 mapIndex = tBlock->mMPMIndex[0];
// Emit the dust, the density (time) is scaled by the
// the vehicles velocity.
wheel->emitter->emitParticles(wheel->surface.pos,true,
axis, vel, (U32)(3));//dt * (speed / mEngineDatablock->maxWheelSpeed) * 1000 * wheel->slip));
}
}and be sure to place a disableCollision();/enableCollision(); pair at rhe begining and end of the function
#7
(even if you account for the fact that with minor changes you can do the same thing for footprints for
humans, instead of cars, or alter up sounds, instead in addition to, ect)
so lets cut to the chase then, and see about more generalised solutions:
07/13/2007 (4:58 am)
Now if you've followed along this far, youre likely thinking to yourself that this is pretty darned game specific (even if you account for the fact that with minor changes you can do the same thing for footprints for
humans, instead of cars, or alter up sounds, instead in addition to, ect)
so lets cut to the chase then, and see about more generalised solutions:
#8
shapebase.h
struct ShapeBaseData : public GameBaseData {
class ShapeBase : public GameBase
enum ShapeBaseMasks {
again, you'll note the funcname's processFX, to denote were gonna be working with more than just visuals here
shapebase.cc/.cpp
ShapeBaseData::ShapeBaseData()
void ShapeBaseData::initPersistFields()
(yes, I know. explosions. I'll explain that in a sec)
void ShapeBaseData::packData(BitStream* stream)
void ShapeBaseData::unpackData(BitStream* stream)
07/13/2007 (4:59 am)
Shapebase revisited:shapebase.h
struct ShapeBaseData : public GameBaseData {
//add ExplosionData* mSpark[MAX_MAT_FX]; S32 mSparkId[MAX_MAT_FX]; ParticleEmitterData* SparkEmitter; S32 SparkEmitterId; protected: bool onAdd(); //endadd
class ShapeBase : public GameBase
enum ShapeBaseMasks {
//alter
SparkMask = Parent::NextFreeMask,
NameMask = Parent::NextFreeMask << 1, //change each of these up by 1 till you hit ThreadMaskN = SoundMaskN << MaxSoundThreads,
//endalter//add SimObjectPtr<ParticleEmitter> mSparkParticleEmitter; Point3F mSparkPosition; Point3F mSparkNormal; U32 mCollideSparkHitType; bool mHasCollided; SimObjectPtr<ShapeBase> lastHitObject; void ShapeBase::processFX(); /// What to do when this projectile explodes virtual void ShapeBase::Spark(const Point3F& p, const Point3F& n, S32 matFxIndex); void emitParticles(const Point3F&, const Point3F&, const Point3F&, const U32); bool mFadeValue; //endadd
again, you'll note the funcname's processFX, to denote were gonna be working with more than just visuals here
shapebase.cc/.cpp
ShapeBaseData::ShapeBaseData()
//add
for (int i = 0; i<MAX_MAT_FX; i++)
{
mSpark[i] = NULL;
mSparkId[i] = 0;
}
}
//endadd//add
bool ShapeBaseData::onAdd()
{
if(!Parent::onAdd())
return false;
for (int i = 0; i<MAX_MAT_FX; i++)
if (!mSpark[i] && mSparkId[i] != 0)
if (Sim::findObject(mSparkId[i], mSpark[i]) == false)
Con::errorf(ConsoleLogEntry::General, "ProjectileData::onAdd: Invalid packet, bad datablockId(explosion)[%i]: %d", mSparkId[i],i);
return true;
}
//endaddvoid ShapeBaseData::initPersistFields()
//add
addField("Sparks", TypeExplosionDataPtr, Offset(mSpark, ShapeBaseData), MAX_MAT_FX);
//endadd(yes, I know. explosions. I'll explain that in a sec)
void ShapeBaseData::packData(BitStream* stream)
//add
for (int i=0; i<MAX_MAT_FX; i++)
if (stream->writeFlag(mSpark[i] != NULL))
stream->writeRangedU32(mSpark[i]->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
//endaddvoid ShapeBaseData::unpackData(BitStream* stream)
//add
for (int i = 0; i<MAX_MAT_FX; i++)
if (stream->readFlag())
mSparkId[i] = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
//endadd//add
//==============================================================================
//---------------------- Start FX Implementation ------------------------
//==============================================================================
void ShapeBase::Spark(const Point3F& p, const Point3F& n, S32 matFxIndex)
{
// Make sure we don't explode twice...
//if (mHidden == true)
//return;
//mHidden = true;
// Client just plays the explosion at the right place...
//
Explosion* pSpark = NULL;
F32 waterHeight;
if (mDataBlock->mSpark[matFxIndex])
{
pSpark = new Explosion;
pSpark->onNewDataBlock(mDataBlock->mSpark[matFxIndex]);
}
if( pSpark )
{
MatrixF xform(true);
xform.setPosition(p);
pSpark->setTransform(xform);
pSpark->setInitialState(p, n);
pSpark->setCollideType( STATIC_COLLISION_MASK );
if (pSpark->registerObject() == false)
{
Con::errorf(ConsoleLogEntry::General, "Projectile(%s)::Spark: couldn't register Spark",
mDataBlock->getName() );
delete pSpark;
pSpark = NULL;
}
}
// Client object
//updateSound();
}
void ShapeBase::processFX()
{
if (this->isClientObject()) return;
this->disableCollision();
TriRayInfo rInfo;
for (CollisionTimeout* ptr = mTimeoutList; ptr; ptr = ptr->next)
{
ShapeBase* obj = static_cast<ShapeBase*>(Sim::findObject(ptr->objectNumber));
if (obj != NULL)
{
Point3F sp, ep;
sp = this->getPosition();
ep = obj->getPosition();
if (obj->castRay(sp, ep, &rInfo))
{
Material* matInst = static_cast<Material*>(Sim::findObject(rInfo.material));
if (matInst != NULL)
{
//Con::errorf("Tex: %s", matInst->getName());
//if (this->mDataBlock->tireEmitter[matInst->mFXIndex] != NULL)
//{
Spark(rInfo.point, rInfo.normal,matInst->mFXIndex);
//Con::errorf("FX: %s", mDataBlock->tireEmitter[matInst->mFXIndex]->getName());
//}
}
//else Con::errorf("matInst Still Registers as (%i)",matInst);
}
}
//else Con::errorf("CollisionTimeout Object (%i) Still Registers as (%i)",ptr->objectNumber, obj);
}
//since there's no listing for static objects /interiorinstances in the CollisionTimeout list, we'll just have to
//do this the brute-force way, till someone brighter throws us a bone
Point3F sp, ep;
sp = this->getPosition();
ep = sp + this->getVelocity();
if (mContainer->castRay(sp, ep, STATIC_COLLISION_MASK, &rInfo))
{
Material* matInst = static_cast<Material*>(Sim::findObject(rInfo.material));
if (matInst != NULL)
{
//Con::errorf("Tex: %s", matInst->getName());
Spark(rInfo.point, rInfo.normal,matInst->mFXIndex);
}
//else Con::errorf("matInst Still Registers as (%i)",matInst);
}
this->enableCollision();
}
//endadd
#9
1- a special effect
2- a physical impulse you can use to replicate springiness vs a simple hard impact, vs some give, ect
3- an already embeded sound play state
4- damage dealing capabilities, so that you can modify the damage dealt by hitting that spot on that model
unfortunately folks, now's when I have to mention the catch:
at the moment, for singleplayer you can get the explosions working just fine by adding in
void ShapeBase::onImpact(SceneObject* obj, VectorF vec)
void ShapeBase::onImpact(VectorF vec)
However, doing some dedicated server testing earlier this morning, since I'd promised a couple
weeks back to toss this up for folks to pick apart, it would seem that there's a bit of a missing
link to track on down. I'll be taking a look at www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=8135
shortly, but really can't guarantee anything more solid than whats right here for a bit.
So there ya go. Potentially, at least, the makings of a real material system.
I know I for one will be taking it farther than this, but just how fast that get's done, and how much of the underlaying tech
is shared across other projects I'll leave to you folks, meaning just like this other www.garagegames.com/mg/forums/result.thread.php?qt=58720
don't expect to see it in a rescource any time soon. At least not without some severe acid testing from folks with other angles to throw at it
(and likely point out flaws and breaks, wich to my mind invalidates this type of thing as a 'rescource')
So there we are folks, warts and all. Feel free to shred the thing to your heart's contents. Know I'm not in love with the solution set myself just yet.
07/13/2007 (4:59 am)
That knocks out 90% of what all we'll need to do, as explosions have1- a special effect
2- a physical impulse you can use to replicate springiness vs a simple hard impact, vs some give, ect
3- an already embeded sound play state
4- damage dealing capabilities, so that you can modify the damage dealt by hitting that spot on that model
unfortunately folks, now's when I have to mention the catch:
at the moment, for singleplayer you can get the explosions working just fine by adding in
void ShapeBase::onImpact(SceneObject* obj, VectorF vec)
//add processFX(); //endadd
void ShapeBase::onImpact(VectorF vec)
//add processFX(); //endadd
However, doing some dedicated server testing earlier this morning, since I'd promised a couple
weeks back to toss this up for folks to pick apart, it would seem that there's a bit of a missing
link to track on down. I'll be taking a look at www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=8135
shortly, but really can't guarantee anything more solid than whats right here for a bit.
So there ya go. Potentially, at least, the makings of a real material system.
I know I for one will be taking it farther than this, but just how fast that get's done, and how much of the underlaying tech
is shared across other projects I'll leave to you folks, meaning just like this other www.garagegames.com/mg/forums/result.thread.php?qt=58720
don't expect to see it in a rescource any time soon. At least not without some severe acid testing from folks with other angles to throw at it
(and likely point out flaws and breaks, wich to my mind invalidates this type of thing as a 'rescource')
So there we are folks, warts and all. Feel free to shred the thing to your heart's contents. Know I'm not in love with the solution set myself just yet.
#10
Also, although you didn't discuss it in depth, it's important to note that in stock, explosions are not 'objects' within the world--they are the results of projectiles colliding. There is no way in stock to directly create just an explosion itself in your server or client without re-implementing.
07/13/2007 (8:43 am)
Neat idea, and neat post, but I would highly suggest that TDN is a better format for posting a mini-tutorial like this :)Also, although you didn't discuss it in depth, it's important to note that in stock, explosions are not 'objects' within the world--they are the results of projectiles colliding. There is no way in stock to directly create just an explosion itself in your server or client without re-implementing.
#11
alright. next time will do, though this is, unfortunately, nothing even close to a tutorial, since about the only thing that's accomplished thusfar is decal aplication. Had I not stated I'd put something up by today, this wouldn't have seen the light of day till more was worked on out. (woulda been nice to have presented working sparks, or even terrain, wich what got me started on this line of experimentation: replacing the addmaterialmaping functionality that was gutted out)
figured as much after going through things. One of many aspects I'm still mulling over at this point/researching/forumbrowsing for. But I suppose thats the wonders of self-education for ya. Spending 90% working out that 10% that's obvious to the next guy. Mebbey it'll start to make more sense when I finally get back to the house after this last couple weeks of travel-coding.
07/13/2007 (9:37 am)
Quote:
but I would highly suggest that TDN is a better format for posting a mini-tutorial like this
alright. next time will do, though this is, unfortunately, nothing even close to a tutorial, since about the only thing that's accomplished thusfar is decal aplication. Had I not stated I'd put something up by today, this wouldn't have seen the light of day till more was worked on out. (woulda been nice to have presented working sparks, or even terrain, wich what got me started on this line of experimentation: replacing the addmaterialmaping functionality that was gutted out)
Quote:
Also, although you didn't discuss it in depth, it's important to note that in stock, explosions are not 'objects' within the world--they are the results of projectiles colliding. There is no way in stock to directly create just an explosion itself in your server or client without re-implementing.
figured as much after going through things. One of many aspects I'm still mulling over at this point/researching/forumbrowsing for. But I suppose thats the wonders of self-education for ya. Spending 90% working out that 10% that's obvious to the next guy. Mebbey it'll start to make more sense when I finally get back to the house after this last couple weeks of travel-coding.
#12
07/13/2007 (12:07 pm)
You're casting the contact surface as a TerrainBlock. Any thoughts of support Atlas with this?
#13
If you want to talk an educated guess based on a little research in terms of the available documentation and generation tools (ld3t this end) ... theres not really *a* atlas2, as I understand it, so much as unique and blended versions of the core type...
The former (if i'm reading this right) uses one real big texture, and the above methodologies are essentially based opon grabbing the corresponding material/custommaterial associated with that texture, so 10-1 that's out, while the latter uses an accumulation of textures simliar (yes?no?) to the old-style terrain, so the latter *might* be suitable for a specialised castray... either that or perhapse embedding an additional material channel?
Frankly, right now, i'll just be happy to get half the types of objects working. Should give us some pointers along the way on how to deal with the similar derivations at some point. That, and I for one have to admit reluctance to mess arround with atlas just yet, till I see what kinds of changes they make to support shadowmapping et al.
07/13/2007 (3:08 pm)
Not yet, no. Was one of the things I was reffering to as sceneobject derivations though.If you want to talk an educated guess based on a little research in terms of the available documentation and generation tools (ld3t this end) ... theres not really *a* atlas2, as I understand it, so much as unique and blended versions of the core type...
The former (if i'm reading this right) uses one real big texture, and the above methodologies are essentially based opon grabbing the corresponding material/custommaterial associated with that texture, so 10-1 that's out, while the latter uses an accumulation of textures simliar (yes?no?) to the old-style terrain, so the latter *might* be suitable for a specialised castray... either that or perhapse embedding an additional material channel?
Frankly, right now, i'll just be happy to get half the types of objects working. Should give us some pointers along the way on how to deal with the similar derivations at some point. That, and I for one have to admit reluctance to mess arround with atlas just yet, till I see what kinds of changes they make to support shadowmapping et al.
#14
07/13/2007 (3:58 pm)
You're correct about the 2 kinds of Atlas terrains. There's no real way to use this kind of system on the "unique" textured terrain, right now, without embedding additional information into the Atlas file. The Blended method could use the "blend map" (or texture map) to determine the kind of material that was struck.
#15
I want to highlight this very strongly--that's exactly why you can embed additional information into the Atlas file (Table of Contents, and the abstract nature of the TOC/Chunk interface).
The structures are built in for all sorts of additional information availability, primarily for commercial simulation, but also extremely useful for other purposes as well.
07/13/2007 (7:10 pm)
Quote:
without embedding additional information into the Atlas file.
I want to highlight this very strongly--that's exactly why you can embed additional information into the Atlas file (Table of Contents, and the abstract nature of the TOC/Chunk interface).
The structures are built in for all sorts of additional information availability, primarily for commercial simulation, but also extremely useful for other purposes as well.
#16
or, completely contrary to what I was informed initally that made it seem a viable notion to totally waste this much time towards implementing this type of thing, materials were not merely removed from the ghosting procedure to save on loading, but removed from the server end entirely...that'll require quite a few more fundamental engine changes to get going... suppose for pure singleplayer someone might find the base research usefull though.
07/14/2007 (6:34 am)
...or, completely contrary to what I was informed initally that made it seem a viable notion to totally waste this much time towards implementing this type of thing, materials were not merely removed from the ghosting procedure to save on loading, but removed from the server end entirely...that'll require quite a few more fundamental engine changes to get going... suppose for pure singleplayer someone might find the base research usefull though.
#17
http://www.garagegames.com/mg/forums/result.thread.php?qt=63305
in the function:
void Vehicle::updatePos(F32 dt)
after the check
if (isServerObject()) {
...
}
replacing
sets it up to run the onImpact func on clinets as well as servers,
shifting processFX(); to outside theisGhost() check makes sure it'll play on both clients and servers (though thats more stub-placement for later expansion when I/someone brighter cooks up how to store the material data serverside without it horking, you coud just as easily set it an else call the func and call it a day)
and of course, going on up the chain for your favorite shapebase derivation and making sure you've got a client-side setup for a mTimeoutList, or equivalent.
07/20/2007 (5:26 pm)
Update on the *client-side* explosions bit, as a bit of a modification of the codebase postedhttp://www.garagegames.com/mg/forums/result.thread.php?qt=63305
in the function:
void Vehicle::updatePos(F32 dt)
after the check
if (isServerObject()) {
...
}
replacing
else {
// Play impact sounds on the client.withelse {
notifyCollision();
// Server side impact script callback
if (collided) {
VectorF collVec = mRigid.linVelocity - origVelocity;
F32 collSpeed = collVec.len();
if (collSpeed > mDataBlock->minImpactSpeed)
onImpact(collVec);
}
// Play impact sounds on the client.sets it up to run the onImpact func on clinets as well as servers,
shifting processFX(); to outside theisGhost() check makes sure it'll play on both clients and servers (though thats more stub-placement for later expansion when I/someone brighter cooks up how to store the material data serverside without it horking, you coud just as easily set it an else call the func and call it a day)
and of course, going on up the chain for your favorite shapebase derivation and making sure you've got a client-side setup for a mTimeoutList, or equivalent.
#18
What we need to do is really to fix the broken terrData.cpp so it works as in TGE 1.5x.
In onAdd() find
and change it to
Next we add the method getTerrainMapIndex (also from TGE)
And that's all there's to it. This is how I use it:
I'm also implementing a system for modifying friction, top speed, etc for my racing game depending on the material the wheels touches. The current implementation is just a grid of material vs material modifiers. Easy to implement, but a pain to maintain when you add new materials and properties. So I'm thinking of alternative system which would be more modifier based. I.e. you specify properties for the materials in terms of roughness, hardness, and so on, and calculate how the materials would react against each other; slick rubber vs tarmac = good friction, rough rubber vs tarmac = not as good, etc.
I don't know how racing games normally do this... Any thoghts anyone?
Thanks,
Kristoffer
09/04/2007 (10:34 am)
To add to the list of getting materials from different object types, here's somehing for the legacy terrain (at least 1.01). I haven't embedded it in the castRay method since I've put my stuff in a utils class together with some other friction utils... I'll see if I get around to do it.What we need to do is really to fix the broken terrData.cpp so it works as in TGE 1.5x.
In onAdd() find
StringTableEntry fn = mMaterialFileName[0];
if(!dStrncmp(fn, "terrain.", 8))
fn += 8;
char nameBuff[512];
dStrcpy(nameBuff, mTerrFileName);
char *p = dStrrchr(nameBuff, '/');
if (p) p++;
else p = nameBuff;
dStrcat(p,fn);and change it to
MaterialPropertyMap* pMatMap = static_cast<MaterialPropertyMap*>(Sim::findObject("MaterialPropertyMap"));
for (int i = 0; i < TerrainBlock::MaterialGroups; i++)
{
StringTableEntry fn = mMaterialFileName[i];
if (fn != NULL)
{
if(!dStrncmp(fn, "terrain.", 8))
fn += 8;
char nameBuff[512];
dStrcpy(nameBuff, fn);
char *p = dStrrchr(nameBuff, '/');
if (p) p++;
else p = nameBuff;
mMPMIndex[i] = pMatMap->getIndexFromName(p);
}
else
mMPMIndex[i] = -1;
}You need to include "materials/materialPropertyMap.h" as well.Next we add the method getTerrainMapIndex (also from TGE)
S32 TerrainBlock::getTerrainMapIndex(Point3F& pt)
{
U8 alphas[MaterialGroups];
int mapIndex= -1;
const MatrixF & mat = getTransform();
Point3F origin;
mat.getColumn(3, &origin);
F32 squareSize = (F32) getSquareSize();
F32 halfSquareSize = squareSize / 2;
float x = (pt.x - origin.x + halfSquareSize) / squareSize;
float y = (pt.y - origin.y + halfSquareSize) / squareSize;
getMaterialAlpha(Point2I(x,y), alphas);
MaterialPropertyMap* matMap = static_cast<MaterialPropertyMap*>(Sim::findObject("MaterialPropertyMap"));
if (matMap != NULL)
{
U8 maxAlpha = alphas[0];
U8 maxAlpha2;
int indexTexture = 0;
for (int i=1; i<MaterialGroups; i++)
{
maxAlpha2 = getMax(alphas[i],maxAlpha);
if (maxAlpha2 != maxAlpha)
{
indexTexture = i;
maxAlpha = maxAlpha2;
}
}
mapIndex = mMPMIndex[indexTexture];
}
return mapIndex;
}And that's all there's to it. This is how I use it:
if (ray.object->getTypeMask() & TerrainObjectType)
{
TerrainBlock* tBlock = static_cast<TerrainBlock*>(ray.object);
mapIndex = tBlock->getTerrainMapIndex(ray.point);
}I'm also implementing a system for modifying friction, top speed, etc for my racing game depending on the material the wheels touches. The current implementation is just a grid of material vs material modifiers. Easy to implement, but a pain to maintain when you add new materials and properties. So I'm thinking of alternative system which would be more modifier based. I.e. you specify properties for the materials in terms of roughness, hardness, and so on, and calculate how the materials would react against each other; slick rubber vs tarmac = good friction, rough rubber vs tarmac = not as good, etc.
I don't know how racing games normally do this... Any thoghts anyone?
Thanks,
Kristoffer
#19
tdn.garagegames.com/wiki/Material_Based_Effects_Projects
Wouldn't surprise me to find I missed including a thing or two here or there, but since it would appear the things pretty much stabile, there ya go.
09/24/2007 (2:52 am)
@Stephentdn.garagegames.com/wiki/Material_Based_Effects_Projects
Wouldn't surprise me to find I missed including a thing or two here or there, but since it would appear the things pretty much stabile, there ya go.
#20
Unfortunately this code change in Interior::castRay_r (getting the material) didn't work for me in TGEA 1.0.3.
I replaced it with this (thanks to Clint S. Brewer)
to get it working.
10/02/2007 (1:06 am)
Thanks a lot Kirk and Kristoffer, very much appreciated!Unfortunately this code change in Interior::castRay_r (getting the material) didn't work for me in TGEA 1.0.3.
if(inside)
{
info->face = surfaceIndex;
info->material = rSurface.textureIndex;
MatInstance* matInst = mMaterialList->getMaterialInst( rSurface.textureIndex );
if ( matInst && matInst->getMaterial() )
{
info->material = matInst->getMaterial()->getId();
}
else
{
info->material = 0;
}
}I replaced it with this (thanks to Clint S. Brewer)
if(inside)
{
info->face = surfaceIndex;
MaterialPropertyMap* pMatMap = MaterialPropertyMap::get();
const char* pName = mMaterialList->getMaterialName(rSurface.textureIndex);
info->material = pMatMap->getIndexFromName(pName);
if(info->material == -1)
info->material = 0; //no entry, use material 0
}to get it working.
Torque Owner Kirk Longendyke
www.garagegames.com/mg/forums/result.thread.php?qt=29713
This is so we'll have a few tools in our set that allow us to grab the visual face, and material of an object.
A few examples of this are as follows (and apologies for not making this a generalised sceneobject based solution right off the bat. Still studying that wrinkle):
diffs:
interior.h:
interiorCollision.cc/cpp: (yes, I'm still use the oldschool file naming convention for ease of file comparrison with other resources. shoot me :P)
//replace in, or clone and overload:
bool Interior::castRay_r(
if(inside) { info->face = surfaceIndex; break; } } return true;toif(inside) { info->face = surfaceIndex; info->material = rSurface.textureIndex; MatInstance* matInst = mMaterialList->getMaterialInst( rSurface.textureIndex ); if ( matInst && matInst->getMaterial() ) { info->material = matInst->getMaterial()->getId(); } else { info->material = 0; } } return true; //endadd