加载导出的 Json 文件并将其显示
我将告诉你,在这篇文章的开头,我们已经建立了所需的所有逻辑,可以显示更复杂的苏珊妮网格。我们有面片、网格和顶点的逻辑,这就是我们所需要的了。
Babylone 导出器为我们导出了超过我们所需要的数据并存放在了 Json 文件中。例如:纹理支持、灯光等等。这就是为什么我们在解析的时候直接跳到目前唯一注重的:顶点和面片部分了,因为线框渲染并不需要更多其他数据。
注意:C#开发人员,你需要通过 NuGet 从 Newtonsoft 安装一个 Json.Net 库,就像我们第一章中安装 SharpDX 一样。事实上,Json 解析并不像浏览器中的 JavaScript 一样原生支持.Net。
我们先在设备(Device) 对象中添加加载逻辑:
【译者注:C#代码】
// 以异步加载方式加载 Json 文件
public async Task<Mesh[]> LoadJSONFileAsync(string fileName)
{
var meshes = new List<Mesh>();
var file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync(fileName);
var data = await Windows.Storage.FileIO.ReadTextAsync(file);
dynamic jsonObject = Newtonsoft.Json.JsonConvert.DeserializeObject(data);
for (var meshIndex = 0; meshIndex < jsonObject.meshes.Count; meshIndex++)
{
var verticesArray = jsonObject.meshes[meshIndex].vertices;
// 面片
var indicesArray = jsonObject.meshes[meshIndex].indices;
var uvCount = jsonObject.meshes[meshIndex].uvCount.Value;
var verticesStep = 1;
// 取决于纹理坐标的数量,我们动态的选择 6 步进、8 步进以及 10 步进值
switch ((int)uvCount)
{
case 0:
verticesStep = 6;
break;
case 1:
verticesStep = 8;
break;
case 2:
verticesStep = 10;
break;
}
// 我们感兴趣的顶点信息数量
var verticesCount = verticesArray.Count / verticesStep;
// 面片的数量是索引数组长度除以 3(一个面片有三个顶点索引)
var facesCount = indicesArray.Count / 3;
var mesh = new Mesh(jsonObject.meshes[meshIndex].name.Value, verticesCount, facesCount);
// 首先填充我们网格的顶点数组
for (var index = 0; index < verticesCount; index++)
{
var x = (float)verticesArray[index * verticesStep].Value;
var y = (float)verticesArray[index * verticesStep + 1].Value;
var z = (float)verticesArray[index * verticesStep + 2].Value;
mesh.Vertices[index] = new Vector3(x, y, z);
}
// 然后填充面片数组
for (var index = 0; index < facesCount; index++)
{
var a = (int)indicesArray[index * 3].Value;
var b = (int)indicesArray[index * 3 + 1].Value;
var c = (int)indicesArray[index * 3 + 2].Value;
mesh.Faces[index] = new Face { A = a, B = b, C = c };
}
// 获取在 Blender 中设置的位置坐标
var position = jsonObject.meshes[meshIndex].position;
mesh.Position = new Vector3((float)position[0].Value, (float)position[1].Value, (float)position[2].Value);
meshes.Add(mesh);
}
return meshes.ToArray();
}
【译者注:TypeScript 代码】
// 以异步加载方式加载 Json 文件
// 加载完成后向回调函数传入解析完成的网格
public LoadJSONFileAsync(fileName: string, callback: (result: Mesh[]) => any): void {
var jsonObject = {};
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", fileName, true);
var that = this;
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
jsonObject = JSON.parse(xmlhttp.responseText);
callback(that.CreateMeshesFromJSON(jsonObject));
}
};
xmlhttp.send(null);
}
private CreateMeshesFromJSON(jsonObject): Mesh[] {
var meshes: Mesh[] = [];
for (var meshIndex = 0; meshIndex < jsonObject.meshes.length; meshIndex++) {
var verticesArray: number[] = jsonObject.meshes[meshIndex].vertices;
// 面片
var indicesArray: number[] = jsonObject.meshes[meshIndex].indices;
var uvCount: number = jsonObject.meshes[meshIndex].uvCount;
var verticesStep = 1;
// 取决于纹理坐标的数量,我们动态的选择 6 步进、8 步进以及 10 步进值
switch (uvCount) {
case 0:
verticesStep = 6;
break;
case 1:
verticesStep = 8;
break;
case 2:
verticesStep = 10;
break;
}
// 我们感兴趣的顶点信息数量
var verticesCount = verticesArray.length / verticesStep;
// 面片的数量是索引数组长度除以 3(一个面片有三个顶点索引)
var facesCount = indicesArray.length / 3;
var mesh = new SoftEngine.Mesh(jsonObject.meshes[meshIndex].name, verticesCount, facesCount);
// 首先填充我们网格的顶点数组
for (var index = 0; index < verticesCount; index++) {
var x = verticesArray[index * verticesStep];
var y = verticesArray[index * verticesStep + 1];
var z = verticesArray[index * verticesStep + 2];
mesh.Vertices[index] = new BABYLON.Vector3(x, y, z);
}
// 然后填充面片数组
for (var index = 0; index < facesCount; index++) {
var a = indicesArray[index * 3];
var b = indicesArray[index * 3 + 1];
var c = indicesArray[index * 3 + 2];
mesh.Faces[index] = {
A: a,
B: b,
C: c
};
}
// 获取在 Blender 中设置的位置坐标
var position = jsonObject.meshes[meshIndex].position;
mesh.Position = new BABYLON.Vector3(position[0], position[1], position[2]);
meshes.push(mesh);
}
return meshes;
}
【译者注:JavaScript 代码】
// 以异步加载方式加载 Json 文件
// 加载完成后向回调函数传入解析完成的网格
Device.prototype.LoadJSONFileAsync = function (fileName, callback) {
var jsonObject = {};
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", fileName, true);
var that = this;
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
jsonObject = JSON.parse(xmlhttp.responseText);
callback(that.CreateMeshesFromJSON(jsonObject));
}
};
xmlhttp.send(null);
};
Device.prototype.CreateMeshesFromJSON = function (jsonObject) {
var meshes = [];
for (var meshIndex = 0; meshIndex < jsonObject.meshes.length; meshIndex++) {
var verticesArray = jsonObject.meshes[meshIndex].vertices;
// 面片
var indicesArray = jsonObject.meshes[meshIndex].indices;
var uvCount = jsonObject.meshes[meshIndex].uvCount;
var verticesStep = 1;
// 取决于纹理坐标的数量,我们动态的选择 6 步进、8 步进以及 10 步进值
switch (uvCount) {
case 0:
verticesStep = 6;
break;
case 1:
verticesStep = 8;
break;
case 2:
verticesStep = 10;
break;
}
// 我们感兴趣的顶点信息数量
var verticesCount = verticesArray.length / verticesStep;
// 面片的数量是索引数组长度除以 3(一个面片有三个顶点索引)
var facesCount = indicesArray.length / 3;
var mesh = new SoftEngine.Mesh(jsonObject.meshes[meshIndex].name, verticesCount, facesCount);
// 首先填充我们网格的顶点数组
for (var index = 0; index < verticesCount; index++) {
var x = verticesArray[index * verticesStep];
var y = verticesArray[index * verticesStep + 1];
var z = verticesArray[index * verticesStep + 2];
mesh.Vertices[index] = new BABYLON.Vector3(x, y, z);
}
// 然后填充面片数组
for (var index = 0; index < facesCount; index++) {
var a = indicesArray[index * 3];
var b = indicesArray[index * 3 + 1];
var c = indicesArray[index * 3 + 2];
mesh.Faces[index] = {
A: a,
B: b,
C: c
};
}
// 获取在 Blender 中设置的位置坐标
var position = jsonObject.meshes[meshIndex].position;
mesh.Position = new BABYLON.Vector3(position[0], position[1], position[2]);
meshes.push(mesh);
}
return meshes;
};
你可能会问,为什么我们要设置 6、8、10 的步进值?这是因为 Babylon 增加了更多的细节,我们在使用的时候直接把这些细节过滤掉。
这种逻辑是特定于我们的文件格式的,如果要加载其他(如 Three.js)导出器的文件,你只需要实现另一种文件格式的规范读取点、面和网格。
注意 :要想能够载入我们的 .babylon 文件,对于 TypeScript/JavaScript 开发者而言,在 IIS 中,需要在 web.config 中定义一个新的 MIME 类型 "application/babylon",扩展名为 ".babylon"。否则将出现 404.3 错误。
<system.webServer>
<staticContent>
<mimeMap fileExtension=".babylon" mimeType="application/babylon" />
</staticContent>
</system.webServer>
对于 C#开发者来说,你需要更改文件属性,将其包含在解决方案中,编译方式为“内容”,并在复制输出目录中选择 “始终复制”。否则,该文件将不会被发现。最后,我们需要更新我们的主要功能,手动调用 LoadJSONFileAsync 函数。如果我们加载多个网格动画,则需要在绘制时旋转每一个网格:
【译者注:C#代码】
private Device device;
Mesh[] meshes;
Camera mera = new Camera();
private async void Page_Loaded(object sender, RoutedEventArgs e)
{
// 在这里设置后台缓冲区的分辨率
WriteableBitmap bmp = new WriteableBitmap(640, 480);
// 设置我们的 XAML 图像源
frontBuffer.Source = bmp;
device = new Device(bmp);
meshes = await device.LoadJSONFileAsync("monkey.babylon");
mera.Position = new Vector3(0, 0, 10.0f);
mera.Target = Vector3.Zero;
// 注册 XAML 渲染循环
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
// 渲染循环处理
void CompositionTarget_Rendering(object sender, object e)
{
device.Clear(0, 0, 0, 255);
foreach (var mesh in meshes)
{
// 每一帧都稍微转动一下立方体
mesh.Rotation = new Vector3(mesh.Rotation.X + 0.01f, mesh.Rotation.Y + 0.01f, mesh.Rotation.Z);
}
// 做各种矩阵运算
device.Render(mera, meshes);
// 刷新后台缓冲区到前台缓冲区
device.Present();
}
【译者注:TypeScript 代码】
///<reference path="SoftEngine.ts"/>
var canvas: HTMLCanvasElement;
var device: SoftEngine.Device;
var meshes: SoftEngine.Mesh[] = [];
var mera: SoftEngine.Camera;
document.addEventListener("DOMContentLoaded", init, false);
function init() {
canvas = <HTMLCanvasElement> document.getElementById("frontBuffer");
mera = new SoftEngine.Camera();
device = new SoftEngine.Device(canvas);
mera.Position = new BABYLON.Vector3(0, 0, 10);
mera.Target = new BABYLON.Vector3(0, 0, 0);
device.LoadJSONFileAsync("monkey.babylon", loadJSONCompleted)
}
function loadJSONCompleted(meshesLoaded: SoftEngine.Mesh[]) {
meshes = meshesLoaded;
// 调用 Html5 渲染循环
requestAnimationFrame(drawingLoop);
}
// 渲染循环处理
function drawingLoop() {
device.clear();
for (var i = 0; i < meshes.length; i++) {
// 每帧都稍微转动一下立方体
meshes[i].Rotation.x += 0.01;
meshes[i].Rotation.y += 0.01;
}
// 做各种矩阵运算
device.render(mera, meshes);
// 刷新后台缓冲区到前台缓冲区
device.present();
// 递归调用 Html5 渲染循环
requestAnimationFrame(drawingLoop);
}
【译者注:JavaScript 代码】
var canvas;
var device;
var meshes = [];
var mera;
document.addEventListener("DOMContentLoaded", init, false);
function init() {
canvas = document.getElementById("frontBuffer");
mera = new SoftEngine.Camera();
device = new SoftEngine.Device(canvas);
mera.Position = new BABYLON.Vector3(0, 0, 10);
mera.Target = new BABYLON.Vector3(0, 0, 0);
device.LoadJSONFileAsync("monkey.babylon", loadJSONCompleted);
}
function loadJSONCompleted(meshesLoaded) {
meshes = meshesLoaded;
// 调用 Html5 渲染循环
requestAnimationFrame(drawingLoop);
}
// 渲染循环处理
function drawingLoop() {
device.clear();
for (var i = 0; i < meshes.length; i++) {
// 每帧都稍微转动一下立方体
meshes[i].Rotation.x += 0.01;
meshes[i].Rotation.y += 0.01;
}
// 做各种矩阵运算
device.render(mera, meshes);
// 刷新后台缓冲区到前台缓冲区
device.present();
// 递归调用 Html5 渲染循环
requestAnimationFrame(drawingLoop);
}
你现在应该有一个 3D 引擎,它可以加载一个由 Blender 导出的网格文件并且以线框模式渲染了还有动画!虽然我不知道你现在的感觉,但我还是很高兴能够到达这个阶段。
如果没有,下载源代码:
- C#: SoftEngineCSharpPart3.zip
- TypeScript: SoftEngineTSPart3.zip
- JavaScript: SoftEngineJSPart3.zip 或只需右键点击 -> 查看框架的源代码
那么,接下来会发生些什么呢?好了,我们需要填充三角形。这就是所谓的光栅化。我们也将使用深度缓冲区用来实现正确的渲染效果。在接下来的教程中,你将会了解如何获得这样的效果。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论