Jump to content
  • Advertisement
Charlie Malmqvist

OpenGL Problem with assimp, some uv's on some meshes are incorrect

Recommended Posts

I'm trying to implement model loading, and so far the meshes and most vertex data is imported correctly. However, some meshes seems to get the uv's wrong (as seen in attached screenshot) and I don't really have any idea how to debug this. Below are the shaders and the model class which does the loading with assimp. The whole project can be found at https://github.com/N00TN00T/Wizzy.

 

Result:

screenshot_wizzy.thumb.png.fc222d2a5d7f06c7f89afb761f8a2239.png

Shaders:

// VERTEX SHADER

layout(location = 0) in vec4 vertexPosition;
layout(location = 1) in vec2 vertexUv;
layout(location = 2) in vec3 vertexNormal;

uniform mat4 camTransform;
uniform mat4 worldTransform;

out vec4 worldPosition;
out vec2 uv;

void main() {
    worldPosition = camTransform * worldTransform * vertexPosition;
    gl_Position = worldPosition;

    uv = vertexUv;
}

// FRAGMENT SHADER

out vec4 outColor;

in vec4 worldPosition;
in vec2 uv;

uniform vec4 albedo = vec4(1.0, 1.0, 1.0, 1.0);
uniform vec4 diffuseColor = vec4(1.0, 1.0, 1.0, 1.0);
uniform bool useDiffuseMap = false;
uniform sampler2D diffuseMap;

void main()
{
    vec4 textureColor = vec4(1.0, 1.0, 1.0, 1.0);
    if (useDiffuseMap) {
        textureColor = texture(diffuseMap, uv);
    }
    outColor = textureColor * diffuseColor * albedo;
}

Model class:

// model.h

class Model
        : public Resource {
    public:
        Model(const string& sourceFile, const string& data, const ulib::Bitset& flags);

        inline
        std::vector<Mesh>& GetMeshes() { return m_meshes; }

        virtual
        string Serialize() const override;

        static
        Model* Create(const string& sourceFile, const string& data, ulib::Bitset flags = ulib::Bitset());

    private:
        bool VerifyAssimpScene(const aiScene *scene, string *error);
        bool VerifyAssimpMesh(const aiMesh *mesh, string *error);
        bool ProcessNode(aiNode *node, const aiScene *scene,
                         const string& sourceFile);
        Mesh ProcessMesh(aiMesh *mesh, const aiScene *scene, bool *success,
                         const string& sourceFile, int32 meshIndex);
        MaterialHandle ProcessMaterial(aiMaterial *mat,
                                       const string& meshName,
                                       const aiScene *scene,
                                       const string& sourceFile);
        std::vector<TextureHandle> LoadMaterialTextures(aiMaterial *mat,
                                                        int32 textureType,
                                                        const aiScene *scene,
                                                        const string& sourceFile);


    private:
        std::vector<Mesh> m_meshes;

    };
          
