【问题标题】:Loading Wavefont .obj files from relative path error从相对路径错误加载 Wavefont .obj 文件
【发布时间】:2014-05-22 19:34:36
【问题描述】:

首先,感谢您花时间阅读本文。

我正在看书Developing Games in Java。第 8 章展示了如何加载 Wavefront .obj 文件。 我的 obj 文件位于 C://pathToMyWorkspace//ProjectName//res//coffeCup.obj

当我尝试加载 Wavefront .obj 文件时,编译器会抛出错误:

java.io.FileNotFoundException: res\coffe
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:146)
at java.io.FileReader.<init>(FileReader.java:72)
at com.base.graphics.graphics3D.ObjectLoader.parseFile(ObjectLoader.java:141)
at com.base.graphics.graphics3D.ObjectLoader$ObjLineParser.parseLine(ObjectLoader.java:228)
at com.base.graphics.graphics3D.ObjectLoader.parseFile(ObjectLoader.java:169)
at com.base.graphics.graphics3D.ObjectLoader.loadObject(ObjectLoader.java:116)
at com.testGame.Texture3DTest.createPolygons(Texture3DTest.java:69)
at com.base.graphics.GameCore3D.init(GameCore3D.java:24)
at com.testGame.Texture3DTest.init(Texture3DTest.java:45)
at com.base.graphics.GameCore.start(GameCore.java:49)
at com.testGame.Texture3DTest.main(Texture3DTest.java:41)

由于某种原因,编译器试图从 "res/res/coffCup.obj" 加载,但我指定的路径只是 "res/coffeCup.obj" >.

这是新的 ObjectLoader 类(加载 .obj 文件):

package com.base.graphics.graphics3D;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;

import com.base.math.PolygonGroup;
import com.base.math.ShadedSurface;
import com.base.math.ShadedTexture;
import com.base.math.TexturedPolygon3D;
import com.base.math.Vector3D;

/**
 * The ObjectLoader class loads a subset of the Alias|Wavefront OBJ file
 * specification.
 * 
 * Lines that begin with '#' are comments.
 * 
 * OBJ file keywords:
 * 
 * <pre>
 *     mtllib [filename]    - Load materials from an external .mtl
 *                            file.
 *     v [x] [y] [z]        - Define a vertex with floating-point
 *                            coords (x,y,z).
 *     f [v1] [v2] [v3] ... - Define a new face. a face is a flat,
 *                            convex polygon with vertices in
 *                            counter-clockwise order. Positive
 *                            numbers indicate the index of the
 *                            vertex that is defined in the file.
 *                            Negative numbers indicate the vertex
 *                            defined relative to last vertex read.
 *                            For example, 1 indicates the first
 *                            vertex in the file, -1 means the last
 *                            vertex read, and -2 is the vertex
 *                            before that.
 *     g [name]             - Define a new group by name. The faces
 *                            following are added to this group.
 *     usemtl [name]        - Use the named material (loaded from a
 *                            .mtl file) for the faces in this group.
 * </pre>
 * 
 * MTL file keywords:
 * 
 * <pre>
 *     newmtl [name]        - Define a new material by name.
 *     map_Kd [filename]    - Give the material a texture map.
 * </pre>
 */
public class ObjectLoader {

    /**
     * The Material class wraps a ShadedTexture.
     */
    public static class Material {
        public File sourceFile;
        public ShadedTexture texture;
    }

    /**
     * A LineParser is an interface to parse a line in a text file. Separate
     * LineParsers and are used for OBJ and MTL files.
     */
    protected interface LineParser {
        public void parseLine(String line) throws IOException, NumberFormatException, NoSuchElementException;
    }

    protected File path;
    protected List vertices;
    protected Material currentMaterial;
    protected HashMap materials;
    protected List lights;
    protected float ambientLightIntensity;
    protected HashMap parsers;
    private PolygonGroup object;
    private PolygonGroup currentGroup;

    /**
     * Creates a new ObjectLoader.
     */
    public ObjectLoader() {
        materials = new HashMap();
        vertices = new ArrayList();
        parsers = new HashMap();
        parsers.put("obj", new ObjLineParser());
        parsers.put("mtl", new MtlLineParser());
        currentMaterial = null;
        setLights(new ArrayList(), 1);
    }

    /**
     * Sets the lights used for the polygons in the parsed objects. After
     * calling this method calls to loadObject use these lights.
     */
    public void setLights(List lights, float ambientLightIntensity) {
        this.lights = lights;
        this.ambientLightIntensity = ambientLightIntensity;
    }

