OpenGL 3D Game Tutorial 10: Loading 3D Models

OpenGL 3D Game Tutorial 10: Loading 3D Models

ThinMatrix

10 лет назад

205,260 Просмотров

Ссылки и html тэги не поддерживаются


Комментарии:

@angelahornung8488
@angelahornung8488 - 05.08.2019 03:58

So everything was working fine in the prior tutorial, and if I put the vertices and stuff in manually all works well, but for some reason the stall model is not loading what so ever. I'm not getting any errors, it just isn't there. I was wondering if anyone could help me with this? I'm willing to email the code to see if maybe I screwed something up.

Ответить
@jagathnath7973
@jagathnath7973 - 15.09.2019 19:03

Will you please help me solve this error:
Java.lang.NumberFormatException : empty string
I really need some help here.
Thank you

Ответить
@tenshidev3658
@tenshidev3658 - 16.11.2019 18:08

HERE IS THE CLASS DONT WRITE IT BY YOURSELF TOO MUCH WORK:


public static Mesh loadObj(String filename, MeshLoader loader) {
FileReader fr = null;

try {
fr = new FileReader(new File("rsc/models/"+filename+".obj"));
} catch(FileNotFoundException e) {
e.printStackTrace();
}

BufferedReader reader = new BufferedReader(fr);
String line;

List<Vector3f> vertices = new ArrayList<>();
List<Vector3f> normals = new ArrayList<>();
List<Vector2f> textures = new ArrayList<>();
List<Integer> indices = new ArrayList<>();

float[] verticesArray = null;
float[] normalsArray = null;
float[] texturesArray = null;
int[] indicesArray = null;

try {
while(true) {
line = reader.readLine();
String[] currentLine = line.split(" ");

if(line.startsWith("v ")) {
Vector3f vertex = new Vector3f(Float.parseFloat(currentLine[1]), Float.parseFloat(currentLine[2]), Float.parseFloat(currentLine[3]));
vertices.add(vertex);
} else if(line.startsWith("vt ")) {
Vector2f texture = new Vector2f(Float.parseFloat(currentLine[1]), Float.parseFloat(currentLine[2]));
textures.add(texture);
} else if(line.startsWith("vn ")) {
Vector3f normal = new Vector3f(Float.parseFloat(currentLine[1]), Float.parseFloat(currentLine[2]), Float.parseFloat(currentLine[3]));
normals.add(normal);
} else if(line.startsWith("f ")) {
texturesArray = new float[vertices.size()*2];
normalsArray = new float[normals.size()*3];
break;
}
}

while(line!=null) {
if(!line.startsWith("f ")) {
line = reader.readLine();
continue;
}

String[] currentLine = line.split(" ");
String[] vertex1 = currentLine[1].split("/");
String[] vertex2 = currentLine[2].split("/");
String[] vertex3 = currentLine[3].split("/");

processVertex(vertex1, indices, textures, normals, texturesArray, normalsArray);
processVertex(vertex2, indices, textures, normals, texturesArray, normalsArray);
processVertex(vertex3, indices, textures, normals, texturesArray, normalsArray);

line = reader.readLine();
}

reader.close();
} catch(Exception e) {
e.printStackTrace();
}

verticesArray = new float[vertices.size()*3];
indicesArray = new int[indices.size()];

int vertexPointer = 0;
for(Vector3f vertex: vertices) {
verticesArray[vertexPointer++] = vertex.x;
verticesArray[vertexPointer++] = vertex.y;
verticesArray[vertexPointer++] = vertex.z;
}

for(int i = 0; i < indices.size(); i++) {
indicesArray[i] = indices.get(i);
}

return loader.loadToVao(verticesArray, texturesArray, indicesArray);
}

