GuiRadarCtrl and GuiRadarTSCtrl port for T3D
by Sean Rice · 03/24/2010 (7:16 am) · 36 comments
Some credit for the original work!
www.torquepowered.com/community/resources/view/16417 3d spherical radar
www.torquepowered.com/community/resources/view/10333 Radar/Map/Compass
Download the files!www.filefront.com/15918935/radar.rar/
First the 3d Radar
Note: the sphere doesn't seem to want to render correctly. Sometimes it renders when you pop in the first control; although, any adjustment causes the sphere to disappear. Look at the link above to see how it is supposed to look. If anyone can give assistance for this aspect it would be greatly appreciated. Link is above for what it should look like.
source/gui/control/GuiRadarCtrl.cpp
now source/gui/control/GuiRadarCtrl.h
Now the Radar/Map/Compass
now source/gui/control/GuiRadarTSCtrl.cpp
now source/gui/control/GuiRadarTSCtrl.h
The videos of the stuff in action.
www.torquepowered.com/community/resources/view/16417 3d spherical radar
www.torquepowered.com/community/resources/view/10333 Radar/Map/Compass
Download the files!www.filefront.com/15918935/radar.rar/
First the 3d Radar
Note: the sphere doesn't seem to want to render correctly. Sometimes it renders when you pop in the first control; although, any adjustment causes the sphere to disappear. Look at the link above to see how it is supposed to look. If anyone can give assistance for this aspect it would be greatly appreciated. Link is above for what it should look like.
source/gui/control/GuiRadarCtrl.cpp
//-----------------------------------------------------------------------------
// Torque Game Engine Advanced v1.0
// Copyright (C) GarageGames.com, Inc.
// Convertion to TGEA 1.8.1 with karakurty
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "gui/core/guiControl.h"
#include "gfx/gfxDrawUtil.h"
#include "T3D/player.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "gfx/gfxDevice.h"
#include "app/game.h"
#include "T3D/gameConnection.h"
#include "T3D/ShapeBase.h"
#include "gfx/primBuilder.h"
#include "gui/controls/GuiRadarCtrl.h"
IMPLEMENT_CONOBJECT(GuiRadarCtrl);
GuiRadarCtrl::GuiRadarCtrl(void)
{
// Radar Background default
mRadarBitmapName = StringTable->insert("art/gui/compass_map_radar/radar_base.png");
// Blip defaults
mBlipUpName = StringTable->insert("art/gui/compass_map_radar/dot_teal.png");
mBlipBelowName = StringTable->insert("art/gui/compass_map_radar/dot_black.png");
mBlipLevelName = StringTable->insert("art/gui/compass_map_radar/dot_red.png");
mShowRadar = true;
//mRadarWrap = false;
mRadarRadiusRange = 100.0f;
mLevelRange = 20.0f;
// As default show only players
mShowShapeBase = false;
mShowVehicles = false;
mShowPlayers = true;
mShowBots = true;
// Compass defaults
mCompassBitmapName = StringTable->insert("art/gui/compass_map_radar/compass.png");
mShowCompass = true;
mCompassRotation = 0.0f;
// <MH>
// Map defaults
mMapBitmapName = StringTable->insert("art/gui/compass_map_radar/scorchedPlanet.png");
mShowMap = true;
// </MH>
mHideAll = false;
}
void GuiRadarCtrl::initPersistFields()
{
Parent::initPersistFields();
addGroup("Radar");
addField("RadarBitmap", TypeFilename, Offset(mRadarBitmapName, GuiRadarCtrl));
//addField("RadarWrap", TypeBool, Offset(mRadarWrap, GuiRadarCtrl));
addField("blip_above", TypeFilename, Offset(mBlipUpName, GuiRadarCtrl));
addField("blip_below", TypeFilename, Offset(mBlipBelowName, GuiRadarCtrl));
addField("blip_level", TypeFilename, Offset(mBlipLevelName, GuiRadarCtrl));
addField("RadarRange", TypeF32, Offset(mRadarRadiusRange, GuiRadarCtrl));
addField("ShowRadar", TypeBool, Offset(mShowRadar, GuiRadarCtrl));
endGroup("Radar");
addGroup("Compass");
addField("CompassBitmap", TypeFilename, Offset(mCompassBitmapName, GuiRadarCtrl));
addField("ShowCompass", TypeBool, Offset(mShowCompass, GuiRadarCtrl),"Show Compass)");
endGroup("Compass");
// <MH>
addGroup("Map");
addField("MapBitmap", TypeFilename, Offset(mMapBitmapName, GuiRadarCtrl));
addField("ShowMap", TypeBool, Offset(mShowMap, GuiRadarCtrl), "Show Map");
endGroup("Map");
// </MH>
addGroup("Misc");
addField("RadarShowPlayers", TypeBool, Offset(mShowPlayers, GuiRadarCtrl),"Show Players on Radar");
addField("RadarShowVehicles", TypeBool, Offset(mShowVehicles, GuiRadarCtrl),"Show Vehicles on Radar");
addField("RadarShowBots", TypeBool, Offset(mShowBots, GuiRadarCtrl),"Show Bots on Radar");
addField("RadarShowShapeBase", TypeBool, Offset(mShowShapeBase, GuiRadarCtrl),"Show All ShapeBase derived objects on Radar");
addField("LevelRange", TypeS32, Offset(mLevelRange, GuiRadarCtrl),"The height difference at which the Level blip is displayed");
addField("HideAll", TypeBool, Offset(mHideAll, GuiRadarCtrl));
addField("ShowFrame", TypeBool, Offset(mShowFrame, GuiRadarCtrl));
endGroup("Misc");
}
ConsoleMethod( GuiRadarCtrl, setValue, void, 4, 4, "(int xAxis, int yAxis)"
"Set the offset of the bitmap.")
{
object->setValue(dAtoi(argv[2]), dAtoi(argv[3]));
}
ConsoleMethod( GuiRadarCtrl, setRadarBitmap, void, 3, 4, "(string filename, bool resize=false)"
"Set the bitmap displayed in the control. Note that it is limited in size, to 256x256.")
{
char fileName[1024];
Con::expandScriptFilename(fileName, sizeof(fileName), argv[2]);
object->setRadarBitmap(fileName, argc > 3 ? dAtob( argv[3] ) : false );
}
ConsoleMethod( GuiRadarCtrl, setRadarRange, void, 3, 3, "")
{
Con::printf("Object->SetRadius = %f", dAtof(argv[2]));
object->setRadius(dAtof(argv[2]));
}
bool GuiRadarCtrl::onWake()
{
if (! Parent::onWake())
return false;
setActive(true);
setRadarBitmap(mRadarBitmapName);
setRadarUpBlipBitmap(mBlipUpName);
setRadarBelowBlipBitmap(mBlipBelowName);
setRadarLevelBlipBitmap(mBlipLevelName);
setRadius(mRadarRadiusRange);
setLevelRange(mLevelRange);
setCompassBitmap(mCompassBitmapName);
setMapBitmap(mMapBitmapName);
return true;
}
void GuiRadarCtrl::onSleep()
{
//Parent::onSleep();
mRadarTextureObject = NULL;
mRadarDot = NULL;
mRadarUp = NULL;
mRadarDown = NULL;
mCompassTextureObject = NULL;
mMapTextureObject = NULL;
Parent::onSleep();
}
//-------------------------------------
void GuiRadarCtrl::inspectPostApply()
{
// if the extent is set to (0,0) in the gui editor and appy hit, this control will
// set it's extent to be exactly the size of the bitmap (if present)
Parent::inspectPostApply();
if ((getExtent().y == 0) && (getExtent().x == 0) && mRadarTextureObject)
{
setExtent(mRadarTextureObject->getWidth(), mRadarTextureObject->getHeight());
}
if ((getExtent().y == 0) && (getExtent().x == 0) && mCompassTextureObject)
{
//setExtent(mCompassTextureObject->getWidth(), mCompassTextureObject->getHeight());
setExtent(mRadarTextureObject->getWidth(), mRadarTextureObject->getHeight());
}
}
void GuiRadarCtrl::setRadarBitmap(const char *name, bool resize)
{
mRadarBitmapName = StringTable->insert(name);
if (*mRadarBitmapName)
{
mRadarTextureObject.set( mRadarBitmapName, &GFXDefaultGUIProfile, avar("%s() - mRadarTextureObject (line %d)", __FUNCTION__, __LINE__) );
// Resize the control to fit the bitmap
if( mRadarTextureObject && resize )
{
setExtent(mRadarTextureObject->getWidth(), mRadarTextureObject->getHeight());
updateSizing();
}
}
else
mRadarTextureObject = NULL;
setUpdate();
}
void GuiRadarCtrl::setRadarLevelBlipBitmap(const char *name)
{
mBlipLevelName = StringTable->insert(name);
if (*mBlipLevelName)
mRadarDot.set( mBlipLevelName, &GFXDefaultGUIProfile, avar("%s() - mRadarDot (line %d)", __FUNCTION__, __LINE__) );
else
mRadarDot = NULL;
setUpdate();
}
void GuiRadarCtrl::setRadarUpBlipBitmap(const char *name)
{
mBlipUpName = StringTable->insert(name);
if (*mBlipUpName)
mRadarUp.set( mBlipUpName, &GFXDefaultGUIProfile, avar("%s() - mRadarUp (line %d)", __FUNCTION__, __LINE__) );
else
mRadarUp = NULL;
setUpdate();
}
void GuiRadarCtrl::setRadarBelowBlipBitmap(const char *name)
{
mBlipBelowName = StringTable->insert(name);
if (*mBlipBelowName)
mRadarDown.set( mBlipBelowName, &GFXDefaultGUIProfile, avar("%s() - mRadarDown (line %d)", __FUNCTION__, __LINE__) );
else
mRadarDown = NULL;
setUpdate();
}
void GuiRadarCtrl::setCompassBitmap(const char *name, bool resize)
{
mCompassBitmapName = StringTable->insert(name);
if (*mCompassBitmapName)
{
mCompassTextureObject.set( mCompassBitmapName, &GFXDefaultGUIProfile, avar("%s() - mCompassTextureObject (line %d)", __FUNCTION__, __LINE__) );
// Resize the control to fit the bitmap
if( mCompassTextureObject && resize )
{
setExtent(mCompassTextureObject->getWidth(), mCompassTextureObject->getHeight());
updateSizing();
}
}
else
mCompassTextureObject = NULL;
setUpdate();
}
// <MH>
void GuiRadarCtrl::setMapBitmap(const char *name)
{
mMapBitmapName = StringTable->insert(name);
if (*mMapBitmapName)
mMapTextureObject.set( mMapBitmapName, &GFXDefaultGUIProfile, avar("%s() - mMapTextureObject (line %d)", __FUNCTION__, __LINE__) );
else
mMapTextureObject = NULL;
setUpdate();
}
// </MH>
void GuiRadarCtrl::setRadius(const F32 newRange)
{
mRadarRadiusRange = newRange;
}
void GuiRadarCtrl::setLevelRange(const F32 newLevelRange)
{
mLevelRange = newLevelRange;
}
void GuiRadarCtrl::setCompassRotation(const F32 rotation)
{
mCompassRotation = rotation;
}
ConsoleMethod( GuiRadarCtrl, setCompassRotation, void, 3, 3, "")
{
Con::printf("Object->SetRadius = %f", dAtof(argv[2]));
object->setCompassRotation(dAtof(argv[2]));
}
float Vector3dToDegree(Point3F vector)
{
float angle;
if (vector.x == 0.0F)
{
if (vector.y > 0.0F)
return 0.0F;
else if (vector.y == 0.0F)
return -1.0F;
else
return 180.0F;
}
if (vector.y == 0.0F)
{
if (vector.x < 0.0F)
return 270.0F;
else
return 90.0F;
}
angle = atanf((vector.x) / (-vector.y)) * (180.0F / M_PI);
if ((-vector.y) < 0.0F)
return angle + 180.0F;
else
{
if (vector.x > 0.0F)
return angle;
else
return angle + 360.0F;
}
}
// Conversion function, Degrees to vector (used after manipulating camera angle to represent angle of object on radar)
void DegreeToVector2d(float angle, float length, Point3F &vector)
{
angle = (angle / 180.0F) * M_PI;
vector.x = length * (sin(angle) );
vector.y = length * (-cos(angle) );
}
void GuiRadarCtrl::onRender(Point2I offset, const RectI &updateRect)
{
// Must have a connection
GameConnection* conn = GameConnection::getConnectionToServer();
if (!conn) return;
// Must have controlled object
GameBase* control = conn->getControlObject();
if (!control) return;
//Find distance from top-left corner to center of radar image
Point2F center(getExtent().x / 2, getExtent().y / 2);
F32 HW = getExtent().y / 2.0;
//Make center the UI object's coordinate center
center.x += getBounds().point.x;
center.y += getBounds().point.y;
MatrixF cam;
VectorF camDir;
Point3F cameraRot;
conn->getControlCameraTransform(0,&cam); // store camera information
cam.getColumn(3, &mMyCoords); // get camera position
cam.getColumn(1, &camDir); // get camera vector
cam.getRow(1,&cameraRot); // get camera rotation
// Remove Rotation around the X/Y axis
//cameraRot.x = 0;
//cameraRot.y = 0;
cameraRot.neg(); // bug forces us to need to invert camera rotation angle
// get angle that camera is facing
float cameraAngle = Vector3dToDegree(cameraRot);
if (mMapTextureObject && mShowMap)
{
GFX->getDrawUtil()->clearBitmapModulation();
GFXTextureObject* texture = mMapTextureObject;
setupStateBlocks();
GFX->setStateBlock(mBeforeStateBlock);
GFX->setTexture( 0, texture );
GFX->disableShaders();
F32 width = getBounds().extent.x * 0.5;
MatrixF rotMatrix(EulerF(0.0, 0.0, mDegToRad(cameraAngle)));
// Point3F offset(offset.x + mBounds.extent.x / 2, offset.y + mBounds.extent.y / 2, 0.0);
Point3F offset( offset.x + getBounds().extent.x / 2, offset.y + getBounds().extent.y / 2, 0.0);
// The 8 will help model the scale of which the player moves across the map
Point2F offsetTexture(mMyCoords.x / ((float)mMapTextureObject->getBitmapWidth() * 8),
-mMyCoords.y / ((float)mMapTextureObject->getBitmapHeight() * 8));
// F32 uvOffset = (float)mBounds.extent.x / (float)mMapTextureObject->getBitmapWidth() / 2;
F32 uvOffset = (float)getBounds().extent.x / (float)mMapTextureObject->getBitmapWidth() / 2;
// Octagons Vertices
Point3F points[10];
points[0] = Point3F(0.0f, 0.0f, 0.0f);
points[1] = Point3F(0.0f, width, 0.0f);
points[2] = Point3F(width * 0.7f, width * 0.7f, 0.0f);
points[3] = Point3F(width, 0.0f, 0.0f);
points[4] = Point3F(width * 0.7f, -width * 0.7f, 0.0f);
points[5] = Point3F(0.0f, -width, 0.0f);
points[6] = Point3F(-width * 0.7f, -width * 0.7f, 0.0f);
points[7] = Point3F(-width, 0.0f, 0.0f);
points[8] = Point3F(-width * 0.7f, width * 0.7f, 0.0f);
points[9] = Point3F(0.0f, width, 0.0f);
for (int i = 0; i < 10; i++)
{
rotMatrix.mulP(points[i]);
points[i] += offset;
}
PrimBuild::color3f(1.0f, 1.0f, 1.0f);
PrimBuild::begin(GFXTriangleFan, 10);
PrimBuild::texCoord2f(offsetTexture.x + 0.0f, offsetTexture.y + 0.0f);
PrimBuild::vertex3fv(points[0]);
PrimBuild::texCoord2f(offsetTexture.x + 0.0f, offsetTexture.y + uvOffset);
PrimBuild::vertex3fv(points[1]);
PrimBuild::texCoord2f(offsetTexture.x + uvOffset * 0.7f, offsetTexture.y + uvOffset * 0.7f);
PrimBuild::vertex3fv(points[2]);
PrimBuild::texCoord2f(offsetTexture.x + uvOffset, offsetTexture.y + 0.0f);
PrimBuild::vertex3fv(points[3]);
PrimBuild::texCoord2f(offsetTexture.x + uvOffset * 0.7f, offsetTexture.y + -uvOffset * 0.7f);
PrimBuild::vertex3fv(points[4]);
PrimBuild::texCoord2f(offsetTexture.x + 0.0f, offsetTexture.y + -uvOffset);
PrimBuild::vertex3fv(points[5]);
PrimBuild::texCoord2f(offsetTexture.x + -uvOffset * 0.7f, offsetTexture.y + -uvOffset * 0.7f);
PrimBuild::vertex3fv(points[6]);
PrimBuild::texCoord2f(offsetTexture.x + -uvOffset, offsetTexture.y + 0.0f);
PrimBuild::vertex3fv(points[7]);
PrimBuild::texCoord2f(offsetTexture.x + -uvOffset * 0.7f, offsetTexture.y + uvOffset * 0.7f);
PrimBuild::vertex3fv(points[8]);
PrimBuild::texCoord2f(offsetTexture.x + 0.0f, offsetTexture.y + uvOffset);
PrimBuild::vertex3fv(points[9]);
PrimBuild::end();
GFX->setStateBlock(mBlendDisabledStateBlock);
}
if (mRadarTextureObject && mShowRadar)
{
GFX->getDrawUtil()->clearBitmapModulation();
RectI rect(offset, getExtent());
GFX->getDrawUtil()->drawBitmapStretch(mRadarTextureObject, rect);
}
GFX->getDrawUtil()->clearBitmapModulation();
GFXTextureObject* LevelBlip = (GFXTextureObject*) mRadarDot;
GFXTextureObject* HighBlip = (GFXTextureObject*) mRadarUp;
GFXTextureObject* LowBlip = (GFXTextureObject*) mRadarDown;
// Go through all ghosted objects on connection (client-side)
for (SimSetIterator itr(conn); *itr; ++itr) {
// Make sure that the object is a ShapeBase object
if ((*itr)->getType() & ShapeBaseObjectType) {
ShapeBase* shape = static_cast<ShapeBase*>(*itr);
// Make sure that the object isn't the client
if (shape != control && shape->getShapeName()) {
// Make sure the ShapeBase object is a player
if (shape->getType()) {
//ONly check when ShapeBase is not selecting, we are after all iterating through ShapeBaseObjects
if (!mShowShapeBase)
{
if (!mShowPlayers && shape->getType() == PlayerObjectType)
continue;
if (!mShowVehicles && shape->getType() == VehicleObjectType)
continue;
if (!mShowBots && shape->getType() == AIObjectType)
continue;
}
Point3F newCoord;
// Get coords of player object
newCoord = shape->getPosition();
// Find distance from point A (player object) to point B (client's player)
VectorF shapeDir = newCoord - mMyCoords;
// Test to see if player object in range (deal with squared objects in cheap way to use non-negative value)
F32 shapeDist = shapeDir.lenSquared();
if (shapeDist == 0 || shapeDist > (mRadarRadiusRange * mRadarRadiusRange))
continue;
// Convert map coordinates to screen coordinates
newCoord.x -= mMyCoords.x;
newCoord.y -= mMyCoords.y;
newCoord.y = -newCoord.y;
float coord_z = newCoord.z - mMyCoords.z;
newCoord.z = 0;
// Adjust object's vector to represent rotation of camera
float objectAngle = Vector3dToDegree(newCoord);
float length = newCoord.len()*HW/mRadarRadiusRange;
DegreeToVector2d(((360-objectAngle)+(cameraAngle) ), length, newCoord);
newCoord.x = -newCoord.x;
// Add tour calculation to the centre of the GuiControl
newCoord.x += (center.x);
newCoord.y += (center.y);
// Draw radar blips based on height
if(coord_z < F32(0 - mLevelRange)){
if (LowBlip)
{
GFX->getDrawUtil()->drawBitmap(LowBlip, Point2I(newCoord.x,newCoord.y));
}
}
else if(coord_z > mLevelRange){
if (HighBlip)
{
GFX->getDrawUtil()->drawBitmap(HighBlip, Point2I(newCoord.x,newCoord.y));
}
}
else{
if (LevelBlip)
{
GFX->getDrawUtil()->drawBitmap(LevelBlip, Point2I(newCoord.x,newCoord.y));
}
}
}
}
}
}
if (mProfile->mBorder || !mRadarTextureObject)
{
if (mShowFrame)
{
RectI rect(offset.x, offset.y, getExtent().y, getExtent().x);
GFX->getDrawUtil()->drawRect(rect, mProfile->mBorderColor);
}
}
if (mCompassTextureObject && mShowCompass)
{
GFX->getDrawUtil()->clearBitmapModulation();
GFXTextureObject* texture = mCompassTextureObject;
setupStateBlocks();
GFX->setStateBlock(mBeforeStateBlock);
GFX->setTexture( 0, texture );
GFX->disableShaders();
F32 width = getExtent().y * 0.5;
MatrixF rotMatrix( EulerF( 0.0, 0.0, mDegToRad(cameraAngle)));
Point3F offset( offset.x + getExtent().y / 2,
offset.y + getExtent().x / 2, 0.0);
Point3F points[4];
points[0] = Point3F(-width, -width, 0.0f);
points[1] = Point3F(-width, width, 0.0f);
points[2] = Point3F( width, width, 0.0f);
points[3] = Point3F( width, -width, 0.0f);
for(int i = 0; i < 4; i++)
{
rotMatrix.mulP( points[i] );
points[i] += offset;
}
PrimBuild::color3f(1.0f,1.0f,1.0f);
PrimBuild::begin(GFXTriangleFan, 4);
PrimBuild::texCoord2f(0, 0);
PrimBuild::vertex3fv(points[0]);
PrimBuild::texCoord2f(0, 1);
PrimBuild::vertex3fv(points[1]);
PrimBuild::texCoord2f(1, 1);
PrimBuild::vertex3fv(points[2]);
PrimBuild::texCoord2f(1, 0);
PrimBuild::vertex3fv(points[3]);
PrimBuild::end();
GFX->setStateBlock(mBlendDisabledStateBlock);
}
renderChildControls(offset, updateRect);
}
void GuiRadarCtrl::setupStateBlocks()
{
if (mBeforeStateBlock.isNull())
{
GFXStateBlockDesc desc;
desc.setCullMode(GFXCullNone);
//desc.setZEnable(false);
desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
desc.samplersDefined = true;
desc.samplers[0].textureColorOp = GFXTOPModulate;
desc.samplers[1].textureColorOp = GFXTOPDisable;
desc.samplers[0].addressModeU = GFXAddressWrap;
desc.samplers[0].addressModeV = GFXAddressWrap;
mBeforeStateBlock = GFX->createStateBlock( desc );
}
if (mBlendDisabledStateBlock.isNull())
{
GFXStateBlockDesc desc;
desc.setBlend(false, GFXBlendOne, GFXBlendZero);
mBlendDisabledStateBlock = GFX->createStateBlock( desc );
}
}now source/gui/control/GuiRadarCtrl.h
//-----------------------------------------------------------------------------
// Torque Game Engine Advanced
// Copyright (C) GarageGames.com, Inc.
// Convertion to TGEA 1.8.1 with karakurty
//-----------------------------------------------------------------------------
#ifndef _GUIRADARCTRL_H_
#define _GUIRADARCTRL_H_
#ifndef _GUIBITMAPCTRL_H_
#include "gui/controls/guiBitmapCtrl.h"
#endif
/// Renders a bitmap.
class GuiRadarCtrl : public GuiBitmapCtrl //GuiControl
{
private:
typedef /*GuiControl*/ GuiBitmapCtrl Parent;
Point3F mMyCoords;
protected:
// Radar
StringTableEntry mRadarBitmapName;
StringTableEntry mBlipUpName;
StringTableEntry mBlipLevelName;
StringTableEntry mBlipBelowName;
GFXTexHandle mRadarTextureObject;
GFXTexHandle mRadarDot;
GFXTexHandle mRadarUp;
GFXTexHandle mRadarDown;
bool mShowRadar;
bool mShowVehicles;
bool mShowPlayers;
bool mShowShapeBase;
bool mShowBots;
bool mShowFrame;
F32 mLevelRange;
F32 mRadarRadiusRange;
bool mHideAll;
//Compass
StringTableEntry mCompassBitmapName;
GFXTexHandle mCompassTextureObject;
bool mShowCompass;
// <MH>
// Map
StringTableEntry mMapBitmapName;
GFXTexHandle mMapTextureObject;
bool mShowMap;
F32 mCompassRotation;
// </MH>
Point2I startPoint;
//bool mRadarWrap;
GFXStateBlockRef mBlendDisabledStateBlock;
GFXStateBlockRef mBeforeStateBlock;
public:
//creation methods
DECLARE_CONOBJECT(GuiRadarCtrl);
GuiRadarCtrl();
static void initPersistFields();
//Parental methods
bool onWake();
void onSleep();
void inspectPostApply();
void setRadarBitmap(const char *name, bool resize = false);
void setRadarBitmap(GFXTexHandle handle, bool resize = false);
void setRadarLevelBlipBitmap(const char *name);
void setRadarUpBlipBitmap(const char *name);
void setRadarBelowBlipBitmap(const char *name);
void setCompassBitmap(const char *name, bool resize = false);
void setCompassBitmap(GFXTexHandle handle, bool resize = false);
void setMapBitmap(const char *name);
void setRadius(const F32 newRange);
void setLevelRange(const F32 newLevelRange);
S32 getWidth() const { return ( (mRadarTextureObject == NULL) ? 0 : mRadarTextureObject->getWidth() ) ; }
S32 getHeight() const { return( (mRadarTextureObject == NULL) ? 0 : mRadarTextureObject->getHeight() ); }
void setCompassRotation(const F32 rotation);
void onRender(Point2I offset, const RectI &updateRect);
void setupStateBlocks();
};
#endifNow the Radar/Map/Compass
now source/gui/control/GuiRadarTSCtrl.cpp
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
// Class: GuiRadarTSCtrl for TGEA
// Tested with TGEA 1.0.3
// A 3d Spherical Radar system, ideal for space and flight games
// If you've played Elite, you know the score :)
// Uses some ideas from the GuiRadarCtrl by Matt "CaptFallout" Webster
// Guy Allard 2008
//-----------------------------------------------------------------------------
// Upgraded and cleaned by James Laker
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "gui/core/guiControl.h"
#include "gfx/gfxDrawUtil.h"
#include "T3D/player.h"
#include "console/console.h"
#include "gfx/gfxDevice.h"
#include "app/game.h"
#include "T3D/ShapeBase.h"
#include "gui/controls/guiRadarTSCtrl.h"
#include "console/consoleTypes.h"
#include "t3d/gameConnection.h"
#include "t3d/shapeBase.h"
#include "t3d/sphere.h"
#include "gfx/primBuilder.h"
IMPLEMENT_CONOBJECT(GuiRadarTSCtrl);
// vertices for the quad used for rendering the elliptic plane
static const Point3F planeVerts[4]=
{
Point3F(-1, 1, 0),
Point3F(1, 1, 0),
Point3F(-1, -1, 0),
Point3F(1, -1, 0)
};
// and these are for the blip billboards
static const Point3F quadVerts[4]=
{
Point3F(-1, 0, 1),
Point3F(1, 0, 1),
Point3F(-1, 0, -1),
Point3F(1, 0, -1)
};
// texture coords for the quads (when drawn as a trianglestrip)
static const Point2F quadTexCoords[4] =
{
Point2F(0,0),
Point2F(1,0),
Point2F(0,1),
Point2F(1,1)
};
// sphere
static Sphere sphere(Sphere::Icosahedron);
//--------------------------------------------------------------------------
GuiRadarTSCtrl::GuiRadarTSCtrl()
{
mSphereDetail = 2;
// textures
mEllipticBitmap = StringTable->insert("");
mEllipticTextureHandle = NULL;
mBlipBitmap = StringTable->insert("");
mBlipTextureHandle = NULL;
mTargetBlipBitmap = StringTable->insert("");
mTargetBlipTextureHandle = NULL;
strcpy(mTargetShapeName,"");
// colors
mEllipticColor.set(0.0, 0.75, 0.0, 0.75);
mSphereColor.set(0.0f, 0.5f, 0.0f, 0.25f);
mBlipColor.set(1.0f, 1.0f, 1.0f, 1.0f);
mStemColor.set(1.0f, 1.0f, 1.0f, 1.0f);
// range
mRange = 250.0f;
// blip size
mBlipSize = 1.0f;
}
//---------------------------------------------------------------------------
void GuiRadarTSCtrl::initPersistFields()
{
Parent::initPersistFields();
addGroup("Radar");
addField("PlaneBitmap", TypeFilename, Offset(mEllipticBitmap, GuiRadarTSCtrl));
addField("PlaneColor", TypeColorF, Offset(mEllipticColor, GuiRadarTSCtrl));
addField("BlipBitmap", TypeFilename, Offset(mBlipBitmap, GuiRadarTSCtrl));
addField("BlipColor", TypeColorF, Offset(mBlipColor, GuiRadarTSCtrl));
addField("BlipSize", TypeF32, Offset(mBlipSize, GuiRadarTSCtrl));
addField("StemColor", TypeColorF, Offset(mStemColor, GuiRadarTSCtrl));
addField("SphereColor", TypeColorF, Offset(mSphereColor, GuiRadarTSCtrl));
addField("SphereDetail", TypeS32, Offset(mSphereDetail, GuiRadarTSCtrl));
addField("Range", TypeF32, Offset(mRange, GuiRadarTSCtrl));
endGroup("Radar");
}
void GuiRadarTSCtrl::setTarget(const char *TargetShapeBaseName)
{
if (*TargetShapeBaseName)
strcpy(mTargetShapeName,TargetShapeBaseName);
}
//---------------------------------------------------------------------------
void GuiRadarTSCtrl::setEllipticBitmap(const char *name)
{
mEllipticBitmap = StringTable->insert(name);
if (*mEllipticBitmap)
mEllipticTextureHandle = GFXTexHandle(mEllipticBitmap, &GFXDefaultStaticDiffuseProfile, "Adescription");
//mEllipticTextureHandle = GFXTexHandle(mEllipticBitmap, &GFXDefaultStaticDiffuseProfile);
else
// Reset handles if UI object is hidden
mEllipticTextureHandle = NULL;
setUpdate();
}
//---------------------------------------------------------------------------
void GuiRadarTSCtrl::setBlipBitmap(const char *name)
{
mBlipBitmap = StringTable->insert(name);
if (*mBlipBitmap)
mBlipTextureHandle = GFXTexHandle(mBlipBitmap, &GFXDefaultStaticDiffuseProfile, "Adescription");
//mBlipTextureHandle = GFXTexHandle(mBlipBitmap, &GFXDefaultStaticDiffuseProfile);
else
// Reset handles if UI object is hidden
mBlipTextureHandle = NULL;
setUpdate();
}
//---------------------------------------------------------------------------
bool GuiRadarTSCtrl::onWake()
{
if (! Parent::onWake())
return false;
setActive(true);
setEllipticBitmap(mEllipticBitmap);
setBlipBitmap(mBlipBitmap);
return true;
}
//--------------------------------------------------------------------------
void GuiRadarTSCtrl::onSleep()
{
mEllipticTextureHandle = NULL;
mBlipTextureHandle = NULL;
Parent::onSleep();
}
//---------------------------------------------------------------------------
// set up the camera position for rendering the 3D scene within this control
bool GuiRadarTSCtrl::processCameraQuery(CameraQuery *camq)
{
// pretty hacky hardcoded values
// gives a reasonable viewing angle
camq->nearPlane = 0.1f; // near clip plane
camq->farPlane = 20.0f; // far clip plane
camq->fov = (F32)M_PI/6; // field of view
MatrixF cam;
cam.set(EulerF((F32)M_PI/8, 0, 0)); // rotation
cam.setColumn(3, Point3F(0, -4, 1.65f)); // position
camq->cameraMatrix = cam;
return (true);
}
//---------------------------------------------------------------------------
// render the world that this control sees
void GuiRadarTSCtrl::renderWorld(const RectI &updateRect)
{
// Must have a connection
GameConnection* conn = GameConnection::getConnectionToServer();
if (!conn) return;
// Must have controlled object
GameBase * control = dynamic_cast<GameBase*>(conn->getControlObject());
if (!control) return;
// get the camera transform for the connection
MatrixF camMat;
conn->getControlCameraTransform(0,&camMat);
// get the position for use later
Point3F camPos = camMat.getPosition();
// invert the camera transform -
// this will then allow us to transform other objects into our own object space
camMat.inverse();
// set up the graphics
// HNG
/*
GFX->setAlphaBlendEnable(true);
GFX->setSrcBlend(GFXBlendSrcAlpha);
GFX->setDestBlend(GFXBlendInvSrcAlpha);
GFX->setZEnable( false );
GFX->setCullMode( GFXCullNone );
GFX->setTextureStageColorOp(0, GFXTOPModulate);
GFX->setTextureStageColorOp(1, GFXTOPDisable);
GFX->disableShaders();*/
GFXStateBlockDesc desc;
desc.zEnable = false;
desc.ffLighting = false;
desc.setCullMode( GFXCullNone );
desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
desc.samplers[0].textureColorOp = GFXTOPModulate;
desc.samplers[1].textureColorOp = GFXTOPDisable;
GFXStateBlockRef myState = GFX->createStateBlock(desc);
GFX->setStateBlock(myState);
// HNG
// first draw the elliptic plane if we need to
if(mEllipticTextureHandle)
{
GFXTextureObject *texture = mEllipticTextureHandle;
GFX->setTexture(0, texture);
PrimBuild::color(mEllipticColor);
PrimBuild::begin(GFXTriangleStrip, 4);
for(int i=0; i<4; i++)
{
PrimBuild::texCoord2f(quadTexCoords[i].x, quadTexCoords[i].y);
PrimBuild::vertex3fv(planeVerts[i]);
}
PrimBuild::end();
}
if (mSphereDetail > 4)
mSphereDetail = 4;
const Sphere::TriangleMesh *sphereMesh = sphere.getMesh(mSphereDetail); // sphere.getMesh(2) gives a slighty 'chunky' sphere - go higher if you can handle the framerate hit
S32 numPoly = sphereMesh->numPoly;
S32 totalPoly = 0;
GFXVertexBufferHandle<GFXVertexPC> verts(GFX, numPoly*3, GFXBufferTypeVolatile);
verts.lock();
S32 vertexIndex = 0;
for (S32 i=0; i<numPoly; i++)
{
totalPoly++;
verts[vertexIndex].point = sphereMesh->poly[i].pnt[0];
verts[vertexIndex].color = mSphereColor;
vertexIndex++;
verts[vertexIndex].point = sphereMesh->poly[i].pnt[1];
verts[vertexIndex].color = mSphereColor;
vertexIndex++;
verts[vertexIndex].point = sphereMesh->poly[i].pnt[2];
verts[vertexIndex].color = mSphereColor;
vertexIndex++;
}
verts.unlock();
GFXStateBlockDesc desc2;
desc2.setCullMode(GFXCullNone);
desc2.zEnable = false;
desc2.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
GFX->setStateBlockByDesc( desc2 );
GFX->setupGenericShaders( GFXDevice::GSColor );
GFX->setVertexBuffer( verts );
GFX->drawPrimitive( GFXTriangleList, 0, totalPoly );
GFX->setStateBlockByDesc( desc );
GFX->setupGenericShaders();
// Now do the radar signatures
// firstly, we will be drawing billboards, so get the current world transform matrix
MatrixF worldMat = GFX->getWorldMatrix();
// extract the up and right vectors
Point3F up;
Point3F right;
worldMat.getRow(0,&right);
worldMat.getRow(2,&up);
right.normalize();
up.normalize();
// then build the coordinates that we'll use for the billboards
// oops, another hard coded value, ah well...
right *= mBlipSize * 0.05f;
up *= mBlipSize * 0.05f;
// set up the texture that we'll use for the blips
// is this the correct way to do it???
GFXTextureObject *texture = mBlipTextureHandle;
GFXTextureObject *targetTexture = mTargetBlipTextureHandle;
// Go through all ghosted objects on connection (client-side)
for (SimSetIterator itr(conn); *itr; ++itr)
{
// Make sure that the object is a shapebase object
if ((*itr)->getType() & ShapeBaseObjectType)
{
ShapeBase* shape = static_cast<ShapeBase*>(*itr);
// Make sure that the object isn't the client
if (shape != control)
{
// Make sure the shapebase object is a player (or Vehicle)
if (shape->getType() & ( PlayerObjectType | VehicleObjectType ) )
{
// get position of object
Point3F objPos = shape->getPosition();
// get vector between object and observer
Point3F objVec = objPos - camPos;
// don't draw if outside of current radar range
if(objVec.lenSquared() > (mRange * mRange))
continue;
// transform it into the coordinate space of the viewer
camMat.mulP(objPos);
// scale it according to the current radar range
objPos /= mRange;
// compress the z transform a bit, just for looks
//objPos.z *= 0.5f;
if (!strcmp(mTargetShapeName,""))
GFX->setTexture(0, targetTexture);
else
GFX->setTexture(0, texture);
// draw the blip
// unfortunately, DX doesn't appear to allow specification of point size or line width like opengl
// SO, lets build a quad using the view aligned coordinates we generated earlier....
desc.samplers[0].textureColorOp = GFXTOPModulate;
//GFX->setTextureStageColorOp(0, GFXTOPModulate);
PrimBuild::color(mBlipColor);
PrimBuild::begin(GFXTriangleStrip, 4);
PrimBuild::texCoord2f(quadTexCoords[0].x, quadTexCoords[0].y);
PrimBuild::vertex3fv(objPos - right + up);
PrimBuild::texCoord2f(quadTexCoords[1].x, quadTexCoords[1].y);
PrimBuild::vertex3fv(objPos + right + up);
PrimBuild::texCoord2f(quadTexCoords[2].x, quadTexCoords[2].y);
PrimBuild::vertex3fv(objPos - right - up);
PrimBuild::texCoord2f(quadTexCoords[3].x, quadTexCoords[3].y);
PrimBuild::vertex3fv(objPos + right - up);
PrimBuild::end();
// draw a line from the blip to the elliptic - will have to use quads if you want a line thicker than 1px
// end point of line
Point3F endPos = objPos;
endPos.z = 0;
// draw the line
desc.samplers[1].textureColorOp = GFXTOPDisable;
//GFX->setTextureStageColorOp(0, GFXTOPDisable);
PrimBuild::color(mStemColor);
PrimBuild::begin(GFXLineList, 2);
PrimBuild::vertex3fv(objPos);
PrimBuild::vertex3fv(endPos);
PrimBuild::end();
}
}
}
}
// all done
GFX->setClipRect(updateRect);
}
//------------------------------------------------------------------------
// script interface
ConsoleMethod( GuiRadarTSCtrl, setEllipticBitmap, void, 3, 3, "(string filename)"
"Set the bitmap for the horizontal plane of the control.")
{
object->setEllipticBitmap(argv[2]);
}
ConsoleMethod( GuiRadarTSCtrl, SetRange, void, 3, 3, "(float)"
"Sets the Range of the the control. Default is 250")
{
object->setRange(dAtof(argv[2]));
}
ConsoleMethod( GuiRadarTSCtrl, getRange, F32, 2, 2, "()"
"gets the current range of the control")
{
return object->getRange();
}
ConsoleMethod( GuiRadarTSCtrl, setEllipticColor, void, 3, 3, "(Color)"
"set the color of the horizontal plane")
{
ColorF color;
dSscanf(argv[2], "%g %g %g %g", &color.red, &color.green, &color.blue, &color.alpha);
object->setEllipticColor(color);
}
ConsoleMethod( GuiRadarTSCtrl, setSphereColor, void, 3, 3, "(Color)"
"set the color of the sphere")
{
ColorF color;
dSscanf(argv[2], "%g %g %g %g", &color.red, &color.green, &color.blue, &color.alpha);
object->setSphereColor(color);
}
ConsoleMethod( GuiRadarTSCtrl, setBlipColor, void, 3, 3, "(Color)"
"set the color of the radar blips")
{
ColorF color;
dSscanf(argv[2], "%g %g %g %g", &color.red, &color.green, &color.blue, &color.alpha);
object->setBlipColor(color);
}
ConsoleMethod( GuiRadarTSCtrl, setStemColor, void, 3, 3, "(Color)"
"set the color of the blip lines")
{
ColorF color;
dSscanf(argv[2], "%g %g %g %g", &color.red, &color.green, &color.blue, &color.alpha);
object->setStemColor(color);
}
ConsoleMethod( GuiRadarTSCtrl, setTarget, void, 2, 3, "(str Target ShapebaseName)"
"Set the target ShapeBaseName, if no Target is sent it is cleared.")
{
if (argv[2])
object->setTarget(argv[2]);
else
object->setTarget("");
}now source/gui/control/GuiRadarTSCtrl.h
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIRADARTSCTRL_H_
#define _GUIRADARTSCTRL_H_
#ifndef _GUITSCONTROL_H_
#include "gui/3d/guiTSControl.h"
#endif
#ifndef _GFXTEXTUREHANDLE_H_
#include "gfx/gfxTextureHandle.h"
#endif
//----------------------------------------------------------------------------
class GuiRadarTSCtrl : public GuiTSCtrl
{
private:
typedef GuiTSCtrl Parent;
StringTableEntry mEllipticBitmap; // path to the bitmap for the horizontal plane
GFXTexHandle mEllipticTextureHandle; // texture handle for the horizontal plane
StringTableEntry mBlipBitmap; // path to the bitmap we'll use for the blips
GFXTexHandle mBlipTextureHandle; // texture handle for the blip texture
StringTableEntry mTargetBlipBitmap; // path to the bitmap we'll use for the blips
GFXTexHandle mTargetBlipTextureHandle; // texture handle for the blip texture
S32 mSphereDetail;
ColorF mEllipticColor; // color to use when drawing the elliptic plane
ColorF mSphereColor; // color of the sphere
ColorF mBlipColor; // color for the radar 'blips'
ColorF mStemColor; // color for the stems of the blips
F32 mRange; // range of the radar
F32 mBlipSize; // base size for the blips
char mTargetShapeName[32]; //This keeps the name of the locked target
public:
GuiRadarTSCtrl();
bool processCameraQuery(CameraQuery *query);
void renderWorld(const RectI &updateRect);
static void initPersistFields();
bool onWake();
void onSleep();
// gets and sets
void setEllipticBitmap(const char *name);
void setBlipBitmap(const char *name);
void setRange(F32 range) { mRange = range; }
F32 getRange() { return mRange; }
void setEllipticColor(ColorF color) { mEllipticColor = color; }
void setSphereColor(ColorF color) { mSphereColor = color; }
void setBlipColor(ColorF color) { mBlipColor = color; }
void setStemColor(ColorF color) { mStemColor = color; }
void setTarget(const char *TargetShapeBaseName);
DECLARE_CONOBJECT(GuiRadarTSCtrl);
};
#endifThe videos of the stuff in action.
About the author
http://www.hngamers.com
#2
Also, small goof on my part in the package. The following files need to be in folder in art/gui/compass_map_radar
GUIRadarTSCtrl will be under 3D and GuiRadarCtrl will be under Images in the gui Library.
03/24/2010 (10:48 am)
For T3D 1.1 in both of the .cpp files change your includes with the info below. It compiles without issue and still displays or doesn't for that matter, the sphere. Also, small goof on my part in the package. The following files need to be in folder in art/gui/compass_map_radar
GUIRadarTSCtrl will be under 3D and GuiRadarCtrl will be under Images in the gui Library.
compass.png dot_black.png dot_blue.png dot_green.png dot_red.png dot_teal.png radar_base.png scorchedPlanet.png
#include "T3D/gameConnection.h"to
#include "T3D/gameBase/gameConnection.h"
#3
03/24/2010 (11:14 am)
top guy! cheers Sean
#4
03/24/2010 (1:07 pm)
Sean, try using GFXDrawUtil::drawSphere instead of drawing it manually.
#5
03/24/2010 (2:28 pm)
Not sure thats the issue, the sphere is actually there, it's just transparent. If you remove GFX->setStateBlock(myState); from the statement, the sphere appears and is solid. You can adjust the color, but not the alpha. Without the line the sphere is there all the time. Adding the line and you can see the sphere until exiting the gui editor, then it disappears from the screen. Enter the editor and the sphere reappears.
#6
03/24/2010 (11:28 pm)
Sean, it if is a texture, then be sure to export it in a paint program as alpha transparency. Not sure if this is the issue, but worth a shot.
#7
03/25/2010 (4:56 pm)
Sean, I have the fix for the sphere rendering. In GuiRadarTSCtrl.cpp -> renderWorld Replace:// then the sphere
// I was planning on texturing the sphere at some point....or maybe even a nice shader......
PrimBuild::color(mSphereColor);
desc.samplers[0].textureColorOp = GFXTOPDisable;
//GFX->setTextureStageColorOp( 0, GFXTOPDisable ); // turn off texture
const Sphere::TriangleMesh *sphereMesh = sphere.getMesh(mSphereDetail); // sphere.getMesh(2) gives a slighty 'chunky' sphere - go higher if you can handle the framerate hit
PrimBuild::begin(GFXTriangleList, (sphereMesh->numPoly * 3));
for(int i=0; i<sphereMesh->numPoly; i++)
{
Sphere::Triangle *tri = &sphereMesh->poly[i];
for(int j=0; j<3; j++)
PrimBuild::vertex3fv(tri->pnt[j]);
}
PrimBuild::end();withconst Sphere::TriangleMesh *sphereMesh = sphere.getMesh(mSphereDetail); // sphere.getMesh(2) gives a slighty 'chunky' sphere - go higher if you can handle the framerate hit
S32 numPoly = sphereMesh->numPoly;
S32 totalPoly = 0;
GFXVertexBufferHandle<GFXVertexPC> verts(GFX, numPoly*3, GFXBufferTypeVolatile);
verts.lock();
S32 vertexIndex = 0;
for (S32 i=0; i<numPoly; i++)
{
totalPoly++;
verts[vertexIndex].point = sphereMesh->poly[i].pnt[0];
verts[vertexIndex].color = mSphereColor;
vertexIndex++;
verts[vertexIndex].point = sphereMesh->poly[i].pnt[1];
verts[vertexIndex].color = mSphereColor;
vertexIndex++;
verts[vertexIndex].point = sphereMesh->poly[i].pnt[2];
verts[vertexIndex].color = mSphereColor;
vertexIndex++;
}
verts.unlock();
GFXStateBlockDesc desc2;
desc2.setCullMode(GFXCullNone);
desc2.zEnable = false;
desc2.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
GFX->setStateBlockByDesc( desc2 );
GFX->setupGenericShaders( GFXDevice::GSColor );
GFX->setVertexBuffer( verts );
GFX->drawPrimitive( GFXTriangleList, 0, totalPoly );
GFX->setStateBlockByDesc( desc );
GFX->setupGenericShaders();
#8
03/25/2010 (7:18 pm)
And so you have! Many thanks, that was definite above my head without an example.
#9
03/26/2010 (8:10 am)
MUST.....RESIST......URGE......TO......MODIFY.............
#10
03/26/2010 (9:50 am)
Hahaha, please modify away! Man, I love this thing with the sphere on it, absolutely completes it. Although, I have yet to try the blips to see if your code fixed that as well. It seems to have corrected the small color issue that was present too.
#11
03/26/2010 (10:27 am)
Yeah, the sphere got me thinking about trying to impose a texture on it for a compass and/or artificial horizon. Also I seem to like to animate textures so I started to think about adding texture swapping on the plane so you can make it look like scan lines radiating out. Oh boy, here we go. If I make any progress I'll let you know (my wife is going to kill me).
#12
Hmmm, I would like to see about getting this to rotate like the other Radar, you could use one control instead of two then.
03/26/2010 (10:30 am)
Blame me if you need to... Look honey, it's all THIS duded fault! He even says it is! :pHmmm, I would like to see about getting this to rotate like the other Radar, you could use one control instead of two then.
#13
and change it to
This will cause it to render blips.
03/28/2010 (10:50 am)
In preparation to the Flight Game Port, we will need to adjust one thing in these resources. In guiRadarCtrl.cpp locateif (shape != control) && shape->getShapeName()) {and change it to
// HNG
// if (shape != control) && shape->getShapeName()) {
// HNG
if (shape != control) {This will cause it to render blips.
#14
03/28/2010 (1:08 pm)
Thanks Sean! This is really coming in handy.
#15
04/06/2010 (12:44 am)
Thanks for posting this resource :)
#19
As you can see, some parts of the radar are only displayed over the sky, but hidden by terrain and other objects.
09/02/2010 (7:02 am)
I'm using the 2D radar in T3D 1.0.1, and I'm having the following issue.
As you can see, some parts of the radar are only displayed over the sky, but hidden by terrain and other objects.
#20
08/25/2011 (6:51 am)
I'm sure many people are or have used this great resource. However, the current version does not work with T3D 1.1, changes to the placement of code from the sphere.h if I recall correctly. Has anyone got this to work with 1.1? If so, could you spill your wisdom upon us? Thanks in advance guys! 

Torque Owner Jules
Something2Play