You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/RenderDll/XRenderD3D9/D3DRenderAuxGeom.cpp

1911 lines
63 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "RenderDll_precompiled.h"
#include "DriverD3D.h"
#include "D3DRenderAuxGeom.h"
#include <Pak/CryPakUtils.h>
#if defined(ENABLE_RENDER_AUX_GEOM)
const float c_clipThres(0.1f);
enum EAuxGeomBufferSizes
{
e_auxGeomVBSize = 0xffff,
e_auxGeomIBSize = e_auxGeomVBSize * 2 * 3
};
struct SAuxObjVertex
{
SAuxObjVertex()
{
}
SAuxObjVertex(const Vec3& pos, const Vec3& normal)
: m_pos(pos)
, m_normal(normal)
{
}
Vec3 m_pos;
Vec3 m_normal;
};
typedef std::vector< SAuxObjVertex > AuxObjVertexBuffer;
typedef std::vector< uint16 > AuxObjIndexBuffer;
CRenderAuxGeomD3D::CRenderAuxGeomD3D(CD3D9Renderer& renderer)
: m_renderer(renderer)
, m_pAuxGeomVB(0)
, m_pAuxGeomIB(0)
, m_pCurVB(0)
, m_pCurIB(0)
, m_auxGeomSBM()
, m_wndXRes(0)
, m_wndYRes(0)
, m_aspect(1.0f)
, m_aspectInv(1.0f)
, m_matrices()
, m_curPrimType(CAuxGeomCB::e_PrimTypeInvalid)
, m_curPointSize(1)
, m_curTransMatrixIdx(-1)
, m_pAuxGeomShader(0)
, m_curDrawInFrontMode(e_DrawInFrontOff)
, m_auxSortedPushBuffer()
, m_pCurCBRawData(0)
, m_auxGeomCBCol()
, CV_r_auxGeom(1)
{
REGISTER_CVAR2("r_auxGeom", &CV_r_auxGeom, 1, VF_NULL, "");
}
CRenderAuxGeomD3D::~CRenderAuxGeomD3D()
{
//SAFE_RELEASE(m_pAuxGeomShader);
// m_pAuxGeomShader released by CShaderMan ???
//delete m_pAuxGeomShader;
//m_pAuxGeomShader = 0;
}
void CRenderAuxGeomD3D::GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject((char*)this - 16, sizeof(*this) + 16); // adjust for new operator
pSizer->AddObject(m_auxSortedPushBuffer);
pSizer->AddObject(m_auxGeomCBCol);
}
void CRenderAuxGeomD3D::ReleaseDeviceObjects()
{
gcpRendD3D->m_DevMan.ReleaseD3D11Buffer(m_pAuxGeomVB);
m_pAuxGeomVB = nullptr;
gcpRendD3D->m_DevMan.ReleaseD3D11Buffer(m_pAuxGeomIB);
m_pAuxGeomIB = nullptr;
for (uint32 i(0); i < e_auxObjNumLOD; ++i)
{
m_sphereObj[ i ].Release();
m_diskObj[ i ].Release();
m_quadObj[ i ].Release();
m_coneObj[ i ].Release();
m_cylinderObj[ i ].Release();
}
}
int CRenderAuxGeomD3D::GetDeviceDataSize()
{
int nSize = 0;
nSize += _VertBufferSize(m_pAuxGeomVB);
nSize += _IndexBufferSize(m_pAuxGeomIB);
for (uint32 i = 0; i < e_auxObjNumLOD; ++i)
{
nSize += m_sphereObj[i].GetDeviceDataSize();
nSize += m_diskObj[i].GetDeviceDataSize();
nSize += m_quadObj[i].GetDeviceDataSize();
nSize += m_coneObj[i].GetDeviceDataSize();
nSize += m_cylinderObj[i].GetDeviceDataSize();
}
return nSize;
}
// function to generate a sphere mesh
static void CreateSphere(AuxObjVertexBuffer& vb, AuxObjIndexBuffer& ib, float radius, unsigned int rings, unsigned int sections)
{
// calc required number of vertices/indices/triangles to build a sphere for the given parameters
uint32 numVertices((rings - 1) * (sections + 1) + 2);
uint32 numTriangles((rings - 2) * sections * 2 + 2 * sections);
uint32 numIndices(numTriangles * 3);
// setup buffers
vb.clear();
vb.reserve(numVertices);
ib.clear();
ib.reserve(numIndices);
// 1st pole vertex
vb.push_back(SAuxObjVertex(Vec3(0.0f, 0.0f, radius), Vec3(0.0f, 0.0f, 1.0f)));
// calculate "inner" vertices
float sectionSlice(DEG2RAD(360.0f / (float) sections));
float ringSlice(DEG2RAD(180.0f / (float) rings));
for (uint32 a(1); a < rings; ++a)
{
float w(sinf(a * ringSlice));
for (uint32 i(0); i <= sections; ++i)
{
Vec3 v;
v.x = radius * cosf(i * sectionSlice) * w;
v.y = radius * sinf(i * sectionSlice) * w;
v.z = radius * cosf(a * ringSlice);
vb.push_back(SAuxObjVertex(v, v.GetNormalized()));
}
}
// 2nd vertex of pole (for end cap)
vb.push_back(SAuxObjVertex(Vec3(0.0f, 0.0f, -radius), Vec3(0.0f, 0.0f, 1.0f)));
// build "inner" faces
for (uint32 a(0); a < rings - 2; ++a)
{
for (uint32 i(0); i < sections; ++i)
{
ib.push_back((uint16) (1 + a * (sections + 1) + i + 1));
ib.push_back((uint16) (1 + a * (sections + 1) + i));
ib.push_back((uint16) (1 + (a + 1) * (sections + 1) + i + 1));
ib.push_back((uint16) (1 + (a + 1) * (sections + 1) + i));
ib.push_back((uint16) (1 + (a + 1) * (sections + 1) + i + 1));
ib.push_back((uint16) (1 + a * (sections + 1) + i));
}
}
// build faces for end caps (to connect "inner" vertices with poles)
for (uint32 i(0); i < sections; ++i)
{
ib.push_back((uint16) (1 + (0) * (sections + 1) + i));
ib.push_back((uint16) (1 + (0) * (sections + 1) + i + 1));
ib.push_back((uint16) 0);
}
for (uint32 i(0); i < sections; ++i)
{
ib.push_back((uint16) (1 + (rings - 2) * (sections + 1) + i + 1));
ib.push_back((uint16) (1 + (rings - 2) * (sections + 1) + i));
ib.push_back((uint16) ((rings - 1) * (sections + 1) + 1));
}
}
// function to generate a disk mesh
static void CreateDisk(AuxObjVertexBuffer& vb, AuxObjIndexBuffer& ib, float radius, unsigned int sections)
{
// calc required number of vertices/indices/triangles to build a disk for the given parameters
uint32 numVertices = (sections + 1) * 2;
uint32 numTriangles = sections * 2;
uint32 numIndices = numTriangles * 3;
// setup buffers
vb.clear();
vb.reserve(numVertices);
ib.clear();
ib.reserve(numIndices);
Vec3 yUp = Vec3(0.0f, 1.0f, 0.0f);
Vec3 yDown = Vec3(0.0f, -1.0f, 0.0f);
// center vertex
vb.push_back(SAuxObjVertex(Vec3(0.0f, 0.0f, 0.0f), yUp));
vb.push_back(SAuxObjVertex(Vec3(0.0f, 0.0f, 0.0f), yDown));
// create circle around it
float sectionSlice(DEG2RAD(360.0f / (float)sections));
for (uint32 i(0); i <= sections; ++i)
{
Vec3 v;
v.x = radius * cosf(i * sectionSlice);
v.y = 0.0f;
v.z = radius * sinf(i * sectionSlice);
vb.push_back(SAuxObjVertex(v, yUp));
vb.push_back(SAuxObjVertex(v, yDown));
}
// build faces
for (uint16 i = 0; i < numTriangles; i += 2)
{
// top face
ib.push_back(0);
ib.push_back(2 + i + 2);
ib.push_back(2 + i);
// bottom face
ib.push_back(1);
ib.push_back(3 + i);
ib.push_back(3 + i + 2);
}
}
// function to generate a quad mesh on the x z plane
static void CreateQuad(AuxObjVertexBuffer& vb, AuxObjIndexBuffer& ib, float width, float height)
{
// Calc required number of vertices/indices/triangles to build a quad for the given parameters
uint32 numVertices = 4 * 2; // 4 corners * 2 sides
uint32 numTriangles = 2 * 2; // 2 triangles * 2 sides
uint32 numIndices = numTriangles * 3;
float halfWidth = width * 0.5f;
float halfHeight = height * 0.5f;
// setup buffers
vb.clear();
vb.reserve(numVertices);
ib.clear();
ib.reserve(numIndices);
Vec3 yUp = Vec3(0.0f, 1.0f, 0.0f);
Vec3 yDown = Vec3(0.0f, -1.0f, 0.0f);
// top faces
vb.push_back(SAuxObjVertex(Vec3(-halfWidth, 0.0f, halfHeight), yUp));
vb.push_back(SAuxObjVertex(Vec3( halfWidth, 0.0f, halfHeight), yUp));
vb.push_back(SAuxObjVertex(Vec3(-halfWidth, 0.0f, -halfHeight), yUp));
vb.push_back(SAuxObjVertex(Vec3( halfWidth, 0.0f, -halfHeight), yUp));
ib.insert(ib.end(), {1, 2, 0, 3, 2, 1});
// bottom faces
vb.push_back(SAuxObjVertex(Vec3(-halfWidth, 0.0f, halfHeight), yDown));
vb.push_back(SAuxObjVertex(Vec3( halfWidth, 0.0f, halfHeight), yDown));
vb.push_back(SAuxObjVertex(Vec3(-halfWidth, 0.0f, -halfHeight), yDown));
vb.push_back(SAuxObjVertex(Vec3( halfWidth, 0.0f, -halfHeight), yDown));
ib.insert(ib.end(), { 4, 6, 5, 5, 6, 7 });
}
// function to generate a cone mesh
static void CreateCone(AuxObjVertexBuffer& vb, AuxObjIndexBuffer& ib, float radius, float height, unsigned int sections)
{
// calc required number of vertices/indices/triangles to build a cone for the given parameters
uint32 numVertices(2 * (sections + 1) + 2);
uint32 numTriangles(2 * sections);
uint32 numIndices(numTriangles * 3);
// setup buffers
vb.clear();
vb.reserve(numVertices);
ib.clear();
ib.reserve(numIndices);
// center vertex
vb.push_back(SAuxObjVertex(Vec3(0.0f, 0.0f, 0.0f), Vec3(0.0f, -1.0f, 0.0f)));
// create circle around it
float sectionSlice(DEG2RAD(360.0f / (float) sections));
for (uint32 i(0); i <= sections; ++i)
{
Vec3 v;
v.x = radius * cosf(i * sectionSlice);
v.y = 0.0f;
v.z = radius * sinf(i * sectionSlice);
vb.push_back(SAuxObjVertex(v, Vec3(0.0f, -1.0f, 0.0f)));
}
// build faces for end cap
for (uint16 i(0); i < sections; ++i)
{
ib.push_back(0);
ib.push_back(1 + i);
ib.push_back(1 + i + 1);
}
// top
vb.push_back(SAuxObjVertex(Vec3(0.0f, height, 0.0f), Vec3(0.0f, 1.0f, 0.0f)));
for (uint32 i(0); i <= sections; ++i)
{
Vec3 v;
v.x = radius * cosf(i * sectionSlice);
v.y = 0.0f;
v.z = radius * sinf(i * sectionSlice);
Vec3 v1;
v1.x = radius * cosf(i * sectionSlice + 0.01f);
v1.y = 0.0f;
v1.z = radius * sinf(i * sectionSlice + 0.01f);
Vec3 d(v1 - v);
Vec3 d1(Vec3(0.0, height, 0.0f) - v);
Vec3 n((d1.Cross(d)).normalized());
vb.push_back(SAuxObjVertex(v, n));
}
// build faces
for (uint16 i(0); i < sections; ++i)
{
ib.push_back(sections + 2);
ib.push_back(sections + 3 + i + 1);
ib.push_back(sections + 3 + i);
}
}
// function to generate a cylinder mesh
static void CreateCylinder(AuxObjVertexBuffer& vb, AuxObjIndexBuffer& ib, float radius, float height, unsigned int sections)
{
// calc required number of vertices/indices/triangles to build a cylinder for the given parameters
uint32 numVertices(4 * (sections + 1) + 2);
uint32 numTriangles(4 * sections);
uint32 numIndices(numTriangles * 3);
// setup buffers
vb.clear();
vb.reserve(numVertices);
ib.clear();
ib.reserve(numIndices);
float sectionSlice(DEG2RAD(360.0f / (float) sections));
// bottom cap
{
// center bottom vertex
vb.push_back(SAuxObjVertex(Vec3(0.0f, -0.5f * height, 0.0f), Vec3(0.0f, -1.0f, 0.0f)));
// create circle around it
for (uint32 i(0); i <= sections; ++i)
{
Vec3 v;
v.x = radius * cosf(i * sectionSlice);
v.y = -0.5f * height;
v.z = radius * sinf(i * sectionSlice);
vb.push_back(SAuxObjVertex(v, Vec3(0.0f, -1.0f, 0.0f)));
}
// build faces
for (uint16 i(0); i < sections; ++i)
{
ib.push_back(0);
ib.push_back(1 + i);
ib.push_back(1 + i + 1);
}
}
// side
{
int vIdx(vb.size());
for (uint32 i(0); i <= sections; ++i)
{
Vec3 v;
v.x = radius * cosf(i * sectionSlice);
v.y = -0.5f * height;
v.z = radius * sinf(i * sectionSlice);
Vec3 n(v.normalized());
vb.push_back(SAuxObjVertex(v, n));
vb.push_back(SAuxObjVertex(Vec3(v.x, -v.y, v.z), n));
}
// build faces
for (uint16 i(0); i < sections; ++i, vIdx += 2)
{
ib.push_back(vIdx);
ib.push_back(vIdx + 1);
ib.push_back(vIdx + 2);
ib.push_back(vIdx + 1);
ib.push_back(vIdx + 3);
ib.push_back(vIdx + 2);
}
}
// top cap
{
int vIdx(vb.size());
// center top vertex
vb.push_back(SAuxObjVertex(Vec3(0.0f, 0.5f * height, 0.0f), Vec3(0.0f, 1.0f, 0.0f)));
// create circle around it
for (uint32 i(0); i <= sections; ++i)
{
Vec3 v;
v.x = radius * cosf(i * sectionSlice);
v.y = 0.5f * height;
v.z = radius * sinf(i * sectionSlice);
vb.push_back(SAuxObjVertex(v, Vec3(0.0f, 1.0f, 0.0f)));
}
// build faces
for (uint16 i(0); i < sections; ++i)
{
ib.push_back(vIdx);
ib.push_back(vIdx + 1 + i + 1);
ib.push_back(vIdx + 1 + i);
}
}
}
// Functor to generate a sphere mesh. To be used with generalized CreateMesh function.
struct SSphereMeshCreateFunc
{
SSphereMeshCreateFunc(float radius, unsigned int rings, unsigned int sections)
: m_radius(radius)
, m_rings(rings)
, m_sections(sections)
{
}
void CreateMesh(AuxObjVertexBuffer& vb, AuxObjIndexBuffer& ib)
{
CreateSphere(vb, ib, m_radius, m_rings, m_sections);
}
float m_radius;
unsigned int m_rings;
unsigned int m_sections;
};
// Functor to generate a disk mesh. To be used with generalized CreateMesh function.
struct SDiskMeshCreateFunc
{
SDiskMeshCreateFunc(float radius, unsigned int rings)
: m_radius(radius)
, m_rings(rings)
{
}
void CreateMesh(AuxObjVertexBuffer& vb, AuxObjIndexBuffer& ib)
{
CreateDisk(vb, ib, m_radius, m_rings);
}
float m_radius;
unsigned int m_rings;
};
// Functor to generate a quad mesh. To be used with generalized CreateMesh function.
struct SQuadMeshCreateFunc
{
SQuadMeshCreateFunc(float width, float height)
: m_width(width)
, m_height(height)
{
}
void CreateMesh(AuxObjVertexBuffer& vb, AuxObjIndexBuffer& ib)
{
CreateQuad(vb, ib, m_width, m_height);
}
float m_width;
float m_height;
};
// Functor to generate a cone mesh. To be used with generalized CreateMesh function.
struct SConeMeshCreateFunc
{
SConeMeshCreateFunc(float radius, float height, unsigned int sections)
: m_radius(radius)
, m_height(height)
, m_sections(sections)
{
}
void CreateMesh(AuxObjVertexBuffer& vb, AuxObjIndexBuffer& ib)
{
CreateCone(vb, ib, m_radius, m_height, m_sections);
}
float m_radius;
float m_height;
unsigned int m_sections;
};
// Functor to generate a cylinder mesh. To be used with generalized CreateMesh function.
struct SCylinderMeshCreateFunc
{
SCylinderMeshCreateFunc(float radius, float height, unsigned int sections)
: m_radius(radius)
, m_height(height)
, m_sections(sections)
{
}
void CreateMesh(AuxObjVertexBuffer& vb, AuxObjIndexBuffer& ib)
{
CreateCylinder(vb, ib, m_radius, m_height, m_sections);
}
float m_radius;
float m_height;
unsigned int m_sections;
};
// Generalized CreateMesh function to create any mesh via passed-in functor.
// The functor needs to provide a CreateMesh function which accepts an
// AuxObjVertexBuffer and AuxObjIndexBuffer to stored the resulting mesh.
template< typename TMeshFunc >
HRESULT CRenderAuxGeomD3D::CreateMesh(SDrawObjMesh& mesh, TMeshFunc meshFunc)
{
// create mesh
std::vector< SAuxObjVertex > vb;
std::vector< uint16 > ib;
meshFunc.CreateMesh(vb, ib);
// create vertex buffer and copy data
HRESULT hr(S_OK);
D3D11_BUFFER_DESC BufDescV;
ZeroStruct(BufDescV);
BufDescV.ByteWidth = vb.size() * sizeof(SAuxObjVertex);
BufDescV.Usage = D3D11_USAGE_DEFAULT;
BufDescV.BindFlags = D3D11_BIND_VERTEX_BUFFER;
BufDescV.CPUAccessFlags = 0;
BufDescV.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
InitData.pSysMem = &vb[0];
InitData.SysMemPitch = 0;
InitData.SysMemSlicePitch = 0;
if (FAILED(hr = m_renderer.m_DevMan.CreateD3D11Buffer(&BufDescV, &InitData, &mesh.m_pVB, "AuxGeometryMesh")))
{
assert(SUCCEEDED(hr));
return(hr);
}
D3D11_BUFFER_DESC BufDescI;
ZeroStruct(BufDescI);
BufDescI.ByteWidth = ib.size() * sizeof(uint16);
BufDescI.Usage = D3D11_USAGE_DEFAULT;
BufDescI.BindFlags = D3D11_BIND_INDEX_BUFFER;
BufDescI.CPUAccessFlags = 0;
BufDescI.MiscFlags = 0;
InitData.pSysMem = &ib[0];
InitData.SysMemPitch = 0;
InitData.SysMemSlicePitch = 0;
if (FAILED(hr = m_renderer.m_DevMan.CreateD3D11Buffer(&BufDescI, &InitData, &mesh.m_pIB, "AuxGeometryMesh")))
{
assert(SUCCEEDED(hr));
return(hr);
}
// write mesh info
mesh.m_numVertices = vb.size();
mesh.m_numFaces = ib.size() / 3;
return(hr);
}
HRESULT CRenderAuxGeomD3D::RestoreDeviceObjects()
{
HRESULT hr(S_OK);
// recreate vertex buffer
gcpRendD3D->m_DevMan.ReleaseD3D11Buffer(m_pAuxGeomVB);
m_pAuxGeomVB = nullptr;
D3D11_BUFFER_DESC BufDescV;
ZeroStruct(BufDescV);
BufDescV.ByteWidth = e_auxGeomVBSize * sizeof(SAuxVertex);
#if defined(CRY_USE_METAL)
//Direct access memory is faster on metal as it only needs one CPU->GPU copy whereas the
//dynamic memory (for vertex buffers) will do a CPU->GPU and then a GPU->GPU copy.
BufDescV.Usage = D3D11_USAGE_DIRECT_ACCESS;
#else
BufDescV.Usage = D3D11_USAGE_DYNAMIC;
#endif
BufDescV.BindFlags = D3D11_BIND_VERTEX_BUFFER;
BufDescV.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
BufDescV.MiscFlags = 0;
if (FAILED(hr = m_renderer.m_DevMan.CreateD3D11Buffer(&BufDescV, 0, &m_pAuxGeomVB, "AuxGeometry")))
{
assert(0);
return(hr);
}
// recreate index buffer
gcpRendD3D->m_DevMan.ReleaseD3D11Buffer(m_pAuxGeomIB);
m_pAuxGeomIB = nullptr;
D3D11_BUFFER_DESC BufDescI;
ZeroStruct(BufDescI);
BufDescI.ByteWidth = e_auxGeomIBSize * sizeof(uint16);
#if defined(CRY_USE_METAL)
//Direct access memory is faster on metal as it only needs one CPU->GPU copy whereas the
//dynamic memory (for index buffers) will do a CPU->GPU and then a GPU->GPU copy.
BufDescI.Usage = D3D11_USAGE_DIRECT_ACCESS;
#else
BufDescI.Usage = D3D11_USAGE_DYNAMIC;
#endif
BufDescI.BindFlags = D3D11_BIND_INDEX_BUFFER;
BufDescI.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
BufDescI.MiscFlags = 0;
if (FAILED(hr = m_renderer.m_DevMan.CreateD3D11Buffer(&BufDescI, 0, &m_pAuxGeomIB, "AuxGeometry")))
{
assert(0);
return(hr);
}
// recreate aux objects
for (uint32 i(0); i < e_auxObjNumLOD; ++i)
{
m_sphereObj[ i ].Release();
if (FAILED(hr = CreateMesh(m_sphereObj[ i ], SSphereMeshCreateFunc(1.0f, 9 + 4 * i, 9 + 4 * i))))
{
return(hr);
}
m_diskObj[i].Release();
if (FAILED(hr = CreateMesh(m_diskObj[ i ], SDiskMeshCreateFunc(1.0f, 9 + 4 * i))))
{
return(hr);
}
m_quadObj[i].Release();
if (FAILED(hr = CreateMesh(m_quadObj[i], SQuadMeshCreateFunc(1.0f, 1.0f))))
{
return(hr);
}
m_coneObj[ i ].Release();
if (FAILED(hr = CreateMesh(m_coneObj[ i ], SConeMeshCreateFunc(1.0f, 1.0f, 10 + i * 6))))
{
return(hr);
}
m_cylinderObj[ i ].Release();
if (FAILED(hr = CreateMesh(m_cylinderObj[ i ], SCylinderMeshCreateFunc(1.0f, 1.0f, 10 + i * 6))))
{
return(hr);
}
}
return(hr);
}
void CRenderAuxGeomD3D::DrawAuxPrimitives(CAuxGeomCB::AuxSortedPushBuffer::const_iterator itBegin, CAuxGeomCB::AuxSortedPushBuffer::const_iterator itEnd, const CAuxGeomCB::EPrimType& primType)
{
assert(CAuxGeomCB::e_PtList == primType || CAuxGeomCB::e_LineList == primType || CAuxGeomCB::e_TriList == primType);
HRESULT hr(S_OK);
// bind vertex and index streams and set vertex declaration
bool streamsBound = BindStreams(m_auxGeomPrimitiveVertexFormat, m_pAuxGeomVB, m_pAuxGeomIB);
// get aux vertex buffer
const CAuxGeomCB::AuxVertexBuffer& auxVertexBuffer(GetAuxVertexBuffer());
// determine flags for prim type
uint32 d3dNumPrimDivider;
eRenderPrimitiveType ePrimType;
DetermineAuxPrimitveFlags(d3dNumPrimDivider, ePrimType, primType);
// helpers for DP call
uint32 initialVBLockOffset(m_auxGeomSBM.m_curVBIndex);
uint32 numVerticesWrittenToVB(0);
m_renderer.FX_Commit();
// process each entry
for (CAuxGeomCB::AuxSortedPushBuffer::const_iterator it(itBegin); it != itEnd; ++it)
{
// get current push buffer entry
const CAuxGeomCB::SAuxPushBufferEntry* curPBEntry(*it);
// number of vertices to copy
uint32 verticesToCopy(curPBEntry->m_numVertices);
uint32 verticesCopied(0);
// stream vertex data
while (verticesToCopy > 0)
{
// number of vertices which fit into current vb
uint32 maxVerticesInThisBatch(e_auxGeomVBSize - m_auxGeomSBM.m_curVBIndex);
// round down to previous multiple of "d3dNumPrimDivider"
maxVerticesInThisBatch -= maxVerticesInThisBatch % d3dNumPrimDivider;
// still enough space to feed data in the current vb
if (maxVerticesInThisBatch > 0)
{
// compute amount of vertices to move in this batch
uint32 toCopy(verticesToCopy > maxVerticesInThisBatch ? maxVerticesInThisBatch : verticesToCopy);
// get pointer to vertex buffer
SAuxVertex* pVertices(0);
// determine lock flags
D3D11_MAP mapFlags(D3D11_MAP_WRITE_NO_OVERWRITE);
if (false != m_auxGeomSBM.m_discardVB)
{
m_auxGeomSBM.m_discardVB = false;
mapFlags = D3D11_MAP_WRITE_DISCARD;
}
D3D11_MAPPED_SUBRESOURCE mappedResource;
if (FAILED(hr = m_renderer.GetDeviceContext().Map(m_pAuxGeomVB, 0, mapFlags, 0, &mappedResource)))
{
assert(0);
iLog->Log("ERROR: CD3DRenderAuxGeom::DrawAuxPrimitives() - Vertex buffer could not be locked!");
return;
}
pVertices = (SAuxVertex*)mappedResource.pData;
pVertices += m_auxGeomSBM.m_curVBIndex;
// move vertex data
// cppcheck-suppress nullPointer
memcpy(pVertices, &auxVertexBuffer[ curPBEntry->m_vertexOffs + verticesCopied ], toCopy * sizeof(SAuxVertex));
// unlock vb
m_renderer.GetDeviceContext().Unmap(m_pAuxGeomVB, 0);
// update accumulators and buffer indices
verticesCopied += toCopy;
verticesToCopy -= toCopy;
m_auxGeomSBM.m_curVBIndex += toCopy;
numVerticesWrittenToVB += toCopy;
}
else
{
// not enough space in vb for (remainder of) current push buffer entry
if (numVerticesWrittenToVB > 0)
{
// commit batch
assert(0 == numVerticesWrittenToVB % d3dNumPrimDivider);
if (streamsBound)
{
m_renderer.FX_DrawPrimitive(ePrimType, initialVBLockOffset, numVerticesWrittenToVB);
}
}
// request a DISCARD lock of vb in the next run
m_auxGeomSBM.DiscardVB();
initialVBLockOffset = m_auxGeomSBM.m_curVBIndex;
numVerticesWrittenToVB = 0;
}
}
}
if (numVerticesWrittenToVB > 0)
{
// commit batch
assert(0 == numVerticesWrittenToVB % d3dNumPrimDivider);
if (streamsBound)
{
m_renderer.FX_DrawPrimitive(ePrimType, initialVBLockOffset, numVerticesWrittenToVB);
}
}
}
void CRenderAuxGeomD3D::DrawAuxIndexedPrimitives(CAuxGeomCB::AuxSortedPushBuffer::const_iterator itBegin, CAuxGeomCB::AuxSortedPushBuffer::const_iterator itEnd, const CAuxGeomCB::EPrimType& primType)
{
assert(CAuxGeomCB::e_LineListInd == primType || CAuxGeomCB::e_TriListInd == primType);
HRESULT hr(S_OK);
// bind vertex and index streams and set vertex declaration
bool streamsBound = BindStreams(eVF_P3F_C4B_T2F, m_pAuxGeomVB, m_pAuxGeomIB);
// get aux vertex and index buffer
const CAuxGeomCB::AuxVertexBuffer& auxVertexBuffer(GetAuxVertexBuffer());
const CAuxGeomCB::AuxIndexBuffer& auxIndexBuffer(GetAuxIndexBuffer());
// determine flags for prim type
uint32 d3dNumPrimDivider;
eRenderPrimitiveType ePrimType;
DetermineAuxPrimitveFlags(d3dNumPrimDivider, ePrimType, primType);
// helpers for DP call
uint32 initialVBLockOffset(m_auxGeomSBM.m_curVBIndex);
uint32 numVerticesWrittenToVB(0);
uint32 initialIBLockOffset(m_auxGeomSBM.m_curIBIndex);
uint32 numIndicesWrittenToIB(0);
m_renderer.FX_Commit();
// process each entry
for (CAuxGeomCB::AuxSortedPushBuffer::const_iterator it(itBegin); it != itEnd; )
{
// get current push buffer entry
const CAuxGeomCB::SAuxPushBufferEntry* curPBEntry(*it);
// process a push buffer entry if it can fit at all (otherwise silently skip it)
if (e_auxGeomVBSize >= curPBEntry->m_numVertices && e_auxGeomIBSize >= curPBEntry->m_numIndices)
{
// check if push buffer still fits into current buffer
if (e_auxGeomVBSize >= m_auxGeomSBM.m_curVBIndex + curPBEntry->m_numVertices && e_auxGeomIBSize >= m_auxGeomSBM.m_curIBIndex + curPBEntry->m_numIndices)
{
// determine lock vb flags
// get pointer to vertex buffer
SAuxVertex* pVertices(0);
D3D11_MAP mp (D3D11_MAP_WRITE_NO_OVERWRITE);
if (false != m_auxGeomSBM.m_discardVB)
{
m_auxGeomSBM.m_discardVB = false;
mp = D3D11_MAP_WRITE_DISCARD;
}
D3D11_MAPPED_SUBRESOURCE mappedResource;
if (FAILED(hr = m_renderer.GetDeviceContext().Map(m_pAuxGeomVB, 0, mp, 0, &mappedResource)))
{
assert(0);
iLog->Log("ERROR: CD3DRenderAuxGeom::DrawAuxIndexedPrimitives() - Vertex buffer could not be locked!");
return;
}
pVertices = (SAuxVertex*)mappedResource.pData;
pVertices += m_auxGeomSBM.m_curVBIndex;
// move vertex data of this entry
// cppcheck-suppress nullPointer
memcpy(pVertices, &auxVertexBuffer[ curPBEntry->m_vertexOffs ], curPBEntry->m_numVertices * sizeof(SAuxVertex));
// unlock vb
m_renderer.GetDeviceContext().Unmap(m_pAuxGeomVB, 0);
// get pointer to index buffer
uint16* pIndices(0);
mp = D3D11_MAP_WRITE_NO_OVERWRITE;
if (false != m_auxGeomSBM.m_discardIB)
{
m_auxGeomSBM.m_discardIB = false;
mp = D3D11_MAP_WRITE_DISCARD;
}
if (FAILED(hr = m_renderer.GetDeviceContext().Map(m_pAuxGeomIB, 0, mp, 0, &mappedResource)))
{
assert(0);
iLog->Log("ERROR: CD3DRenderAuxGeom::DrawAuxIndexedPrimitives() - Index buffer could not be locked!");
m_renderer.GetDeviceContext().Unmap(m_pAuxGeomVB, 0);
return;
}
pIndices = (uint16*)mappedResource.pData;
pIndices += m_auxGeomSBM.m_curIBIndex;
// move index data of this entry (modify indices to match VB insert location)
for (uint32 i(0); i < curPBEntry->m_numIndices; ++i)
{
// cppcheck-suppress nullPointer
pIndices[ i ] = numVerticesWrittenToVB + auxIndexBuffer[ curPBEntry->m_indexOffs + i ];
}
// unlock ib
m_renderer.GetDeviceContext().Unmap(m_pAuxGeomIB, 0);
// update buffer indices
m_auxGeomSBM.m_curVBIndex += curPBEntry->m_numVertices;
m_auxGeomSBM.m_curIBIndex += curPBEntry->m_numIndices;
numVerticesWrittenToVB += curPBEntry->m_numVertices;
numIndicesWrittenToIB += curPBEntry->m_numIndices;
// advance to next push puffer entry
++it;
}
else
{
// push buffer entry currently doesn't fit, will be processed in the next iteration when buffers got flushed
if (numVerticesWrittenToVB > 0 && numIndicesWrittenToIB > 0)
{
// commit batch
assert(0 == numIndicesWrittenToIB % d3dNumPrimDivider);
if (streamsBound)
{
m_renderer.FX_DrawIndexedPrimitive(ePrimType, initialVBLockOffset, 0, numVerticesWrittenToVB, initialIBLockOffset, numIndicesWrittenToIB);
}
}
// request a DISCARD lock / don't advance iterator!
m_auxGeomSBM.DiscardVB();
initialVBLockOffset = m_auxGeomSBM.m_curVBIndex;
numVerticesWrittenToVB = 0;
m_auxGeomSBM.DiscardIB();
initialIBLockOffset = m_auxGeomSBM.m_curIBIndex;
numIndicesWrittenToIB = 0;
}
}
else
{
// push buffer entry too big for dedicated vb/ib buffer
// advance to next push puffer entry
assert(0);
iLog->Log("ERROR: CD3DRenderAuxGeom::DrawAuxIndexedPrimitives() - Auxiliary geometry too big to render!");
++it;
}
}
if (numVerticesWrittenToVB > 0 && numIndicesWrittenToIB > 0)
{
// commit batch
assert(0 == numIndicesWrittenToIB % d3dNumPrimDivider);
if (streamsBound)
{
m_renderer.FX_DrawIndexedPrimitive(ePrimType, initialVBLockOffset, 0, numVerticesWrittenToVB, initialIBLockOffset, numIndicesWrittenToIB);
}
}
}
void CRenderAuxGeomD3D::DrawAuxObjects(CAuxGeomCB::AuxSortedPushBuffer::const_iterator itBegin, CAuxGeomCB::AuxSortedPushBuffer::const_iterator itEnd)
{
CAuxGeomCB::EAuxDrawObjType objType(CAuxGeomCB::GetAuxObjType((*itBegin)->m_renderFlags));
// get draw params buffer
const CAuxGeomCB::AuxDrawObjParamBuffer& auxDrawObjParamBuffer(GetAuxDrawObjParamBuffer());
// process each entry
for (CAuxGeomCB::AuxSortedPushBuffer::const_iterator it(itBegin); it != itEnd; ++it)
{
// get current push buffer entry
const CAuxGeomCB::SAuxPushBufferEntry* curPBEntry(*it);
// assert than all objects in this batch are of same type
assert(CAuxGeomCB::GetAuxObjType(curPBEntry->m_renderFlags) == objType);
uint32 drawParamOffs(0);
if (curPBEntry->GetDrawParamOffs(drawParamOffs))
{
// get draw params
const CAuxGeomCB::SAuxDrawObjParams& drawParams(auxDrawObjParamBuffer[ drawParamOffs ]);
// Prepare d3d world space matrix in draw param structure
// Attention: in d3d terms matWorld is actually matWorld^T
Matrix44A matWorld;
matWorld.SetIdentity();
memcpy(&matWorld, &drawParams.m_matWorld, sizeof(drawParams.m_matWorld));
// set transformation matrices
static CCryNameR matWorldViewProjName("matWorldViewProj");
if (m_curDrawInFrontMode == e_DrawInFrontOn)
{
Matrix44A matScale(Matrix44A(Matrix34::CreateScale(Vec3(0.999f, 0.999f, 0.999f))));
Matrix44A matWorldViewScaleProjT;
matWorldViewScaleProjT = (GetCurrentView() * matScale);
matWorldViewScaleProjT = matWorldViewScaleProjT * GetCurrentProj();
matWorldViewScaleProjT = matWorldViewScaleProjT.GetTransposed();
matWorldViewScaleProjT = matWorldViewScaleProjT * matWorld;
m_pAuxGeomShader->FXSetVSFloat(matWorldViewProjName, alias_cast<Vec4*>(&matWorldViewScaleProjT), 4);
}
else
{
Matrix44A matWorldViewProjT;
matWorldViewProjT = m_matrices.m_pCurTransMat->GetTransposed();
matWorldViewProjT = matWorldViewProjT * matWorld;
m_pAuxGeomShader->FXSetVSFloat(matWorldViewProjName, alias_cast<Vec4*>(&matWorldViewProjT), 4);
}
// set color
ColorF col(drawParams.m_color);
Vec4 colVec(col.b, col.g, col.r, col.a); // need to flip r/b as drawParams.m_color was originally argb
static CCryNameR auxGeomObjColorName("auxGeomObjColor");
m_pAuxGeomShader->FXSetVSFloat(auxGeomObjColorName, &colVec, 1);
// set shading flag
Vec4 shadingVec(drawParams.m_shaded ? 0.4f : 0, drawParams.m_shaded ? 0.6f : 1, 0, 0);
static CCryNameR auxGeomObjShadingName("auxGeomObjShading");
m_pAuxGeomShader->FXSetVSFloat(auxGeomObjShadingName, &shadingVec, 1);
// set light vector (rotate back into local space)
Matrix33 matWorldInv(drawParams.m_matWorldRotation.GetInverted());
Vec3 lightLocalSpace(matWorldInv * Vec3(0.5773f, 0.5773f, 0.5773f));
// normalize light vector (matWorld could contain non-uniform scaling)
lightLocalSpace.Normalize();
Vec4 lightVec(lightLocalSpace.x, lightLocalSpace.y, lightLocalSpace.z, 0.0f);
static CCryNameR globalLightLocalName("globalLightLocal");
m_pAuxGeomShader->FXSetVSFloat(globalLightLocalName, &lightVec, 1);
// LOD calculation
Matrix44A matWorldT;
matWorldT = matWorld.GetTransposed();
Vec4 objCenterWorld;
Vec3 nullVec(0.0f, 0.0f, 0.0f);
mathVec3TransformF(&objCenterWorld, &nullVec, &matWorldT);
Vec4 objOuterRightWorld(objCenterWorld + (Vec4(GetCurrentView().m00, GetCurrentView().m10, GetCurrentView().m20, 0.0f) * drawParams.m_size));
Vec4 v0, v1;
Vec3 objCenterWorldVec(objCenterWorld.x, objCenterWorld.y, objCenterWorld.z);
Vec3 objOuterRightWorldVec(objOuterRightWorld.x, objOuterRightWorld.y, objOuterRightWorld.z);
mathVec3TransformF(&v0, &objCenterWorldVec, m_matrices.m_pCurTransMat);
mathVec3TransformF(&v1, &objOuterRightWorldVec, m_matrices.m_pCurTransMat);
float scale;
assert(fabs(v0.w - v0.w) < 1e-4);
if (fabs(v0.w) < 1e-2)
{
scale = 0.5f;
}
else
{
scale = ((v1.x - v0.x) / v0.w) * (float) max(m_wndXRes, m_wndYRes) / 500.0f;
}
// map scale to detail level
uint32 lodLevel((uint32) ((scale / 0.5f) * (e_auxObjNumLOD - 1)));
if (lodLevel >= e_auxObjNumLOD)
{
lodLevel = e_auxObjNumLOD - 1;
}
// get appropriate mesh
assert(lodLevel >= 0 && lodLevel < e_auxObjNumLOD);
SDrawObjMesh* pMesh(0);
switch (objType)
{
case CAuxGeomCB::eDOT_Sphere:
default:
{
pMesh = &m_sphereObj[ lodLevel ];
break;
}
case CAuxGeomCB::eDOT_Disk:
{
pMesh = &m_diskObj[ lodLevel ];
break;
}
case CAuxGeomCB::eDOT_Quad:
{
pMesh = &m_quadObj[lodLevel];
break;
}
case CAuxGeomCB::eDOT_Cone:
{
pMesh = &m_coneObj[ lodLevel ];
break;
}
case CAuxGeomCB::eDOT_Cylinder:
{
pMesh = &m_cylinderObj[ lodLevel ];
break;
}
}
assert(0 != pMesh);
// bind vertex and index streams and set vertex declaration
if (BindStreams(m_auxGeomObjectVertexFormat, pMesh->m_pVB, pMesh->m_pIB))
{
m_renderer.FX_Commit();
// draw mesh
m_renderer.FX_DrawIndexedPrimitive(eptTriangleList, 0, 0, pMesh->m_numVertices, 0, pMesh->m_numFaces * 3);
}
}
else
{
assert(0); // GetDrawParamOffs( ... ) failed -- corrupt data in push buffer?
}
}
}
static inline Vec3 IntersectLinePlane(const Vec3& o, const Vec3& d, const Plane& p, float& t)
{
t = -((p.n | o) + (p.d + c_clipThres)) / (p.n | d);
return(o + d * t);
}
// maps floating point channels (0.f to 1.f range) to DWORD
#define DWORD_COLORVALUE(r, g, b, a) \
DWORD( \
(((DWORD)((a) * 255.f) & 0xff) << 24) | \
(((DWORD)((r) * 255.f) & 0xff) << 16) | \
(((DWORD)((g) * 255.f) & 0xff) << 8) | \
(((DWORD)((b) * 255.f) & 0xff) << 0))
static inline DWORD ClipColor(const DWORD& c0, const DWORD& c1, float t)
{
// convert D3D DWORD color storage (ARGB) to custom ColorF storage (ColorB uses ABGR!)
const float f = 1.0f / 255.0f;
ColorF v0(
f * (float)(unsigned char)(c0 >> 16),
f * (float)(unsigned char)(c0 >> 8),
f * (float)(unsigned char)(c0 >> 0),
f * (float)(unsigned char)(c0 >> 24));
ColorF v1(
f * (float)(unsigned char)(c1 >> 16),
f * (float)(unsigned char)(c1 >> 8),
f * (float)(unsigned char)(c1 >> 0),
f * (float)(unsigned char)(c1 >> 24));
ColorF vRes(v0 + (v1 - v0) * t);
return(DWORD_COLORVALUE(vRes.r, vRes.g, vRes.b, vRes.a));
}
static bool ClipLine(Vec3* v, DWORD* c)
{
// get near plane to perform clipping
Plane nearPlane(*gRenDev->GetCamera().GetFrustumPlane(FR_PLANE_NEAR));
// get clipping flags
bool bV0Behind(-((nearPlane.n | v[ 0 ]) + nearPlane.d) < c_clipThres);
bool bV1Behind(-((nearPlane.n | v[ 1 ]) + nearPlane.d) < c_clipThres);
// proceed only if both are not behind near clipping plane
if (false == bV0Behind || false == bV1Behind)
{
if (false == bV0Behind && false == bV1Behind)
{
// no clipping needed
return(true);
}
// define line to be clipped
Vec3 p(v[ 0 ]);
Vec3 d(v[ 1 ] - v[ 0 ]);
// get clipped position
float t;
v[ 0 ] = (false == bV0Behind) ? v[ 0 ] : IntersectLinePlane(p, d, nearPlane, t);
v[ 1 ] = (false == bV1Behind) ? v[ 1 ] : IntersectLinePlane(p, d, nearPlane, t);
// get clipped colors
c[ 0 ] = (false == bV0Behind) ? c[ 0 ] : ClipColor(c[ 0 ], c[ 1 ], t);
c[ 1 ] = (false == bV1Behind) ? c[ 1 ] : ClipColor(c[ 0 ], c[ 1 ], t);
return(true);
}
else
{
return(false);
}
}
static float ComputeConstantScale(const Vec3& v, const Matrix44A& matView, const Matrix44A& matProj, const uint32 wndXRes)
{
Vec4 vCam0;
mathVec3TransformF(&vCam0, &v, &matView);
Vec4 vCam1(vCam0);
vCam1.x += 1.0f;
const float a = vCam0.y * matProj.m10 + vCam0.z * matProj.m20 + matProj.m30;
const float b = vCam0.y * matProj.m13 + vCam0.z * matProj.m23 + matProj.m33;
float c0((vCam0.x * matProj.m00 + a) / (vCam0.x * matProj.m03 + b));
float c1((vCam1.x * matProj.m00 + a) / (vCam1.x * matProj.m03 + b));
float s = (float)wndXRes * (c1 - c0);
const float epsilon = 0.001f;
return (fabsf(s) >= epsilon) ? 1.0f / s : 1.0f / epsilon;
}
void CRenderAuxGeomD3D::PrepareThickLines3D(CAuxGeomCB::AuxSortedPushBuffer::const_iterator itBegin, CAuxGeomCB::AuxSortedPushBuffer::const_iterator itEnd)
{
const CAuxGeomCB::AuxVertexBuffer& auxVertexBuffer(GetAuxVertexBuffer());
// process each entry
for (CAuxGeomCB::AuxSortedPushBuffer::const_iterator it(itBegin); it != itEnd; ++it)
{
// get current push buffer entry
const CAuxGeomCB::SAuxPushBufferEntry* curPBEntry(*it);
uint32 offset(curPBEntry->m_vertexOffs);
for (uint32 i(0); i < curPBEntry->m_numVertices / 6; ++i, offset += 6)
{
// get line vertices and thickness parameter
const float* aTmp0 = (const float*) &(auxVertexBuffer[ offset + 0 ].xyz.x);
const float* aTmp1 = (const float*) &(auxVertexBuffer[ offset + 1 ].xyz.x);
const Vec3 v[ 2 ] =
{
Vec3(aTmp0[0], aTmp0[1], aTmp0[2]),
Vec3(aTmp1[0], aTmp1[1], aTmp1[2])
};
DWORD col[ 2 ] =
{
auxVertexBuffer[ offset + 0 ].color.dcolor,
auxVertexBuffer[ offset + 1 ].color.dcolor
};
float thickness(auxVertexBuffer[ offset + 2 ].xyz.x);
bool skipLine(false);
Vec4 vf[ 4 ];
if (false == IsOrthoMode()) // regular, 3d projected geometry
{
skipLine = !ClipLine((Vec3*)v, col);
if (false == skipLine)
{
// compute depth corrected thickness of line end points
float thicknessV0(0.5f * thickness * ComputeConstantScale(v[ 0 ], GetCurrentView(), GetCurrentProj(), m_wndXRes));
float thicknessV1(0.5f * thickness * ComputeConstantScale(v[ 1 ], GetCurrentView(), GetCurrentProj(), m_wndXRes));
// compute camera space line delta
Vec4 vt[ 2 ];
mathVec3TransformF(&vt[ 0 ], &v[ 0 ], &GetCurrentView());
mathVec3TransformF(&vt[ 1 ], &v[ 1 ], &GetCurrentView());
vt[0].z = (float) fsel(-vt[0].z - c_clipThres, vt[0].z, -c_clipThres);
vt[1].z = (float) fsel(-vt[1].z - c_clipThres, vt[1].z, -c_clipThres);
Vec4 tmp(vt[ 1 ] / vt[ 1 ].z - vt[ 0 ] / vt[ 0 ].z);
Vec2 delta(tmp.x, tmp.y);
// create screen space normal of line delta
Vec2 normalVec(-delta.y, delta.x);
mathVec2NormalizeF(&normalVec, &normalVec);
Vec2 normal(normalVec.x, normalVec.y);
Vec2 n[ 2 ];
n[ 0 ] = normal * thicknessV0;
n[ 1 ] = normal * thicknessV1;
// compute final world space vertices of thick line
Vec4 vertices[4] =
{
Vec4(vt[ 0 ].x + n[ 0 ].x, vt[ 0 ].y + n[ 0 ].y, vt[ 0 ].z, vt[ 0 ].w),
Vec4(vt[ 1 ].x + n[ 1 ].x, vt[ 1 ].y + n[ 1 ].y, vt[ 1 ].z, vt[ 1 ].w),
Vec4(vt[ 1 ].x - n[ 1 ].x, vt[ 1 ].y - n[ 1 ].y, vt[ 1 ].z, vt[ 1 ].w),
Vec4(vt[ 0 ].x - n[ 0 ].x, vt[ 0 ].y - n[ 0 ].y, vt[ 0 ].z, vt[ 0 ].w)
};
mathVec4TransformF(&vf[ 0 ], &vertices[0], &GetCurrentViewInv());
mathVec4TransformF(&vf[ 1 ], &vertices[1], &GetCurrentViewInv());
mathVec4TransformF(&vf[ 2 ], &vertices[2], &GetCurrentViewInv());
mathVec4TransformF(&vf[ 3 ], &vertices[3], &GetCurrentViewInv());
}
}
else // orthogonal projected geometry
{
// compute depth corrected thickness of line end points
float thicknessV0(0.5f * thickness * ComputeConstantScale(v[ 0 ], GetCurrentView(), GetCurrentProj(), m_wndXRes));
float thicknessV1(0.5f * thickness * ComputeConstantScale(v[ 1 ], GetCurrentView(), GetCurrentProj(), m_wndXRes));
// compute line delta
Vec2 delta(v[ 1 ] - v[ 0 ]);
// create normal of line delta
Vec2 normalVec(-delta.y, delta.x);
mathVec2NormalizeF(&normalVec, &normalVec);
Vec2 normal(normalVec.x, normalVec.y);
Vec2 n[ 2 ];
n[ 0 ] = normal * thicknessV0 * 2.0f;
n[ 1 ] = normal * thicknessV1 * 2.0f;
// compute final world space vertices of thick line
vf[ 0 ] = Vec4(v[ 0 ].x + n[ 0 ].x, v[ 0 ].y + n[ 0 ].y, v[ 0 ].z, 1.0f);
vf[ 1 ] = Vec4(v[ 1 ].x + n[ 1 ].x, v[ 1 ].y + n[ 1 ].y, v[ 1 ].z, 1.0f);
vf[ 2 ] = Vec4(v[ 1 ].x - n[ 1 ].x, v[ 1 ].y - n[ 1 ].y, v[ 1 ].z, 1.0f);
vf[ 3 ] = Vec4(v[ 0 ].x - n[ 0 ].x, v[ 0 ].y - n[ 0 ].y, v[ 0 ].z, 1.0f);
}
SAuxVertex* pVertices(const_cast<SAuxVertex*>(&auxVertexBuffer[ offset ]));
if (false == skipLine)
{
// copy data to vertex buffer
pVertices[ 0 ].xyz = Vec3(vf[ 0 ].x, vf[ 0 ].y, vf[ 0 ].z);
pVertices[ 0 ].color.dcolor = col[ 0 ];
pVertices[ 1 ].xyz = Vec3(vf[ 1 ].x, vf[ 1 ].y, vf[ 1 ].z);
pVertices[ 1 ].color.dcolor = col[ 1 ];
pVertices[ 2 ].xyz = Vec3(vf[ 2 ].x, vf[ 2 ].y, vf[ 2 ].z);
pVertices[ 2 ].color.dcolor = col[ 1 ];
pVertices[ 3 ].xyz = Vec3(vf[ 0 ].x, vf[ 0 ].y, vf[ 0 ].z);
pVertices[ 3 ].color.dcolor = col[ 0 ];
pVertices[ 4 ].xyz = Vec3(vf[ 2 ].x, vf[ 2 ].y, vf[ 2 ].z);
pVertices[ 4 ].color.dcolor = col[ 1 ];
pVertices[ 5 ].xyz = Vec3(vf[ 3 ].x, vf[ 3 ].y, vf[ 3 ].z);
pVertices[ 5 ].color.dcolor = col[ 0 ];
}
else
{
// invalidate parameter data of thick line stored in vertex buffer
// (generates two black degenerated triangles at (0,0,0))
memset(pVertices, 0, sizeof(SAuxVertex) * 6);
}
}
}
}
void CRenderAuxGeomD3D::PrepareThickLines2D(CAuxGeomCB::AuxSortedPushBuffer::const_iterator itBegin, CAuxGeomCB::AuxSortedPushBuffer::const_iterator itEnd)
{
const CAuxGeomCB::AuxVertexBuffer& auxVertexBuffer(GetAuxVertexBuffer());
// process each entry
for (CAuxGeomCB::AuxSortedPushBuffer::const_iterator it(itBegin); it != itEnd; ++it)
{
// get current push buffer entry
const CAuxGeomCB::SAuxPushBufferEntry* curPBEntry(*it);
uint32 offset(curPBEntry->m_vertexOffs);
for (uint32 i(0); i < curPBEntry->m_numVertices / 6; ++i, offset += 6)
{
// get line vertices and thickness parameter
const float* aTmp0 = (const float*) &(auxVertexBuffer[ offset + 0 ].xyz.x);
const float* aTmp1 = (const float*) &(auxVertexBuffer[ offset + 1 ].xyz.x);
const Vec3 v[ 2 ] =
{
Vec3(aTmp0[0], aTmp0[1], aTmp0[2]),
Vec3(aTmp1[0], aTmp1[1], aTmp1[2])
};
const DWORD col[ 2 ] =
{
auxVertexBuffer[ offset + 0 ].color.dcolor,
auxVertexBuffer[ offset + 1 ].color.dcolor
};
float thickness(auxVertexBuffer[ offset + 2 ].xyz.x);
// get line delta and aspect ratio corrected normal
Vec3 delta(v[ 1 ] - v[ 0 ]);
Vec3 normalVec(-delta.y * m_aspectInv, delta.x * m_aspect, 0.0f);
// normalize and scale to line thickness
mathVec3NormalizeF(&normalVec, &normalVec);
Vec3 normal(normalVec.x, normalVec.y, normalVec.z);
normal *= thickness * 0.001f;
// compute final 2D vertices of thick line in normalized device space
Vec3 vf[ 4 ];
vf[ 0 ] = v[ 0 ] + normal;
vf[ 1 ] = v[ 1 ] + normal;
vf[ 2 ] = v[ 1 ] - normal;
vf[ 3 ] = v[ 0 ] - normal;
// copy data to vertex buffer
SAuxVertex* pVertices(const_cast<SAuxVertex*>(&auxVertexBuffer[ offset ]));
pVertices[ 0 ].xyz = Vec3(vf[ 0 ].x, vf[ 0 ].y, vf[ 0 ].z);
pVertices[ 0 ].color.dcolor = col[ 0 ];
pVertices[ 1 ].xyz = Vec3(vf[ 1 ].x, vf[ 1 ].y, vf[ 1 ].z);
pVertices[ 1 ].color.dcolor = col[ 1 ];
pVertices[ 2 ].xyz = Vec3(vf[ 2 ].x, vf[ 2 ].y, vf[ 2 ].z);
pVertices[ 2 ].color.dcolor = col[ 1 ];
pVertices[ 3 ].xyz = Vec3(vf[ 0 ].x, vf[ 0 ].y, vf[ 0 ].z);
pVertices[ 3 ].color.dcolor = col[ 0 ];
pVertices[ 4 ].xyz = Vec3(vf[ 2 ].x, vf[ 2 ].y, vf[ 2 ].z);
pVertices[ 4 ].color.dcolor = col[ 1 ];
pVertices[ 5 ].xyz = Vec3(vf[ 3 ].x, vf[ 3 ].y, vf[ 3 ].z);
pVertices[ 5 ].color.dcolor = col[ 0 ];
}
}
}
void CRenderAuxGeomD3D::PrepareRendering()
{
// update transformation matrices
m_matrices.UpdateMatrices(m_renderer);
// get current window resultion and update aspect ratios
m_wndXRes = m_renderer.GetWidth();
m_wndYRes = m_renderer.GetHeight();
m_aspect = 1.0f;
m_aspectInv = 1.0f;
if (m_wndXRes > 0 && m_wndYRes > 0)
{
m_aspect = (float) m_wndXRes / (float) m_wndYRes;
m_aspectInv = 1.0f / m_aspect;
}
// reset DrawInFront mode
m_curDrawInFrontMode = e_DrawInFrontOff;
// reset stream buffer manager
m_auxGeomSBM.Reset();
// reset current VB/IB
m_pCurVB = 0;
m_pCurIB = 0;
// reset current prim type
m_curPrimType = CAuxGeomCB::e_PrimTypeInvalid;
// set vertex format
//m_renderer.m_RP.m_CurVFormat = m_auxGeomVertexFormat;
}
bool CRenderAuxGeomD3D::BindStreams(const AZ::Vertex::Format& newVertexFormat, D3DBuffer* pNewVB, D3DBuffer* pNewIB)
{
// set vertex declaration
if (FAILED(m_renderer.FX_SetVertexDeclaration(0, newVertexFormat)))
{
return false;
}
// bind streams
HRESULT hr = S_OK;
if (m_pCurVB != pNewVB)
{
hr = m_renderer.FX_SetVStream(0, pNewVB, 0, newVertexFormat.GetStride());
m_pCurVB = pNewVB;
}
if (m_pCurIB != pNewIB)
{
hr = m_renderer.FX_SetIStream(pNewIB, 0, Index16);
m_pCurIB = pNewIB;
}
return SUCCEEDED(hr);
}
void CRenderAuxGeomD3D::SetShader(const SAuxGeomRenderFlags& renderFlags)
{
if (0 == m_pAuxGeomShader)
{
// allow invalid file access for this shader because it shouldn't be used in the final build anyway
CDebugAllowFileAccess ignoreInvalidFileAccess;
m_pAuxGeomShader = m_renderer.m_cEF.mfForName("AuxGeom", EF_SYSTEM);
assert(0 != m_pAuxGeomShader);
}
if (0 != m_pAuxGeomShader)
{
bool dirty(0 != (m_renderer.m_RP.m_TI[m_renderer.m_RP.m_nProcessThreadID].m_PersFlags & RBPF_FP_DIRTY));
if (false != dirty)
{
// NOTE: dirty flags are either set when setting EF_ColorOp in AdjustRenderStates
m_renderer.m_RP.m_TI[m_renderer.m_RP.m_nProcessThreadID].m_PersFlags &= ~RBPF_FP_DIRTY;
m_renderer.m_RP.m_pCurObject = m_renderer.m_RP.m_pIdendityRenderObject;
m_renderer.m_RP.m_FlagsShader_LT = m_renderer.m_RP.m_TI[m_renderer.m_RP.m_nProcessThreadID].m_eCurColorOp | (m_renderer.m_RP.m_TI[m_renderer.m_RP.m_nProcessThreadID].m_eCurAlphaOp << 8) |
(m_renderer.m_RP.m_TI[m_renderer.m_RP.m_nProcessThreadID].m_eCurColorArg << 16) | (m_renderer.m_RP.m_TI[m_renderer.m_RP.m_nProcessThreadID].m_eCurAlphaArg << 24);
}
EAuxGeomPublicRenderflags_DrawInFrontMode newDrawInFrontMode(renderFlags.GetDrawInFrontMode());
CAuxGeomCB::EPrimType newPrimType(CAuxGeomCB::GetPrimType(renderFlags));
if (false != dirty || m_pAuxGeomShader != m_renderer.m_RP.m_pShader || m_curDrawInFrontMode != newDrawInFrontMode || m_curPrimType != newPrimType)
{
if (CAuxGeomCB::e_Obj != newPrimType)
{
static CCryNameTSCRC techName("AuxGeometry");
m_pAuxGeomShader->FXSetTechnique(techName);
m_pAuxGeomShader->FXBegin(&m_renderer.m_RP.m_nNumRendPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
m_pAuxGeomShader->FXBeginPass(0);
m_renderer.m_RP.m_CurVFormat = m_auxGeomPrimitiveVertexFormat;
static CCryNameR matViewProjName("matViewProj");
if (e_DrawInFrontOn == renderFlags.GetDrawInFrontMode() && e_Mode3D == renderFlags.GetMode2D3DFlag())
{
Matrix44A matScale(Matrix44(Matrix34::CreateScale(Vec3(0.999f, 0.999f, 0.999f))));
Matrix44A matViewScaleProjT;
matViewScaleProjT = GetCurrentView() * matScale;
matViewScaleProjT = matViewScaleProjT * GetCurrentProj();
matViewScaleProjT = matViewScaleProjT.GetTransposed();
m_pAuxGeomShader->FXSetVSFloat(matViewProjName, alias_cast<Vec4*>(&matViewScaleProjT), 4);
m_curDrawInFrontMode = e_DrawInFrontOn;
}
else
{
Matrix44A matViewProjT = m_matrices.m_pCurTransMat->GetTransposed();
m_pAuxGeomShader->FXSetVSFloat(matViewProjName, alias_cast<Vec4*>(&matViewProjT), 4);
m_curDrawInFrontMode = e_DrawInFrontOff;
}
}
else
{
static CCryNameTSCRC techName("AuxGeometryObj");
m_pAuxGeomShader->FXSetTechnique(techName);
m_pAuxGeomShader->FXBegin(&m_renderer.m_RP.m_nNumRendPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
m_pAuxGeomShader->FXBeginPass(0);
m_renderer.m_RP.m_CurVFormat = m_auxGeomObjectVertexFormat;
if (e_DrawInFrontOn == renderFlags.GetDrawInFrontMode() && e_Mode3D == renderFlags.GetMode2D3DFlag())
{
m_curDrawInFrontMode = e_DrawInFrontOn;
}
else
{
m_curDrawInFrontMode = e_DrawInFrontOff;
}
}
m_curPrimType = newPrimType;
}
}
else
{
m_renderer.FX_SetFPMode();
}
}
void CRenderAuxGeomD3D::AdjustRenderStates(const SAuxGeomRenderFlags& renderFlags)
{
// init current render states mask
uint32 curRenderStates(0);
// mode 2D/3D -- set new transformation matrix
const Matrix44A* pNewTransMat(&GetCurrentTrans3D());
if (e_Mode2D == renderFlags.GetMode2D3DFlag())
{
pNewTransMat = &GetCurrentTrans2D();
}
if (m_matrices.m_pCurTransMat != pNewTransMat)
{
m_matrices.m_pCurTransMat = pNewTransMat;
m_renderer.m_RP.m_TI[m_renderer.m_RP.m_nProcessThreadID].m_matView.SetIdentity();
m_renderer.m_RP.m_TI[m_renderer.m_RP.m_nProcessThreadID].m_matProj = *pNewTransMat;
}
// set alpha blending mode
switch (renderFlags.GetAlphaBlendMode())
{
case e_AlphaAdditive:
{
curRenderStates |= GS_BLSRC_ONE | GS_BLDST_ONE;
break;
}
case e_AlphaBlended:
{
curRenderStates |= GS_BLSRC_SRCALPHA | GS_BLDST_ONEMINUSSRCALPHA;
break;
}
case e_AlphaNone:
default:
{
break;
}
}
// set fill mode
switch (renderFlags.GetFillMode())
{
case e_FillModeWireframe:
{
curRenderStates |= GS_WIREFRAME;
break;
}
case e_FillModeSolid:
default:
{
break;
}
}
// set cull mode
switch (renderFlags.GetCullMode())
{
case e_CullModeNone:
{
m_renderer.SetCullMode(R_CULL_NONE);
break;
}
case e_CullModeFront:
{
m_renderer.SetCullMode(R_CULL_FRONT);
break;
}
case e_CullModeBack:
default:
{
m_renderer.SetCullMode(R_CULL_BACK);
break;
}
}
// set depth write mode
switch (renderFlags.GetDepthWriteFlag())
{
case e_DepthWriteOff:
{
break;
}
case e_DepthWriteOn:
default:
{
curRenderStates |= GS_DEPTHWRITE;
break;
}
}
// set depth test mode
switch (renderFlags.GetDepthTestFlag())
{
case e_DepthTestOff:
{
curRenderStates |= GS_NODEPTHTEST;
break;
}
case e_DepthTestOn:
default:
{
break;
}
}
// set point size
uint8 newPointSize(m_curPointSize);
if (CAuxGeomCB::e_PtList == CAuxGeomCB::GetPrimType(renderFlags))
{
newPointSize = CAuxGeomCB::GetPointSize(renderFlags);
}
else
{
newPointSize = 1;
}
if (newPointSize != m_curPointSize)
{
assert(newPointSize > 0);
float pointSize((float) newPointSize);
assert(0);
m_curPointSize = newPointSize;
}
// apply states
m_renderer.FX_SetState(curRenderStates);
// set color operations
m_renderer.EF_SetColorOp(eCO_REPLACE, eCO_REPLACE, (eCA_Diffuse | (eCA_Diffuse << 3)), (eCA_Diffuse | (eCA_Diffuse << 3)));
m_renderer.EF_SetSrgbWrite(false);
}
void CRenderAuxGeomD3D::RT_Flush(SAuxGeomCBRawDataPackaged& data, size_t begin, size_t end, bool reset)
{
if (!CV_r_auxGeom)
{
return;
}
PROFILE_LABEL_SCOPE("AuxGeom");
// should only be called from render thread
assert(m_renderer.m_pRT->IsRenderThread());
assert(data.m_pData);
if (begin < end)
{
m_pCurCBRawData = data.m_pData;
Matrix44A origMatProj = m_renderer.m_RP.m_TI[m_renderer.m_RP.m_nProcessThreadID].m_matProj;
Matrix44A origMatView = m_renderer.m_RP.m_TI[m_renderer.m_RP.m_nProcessThreadID].m_matView;
if (false == m_renderer.IsDeviceLost())
{
// prepare rendering
PrepareRendering();
// get push buffer to process all submitted auxiliary geometries
m_pCurCBRawData->GetSortedPushBuffer(begin, end, m_auxSortedPushBuffer);
// process push buffer
for (CAuxGeomCB::AuxSortedPushBuffer::const_iterator it(m_auxSortedPushBuffer.begin()), itEnd(m_auxSortedPushBuffer.end()); it != itEnd; )
{
// mark current push buffer position
CAuxGeomCB::AuxSortedPushBuffer::const_iterator itCur(it);
// get current render flags
const SAuxGeomRenderFlags& curRenderFlags((*itCur)->m_renderFlags);
m_curTransMatrixIdx = (*itCur)->m_transMatrixIdx;
// get prim type
CAuxGeomCB::EPrimType primType(CAuxGeomCB::GetPrimType(curRenderFlags));
// find all entries sharing the same render flags
while (true)
{
++it;
if ((it == itEnd) || ((*it)->m_renderFlags != curRenderFlags) || ((*it)->m_transMatrixIdx != m_curTransMatrixIdx))
{
break;
}
}
// Adjust render states based on current render flags and m_curTransMatrixIdx
AdjustRenderStates(curRenderFlags);
// Force the constant buffer to update the camera info - AdjustRenderStates() may have changed the projection matrix.
m_renderer.RT_SetCameraInfo();
// prepare thick lines
if (CAuxGeomCB::e_TriList == primType && false != CAuxGeomCB::IsThickLine(curRenderFlags))
{
if (e_Mode3D == curRenderFlags.GetMode2D3DFlag())
{
PrepareThickLines3D(itCur, it);
}
else
{
PrepareThickLines2D(itCur, it);
}
}
// set appropriate shader
SetShader(curRenderFlags);
// draw push buffer entries
switch (primType)
{
case CAuxGeomCB::e_PtList:
case CAuxGeomCB::e_LineList:
case CAuxGeomCB::e_TriList:
{
DrawAuxPrimitives(itCur, it, primType);
}
break;
case CAuxGeomCB::e_LineListInd:
case CAuxGeomCB::e_TriListInd:
{
DrawAuxIndexedPrimitives(itCur, it, primType);
}
break;
case CAuxGeomCB::e_Obj:
default:
{
DrawAuxObjects(itCur, it);
}
break;
}
}
}
m_renderer.m_RP.m_TI[m_renderer.m_RP.m_nProcessThreadID].m_matProj = origMatProj;
m_renderer.m_RP.m_TI[m_renderer.m_RP.m_nProcessThreadID].m_matView = origMatView;
m_pCurCBRawData = 0;
m_curTransMatrixIdx = 0;
}
if (reset)
{
FlushTextMessages(data.m_pData->m_TextMessages, true);
data.m_pData->SetUsed(false);
}
}
void CRenderAuxGeomD3D::Flush(SAuxGeomCBRawDataPackaged& data, size_t begin, size_t end, bool reset)
{
m_renderer.m_pRT->RC_AuxFlush(this, data, begin, end, reset);
}
void CRenderAuxGeomD3D::FlushTextMessages(CTextMessages& tMessages, bool reset)
{
gRenDev->RenderTextMessages(tMessages);
tMessages.Clear(!reset);
}
void CRenderAuxGeomD3D::SetOrthoMode(bool enable, Matrix44A* pMatrix)
{
GetRenderAuxGeom()->SetOrthoMode(enable, pMatrix);
}
const Matrix44A& CRenderAuxGeomD3D::GetCurrentView() const
{
return IsOrthoMode() ? gRenDev->m_IdentityMatrix : m_matrices.m_matView;
}
const Matrix44A& CRenderAuxGeomD3D::GetCurrentViewInv() const
{
return IsOrthoMode() ? gRenDev->m_IdentityMatrix : m_matrices.m_matViewInv;
}
const Matrix44A& CRenderAuxGeomD3D::GetCurrentProj() const
{
return IsOrthoMode() ? GetAuxOrthoMatrix(m_curTransMatrixIdx) : m_matrices.m_matProj;
}
const Matrix44A& CRenderAuxGeomD3D::GetCurrentTrans3D() const
{
return IsOrthoMode() ? GetAuxOrthoMatrix(m_curTransMatrixIdx) : m_matrices.m_matTrans3D;
}
const Matrix44A& CRenderAuxGeomD3D::GetCurrentTrans2D() const
{
return m_matrices.m_matTrans2D;
}
bool CRenderAuxGeomD3D::IsOrthoMode() const
{
return m_curTransMatrixIdx != -1;
}
void CRenderAuxGeomD3D::SMatrices::UpdateMatrices(CD3D9Renderer& renderer)
{
renderer.GetModelViewMatrix(&m_matView.m00);
renderer.GetProjectionMatrix(&m_matProj.m00);
m_matViewInv = m_matView.GetInverted();
m_matTrans3D = m_matView * m_matProj;
m_pCurTransMat = 0;
}
void CRenderAuxGeomD3D::FreeMemory()
{
m_auxGeomCBCol.FreeMemory();
stl::free_container(m_auxSortedPushBuffer);
}
void CRenderAuxGeomD3D::Process()
{
m_auxGeomCBCol.Process();
}
CAuxGeomCB* CRenderAuxGeomD3D::GetRenderAuxGeom(void* jobID)
{
return m_auxGeomCBCol.Get(this, jobID);
}
inline const CAuxGeomCB::AuxVertexBuffer& CRenderAuxGeomD3D::GetAuxVertexBuffer() const
{
assert(m_pCurCBRawData);
return m_pCurCBRawData->m_auxVertexBuffer;
}
inline const CAuxGeomCB::AuxIndexBuffer& CRenderAuxGeomD3D::GetAuxIndexBuffer() const
{
assert(m_pCurCBRawData);
return m_pCurCBRawData->m_auxIndexBuffer;
}
inline const CAuxGeomCB::AuxDrawObjParamBuffer& CRenderAuxGeomD3D::GetAuxDrawObjParamBuffer() const
{
assert(m_pCurCBRawData);
return m_pCurCBRawData->m_auxDrawObjParamBuffer;
}
inline const Matrix44A& CRenderAuxGeomD3D::GetAuxOrthoMatrix(int idx) const
{
assert(m_pCurCBRawData && idx >= 0 && idx < (int)m_pCurCBRawData->m_auxOrthoMatrices.size());
return m_pCurCBRawData->m_auxOrthoMatrices[idx];
}
void CRenderAuxGeomD3D::SDrawObjMesh::Release()
{
gcpRendD3D->m_DevMan.ReleaseD3D11Buffer(m_pVB);
m_pVB = nullptr;
gcpRendD3D->m_DevMan.ReleaseD3D11Buffer(m_pIB);
m_pIB = nullptr;
m_numVertices = 0;
m_numFaces = 0;
}
#endif // #if defined(ENABLE_RENDER_AUX_GEOM)