private static void processVertex(String[] vertexData, List<Integer> indices, List<Vector2f> textures, List<Vector3f> normals, float[] textureArray, float[] normalsArray) {
int currentVertexPointer = Integer.parseInt(vertexData[0]);
indices.add(currentVertexPointer);

Vector2f currentTex = textures.get(Integer.parseInt(vertexData[1])-1);
textureArray[currentVertexPointer*2] = currentTex.x;
textureArray[currentVertexPointer*2+1] = 1 - currentTex.y;

Vector3f currentNorm = normals.get(Integer.parseInt(vertexData[2]) -1);
normalsArray[currentVertexPointer*3] = currentNorm.x;
normalsArray[currentVertexPointer*3+1] = currentNorm.y;
normalsArray[currentVertexPointer*3+2] = currentNorm.z;
}

Ответить
@meeharbin4205
@meeharbin4205 - 26.01.2020 06:30

Will that work with objects with sharp corners, because in that case vertices need mutiple normals.

Ответить
@6Azamorn9
@6Azamorn9 - 14.02.2020 01:58

While I think the code for loading the OBJ is working well, I have a problem where my OBJ file has multiple textures and I have no idea how to do multiple textures in a single model :/

Ответить
@coderhere127
@coderhere127 - 17.03.2020 10:09

i get a array out of bounds error pls help


java.lang.ArrayIndexOutOfBoundsException: 1
at renderEngine.OBJLoader.loadObjModel(OBJLoader.java:41)
at engineTester.MainGameLoop.main(MainGameLoop.java:27)
Exception in thread "main" java.lang.NullPointerException
at renderEngine.Loader.storeDataInFloatBuffer(Loader.java:100)
at renderEngine.Loader.storeDataInAttributeList(Loader.java:74)
at renderEngine.Loader.loadToVAO(Loader.java:32)
at renderEngine.OBJLoader.loadObjModel(OBJLoader.java:92)
at engineTester.MainGameLoop.main(MainGameLoop.java:27)

Ответить
@redcookielp7760
@redcookielp7760 - 02.04.2020 21:46

I am getting an OutOfBoundsException in the line with the "Vector2f currentTex = textures.get(...)"

Edit: Nevermind, I had a small brackets-error _: D

Ответить
@domosc1
@domosc1 - 05.04.2020 21:18

Im having an issue, any method from the entity class isn't working.

Ответить
@alexvolkov6269
@alexvolkov6269 - 20.04.2020 14:49

How to Fix here?
java.lang.NumberFormatException: For input string: " "
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
at java.lang.Integer.parseInt(Integer.java:470)
at java.lang.Integer.parseInt(Integer.java:499)
at RenderEngine.OBJLoader.processVertex(OBJLoader.java:88)
at RenderEngine.OBJLoader.loadOBJModel(OBJLoader.java:63)
at EngineTester.MainGameLoop.main(MainGameLoop.java:25)

Ответить
@Francisco-qh3qh
@Francisco-qh3qh - 02.05.2020 21:50

last night I watched 9 episodes of your tutorial. this night I had weird dreams, about code and openGL but without sense. when I woke up I was indisposed and threw up 2 times. what did you do to me?

Ответить
@barneyhillman8337
@barneyhillman8337 - 18.06.2020 22:08

So ive done everything so far and it all seemed to work but my program doesnt display the model, there are no errors, my screen just shows up green? any help would be appreciated

Ответить
@lifeoficeldmy
@lifeoficeldmy - 06.07.2020 01:19

hey dude, can i use your OBJ File Loader code in my 3D Render?

Ответить
@t16bros96
@t16bros96 - 11.08.2020 01:12

Just cause you know something doesn’t make you a good teacher

Ответить
@oskarbalcerzak4765
@oskarbalcerzak4765 - 14.08.2020 22:42

In my case model has end up orbiting the center of the world, instead of spinning in place.

Ответить
@mahmoudaasi6725
@mahmoudaasi6725 - 20.08.2020 01:56

I tried a lot to load a 3d model for a jet plane, but it didn't work. Someone can help me if they have a pilot model that works with the classes in the video. @ThinMatrix Help Me Plz

Ответить
@VladimirLem1946
@VladimirLem1946 - 21.08.2020 18:14

