从相对路径错误加载Wavefont .obj文件

时间:2014-05-22 19:34:37

标签: java file 3d relative-path wavefront

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

我正在阅读这本书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)

由于某种原因,编译器尝试从&#34; res / res / coffeCup.obj&#34; 加载,但我指定的路径只是&#34; 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

非常感谢

1 个答案:

答案 0 :(得分:1)

  

(此答案涉及问题的previous revision。问题已根据此答案进行了更新。请参阅评论以获取详细信息)

嗯,这里的问题是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());
    }
}

从给定文件中,它获取&#34;父文件&#34; (即包含文件的目录)并将其存储为path。稍后,文件名再次附加到此路径,以获取最终文件名。因此,当filename以相对路径前缀(例如您的res/)开头时,此部分将重复。

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

最简单的&#34;我现在可以想象的解决方案是手动处理路径和文件名。这里概述了基本思想,应该可以将其转移到原来的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
            }
        }
    }
}