Better Wavefront OBJ loader.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m35s
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m35s
This commit is contained in:
44
assets/models/cube.obj
Normal file
44
assets/models/cube.obj
Normal file
@@ -0,0 +1,44 @@
|
||||
# Blender 3.6.19
|
||||
# www.blender.org
|
||||
o Cube
|
||||
v 1.000000 1.000000 -1.000000
|
||||
v 1.000000 -1.000000 -1.000000
|
||||
v 1.000000 1.000000 1.000000
|
||||
v 1.000000 -1.000000 1.000000
|
||||
v -1.000000 1.000000 -1.000000
|
||||
v -1.000000 -1.000000 -1.000000
|
||||
v -1.000000 1.000000 1.000000
|
||||
v -1.000000 -1.000000 1.000000
|
||||
vn -0.0000 1.0000 -0.0000
|
||||
vn -0.0000 -0.0000 1.0000
|
||||
vn -1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -1.0000 -0.0000
|
||||
vn 1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.0000 -1.0000
|
||||
vt 0.875000 0.500000
|
||||
vt 0.625000 0.750000
|
||||
vt 0.625000 0.500000
|
||||
vt 0.375000 1.000000
|
||||
vt 0.375000 0.750000
|
||||
vt 0.625000 0.000000
|
||||
vt 0.375000 0.250000
|
||||
vt 0.375000 0.000000
|
||||
vt 0.375000 0.500000
|
||||
vt 0.125000 0.750000
|
||||
vt 0.125000 0.500000
|
||||
vt 0.625000 0.250000
|
||||
vt 0.875000 0.750000
|
||||
vt 0.625000 1.000000
|
||||
s 0
|
||||
f 5/1/1 3/2/1 1/3/1
|
||||
f 3/2/2 8/4/2 4/5/2
|
||||
f 7/6/3 6/7/3 8/8/3
|
||||
f 2/9/4 8/10/4 6/11/4
|
||||
f 1/3/5 4/5/5 2/9/5
|
||||
f 5/12/6 2/9/6 6/7/6
|
||||
f 5/1/1 7/13/1 3/2/1
|
||||
f 3/2/2 7/14/2 8/4/2
|
||||
f 7/6/3 5/12/3 6/7/3
|
||||
f 2/9/4 4/5/4 8/10/4
|
||||
f 1/3/5 3/2/5 4/5/5
|
||||
f 5/12/6 1/3/6 2/9/6
|
@@ -90,9 +90,12 @@ public:
|
||||
/// Vertices are required, Everything else is optional.
|
||||
explicit VertexArray(const std::vector<Vertex>& vertex_positions, const std::vector<unsigned int>& vertex_indices = {},
|
||||
const std::vector<Normal>& vertex_normals = {}, const std::vector<TextureCoordinate>& texture_coordinates = {});
|
||||
|
||||
static VertexArray LoadWavefrontOBJ(const std::string& file_text);
|
||||
};
|
||||
|
||||
|
||||
using namespace JGL;
|
||||
|
||||
static VertexArray Animate(int animation_id, float animation_time);
|
||||
static VertexArray Animate(const AnimationState& anim_state);
|
||||
|
||||
|
23
main.cpp
23
main.cpp
@@ -6,6 +6,7 @@
|
||||
#include <JGL/logger/logger.h>
|
||||
#include <J3ML/Geometry/AABB.hpp>
|
||||
#include <rewindow/logger/logger.h>
|
||||
#include <JGL/types/VertexArray.h>
|
||||
|
||||
using J3ML::LinearAlgebra::Vector2;
|
||||
using namespace JGL::Fonts;
|
||||
@@ -166,7 +167,7 @@ public:
|
||||
camera->render();
|
||||
// All 3D elements of the scene and JGL elements *must* be rendered before the 2D stuff
|
||||
/* if rendering to screen space directly. */
|
||||
auto test_light = PointLight({2,1,2}, {pulse,pulse,pulse, 255}, {0, 0, 0, 255}, {0,0,0}, 1);
|
||||
auto test_light = PointLight({2,1,2}, {pulse,pulse,pulse, 255}, {pulse, pulse, pulse, 255}, {0,0,0}, 1, 0.1, 0.01);
|
||||
// If a 3D object has transparency. The things you'd like to see through it must be drawn before.
|
||||
J3D::Begin();
|
||||
J3D::DrawLine(Colors::Red, {-0.33,-0.125,1}, {-1,-0.125,1});
|
||||
@@ -174,9 +175,9 @@ public:
|
||||
J3D::DrawString(Colors::Red, "JGL Sample Text", {-0.33, -0.1, 1.0f}, 1.f, 32, FreeSans, textAngle, true);
|
||||
//J3D::WireframeSphere(Colors::Green, {0,0,0.5f}, 0.25f, 1, 128, 128);
|
||||
Sphere sphere = {{0,0, 0.5f}, 0.2125};
|
||||
J3D::BatchWireframeRevoSphere(Colors::Green, &sphere, 1, 1, 16, 16, true);
|
||||
J3D::BatchWireframeRevoSphere(Colors::Green, &sphere, 1, 1, 8, 8, true);
|
||||
J3D::RequiredLight(&test_light);
|
||||
J3D::FillAABB(Colors::Whites::AliceBlue, {0,0,0.5f}, {0.1f, 0.1f, 0.1f});
|
||||
J3D::FillAABB(Colors::Whites::AliceBlue, {0,0,0.5f}, {0.05f, 0.05f, 0.05f});
|
||||
J3D::WireframeAABB(Colors::Gray, {0,0,0.5f}, {0.11f, 0.06f, 0.11f});
|
||||
|
||||
AABB boxes[1] = {{Vector3(-0.2125, -0.2125,0.28750), Vector3(0.2125,0.2125,0.7125)}};
|
||||
@@ -308,12 +309,25 @@ public:
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
auto* window = new JGLDemoWindow("JGL Demo Window", 1280, 720);
|
||||
window->SetRenderer(RenderingAPI::OPENGL);
|
||||
window->Open();
|
||||
window->initGL();
|
||||
window->SetResizable(true);
|
||||
window->SetVsyncEnabled(true);
|
||||
|
||||
std::ifstream file("assets/models/cube.obj");
|
||||
if (!file.is_open())
|
||||
return -1;
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
std::string file_text = buffer.str();
|
||||
file.close();
|
||||
//std::cout << "File contents:\n" << file_text << std::endl;
|
||||
|
||||
auto result = VertexArray::LoadWavefrontOBJ(file_text);
|
||||
|
||||
ReWindow::Logger::Error.EnableConsole(false);
|
||||
ReWindow::Logger::Warning.EnableConsole(false);
|
||||
@@ -322,4 +336,7 @@ int main(int argc, char** argv) {
|
||||
while (window->IsAlive())
|
||||
window->ManagedRefresh();
|
||||
return 0;
|
||||
|
||||
//for (const auto& v3 : result)
|
||||
//std::cout << v3.x << " " << v3.y << " " << v3.z << std::endl;
|
||||
}
|
@@ -5,7 +5,7 @@
|
||||
#include <glad/glad.h>
|
||||
#include <JGL/JGL.h>
|
||||
#include <JGL/logger/logger.h>
|
||||
#include "internals/internals.h"
|
||||
#include "internals/include/internals.h"
|
||||
|
||||
namespace JGL {
|
||||
using namespace J3ML;
|
||||
|
9
src/internals/include/WavefrontOBJ.h
Normal file
9
src/internals/include/WavefrontOBJ.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace JGL {
|
||||
class VertexArray;
|
||||
|
||||
VertexArray LoadWavefrontOBJ(const std::string& file_text);
|
||||
}
|
@@ -1,13 +1,13 @@
|
||||
/// Things used in J2D and J3D that should not be exposed to the user of the library.
|
||||
#pragma once
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include "glad/glad.h"
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <J3ML/LinearAlgebra/Vector2.hpp>
|
||||
#include <JGL/types/RenderTarget.h>
|
||||
#include <JGL/types/Light.h>
|
||||
#include <JGL/logger/logger.h>
|
||||
#include "J3ML/LinearAlgebra/Vector2.hpp"
|
||||
#include "JGL/types/RenderTarget.h"
|
||||
#include "JGL/types/Light.h"
|
||||
#include "JGL/logger/logger.h"
|
||||
|
||||
namespace JGL {
|
||||
inline constexpr std::array<const LightBase*, 8> empty_light_array = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
|
@@ -1,4 +1,4 @@
|
||||
#include <JGL/JGL.h>
|
||||
#include "JGL/JGL.h"
|
||||
|
||||
namespace JGL::Data {
|
||||
unsigned char Jupiteroid_Data[] = {
|
@@ -1,5 +1,4 @@
|
||||
#include <JGL/JGL.h>
|
||||
#include "internals.h"
|
||||
|
||||
void JGL::ShapeCache::Init() {
|
||||
std::array<Vector3, 8> vertices = {
|
188
src/internals/src/WavefrontOBJ.cpp
Normal file
188
src/internals/src/WavefrontOBJ.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
#include "../include/WavefrontOBJ.h"
|
||||
#include "JGL/types/VertexArray.h"
|
||||
#include "JGL/logger/logger.h"
|
||||
|
||||
std::pair<float, unsigned long> ParseNumber(const std::string& file_text, const unsigned long& offset) {
|
||||
std::string number;
|
||||
unsigned long new_offset = offset;
|
||||
bool decimal_used = false;
|
||||
|
||||
if (offset >= file_text.size())
|
||||
return {0.0f, offset};
|
||||
|
||||
for (; new_offset < file_text.size(); new_offset++) {
|
||||
if (file_text[new_offset] == '-') {
|
||||
if (number.empty()) {
|
||||
number.push_back(file_text[new_offset]);
|
||||
continue;
|
||||
}
|
||||
Logger::Error("Error while parsing number, Index: " + std::to_string(new_offset) + " Extra negative sign.");
|
||||
return {0.0f, offset};
|
||||
}
|
||||
|
||||
else if (file_text[new_offset] == '.') {
|
||||
if (!decimal_used) {
|
||||
number.push_back(file_text[new_offset]);
|
||||
decimal_used = true;
|
||||
continue;
|
||||
}
|
||||
Logger::Error("Error parsing number at: " + std::to_string(new_offset) + " Extra decimal point.");
|
||||
return {0.0f, offset};
|
||||
}
|
||||
|
||||
else if (isdigit(file_text[new_offset]))
|
||||
number.push_back(file_text[new_offset]);
|
||||
else
|
||||
break;
|
||||
}
|
||||
return {std::stof(number), new_offset};
|
||||
}
|
||||
|
||||
std::pair<Vector2, unsigned long> ParseVector2(const std::string& file_text, const unsigned long& offset) {
|
||||
auto x_result = ParseNumber(file_text, offset);
|
||||
auto y_result = ParseNumber(file_text, x_result.second + 1);
|
||||
|
||||
// If the new offset is the same as the offset we passed in then we know it didn't work.
|
||||
if (x_result.second == offset || y_result.second == x_result.second)
|
||||
return {Vector2(0, 0), offset};
|
||||
|
||||
return {Vector2(x_result.first, y_result.first), y_result.second + 1};
|
||||
}
|
||||
|
||||
std::pair<Vector3, unsigned long> ParseVector3(const std::string& file_text, const unsigned long& offset) {
|
||||
auto x_result = ParseNumber(file_text, offset);
|
||||
auto y_result = ParseNumber(file_text, x_result.second + 1);
|
||||
auto z_result = ParseNumber(file_text, y_result.second + 1);
|
||||
|
||||
// If the new offset is the same as the offset we passed in then we know it didn't work.
|
||||
if (x_result.second == offset || y_result.second == x_result.second || z_result.second == y_result.second)
|
||||
return {Vector3(0,0,0), offset};
|
||||
|
||||
return {Vector3(x_result.first, y_result.first, z_result.first), z_result.second + 1};
|
||||
}
|
||||
|
||||
std::pair<std::array<Vector3, 3>, unsigned long> ParseFaceData(const std::string& file_text, const unsigned long& offset) {
|
||||
unsigned long new_offset = offset;
|
||||
std::array<Vector3, 3> face_data;
|
||||
|
||||
for (unsigned int i = 0; i < 3; i++) {
|
||||
Vector3 vertex = {-1, -1, -1};
|
||||
|
||||
// Vertex
|
||||
if (std::isdigit(file_text[new_offset])) {
|
||||
auto v_result = ParseNumber(file_text, new_offset);
|
||||
vertex.x = v_result.first;
|
||||
new_offset = v_result.second;
|
||||
}
|
||||
|
||||
// UV
|
||||
if (new_offset < file_text.size() && file_text[new_offset] == '/') {
|
||||
new_offset++;
|
||||
|
||||
if (new_offset < file_text.size() && (std::isdigit(file_text[new_offset]))) {
|
||||
auto vt_result = ParseNumber(file_text, new_offset);
|
||||
vertex.y = vt_result.first;
|
||||
new_offset = vt_result.second;
|
||||
}
|
||||
}
|
||||
|
||||
// Normal
|
||||
if (new_offset < file_text.size() && file_text[new_offset] == '/') {
|
||||
new_offset++;
|
||||
|
||||
if (new_offset < file_text.size() && (std::isdigit(file_text[new_offset]))) {
|
||||
auto vn_result = ParseNumber(file_text, new_offset);
|
||||
vertex.z = vn_result.first;
|
||||
new_offset = vn_result.second;
|
||||
}
|
||||
}
|
||||
|
||||
face_data[i] = vertex;
|
||||
new_offset++;
|
||||
}
|
||||
return {face_data, new_offset};
|
||||
}
|
||||
|
||||
VertexArray JGL::LoadWavefrontOBJ(const std::string &file_text) {
|
||||
std::vector<std::array<Vector3, 3>> faceline_data;
|
||||
|
||||
std::vector<TextureCoordinate> temp_uvs;
|
||||
std::vector<Normal> temp_normals;
|
||||
|
||||
std::vector<Vertex> vertices;
|
||||
std::vector<Normal> normals;
|
||||
std::vector<TextureCoordinate> uvs;
|
||||
std::vector<unsigned int> indices;
|
||||
|
||||
unsigned long offset = 0;
|
||||
|
||||
while (offset < file_text.size()) {
|
||||
char c = file_text[offset];
|
||||
|
||||
// Skip comments and empty lines
|
||||
if (c == '#' || c == '\n' || c == '\r') {
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Vertices
|
||||
if (c == 'v' && offset + 1 < file_text.size() && file_text[offset + 1] == ' ') {
|
||||
offset += 2;
|
||||
|
||||
auto vertex_result = ParseVector3(file_text, offset);
|
||||
if (vertex_result.second != offset) {
|
||||
vertices.push_back(vertex_result.first);
|
||||
offset = vertex_result.second;
|
||||
}
|
||||
}
|
||||
|
||||
// Normals.
|
||||
else if (c == 'v' && offset + 2 < file_text.size() && file_text[offset + 1] == 'n' && file_text[offset + 2] == ' ') {
|
||||
offset += 3;
|
||||
|
||||
auto normal_result = ParseVector3(file_text, offset);
|
||||
if (normal_result.second != offset) {
|
||||
temp_normals.push_back(normal_result.first);
|
||||
offset = normal_result.second;
|
||||
}
|
||||
}
|
||||
|
||||
// Texture Coordinates
|
||||
else if (c == 'v' && offset + 2 < file_text.size() && file_text[offset + 1] == 't' && file_text[offset + 2] == ' ') {
|
||||
offset += 3;
|
||||
|
||||
auto uv_result = ParseVector2(file_text, offset);
|
||||
if (uv_result.second != offset) {
|
||||
temp_uvs.push_back(uv_result.first);
|
||||
offset = uv_result.second;
|
||||
}
|
||||
}
|
||||
|
||||
// Face lines.
|
||||
else if (c == 'f' && offset + 1 < file_text.size() && file_text[offset + 1] == ' ') {
|
||||
offset += 2;
|
||||
|
||||
auto faceline_result = ParseFaceData(file_text, offset);
|
||||
if (faceline_result.second != offset) {
|
||||
faceline_data.push_back(faceline_result.first);
|
||||
offset = faceline_result.second;
|
||||
}
|
||||
}
|
||||
else
|
||||
offset++;
|
||||
}
|
||||
|
||||
// Pick everything out of the face lines how OpenGL expects it to be.
|
||||
for (const auto& face : faceline_data) {
|
||||
for (const auto& vp : face){
|
||||
if (vp.x != -1)
|
||||
indices.push_back(vp.x -1);
|
||||
if (vp.y != -1)
|
||||
uvs.push_back(temp_uvs[vp.y -1]);
|
||||
if (vp.z != -1)
|
||||
normals.push_back(temp_normals[vp.z -1]);
|
||||
}
|
||||
}
|
||||
|
||||
return VertexArray(vertices.data(), vertices.size(), indices.data(), indices.size(), normals.data(), normals.size(), uvs.data(), uvs.size());
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
#include "internals.h"
|
||||
#include <J3ML/LinearAlgebra/Vector4.hpp>
|
||||
#include <JGL/types/Light.h>
|
||||
#include "../include/internals.h"
|
||||
#include "J3ML/LinearAlgebra/Vector4.hpp"
|
||||
#include "JGL/types/Light.h"
|
||||
|
||||
// TODO handle the case that a required light is in the list of optional lights.
|
||||
void JGL::J3D::SelectLights(const Vector3& position) {
|
@@ -1,7 +1,7 @@
|
||||
#include <JGL/JGL.h>
|
||||
#include <JGL/logger/logger.h>
|
||||
#include <J3ML/Algorithm/Bezier.hpp>
|
||||
#include "../internals/internals.h"
|
||||
#include "../internals/include/internals.h"
|
||||
|
||||
void JGL::J2D::Begin(RenderTarget* rt, bool clear_buffers) {
|
||||
GLfloat old_clear_color[4];
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#include <J3ML/Geometry/OBB.hpp>
|
||||
#include <J3ML/Algorithm/Bezier.hpp>
|
||||
#include <JGL/types/Light.h>
|
||||
#include "../internals/internals.h"
|
||||
#include "../internals/include/internals.h"
|
||||
|
||||
|
||||
std::array<GLfloat, 16> JGL::OpenGLPerspectiveProjectionRH(float fovY, float aspect, float z_near, float z_far) {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
#include <JGL/JGL.h>
|
||||
#include "../internals/internals.h"
|
||||
#include "../internals/include/internals.h"
|
||||
|
||||
|
||||
#if __linux__
|
||||
|
@@ -1,4 +1,6 @@
|
||||
#include <JGL/types/VertexArray.h>
|
||||
#include <JGL/logger/logger.h>
|
||||
#include "../internals/include/WavefrontOBJ.h"
|
||||
|
||||
using namespace JGL;
|
||||
|
||||
@@ -159,7 +161,7 @@ VertexArray::VertexArray(const Vector3* vertex_positions, const long& vp_length,
|
||||
|
||||
if (texture_coordinates && vt_length) {
|
||||
this->texture_coordinates = VRamList(texture_coordinates, vt_length);
|
||||
local_normals.resize(vt_length);
|
||||
local_texture_coordinates.resize(vt_length);
|
||||
memcpy(local_texture_coordinates.data(), texture_coordinates, sizeof(TextureCoordinate) * vt_length);
|
||||
}
|
||||
|
||||
@@ -189,3 +191,7 @@ VertexArray::VertexArray(const std::vector<Vertex>& vertex_positions, const std:
|
||||
bool VertexArray::Static() {
|
||||
return animations.empty();
|
||||
}
|
||||
|
||||
VertexArray VertexArray::LoadWavefrontOBJ(const std::string& file_text) {
|
||||
return JGL::LoadWavefrontOBJ(file_text);
|
||||
}
|
||||
|
Reference in New Issue
Block a user