返回介绍

加载导出的 Json 文件并将其显示

发布于 2025-02-18 12:46:46 字数 11298 浏览 0 评论 0 收藏 0

我将告诉你,在这篇文章的开头,我们已经建立了所需的所有逻辑,可以显示更复杂的苏珊妮网格。我们有面片、网格和顶点的逻辑,这就是我们所需要的了。

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 导出的网格文件并且以线框模式渲染了还有动画!虽然我不知道你现在的感觉,但我还是很高兴能够到达这个阶段。

如果没有,下载源代码:

那么,接下来会发生些什么呢?好了,我们需要填充三角形。这就是所谓的光栅化。我们也将使用深度缓冲区用来实现正确的渲染效果。在接下来的教程中,你将会了解如何获得这样的效果。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文