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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 17:39:51