google / google/draco

Texture mapping got flipped when trying to view the encoded mesh

Open
#1,091 4 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
C++
Stars
7.5k
Forks
1.1k
Avg merge
47m
Merged PRs (30d)
1

Description

Hi folks, I am having issues with encoding and viewing the encoded mesh. After encoding the mesh, when I opened the mesh for viewing, along with the texture, the texture mapping looks way off

I'm using the model and texture from vulkan-tutorial:
https://vulkan-tutorial.com/resources/viking_room.obj
https://vulkan-tutorial.com/resources/viking_room.png

Encoded with the provided encoder program
`./draco_encoder -i viking_room.obj -o viking_room.drc`

Viewer program in C++
``` cpp
#include
#include
#include
#include
#include
#include

#define GLFW_INCLUDE_NONE
#include
#include
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

// Draco includes (Make sure Draco is properly installed and include paths are set)
#include "draco/compression/decode.h"
#include "draco/mesh/mesh.h"
#include "draco/core/decoder_buffer.h"
#include "draco/attributes/point_attribute.h"
#include "draco/attributes/geometry_attribute.h"
//#include "draco/attributes/attribute_values.h"

// Shader sources
// A very basic vertex and fragment shader for rendering the mesh
// The vertex shader applies the rotation and projection. For simplicity, we use uniform transformations.
static const char* vertexShaderSource = R"glsl(
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoord;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoord;

void main()
{
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = mat3(transpose(inverse(model))) * aNormal;
TexCoord = aTexCoord;
gl_Position = projection * view * vec4(FragPos, 1.0);
}
)glsl";

static const char* fragmentShaderSource = R"glsl(
#version 330 core
out vec4 FragColor;

in vec3 Normal;
in vec3 FragPos;
in vec2 TexCoord;

uniform sampler2D ourTexture;
uniform bool useTexture;

void main()
{
vec3 color = vec3(0.8, 0.8, 0.8);
if (useTexture) {
color = texture(ourTexture, TexCoord).rgb;
}

// Simple directional lighting
vec3 lightDir = normalize(vec3(1.0, 1.0, 1.0));
float diff = max(dot(normalize(Normal), lightDir), 0.0);
vec3 diffuse = diff * color;

vec3 ambient = 0.3 * color;
vec3 result = ambient + diffuse;
FragColor = vec4(result, 1.0);
}
)glsl";

// Handle input rotation
void processInput(GLFWwindow* window, glm::vec3& rotationAngles, float deltaTime) {
const float rotationSpeed = 50.0f; // Degrees per second

if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
rotationAngles.x -= rotationSpeed * deltaTime;
}
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
rotationAngles.x += rotationSpeed * deltaTime;
}
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
rotationAngles.z -= rotationSpeed * deltaTime;
}
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
rotationAngles.z += rotationSpeed * deltaTime;
}
if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS) {
rotationAngles.y -= rotationSpeed * deltaTime;
}
if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS) {
rotationAngles.y += rotationSpeed * deltaTime;
}
if (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS) {
rotationAngles = glm::vec3(0.0f);
}
}

// Utility function to compile shaders
GLuint compileShader(GLenum type, const char* source) {
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, nullptr);
glCompileShader(shader);

GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
char log[512];
glGetShaderInfoLog(shader, 512, nullptr, log);
std::cerr << "Shader compilation failed:\n" << log << std::endl;
return 0;
}

return shader;
}

GLuint createShaderProgram(const char* vSource, const char* fSource) {
GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vSource);
GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fSource);

GLuint program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);

GLint success;
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success) {
char log[512];
glGetProgramInfoLog(program, 512, nullptr, log);
std::cerr << "Program linking failed:\n" << log << std::endl;
}

glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);

return program;
}

// Load Draco mesh from file
bool loadDracoMesh(const std::string& filepath, std::vector& vertices, std::vector& normals, std::vector& texCoords, std::vector& indices) {
// Load the Draco file into memory
std::ifstream ifs(filepath, std::ios::binary);
if (!ifs) {
std::cerr << "Failed to open Draco file: " << filepath << std::endl;
return false;
}

std::vector data((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator()));
if (data.empty()) {
std::cerr << "Draco file is empty or could not be read: " << filepath << std::endl;
return false;
}

draco::DecoderBuffer buffer;
buffer.Init(data.data(), data.size());

draco::Decoder decoder;
auto maybeGeom = decoder.DecodeMeshFromBuffer(&buffer);
if (!maybeGeom.ok()) {
std::cerr << "Failed to decode Draco mesh: " << maybeGeom.status().error_msg() << std::endl;
return false;
}

// Move from the StatusOr itself, which should return a movable unique_ptr
std::unique_ptr mesh = std::move(maybeGeom).value();

// Get position attribute
const draco::PointAttribute* pos_att = mesh->GetNamedAttribute(draco::GeometryAttribute::POSITION);
if (!pos_att) {
std::cerr << "No position attribute found in Draco file." << std::endl;
return false;
}

// Try to get normal attribute
const draco::PointAttribute* norm_att = mesh->GetNamedAttribute(draco::GeometryAttribute::NORMAL);

// Try to get texture coordinate attribute
const draco::PointAttribute* tex_att = mesh->GetNamedAttribute(draco::GeometryAttribute::TEX_COORD);

// Extract indices
indices.reserve(mesh->num_faces() * 3);
for (draco::FaceIndex i(0); i < mesh->num_faces(); ++i) {
const draco::Mesh::Face& face = mesh->face(i);
for (int c = 0; c < 3; ++c) {
indices.push_back(face[c].value());
}
}

// Extract vertex data
int num_points = mesh->num_points();
vertices.resize(num_points * 3, 0.0f);
if (norm_att) normals.resize(num_points * 3, 0.0f);
if (tex_att) texCoords.resize(num_points * 2, 0.0f);

draco::Vector3f pos_value;
draco::Vector3f norm_value;
draco::Vector2f tex_value;

for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
// Positions
draco::AttributeValueIndex pos_val_id = pos_att->mapped_index(i);
pos_att->GetValue(pos_val_id, &vertices[i.value() * 3]);

// Normals
if (norm_att) {
draco::AttributeValueIndex norm_val_id = norm_att->mapped_index(i);
norm_att->GetValue(norm_val_id, &normals[i.value() * 3]);
}

// Texture coordinates
if (tex_att) {
draco::AttributeValueIndex tex_val_id = tex_att->mapped_index(i);
tex_att->GetValue(tex_val_id, &texCoords[i.value() * 2]);
}
}

return true;
}

int main(int argc, char** argv) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " [path_to_texture]" << std::endl;
return 1;
}

std::string dracoPath = argv[1];
std::string texturePath;
bool useTexture = false;
if (argc >= 3) {
texturePath = argv[2];
useTexture = true;
}

// Initialize GLFW
if (!glfwInit()) {
std::cerr << "Failed to initialize GLFW.\n";
return 1;
}

// Setup GLFW window and context
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

GLFWwindow* window = glfwCreateWindow(800, 600, "Draco Mesh Viewer", nullptr, nullptr);
if (!window) {
std::cerr << "Failed to create GLFW window.\n";
glfwTerminate();
return 1;
}
glfwMakeContextCurrent(window);

// Initialize GLEW
glewExperimental = GL_TRUE;
GLenum glewErr = glewInit();
if (glewErr != GLEW_OK) {
std::cerr << "Failed to initialize GLEW: " << glewGetErrorString(glewErr) << "\n";
return 1;
}

// Load Draco mesh
std::vector vertices, normals, texCoords;
std::vector indices;
if (!loadDracoMesh(dracoPath, vertices, normals, texCoords, indices)) {
return 1;
}

// Create shader program
GLuint shaderProgram = createShaderProgram(vertexShaderSource, fragmentShaderSource);
if (!shaderProgram) {
return 1;
}

// Generate buffers and arrays
GLuint VAO, VBO, NBO, TBO, EBO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);

// Positions
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW);

// Indices
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);

// Normals
if (!normals.empty()) {
glGenBuffers(1, &NBO);
glBindBuffer(GL_ARRAY_BUFFER, NBO);
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(float), normals.data(), GL_STATIC_DRAW);
}

// Texture coords
if (!texCoords.empty()) {
glGenBuffers(1, &TBO);
glBindBuffer(GL_ARRAY_BUFFER, TBO);
glBufferData(GL_ARRAY_BUFFER, texCoords.size() * sizeof(float), texCoords.data(), GL_STATIC_DRAW);
}

// Set up vertex attribute pointers
// Position attribute
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

// Normal attribute
if (!normals.empty()) {
glBindBuffer(GL_ARRAY_BUFFER, NBO);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
}

// Texture coordinate attribute
if (!texCoords.empty()) {
glBindBuffer(GL_ARRAY_BUFFER, TBO);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
glEnableVertexAttribArray(2);
}

glBindVertexArray(0);

// Load texture if available
GLuint texture = 0;
if (useTexture) {
int width, height, nrChannels;
unsigned char* data = stbi_load(texturePath.c_str(), &width, &height, &nrChannels, 0);
if (!data) {
std::cerr << "Failed to load texture: " << texturePath << "\n";
useTexture = false;
}
else {
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
GLenum format = GL_RGB;
if (nrChannels == 1) format = GL_RED;
else if (nrChannels == 3) format = GL_RGB;
else if (nrChannels == 4) format = GL_RGBA;

glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

stbi_image_free(data);
}
}

// Enable depth test
glEnable(GL_DEPTH_TEST);

// Set up transformations and camera
glm::vec3 rotationAngles(0.0f);
float fov = 45.0f;

auto lastTime = std::chrono::high_resolution_clock::now();

// Main loop
while (!glfwWindowShouldClose(window)) {
// Calculate delta time
auto currentTime = std::chrono::high_resolution_clock::now();
float deltaTime = std::chrono::duration(currentTime - lastTime).count();
lastTime = currentTime;

// Input
glfwPollEvents();
processInput(window, rotationAngles, deltaTime);

if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}

// Render
glClearColor(0.2f, 0.3f, 0.35f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glUseProgram(shaderProgram);

// Projection matrix
int width, height;
glfwGetFramebufferSize(window, &width, &height);
float aspect = width / (float)height;
glm::mat4 projection = glm::perspective(glm::radians(fov), aspect, 0.1f, 100.0f);

// View matrix
glm::mat4 view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -5.0f));

// Model matrix with rotations
glm::mat4 model = glm::mat4(1.0f);
model = glm::rotate(model, glm::radians(rotationAngles.x), glm::vec3(1.0f, 0.0f, 0.0f));
model = glm::rotate(model, glm::radians(rotationAngles.y), glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::rotate(model, glm::radians(rotationAngles.z), glm::vec3(0.0f, 0.0f, 1.0f));

GLint modelLoc = glGetUniformLocation(shaderProgram, "model");
GLint viewLoc = glGetUniformLocation(shaderProgram, "view");
GLint projLoc = glGetUniformLocation(shaderProgram, "projection");
GLint useTextureLoc = glGetUniformLocation(shaderProgram, "useTexture");

glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

glUniform1i(useTextureLoc, (GLint)useTexture);

if (useTexture) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
GLint texLoc = glGetUniformLocation(shaderProgram, "ourTexture");
glUniform1i(texLoc, 0);
}

glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, (GLsizei)indices.size(), GL_UNSIGNED_INT, 0);

glfwSwapBuffers(window);
}

// Cleanup
glDeleteProgram(shaderProgram);
glDeleteBuffers(1, &VBO);
if (!normals.empty()) glDeleteBuffers(1, &NBO);
if (!texCoords.empty()) glDeleteBuffers(1, &TBO);
glDeleteBuffers(1, &EBO);
if (useTexture) glDeleteTextures(1, &texture);
glDeleteVertexArrays(1, &VAO);

glfwTerminate();
return 0;
}

```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.