Clojure中使用lwjgl-assimp读取3D模型失败问题排查
问题:Clojure调用LWJGL Assimp时出现负容量错误,Java代码正常运行
在使用Clojure调用LWJGL的Assimp库读取3D模型时,调用AIMesh/create方法触发IllegalArgumentException,提示capacity < 0,但逻辑完全一致的Java代码可以正常运行。
Clojure代码
(ns learnopengl.mesh-model (:import [org.lwjgl.assimp Assimp AINode AIMesh])) (defn read-model "从文件读取3D模型" [path] (let [scene (Assimp/aiImportFile path (bit-or Assimp/aiProcess_Triangulate Assimp/aiProcess_FlipUVs))] (if (= scene nil) (println (Assimp/aiGetErrorString)) (for [index (range (.mNumMeshes scene))] (do (println index) (AIMesh/create (.get (.mMeshes scene) index)))))))
运行错误信息
0 Execution error (IllegalArgumentException) at java.nio.Buffer/createCapacityException (Buffer.java:290). capacity < 0: (-576307456 < 0)
Java对比代码
import org.lwjgl.assimp.Assimp; import org.lwjgl.assimp.AIScene; import org.lwjgl.assimp.AIMesh; class Test { public static void main(String[] args) { AIScene scene = Assimp.aiImportFile("backpack/backpack.obj", Assimp.aiProcess_Triangulate); if (scene == null) { System.out.println(Assimp.aiGetErrorString()); } else { for (int i = 0; i < scene.mNumMeshes(); i++) { System.out.println(i); AIMesh mesh = AIMesh.create(scene.mMeshes().get(i)); } } } }
补充信息
1. project.clj配置
(defproject learnopengl "0.1.0-SNAPSHOT" :description "learnopengl book code" :url "learnopengl.com" :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0" :url "https://www.eclipse.org/legal/epl-2.0/"} :dependencies [[org.clojure/clojure "1.11.1"] [org.lwjgl/lwjgl "3.3.6"] [org.lwjgl/lwjgl "3.3.6" :classifier "natives-linux"] [org.lwjgl/lwjgl-opengl "3.3.6"] [org.lwjgl/lwjgl-opengl "3.3.6" :classifier "natives-linux"] [org.lwjgl/lwjgl-stb "3.3.6"] [org.lwjgl/lwjgl-stb "3.3.6" :classifier "natives-linux"] [org.lwjgl/lwjgl-glfw "3.3.6"] [org.lwjgl/lwjgl-glfw "3.3.6" :classifier "natives-linux"] [org.lwjgl/lwjgl-assimp "3.3.6"] [org.lwjgl/lwjgl-assimp "3.3.6" :classifier "natives-linux"] [org.joml/joml "1.10.8"]] :main ^:skip-aot learnopengl.core :target-path "target/%s" :profiles {:uberjar {:aot :all :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})
2. 堆栈跟踪信息
{:clojure.main/message "Execution error (IllegalArgumentException) at java.nio.Buffer/createCapacityException (Buffer.java:290).\ncapacity < 0: (-689151616 < 0)\n", :clojure.main/triage {:clojure.error/class java.lang.IllegalArgumentException, :clojure.error/line 290, :clojure.error/cause "capacity < 0: (-689151616 < 0)", :clojure.error/symbol java.nio.Buffer/createCapacityException, :clojure.error/source "Buffer.java", :clojure.error/phase :execution}, :clojure.main/trace {:via [{:type clojure.lang.Compiler$CompilerException, :message "Syntax error macroexpanding at (learnopengl/core.clj:12:1).", :data {:clojure.error/phase :execution, :clojure.error/line 12, :clojure.error/column 1, :clojure.error/source "learnopengl/core.clj"}, :at [clojure.lang.Compiler load "Compiler.java" 7665]} {:type java.lang.IllegalArgumentException, :message "capacity < 0: (-689151616 < 0)", :at [java.nio.Buffer createCapacityException "Buffer.java" 290]}], :trace [[java.nio.Buffer createCapacityException "Buffer.java" 290] [java.nio.Buffer <init> "Buffer.java" 253] [java.nio.ByteBuffer <init> "ByteBuffer.java" 316] [java.nio.ByteBuffer <init> "ByteBuffer.java" 324] [java.nio.MappedByteBuffer <init> "MappedByteBuffer.java" 113] [java.nio.DirectByteBuffer <init> "DirectByteBuffer.java" 107] [java.nio.ByteBuffer allocateDirect "ByteBuffer.java" 360] [org.lwjgl.system.Struct __create "Struct.java" 118] [org.lwjgl.assimp.AIMesh create "AIMesh.java" 487] [jdk.internal.reflect.DirectMethodHandleAccessor invoke "DirectMethodHandleAccessor.java" 103] [java.lang.reflect.Method invoke "Method.java" 580] [clojure.lang.Reflector invokeMatchingMethod "Reflector.java" 167] [clojure.lang.Reflector invokeStaticMethod "Reflector.java" 332] [learnopengl.mesh_model$read_model$iter__332__336$fn__337$fn__338 invoke "mesh_model.clj" 66] [learnopengl.mesh_model$read_model$iter__332__336$fn__337 invoke "mesh_model.clj" 63] [clojure.lang.LazySeq sval "LazySeq.java" 42] [clojure.lang.LazySeq seq "LazySeq.java" 51] [clojure.lang.RT seq "RT.java" 535] [clojure.lang.RT countFrom "RT.java" 650] [clojure.lang.RT count "RT.java" 643] [learnopengl.core$eval351 invokeStatic "core.clj" 12] [learnopengl.core$eval351 invoke "core.clj" 12] [clojure.lang.Compiler eval "Compiler.java" 7194] [clojure.lang.Compiler load "Compiler.java" 7653] [clojure.lang.RT loadResourceScript "RT.java" 381] [clojure.lang.RT loadResourceScript "RT.java" 372] [clojure.lang.RT load "RT.java" 459] [clojure.lang.RT load "RT.java" 424] [clojure.core$load$fn__6908 invoke "core.clj" 6161] [clojure.core$load invokeStatic "core.clj" 6160] [clojure.core$load doInvoke "core.clj" 6144] [clojure.lang.RestFn invoke "RestFn.java" 408] [clojure.core$load_one invokeStatic "core.clj" 5933] [clojure.core$load_one invoke "core.clj" 5928] [clojure.core$load_lib$fn__6850 invoke "core.clj" 5975] [clojure.core$load_lib invokeStatic "core.clj" 5974] [clojure.core$load_lib doInvoke "core.clj" 5953] [clojure.lang.RestFn applyTo "RestFn.java" 142] [clojure.core$apply invokeStatic "core.clj" 669] [clojure.core$load_libs invokeStatic "core.clj" 6016] [clojure.core$load_libs doInvoke "core.clj" 6000] [clojure.lang.RestFn applyTo "RestFn.java" 137] [clojure.core$apply invokeStatic "core.clj" 669] [clojure.core$require invokeStatic "core.clj" 6038] [clojure.core$require doInvoke "core.clj" 6038] [clojure.lang.RestFn invoke "RestFn.java" 408] [user$eval140$fn__144 invoke "form-init15974239925031016013.clj" 1] [user$eval140 invokeStatic "form-init15974239925031016013.clj" 1] [user$eval140 invoke "form-init15974239925031016013.clj" 1] [clojure.lang.Compiler eval "Compiler.java" 7194] [clojure.lang.Compiler eval "Compiler.java" 7184] [clojure.lang.Compiler load "Compiler.java" 7653] [clojure.lang.Compiler loadFile "Compiler.java" 7591] [clojure.main$load_script invokeStatic "main.clj" 475] [clojure.main$init_opt invokeStatic "main.clj" 477] [clojure.main$init_opt invoke "main.clj" 477] [clojure.main$initialize invokeStatic "main.clj" 508] [clojure.main$null_opt invokeStatic "main.clj" 542] [clojure.main$null_opt invoke "main.clj" 539] [clojure.main$main invokeStatic "main.clj" 664] [clojure.main$main doInvoke "main.clj" 616] [clojure.lang.RestFn applyTo "RestFn.java" 137] [clojure.lang.Var applyTo "Var.java" 705] [clojure.main main "main.java" 40]], :cause "capacity < 0: (-689151616 < 0)", :phase :execution}}
问题原因与解决方案
原因
Clojure的反射机制在处理LWJGL的指针类型时出现类型转换错误:
scene.mMeshes().get(i)返回的是指针地址(Long类型),但Clojure反射调用AIMesh.create时,未正确识别该参数的类型,将其当作普通整数处理,导致在创建ByteBuffer时计算出负数容量,触发异常。- Java代码依赖静态类型检查,参数会被正确识别为
long类型的指针,因此不会出现该问题。 - 堆栈跟踪显示错误发生在
count操作时:for生成的惰性序列被强制求值(count会遍历整个序列),此时若Assimp资源未被正确持有,也可能加剧问题,但核心原因是类型不匹配。
解决方案
方案1:添加类型提示(最直接有效)
给AIMesh/create的参数添加^long类型提示,告诉Clojure编译器参数的正确类型,避免反射时的类型错误:
(AIMesh/create ^long (.get (.mMeshes scene) index))
方案2:避免惰性序列的不当求值
若代码中存在对read-model返回序列的count操作,可改为使用doseq直接遍历处理(而非返回惰性序列),确保所有操作在scene资源有效期间完成:
(defn read-model "从文件读取3D模型" [path] (let [scene (Assimp/aiImportFile path (bit-or Assimp/aiProcess_Triangulate Assimp/aiProcess_FlipUVs))] (if (= scene nil) (println (Assimp/aiGetErrorString)) (doseq [index (range (.mNumMeshes scene))] (println index) (AIMesh/create ^long (.get (.mMeshes scene) index))))))
内容的提问来源于stack exchange,提问作者user1785730
相关产品推荐
相关产品推荐

