0

In order to build a file converter with gltf/glb being the starting file type, i need input the data of the glb file first. When tring to do that, I encountered a negative arry error given the fact it tried to read the JSON part of the glb file

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Paths;
public class glTFConverter implements ActionListener {

    private static final String[] SUPPORTED_FILE_EXTENSIONS = {"gltf", "glb"};
    private static final String[] SUPPORTED_OUTPUT_FORMATS = {"obj"};

    private JFrame frame;
    private JFileChooser fileChooser;
    private JCheckBox keepOriginalCheckBox;

    public glTFConverter() {
        frame = new JFrame("GLTF to OBJ Converter");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 200);
        frame.setLayout(new FlowLayout());

        fileChooser = new JFileChooser();
        FileNameExtensionFilter filter = new FileNameExtensionFilter("GLTF Files", SUPPORTED_FILE_EXTENSIONS);
        fileChooser.setFileFilter(filter);
        fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);

        JButton selectButton = new JButton("Select File or Folder");
        selectButton.addActionListener(this);

        keepOriginalCheckBox = new JCheckBox("Keep Original Files", true);

        frame.add(selectButton);
        frame.add(keepOriginalCheckBox);
        frame.setVisible(true);
    }

    public void actionPerformed(ActionEvent e) {
        if (e.getActionCommand().equals("Select File or Folder")) {
            int result = fileChooser.showOpenDialog(frame);
            if (result == JFileChooser.APPROVE_OPTION) {
                File selectedFile = fileChooser.getSelectedFile();
                if (selectedFile.isDirectory()) {
                    convertFolder(selectedFile.getAbsolutePath());
                } else {
                    convertFile(selectedFile.getAbsolutePath());
                }
            }
        }
    }

    public void convertFolder(String folderPath) {
        File folder = new File(folderPath);
        if (!folder.exists() || !folder.isDirectory()) {
            System.out.println("Invalid folder path: " + folderPath);
            return;
        }

        File[] files = folder.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isFile()) {
                    convertFile(file.getAbsolutePath());
                }
            }
        }
    }

    public void convertFile(String filePath) {
        String fileExtension = getFileExtension(filePath);
        if (fileExtension != null && isSupportedFileExtension(fileExtension)) {
            String outputFormat = SUPPORTED_OUTPUT_FORMATS[0];
            String outputFilePath = convertGLTFToOutputFormat(filePath, outputFormat);
            if (outputFilePath != null) {
                System.out.println("Converted file: " + outputFilePath);

                // Delete the original file if required
                if (!keepOriginalCheckBox.isSelected()) {
                    File originalFile = new File(filePath);
                    originalFile.delete();
                }
            }
        } else {
            System.out.println("Unsupported file extension: " + fileExtension);
        }
    }

    private String convertGLTFToOutputFormat(String filePath, String outputFormat) {
        String fileExtension = getFileExtension(filePath);
        if (fileExtension.equalsIgnoreCase("gltf")) {
            if (outputFormat.equalsIgnoreCase("obj")) {
                return convertGLTFToOBJ(filePath);
            } else {
                System.out.println("Unsupported output format: " + outputFormat);
            }
        } else if (fileExtension.equalsIgnoreCase("glb")) {
            if (outputFormat.equalsIgnoreCase("obj")) {
                return convertGLBToOBJ(filePath);
            } else {
                System.out.println("Unsupported output format: " + outputFormat);
            }
        } else {
            System.out.println("Unsupported file extension: " + fileExtension);
        }
        return null;
    }



    private String convertGLBToOBJ(String glbFilePath) {
        try (DataInputStream dataInputStream = new DataInputStream(new FileInputStream(glbFilePath))) {
            byte[] magic = new byte[4];
            dataInputStream.readFully(magic);

            if (!new String(magic).equals("glTF")) {
                System.out.println("Invalid GLB file format: " + glbFilePath);
                return null;
            }

            dataInputStream.skipBytes(8);
            int jsonChunkLength = dataInputStream.readInt();
            System.out.println("JSON Chunk Length: " + jsonChunkLength);
            int jsonChunkType = dataInputStream.readInt();
            System.out.println("JSON Chunk Type: " + jsonChunkType);

            byte[] jsonChunkBytes = new byte[jsonChunkLength];
            dataInputStream.readFully(jsonChunkBytes);
            String jsonString = new String(jsonChunkBytes);
            System.out.println("JSON String: " + jsonString);

            String tempGLTFFilePath = convertGLBToGLTF(glbFilePath);
            if (tempGLTFFilePath != null) {
                JSONObject gltfJson = readGLTFFile(tempGLTFFilePath);
                if (gltfJson != null) {
                    JSONArray gltfMeshes;
                    if (gltfJson.has("meshes")) {
                        Object meshes = gltfJson.get("meshes");
                        if (meshes instanceof JSONArray) {
                            gltfMeshes = (JSONArray) meshes;
                        } else {
                            gltfMeshes = new JSONArray().put(meshes);
                        }
                    } else {
                        gltfMeshes = new JSONArray();
                    }

                    JSONArray gltfNodes;
                    if (gltfJson.has("nodes")) {
                        Object nodes = gltfJson.get("nodes");
                        if (nodes instanceof JSONArray) {
                            gltfNodes = (JSONArray) nodes;
                        } else {
                            gltfNodes = new JSONArray().put(nodes);
                        }
                    } else {
                        gltfNodes = new JSONArray();
                    }

                    JSONObject gltfAccessors;
                    if (gltfJson.has("accessors") && gltfJson.get("accessors") instanceof JSONObject) {
                        gltfAccessors = gltfJson.getJSONObject("accessors");
                    } else {
                        gltfAccessors = new JSONObject();
                    }

                    String objFilePath = glbFilePath.replace(".glb", ".obj");
                    try (FileWriter fileWriter = new FileWriter(objFilePath);
                         BufferedWriter bufferedWriter = new BufferedWriter(fileWriter)) {
                        bufferedWriter.write("# OBJ file");
                        bufferedWriter.newLine();

                        JSONArray positions = gltfAccessors.getJSONArray("POSITION");

                        for (int i = 0; i < positions.length(); i += 3) {
                            double x = positions.getJSONArray(i).getDouble(0);
                            double y = positions.getJSONArray(i + 1).getDouble(0);
                            double z = positions.getJSONArray(i + 2).getDouble(0);
                            bufferedWriter.write("v " + x + " " + y + " " + z);
                            bufferedWriter.newLine();
                        }

                        for (int i = 0; i < gltfMeshes.length(); i++) {
                            JSONObject mesh = gltfMeshes.getJSONObject(i);
                            JSONArray primitives = mesh.getJSONArray("primitives");
                            for (int j = 0; j < primitives.length(); j++) {
                                JSONObject primitive = primitives.getJSONObject(j);
                                JSONObject indicesObject = primitive.getJSONObject("indices");
                                JSONArray indicesArray = gltfAccessors.getJSONArray(indicesObject.getString("accessor"));
                                for (int k = 0; k < indicesArray.length(); k += 3) {
                                    int index1 = indicesArray.getJSONArray(k).getInt(0) + 1;
                                    int index2 = indicesArray.getJSONArray(k + 1).getInt(0) + 1;
                                    int index3 = indicesArray.getJSONArray(k + 2).getInt(0) + 1;
                                    bufferedWriter.write("f " + index1 + " " + index2 + " " + index3);
                                    bufferedWriter.newLine();
                                }
                            }
                        }
                    }
                    return objFilePath;
                }
            }
        } catch (EOFException e) {
            System.out.println("Unexpected end of file encountered: " + glbFilePath);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }






    private JSONObject readGLTFFile(String filePath) throws IOException {
        byte[] bytes = Files.readAllBytes(Paths.get(filePath));
        String jsonString = new String(bytes);
        try {
            return new JSONObject(jsonString);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return null;
    }


    private String getFileExtension(String filePath) {
        int dotIndex = filePath.lastIndexOf(".");
        if (dotIndex > 0 && dotIndex < filePath.length() - 1) {
            return filePath.substring(dotIndex + 1).toLowerCase();
        }
        return null;
    }

    private boolean isSupportedFileExtension(String extension) {
        for (String supportedExtension : SUPPORTED_FILE_EXTENSIONS) {
            if (extension.equalsIgnoreCase(supportedExtension)) {
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        new glTFConverter();
    }
}
Exception in thread "AWT-EventQueue-0" java.lang.NegativeArraySizeException
    at glTFConverter.convertGLBToOBJ(glTFConverter.java:184)
    at glTFConverter.convertGLTFToOutputFormat(glTFConverter.java:106)
JSON Chunk Length: -133890048
JSON Chunk Type: 1246973774

tried to work around that error with turning the negtive value positive and also in using a different start point when reading the JSON part of the file

Rhysling
  • 1
  • 2
  • The chunk size is an unsigned int, which is not the same as just flipping a negative value positive. Try `long foo = dataInputStream.readInt() & 0xffffffffL;` as suggested in this answer: https://stackoverflow.com/a/22938125/836708 – emackey Jul 12 '23 at 14:57

0 Answers0