gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-01 12:56:50 27 4
gpt4 key购买 nike

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

我正在看书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/coffeCup.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

非常感谢

最佳答案

(This answer refers to a previous revision of the question. The question has been updated based on this answer. See the comments for details)

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

从给定的文件中,它获取“父文件”(即包含该文件的目录)并将其存储为路径。之后再将文件名附加到该路径中,即可得到最终的文件名。因此,当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
}
}
}
}

关于java - 从相对路径错误加载 Wavefont .obj 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23815676/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com