目录

JavaScript 模型处理

介绍

在 NTE 中一种较为基础的处理模型的方法是,模型文件首先可以加载为 RawModel,接下来可以随意对他进行一些处理,然后要进行一个上传过程得到 ModelClusterDynamicModelHolder,最后在渲染时将 ModelClusterDynamicModelHolder 交给 NTE 显示。

如果不使用模型文件而是程序地添加顶点数据,可以使用 RawMeshBuilder 构建 RawMesh 以及 RawModel ,接下来可以对他进行一些处理,然后上传得到 ModelClusterDynamicModelHolder

NTE 使用这些类加载和处理模型:

ModelManager

NTE 提供了一个方便的模型管理器方便加载模型等,打包了一些常用的加载模型的函数。

源码为 ModelManager.java 文件。

RawMeshBuilder

NTE 有 RawMeshBuilder 类,用于在程序中创建 RawMesh 。该类在DisplayHelper类中有用到,用于在程序中创建网格对象。 RawMeshBuilder 支持链式调用,可以将一个操作跟到另一个操作后面,在下面的示例文件中可以看到具体的用法。

源码为 RawMeshBuilder.java 文件。

下面是一些函数可以使用

每一个 RawModel 类内含有 sourceLocation(ResourceLocation) 和 meshList(HashMap<MaterialProp, RawMesh>) 两个成员变量。可以直接通过RawModel.sourceLocation 或 RawModel.meshList 来访问或更改。

sourceLocation 变量用于记录来源,如果两个的 RawModel 的来源一样,那么这两个 RawModel 实际上都是同一个 RawModel ,使用 RawModel.copy() 或RawModel.copyForMaterialChanges() 新的RawModel 都会记录此来源为原 RawModel 的来源 ,如果你不希望新对的 RawModel 的操作会影响原 RawModel ,可以在.copy()或.copyForMaterialChanges() 后设置 newRawModel.sourceLocation = null 来断开与原 RawModel 的关联,之后对两个模型的操作不会影响另一个 RawModel 。

源码为 RawModel.java 文件。

RawModel 类提供了以下方法:

函数 说明
new RawModel() 创建一个空的 RawModel 对象。
RawModel.append(other: RawModel): void 将另一个 RawModel 合并到这个 RawModel 里面。
RawModel.append(other: RawMesh): void 将一个 RawMesh 合并到这个 RawModel 里面。
RawModel.append(other: Collection<RawMesh>): void 将一些 RawMesh 合并到这个 RawModel 里
RawModel.applyMatrix(transform: atrix4f): void 用一个矩阵来变换模型里的所有顶点。
RawModel.applyTranslation(x: float, y: float, z: float): void 平移模型里的所有顶点。
RawModel.applyRotation(direction: Vector3f, angle: float): void 以原点为中心绕一个轴旋转模型里的所有顶点。角度采用角度制。
RawModel.applyScale(x: float, y: float, z: float): void 缩放模型。
RawModel.applyMirror(vx: boolean, vy: boolean, vz: boolean, nx: boolean, ny: boolean, nz: boolean): void 镜面翻转模型。
六个布尔值,前三个为要否变换顶点,后三个为要否翻转法线方向。
RawModel.applyUVMirror(u: boolean, v: boolean): void 反转 UV 方向。最终需要 V 正方向向下,所以导入 Blockbench 或 Blender 模型时需 rawModel.applyUVMirror(false, true)
RawModel.replaceTexture(oldFileName: String, path: ResourceLocation): void 把所有文件名为 oldFileName 的贴图替换为 resourceLocation 所指定的贴图。
RawModel.replaceAllTexture(path: ResourceLocation): void 把所有贴图替换为 resourceLocation 所指定的贴图。
RawModel.copy(): RawModel 复制模型的材质和顶点数据为新模型。
RawModel.copyForMaterialChanges(): RawModel 复制模型的材质为新模型,但和原先的模型共用一组顶点数据。
RawModel.setAllRenderType(renderType: String): void 设置所有材质的渲染类型。如“exterior”
RawModel.generateNormals(): void 生成法线。
RawModel.distinct(): void 精简模型,去除完全重复的面。
RawModel.triangulate(): void 三角化面,可以解决一些由于组成面的顶点不在同一平面导致的渲染问题。
RawModel.applyShear(direction: Vector3f, shear: Vector3f, ratio: float): void 应用切变变换。
RawModel.clearAttrState(attrType: VertAttrType): void 删除其中指定的顶点属性
RawModel.copyForMaterialChanges(): RawModel 创建当前模型的副本,但只复制材质属性,顶点数据与原模型共享。

ModelCluster

模型上传之后就不能再修改顶点数据和渲染阶段了,不过也还可以替换贴图。因此一个模型需要多次替换贴图时,可以先上传再替换,避免每次都替换后再上传产生的不必要的上传操作。

其中含有 uploadedOpaqueParts(VertArrays)、opaqueParts(RawModel)、uploadedTranslucentParts(VertArrays)、translucentParts(RawModel)几个成员变量。他们都带有final修饰符,不能被二次修改为其他值,但可以被读取并使用其的函数。RawModel 可以参考上面的 RawModel 部分,VertArrays 可以获得MatreialProp ,可以开发更加基本的渲染功能,详见下面MaterialProp。

下面是一些函数可以使用:

