You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用TriangleMesh加载OBJ模型报错:faces.size()需被getFaceElementSize()整除

解决JavaFX TriangleMesh加载OBJ模型不显示及faces尺寸警告问题

问题根源

  1. 索引格式不匹配:OBJ文件的顶点、纹理、法线索引是1-based,但JavaFX TriangleMesh要求0-based索引,直接转换会导致索引无效或格式错误。
  2. 面格式不符合要求:TriangleMesh仅支持三角形面,OBJ文件中可能存在四边形等多边形,未拆分的话会导致面数组长度不满足getFaceElementSize()的校验(同时使用顶点、纹理、法线时,每个顶点需3个索引,每个三角形需3个顶点,因此面数组总长度必须是9的倍数)。
  3. 相机位置未适配:即使模型加载正确,相机位置过近或未对准也会导致看不到模型。

解决方案

1. 修改OBJ解析逻辑(核心修复)

修正索引转换,添加多边形三角化处理,修改后的objParser方法如下:

private void objParser(ArrayList<Float> v, ArrayList<Float> vt, ArrayList<Float> vn, ArrayList<Integer> f) {
    try {
        File file = new File("C:\\model\\source\\MesaF\\edgy.obj");
        FileReader fr = new FileReader(file);
        BufferedReader reader = new BufferedReader(fr);
        String line = reader.readLine();
        
        while (line != null) {
            line = line.trim();
            if (line.isEmpty()) {
                line = reader.readLine();
                continue;
            }
            
            String[] strArr = line.split("\\s+");
            if (strArr[0].equals("v")) {
                for (int i = 1; i < strArr.length; i++) {
                    v.add(Float.parseFloat(strArr[i]));
                }
            } else if (strArr[0].equals("vt")) {
                for (int i = 1; i < strArr.length; i++) {
                    vt.add(Float.parseFloat(strArr[i]));
                }
            } else if (strArr[0].equals("vn")) {
                for (int i = 1; i < strArr.length; i++) {
                    vn.add(Float.parseFloat(strArr[i]));
                }
            } else if (strArr[0].equals("f")) {
                // 收集每个顶点的三个索引(转换为0-based)
                List<int[]> vertexIndices = new ArrayList<>();
                for (int i = 1; i < strArr.length; i++) {
                    String[] parts = strArr[i].split("/");
                    int vIdx = Integer.parseInt(parts[0]) - 1;
                    int vtIdx = parts.length > 1 && !parts[1].isEmpty() ? Integer.parseInt(parts[1]) - 1 : 0;
                    int vnIdx = parts.length > 2 && !parts[2].isEmpty() ? Integer.parseInt(parts[2]) - 1 : 0;
                    vertexIndices.add(new int[]{vIdx, vtIdx, vnIdx});
                }
                
                // 将多边形拆分为三角形
                if (vertexIndices.size() >= 3) {
                    for (int i = 1; i < vertexIndices.size() - 1; i++) {
                        // 第一个顶点
                        int[] v0 = vertexIndices.get(0);
                        f.add(v0[0]);
                        f.add(v0[1]);
                        f.add(v0[2]);
                        // 第i个顶点
                        int[] v1 = vertexIndices.get(i);
                        f.add(v1[0]);
                        f.add(v1[1]);
                        f.add(v1[2]);
                        // 第i+1个顶点
                        int[] v2 = vertexIndices.get(i+1);
                        f.add(v2[0]);
                        f.add(v2[1]);
                        f.add(v2[2]);
                    }
                }
            }
            line = reader.readLine();
        }
        
        reader.close();
        fr.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

2. 调整相机位置

在创建相机后添加位置和视角设置,确保能看到模型:

Camera camera = new PerspectiveCamera(true);
camera.setTranslateZ(-10); // 拉远相机距离
camera.setFieldOfView(60); // 设置合适的视角
scene.setCamera(camera);

修复说明

  • 索引转换:将OBJ的1-based索引减1,匹配TriangleMesh的0-based要求,避免索引越界。
  • 三角化处理:将OBJ中的多边形拆分为TriangleMesh支持的三角形,保证面数组长度符合getFaceElementSize()的校验规则。
  • 相机适配:通过调整相机位置和视角,确保模型处于相机可视范围内。

内容的提问来源于stack exchange,提问作者dadaya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 03:01:21