Is there any way to make a program that rips from direct x games

Ответить
@Construkid
@Construkid - 26.08.2020 23:07

I know that the serie is 5 years old, but I'm following it with the same LWJGL version, and for those who'd like to import some more complex model the function isn't going to work. Indeed, it will only work for a particular case where each vertice is only mapped to one place on the texture, which is not always the case (imagine 2 cubes next to each other, they share 4 vertices with different colors mapped to them). So here is my version of the code, which is definitely less perfomant because I got to get rid of the index buffer (kinda), but it will work for almost all 3d models you'll import :)

package renderEngine;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;

import models.RawModel;

import org.lwjgl.util.vector.Vector2f;
import org.lwjgl.util.vector.Vector3f;

public class OBJLoader {

public static RawModel loadObjModel(String fileName, Loader loader) {
FileReader fr = null;
try {
fr = new FileReader(new File("res/" + fileName + ".obj"));
} catch (FileNotFoundException e) {
System.err.println("Couldn't load file!");
e.printStackTrace();
}
BufferedReader reader = new BufferedReader(fr);
String line;
List<Vector3f> rawvertices = new ArrayList<Vector3f>();
List<Vector2f> rawtextures = new ArrayList<Vector2f>();
List<Vector3f> rawnormals = new ArrayList<Vector3f>();
List<Integer> rawindices = new ArrayList<Integer>();

float[] verticesArray = null;
float[] normalsArray = null;
float[] textureArray = null;
int[] indicesArray = null;

try {

while (true) {
line = reader.readLine();
if(line == null)
break;
String[] currentLine = line.split(" ");
if (line.startsWith("v ")) {
Vector3f vertex = new Vector3f(Float.parseFloat(currentLine[1]),
Float.parseFloat(currentLine[2]), Float.parseFloat(currentLine[3]));
rawvertices.add(vertex);
} else if (line.startsWith("vt ")) {
Vector2f texture = new Vector2f(Float.parseFloat(currentLine[1]),
Float.parseFloat(currentLine[2]));
rawtextures.add(texture);
} else if (line.startsWith("vn ")) {
Vector3f normal = new Vector3f(Float.parseFloat(currentLine[1]),
Float.parseFloat(currentLine[2]), Float.parseFloat(currentLine[3]));
rawnormals.add(normal);
} else if (line.startsWith("f ")) {
String[] vertex1 = currentLine[1].split("/");
String[] vertex2 = currentLine[2].split("/");
String[] vertex3 = currentLine[3].split("/");

rawindices.add(Integer.parseInt(vertex1[0]));
rawindices.add(Integer.parseInt(vertex1[1]));
rawindices.add(Integer.parseInt(vertex1[2]));

rawindices.add(Integer.parseInt(vertex2[0]));
rawindices.add(Integer.parseInt(vertex2[1]));
rawindices.add(Integer.parseInt(vertex2[2]));

rawindices.add(Integer.parseInt(vertex3[0]));
rawindices.add(Integer.parseInt(vertex3[1]));
rawindices.add(Integer.parseInt(vertex3[2]));

}
}

reader.close();

} catch (Exception e) {
e.printStackTrace();
}

verticesArray = new float[rawindices.size()];
int vertI = 0;
indicesArray = new int[rawindices.size()/3];
int indiceI = 0;
textureArray = new float[(rawindices.size()/3) * 2];
int texI = 0;
normalsArray = new float[rawindices.size()]; // * 3 / 3
int normI = 0;

for(int i = 0; i < rawindices.size();i+=0) {

Vector3f coord = rawvertices.get(rawindices.get(i++)-1);
Vector2f tex = rawtextures.get(rawindices.get(i++)-1);
Vector3f norm = rawnormals.get(rawindices.get(i++)-1);

indicesArray[indiceI] = indiceI;
indiceI++;

verticesArray[vertI++] = coord.x;
verticesArray[vertI++] = coord.y;
verticesArray[vertI++] = coord.z;

textureArray[texI++] = tex.x;
textureArray[texI++] = tex.y;

normalsArray[normI++] = norm.x;
normalsArray[normI++] = norm.y;
normalsArray[normI++] = norm.z;

}


return loader.loadToVAO(verticesArray, indicesArray, textureArray);

}


}

