由于前几个月工作较忙,一直都没有继续研究Axiom,最近终于节点结束可以继续研究一下C#了^_^
上一篇说道了如何将Axiom连接到我们的工程,并使用Axiom应用程序框架生成一个空Axiom项目。今天我们来探讨一下如何让Axiom渲染一些我们需要的3D物件。
首先渲染一个3D物件到场景。在Axiom给出的ExampleApplication类当中有这样一个虚函数protected virtual void CreateScene() ,它是用来初始化场景的我们可以在它里面设置我们要渲染3D物件。上一节我们用AxiomTest继承了ExampleApplication,我们可以在AxiomTest中复写CreateScene() 并在它里创建我们的3D世界,代码如下:
protected override void CreateScene()
{
// 实体
Entity ent;
// 节点
SceneNode node;
// Set the default lighting
SceneManager.AmbientLight = ColorEx.White;
// Create knot object
ent = SceneManager.CreateEntity("Knot1", "knot.mesh");
node = SceneManager.RootSceneNode;
node.AttachObject(ent);
node.Scale(new Vector3(0.1f, 0.1f, 0.1f));
// 给我们的物件换个材质
ent.MaterialName = "TextureFX/Water";
}
这就是显示出来的结果:
接下来我们添加天空盒和地面,在Axiom下这些操作都变得非常简单:
SceneManager.SetSkyBox(true, "Skybox/Stormy", 50);
Plane plane = new Plane(Vector3.UnitY, Vector3.Zero); MeshManager.Instance.CreatePlane( "MyPlane", plane, 150000, 150000, 20, 20, true, 1, 100, 100, Vector3.UnitZ);
Entity planeEnt = SceneManager.CreateEntity("Plane", "MyPlane"); planeEnt.MaterialName = "Examples/Rockwall"; node.AttachObject(planeEnt); 将上面的代码插入到CreateScene()函数中即可。
以上是最基本的通过Entity在场景中绘制物体,基本过程就是用SceneManager的CreateEntity方法通过已经存在的资源来创建一个Entity然后再把这个Entity的节点绑到场景节点上,是不是很简单^_^
接下来讨论点稍微有难度的,我们来画线段,当然在Axiom中有有关画线的例子,你会发现用Axiom画线比较繁琐,基本和直接用Direct画线差不多。
这里给出一个简单的例子:
/// <summary>
/// 此类用于绘制3D空间中的线
/// </summary>
public class Line3d : SimpleRenderable
{
// constants for buffer source bindings
const int POSITION = 0;
const int COLOR = 1;
public Line3d(Vector3 startPoint, Vector3 endPoint, ColorEx color)
{
vertexData = new VertexData();
vertexData.vertexCount = 2;
vertexData.vertexStart = 0;
VertexDeclaration decl = vertexData.vertexDeclaration;
VertexBufferBinding binding = vertexData.vertexBufferBinding;
// add a position and color element to the declaration
decl.AddElement(POSITION, 0, VertexElementType.Float3, VertexElementSemantic.Position);
decl.AddElement(COLOR, 0, VertexElementType.Color, VertexElementSemantic.Diffuse);
// create a vertex buffer for the position
HardwareVertexBuffer buffer =
HardwareBufferManager.Instance.CreateVertexBuffer(
decl.GetVertexSize(POSITION),
vertexData.vertexCount,
BufferUsage.StaticWriteOnly);
Vector3[] pos = new Vector3[] { startPoint, endPoint };
// write the data to the position buffer
buffer.WriteData(0, buffer.Size, pos, true);
// bind the position buffer
binding.SetBinding(POSITION, buffer);
// create a color buffer
buffer = HardwareBufferManager.Instance.CreateVertexBuffer(
decl.GetVertexSize(COLOR),
vertexData.vertexCount,
BufferUsage.StaticWriteOnly);
int colorValue = Root.Instance.RenderSystem.ConvertColor(color);
int[] colors = new int[] { colorValue, colorValue };
// write the data to the position buffer
buffer.WriteData(0, buffer.Size, colors, true);
// bind the color buffer
binding.SetBinding(COLOR, buffer);
// MATERIAL
// grab a copy of the BaseWhite material for our use
Material material = MaterialManager.Instance.GetByName("BaseWhite");
material = material.Clone("LineMat");
// disable lighting to vertex colors are used
material.Lighting = false;
// set culling to none so the triangle is drawn 2 sided
material.CullingMode = CullingMode.None;
this.Material = material;
// set the bounding box of the line
this.box = new AxisAlignedBox(startPoint, endPoint);
}
/// <summary>
///
/// </summary>
/// <param name="startPoint">Point where the line will start.</param>
/// <param name="direction">The direction the vector is heading in.</param>
/// <param name="length">The length (magnitude) of the line vector.</param>
/// <param name="color">The color which this line should be.</param>
public Line3d(Vector3 startPoint, Vector3 direction, float length, ColorEx color)
{
// normalize the direction vector to ensure all elements fall in [0,1] range.
direction.Normalize();
// calculate the actual endpoint
Vector3 endPoint = startPoint + (direction * length);
vertexData = new VertexData();
vertexData.vertexCount = 2;
vertexData.vertexStart = 0;
VertexDeclaration decl = vertexData.vertexDeclaration;
VertexBufferBinding binding = vertexData.vertexBufferBinding;
// add a position and color element to the declaration
decl.AddElement(POSITION, 0, VertexElementType.Float3, VertexElementSemantic.Position);
decl.AddElement(COLOR, 0, VertexElementType.Color, VertexElementSemantic.Diffuse);
// create a vertex buffer for the position
HardwareVertexBuffer buffer =
HardwareBufferManager.Instance.CreateVertexBuffer(
decl.GetVertexSize(POSITION),
vertexData.vertexCount,
BufferUsage.StaticWriteOnly);
Vector3[] pos = new Vector3[] { startPoint, endPoint };
// write the data to the position buffer
buffer.WriteData(0, buffer.Size, pos, true);
// bind the position buffer
binding.SetBinding(POSITION, buffer);
// create a color buffer
buffer = HardwareBufferManager.Instance.CreateVertexBuffer(
decl.GetVertexSize(COLOR),
vertexData.vertexCount,
BufferUsage.StaticWriteOnly);
int colorValue = Root.Instance.RenderSystem.ConvertColor(color);
int[] colors = new int[] { colorValue, colorValue };
// write the data to the position buffer
buffer.WriteData(0, buffer.Size, colors, true);
// bind the color buffer
binding.SetBinding(COLOR, buffer);
// MATERIAL
// grab a copy of the BaseWhite material for our use
Material material = MaterialManager.Instance.GetByName("BaseWhite");
material = material.Clone("LineMat");
// disable lighting to vertex colors are used
material.Lighting = false;
// set culling to none so the triangle is drawn 2 sided
material.CullingMode = CullingMode.None;
this.Material = material;
// set the bounding box of the line
this.box = new AxisAlignedBox(startPoint, endPoint);
}
/// <summary>
///
/// </summary>
/// <param name="camera"></param>
/// <returns></returns>
public override float GetSquaredViewDepth(Camera camera)
{
Vector3 min, max, mid, dist;
min = box.Minimum;
max = box.Maximum;
mid = ((min - max) * 0.5f) + min;
dist = camera.DerivedPosition - mid;
return dist.LengthSquared;
}
/// <summary>
///
/// </summary>
/// <param name="op"></param>
public override void GetRenderOperation(RenderOperation op)
{
op.vertexData = vertexData;
op.indexData = null;
op.operationType = OperationType.LineList;
op.useIndices = false;
}
public override float BoundingRadius
{
get
{
return 0;
}
}
}
这个类封装了简单的画线方法,但是问题出现了,如果要画不定数量条线断而且每条每帧位置都有变化(比如要把场景中所有的实体的包围球用线框画出来),那该怎么办呢?
在游戏循环中是不能每帧都new的!否则效率将相当低!
以下是我想到的一种解决方案:
class LinesList : SimpleRenderable { // constants for buffer source bindings const int POSITION = 0; const int COLOR = 1; const int c_maxPointCount = 10000; public static int MaxPointCount { get { return c_maxPointCount; } } HardwareVertexBuffer _posBuffer = null; Vector3[] _posData = new Vector3[c_maxPointCount]; HardwareVertexBuffer _colorBuffer = null; int[] _colorData = new int[c_maxPointCount]; public LinesList() { vertexData = new VertexData(); vertexData.vertexCount = c_maxPointCount; vertexData.vertexStart = 0; VertexDeclaration decl = vertexData.vertexDeclaration; VertexBufferBinding binding = vertexData.vertexBufferBinding; // add a position and color element to the declaration decl.AddElement(POSITION, 0, VertexElementType.Float3, VertexElementSemantic.Position); decl.AddElement(COLOR, 0, VertexElementType.Color, VertexElementSemantic.Diffuse); // create a vertex buffer for the position _posBuffer = HardwareBufferManager.Instance.CreateVertexBuffer( decl.GetVertexSize(POSITION), vertexData.vertexCount, BufferUsage.DynamicWriteOnly); // write the data to the position buffer _posBuffer.WriteData(0, _posBuffer.Size, _posData, true); // bind the position buffer binding.SetBinding(POSITION, _posBuffer); // create a color buffer _colorBuffer = HardwareBufferManager.Instance.CreateVertexBuffer( decl.GetVertexSize(COLOR), vertexData.vertexCount, BufferUsage.DynamicWriteOnly); // write the data to the position buffer _colorBuffer.WriteData(0, _colorBuffer.Size, _colorData, true); // bind the color buffer binding.SetBinding(COLOR, _colorBuffer); // MATERIAL // grab a copy of the BaseWhite material for our use Material material = MaterialManager.Instance.GetByName("BaseWhite"); material = material.Clone("LineMat"); // disable lighting to vertex colors are used material.Lighting = false; // set culling to none so the triangle is drawn 2 sided material.CullingMode = CullingMode.None; this.Material = material; // set the bounding box of the line this.box = new AxisAlignedBox(new Vector3(-1000, -1000, -1000), new Vector3(1000, 1000, 1000)); } /// <summary> /// /// </summary> /// <param name="camera"></param> /// <returns></returns> public override float GetSquaredViewDepth(Camera camera) { Vector3 min, max, mid, dist; min = box.Minimum; max = box.Maximum; mid = ((min - max) * 0.5f) + min; dist = camera.DerivedPosition - mid; return dist.LengthSquared; } /// <summary> /// /// </summary> /// <param name="op"></param> public override void GetRenderOperation(RenderOperation op) { op.vertexData = vertexData; op.indexData = null; op.operationType = OperationType.LineList; op.useIndices = false; } public override float BoundingRadius { get { return 0; } } public void Update(Vector3[] posList, ColorEx[] colorList) { for (int i = 0; i < c_maxPointCount; i++) { _posData[i] = Vector3.Zero; if (i < posList.Length) { _posData[i] = posList[i]; } _colorData[i] = 0; if (i < colorList.Length) { _colorData[i] = Root.Instance.RenderSystem.ConvertColor(ColorEx.Blue); } } _posBuffer.WriteData(0, _posBuffer.Size, _posData, true); _colorBuffer.WriteData(0, _colorBuffer.Size, _colorData, true); } }
使用这个类创建一个对象然后每帧更新所有线段起点和终点的位置还有颜色即可。
本节到此结束。下一节我们继续深入Axiom。