// model.cpp
          
    Model::Model(const string& sourceFile, const string& data, const ulib::Bitset& flags)
        : Resource(flags, "Model", WZ_EXTENSION_MODEL) {
        WZ_CORE_TRACE("Reading model with assimp from data");
        Assimp::Importer _importer;
        std::vector<Material*> _materials;
        std::vector<string> _meshNames;

        const aiScene *_scene
                = _importer.ReadFileFromMemory(&data[0], data.size(),
                                     aiProcess_Triangulate |
                                     aiProcess_FlipUVs |
                                     aiProcess_JoinIdenticalVertices  |
                                     aiProcess_SortByPType |
                                     aiProcess_GenSmoothNormals  |
                                     aiProcess_CalcTangentSpace |
                                     aiProcess_RemoveRedundantMaterials |
                                     aiProcess_OptimizeMeshes |
                                     aiProcess_OptimizeGraph
                                 );

        string _sceneError = "";

        if (!_scene) {

            WZ_CORE_ERROR("Failed loading model, Assimp error: {0}", _importer.GetErrorString());

            m_isValid = false;
            return;
        }
        WZ_CORE_INFO("Successfully read model from data");

        WZ_CORE_TRACE("Verifying scene...");
        if (!VerifyAssimpScene(_scene, &_sceneError)) {

            WZ_CORE_ERROR("Failed loading model from data: '{0}'...", _sceneError);

            m_isValid = false;
            return;
        }
        WZ_CORE_INFO("Scene was verified successfully");

        ProcessNode(_scene->mRootNode, _scene, sourceFile);

        m_isValid = true;
        WZ_CORE_INFO("Model successfully initialized");
    }

    string Model::Serialize() const {
        WZ_CORE_ERROR("Model serializing not yet implemented");
        return "";
    }

    bool Model::VerifyAssimpScene(const aiScene *scene, string *error) {

        *error = "Unknown error";

        if (!scene) {
            *error = "Scene was null, assimp error...";
            return false;
        }

        if (!scene->HasMeshes()) {
            *error = "File has no meshes";
            return false;
        }

        return true;
    }
    bool Model::VerifyAssimpMesh(const aiMesh *mesh, string *error) {

        *error = "Unknown error";

        if (!mesh) {
            *error = "Mesh is null";
            return false;
        }

        if (!mesh->HasFaces()) {
            *error = "Mesh has no indices";
            return false;
        }

        if (!mesh->HasNormals()) {
            *error = "Mesh has no normals";
            return false;
        }

        if (!mesh->HasPositions()) {
            *error = "Mesh has no positions";
            return false;
        }

        return true;
    }

    bool Model::ProcessNode(aiNode *node, const aiScene *scene, const string& sourceFile) {
        WZ_CORE_TRACE("Processing ai node '{0}'...", node->mName.C_Str());
        WZ_CORE_TRACE("Processing {0} meshes...", node->mNumMeshes);
        m_meshes.reserve(node->mNumMeshes);
        for (u32 i = 0; i < node->mNumMeshes; i++) {
            WZ_CORE_TRACE("Processing mesh {0}/{1}", i + 1, node->mNumMeshes);
            aiMesh *_mesh = scene->mMeshes[node->mMeshes[i]];
            bool _meshSuccess = false;
            auto _processedMesh = ProcessMesh(_mesh, scene, &_meshSuccess, sourceFile, i);
            if (_meshSuccess) {
                m_meshes.push_back(_processedMesh);
            } else {
                WZ_CORE_ERROR("Failed processing node, error when processing mesh.");
                return false;
            }
            WZ_CORE_INFO("Successfully processed mesh {0}/{1}", i + 1, node->mNumMeshes);
        }

        WZ_CORE_TRACE("Processing {0} node children...", node->mNumChildren);
        bool _anyFailure = false;
        for (u32 i = 0; i < node->mNumChildren; i++) {
            WZ_CORE_TRACE("Processing child {0}/{1}", i + 1, node->mNumChildren);
            if (!ProcessNode(node->mChildren[i], scene, sourceFile)) {
                _anyFailure = true;
                break;
            }
        }

        if (_anyFailure) {
            return false;
        }
        WZ_CORE_INFO("Successfully processed ai node '{0}'", node->mName.C_Str());
        return true;
    }

    Mesh Model::ProcessMesh(aiMesh *mesh, const aiScene *scene, bool *success, const string& sourceFile, int32 meshIndex) {
        *success = true;
        string meshError = "";
        std::vector<Vertex> _vertices;
        std::vector<u32> _indices;

        WZ_CORE_TRACE("Verifying mesh");
        if (!VerifyAssimpMesh(mesh, &meshError)) {
            WZ_CORE_ERROR("Failed processing mesh: {0}", meshError);
            *success = false;
            return Mesh();
        }
        WZ_CORE_TRACE("Mesh OK");

        WZ_CORE_TRACE("Reserving memory for {0} vertices and {1} faces ({2} indices) [{2} Bytes]",
                        mesh->mNumVertices, mesh->mNumFaces,
                        mesh->mNumFaces * 3,
                        (sizeof(Vertex) * mesh->mNumVertices + sizeof(u32) * mesh->mNumFaces * 3));

        _vertices.reserve(mesh->mNumVertices);
        _indices.reserve(mesh->mNumFaces * 3);

        WZ_CORE_TRACE("Iterating {0} vertices...", mesh->mNumVertices);
        for (u32 i = 0; i < mesh->mNumVertices; i++) {
            if (rand() % 500 == 3) {
                WZ_CORE_TRACE("Vertices progress: {0}%", (int)(((float)i / (float)mesh->mNumVertices) * 100));
            }
            aiVector3D& _pos = mesh->mVertices[i];
            aiVector3D _uv;
            aiVector3D& _normal = mesh->mNormals[i];
            if (mesh->HasTextureCoords(0)) {
                _uv = mesh->mTextureCoords[0][i];
            } else {
                _uv = aiVector3D(0, 0, 0);
            }

            _vertices.push_back({
                vec3(_pos.x, _pos.y, _pos.z),
                vec2(_uv.x, _uv.y),
                vec3(_normal.x, _normal.y, _normal.z)
            });
        }

        for (u32 i = 0; i < mesh->mNumFaces; i++) {
            aiFace& _face = mesh->mFaces[i];
            WZ_CORE_ASSERT(_face.mNumIndices == 3, "Model must be triangulated (Should be done automatically at import?)");

            for (u32 j = 0; j < _face.mNumIndices; j++) {
                _indices.push_back(_face.mIndices[j]);
            }
        }

        aiMaterial *_aiMat = scene->mMaterials[mesh->mMaterialIndex];
        string _meshName = ulib::File::name_of(sourceFile) + "_mesh_" + std::to_string(meshIndex) + string("_") + mesh->mName.C_Str();

        auto _material = ProcessMaterial(_aiMat, _meshName, scene, sourceFile);
        if (_material == WZ_NULL_RESOURCE_HANDLE) {
            WZ_CORE_ERROR("Failed processing mesh, couldn't create material");
            *success = false;
            return Mesh();
        }

        return { _vertices, _indices, _material, _meshName };
    }

    MaterialHandle Model::ProcessMaterial(aiMaterial *mat, const string& meshName, const aiScene *scene, const string& sourceFile) {
        string _materialHandle = meshName + "_material";
        Material *_material = new Material(WZ_DEFAULT_SHADER_HANDLE);

        auto _diffuseTextures = LoadMaterialTextures(mat, (int32)aiTextureType_DIFFUSE, scene, sourceFile);
        if (_diffuseTextures.size() == 0) {
            return WZ_NULL_RESOURCE_HANDLE;
        }
        auto _mainDiffuseTexture = _diffuseTextures[0];

        _material->diffuseMapHandle = _mainDiffuseTexture;

        ResourceManagement::Add(_material, _materialHandle);

        return _materialHandle;
    }

    std::vector<TextureHandle> Model::LoadMaterialTextures(aiMaterial *mat, int32 textureType, const aiScene *scene, const string& sourceFile) {
        WZ_CORE_TRACE("Handling textures of texture type {0} for material...", textureType);
        std::vector<TextureHandle> _textures;
        u32 _count = mat->GetTextureCount((aiTextureType)textureType);
        string _fileDirectory = ulib::File::directory_of(sourceFile);
        string _fileName = ulib::File::without_extension(ulib::File::name_of(sourceFile));

        if (_count == 0) {
            WZ_CORE_TRACE("No texture found in model file...");

            WZ_CORE_TRACE("Searching for common disk textures in model directory...");
            static string _fmts[] = {
                ".jpg", ".jpeg", ".png", ".tga", ".bmp", ".psd", ".gid", ".hdr", ".pic"
            }; // TODO

            string _diffuseFile = _fileDirectory + "/diffuse";
            bool _foundDiffuse = false;
            for (const auto& _fmt : _fmts) {
                if (ulib::File::exists(_diffuseFile + _fmt)) {
                    WZ_CORE_TRACE("Found '{0}'", _diffuseFile + _fmt);
                    _diffuseFile += _fmt;
                    _foundDiffuse = true;
                    break;
                }
            }

            if (_foundDiffuse) {
                string _handle = _fileName + "_" + std::to_string(textureType) + "_" + ulib::File::without_extension(ulib::File::name_of(_diffuseFile));
                ResourceManagement::Load<Texture>(_diffuseFile, _handle);
                _textures.push_back(_handle);
            } else {

                aiColor3D _aiColor(0.0f, 0.0f, 0.0f);

                switch ((aiTextureType)textureType) {
                    case aiTextureType_DIFFUSE:
                        mat->Get(AI_MATKEY_COLOR_DIFFUSE, _aiColor);
                        break;
                    default: WZ_CORE_ASSERT(false, "Unimplemented aiTextureType"); break;
                }

                if (_aiColor.IsBlack()) {
                    WZ_CORE_TRACE("No color property found, using unloaded texture...");
                    _textures.push_back(Texture::UnloadedTexture());
                } else {
                    WZ_CORE_TRACE("Color property found, creating 1x1 texture...");
                    auto _color = Color(_aiColor.r, _aiColor.g, _aiColor.b, 1.f);
                    Texture *_texture = Texture::Create((byte*)&_color, 1, 1);
                    auto _handle = ulib::File::name_of(sourceFile) + "_" + std::to_string(textureType) + "_1x1_texture";
                    ResourceManagement::Add(_texture, _handle);
                    _textures.push_back(_handle);
                }

            }

        } else {
            WZ_CORE_TRACE("{0} Textures found...", _count);
            _textures.reserve(_count);
            for (u32 i = 0; i < _count; i++) {
                aiString _path;
                mat->GetTexture((aiTextureType)textureType, i ,&_path);
                string _file = ulib::File::directory_of(sourceFile) + "/" +
                        ulib::File::name_of(ulib::File::to_portable_path(_path.C_Str()));
                string _handle = ulib::File::name_of(sourceFile) + "_" + std::to_string(textureType) + "_" + ulib::File::name_of(_file);
                WZ_CORE_ASSERT(ulib::File::exists(_file), "Embedded textures not yet implemnted");
                ResourceManagement::Load<Texture>(_file, _handle);
                _textures.push_back(_handle);
            }
        }

        if (_textures.size() == 0) {
            WZ_CORE_ERROR("Something went wrong when processing textures, applying invalid texture");
            _textures.push_back(Texture::InvalidTexture());
        }

        return _textures;
    }

 

