Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Texture mapping got messed up when trying to view the encoded mesh #1091

Open
Terra854 opened this issue Dec 7, 2024 · 3 comments
Open

Texture mapping got messed up when trying to view the encoded mesh #1091

Terra854 opened this issue Dec 7, 2024 · 3 comments

Comments

@Terra854
Copy link

Terra854 commented Dec 7, 2024

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++

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cmath>
#include <chrono>

#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <GL/glew.h>
#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<float>& vertices, std::vector<float>& normals, std::vector<float>& texCoords, std::vector<unsigned int>& 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<char> data((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
    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<draco::Mesh> 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_draco_mesh> [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<float> vertices, normals, texCoords;
    std::vector<unsigned int> 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<float>(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;
}
@Terra854 Terra854 changed the title Texture mapping got messed up when encoding Texture mapping got messed up when trying to view the encoded mesh Dec 7, 2024
@marcinwalus
Copy link

Hello.
Could you please check if "texture mapping" isn't just "vertically flipped"? I spot that when trying to check my model in Unity.

Image

BR

@Terra854
Copy link
Author

Terra854 commented Dec 12, 2024

The texture is mapped to the mesh properly in the original obj file, not sure why the draco encoder is flipping the uv coordinates. Might be a bug in the encoder or something as I am able to replicate this exact flip on another obj mesh.

@Terra854
Copy link
Author

Update: Tried an fbx mesh using the encoder and the UVs also got flipped

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants