Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assimp trying to load 3ds File #982

Open
Verschwiegener opened this issue May 24, 2024 · 2 comments
Open

Assimp trying to load 3ds File #982

Verschwiegener opened this issue May 24, 2024 · 2 comments

Comments

@Verschwiegener
Copy link

Version

3.3.3

Platform

Linux x64

JDK

OpenJDK-17-Amd64

Module

Assimp

Bug description

Im trying to load a .3ds File with Assimp but the code crashes in Native Code, the Model Load code is a slightly changed Version of the Code in the Assimp Example. I checked that the Path to the 3ds File is valid and the Code does work with OBJ Files.

Any help would be appreciated :)

Model Load Code:

static AIFileIO fileIo = AIFileIO.create().OpenProc((pFileIO, fileName, openMode) -> {
		ByteBuffer data;
		String fileNameUtf8 = memUTF8(fileName);
		try {
			data = ioResourceToByteBuffer(fileNameUtf8, 8192);
		} catch (IOException e) {
			e.printStackTrace();
			throw new RuntimeException("Could not open file: " + fileNameUtf8);
		}
		
		return AIFile.create().ReadProc((pFile, pBuffer, size, count) -> {
			long max = Math.min(data.remaining() / size, count);
			memCopy(memAddress(data), pBuffer, max * size);
			data.position(data.position() + (int) (max * size));
			return max;
		}).SeekProc((pFile, offset, origin) -> {
			if (origin == Assimp.aiOrigin_CUR) {
				data.position(data.position() + (int) offset);
			} else if (origin == Assimp.aiOrigin_SET) {
				data.position((int) offset);
			} else if (origin == Assimp.aiOrigin_END) {
				data.position(data.limit() + (int) offset);
			}
			return 0;
		}).FileSizeProc(pFile -> data.limit()).address();
	}).CloseProc((pFileIO, pFile) -> {
		AIFile aiFile = AIFile.create(pFile);
		aiFile.ReadProc().free();
		aiFile.SeekProc().free();
		aiFile.FileSizeProc().free();
	});

	public static Object[] loadObject(String path) {
		AIScene scene = aiImportFileEx(path,
				aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_FixInfacingNormals, fileIo);
		
		if (scene == null)
			System.err.println("Couldn't load model at: " + path);
		
		assert scene != null;

		AIMesh[] meshes = new AIMesh[scene.mNumMeshes()];
		Object[] internal = new Object[scene.mNumMeshes()];

		for (int i = 0; i < scene.mNumMeshes(); i++) {
			meshes[i] = AIMesh.create(scene.mMeshes().get(i));
			int vertexCount = meshes[i].mNumVertices();

			AIVector3D.Buffer vertices = meshes[i].mVertices();
			AIVector3D.Buffer normals = meshes[i].mNormals();

			Vertex[] vertexList = new Vertex[vertexCount];
			for (int j = 0; j < vertexCount; j++) {
				AIVector3D vertex = vertices.get(j);
				Vector3f meshVertex = new Vector3f(vertex.x(), vertex.y(), vertex.z());

				AIVector3D normal = normals.get(j);
				Vector3f meshNormal = new Vector3f(normal.x(), normal.y(), normal.z());

				Vector2f meshTextureCoord = new Vector2f();
				if (meshes[i].mNumUVComponents().get(0) != 0) {
					AIVector3D texture = meshes[i].mTextureCoords(0).get(j);
					meshTextureCoord.x = texture.x();
					meshTextureCoord.y = texture.y();
				}

				vertexList[j] = new Vertex(meshVertex, new Vector3f(1, 1, 1), meshTextureCoord, meshNormal);
			}

			int faceCount = meshes[i].mNumFaces();
			AIFace.Buffer indices = meshes[i].mFaces();
			int[] indicesList = new int[faceCount * 3];
			for (int k = 0; k < faceCount; k++) {
				AIFace face = indices.get(k);
				indicesList[k * 3] = face.mIndices().get(0);
				indicesList[k * 3 + 1] = face.mIndices().get(1);
				indicesList[k * 3 + 2] = face.mIndices().get(2);
			}

			internal[i] = new Object(vertexList, indicesList);
		}
		return internal;
	}
	
	public static ByteBuffer ioResourceToByteBuffer(String resource, int bufferSize) throws IOException {
        ByteBuffer buffer;
        URL url = Thread.currentThread().getContextClassLoader().getResource(resource);
        if (url == null)
            throw new IOException("Classpath resource not found: " + resource);
        File file = new File(url.getFile());
        if (file.isFile()) {
            FileInputStream fis = new FileInputStream(file);
            FileChannel fc = fis.getChannel();
            buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
            fc.close();
            fis.close();
        } else {
            buffer = BufferUtils.createByteBuffer(bufferSize);
            InputStream source = url.openStream();
            if (source == null)
                throw new FileNotFoundException(resource);
            try {
                byte[] buf = new byte[8192];
                while (true) {
                    int bytes = source.read(buf, 0, buf.length);
                    if (bytes == -1)
                        break;
                    if (buffer.remaining() < bytes)
                        buffer = resizeBuffer(buffer, Math.max(buffer.capacity() * 2, buffer.capacity() - buffer.remaining() + bytes));
                    buffer.put(buf, 0, bytes);
                }
                buffer.flip();
            } finally {
                source.close();
            }
        }
        return buffer;
    }
	
	private static ByteBuffer resizeBuffer(ByteBuffer buffer, int newCapacity) {
        ByteBuffer newBuffer = BufferUtils.createByteBuffer(newCapacity);
        buffer.flip();
        newBuffer.put(buffer);
        return newBuffer;
    }

