Removed reverse dependency on SOverlayTexturedLine from CTexturedLineRData; fixes dangling pointer dereference. Fixes #1495.

This was SVN commit r11979.
This commit is contained in:
vts 2012-06-17 03:40:35 +00:00
parent 972f28c6a7
commit 821cfe8862
5 changed files with 75 additions and 50 deletions

View File

@ -17,9 +17,10 @@
#include "precompiled.h" #include "precompiled.h"
#include "ps/CStr.h"
#include "Overlay.h" #include "Overlay.h"
#include "ps/CStr.h"
SOverlayTexturedLine::LineCapType SOverlayTexturedLine::StrToLineCapType(const std::wstring& str) SOverlayTexturedLine::LineCapType SOverlayTexturedLine::StrToLineCapType(const std::wstring& str)
{ {
if (str == L"round") if (str == L"round")
@ -35,3 +36,4 @@ SOverlayTexturedLine::LineCapType SOverlayTexturedLine::StrToLineCapType(const s
return LINECAP_FLAT; return LINECAP_FLAT;
} }
} }

View File

@ -27,6 +27,7 @@
class CTerrain; class CTerrain;
class CSimContext; class CSimContext;
class CTexturedLineRData;
/** /**
* Line-based overlay, with world-space coordinates, rendered in the world * Line-based overlay, with world-space coordinates, rendered in the world
@ -50,8 +51,13 @@ struct SOverlayLine
}; };
/** /**
* Textured line overlay, with world-space coordinates, rendered in the world * Textured line overlay, with world-space coordinates, rendered in the world onto the terrain.
* onto the terrain. Designed for territory borders. * Designed for relatively static textured lines, i.e. territory borders, originally.
*
* Once submitted for rendering, instances must not be copied afterwards. The reason is that they
* are assigned rendering data that is unique to the submitted instance, and non-transferable to
* any copies that would otherwise be made. Amongst others, this restraint includes that they must
* not be submitted by their address inside a std::vector storing them by value.
*/ */
struct SOverlayTexturedLine struct SOverlayTexturedLine
{ {
@ -71,8 +77,8 @@ struct SOverlayTexturedLine
}; };
SOverlayTexturedLine() SOverlayTexturedLine()
: m_SimContext(NULL), m_Thickness(1.0f), m_Closed(false), m_AlwaysVisible(false), : m_Thickness(1.0f), m_Closed(false), m_AlwaysVisible(false),
m_StartCapType(LINECAP_FLAT), m_EndCapType(LINECAP_FLAT) m_StartCapType(LINECAP_FLAT), m_EndCapType(LINECAP_FLAT), m_SimContext(NULL)
{ } { }
CTexturePtr m_TextureBase; CTexturePtr m_TextureBase;
@ -92,11 +98,21 @@ struct SOverlayTexturedLine
LineCapType m_StartCapType; LineCapType m_StartCapType;
LineCapType m_EndCapType; LineCapType m_EndCapType;
/// Simulation context applicable for this overlay line; used to obtain terrain information /**
/// during automatic computation of Y coordinates. * Simulation context applicable for this overlay line; used to obtain terrain information
* during automatic computation of Y coordinates.
*/
const CSimContext* m_SimContext; const CSimContext* m_SimContext;
/// Cached renderer data (shared_ptr so that copies/deletes are automatic).
shared_ptr<CRenderData> m_RenderData; /**
* Cached renderer data, because expensive to compute. Allocated by the renderer when necessary
* for rendering purposes.
*
* Note: the rendering data may be shared between copies of this object to prevent having to
* recompute it, while at the same time maintaining copyability of this object (see also docs on
* CTexturedLineRData).
*/
shared_ptr<CTexturedLineRData> m_RenderData;
/** /**
* Converts a string line cap type into its corresponding LineCap enum value, and returns * Converts a string line cap type into its corresponding LineCap enum value, and returns

View File

@ -254,8 +254,8 @@ void OverlayRenderer::PrepareForRendering()
SOverlayTexturedLine* line = m->texlines[i]; SOverlayTexturedLine* line = m->texlines[i];
if (!line->m_RenderData) if (!line->m_RenderData)
{ {
line->m_RenderData = shared_ptr<CRenderData>(new CTexturedLineRData(line)); line->m_RenderData = shared_ptr<CTexturedLineRData>(new CTexturedLineRData());
static_cast<CTexturedLineRData*>(line->m_RenderData.get())->Update(); line->m_RenderData->Update(*line);
// We assume the overlay line will get replaced by the caller // We assume the overlay line will get replaced by the caller
// if terrain changes, so we don't need to detect that here and // if terrain changes, so we don't need to detect that here and
// call Update again. Also we assume the caller won't change // call Update again. Also we assume the caller won't change
@ -464,10 +464,8 @@ void OverlayRenderer::RenderTexturedOverlayLines(CShaderProgramPtr shader, bool
if (!line->m_RenderData || line->m_AlwaysVisible != alwaysVisible) if (!line->m_RenderData || line->m_AlwaysVisible != alwaysVisible)
continue; continue;
CTexturedLineRData* rdata = static_cast<CTexturedLineRData*>(line->m_RenderData.get()); ENSURE(line->m_RenderData);
ENSURE(rdata); line->m_RenderData->Render(*line, shader);
rdata->Render(shader);
} }
} }

View File

@ -32,7 +32,7 @@
* because it allows you to work with variable amounts of vertices and indices more easily. New code should prefer * because it allows you to work with variable amounts of vertices and indices more easily. New code should prefer
* to use VertexArray where possible, though. */ * to use VertexArray where possible, though. */
void CTexturedLineRData::Render(const CShaderProgramPtr& shader) void CTexturedLineRData::Render(const SOverlayTexturedLine& line, const CShaderProgramPtr& shader)
{ {
if (!m_VB || !m_VBIndices) if (!m_VB || !m_VBIndices)
return; // might have failed to allocate return; // might have failed to allocate
@ -41,9 +41,9 @@ void CTexturedLineRData::Render(const CShaderProgramPtr& shader)
const int streamFlags = shader->GetStreamFlags(); const int streamFlags = shader->GetStreamFlags();
shader->BindTexture("baseTex", m_Line->m_TextureBase->GetHandle()); shader->BindTexture("baseTex", line.m_TextureBase->GetHandle());
shader->BindTexture("maskTex", m_Line->m_TextureMask->GetHandle()); shader->BindTexture("maskTex", line.m_TextureMask->GetHandle());
shader->Uniform("objectColor", m_Line->m_Color); shader->Uniform("objectColor", line.m_Color);
GLsizei stride = sizeof(CTexturedLineRData::SVertex); GLsizei stride = sizeof(CTexturedLineRData::SVertex);
CTexturedLineRData::SVertex* vertexBase = reinterpret_cast<CTexturedLineRData::SVertex*>(m_VB->m_Owner->Bind()); CTexturedLineRData::SVertex* vertexBase = reinterpret_cast<CTexturedLineRData::SVertex*>(m_VB->m_Owner->Bind());
@ -66,7 +66,7 @@ void CTexturedLineRData::Render(const CShaderProgramPtr& shader)
g_Renderer.GetStats().m_OverlayTris += m_VBIndices->m_Count/3; g_Renderer.GetStats().m_OverlayTris += m_VBIndices->m_Count/3;
} }
void CTexturedLineRData::Update() void CTexturedLineRData::Update(const SOverlayTexturedLine& line)
{ {
if (m_VB) if (m_VB)
{ {
@ -79,21 +79,21 @@ void CTexturedLineRData::Update()
m_VBIndices = NULL; m_VBIndices = NULL;
} }
if (!m_Line->m_SimContext) if (!line.m_SimContext)
{ {
debug_warn(L"[TexturedLineRData] No SimContext set for textured overlay line, cannot render (no terrain data)"); debug_warn(L"[TexturedLineRData] No SimContext set for textured overlay line, cannot render (no terrain data)");
return; return;
} }
const CTerrain& terrain = m_Line->m_SimContext->GetTerrain(); const CTerrain& terrain = line.m_SimContext->GetTerrain();
CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Line->m_SimContext, SYSTEM_ENTITY); CmpPtr<ICmpWaterManager> cmpWaterManager(*line.m_SimContext, SYSTEM_ENTITY);
float v = 0.f; float v = 0.f;
std::vector<SVertex> vertices; std::vector<SVertex> vertices;
std::vector<u16> indices; std::vector<u16> indices;
size_t n = m_Line->m_Coords.size() / 2; // number of line points size_t n = line.m_Coords.size() / 2; // number of line points
bool closed = m_Line->m_Closed; bool closed = line.m_Closed;
ENSURE(n >= 2); // minimum needed to avoid errors (also minimum value to make sense, can't draw a line between 1 point) ENSURE(n >= 2); // minimum needed to avoid errors (also minimum value to make sense, can't draw a line between 1 point)
@ -102,12 +102,12 @@ void CTexturedLineRData::Update()
// recompute p2 at the end of each iteration. // recompute p2 at the end of each iteration.
CVector3D p0; CVector3D p0;
CVector3D p1(m_Line->m_Coords[0], 0, m_Line->m_Coords[1]); CVector3D p1(line.m_Coords[0], 0, line.m_Coords[1]);
CVector3D p2(m_Line->m_Coords[(1 % n)*2], 0, m_Line->m_Coords[(1 % n)*2+1]); CVector3D p2(line.m_Coords[(1 % n)*2], 0, line.m_Coords[(1 % n)*2+1]);
if (closed) if (closed)
// grab the ending point so as to close the loop // grab the ending point so as to close the loop
p0 = CVector3D(m_Line->m_Coords[(n-1)*2], 0, m_Line->m_Coords[(n-1)*2+1]); p0 = CVector3D(line.m_Coords[(n-1)*2], 0, line.m_Coords[(n-1)*2+1]);
else else
// we don't want to loop around and use the direction towards the other end of the line, so create an artificial p0 that // we don't want to loop around and use the direction towards the other end of the line, so create an artificial p0 that
// extends the p2 -> p1 direction, and use that point instead // extends the p2 -> p1 direction, and use that point instead
@ -157,7 +157,7 @@ void CTexturedLineRData::Update()
// Adjust bisector length to match the line thickness, along the line's width // Adjust bisector length to match the line thickness, along the line's width
float l = b.Dot((p2 - p1).Normalized().Cross(norm)); float l = b.Dot((p2 - p1).Normalized().Cross(norm));
if (fabs(l) > 0.000001f) // avoid unlikely divide-by-zero if (fabs(l) > 0.000001f) // avoid unlikely divide-by-zero
b *= m_Line->m_Thickness / l; b *= line.m_Thickness / l;
// Push vertices and indices for each quad in GL_TRIANGLES order. The two triangles of each quad are indexed using // Push vertices and indices for each quad in GL_TRIANGLES order. The two triangles of each quad are indexed using
// the winding orders (BR, BL, TR) and (TR, BL, TR) (where BR is bottom-right of this iteration's quad, TR top-right etc). // the winding orders (BR, BL, TR) and (TR, BL, TR) (where BR is bottom-right of this iteration's quad, TR top-right etc).
@ -209,7 +209,7 @@ void CTexturedLineRData::Update()
// next iteration is the last point of the line, so create an artificial p2 that extends the p0 -> p1 direction // next iteration is the last point of the line, so create an artificial p2 that extends the p0 -> p1 direction
p2 = p1 + (p1 - p0); p2 = p1 + (p1 - p0);
else else
p2 = CVector3D(m_Line->m_Coords[((i+2) % n)*2], 0, m_Line->m_Coords[((i+2) % n)*2+1]); p2 = CVector3D(line.m_Coords[((i+2) % n)*2], 0, line.m_Coords[((i+2) % n)*2+1]);
p2.Y = terrain.GetExactGroundLevel(p2.X, p2.Z); p2.Y = terrain.GetExactGroundLevel(p2.X, p2.Z);
if (p2.Y < w) if (p2.Y < w)
@ -242,15 +242,16 @@ void CTexturedLineRData::Update()
// create end cap // create end cap
CreateLineCap( CreateLineCap(
line,
// the order of these vertices is important here, swapping them produces caps at the wrong side // the order of these vertices is important here, swapping them produces caps at the wrong side
vertices[vertices.size()-2].m_Position, // top-right vertex of last quad vertices[vertices.size()-2].m_Position, // top-right vertex of last quad
vertices[vertices.size()-1].m_Position, // top-left vertex of last quad vertices[vertices.size()-1].m_Position, // top-left vertex of last quad
// directional vector between centroids of last vertex pair and second-to-last vertex pair // directional vector between centroids of last vertex pair and second-to-last vertex pair
(Centroid(vertices[vertices.size()-2], vertices[vertices.size()-1]) - Centroid(vertices[vertices.size()-4], vertices[vertices.size()-3])).Normalized(), (Centroid(vertices[vertices.size()-2], vertices[vertices.size()-1]) - Centroid(vertices[vertices.size()-4], vertices[vertices.size()-3])).Normalized(),
m_Line->m_EndCapType, line.m_EndCapType,
capVertices, capVertices,
capIndices capIndices
); );
for (unsigned i = 0; i < capIndices.size(); i++) for (unsigned i = 0; i < capIndices.size(); i++)
capIndices[i] += vertices.size(); capIndices[i] += vertices.size();
@ -263,15 +264,16 @@ void CTexturedLineRData::Update()
// create start cap // create start cap
CreateLineCap( CreateLineCap(
line,
// the order of these vertices is important here, swapping them produces caps at the wrong side // the order of these vertices is important here, swapping them produces caps at the wrong side
vertices[1].m_Position, vertices[1].m_Position,
vertices[0].m_Position, vertices[0].m_Position,
// directional vector between centroids of first vertex pair and second vertex pair // directional vector between centroids of first vertex pair and second vertex pair
(Centroid(vertices[1], vertices[0]) - Centroid(vertices[3], vertices[2])).Normalized(), (Centroid(vertices[1], vertices[0]) - Centroid(vertices[3], vertices[2])).Normalized(),
m_Line->m_StartCapType, line.m_StartCapType,
capVertices, capVertices,
capIndices capIndices
); );
for (unsigned i = 0; i < capIndices.size(); i++) for (unsigned i = 0; i < capIndices.size(); i++)
capIndices[i] += vertices.size(); capIndices[i] += vertices.size();
@ -297,8 +299,8 @@ void CTexturedLineRData::Update()
} }
void CTexturedLineRData::CreateLineCap(const CVector3D& corner1, const CVector3D& corner2, const CVector3D& lineDirectionNormal, void CTexturedLineRData::CreateLineCap(const SOverlayTexturedLine& line, const CVector3D& corner1, const CVector3D& corner2,
SOverlayTexturedLine::LineCapType endCapType, std::vector<SVertex>& verticesOut, const CVector3D& lineDirectionNormal, SOverlayTexturedLine::LineCapType endCapType, std::vector<SVertex>& verticesOut,
std::vector<u16>& indicesOut) std::vector<u16>& indicesOut)
{ {
if (endCapType == SOverlayTexturedLine::LINECAP_FLAT) if (endCapType == SOverlayTexturedLine::LINECAP_FLAT)
@ -317,7 +319,7 @@ void CTexturedLineRData::CreateLineCap(const CVector3D& corner1, const CVector3D
// //
int roundCapPoints = 8; // amount of points to sample along the semicircle for rounded caps (including corner points) int roundCapPoints = 8; // amount of points to sample along the semicircle for rounded caps (including corner points)
float radius = m_Line->m_Thickness; float radius = line.m_Thickness;
CVector3D centerPoint = (corner1 + corner2) * 0.5f; CVector3D centerPoint = (corner1 + corner2) * 0.5f;
SVertex centerVertex(centerPoint, 0.5f, 0.5f); SVertex centerVertex(centerPoint, 0.5f, 0.5f);
@ -397,8 +399,8 @@ void CTexturedLineRData::CreateLineCap(const CVector3D& corner1, const CVector3D
// are rendered only one-sided; the wrong order of vertices will make the cap visible only from the bottom. // are rendered only one-sided; the wrong order of vertices will make the cap visible only from the bottom.
verticesOut.push_back(centerVertex); verticesOut.push_back(centerVertex);
verticesOut.push_back(SVertex(corner2, 0.f, 0.f)); verticesOut.push_back(SVertex(corner2, 0.f, 0.f));
verticesOut.push_back(SVertex(corner2 + (lineDirectionNormal * (m_Line->m_Thickness)), 0.f, 0.33333f)); // extend butt corner point 2 along the normal vector verticesOut.push_back(SVertex(corner2 + (lineDirectionNormal * (line.m_Thickness)), 0.f, 0.33333f)); // extend butt corner point 2 along the normal vector
verticesOut.push_back(SVertex(corner1 + (lineDirectionNormal * (m_Line->m_Thickness)), 0.f, 0.66666f)); // extend butt corner point 1 along the normal vector verticesOut.push_back(SVertex(corner1 + (lineDirectionNormal * (line.m_Thickness)), 0.f, 0.66666f)); // extend butt corner point 1 along the normal vector
verticesOut.push_back(SVertex(corner1, 0.f, 1.0f)); // push butt corner point 1 verticesOut.push_back(SVertex(corner1, 0.f, 1.0f)); // push butt corner point 1
for (int i=1; i < 4; ++i) for (int i=1; i < 4; ++i)

View File

@ -25,17 +25,26 @@
#include "renderer/VertexBufferManager.h" #include "renderer/VertexBufferManager.h"
/** /**
* Rendering data for a single textured overlay line. Used by the OverlayRenderer. * Rendering data for an STexturedOverlayLine.
*
* Note that instances may be shared amongst multiple copies of the same STexturedOverlayLine instance.
* The reason is that this rendering data is non-copyable, but we do wish to maintain copyability of
* SOverlayTexturedLineData to not limit its usage patterns too much (particularly the practice of storing
* them into containers).
*
* For this reason, instead of storing a reverse pointer back to any single SOverlayTexturedLine, the methods
* in this class accept references to STexturedOverlayLines to work with. It is up to client code to pass in
* SOverlayTexturedLines to all methods that are consistently the same instance or non-modified copies of it.
*/ */
class CTexturedLineRData : public CRenderData class CTexturedLineRData : public CRenderData
{ {
// we hold raw pointers to vertex buffer chunks that are handed out by the vertex buffer manager
// and can not be safely duplicated by us.
NONCOPYABLE(CTexturedLineRData);
public: public:
/** CTexturedLineRData() : m_VB(NULL), m_VBIndices(NULL) { }
* @param line Overlay line to associate this render data with. Must not be null.
*/
CTexturedLineRData(SOverlayTexturedLine* line) : m_Line(line), m_VB(NULL), m_VBIndices(NULL)
{ ENSURE(m_Line && "Cannot create textured line render data for null overlay line"); }
~CTexturedLineRData() ~CTexturedLineRData()
{ {
@ -45,9 +54,8 @@ public:
g_VBMan.Release(m_VBIndices); g_VBMan.Release(m_VBIndices);
} }
void Update(); void Update(const SOverlayTexturedLine& line);
void Render(const SOverlayTexturedLine& line, const CShaderProgramPtr& shader);
void Render(const CShaderProgramPtr& shader);
protected: protected:
@ -71,7 +79,7 @@ protected:
* @param verticesOut Output vector of vertices for passing to the renderer. * @param verticesOut Output vector of vertices for passing to the renderer.
* @param indicesOut Output vector of vertex indices for passing to the renderer. * @param indicesOut Output vector of vertex indices for passing to the renderer.
*/ */
void CreateLineCap(const CVector3D& corner1, const CVector3D& corner2, const CVector3D& normal, void CreateLineCap(const SOverlayTexturedLine& line, const CVector3D& corner1, const CVector3D& corner2, const CVector3D& normal,
SOverlayTexturedLine::LineCapType endCapType, std::vector<SVertex>& verticesOut, std::vector<u16>& indicesOut); SOverlayTexturedLine::LineCapType endCapType, std::vector<SVertex>& verticesOut, std::vector<u16>& indicesOut);
/// Small utility function; grabs the centroid of the positions of two vertices /// Small utility function; grabs the centroid of the positions of two vertices
@ -80,7 +88,6 @@ protected:
return (v1.m_Position + v2.m_Position) * 0.5; return (v1.m_Position + v2.m_Position) * 0.5;
} }
SOverlayTexturedLine* m_Line;
CVertexBuffer::VBChunk* m_VB; CVertexBuffer::VBChunk* m_VB;
CVertexBuffer::VBChunk* m_VBIndices; CVertexBuffer::VBChunk* m_VBIndices;
}; };