    /**
     * Loads an OBJ file as a PolygonGroup.
     */
    public PolygonGroup loadObject(String parent, String filename) throws IOException {
        object = new PolygonGroup();
        object.setFilename(filename);
        path = new File(parent);

        vertices.clear();
        currentGroup = object;
        parseFile(filename);

        return object;
    }

    /**
     * Gets a Vector3D from the list of vectors in the file. Negative indeces
     * count from the end of the list, postive indeces count from the beginning.
     * 1 is the first index, -1 is the last. 0 is invalid and throws an
     * exception.
     */
    protected Vector3D getVector(String indexStr) {
        int index = Integer.parseInt(indexStr);
        if (index < 0) {
            index = vertices.size() + index + 1;
        }
        return (Vector3D) vertices.get(index - 1);
    }

    /**
     * Parses an OBJ (ends with ".obj") or MTL file (ends with ".mtl").
     */
    protected void parseFile(String filename) throws IOException {
        // get the file relative to the source path
        File file = new File(path, filename);
        BufferedReader reader = new BufferedReader(new FileReader(file));

        // get the parser based on the file extention
        LineParser parser = null;
        int extIndex = filename.lastIndexOf('.');
        if (extIndex != -1) {
            String ext = filename.substring(extIndex + 1);
            parser = (LineParser) parsers.get(ext.toLowerCase());
        }
        if (parser == null) {
            parser = (LineParser) parsers.get("obj");
        }

        // parse every line in the file
        while (true) {
            String line = reader.readLine();
            // no more lines to read
            if (line == null) {
                reader.close();
                return;
            }

            line = line.trim();

            // ignore blank lines and comments
            if (line.length() > 0 && !line.startsWith("#")) {
                // interpret the line
                try {
                    parser.parseLine(line);
                } catch (NumberFormatException ex) {
                    throw new IOException(ex.getMessage());
                } catch (NoSuchElementException ex) {
                    throw new IOException(ex.getMessage());
                }
            }

        }
    }

    /**
     * Parses a line in an OBJ file.
     */
    protected class ObjLineParser implements LineParser {

        public void parseLine(String line) throws IOException, NumberFormatException, NoSuchElementException {
            StringTokenizer tokenizer = new StringTokenizer(line);
            String command = tokenizer.nextToken();
            if (command.equals("v")) {
                // create a new vertex
                vertices.add(new Vector3D(Float.parseFloat(tokenizer.nextToken()), Float.parseFloat(tokenizer.nextToken()), Float.parseFloat(tokenizer.nextToken())));
            } else if (command.equals("f")) {
                // create a new face (flat, convex polygon)
                List currVertices = new ArrayList();
                while (tokenizer.hasMoreTokens()) {
                    String indexStr = tokenizer.nextToken();

                    // ignore texture and normal coords
                    int endIndex = indexStr.indexOf('/');
                    if (endIndex != -1) {
                        indexStr = indexStr.substring(0, endIndex);
                    }

                    currVertices.add(getVector(indexStr));
                }

                // create textured polygon
                Vector3D[] array = new Vector3D[currVertices.size()];
                currVertices.toArray(array);
                TexturedPolygon3D poly = new TexturedPolygon3D(array);

                // set the texture
                ShadedSurface.createShadedSurface(poly, currentMaterial.texture, lights, ambientLightIntensity);

                // add the polygon to the current group
                currentGroup.addPolygon(poly);
            } else if (command.equals("g")) {
                // define the current group
                if (tokenizer.hasMoreTokens()) {
                    String name = tokenizer.nextToken();
                    currentGroup = new PolygonGroup(name);
                } else {
                    currentGroup = new PolygonGroup();
                }
                object.addPolygonGroup(currentGroup);
            } else if (command.equals("mtllib")) {
                // load materials from file
                String name = tokenizer.nextToken();
                parseFile(name);
            } else if (command.equals("usemtl")) {
                // define the current material
                String name = tokenizer.nextToken();
                currentMaterial = (Material) materials.get(name);
                if (currentMaterial == null) {
                    System.out.println("no material: " + name);
                }
            } else {
                // unknown command - ignore it
            }

        }
    }

    /**
     * Parses a line in a material MTL file.
     */
    protected class MtlLineParser implements LineParser {

        public void parseLine(String line) throws NoSuchElementException {
            StringTokenizer tokenizer = new StringTokenizer(line);
            String command = tokenizer.nextToken();

            if (command.equals("newmtl")) {
                // create a new material if needed
                String name = tokenizer.nextToken();
                currentMaterial = (Material) materials.get(name);
                if (currentMaterial == null) {
                    currentMaterial = new Material();
                    materials.put(name, currentMaterial);
                }
            } else if (command.equals("map_Kd")) {
                // give the current material a texture
                String name = tokenizer.nextToken();
                File file = new File(path, name);
                if (!file.equals(currentMaterial.sourceFile)) {
                    currentMaterial.sourceFile = file;
                    currentMaterial.texture = (ShadedTexture) Texture.createTexture(file.getPath(), true);
                }
            } else {
                // unknown command - ignore it
            }
        }
    }
}

新的 Texture3DTest(我加载文件的地方):

package com.testGame;

import java.awt.event.KeyEvent;
import java.io.IOException;

import com.base.graphics.GameCore3D;
import com.base.graphics.graphics3D.ObjectLoader;
import com.base.graphics.graphics3D.Rectangle3D;
import com.base.graphics.graphics3D.Texture;
import com.base.graphics.graphics3D.ZBufferedRenderer;
import com.base.input.GameAction;
import com.base.input.InputManager;
import com.base.input.Mouse;
import com.base.math.PolygonGroup;
import com.base.math.TexturedPolygon3D;
import com.base.math.Transform3D;
import com.base.math.Vector3D;
import com.base.math.ViewWindow;

public class Texture3DTest extends GameCore3D {

    protected InputManager inputManager;
    protected GameAction exit = new GameAction("exit", GameAction.DETECT_INITAL_PRESS_ONLY);

    protected GameAction moveForward = new GameAction("moveForward");
    protected GameAction moveBackward = new GameAction("moveBackward");
    protected GameAction moveUp = new GameAction("moveUp");
    protected GameAction moveDown = new GameAction("moveDown");
    protected GameAction moveLeft = new GameAction("moveLeft");
    protected GameAction moveRight = new GameAction("moveRight");

    protected GameAction rootUp = new GameAction("rootUp");
    protected GameAction rootDown = new GameAction("rootDown");
    protected GameAction rootLeft = new GameAction("rootLeft");
    protected GameAction rootRight = new GameAction("rootRight");

    protected final int SPEED = 6;
    protected final float ROOTATION_SPEED = 0.01f;

    public static void main(String[] args) {
        new Texture3DTest().start();
    }

    public void init() {
        super.init();
        Mouse.hide(frame);

        inputManager = new InputManager(frame);

        inputManager.mapToKey(exit, KeyEvent.VK_ESCAPE);

        inputManager.mapToKey(moveForward, KeyEvent.VK_W);
        inputManager.mapToKey(moveBackward, KeyEvent.VK_S);
        inputManager.mapToKey(moveLeft, KeyEvent.VK_A);
        inputManager.mapToKey(moveRight, KeyEvent.VK_D);
        inputManager.mapToKey(moveUp, KeyEvent.VK_SPACE);
        inputManager.mapToKey(moveDown, KeyEvent.VK_SHIFT);

        inputManager.mapToKey(rootUp, KeyEvent.VK_UP);
        inputManager.mapToKey(rootDown, KeyEvent.VK_DOWN);
        inputManager.mapToKey(rootLeft, KeyEvent.VK_LEFT);
        inputManager.mapToKey(rootRight, KeyEvent.VK_RIGHT);
    }

    public void createPolygons() {
        ObjectLoader objLoader = new ObjectLoader();

        try {
            PolygonGroup object = objLoader.loadObject("res/", "coffeCup.obj");
            polygons.add(object);
        } catch (IOException e) { 
            e.printStackTrace();
        }
    }

    public void setTexture(TexturedPolygon3D poly, Texture texture) {
        Vector3D origin = poly.getVertex(0);

        Vector3D dv = new Vector3D(poly.getVertex(1));
        dv.subtract(origin);

        Vector3D du = new Vector3D();
        du.setToCrossProduct(poly.getNormal(), dv);

        Rectangle3D textureBounds = new Rectangle3D(origin, du, dv, texture.getWidth(), texture.getHeight());

        poly.setTexture(texture, textureBounds);
    }