Stacktrace or crash log output

---------------  S U M M A R Y ------------

Command Line: -XX:+ShowCodeDetailsInExceptionMessages -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:35597 -javaagent:/home/julius/eclipse/java-2021-12/eclipse/configuration/org.eclipse.osgi/216/0/.cp/lib/javaagent-shaded.jar -Dfile.encoding=UTF-8 de.verschwiegener.zeustracking.Management

Host: AMD Ryzen 7 1700X Eight-Core Processor, 16 cores, 31G, Ubuntu 22.04.4 LTS
Time: Fri May 24 04:29:41 2024 CEST elapsed time: 2.074218 seconds (0d 0h 0m 2s)

---------------  T H R E A D  ---------------

Current thread (0x00007f5ca88e51e0):  JavaThread "GUI_RENDERER" [_thread_in_native, id=76861, stack(0x00007f5ce8078000,0x00007f5ce8178000)]

Stack: [0x00007f5ce8078000,0x00007f5ce8178000],  sp=0x00007f5ce8175120,  free space=1012k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [libassimp.so+0x26dba0]

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  org.lwjgl.system.JNI.invokePPP(JIJJ)J+0
j  org.lwjgl.assimp.Assimp.naiImportFileEx(JIJ)J+26
j  org.lwjgl.assimp.Assimp.aiImportFileEx(Ljava/lang/CharSequence;ILorg/lwjgl/assimp/AIFileIO;)Lorg/lwjgl/assimp/AIScene;+30
j  com.spinyowl.legui.system.renderer.openGL.Model.loadObject(Ljava/lang/String;)[Lcom/spinyowl/legui/system/renderer/openGL/mesh/Object;+7
j  de.verschwiegener.zeustracking.MVREntity.initialize(Lcom/spinyowl/legui/system/renderer/openGL/engine/Engine;)V+2
j  com.spinyowl.legui.system.renderer.openGL.engine.Engine.update(Z)V+54
j  com.spinyowl.legui.system.renderer.nvg.component.NvgEngineRenderer.renderSelf(Lcom/spinyowl/legui/component/EngineComponent;Lcom/spinyowl/legui/system/context/Context;J)V+259
j  com.spinyowl.legui.system.renderer.nvg.component.NvgEngineRenderer.renderSelf(Lcom/spinyowl/legui/component/Component;Lcom/spinyowl/legui/system/context/Context;J)V+7
J 2097 c1 com.spinyowl.legui.system.renderer.nvg.component.NvgDefaultComponentRenderer.renderComponent(Lcom/spinyowl/legui/component/Component;Lcom/spinyowl/legui/system/context/Context;J)V (44 bytes) @ 0x00007f5d599d34a4 [0x00007f5d599d3080+0x0000000000000424]
J 2095 c1 com.spinyowl.legui.system.renderer.nvg.NvgComponentRenderer.renderComponent(Lcom/spinyowl/legui/component/Component;Lcom/spinyowl/legui/system/context/Context;)V (94 bytes) @ 0x00007f5d599d2594 [0x00007f5d599d1fe0+0x00000000000005b4]
J 2314 c1 com.spinyowl.legui.system.renderer.nvg.component.NvgDefaultComponentRenderer.renderChildComponents(Lcom/spinyowl/legui/component/Component;Lcom/spinyowl/legui/system/context/Context;J)V (56 bytes) @ 0x00007f5d59a2f7f4 [0x00007f5d59a2ee60+0x0000000000000994]
J 2097 c1 com.spinyowl.legui.system.renderer.nvg.component.NvgDefaultComponentRenderer.renderComponent(Lcom/spinyowl/legui/component/Component;Lcom/spinyowl/legui/system/context/Context;J)V (44 bytes) @ 0x00007f5d599d356c [0x00007f5d599d3080+0x00000000000004ec]
J 2095 c1 com.spinyowl.legui.system.renderer.nvg.NvgComponentRenderer.renderComponent(Lcom/spinyowl/legui/component/Component;Lcom/spinyowl/legui/system/context/Context;)V (94 bytes) @ 0x00007f5d599d2594 [0x00007f5d599d1fe0+0x00000000000005b4]
J 2094 c1 com.spinyowl.legui.system.renderer.ComponentRenderer.render(Lcom/spinyowl/legui/component/Component;Lcom/spinyowl/legui/system/context/Context;)V (7 bytes) @ 0x00007f5d599ca154 [0x00007f5d599ca040+0x0000000000000114]
j  com.spinyowl.legui.system.renderer.AbstractRenderer.render(Lcom/spinyowl/legui/component/Frame;Lcom/spinyowl/legui/system/context/Context;)V+44
j  com.spinyowl.legui.Window.render()V+254
j  com.spinyowl.legui.Window$$Lambda$90+0x00007f5cec0c5c70.run()V+4
j  java.lang.Thread.run()V+11 java.base@17.0.10
v  ~StubRoutines::call_stub

siginfo: si_signo: 11 (SIGSEGV), si_code: 1 (SEGV_MAPERR), si_addr: 0x0000000000000000

Registers:
RAX=0x00007f5c4cd2d200, RBX=0x00007f5d77d6e600, RCX=0x0000000000000003, RDX=0x00007f5ca88bf1c8
RSP=0x00007f5ce8175118, RBP=0x00007f5c509c4630, RSI=0x00007f5ce81750f0, RDI=0x00007f5c509b5bd0
R8 =0x00007f5d31057e90, R9 =0x0000000000000004, R10=0x00007f5d48063248, R11=0x00007f5d48063210
R12=0x00007f5ce8175180, R13=0x00007f5c509b6140, R14=0x00000000000bef4e, R15=0x00007f5ce81755f0
RIP=0x0000000000000000, EFLAGS=0x0000000000010293, CSGSFS=0x002b000000000033, ERR=0x0000000000000014
  TRAPNO=0x000000000000000e


Register to memory mapping:

RAX=0x00007f5c4cd2d200: <offset 0x00000000008ae200> in /tmp/lwjgl_julius/3.3.3+5/x64/libassimp.so at 0x00007f5c4c47f000
RBX=0x00007f5d77d6e600: _ZNSs4_Rep20_S_empty_rep_storageE+0x0000000000000000 in /lib/x86_64-linux-gnu/libstdc++.so.6 at 0x00007f5d77b43000
RCX=0x0000000000000003 is an unknown value
RDX=0x00007f5ca88bf1c8 points into unknown readable memory: 0x0000000000000105 | 05 01 00 00 00 00 00 00
RSP=0x00007f5ce8175118 is pointing into the stack for thread: 0x00007f5ca88e51e0
RBP=0x00007f5c509c4630 points into unknown readable memory: 0x00007f5c4cd2db90 | 90 db d2 4c 5c 7f 00 00
RSI=0x00007f5ce81750f0 is pointing into the stack for thread: 0x00007f5ca88e51e0
RDI=0x00007f5c509b5bd0 points into unknown readable memory: 0x00007f5d74000d60 | 60 0d 00 74 5d 7f 00 00
R8 ={method} {0x00007f5d31057e90} 'callback' '(JJ)V' in 'org/lwjgl/assimp/AIFileTellProcI'
R9 =0x0000000000000004 is an unknown value
R10=0x00007f5d48063248: <offset 0x0000000000058248> in /tmp/lwjgl_julius/3.3.3+5/x64/liblwjgl.so at 0x00007f5d4800b000
R11=0x00007f5d48063210: <offset 0x0000000000058210> in /tmp/lwjgl_julius/3.3.3+5/x64/liblwjgl.so at 0x00007f5d4800b000
R12=0x00007f5ce8175180 is pointing into the stack for thread: 0x00007f5ca88e51e0
R13=0x00007f5c509b6140 points into unknown readable memory: 0x00007f5c00000000 | 00 00 00 00 5c 7f 00 00
R14=0x00000000000bef4e is an unknown value
R15=0x00007f5ce81755f0 is pointing into the stack for thread: 0x00007f5ca88e51e0
@moomba42
Copy link

Similar issue here, but with loading an .obj file

@TheIncgi
Copy link

TheIncgi commented Sep 8, 2024

I just ran into this issue (or at least a very similar one) trying to load an .obj file and having the JVM crash when trying to do

memCopy(memAddress(data), pBuffer, max * size);

I managed to fix my issue with it crashing by making my ByteBuffer with MemoryUtil.memAlloc (instead of ByteBuffer.wrap which I was using before)
Here's what my equivilant of public static ByteBuffer ioResourceToByteBuffer(String resource, int bufferSize) looks like

/** Allocates a new byte buffer which must be freed with MemoryUtil.memFree */
public static ByteBuffer resourceToByteBuffer(String fileName) throws FileNotFoundException, IOException {
	try(InputStream in = Main.class.getResourceAsStream(fileName)) {		
		var buf = MemoryUtil.memAlloc( in.available() );
		while(in.available() > 0)
			buf.put(in.readNBytes(8192));
		return buf;
	}
}

If you do this then you also have to call MemoryUtil.memFree, so I changed my AIFileIO to track open files by address with a HashMap so I can make sure it frees the memory
So now it looks like this:

protected static AIFileIO makeFileIO() {
        // keep track of file addresses & buffers
	HashMap<Long, ByteBuffer> opened = new HashMap<>();
		
	return AIFileIO.create()
            .OpenProc((pFileIO, fileName, openMode) -> {
                ByteBuffer data;
                String fileNameUtf8 = memUTF8(fileName);
                
                try {
                	data = FileUtils.resourceToByteBuffer(fileNameUtf8);
                	data.position(0); //File read error when reading the next file if you don't set this to 0
                } catch (IOException e) {
                    throw new RuntimeException("Could not open file: " + fileNameUtf8);
                }

                var address = AIFile.create()
                    .ReadProc((pFile, pBuffer, size, count) -> {
                        long max = Math.min(data.remaining() / size, count);
                        
                        memCopy(memAddress(data), pBuffer, max * size);
                        data.position(data.position() + (int) (max * size));
                        return max;
                    })
                    .SeekProc((pFile, offset, origin) -> {
                        if (origin == Assimp.aiOrigin_CUR) {
                            data.position(data.position() + (int) offset);
                        } else if (origin == Assimp.aiOrigin_SET) {
                            data.position((int) offset);
                        } else if (origin == Assimp.aiOrigin_END) {
                            data.position(data.limit() + (int) offset);
                        }
                        return 0;
                    })
                    .FileSizeProc(pFile -> data.limit())
                    .address();
                opened.put(address, data);
                return address;
            })
            .CloseProc((pFileIO, pFile) -> {
                AIFile aiFile = AIFile.create(pFile);
                // find and free buffer
                var buf = opened.get(pFile);
                if(buf != null)
                	MemoryUtil.memFree(buf);
                else
                	System.err.println("Buffer not closed!");

                aiFile.ReadProc().free();
                aiFile.SeekProc().free();
                aiFile.FileSizeProc().free();
            });
}

If there's a better way to handle the buffers/AIFileIO I'm curious to know, but I'm happy I got it working at least.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants