# Updates to the entity bar display system by Andrew.

Bars and rank icons now scale with zoom level. I've left in the old
render functions for now, but they can be removed later.

This was SVN commit r4120.
This commit is contained in:
Matei 2006-07-16 19:32:48 +00:00
parent a833ef0770
commit 4630745625
9 changed files with 391 additions and 48 deletions

View File

@ -60,7 +60,7 @@ void CSprite::Render()
BeginBillboard();
glDisable(GL_CULL_FACE);
if ( m_texture->GetHandle() != 0 )
if ( m_texture && m_texture->GetHandle() != 0 )
ogl_tex_bind(m_texture->GetHandle());
glColor4fv(m_colour);
@ -187,6 +187,14 @@ void CSprite::SetColour(float * colour)
m_colour[3] = colour[3];
}
void CSprite::SetColour(float r, float g, float b, float a)
{
m_colour[0] = r;
m_colour[1] = g;
m_colour[2] = b;
m_colour[3] = a;
}
//Must call glPushMatrix() before this. Should be called before any other gl calls
void CSprite::BeginBillboard()
{

View File

@ -457,4 +457,190 @@ public:
};
// Matei's slightly friendlier hashtable for value-type keys
template<typename K, typename T, typename HashCompare >
class MateiHashTbl
{
static const size_t initial_entries = 16;
struct Entry {
bool valid;
K key;
T value;
Entry() : valid(false) {}
Entry(const K& k, T v) { key=k; value=v; }
Entry& operator=(const Entry& other) {
valid = other.valid;
key = other.key;
value = other.value;
return *this;
}
};
Entry* tbl;
u16 num_entries;
u16 max_entries; // when initialized, = 2**n for faster modulo
HashCompare hashFunc;
Entry& get_slot(K key) const
{
size_t hash = hashFunc(key);
//debug_assert(max_entries != 0); // otherwise, mask will be incorrect
const uint mask = max_entries-1;
for(;;)
{
Entry& e = tbl[hash & mask];
// empty slot encountered => not found
if(!e.valid)
return e;
// keys are actually equal => found it
if(e.key == key)
return e;
// keep going (linear probing)
hash++;
}
}
void expand_tbl()
{
// alloc a new table (but don't assign it to <tbl> unless successful)
Entry* old_tbl = tbl;
tbl = new Entry[max_entries*2];
if(!tbl)
{
tbl = old_tbl;
throw std::bad_alloc();
}
max_entries += max_entries;
// must be set before get_slot
// newly initialized, nothing to copy - done
if(!old_tbl)
return;
// re-hash from old table into the new one
for(size_t i = 0; i < max_entries/2u; i++)
{
Entry& e = old_tbl[i];
if(e.valid)
get_slot(e.key) = e;
}
delete[] old_tbl;
}
void delete_contents()
{
if(tbl)
{
delete[] tbl;
tbl = 0;
}
}
public:
MateiHashTbl()
{
tbl = 0;
num_entries = 0;
max_entries = initial_entries/2; // will be doubled in expand_tbl
//debug_assert(is_pow2(max_entries));
expand_tbl();
}
~MateiHashTbl()
{
delete_contents();
}
void clear()
{
delete_contents();
num_entries = 0;
// rationale: must not set to 0 because expand_tbl only doubles the size.
// don't keep the previous size because it may have become huge and
// there is no provision for shrinking.
max_entries = initial_entries/2; // will be doubled in expand_tbl
expand_tbl();
}
bool contains(const K& key) const
{
return get_slot(key).valid;
}
T& operator[](const K& key)
{
Entry* slot = &get_slot(key);
if(slot->valid)
{
return slot->value;
}
// no element exists for this key - insert it into the table
// (this is slightly different from STL::hash_map in that we insert a new element
// on a get for a nonexistent key, but hopefully that's not a problem)
// if more than 75% full, increase table size and find slot again
if(num_entries*4 >= max_entries*3)
{
expand_tbl();
slot = &get_slot(key); // find slot again since we expanded
}
slot->valid = true;
slot->key = key;
num_entries++;
return slot->value;
}
size_t size() const
{
return num_entries;
}
// Not an STL iterator, more like a Java one
// Usage: for(HashTable::Iterator it(table); it.valid(); it.advance()) { do stuff to it.key() and it.value() }
class Iterator
{
private:
Entry* pos;
Entry* end;
public:
Iterator(const MateiHashTbl& ht)
{
pos = ht.tbl;
end = ht.tbl + ht.max_entries;
while(pos < end && !pos->valid)
pos++;
};
bool valid() const
{
return pos < end;
}
void advance()
{
do {
pos++;
}
while(pos < end && !pos->valid);
}
const K& key()
{
return pos->key;
}
T& value()
{
return pos->value;
}
};
};
#endif // #ifndef ADTS_H__

