RPG Object Selection Hud (TGEA 1.8.x)
by Taras (TSK) Anatsko · 03/13/2009 (1:54 pm) · 3 comments
Original resource by Matt Huston
Tested with TGEA 1.8.1
Ported by TSK
Overview
Object selection similar to Deus Ex, System Shock and other Action-RPGs. A outline rectangle is drawn and the objects name is printed at the top right corner. To add new objects, check the ObjectMask but remember you will need to make sure the object type being added has its own CastRay function or it won't be picked up. ItemObjectType for instance doesn't have one so you will need to add it yourself.
Add guiObjectSelectionHud.cpp to your project in the engine/T3D/fps directory.
Compile!
Add the following to the bottom of you playGui.gui.
Tested with TGEA 1.8.1
Ported by TSK
Overview
Object selection similar to Deus Ex, System Shock and other Action-RPGs. A outline rectangle is drawn and the objects name is printed at the top right corner. To add new objects, check the ObjectMask but remember you will need to make sure the object type being added has its own CastRay function or it won't be picked up. ItemObjectType for instance doesn't have one so you will need to add it yourself.
Add guiObjectSelectionHud.cpp to your project in the engine/T3D/fps directory.
//-----------------------------------------------------------------------------
// guiObjectSelectionHud.cpp
//
// Description: Action-RPG object selection gui element.
//
// Author: Matt Huston (matthuston@gmail.com)
// Date: June 28th, 2007
//
// TGEA 1.8.x port
// by TSK (TSK@TSKGames.com)
//-----------------------------------------------------------------------------
#include "gui/core/guiControl.h"
#include "gui/3d/guiTSControl.h"
#include "console/consoleTypes.h"
#include "sceneGraph/sceneGraph.h"
#include "T3D/shapeBase.h"
#include "T3D/gameConnection.h"
class GuiObjectSelectionHud : public GuiControl
{
typedef GuiControl Parent;
S32 mOffset;
ColorF mTextColor;
ColorF mTextOutlineColor;
ColorF mTextFillColor;
ColorF mOutlineColor;
protected:
void drawTargetingRect( const Point2I &a, const Point2I &b, F32 offset, const ColorI &color );
void drawName( Point2I offset, const char *buf, F32 opacity);
public:
DECLARE_CONOBJECT( GuiObjectSelectionHud );
GuiObjectSelectionHud();
static void initPersistFields();
void onRender( Point2I, const RectI &);
};
// Objects we want the castRay to detect
static const U32 ObjectMask = PlayerObjectType | VehicleObjectType;
IMPLEMENT_CONOBJECT( GuiObjectSelectionHud );
GuiObjectSelectionHud::GuiObjectSelectionHud()
{
mOffset = 10.0f;
mTextColor.set(0.2f, 0.2f, 0.2f, 1.0f);
mTextOutlineColor.set(0.0f, 1.0f, 0.0f, 1.0f);
mTextFillColor.set(0.8f, 0.8f, 0.8f, 0.8f);
mOutlineColor.set(0.0f, 1.0f, 0.0f, 1.0f);
}
void GuiObjectSelectionHud::initPersistFields()
{
Parent::initPersistFields();
addField( "Offset", TypeS32, Offset( mOffset, GuiObjectSelectionHud ) );
addField( "TextColor", TypeColorF, Offset( mTextColor, GuiObjectSelectionHud ) );
addField( "TextOutlineColor", TypeColorF, Offset( mTextOutlineColor, GuiObjectSelectionHud ) );
addField( "TextFillColor", TypeColorF, Offset( mTextFillColor, GuiObjectSelectionHud ) );
addField( "OutlineColor", TypeColorF, Offset( mOutlineColor, GuiObjectSelectionHud ) );
}
void GuiObjectSelectionHud::onRender(Point2I offset, const RectI &updateRect)
{
// Must have a connection and player control object
GameConnection* conn = GameConnection::getConnectionToServer();
if (!conn)
return;
GameBase * control = dynamic_cast<GameBase*>(conn->getControlObject());
if (!control) return;
GuiTSCtrl *parent = dynamic_cast<GuiTSCtrl*>(getParent());
if (!parent)
return;
// Parent render.
Parent::onRender(offset, updateRect);
// Get control camera info
MatrixF cam;
Point3F camPos;
conn->getControlCameraTransform(0, &cam);
cam.getColumn(3, &camPos);
// Extend the camera vector to create an endpoint for our ray
Point3F endPos;
cam.getColumn(1, &endPos);
endPos *= 10.0f;
endPos += camPos;
// Collision info. We're going to be running LOS tests and we don't
// want to collide with the control object, terrain or interiors.
static U32 losMask = TerrainObjectType | InteriorObjectType | ShapeBaseObjectType;
control->disableCollision();
RayInfo info;
if (gClientContainer.castRay(camPos, endPos, losMask, &info))
{
if (ShapeBase* obj = dynamic_cast<ShapeBase*>(info.object))
{
// If the object has a name, we will allow it to be selected
if (obj->getShapeName())
{
Box3F bounds = obj->getRenderWorldBox();
// Generate the 8 bounding points of the box.
Point3F pts[8];
U32 i = 0;
pts[i].x = bounds.minExtents.x;
pts[i].y = bounds.minExtents.y;
pts[i].z = bounds.minExtents.z;
i++;
pts[i].x = bounds.minExtents.x;
pts[i].y = bounds.minExtents.y;
pts[i].z = bounds.maxExtents.z;
i++;
pts[i].x = bounds.minExtents.x;
pts[i].y = bounds.maxExtents.y;
pts[i].z = bounds.minExtents.z;
i++;
pts[i].x = bounds.minExtents.x;
pts[i].y = bounds.maxExtents.y;
pts[i].z = bounds.maxExtents.z;
i++;
pts[i].x = bounds.maxExtents.x;
pts[i].y = bounds.minExtents.y;
pts[i].z = bounds.minExtents.z;
i++;
pts[i].x = bounds.maxExtents.x;
pts[i].y = bounds.minExtents.y;
pts[i].z = bounds.maxExtents.z;
i++;
pts[i].x = bounds.maxExtents.x;
pts[i].y = bounds.maxExtents.y;
pts[i].z = bounds.minExtents.z;
i++;
pts[i].x = bounds.maxExtents.x;
pts[i].y = bounds.maxExtents.y;
pts[i].z = bounds.maxExtents.z;
i++;
Point3F boxCenter;
bounds.getCenter(&boxCenter);
Point3F rectCenter;
parent->project(boxCenter, &rectCenter);
RectI rect(rectCenter.x, rectCenter.y, 1, 1);
Point3F pt;
for(S32 i = 0 ; i < 8; i++)
{
parent->project(pts[i], &pt);
if (pt.x < rect.point.x)
{
rect.point.x = pt.x;
}
if (pt.y < rect.point.y)
{
rect.point.y = pt.y;
}
}
for(S32 i = 0 ; i < 8 ; i++)
{
parent->project(pts[i], &pt);
if (pt.x > rect.point.x + rect.extent.x)
{
rect.extent.x = pt.x - rect.point.x;
}
if (pt.y > rect.point.y + rect.extent.y)
{
rect.extent.y = pt.y - rect.point.y;
}
}
// Draw box
drawTargetingRect(rect.point,
Point2I(rect.extent.x + rect.point.x - 1, rect.extent.y + rect.point.y - 1),
20.0f,
mOutlineColor);
drawName(Point2I(rect.point.x, rect.point.y), obj->getShapeName(), 1.0f);
}
}
}
// Restore control object collision
control->enableCollision();
}
void GuiObjectSelectionHud::drawName(Point2I offset, const char *name, F32 opacity)
{
offset.x += 5.0f;
offset.y += 5.0f;
// Text outline rectangle
GFX->getDrawUtil()->drawRect(Point2I(offset.x, offset.y),
Point2I(offset.x + mProfile->mFont->getStrWidth((const UTF8 *)name) + mOffset,
offset.y + mProfile->mFont->getHeight()),
mTextOutlineColor);
// Text outline fill rectangle
GFX->getDrawUtil()->drawRectFill(Point2I(offset.x + 1.0f, offset.y + 1.0f),
Point2I(offset.x + mProfile->mFont->getStrWidth((const UTF8 *)name) + mOffset - 1.0f,
offset.y + mProfile->mFont->getHeight() - 1.0f),
mTextFillColor);
// Position text in the rectangle and draw
offset.x = offset.x + (mOffset / 2);
GFX->getDrawUtil()->setBitmapModulation(mTextColor);
GFX->getDrawUtil()->drawText(mProfile->mFont, offset, name);
GFX->getDrawUtil()->clearBitmapModulation();
}
void GuiObjectSelectionHud::drawTargetingRect( const Point2I &a, const Point2I &b, F32 offset, const ColorI &color )
{
//GFX->setBaseRenderState();
// NorthWest and NorthEast facing offset vectors
Point2F l(-0.5f, -0.5f);
Point2F r(+0.5f, -0.5f);
for(int i = 0 ; i < 4 ; i++)
{
GFXVertexBufferHandle<GFXVertexPC> verts (GFX, 8, GFXBufferTypeVolatile );
verts.lock();
switch(i)
{
// Top Left Corner
case 0:
verts[0].point.set( a.x, a.y - l.y, 0.0f );
verts[1].point.set( a.x, a.y + l.y, 0.0f );
verts[2].point.set( a.x + offset, a.y + r.y, 0.0f );
verts[3].point.set( a.x + offset, a.y - r.y, 0.0f );
verts[4].point.set( a.x - l.x, a.y, 0.0f );
verts[5].point.set( a.x + l.x, a.y, 0.0f );
verts[6].point.set( a.x + r.x, a.y + offset, 0.0f );
verts[7].point.set( a.x - r.x, a.y + offset, 0.0f );
break;
// Top Right Corner
case 1:
verts[0].point.set( b.x, a.y - l.y, 0.0f );
verts[1].point.set( b.x, a.y + l.y, 0.0f );
verts[2].point.set( b.x - offset, a.y + r.y, 0.0f );
verts[3].point.set( b.x - offset, a.y - r.y, 0.0f );
verts[4].point.set( b.x - l.x, a.y, 0.0f );
verts[5].point.set( b.x + l.x, a.y, 0.0f );
verts[6].point.set( b.x + r.x, a.y + offset, 0.0f );
verts[7].point.set( b.x - r.x, a.y + offset, 0.0f );
break;
// Bottom Left Corner
case 2:
verts[0].point.set( a.x, b.y - l.y, 0.0f );
verts[1].point.set( a.x, b.y + l.y, 0.0f );
verts[2].point.set( a.x + offset, b.y + r.y, 0.0f );
verts[3].point.set( a.x + offset, b.y - r.y, 0.0f );
verts[4].point.set( a.x - l.x, b.y, 0.0f );
verts[5].point.set( a.x + l.x, b.y, 0.0f );
verts[6].point.set( a.x + r.x, b.y - offset, 0.0f );
verts[7].point.set( a.x - r.x, b.y - offset, 0.0f );
break;
// Bottom Right Corner
case 3:
verts[0].point.set( b.x, b.y - l.y, 0.0f );
verts[1].point.set( b.x, b.y + l.y, 0.0f );
verts[2].point.set( b.x - offset, b.y + r.y, 0.0f );
verts[3].point.set( b.x - offset, b.y - r.y, 0.0f );
verts[4].point.set( b.x - l.x, b.y, 0.0f );
verts[5].point.set( b.x + l.x, b.y, 0.0f );
verts[6].point.set( b.x + r.x, b.y - offset, 0.0f );
verts[7].point.set( b.x - r.x, b.y - offset, 0.0f );
break;
}
for (int j = 0 ; j < 8 ; j++)
verts[j].color = color;
verts.unlock();
GFX->setVertexBuffer( verts );
GFX->drawPrimitive( GFXTriangleStrip, 0, 6 );
}
}Compile!
Add the following to the bottom of you playGui.gui.
new GuiObjectSelectionHud() {
canSaveDynamicFields = "0";
Profile = "GuiDefaultProfile";
HorizSizing = "relative";
VertSizing = "relative";
Position = "0 0";
Extent = "640 480";
MinExtent = "8 2";
canSave = "1";
Visible = "1";
hovertime = "1000";
TextColor = "1 1 1 1";
TextOutlineColor = "1 1 1 1";
TextFillColor = "0.3 0.3 0.3 0.75";
OutlineColor = "1 1 1 1";
Offset = "10";
};
#2
03/14/2009 (6:13 am)
Clever and very easy to modify, thanks to your (and Matt Huston) clean coding style. I immediately incorporated this into an 'world editor improvement' i have been working on, saved me 2 days(estimated) work!
#3
03/15/2009 (9:09 pm)
I did get the old version to work in TGEA, but thanks for the port so i could clean up my code :D
Torque 3D Owner Benster
Internal Inferno