基本搞图形的都学过点OpenGL,或者OSG。在OpenGL或者OSG里面要绘制一个模型基本上是自己通过代码来绘制,自己定义一些顶点、索引、法线、颜色等信息然后make一个Node,把Node加入场景图中才能渲染出一个模型(基本引擎都是这个步骤)。但是在CE或者UE中这种方法很没人提起,因为游戏引擎基本所有模型都从外部导入,很少需要自己从代码开始写,如果想把这部分研究透彻建议去看CE是怎么加载CGF模型的,我也有相关的博客粗浅说了一下。因为我有特殊的需求,模型需要用代码逻辑控制,所以必须自己从代码开始实现一个模型。
本人很不会写文章,就直接上代码,由于材质前期没有搞上但是现在弄上了来补充下。
代码分类
1.头文件
2.源文件
3.调用方式
头文件(MakeModel.h)
#pragma once
#include <vector>
struct IStatObj;
struct Edge;
struct Vertex
{
Vec3 pos;
Vec2 projected;
Vec3 smoothedNormal;
};
struct VertexNormal
{
VertexNormal() : normal(ZERO), weighting(0.0f) {}
Vec3 normal;
float weighting;
};
struct Face
{
Vertex *v[3];
Edge* edges[3];
float a[3];
bool bDone;
};
struct Edge
{
Vertex *v[2];
Face *faces[2];
};
struct Tri
{
Tri(Vec2 &a, Vec2 &b, Vec2 &c, Vec3 &ap, Vec3 &bp, Vec3 &cp, Vertex *va, Vertex *vb, Vertex *vc)
{
v[0] = a;
v[1] = b;
v[2] = c;
pos[0] = ap;
pos[1] = bp;
pos[2] = cp;
orig[0] = va;
orig[1] = vb;
orig[2] = vc;
}
Vec2 v[3];
Vec3 pos[3];
Vec3 tangent;
Vec3 bitangent;
Vec3 normal;
Vertex *orig[3];
};
typedef struct FinalVertex
{
Vec3 pos;
Vec2 uv;
Vec3 tangent;
Vec3 bitangent;
Vec3 normal;
Vertex *orig;
} FinalVertex;
namespace HelperUtil
{
struct MakeModel
{
static IStatObj* MakeMesh(std::vector<FinalVertex> &vertices, std::vector<int> &indices);
static void CalcTBN(std::vector<FinalVertex> &vertices, std::vector<int> &indices);
};
};
源文件(MakeModel.cpp)
#include "StdAfx.h"
#include "MakeModel.h"
#include<Cry3DEngine/IIndexedMesh.h>
using namespace HelperUtil;
IStatObj * MakeModel::MakeMesh(std::vector<FinalVertex>& vertices, std::vector<int>& indices)
{
//计算TBN坐标
HelperUtil::MakeModel::CalcTBN(vertices, indices);
CMesh *outMesh = new CMesh();
outMesh->SetIndexCount(indices.size());
outMesh->SetVertexCount(vertices.size());
outMesh->ReallocStream(CMesh::TEXCOORDS, vertices.size());
outMesh->ReallocStream(CMesh::NORMALS, vertices.size());
outMesh->ReallocStream(CMesh::TANGENTS, vertices.size());
outMesh->ReallocStream(CMesh::COLORS_0, vertices.size());
outMesh->ReallocStream(CMesh::COLORS_1, vertices.size());
AABB bb(AABB::RESET);
Vec3* pVertices0 = outMesh->GetStreamPtr<Vec3>(CMesh::POSITIONS);
SMeshNormal* pNormals = outMesh->GetStreamPtr<SMeshNormal>(CMesh::NORMALS);
SMeshTexCoord* pTexCoords0 = outMesh->GetStreamPtr<SMeshTexCoord>(CMesh::TEXCOORDS);
SMeshTangents* pTangents0 = outMesh->GetStreamPtr<SMeshTangents>(CMesh::TANGENTS);
SMeshColor* pColor0 = outMesh->GetStreamPtr<SMeshColor>(CMesh::COLORS_0);
SMeshColor* pColor1 = outMesh->GetStreamPtr<SMeshColor>(CMesh::COLORS_1);
for (std::vector<FinalVertex>::iterator vit = vertices.begin(), vend = vertices.end(); vit != vend; ++vit)
{
bb.Add(vit->pos);
*pVertices0++ = vit->pos;
*pTexCoords0++ = SMeshTexCoord(vit->uv.x, vit->uv.y);
*pTangents0++ = SMeshTangents(vit->tangent, vit->bitangent,vit->normal);
*pNormals++ = SMeshNormal(vit->normal);
*pColor0++ = SMeshColor(155, 0, 140, 255);
*pColor1++ = SMeshColor(155, 0, 140, 255);
}
vtx_idx* pIndices0 = outMesh->GetStreamPtr<vtx_idx>(CMesh::INDICES);
vtx_idx* pIndices = pIndices0;
for (std::vector<int>::iterator it = indices.begin(), end = indices.end(); it != end; ++it)
{
*(pIndices++) = (vtx_idx)*it;
}
//area
float area = 0.0f;
float tarea = 0.0f;
for (int i = 0; i<indices.size(); i += 3)
{
int idx0 = pIndices0[i + 0];
int idx1 = pIndices0[i + 1];
int idx2 = pIndices0[i + 2];
Vec3 e0 = pVertices0[idx1] - pVertices0[idx0];
Vec3 e1 = pVertices0[idx2] - pVertices0[idx0];
Vec2 t0 = pTexCoords0[idx0].GetUV();
Vec2 t1 = pTexCoords0[idx1].GetUV();
Vec2 t2 = pTexCoords0[idx2].GetUV();
Vec2 ta = Vec2(t1.x - t0.x, t1.y - t0.y);
Vec2 tb = Vec2(t2.x - t0.x, t2.y - t0.y);
area += e0.Cross(e1).len();
tarea += ta.Cross(tb);
}
SMeshSubset subset;
subset.nFirstIndexId = 0;
subset.nNumIndices = indices.size();
subset.nFirstVertId = 0;
subset.nNumVerts = vertices.size();
subset.nMatID = 0;
subset.nMatFlags = 0;
subset.fRadius = 100.0f;
if (area != 0.0f)
subset.fTexelDensity = tarea / area;
else
subset.fTexelDensity = 1.0f;
outMesh->m_subsets.push_back(subset);
IStatObj *pOutStatObj = gEnv->p3DEngine->CreateStatObjOptionalIndexedMesh(false);
IIndexedMesh *pIdxMesh = pOutStatObj->GetIndexedMesh(false);
if (!pIdxMesh)
pIdxMesh = pOutStatObj->CreateIndexedMesh();
pIdxMesh->SetMesh(*outMesh);
delete outMesh;
pOutStatObj->SetBBoxMin(bb.min);
pOutStatObj->SetBBoxMax(bb.max);
pOutStatObj->Invalidate();
return pOutStatObj;
}
void HelperUtil::MakeModel::CalcTBN(std::vector<FinalVertex>& vertices, std::vector<int>& indices)
{
for (int i = 0; i < indices.size(); i += 3)
{
int idx0 = indices[i + 0];
int idx1 = indices[i + 1];
int idx2 = indices[i + 2];
Vec3 v0 = vertices.at(idx0).pos;
Vec3 v1 = vertices.at(idx1).pos;
Vec3 v2 = vertices.at(idx2).pos;
Vec2 t0 = vertices.at(idx0).uv;
Vec2 t1 = vertices.at(idx1).uv;
Vec2 t2 = vertices.at(idx2).uv;
Vec3 n0 = (Vec3(v0.x, t0.x, t0.y) - Vec3(v1.x, t1.x, t1.y)).cross(Vec3(v0.x, t0.x, t0.y) - Vec3(v2.x, t2.x, t2.y));
Vec3 n1 = (Vec3(v0.y, t0.x, t0.y) - Vec3(v1.y, t1.x, t1.y)).cross(Vec3(v0.y, t0.x, t0.y) - Vec3(v2.y, t2.x, t2.y));
Vec3 n2 = (Vec3(v0.z, t0.x, t0.y) - Vec3(v1.z, t1.x, t1.y)).cross(Vec3(v0.z, t0.x, t0.y) - Vec3(v2.z, t2.x, t2.y));
float Tx = -n0.y / n0.x;
float Ty = -n1.y / n1.x;
float Tz = -n2.y / n2.x;
Vec3 T = Vec3(Tx, Ty, Tz);
float Bx = -n0.z / n0.x;
float By = -n1.z / n1.x;
float Bz = -n2.z / n2.x;
Vec3 B = Vec3(Bx, By, Bz);
Vec3 N = T.cross(B);
vertices.at(idx0).tangent = T;
vertices.at(idx1).tangent = T;
vertices.at(idx2).tangent = T;
vertices.at(idx0).bitangent = B;
vertices.at(idx1).bitangent = B;
vertices.at(idx2).bitangent = B;
vertices.at(idx0).normal = N;
vertices.at(idx1).normal = N;
vertices.at(idx2).normal = N;
}
}
调用方式
void CPlayerComponent::DrawModel()
{
/*模型所在位置*/
const float heightOffset = 10.f;
float terrainCenter = gEnv->p3DEngine->GetTerrainSize() / 2.f;
float height = gEnv->p3DEngine->GetTerrainZ(terrainCenter, terrainCenter);
Vec3 playerPosition = Vec3(terrainCenter, terrainCenter, height + heightOffset);
IRenderNode* pRenderNode = gEnv->p3DEngine->CreateRenderNode(EERType::eERType_Brush);
/*从顶点创建Mesh*/
std::vector<FinalVertex> vertices;
for (size_t i = 0; i < 3; ++i)
{
FinalVertex vertex;
vertex.pos = ZERO;
vertex.uv = ZERO;
vertex.tangent = ZERO;
vertex.bitangent = ZERO;
vertex.orig = nullptr;
vertices.push_back(vertex);
}
vertices.at(0).pos = Vec3(-1, 0, -1);
vertices.at(0).uv = Vec2(0.0, 0.0);
vertices.at(1).pos = Vec3(1, 0, -1);
vertices.at(1).uv = Vec2(1.0, 0.0);
vertices.at(2).pos = Vec3(1, 0, 1);
vertices.at(2).uv = Vec2(1.0, 1.0);
std::vector<int> indices{ 0,1,2 };
IStatObj* pStatObj = HelperUtil::MakeModel::MakeMesh(vertices, indices);
IMaterial* pMaterial = gEnv->p3DEngine->GetMaterialManager()->LoadMaterial("G:\\CryEngine\\CEProject\\test\\Assets\\Materials\\generic\\metal\\gold.mtl");
pStatObj->SetMaterial(pMaterial);
Matrix34 mat;
mat.SetIdentity();
mat.SetTranslation(playerPosition);
pRenderNode->SetEntityStatObj(pStatObj, &mat);
gEnv->p3DEngine->RegisterEntity(pRenderNode);
}
模型的绘制只需要UV、顶点坐标、索引数组就行了,TBN信息可以根据以上三个数据信息通过算法生成。