View File

@ -318,57 +318,57 @@ void Render()
{
PROFILE( "render entity overlays" );
glColor3f( 1.0f, 0.0f, 1.0f );
MICROLOG(L"render entities");
g_EntityManager.renderAll(); // <-- collision outlines, pathing routes
}
PROFILE_START( "render selection" );
PROFILE_START( "render entity outlines" );
glEnable( GL_DEPTH_TEST );
g_Mouseover.renderSelectionOutlines();
g_Selection.renderSelectionOutlines();
glDisable( GL_DEPTH_TEST );
PROFILE_END( "render selection" );
PROFILE_END( "render entity outlines" );
PROFILE_START( "render health bars" );
PROFILE_START( "render entity bars" );
pglActiveTextureARB(GL_TEXTURE1_ARB);
glDisable(GL_TEXTURE_2D);
//Not all hardware supports 3 texture units!
//pglActiveTextureARB(GL_TEXTURE2_ARB);
//glDisable(GL_TEXTURE_2D);
pglActiveTextureARB(GL_TEXTURE0_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
/*glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f);
glOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.0f, 1000.f);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glLoadIdentity();*/
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable( GL_TEXTURE_2D );
g_Mouseover.renderBarBorders();
g_Selection.renderBarBorders();
glDisable( GL_TEXTURE_2D );
g_Mouseover.renderHealthBars();
g_Selection.renderHealthBars();
g_Mouseover.renderStaminaBars();
g_Selection.renderStaminaBars();
glEnable( GL_TEXTURE_2D );
g_Selection.renderRanks();
g_Mouseover.renderRanks();
g_Mouseover.renderBars();
g_Selection.renderBars();
glPopMatrix();
glDisable(GL_BLEND);
/*glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
PROFILE_END( "render health bars" );
glMatrixMode(GL_MODELVIEW);*/
PROFILE_END( "render entity bars" );
glPopAttrib();
//Depth test is now enabled
// Depth test is now enabled
PROFILE_START( "render rally points" );
g_Selection.renderRallyPoints();
g_Mouseover.renderRallyPoints();
PROFILE_END( "render rally points" );
}
else
{

View File

@ -91,6 +91,26 @@ void CSelectedEntities::renderSelectionOutlines()
}
}
void CSelectedEntities::renderBars()
{
std::vector<HEntity>::iterator it;
for( it = m_selected.begin(); it < m_selected.end(); it++ )
(*it)->renderBars();
/*if( m_group_highlight != -1 )
{
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glEnable( GL_BLEND );
std::vector<HEntity>::iterator it;
for( it = m_groups[m_group_highlight].begin(); it < m_groups[m_group_highlight].end(); it++ )
(*it)->renderBars();
glDisable( GL_BLEND );
}*/
}
void CSelectedEntities::renderHealthBars()
{
std::vector<HEntity>::iterator it;
@ -839,6 +859,13 @@ void CMouseoverEntities::renderSelectionOutlines()
glDisable( GL_BLEND );
}
void CMouseoverEntities::renderBars()
{
std::vector<SMouseoverFader>::iterator it;
for( it = m_mouseover.begin(); it < m_mouseover.end(); it++ )
it->entity->renderBars();
}
void CMouseoverEntities::renderHealthBars()
{
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

View File

@ -77,11 +77,12 @@ struct CSelectedEntities : public Singleton<CSelectedEntities>
void renderSelectionOutlines();
void renderOverlays();
void renderRallyPoints();
void renderBars();
void renderHealthBars();
void renderStaminaBars();
void renderRanks();
void renderBarBorders();
void renderRallyPoints();
void destroyUnitUITextures();
int loadUnitUITextures();
@ -134,11 +135,12 @@ struct CMouseoverEntities : public Singleton<CMouseoverEntities>
void renderSelectionOutlines();
void renderOverlays();
void renderRallyPoints();
void renderBars();
void renderHealthBars();
void renderStaminaBars();
void renderRanks();
void renderBarBorders();
void renderRallyPoints();
bool isBandbox() { return( m_bandbox ); }
void startBandbox( u16 x, u16 y );

View File

@ -89,8 +89,8 @@ CEntity::CEntity( CEntityTemplate* base, CVector3D position, float orientation,
entf_clear(ENTF_HEALTH_DECAY);
m_frameCheck = 0;
m_lastCombatTime = 0;
m_lastRunTime = 0;
m_lastCombatTime = -100;
m_lastRunTime = -100;
m_currentNotification = 0;
m_currentRequest = 0;
entf_set(ENTF_DESTROY_NOTIFIERS);
@ -1123,6 +1123,7 @@ void CEntity::renderSelectionOutline( float alpha )
glEnd();
}
CVector2D CEntity::getScreenCoords( float height )
{
CCamera &g_Camera=*g_Game->GetView()->GetCamera();
@ -1135,6 +1136,92 @@ CVector2D CEntity::getScreenCoords( float height )
g_Camera.GetScreenCoordinates(above, sx, sy);
return CVector2D( sx, sy );
}
void CEntity::drawRect( CVector3D& centre, CVector3D& up, CVector3D& right, float x1, float y1, float x2, float y2 )
{
glBegin(GL_QUADS);
const int X[] = {1,1,0,0}; // which X and Y to choose at each vertex
const int Y[] = {0,1,1,0};
for( int i=0; i<4; i++ )
{
CVector3D vec = centre + right * (X[i] ? x1 : x2) + up * (Y[i] ? y1 : y2);
glTexCoord2f( X[i], Y[i] );
glVertex3fv( &vec.X );
}
glEnd();
}
void CEntity::drawBar( CVector3D& centre, CVector3D& up, CVector3D& right,
float x1, float y1, float x2, float y2,
SColour col1, SColour col2, float currVal, float maxVal )
{
// Figure out fraction that should be col1
float fraction;
if(maxVal == 0) fraction = 1.0f;
else fraction = clamp( currVal / maxVal, 0.0f, 1.0f );
// Draw the border at full size
ogl_tex_bind( g_Selection.m_unitUITextures[m_base->m_barBorder] );
drawRect( centre, up, right, x1, y1, x2, y2 );
ogl_tex_bind( 0 );
// Make the bar contents slightly smaller than the border
x1 += m_base->m_barBorderSize;
y1 += m_base->m_barBorderSize;
x2 -= m_base->m_barBorderSize;
y2 -= m_base->m_barBorderSize;
// Draw the bar contents
float xMid = x2 * fraction + x1 * (1.0f - fraction);
glColor3fv( &col1.r );
drawRect( centre, up, right, x1, y1, xMid, y2 );
glColor3fv( &col2.r );
drawRect( centre, up, right, xMid, y1, x2, y2 );
}
void CEntity::renderBars()
{
if( !m_base->m_barsEnabled || !m_bounds || !m_visible)
return;
snapToGround();
CVector3D centre = m_graphics_position;
centre.Y += m_base->m_barOffset;
CVector3D up = g_Game->GetView()->GetCamera()->m_Orientation.GetUp();
CVector3D right = -g_Game->GetView()->GetCamera()->m_Orientation.GetLeft();
float w = m_base->m_barWidth;
float h = m_base->m_barHeight;
float borderSize = m_base->m_barBorderSize;
// Draw the health and stamina bars; if the unit has no stamina, the health bar is
// drawn centered, otherwise it's offset slightly up and the stamina bar is offset
// slightly down so that they overlap over an area of size borderSize.
bool hasStamina = (m_staminaMax > 0);
float off = hasStamina ? (h/2 - borderSize/2) : 0;
drawBar( centre, up, right, -w/2, off-h/2, w/2, off+h/2,
SColour(0,1,0), SColour(1,0,0), m_healthCurr, m_healthMax );
if( hasStamina )
{
drawBar( centre, up, right, -w/2, borderSize/2-h, w/2, borderSize/2,
SColour(0,0,1), SColour(0.4f,0.4f,0.1f), m_staminaCurr, m_staminaMax );
}
// Draw the rank icon
std::map<CStr, Handle>::iterator it = g_Selection.m_unitUITextures.find( m_rankName );
if( it != g_Selection.m_unitUITextures.end() )
{
float size = hasStamina ? (2*h - 3*borderSize) : h;
ogl_tex_bind( it->second );
drawRect( centre, up, right, w/2, -size/2, w/2+size, size/2 );
ogl_tex_bind( 0 );
}
}
void CEntity::renderBarBorders()
{
if( !m_visible )
@ -1183,6 +1270,7 @@ void CEntity::renderBarBorders()
glEnd();
}
}
void CEntity::renderHealthBar()
{
if( !m_bounds || !m_visible )
@ -1217,7 +1305,6 @@ void CEntity::renderHealthBar()
glEnd();
glLineWidth(1.0f);
}
void CEntity::renderStaminaBar()
@ -1254,6 +1341,7 @@ void CEntity::renderStaminaBar()
glEnd();
glLineWidth(1.0f);
}
void CEntity::renderRank()
{
if( !m_bounds || !m_visible )
@ -1293,6 +1381,7 @@ void CEntity::renderRank()
glEnd();
}
void CEntity::renderRallyPoint()
{
if( !m_visible )
@ -1322,6 +1411,7 @@ void CEntity::renderRallyPoint()
sprite.SetTranslation(rally);
sprite.Render();
}
void CEntity::CalculateRun(float timestep)
{
if( m_staminaMax > 0 )

View File

@ -36,6 +36,7 @@
#include "ScriptObject.h"
#include "EntitySupport.h"
#include "scripting/DOMEvent.h"
#include "scripting/ScriptCustomTypes.h"
// JW: must be pulled in because CEntity no longer mirrors CEntityTemplate exactly.
// some fields have been moved out of CEntity and are accessed via m_base,
@ -343,12 +344,23 @@ public:
// Things like selection circles and debug info - possibly move to gui if/when it becomes responsible for (and capable of) it.
void render();
void renderSelectionOutline( float alpha = 1.0f );
void renderBarBorders( );
void renderBars();
void renderBarBorders();
void renderHealthBar();
void renderStaminaBar();
void renderRank();
void renderRallyPoint();
// Utility functions for rendering:
// Draw rectangle around the given centre, aligned with the given axes
void drawRect(CVector3D& centre, CVector3D& up, CVector3D& right, float x1, float y1, float x2, float y2);
void drawBar(CVector3D& centre, CVector3D& up, CVector3D& right,
float x1, float y1, float x2, float y2,
SColour col1, SColour col2, float currVal, float maxVal);
CVector2D getScreenCoords( float height );
// After a collision, recalc the path to the next fixed waypoint.
void repath();

View File

@ -452,6 +452,12 @@ void CEntityTemplate::ScriptingInit()
AddClassProperty( L"traits.anchor.conformz", &CEntityTemplate::m_anchorConformZ );
AddClassProperty( L"traits.vision.los", &CEntityTemplate::m_los );
AddClassProperty( L"traits.vision.permanent", &CEntityTemplate::m_permanent );
AddClassProperty( L"traits.display.bars.enabled", &CEntityTemplate::m_barsEnabled );
AddClassProperty( L"traits.display.bars.offset", &CEntityTemplate::m_barOffset );
AddClassProperty( L"traits.display.bars.height", &CEntityTemplate::m_barHeight );
AddClassProperty( L"traits.display.bars.width", &CEntityTemplate::m_barWidth );
AddClassProperty( L"traits.display.bars.border", &CEntityTemplate::m_barBorder );
AddClassProperty( L"traits.display.bars.border_size", &CEntityTemplate::m_barBorderSize );
AddClassProperty( L"traits.is_territory_centre", &CEntityTemplate::m_isTerritoryCentre );
AddClassProperty( L"traits.foundation", &CEntityTemplate::m_foundation );
AddClassProperty( L"traits.socket", &CEntityTemplate::m_socket );

View File

@ -65,32 +65,44 @@ public:
//SP properties
float m_staminaCurr;
float m_staminaMax;
float m_staminaBarHeight;
int m_staminaBarSize;
float m_staminaBarWidth;
int m_staminaBorderWidth;
int m_staminaBorderHeight;
CStr m_staminaBorderName;
// HP properties
float m_healthCurr;
float m_healthMax;
float m_healthBarHeight;
int m_healthBarSize;
float m_healthBarWidth;
int m_healthBorderWidth;
int m_healthBorderHeight;
CStr m_healthBorderName;
float m_healthRegenRate;
float m_healthRegenStart;
float m_healthDecayRate;
//Rank properties
// Display properties
bool m_barsEnabled;
float m_barOffset;
float m_barHeight;
float m_barWidth;
float m_barBorderSize;
CStr m_barBorder;
float m_staminaBarHeight;
int m_staminaBarSize;
float m_staminaBarWidth;
int m_staminaBorderWidth;
int m_staminaBorderHeight;
CStr m_staminaBorderName;
float m_healthBarHeight;
int m_healthBarSize;
float m_healthBarWidth;
int m_healthBorderWidth;
int m_healthBorderHeight;
CStr m_healthBorderName;
float m_rankWidth;
float m_rankHeight;
//Rank properties
CStr m_rankName;
//Rally name
CStr m_rallyName;