函数 说明
ModelCluster.replaceTexture(oldFileName: String, path: ResourceLocation): void 把所有文件名为 oldFileName 字符串的贴图替换为 resourceLocation 所指定的贴图。
ModelCluster.replaceAllTexture(path: ResourceLocation): void 把所有贴图替换为 resourceLocation 所指定的贴图。
ModelCluster.copyForMaterialChanges(): ModelCluster 复制模型的材质为新模型,但和原先的模型共用一组顶点数据。
ModelCluster.close(): void 关闭此 ModelCluster 实例

下面还有一些成员变量可以获取:

方法 说明
ModelCluster.uploadedOpaqueParts: VertArrays 已上传的不透明部分的 VertArrays。
ModelCluster.opaqueParts: RawModel 不透明部分的 RawModel。
ModelCluster.uploadedTranslucentParts: VertArrays 已上传的透明部分的 VertArrays。
ModelCluster.translucentParts: RawModel 透明部分的 RawModel。

VertArrays

VertArrays 可以从 ModelCluster 获得,其包含有以下方法:

源码为 DynamicModelHolder.java 文件。

首先使用 new DynamicModelHolder() 关键字创建一个新的 DynamicModelHolder 实例

加载一个rawModel let rawModel = ModelManager.loadRawModel(Resources.manager(), Resources.id(“mtr:models/cube.obj”), null); 翻转 V 坐标

rawModel.applyUVMirror(false, true);

上传得到一个ModelCluster let modelCluster = ModelManager.uploadVertArrays(rawModel); </code> ==== 示例2:使用RawMeshBuilder创建RawModel,并生成法线,最终使用 DynamicModelHolder 上传 RawModel 得到一个 ModelCluster。 ==== <code javascript> function create(ctx, state, block) { 创建一个RawModel

  let rawModel = new RawModel();
  //创建一个RawMeshBuilder
  let rawModelBuilder = new RawMeshBuilder(4, "interior", Resources.id("minecraft:textures/misc/white.png"));
  //设置顶点
  rawModelBuilder.vertex(0.5, 0.5, 0).normal(0, 0, 0).uv(0, 0).endVertex()
  .vertex(0.5, -0.5, 0).normal(0, 0, 0).uv(0, 1).endVertex()
  .vertex(-0.5, -0.5, 0).normal(0, 0, 0).uv(1, 1).endVertex()
  .vertex(-0.5, 0.5, 0).normal(0, 0, 0).uv(1, 0).endVertex();
  //上传为RawModel
  rawModel.append(rawModelBuilder.getMesh());
  //生成法线
  rawModel.generateNormals();
  //声明一个DynamicModelHolder并存入state
  state.dynamicModelHolder = new DynamicModelHolder();
  //添加到上传队列
  state.dynamicModelHolder.uploadLater(rawModel);

}

function render(ctx, state, block) {

  //得到ModelCluster
  if(state.dynamicModelHolder.getUploadedModel()!= null){
  state.model = dynamicModelHolder.getUploadedModel();
  }

}

</code>

示例3:几个关于VertAttrState的示例函数。

function alterAllRGBA (modelCluster, red ,green , blue, alpha) {
    let vertarray = modelCluster.uploadedTranslucentParts.meshList;
    let vert = vertarray[0];
    for(let i = 0; i < vertarray.length; i++) {
        vert = vertarray[i];
        vert.materialProp.attrState.setColor(red , green , blue , alpha);
    }
    vertarray = modelCluster.uploadedOpaqueParts.meshList;
    vert = vertarray[0];
    for(let i = 0; i < vertarray.length; i++) {
        vert = vertarray[i];
        vert.materialProp.attrState.setColor(red , green , blue , alpha);
    }
}
 
function getAllColor (modelCluster) {
    let result = [];
    let vertarray = modelCluster.uploadedTranslucentParts.meshList;
    let vert = vertarray[0];
    for(let i = 0; i < vertarray.length; i++) {
        vert = vertarray[i];
        result.push(vert.materialProp.attrState.color);
    }
    vertarray = modelCluster.uploadedOpaqueParts.meshList;
    vert = vertarray[0];
    for(let i = 0; i < vertarray.length; i++) {
        vert = vertarray[i];
        result.push(vert.materialProp.attrState.color);
    }
    return result;
}
 
function alterAllAlpha(modelCluster, newAlpha) {
    let vertarrays = modelCluster.uploadedTranslucentParts.meshList;
    let vertarray = vertarrays[0];
    let color = vertarray.materialProp.attrState.color;
    let red=0,green=0,blue=0;
    for(let i = 0; i < vertarrays.length; i++) {
        vertarray = vertarrays[i];
        color = vertarray.materialProp.attrState.color;
        red = (color >> 16) & 0xFF;
        green = (color >> 8) & 0xFF;
        blue = color & 0xFF;
        vertarray.materialProp.attrState.setColor(red , green , blue , newAlpha);
    }
    vertarrays = modelCluster.uploadedOpaqueParts.meshList;
    vertarray = vertarrays[0];
    color = vertarray.materialProp.attrState.color;
    red=0,green=0,blue=0;
    for(let i = 0; i < vertarrays.length; i++) {
        vertarray = vertarrays[i];
        color = vertarray.materialProp.attrState.color;
        red = (color >> 16) & 0xFF;
        green = (color >> 8) & 0xFF;
        blue = color & 0xFF;
        vertarray.materialProp.attrState.setColor(red , green , blue , newAlpha);
    }
}