diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/health_bg.png b/binaries/data/mods/public/art/textures/ui/session/icons/health_bg.png
new file mode 100644
index 0000000000..40f9cf6a87
--- /dev/null
+++ b/binaries/data/mods/public/art/textures/ui/session/icons/health_bg.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ebe285df7d0dfd9279f62028c3a0766dbe6ccb55b3e95ebcf9150e89bc30a476
+size 119
diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/health_fg.png b/binaries/data/mods/public/art/textures/ui/session/icons/health_fg.png
new file mode 100644
index 0000000000..723539cde7
--- /dev/null
+++ b/binaries/data/mods/public/art/textures/ui/session/icons/health_fg.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:21d175d74574ef8b044e79ac3968acfafb141c5aa608d70500569e2b099a1354
+size 108
diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/textures.xml b/binaries/data/mods/public/art/textures/ui/session/icons/textures.xml
new file mode 100644
index 0000000000..e808281b94
--- /dev/null
+++ b/binaries/data/mods/public/art/textures/ui/session/icons/textures.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/binaries/data/mods/public/gui/session_new/selection.js b/binaries/data/mods/public/gui/session_new/selection.js
index 3d7d42afec..a1c1662785 100644
--- a/binaries/data/mods/public/gui/session_new/selection.js
+++ b/binaries/data/mods/public/gui/session_new/selection.js
@@ -4,6 +4,12 @@ function _setHighlight(ents, alpha)
Engine.GuiInterfaceCall("SetSelectionHighlight", { "entities":ents, "alpha":alpha });
}
+function _setStatusBars(ents, enabled)
+{
+ if (ents.length)
+ Engine.GuiInterfaceCall("SetStatusBars", { "entities":ents, "enabled":enabled });
+}
+
function _setMotionOverlay(ents, enabled)
{
if (ents.length)
@@ -207,12 +213,14 @@ EntitySelection.prototype.toggle = function(ent)
if (this.selected[ent])
{
_setHighlight([ent], 0);
+ _setStatusBars([ent], false);
_setMotionOverlay([ent], false);
delete this.selected[ent];
}
else
{
_setHighlight([ent], 1);
+ _setStatusBars([ent], true);
_setMotionOverlay([ent], this.motionDebugOverlay);
this.selected[ent] = ent;
}
@@ -231,6 +239,7 @@ EntitySelection.prototype.addList = function(ents)
}
}
_setHighlight(added, 1);
+ _setStatusBars(added, true);
_setMotionOverlay(added, this.motionDebugOverlay);
if (added.length)
_playSound(added[0]);
@@ -240,6 +249,7 @@ EntitySelection.prototype.addList = function(ents)
EntitySelection.prototype.reset = function()
{
_setHighlight(this.toList(), 0);
+ _setStatusBars(this.toList(), false);
_setMotionOverlay(this.toList(), false);
this.selected = {};
this.dirty = true;
@@ -256,29 +266,32 @@ EntitySelection.prototype.toList = function()
EntitySelection.prototype.setHighlightList = function(ents)
{
+ var highlighted = {};
+ for each (var ent in ents)
+ highlighted[ent] = ent;
+
var removed = [];
var added = [];
- // Remove highlighting for the old units (excluding ones that are actively selected too)
+ // Remove highlighting for the old units that are no longer highlighted
+ // (excluding ones that are actively selected too)
for each (var ent in this.highlighted)
- if (!this.selected[ent])
- removed.push(ent);
+ if (!highlighted[ent] && !this.selected[ent])
+ removed.push(+ent);
- // Add new highlighting
+ // Add new highlighting for units that aren't already highlighted
for each (var ent in ents)
- if (!this.selected[ent])
- added.push(ent);
+ if (!this.highlighted[ent] && !this.selected[ent])
+ added.push(+ent);
_setHighlight(removed, 0);
- _setHighlight(added, 0.5);
+ _setStatusBars(removed, false);
- // TODO: this could be a bit more efficient by only changing the ones that
- // have entered/left the highlight list
+ _setHighlight(added, 0.5);
+ _setStatusBars(added, true);
- // Store the new list
- this.highlighted = {};
- for each (var ent in ents)
- this.highlighted[ent] = ent;
+ // Store the new highlight list
+ this.highlighted = highlighted;
};
EntitySelection.prototype.SetMotionDebugOverlay = function(enabled)
diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js
index a5c9b153d9..e2a50ccdd7 100644
--- a/binaries/data/mods/public/simulation/components/GuiInterface.js
+++ b/binaries/data/mods/public/simulation/components/GuiInterface.js
@@ -233,6 +233,17 @@ GuiInterface.prototype.SetSelectionHighlight = function(player, cmd)
}
};
+GuiInterface.prototype.SetStatusBars = function(player, cmd)
+{
+ for each (var ent in cmd.entities)
+ {
+ var cmpStatusBars = Engine.QueryInterface(ent, IID_StatusBars);
+ if (cmpStatusBars)
+ cmpStatusBars.SetEnabled(cmd.enabled);
+ }
+};
+
+/**
/**
* Displays the rally point of a building
*/
@@ -395,6 +406,7 @@ var exposedFunctions = {
"GetNextNotification": 1,
"SetSelectionHighlight": 1,
+ "SetStatusBars": 1,
"DisplayRallyPoint": 1,
"SetBuildingPlacementPreview": 1,
"PlaySound": 1,
diff --git a/binaries/data/mods/public/simulation/components/Health.js b/binaries/data/mods/public/simulation/components/Health.js
index 15ffca1e65..567971fd40 100644
--- a/binaries/data/mods/public/simulation/components/Health.js
+++ b/binaries/data/mods/public/simulation/components/Health.js
@@ -56,7 +56,10 @@ Health.prototype.SetHitpoints = function(value)
if (this.hitpoints == 0)
return;
+ var old = this.hitpoints;
this.hitpoints = Math.max(1, Math.min(this.GetMaxHitpoints(), value));
+
+ Engine.PostMessage(this.entity, MT_HealthChanged, { "from": old, "to": this.hitpoints });
};
Health.prototype.IsRepairable = function()
@@ -84,13 +87,20 @@ Health.prototype.Reduce = function(amount)
this.CreateCorpse();
Engine.DestroyEntity(this.entity);
+
+ var old = this.hitpoints;
+ this.hitpoints = 0;
+
+ Engine.PostMessage(this.entity, MT_HealthChanged, { "from": old, "to": this.hitpoints });
}
- this.hitpoints = 0;
}
else
{
+ var old = this.hitpoints;
this.hitpoints -= amount;
+
+ Engine.PostMessage(this.entity, MT_HealthChanged, { "from": old, "to": this.hitpoints });
}
};
@@ -100,7 +110,10 @@ Health.prototype.Increase = function(amount)
if (this.hitpoints == 0)
return;
+ var old = this.hitpoints;
this.hitpoints = Math.min(this.hitpoints + amount, this.GetMaxHitpoints());
+
+ Engine.PostMessage(this.entity, MT_HealthChanged, { "from": old, "to": this.hitpoints });
};
//// Private functions ////
diff --git a/binaries/data/mods/public/simulation/components/StatusBars.js b/binaries/data/mods/public/simulation/components/StatusBars.js
new file mode 100644
index 0000000000..2a10f0539f
--- /dev/null
+++ b/binaries/data/mods/public/simulation/components/StatusBars.js
@@ -0,0 +1,74 @@
+function StatusBars() {}
+
+StatusBars.prototype.Schema =
+ "" +
+ "" +
+ "";
+
+// TODO: should add rank icon too
+
+StatusBars.prototype.Init = function()
+{
+ this.enabled = false;
+};
+
+StatusBars.prototype.SetEnabled = function(enabled)
+{
+ // Quick return if no change
+ if (enabled == this.enabled)
+ return;
+
+ // Update the displayed sprites
+
+ this.enabled = enabled;
+
+ if (enabled)
+ this.RegenerateSprites();
+ else
+ this.ResetSprites();
+};
+
+StatusBars.prototype.OnHealthChanged = function(msg)
+{
+ if (this.enabled)
+ this.RegenerateSprites();
+};
+
+StatusBars.prototype.ResetSprites = function()
+{
+ var cmpOverlayRenderer = Engine.QueryInterface(this.entity, IID_OverlayRenderer);
+ cmpOverlayRenderer.Reset();
+};
+
+StatusBars.prototype.RegenerateSprites = function()
+{
+ var cmpOverlayRenderer = Engine.QueryInterface(this.entity, IID_OverlayRenderer);
+ cmpOverlayRenderer.Reset();
+
+ // Size of health bar (in world-space units)
+ var width = 2;
+ var height = 1/3;
+
+ // Offset from the unit's position
+ var offset = { "x": 0, "y": +this.template.HeightOffset, "z": 0 };
+
+ var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
+ if (cmpHealth)
+ {
+ var filled = cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints();
+
+ cmpOverlayRenderer.AddSprite(
+ "art/textures/ui/session/icons/health_bg.png",
+ { "x": -width/2, "y": -height/2 }, { "x": width/2, "y": height/2 },
+ offset
+ );
+
+ cmpOverlayRenderer.AddSprite(
+ "art/textures/ui/session/icons/health_fg.png",
+ { "x": -width/2, "y": -height/2 }, { "x": width*(filled - 0.5), "y": height/2 },
+ offset
+ );
+ }
+};
+
+Engine.RegisterComponentType(IID_StatusBars, "StatusBars", StatusBars);
diff --git a/binaries/data/mods/public/simulation/components/interfaces/Health.js b/binaries/data/mods/public/simulation/components/interfaces/Health.js
index 8040a457c3..5f31193407 100644
--- a/binaries/data/mods/public/simulation/components/interfaces/Health.js
+++ b/binaries/data/mods/public/simulation/components/interfaces/Health.js
@@ -1 +1,5 @@
Engine.RegisterInterface("Health");
+
+// Message of the form { "from": 100, "to", 90 },
+// sent whenever health changes.
+Engine.RegisterMessageType("HealthChanged");
diff --git a/binaries/data/mods/public/simulation/components/interfaces/StatusBars.js b/binaries/data/mods/public/simulation/components/interfaces/StatusBars.js
new file mode 100644
index 0000000000..c56cf6b919
--- /dev/null
+++ b/binaries/data/mods/public/simulation/components/interfaces/StatusBars.js
@@ -0,0 +1 @@
+Engine.RegisterInterface("StatusBars");
diff --git a/binaries/data/mods/public/simulation/templates/template_structure.xml b/binaries/data/mods/public/simulation/templates/template_structure.xml
index 01658d0be7..1d0b86e86c 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure.xml
@@ -46,6 +46,7 @@
12.0
+
36
true
diff --git a/binaries/data/mods/public/simulation/templates/template_unit.xml b/binaries/data/mods/public/simulation/templates/template_unit.xml
index e6770b41f6..d2f12271cb 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit.xml
@@ -57,6 +57,7 @@
5.0
+
2.5
diff --git a/source/graphics/Overlay.h b/source/graphics/Overlay.h
index 6c967cd169..6ae03f0a49 100644
--- a/source/graphics/Overlay.h
+++ b/source/graphics/Overlay.h
@@ -18,11 +18,13 @@
#ifndef INCLUDED_GRAPHICS_OVERLAY
#define INCLUDED_GRAPHICS_OVERLAY
+#include "graphics/Texture.h"
+#include "maths/Vector3D.h"
#include "ps/Overlay.h" // CColor (TODO: that file has nothing to do with overlays, it should be renamed)
/**
- * Line-based overlay. Exists in world-space, but gets rendered on top
- * of all other objects. Designed for selection circles.
+ * Line-based overlay, with world-space coordinates, rendered in the world
+ * potentially behind other objects. Designed for selection circles and debug info.
*/
struct SOverlayLine
{
@@ -33,6 +35,17 @@ struct SOverlayLine
u8 m_Thickness; // pixels
};
-// TODO: OverlaySprite, OverlayText
+/**
+ * Billboard sprite overlay, with world-space coordinates, rendered on top
+ * of all other objects. Designed for health bars and rank icons.
+ */
+struct SOverlaySprite
+{
+ CTexturePtr m_Texture;
+ CVector3D m_Position; // base position
+ float m_X0, m_Y0, m_X1, m_Y1; // billboard corner coordinates, relative to base position
+};
+
+// TODO: OverlayText
#endif // INCLUDED_GRAPHICS_OVERLAY
diff --git a/source/renderer/OverlayRenderer.cpp b/source/renderer/OverlayRenderer.cpp
index e71def3688..aa781694d0 100644
--- a/source/renderer/OverlayRenderer.cpp
+++ b/source/renderer/OverlayRenderer.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 2009 Wildfire Games.
+/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -20,12 +20,14 @@
#include "OverlayRenderer.h"
#include "graphics/Overlay.h"
+#include "graphics/TextureManager.h"
#include "lib/ogl.h"
#include "renderer/Renderer.h"
struct OverlayRendererInternals
{
std::vector lines;
+ std::vector sprites;
};
OverlayRenderer::OverlayRenderer()
@@ -43,9 +45,15 @@ void OverlayRenderer::Submit(SOverlayLine* overlay)
m->lines.push_back(overlay);
}
+void OverlayRenderer::Submit(SOverlaySprite* overlay)
+{
+ m->sprites.push_back(overlay);
+}
+
void OverlayRenderer::EndFrame()
{
m->lines.clear();
+ m->sprites.clear();
// this should leave the capacity unchanged, which is okay since it
// won't be very large or very variable
}
@@ -79,3 +87,46 @@ void OverlayRenderer::RenderOverlays()
glLineWidth(1.f);
glDisable(GL_BLEND);
}
+
+void OverlayRenderer::RenderForegroundOverlays(const CCamera& viewCamera)
+{
+ glEnable(GL_TEXTURE_2D);
+ glEnable(GL_BLEND);
+ glDisable(GL_DEPTH_TEST);
+
+ CVector3D right = -viewCamera.m_Orientation.GetLeft();
+ CVector3D up = viewCamera.m_Orientation.GetUp();
+
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ float uvs[8] = { 0,0, 1,0, 1,1, 0,1 };
+ glTexCoordPointer(2, GL_FLOAT, sizeof(float)*2, &uvs);
+
+ for (size_t i = 0; i < m->sprites.size(); ++i)
+ {
+ SOverlaySprite* sprite = m->sprites[i];
+
+ sprite->m_Texture->Bind();
+
+ CVector3D pos[4] = {
+ sprite->m_Position + right*sprite->m_X0 + up*sprite->m_Y0,
+ sprite->m_Position + right*sprite->m_X1 + up*sprite->m_Y0,
+ sprite->m_Position + right*sprite->m_X1 + up*sprite->m_Y1,
+ sprite->m_Position + right*sprite->m_X0 + up*sprite->m_Y1
+ };
+
+ glVertexPointer(3, GL_FLOAT, sizeof(float)*3, &pos[0].X);
+
+ glDrawArrays(GL_QUADS, 0, (GLsizei)4);
+ }
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ glEnable(GL_DEPTH_TEST);
+ glDisable(GL_BLEND);
+ glDisable(GL_TEXTURE_2D);
+}
diff --git a/source/renderer/OverlayRenderer.h b/source/renderer/OverlayRenderer.h
index 67852bf8ec..67e1559d6c 100644
--- a/source/renderer/OverlayRenderer.h
+++ b/source/renderer/OverlayRenderer.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2009 Wildfire Games.
+/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -19,6 +19,8 @@
#define INCLUDED_OVERLAYRENDERER
struct SOverlayLine;
+struct SOverlaySprite;
+class CCamera;
struct OverlayRendererInternals;
@@ -37,6 +39,11 @@ public:
*/
void Submit(SOverlayLine* overlay);
+ /**
+ * Add a sprite overlay for rendering in this frame.
+ */
+ void Submit(SOverlaySprite* overlay);
+
/**
* Prepare internal data structures for rendering.
* Must be called after all Submit calls for a frame, and before
@@ -50,10 +57,18 @@ public:
void EndFrame();
/**
- * Render all the submitted overlays.
+ * Render all the submitted overlays that are embedded in the world
+ * (i.e. rendered behind other objects, underwater, etc).
*/
void RenderOverlays();
+ /**
+ * Render all the submitted overlays that should appear on top of everything
+ * in the world.
+ * @param viewCamera camera to be used for billboard computations
+ */
+ void RenderForegroundOverlays(const CCamera& viewCamera);
+
private:
OverlayRendererInternals* m;
};
diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp
index e659fe3373..78e110e0d3 100644
--- a/source/renderer/Renderer.cpp
+++ b/source/renderer/Renderer.cpp
@@ -1311,6 +1311,12 @@ void CRenderer::RenderSubmissions()
ogl_WarnIfError();
}
+ // render overlays that should appear on top of all other objects
+ PROFILE_START("render fg overlays");
+ m->overlayRenderer.RenderForegroundOverlays(m_ViewCamera);
+ PROFILE_END("render fg overlays");
+ ogl_WarnIfError();
+
// empty lists
MICROLOG(L"empty lists");
m->terrainRenderer->EndFrame();
@@ -1392,6 +1398,11 @@ void CRenderer::Submit(SOverlayLine* overlay)
m->overlayRenderer.Submit(overlay);
}
+void CRenderer::Submit(SOverlaySprite* overlay)
+{
+ m->overlayRenderer.Submit(overlay);
+}
+
void CRenderer::SubmitNonRecursive(CModel* model)
{
if (model->GetFlags() & MODELFLAG_CASTSHADOWS) {
diff --git a/source/renderer/Renderer.h b/source/renderer/Renderer.h
index 7128d0924a..af8a8df9f1 100644
--- a/source/renderer/Renderer.h
+++ b/source/renderer/Renderer.h
@@ -338,6 +338,7 @@ protected:
//BEGIN: Implementation of SceneCollector
void Submit(CPatch* patch);
void Submit(SOverlayLine* overlay);
+ void Submit(SOverlaySprite* overlay);
void SubmitNonRecursive(CModel* model);
//END: Implementation of SceneCollector
diff --git a/source/renderer/Scene.h b/source/renderer/Scene.h
index eaa1ca5dc7..356a52b136 100644
--- a/source/renderer/Scene.h
+++ b/source/renderer/Scene.h
@@ -32,6 +32,7 @@ class CFrustum;
class CModel;
class CPatch;
struct SOverlayLine;
+struct SOverlaySprite;
class SceneCollector;
@@ -72,10 +73,15 @@ public:
virtual void Submit(CPatch* patch) = 0;
/**
- * Submit a line-based overlay that is part of the scene.
+ * Submit a line-based overlay.
*/
virtual void Submit(SOverlayLine* overlay) = 0;
+ /**
+ * Submit a sprite overlay.
+ */
+ virtual void Submit(SOverlaySprite* overlay) = 0;
+
/**
* Submit a model that is part of the scene,
* without submitting attached models.
diff --git a/source/simulation2/TypeList.h b/source/simulation2/TypeList.h
index c12313cbbe..cc08c6eb83 100644
--- a/source/simulation2/TypeList.h
+++ b/source/simulation2/TypeList.h
@@ -82,6 +82,9 @@ COMPONENT(Obstruction)
INTERFACE(ObstructionManager)
COMPONENT(ObstructionManager)
+INTERFACE(OverlayRenderer)
+COMPONENT(OverlayRenderer)
+
INTERFACE(Ownership)
COMPONENT(Ownership)
@@ -109,9 +112,6 @@ COMPONENT(Selectable)
INTERFACE(SoundManager)
COMPONENT(SoundManager)
-INTERFACE(StatusBars)
-COMPONENT(StatusBars)
-
INTERFACE(Terrain)
COMPONENT(Terrain)
diff --git a/source/simulation2/components/CCmpOverlayRenderer.cpp b/source/simulation2/components/CCmpOverlayRenderer.cpp
new file mode 100644
index 0000000000..d7513068c2
--- /dev/null
+++ b/source/simulation2/components/CCmpOverlayRenderer.cpp
@@ -0,0 +1,147 @@
+/* Copyright (C) 2010 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#include "precompiled.h"
+
+#include "simulation2/system/Component.h"
+#include "ICmpOverlayRenderer.h"
+
+#include "ICmpPosition.h"
+#include "simulation2/MessageTypes.h"
+
+#include "graphics/Overlay.h"
+#include "graphics/TextureManager.h"
+#include "renderer/Renderer.h"
+
+class CCmpOverlayRenderer : public ICmpOverlayRenderer
+{
+public:
+ static void ClassInit(CComponentManager& componentManager)
+ {
+ componentManager.SubscribeToMessageType(MT_Interpolate);
+ componentManager.SubscribeToMessageType(MT_RenderSubmit);
+ }
+
+ DEFAULT_COMPONENT_ALLOCATOR(OverlayRenderer)
+
+ // Currently-enabled set of sprites
+ std::vector m_Sprites;
+
+ // For each entry in m_Sprites, store the offset of the sprite from the unit's position
+ // (so we can recompute the sprite position after the unit moves)
+ std::vector m_SpriteOffsets;
+
+ // Whether the sprites should be drawn (only valid between Interpolate and RenderSubmit)
+ bool m_Enabled;
+
+ static std::string GetSchema()
+ {
+ return "";
+ }
+
+ virtual void Init(const CSimContext& UNUSED(context), const CParamNode& UNUSED(paramNode))
+ {
+ }
+
+ virtual void Deinit(const CSimContext& UNUSED(context))
+ {
+ }
+
+ virtual void Serialize(ISerializer& UNUSED(serialize))
+ {
+ // TODO: should we do anything here?
+ // or should we expect other components to reinitialise us
+ // after deserialization?
+ }
+
+ virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& UNUSED(deserialize))
+ {
+ Init(context, paramNode);
+ }
+
+ virtual void HandleMessage(const CSimContext& UNUSED(context), const CMessage& msg, bool UNUSED(global))
+ {
+ switch (msg.GetType())
+ {
+ case MT_Interpolate:
+ {
+ const CMessageInterpolate& msgData = static_cast (msg);
+ Interpolate(msgData.frameTime, msgData.offset);
+ break;
+ }
+ case MT_RenderSubmit:
+ {
+ const CMessageRenderSubmit& msgData = static_cast (msg);
+ RenderSubmit(msgData.collector);
+ break;
+ }
+ }
+ }
+
+ virtual void Reset()
+ {
+ m_Sprites.clear();
+ m_SpriteOffsets.clear();
+ }
+
+ virtual void AddSprite(std::wstring textureName, CFixedVector2D corner0, CFixedVector2D corner1, CFixedVector3D position)
+ {
+ CTextureProperties textureProps(textureName);
+
+ SOverlaySprite sprite;
+ sprite.m_Texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
+ sprite.m_X0 = corner0.X.ToFloat();
+ sprite.m_Y0 = corner0.Y.ToFloat();
+ sprite.m_X1 = corner1.X.ToFloat();
+ sprite.m_Y1 = corner1.Y.ToFloat();
+
+ m_Sprites.push_back(sprite);
+ m_SpriteOffsets.push_back(CVector3D(position));
+ }
+
+ void Interpolate(float UNUSED(frameTime), float frameOffset)
+ {
+ // Disable rendering of the unit if it has no position
+ CmpPtr cmpPosition(GetSimContext(), GetEntityId());
+ if (cmpPosition.null() || !cmpPosition->IsInWorld())
+ {
+ m_Enabled = false;
+ return;
+ }
+
+ // Find the precise position of the unit
+ CMatrix3D transform(cmpPosition->GetInterpolatedTransform(frameOffset, false));
+ CVector3D position(transform.GetTranslation());
+
+ // Move all the sprites to the desired offset relative to the unit
+ for (size_t i = 0; i < m_Sprites.size(); ++i)
+ m_Sprites[i].m_Position = position + m_SpriteOffsets[i];
+
+ m_Enabled = true;
+ }
+
+ void RenderSubmit(SceneCollector& collector)
+ {
+ if (!m_Enabled)
+ return;
+
+ for (size_t i = 0; i < m_Sprites.size(); ++i)
+ collector.Submit(&m_Sprites[i]);
+ }
+};
+
+REGISTER_COMPONENT_TYPE(OverlayRenderer)
diff --git a/source/simulation2/components/CCmpStatusBars.cpp b/source/simulation2/components/CCmpStatusBars.cpp
deleted file mode 100644
index 766aeea35d..0000000000
--- a/source/simulation2/components/CCmpStatusBars.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Copyright (C) 2010 Wildfire Games.
- * This file is part of 0 A.D.
- *
- * 0 A.D. is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * 0 A.D. is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with 0 A.D. If not, see .
- */
-
-#include "precompiled.h"
-
-#include "simulation2/system/Component.h"
-#include "ICmpStatusBars.h"
-
-class CCmpStatusBars : public ICmpStatusBars
-{
-public:
- static void ClassInit(CComponentManager& UNUSED(componentManager))
- {
- }
-
- DEFAULT_COMPONENT_ALLOCATOR(StatusBars)
-
- /*
- * TODO: this all needs to be designed and implemented
- */
-
- static std::string GetSchema()
- {
- return
- ""
- ""
- "";
- }
-
- virtual void Init(const CSimContext& UNUSED(context), const CParamNode& UNUSED(paramNode))
- {
- }
-
- virtual void Deinit(const CSimContext& UNUSED(context))
- {
- }
-
- virtual void Serialize(ISerializer& UNUSED(serialize))
- {
- }
-
- virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& UNUSED(deserialize))
- {
- Init(context, paramNode);
- }
-};
-
-REGISTER_COMPONENT_TYPE(StatusBars)
diff --git a/source/simulation2/components/ICmpStatusBars.cpp b/source/simulation2/components/ICmpOverlayRenderer.cpp
similarity index 70%
rename from source/simulation2/components/ICmpStatusBars.cpp
rename to source/simulation2/components/ICmpOverlayRenderer.cpp
index 72fb4410e5..898cc85ff3 100644
--- a/source/simulation2/components/ICmpStatusBars.cpp
+++ b/source/simulation2/components/ICmpOverlayRenderer.cpp
@@ -17,9 +17,11 @@
#include "precompiled.h"
-#include "ICmpStatusBars.h"
+#include "ICmpOverlayRenderer.h"
#include "simulation2/system/InterfaceScripted.h"
-BEGIN_INTERFACE_WRAPPER(StatusBars)
-END_INTERFACE_WRAPPER(StatusBars)
+BEGIN_INTERFACE_WRAPPER(OverlayRenderer)
+DEFINE_INTERFACE_METHOD_0("Reset", void, ICmpOverlayRenderer, Reset)
+DEFINE_INTERFACE_METHOD_4("AddSprite", void, ICmpOverlayRenderer, AddSprite, std::wstring, CFixedVector2D, CFixedVector2D, CFixedVector3D)
+END_INTERFACE_WRAPPER(OverlayRenderer)
diff --git a/source/simulation2/components/ICmpOverlayRenderer.h b/source/simulation2/components/ICmpOverlayRenderer.h
new file mode 100644
index 0000000000..9ec89a1857
--- /dev/null
+++ b/source/simulation2/components/ICmpOverlayRenderer.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2010 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#ifndef INCLUDED_ICMPOVERLAYRENDERER
+#define INCLUDED_ICMPOVERLAYRENDERER
+
+#include "simulation2/system/Interface.h"
+
+#include "simulation2/helpers/Position.h"
+
+#include "maths/FixedVector2D.h"
+#include "maths/FixedVector3D.h"
+
+/**
+ * Interface for rendering 'overlay' objects (typically sprites), automatically
+ * positioned relative to the entity.
+ * Usually driven by the StatusBars component.
+ *
+ * (TODO: maybe we should add a "category" argument to Reset/AddSprite/etc,
+ * so different components can each maintain independent sets of overlays here?)
+ */
+class ICmpOverlayRenderer : public IComponent
+{
+public:
+ /**
+ * Delete all sprites that have been previously added.
+ */
+ virtual void Reset() = 0;
+
+ /**
+ * Add a new textured billboard sprite to be rendered.
+ * @param textureName filename of texture to render.
+ * @param corner0,corner1 coordinates of sprite's corners, in world-space units oriented with the camera plane,
+ * relative to the sprite position.
+ * @param offset world-space offset of sprite position from the entity's base position.
+ */
+ virtual void AddSprite(std::wstring textureName, CFixedVector2D corner0, CFixedVector2D corner1, CFixedVector3D offset) = 0;
+
+ DECLARE_INTERFACE_TYPE(OverlayRenderer)
+};
+
+#endif // INCLUDED_ICMPOVERLAYRENDERER
diff --git a/source/simulation2/components/ICmpStatusBars.h b/source/simulation2/components/ICmpStatusBars.h
deleted file mode 100644
index f49d38c8d3..0000000000
--- a/source/simulation2/components/ICmpStatusBars.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* Copyright (C) 2010 Wildfire Games.
- * This file is part of 0 A.D.
- *
- * 0 A.D. is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * 0 A.D. is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with 0 A.D. If not, see .
- */
-
-#ifndef INCLUDED_ICMPSTATUSBARS
-#define INCLUDED_ICMPSTATUSBARS
-
-#include "simulation2/system/Interface.h"
-
-/**
- * Health/stamina status bar renderer.
- */
-class ICmpStatusBars : public IComponent
-{
-public:
- DECLARE_INTERFACE_TYPE(StatusBars)
-};
-
-#endif // INCLUDED_ICMPSTATUSBARS
diff --git a/source/simulation2/scripting/EngineScriptConversions.cpp b/source/simulation2/scripting/EngineScriptConversions.cpp
index 0053dc97ca..f06db98b34 100644
--- a/source/simulation2/scripting/EngineScriptConversions.cpp
+++ b/source/simulation2/scripting/EngineScriptConversions.cpp
@@ -20,6 +20,7 @@
#include "scriptinterface/ScriptInterface.h"
#include "maths/Fixed.h"
+#include "maths/FixedVector2D.h"
#include "maths/FixedVector3D.h"
#include "ps/CLogger.h"
#include "ps/Overlay.h"
@@ -242,3 +243,24 @@ template<> jsval ScriptInterface::ToJSVal(JSContext* cx, const C
return OBJECT_TO_JSVAL(obj);
}
+
+template<> bool ScriptInterface::FromJSVal(JSContext* cx, jsval v, CFixedVector2D& out)
+{
+ ScriptInterface::LocalRootScope scope(cx);
+ if (!scope.OK())
+ return false;
+
+ if (!JSVAL_IS_OBJECT(v))
+ return false; // TODO: report type error
+ JSObject* obj = JSVAL_TO_OBJECT(v);
+
+ jsval p;
+
+ if (!JS_GetProperty(cx, obj, "x", &p)) return false; // TODO: report type errors
+ if (!FromJSVal(cx, p, out.X)) return false;
+
+ if (!JS_GetProperty(cx, obj, "y", &p)) return false;
+ if (!FromJSVal(cx, p, out.Y)) return false;
+
+ return true;
+}