Ответить
@discordgoat4228
@discordgoat4228 - 31.08.2020 13:04

i knoiw this was six years ago, but is there any way to find his code for this? ive been trying for 2 days to fix this and have reqtched the video about 30 times someone pls help me is there any code still?

Ответить
@virenramchandani6113
@virenramchandani6113 - 13.09.2020 01:12

Awesome work teaching great stuff. Kudos for that.
I have been having trouble understanding the handling of .OBJ files having multiple objects (o) and groups (g) parameters.

Ответить
@SmallLeeMaths
@SmallLeeMaths - 16.09.2020 14:38

errr... is it available use in android studio? thanks

Ответить
@techniq4
@techniq4 - 14.10.2020 15:53

My object is not textured, why????

Ответить
@mrfrozen97-despicable
@mrfrozen97-despicable - 28.10.2020 13:54

For those facing problem that only half object is rendered on screen. This is because ur object is in 2 parts and have f's at different positions. So what u can do is opean ur object file in text editor and arrange the v , vt, vf and f in proper order where all f's are at last and ofc save it

Ответить
@DanTheTechMan
@DanTheTechMan - 14.11.2020 20:57

When I try to run the program all I see is a red screen. I tried looking back through the video but as far as I can tell the code is the same, but I could be missing something.

Ответить
@_myron
@_myron - 22.12.2020 08:24

I realize this is an old video but for anyone who is watching this: just download an OBJ loader for java from the internet. Loading an OBJ file is such a common task that surely there is an implementation you can use on the internet. No need to go through the hassle of writing your own.

Ответить
@renaano
@renaano - 05.02.2021 19:58

So far none of the dropbox links working. I don't know if it's just today, or whether those files are removed! Can you please provide new link?

Ответить
@ilnev3738
@ilnev3738 - 27.02.2021 19:04

I`m from 2021
Thanks for tutorials

Ответить
@marcobarros6914
@marcobarros6914 - 15.04.2021 00:43

If anyone is getting an error where the front of the model is invisible, go to tutorial 8 and check the renderer class

Ответить
@k1ngsavage15
@k1ngsavage15 - 16.05.2021 02:11

Ive been following everything correctly but in tutorial 8 the last thing I was able to do was move the square before the quad of course, and no it claims there is no problems and it runs but. I can ever see the model or texture anymore. Just a green background

Ответить
@samuelhulme8347
@samuelhulme8347 - 29.05.2021 21:26

Using blender 2.82 my grassy cube looks wrong with the texture of some of the triangles on the faces being messed up. However your stall model and texture works so it’s not the code.
These are my export settings
Objects as objects = true
With -Z = forward
Y = up
Write normals = true
Include UVs = true
Triangulate Faces = true

Ответить
@AyesC9000
@AyesC9000 - 20.06.2021 00:21

Note that the OBJ loader is half-broken and does not load some models correctly, including models exported from recent versions of Blender.

Ответить
@reginthesmith
@reginthesmith - 30.09.2021 03:47

Does anyone know how to edit the loader so that It can handle seems?

Ответить
@creativedogeMC
@creativedogeMC - 03.03.2022 00:54

Can you tell me the source code?

Ответить
@darcgames6375
@darcgames6375 - 13.04.2022 15:45

Thank you for clarifying. I had the wrong perception of the f part. Struggled with objs for 2 days...

Ответить
@exist1366
@exist1366 - 13.07.2022 19:58

What if the object has more than one image as texture? So its not in one file

Ответить
@exist1366
@exist1366 - 14.07.2022 23:55

Hey. I'm getting indexOutOfBounds exception when loading an obj ? (it worked for two other)

Ответить
@apuppy1858
@apuppy1858 - 09.09.2022 14:41

I was following along this tutorial and i had a problem with the texture with the stall model (the color was weird) so then i asked on his discord and got the solution:
Change this

Vector2f texture = new Vector2f((float) Float.valueOf(currentLine[1]),
(float) Float.valueOf(currentLine[2]));

to

Vector2f texture = new Vector2f((float) Float.valueOf(currentLine[1]),
1.0f - (float) Float.valueOf(currentLine[2]));

if it helps people out there then ur welcome

Ответить
@appsudecasa
@appsudecasa - 05.10.2022 05:33

Some one has a sample about OpenGL ES Java for Android?

Ответить
@MyKarcio
@MyKarcio - 08.11.2022 14:41

If you have missing triangle, that mean blender save .obj as rectangles, not as triangle.
To fix that you need to after line:
processVertex(vertex3,indicies,textures,normals,textureArray,normalsArray);

add this:
if(currentLine.length==5) {
String[] vertex4 = currentLine[4].split("/");
processVertex(vertex1, indicies, textures, normals, textureArray, normalsArray);
processVertex(vertex3, indicies, textures, normals, textureArray, normalsArray);
processVertex(vertex4, indicies, textures, normals, textureArray, normalsArray);
}

Ответить
@NoahtheEpicGuy
@NoahtheEpicGuy - 30.01.2023 07:26

EDIT: As it turns out, I was completely wrong. The Stanford Dragon KILLED my argument. Not really, but random array access is a definite killer when it comes to linked lists, which isn't a problem when it comes to a simple(r) chunk builder. I'll leave my old comment here just for completeness. You could probably do some two-pass to know how much to initialize each array to.

------------------------------------------------------

The use of ArrayLists can lead to slowdowns when it comes to large model files, use a LinkedList instead. This will remove the need for resizing an array, meaning speed is constant rather than somewhat of an O(n^2). I found this out the hard way when generating Minecraft-esque chunks. I believe that you can still use the same for each loop, as that gets an iterator (so it's still fast).

Edit: I just realized that the process vertex function has a somewhat random array access. I'll leave it to someone else to find out if the cost of resizing an array or if the cost of traversing through a LinkedList takes more time (or if it doesn't really matter).

Ответить
@vladimirilyushko5614
@vladimirilyushko5614 - 28.06.2023 14:38

How do I use the .mtl file for my materials?
I have a tree withe leaves and trunk having different uv textures. Is it possible to include them so I won't need to put everything on a single image?

Ответить
@rabitt_bon4316
@rabitt_bon4316 - 19.10.2023 06:57

Hello, I have a problem and that is that instead of me loading the model, it appears as a sphere with the textures, could you help me???

Ответить
@xoxogamewolf7585
@xoxogamewolf7585 - 28.01.2024 19:18

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 18 out of bounds for length 18
at models.OBJLoader.processVertex(OBJLoader.java:96)
at models.OBJLoader.loadObjModel(OBJLoader.java:66)
at Main.main(Main.java:17)

Edit: Fixed it but some triangles are missing

Ответить
@itsmenotjames
@itsmenotjames - 05.05.2024 08:32

this code here fixes the need for triangle objs (it can load quads, really any number of triangles per face). ModelData is just a struct of vertices, indices, and normals. The path is a path relative to your assets dir. so src/main/resources/assets/test.obj would map to assets/test.obj.: ```

import org.joml.Vector2f;
import org.joml.Vector3f;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class OBJParser {
public static ModelData parseOBJ(String path) {
try (InputStream stream = OBJParser.class.getResourceAsStream(path)) {
return parseOBJ(stream);
} catch (Exception e) {
throw new IllegalStateException("Failed to parse OBJ file: " + path, e);
}
}

public static ModelData parseOBJ(InputStream stream) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));

String currentLine;
List<Vector3f> vertices = new ArrayList<>();
List<Vector2f> texCoords = new ArrayList<>();
List<Vector3f> normals = new ArrayList<>();
List<Integer> indices = new ArrayList<>();
float[] verticesArray;
float[] texCoordsArray = null;
float[] normalsArray = null;
int[] indicesArray;

boolean initializedArrays = false;
while ((currentLine = reader.readLine()) != null) {
String[] tokens = currentLine.split(" ");
if (currentLine.startsWith("v ")) {
Vector3f vertex = new Vector3f(Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2]), Float.parseFloat(tokens[3]));
vertices.add(vertex);
} else if (currentLine.startsWith("vt ")) {
Vector2f texCoord = new Vector2f(Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2]));
texCoords.add(texCoord);
} else if (currentLine.startsWith("vn ")) {
Vector3f normal = new Vector3f(Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2]), Float.parseFloat(tokens[3]));
normals.add(normal);
} else if (currentLine.startsWith("f ")) {
if (!initializedArrays) {
texCoordsArray = new float[vertices.size() * 2];
normalsArray = new float[vertices.size() * 3];
initializedArrays = true;
}

List<String[]> verticesList = new ArrayList<>();
for (String token : tokens) {
if (!token.startsWith("f")) {
verticesList.add(token.split("/"));
}
}

for (int i = 0; i < verticesList.size(); i++) {
String[] currentVertex = verticesList.get(i);
if (i == 0) {
String[] vertex1 = verticesList.get(1);
String[] vertex2 = verticesList.get(2);
processVertex(currentVertex, indices, texCoords, normals, texCoordsArray, normalsArray);
processVertex(vertex1, indices, texCoords, normals, texCoordsArray, normalsArray);
processVertex(vertex2, indices, texCoords, normals, texCoordsArray, normalsArray);
} else if (i % 3 == 0) {
String[] vertex1 = verticesList.get(i - 1);
String[] vertex2 = verticesList.get(i - 3);
processVertex(vertex2, indices, texCoords, normals, texCoordsArray, normalsArray);
processVertex(vertex1, indices, texCoords, normals, texCoordsArray, normalsArray);
processVertex(currentVertex, indices, texCoords, normals, texCoordsArray, normalsArray);
}
}
}
}

reader.close();

verticesArray = new float[vertices.size() * 3];
indicesArray = new int[indices.size()];

int vertexPointer = 0;
for (Vector3f vertex : vertices) {
verticesArray[vertexPointer++] = vertex.x;
verticesArray[vertexPointer++] = vertex.y;
verticesArray[vertexPointer++] = vertex.z;
}

for (int i = 0; i < indices.size(); i++) {
indicesArray[i] = indices.get(i);
}

return new ModelData(verticesArray, texCoordsArray, indicesArray);
}

public static void processVertex(String[] data, List<Integer> indices,
List<Vector2f> texCoords, List<Vector3f> normals, float[] texCoordsArray,
float[] normalsArray) {
int currentVertexPointer = Integer.parseInt(data[0]) - 1;
indices.add(currentVertexPointer);
Vector2f currentTexCoord = texCoords.get(Integer.parseInt(data[1]) - 1);
texCoordsArray[currentVertexPointer * 2] = currentTexCoord.x;
texCoordsArray[currentVertexPointer * 2 + 1] = 1 - currentTexCoord.y;

Vector3f currentNormal = normals.get(Integer.parseInt(data[2]) - 1);
normalsArray[currentVertexPointer * 3] = currentNormal.x;
normalsArray[currentVertexPointer * 3 + 1] = currentNormal.y;
normalsArray[currentVertexPointer * 3 + 2] = currentNormal.z;
}

public static record ModelData(float[] vertices, float[] textureCoords, int[] indices) {}
}

```

Ответить
@Luquinha-qf4kb
@Luquinha-qf4kb - 14.05.2024 04:43

what blender version you're using in this video?

Ответить
@nicoadib1888
@nicoadib1888 - 10.03.2025 05:01

Hey there's anyone who knows why when I run my projects it shows empty???

Ответить
@eric44910RobloxPlay
@eric44910RobloxPlay - 17.05.2025 20:47

I can’t even catch up on these tutorials.

Ответить