    public void update() {
        if (exit.isPressed())
            System.exit(0);

        Transform3D camera = polygonRenderer.getCamera();
        Vector3D cameraLoc = polygonRenderer.getCamera().getLocation();

        if (moveForward.isPressed()) {
            cameraLoc.x -= SPEED * camera.getSinAngleY();
            cameraLoc.z -= SPEED * camera.getCosAngleY();
        }

        if (moveBackward.isPressed()) {
            cameraLoc.x += SPEED * camera.getSinAngleY();
            cameraLoc.z += SPEED * camera.getCosAngleY();
        }

        if (moveLeft.isPressed()) {
            cameraLoc.x -= SPEED * camera.getCosAngleY();
            cameraLoc.z += SPEED * camera.getSinAngleY();
        }

        if (moveRight.isPressed()) {
            cameraLoc.x += SPEED * camera.getCosAngleY();
            cameraLoc.z -= SPEED * camera.getSinAngleY();
        }

        if (moveUp.isPressed()) {
            camera.getLocation().y += SPEED;
        }

        if (moveDown.isPressed()) {
            camera.getLocation().y -= SPEED;
        }

        if (rootUp.isPressed())
            camera.rotateAngleX(ROOTATION_SPEED);

        if (rootDown.isPressed())
            camera.rotateAngleX(-ROOTATION_SPEED);

        if (rootLeft.isPressed())
            camera.rotateAngleY(ROOTATION_SPEED);

        if (rootRight.isPressed())
            camera.rotateAngleY(-ROOTATION_SPEED);
    }

    public Texture loadTexture(String imageName) {
        return Texture.createTexture(imageName, true);
    }

    public void createPolygonRenderer() {
        viewWindow = new ViewWindow(0, 0, frame.getWidth(), frame.getHeight(), (float) Math.toRadians(75));

        Transform3D camera = new Transform3D(0, 100, 0);
        polygonRenderer = new ZBufferedRenderer(camera, viewWindow);
    }
}

PS:我用的是eclipse

非常感谢