Share this post


Link to post
Share on other sites
Advertisement
Posted (edited)

I think the problem might be 

aiProcess_FlipUVs

did you try to remove that?

please refer to the following link for more info

http://assimp.sourceforge.net/lib_html/data.html

 

You'll probably want to consider this flag if you use Direct3D for rendering. The aiProcess_ConvertToLeftHanded flag supersedes this setting and bundles all conversions typically required for D3D-based applications.

 

 

Since you are using OpenGL, this might generate an issue.

hope this helps

 

Edited by Diego Gomez

Share this post


Link to post
Share on other sites
Posted (edited)
17 minutes ago, Diego Gomez said:

I think the problem might be 


aiProcess_FlipUVs

did you try to remove that?

please refer to the following link for more info

http://assimp.sourceforge.net/lib_html/data.html

 

You'll probably want to consider this flag if you use Direct3D for rendering. The aiProcess_ConvertToLeftHanded flag supersedes this setting and bundles all conversions typically required for D3D-based applications.

 

 

Since you are using OpenGL, this might generate an issue.

hope this helps

 

Hey,

Thanks for actually going through my post of raw codedumps.

I did also react to that when I read it, but if I don't flip the UV's the result is worse (attached screenshot).

 

screenshot_wizzy_bad.png

Also I found out that the top body and legs + feet are each one mesh, so the original title is incorrect as this means that it's only some parts of the mesh that are given incorrect uv's.

Edited by Charlie Malmqvist

Share this post


Link to post
Share on other sites

Can you try with the following flags?

aiProcess_Triangulate |
  aiProcess_JoinIdenticalVertices |
  aiProcess_PreTransformVertices |
  aiProcess_GenNormals |
  aiProcess_CalcTangentSpace |
  aiProcess_SortByPType

Share this post


Link to post
Share on other sites
6 minutes ago, Diego Gomez said:

Can you try with the following flags?

aiProcess_Triangulate |
  aiProcess_JoinIdenticalVertices |
  aiProcess_PreTransformVertices |
  aiProcess_GenNormals |
  aiProcess_CalcTangentSpace |
  aiProcess_SortByPType

 

screenshot_wizzy_worse.png

Share this post


Link to post
Share on other sites
3 minutes ago, TeaTreeTim said:

When you go mesh.HasTextureCoords(0), there might be multiple channels.

All meshes has 1 channel in this case (I logged it to make sure)

Share this post


Link to post
Share on other sites
Posted (edited)

I found out why I needed to flip uv's; the image loader I'm using loads the images upside down by default, but I made sure to flip it so the images are correct now and no flipping is needed. Still getting the original problem though.

Edited by Charlie Malmqvist

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!