CryEngine5.5 用代码“画”一个模型

本文介绍如何从代码层面构建3D模型,并实现其在游戏引擎中的渲染。主要内容包括定义模型所需的顶点、UV坐标及索引数组,通过算法自动生成TBN信息,以及将模型数据转换为游戏引擎可识别的格式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基本搞图形的都学过点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信息可以根据以上三个数据信息通过算法生成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值