【问题讨论】:

    标签: java file 3d relative-path wavefront


    【解决方案1】:

    (此答案是指问题的previous revision。问题已根据此答案更新。有关详细信息,请参阅 cmets)

    嗯,这里的问题是ObjectLoader类的路径处理是错误的。在这个最小的例子中可以看到相关的调用顺序:

    import java.io.File;
    
    public class FilePathTest
    {
        public static void main(String[] args)
        {
            loadObject("res/SomeFile.txt");
            loadObject("SomeFile.txt");
        }
    
        static File path;
    
        static void loadObject(String filename)
        {
            File file = new File(filename);
            path = file.getParentFile();        
            parseFile(filename);
        }
    
        static void parseFile(String filename)
        {
            File file = new File(path, filename);
            System.out.println("File: "+file+" exists? "+file.exists());
        }
    }
    

    从给定文件中,获取“父文件”(即文件所在目录)并将其存储为path。之后,再次将文件名附加到该路径,以获得最终的文件名。因此,当filename 以相对路径前缀开头(如您的情况下的res/)时,这部分是重复的。

    (顺便说一句:他存储路径的原因是 OBJ 文件可能包含对假定在同一目录中的其他文件的引用,例如 MTL 文件,该文件又可能包含进一步参考纹理文件)。

    我现在可以想象的“最简单”的解决方案是手动处理路径和文件名。基本思路就写到这里了,应该可以把这个转移到原来的ObjectLoader类:

    import java.io.File;
    
    public class FilePathTest
    {
        public static void main(String[] args)
        {
            loadObject("res/", "SomeFile.obj");
        }
    
        static File path;
    
        static void loadObject(String parent, String filename)
        {
            File file = new File(parent+File.separator+filename);
            path = new File(parent);        
            parseFile(file);
        }
    
        static void parseFile(File file)
        {
            System.out.println("File: "+file+" exists? "+file.exists());
    
            String mtlName = "SomeFile.mtl";
            File mtlFile = new File(path, mtlName);
    
            System.out.println("MTL file: "+mtlFile+" exists? "+mtlFile.exists());
        }
    }
    

    编辑:一个 MVCE,通过从导致编译错误的原始代码中删除所有内容而创建

    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileReader;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.NoSuchElementException;
    import java.util.StringTokenizer;
    
    import stackoverflow.objload.ObjectLoader.LineParser;
    import stackoverflow.objload.ObjectLoader.Material;
    import stackoverflow.objload.ObjectLoader.MtlLineParser;
    import stackoverflow.objload.ObjectLoader.ObjLineParser;
    
    public class ObjectLoaderTest
    {
        public static void main(String[] args)
        {
            ObjectLoader objLoader = new ObjectLoader();
    
            try {
                PolygonGroup object = objLoader.loadObject("res/", "coffeCup.obj");
            } catch (IOException e) { 
                e.printStackTrace();
            }    
    
        }
    }
    
    class PolygonGroup
    {
        public PolygonGroup(String name)
        {
        }
    
        public PolygonGroup()
        {
        }
    
        public void setFilename(String filename)
        {
        }
    
        public void addPolygonGroup(PolygonGroup currentGroup)
        {
        }
    }
    class Vector3D
    {
        public Vector3D(float parseFloat, float parseFloat2, float parseFloat3)
        {
        }
    }
    
    class ObjectLoader {
    
        /**
         * The Material class wraps a ShadedTexture.
         */
        public static class Material {
            public File sourceFile;
        }
    
        /**
         * A LineParser is an interface to parse a line in a text file. Separate
         * LineParsers and are used for OBJ and MTL files.
         */
        protected interface LineParser {
            public void parseLine(String line) throws IOException, NumberFormatException, NoSuchElementException;
        }
    
        protected File path;
        protected List vertices;
        protected Material currentMaterial;
        protected HashMap materials;
        protected List lights;
        protected float ambientLightIntensity;
        protected HashMap parsers;
        private PolygonGroup object;
        private PolygonGroup currentGroup;
    
        /**
         * Creates a new ObjectLoader.
         */
        public ObjectLoader() {
            materials = new HashMap();
            vertices = new ArrayList();
            parsers = new HashMap();
            parsers.put("obj", new ObjLineParser());
            parsers.put("mtl", new MtlLineParser());
            currentMaterial = null;
            setLights(new ArrayList(), 1);
        }
    
        /**
         * Sets the lights used for the polygons in the parsed objects. After
         * calling this method calls to loadObject use these lights.
         */
        public void setLights(List lights, float ambientLightIntensity) {
            this.lights = lights;
            this.ambientLightIntensity = ambientLightIntensity;
        }
    
        /**
         * Loads an OBJ file as a PolygonGroup.
         */
        public PolygonGroup loadObject(String parent, String filename) throws IOException {
            object = new PolygonGroup();
            object.setFilename(filename);
            path = new File(parent);
    
            vertices.clear();
            currentGroup = object;
            parseFile(filename);
    
            return object;
        }
    
        /**
         * Gets a Vector3D from the list of vectors in the file. Negative indeces
         * count from the end of the list, postive indeces count from the beginning.
         * 1 is the first index, -1 is the last. 0 is invalid and throws an
         * exception.
         */
        protected Vector3D getVector(String indexStr) {
            int index = Integer.parseInt(indexStr);
            if (index < 0) {
                index = vertices.size() + index + 1;
            }
            return (Vector3D) vertices.get(index - 1);
        }
    
        /**
         * Parses an OBJ (ends with ".obj") or MTL file (ends with ".mtl").
         */
        protected void parseFile(String filename) throws IOException {
            // get the file relative to the source path
            File file = new File(path, filename);
    
            System.out.println("Reading "+file+", exists "+file.exists());
            BufferedReader reader = new BufferedReader(new FileReader(file));
    
            // get the parser based on the file extention
            LineParser parser = null;
            int extIndex = filename.lastIndexOf('.');
            if (extIndex != -1) {
                String ext = filename.substring(extIndex + 1);
                parser = (LineParser) parsers.get(ext.toLowerCase());
            }
            if (parser == null) {
                parser = (LineParser) parsers.get("obj");
            }
    
            // parse every line in the file
            while (true) {
                String line = reader.readLine();
                // no more lines to read
                if (line == null) {
                    reader.close();
                    return;
                }
    
                line = line.trim();
    
                // ignore blank lines and comments
                if (line.length() > 0 && !line.startsWith("#")) {
                    // interpret the line
                    try {
                        parser.parseLine(line);
                    } catch (NumberFormatException ex) {
                        throw new IOException(ex.getMessage());
                    } catch (NoSuchElementException ex) {
                        throw new IOException(ex.getMessage());
                    }
                }
    
            }
        }
    
        /**
         * Parses a line in an OBJ file.
         */
        protected class ObjLineParser implements LineParser {
    
            public void parseLine(String line) throws IOException, NumberFormatException, NoSuchElementException {
                StringTokenizer tokenizer = new StringTokenizer(line);
                String command = tokenizer.nextToken();
                if (command.equals("v")) {
                    // create a new vertex
                    vertices.add(new Vector3D(Float.parseFloat(tokenizer.nextToken()), Float.parseFloat(tokenizer.nextToken()), Float.parseFloat(tokenizer.nextToken())));
                } else if (command.equals("f")) {
                    // create a new face (flat, convex polygon)
                    List currVertices = new ArrayList();
                    while (tokenizer.hasMoreTokens()) {
                        String indexStr = tokenizer.nextToken();
    
                        // ignore texture and normal coords
                        int endIndex = indexStr.indexOf('/');
                        if (endIndex != -1) {
                            indexStr = indexStr.substring(0, endIndex);
                        }
    
                        currVertices.add(getVector(indexStr));
                    }
    
                    // create textured polygon
                    Vector3D[] array = new Vector3D[currVertices.size()];
                    currVertices.toArray(array);
    
                } else if (command.equals("g")) {
                    // define the current group
                    if (tokenizer.hasMoreTokens()) {
                        String name = tokenizer.nextToken();
                        currentGroup = new PolygonGroup(name);
                    } else {
                        currentGroup = new PolygonGroup();
                    }
                    object.addPolygonGroup(currentGroup);
                } else if (command.equals("mtllib")) {
                    // load materials from file
                    String name = tokenizer.nextToken();
                    parseFile(name);
                } else if (command.equals("usemtl")) {
                    // define the current material
                    String name = tokenizer.nextToken();
                    currentMaterial = (Material) materials.get(name);
                    if (currentMaterial == null) {
                        System.out.println("no material: " + name);
                    }
                } else {
                    // unknown command - ignore it
                }
    
            }
        }
    
        /**
         * Parses a line in a material MTL file.
         */
        protected class MtlLineParser implements LineParser {
    
            public void parseLine(String line) throws NoSuchElementException {
                StringTokenizer tokenizer = new StringTokenizer(line);
                String command = tokenizer.nextToken();
    
                if (command.equals("newmtl")) {
                    // create a new material if needed
                    String name = tokenizer.nextToken();
                    currentMaterial = (Material) materials.get(name);
                    if (currentMaterial == null) {
                        currentMaterial = new Material();
                        materials.put(name, currentMaterial);
                    }
                } else if (command.equals("map_Kd")) {
                    // give the current material a texture
                    String name = tokenizer.nextToken();
                    File file = new File(path, name);
                    if (!file.equals(currentMaterial.sourceFile)) {
                        currentMaterial.sourceFile = file;
                    }
                } else {
                    // unknown command - ignore it
                }
            }
        }
    }
    

    【讨论】:

    • 我只是不明白作者是如何加载文件的。他做了类似“../folder/Resource.obj”的事情。
    • @user3666392 很有趣。我无法想象这应该如何与给定的代码一起工作。我认为我提取的测试代码应该完全显示原始ObjectLoader 的行为,并且肯定错误地添加了路径前缀。也许其他人有其他想法并可以对此发表评论......?
    • 您不应该在评论中添加此信息,而是正确格式化为您的问题的“编辑”,可能与堆栈跟踪一起,最好剥离到一个最小的测试示例,例如我已经发布。当您调用 FilePathTest 时,它会打印什么内容?它与您当前对 ObjectLoader 的修改有何不同
    • 抱歉,花了太多时间来回答。好吧,FilePathTest 的“第二个版本”为第一个文件(.obj 文件,因为该文件确实存在)打印 true。第二个打印 false (.mtl 文件,因为它不存在)。 PS:我将问题中的代码编辑到新的 ObjectLoader、新的 Texture3DTest 和新的错误中。感谢您的帮助,我真的很感激!
    • 您没有提到您当前的加载代码与测试用例的不同之处和差异。无论如何,我必须要求澄清:异常是否仍然相同?找不到的文件还是一样吗? (或者 OBJ 文件是否引用了 MTL 文件,是否是 this 现在导致了问题?)。这并不完全清楚,因为文件名似乎不再正确(res/coffeCup.objres\coffe 例外 - 扩展名可能被剥离,但不是 Cup 部分......)跨度>
    猜你喜欢
    • 2016-04-23
    • 2023-04-01
    • 1970-01-01
    • 2021-10-03
    • 2012-02-27
    • 2014-10-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多