想使用java3d显示3d模型,就在网上搜索一番,发现有个作者写的已经实现了,如下:

实验后发现显示的模型没有纹理贴图。又以java3d显示纹理贴图搜索到一篇博客Java3D导入obj和3ds模型整理,使用后还是没有显示出纹理,以为是从网上下载的obj模型文件的问题,就安装了3dmax2019后自己手动把max模型导出成obj格式。

复制文件时偶然间把路径写成了"F:\\tmp\\obj\\taibainv\\aaa.obj"而非"F:/tmp/obj/taibainv/aaa.obj",发现竟然成功显示模型贴图了!!又试了下"F:\\tmp\\obj\\taibainv/aaa.obj"却不能正常显示。跟踪两种情况的代码执行,发现原因就在于com.sun.j3d.loaders.objectfile.ObjectFile的basePath属性,而该属性是由com.sun.j3d.loaders.objectfile.ObjectFile.setBasePathFromFilename赋值的,idea反编译该方法如下:

    private void setBasePathFromFilename(String var1) {
        if(var1.lastIndexOf(File.separator) == -1) {
            this.setBasePath("." + File.separator);
        } else {
            this.setBasePath(var1.substring(0, var1.lastIndexOf(File.separator)));
        }
    }

原来是File.separator的问题。统一把路径中的/换成File.separator就好了。

尽管"F:/tmp/obj/taibainv/aaa.obj"在windows系统下能访问到文件,但是在这里竟然出了坑。 好,问题解决!

附上文件(共两个java文件,一个模型文件):

  1. JavaModelObjLoaderSimpleWithKeyControl.java
  2. ObjFileReader.java
  3. 模型文件:https://gitee.com/valuetodays/ellen/tree/master/blog-attachment/eblog/20181208

import com.sun.j3d.utils.universe.SimpleUniverse;

import javax.media.j3d.*;
import javax.swing.*;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3f;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.util.Enumeration;

/**
 * 显示并控制单个模型
 * LEFT/RIGHT 旋转模型
 * HOME/END   缩放模型
 *
 */
public class JavaModelObjLoaderSimpleWithKeyControl extends JFrame {
    private static final long serialVersionUID = 207092618323821865L;

    public class MyBehavior extends Behavior {
        private TransformGroup transformGroup;
        private Transform3D rotation = new Transform3D();
        private double angleY = 0.0;
        private double angleX = 0.0;
        private double scale = 1.0;

        public MyBehavior(TransformGroup transformGroup) {
            this.transformGroup = transformGroup;
        }

        @Override
        public void initialize() {
            this.wakeupOn(new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED));
        }

        @Override
        public void processStimulus(Enumeration enumeration) {
            AWTEvent[] event = null;
            WakeupCriterion wakeupCriterion = (WakeupCriterion) enumeration.nextElement();
            if (wakeupCriterion instanceof WakeupOnAWTEvent) {
                event = ((WakeupOnAWTEvent) wakeupCriterion).getAWTEvent();
                KeyEvent keyEvent = (KeyEvent) event[0];
                if (keyEvent.getKeyCode() == KeyEvent.VK_LEFT) {
                    angleY -= 0.1;
                } else if (keyEvent.getKeyCode() == KeyEvent.VK_RIGHT) {
                    angleY += 0.1;
                } else if (keyEvent.getKeyCode() == KeyEvent.VK_HOME) {
                    scale = rotation.getScale()*0.9d;
                } else if (keyEvent.getKeyCode() == KeyEvent.VK_END) {
                    scale = rotation.getScale()/0.9d;
                }

                rotation.rotY(angleY);
                rotation.setScale(scale);
                transformGroup.setTransform(rotation);
            }

            this.wakeupOn(new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED));
        }
    }

    public BranchGroup createSceneGraph() {
        // 创建场景图分支
        BranchGroup group = new BranchGroup();
        // 几何变换组节点
        TransformGroup transGroup = new TransformGroup();
        // 几何变换
        Transform3D trans3d = new Transform3D();
        // 缩放变换
        trans3d.setScale(0.8);
        //将几何变换节点对象添加到节点组
        transGroup.setTransform(trans3d);
        //将几何变化组添加到场景
        group.addChild(transGroup);

        // 球体作用范围边界对象
        BoundingSphere bound = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);
        Color3f bgColor = new Color3f(0.05f, 0.05f, 0.2f);
        Background bg = new Background(bgColor);
        bg.setApplicationBounds(bound);
        group.addChild(bg);


        // 设置光源
        Color3f lightColor = new Color3f(1.0f, 1.0f, 0.9f);
        Vector3f lightDirection = new Vector3f(4.0f, -7.0f, -12.0f);
        //设置定向光的颜色和影响范围
        DirectionalLight light = new DirectionalLight(lightColor, lightDirection);
        light.setInfluencingBounds(bound);
        //将光源添加到场景
        group.addChild(light);

        //几何变换组节点 - 加载外部模型
        TransformGroup objTrans = new TransformGroup();
        objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        //加载Obj格式的模型文件 如下路径使用一个风格,要么全使用\\,要么使用/
//        String resourcePath = "F:\\tmp\\obj\\taibainv\\aaa.obj"; // 显示纹理
        String resourcePath = "F:/tmp/obj/taibainv/aaa.obj"; // 不显示纹理
        objTrans.addChild(new ObjFileReader(resourcePath));
        //将模型添加到变换组节点
        transGroup.addChild(objTrans);

        MyBehavior myBehavior = new MyBehavior(objTrans);
        myBehavior.setSchedulingBounds(new BoundingSphere());
        group.addChild(myBehavior);

        group.compile();

        return group;
    }

    public JavaModelObjLoaderSimpleWithKeyControl() {
        // 创建3D场景绘制画布Canvas3D对象
        Canvas3D canvas = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
        BranchGroup scene = createSceneGraph();
        SimpleUniverse universe = new SimpleUniverse(canvas);
        universe.getViewingPlatform().setNominalViewingTransform();
        universe.addBranchGraph(scene);

        this.add(canvas);
        this.setVisible(true);
        this.setPreferredSize(new Dimension(300, 500));
        this.setLocation(100, 50);
        this.pack();
    }

    public static void main(String[] args) {
        new JavaModelObjLoaderSimpleWithKeyControl();
    }
}



import com.sun.j3d.loaders.Scene;
import com.sun.j3d.loaders.objectfile.ObjectFile;

import javax.media.j3d.BranchGroup;

public class ObjFileReader extends BranchGroup {

    private double creaseAngle = 60.0;

    /**
     *
     * 读取ObjModel文件
     *
     * @param filePath obj文件路径
     */
    public ObjFileReader(String filePath) {
        BranchGroup branchGroup = new BranchGroup();
        int flags = ObjectFile.RESIZE;
        ObjectFile objFile = new ObjectFile(flags, (float)(creaseAngle*Math.PI)/180);
        Scene scenen = null;
        try {
            scenen = objFile.load(filePath);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("OBJ模型加载失败" + e.getMessage());
        }
        branchGroup.addChild(scenen.getSceneGroup());
        this.addChild(branchGroup);
    }
}