Hey! raylib forum is closing!

After a year with not much movement in the forum I decided to close it.

The cost of maintaining the hosting is a bit high for the usage of the platform, updating the forum and managing it is also a bit cumbersome and the truth is that I'm already trying to maintain multiple other networks pretty more active than this forum.

I'll recommend you to move to the other raylib networks:

- For a forum style, use Reddit: https://www.reddit.com/r/raylib/
- For direct messaging and questions, use Discord: https://discord.gg/VkzNHUE
- To report issues, use GitHub: https://github.com/raysan5/raylib

- Also, remember you can contact me personally on Twitter: https://twitter.com/raysan5 or directly by mail to ray[at]raylib.com

If you feel generous, you can also contribute to the project on my Patreon: https://www.patreon.com/raylib

Thanks to you all for joining the project and help to improve it with your questions. Keep it up! :)

Best Regards,

Ray

PD. Closing will be effective by October 31th.

Filling Mesh struct by hand to import GLTF assets.

edited February 23 in General Discussions
Hey.

So. Raylib is a lovely, clean, slick piece of C appreciation. But lets get to the point. I will try to explain everything as clearly as possible so everyone can more or less know what is going on here ;)

I started writing GLTF2 importer. It's actually a layer between Lungudunum Engine loader (https://github.com/Lugdunum3D/glTF2-loader) and the Raylib itself.

If you don't know what GLTF2 is it's a new open source format for saving 3D scenes. Seems like a good way to put fbx to rest when more engines will support it. It has been made by Khronos Group. You can read more about it here https://www.khronos.org/gltf/ .

So in order to render the GLTF file, a new Mesh struct needs to be created and filled with proper data read from GLTF file. I think I have everything ready except:

float *tangents; // Vertex tangents (XYZ - 3 components per vertex) (shader-location = 4)

From what I have read usually tangent vectors are stored in Vec4. It contains 3 vector components representing the tangent and one additional scalar deciding handness of the vertex frame. In the picture below you can see normal and tangent vector (welp you can embed pictures here...sorry).

http://www.opengl-tutorial.org/assets/images/tuto-13-normal-mapping/TangentVectorFromUVs.png

Having those two you can compute a third vector which is perpendicular to the plane those two create. It can be perpendicular in two directions. That's what this fourth component of Vec4 is used for. It decides which direction this vector (called bitangent) aligns with. Picture below shows the third vector added.

http://www.opengl-tutorial.org/assets/images/tuto-13-normal-mapping/NTBFromUVs.png

Raylib uses Vec3 meaning that you don't have the control over the direction of the bitangent vector. Since GLTF specs use Vec4 it makes the array of data incompatible. Let's look at the memory of those two:

Vec3 : | x1 | y1 | z1 | x2 | y2 | z2 | x3 | ...
Vec4 : | x1 | y1 | z1 | w1 | x2 | y2 | z2 | ...

If Raylib stored tangents in Vec4 I could just point Mesh.tangents (it's a pointer) to the Vec4 data supplied by GLTF loader and that would be it. Since there is data type difference the data get misaligned.

My question is: why does Raylib use Vec4? Is there anything better I can do except extracting x, y, z components to a new array and pointing Mesh.tangents to this new memory block aligned with Vec3 struct?

Another thing I need a bit more info about are mesh indices. Vertex indices allow us to not repeat vertices positions in the file. Every vertex is defined once in an array. Another array, an array of indices allows building of the triangles. We just take three consecutive indices which tell us which vertices are used to build the triangle.

In Raylib Mesh.indices is a pointer to unsigned short type which would mean that single index would be able to point to 65535th vertex maximum. If the mesh is made out of more vertices then how would you save its index in an array of 16 bit variables? Does this limit primitive density in Raylib? In GLTF the type the indices array holds changes depending on how many vertices are stored.

Final thing are two properties listed below. From what I read in Raylib's code those are filled by the library, am I right? I shouldn't touch those?

unsigned int vaoId; // OpenGL Vertex Array Object id
unsigned int vboId[7]; // OpenGL Vertex Buffer Objects id (7 types of vertex data)

Sorry for the long and convoluted post... I do hope everything is clear :)

If you want to read more I recommend this link:
http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/

Comments

  • edited February 26
    Hi Michał! Welcome to the forum! :)

    First of all, worth to check https://github.com/syoyo/tinygltf for a small glTF 2.0 models loader and https://github.com/nem0/OpenFBX for FBX loading, not included in raylib because they are C++.

    My question is: why does Raylib use Vec3? Is there anything better I can do except extracting x, y, z components to a new array and pointing Mesh.tangents to this new memory block aligned with Vec3 struct?

    That's because raylib has not been tested enough for 3D and most probably using Vector3 is the wrong decision and it should use Vector4, I can just update it.

    About indices, be careful if using them because all vertex attributes (position, texcoords, normals, tangents...) should be aligned with a single index buffer. My recommendation is to avoid index usage if possible, just unindex data to fill buffers.

    About indices size limited to short, that's an OpenGL ES restriction and so I limited Mesh struct to that size but the truth is that OpenGL 3.3 supports int indices (up to 4K million values). Check for reference: https://github.com/raysan5/raylib/blob/master/src/rlgl.c#L213

    Again, that's probably a wrong design decision and should be reviewed... not enough serious 3D development with raylib...

    Your last question about vaoId and vboId, you're right, they are filled by raylib on:
    LoadMesh() --> rlLoadMesh()

    Actually, those are the identifiers returned by OpenGL once data is transferred to VRAM memory... at that point RAM vertices data is not required any more (supposing you don't need to change those vertices along frames, a.k.a animated models) and can be unloaded.

    Let me know if you have further questions! :)

  • Thanks for the reply.

    I looked into tinygltf and it is a better choice. Unfortunately it caused errors during compilation which I didn't feel like debugging. I may change the loader to tinygltf in the future. Not gonna touch FBX though ;)

    Then are you interested in updating the tangents to Vec4 anytime soon? For now I can duplicate the data into a new array. I suppose you have your own roadmap but if you could squeeze in this feature that would be great :)

    I'm not sure I understand what you mean by single index buffer. What do you mean by unindexing data? Just duplicate the vertices according to indices? Basically recreating the mesh without indices feature?

    You say: all vertex attributes (position, texcoords, normals, tangents...) should be aligned with a single index buffer. Does this mean that those attributes also use indexing?

    I'm pretty new to graphics programming. I was always creating graphics content. Never rendered it with code :P
  • Just updated mesh struct to support 4-component tangents, pick latest raylib from GitHub! Not tested, please let me know if it works ok.

    What do you mean by unindexing data? Just duplicate the vertices according to indices? Basically recreating the mesh without indices feature?

    Yes.

    You say: all vertex attributes (position, texcoords, normals, tangents...) should be aligned with a single index buffer. Does this mean that those attributes also use indexing?

    There are some model formats like .OBJ or .FBX that use independent indices for every attribute, it reduces amount of data saved but it's not usable for rendering, data must be completely indexed or at least reorganized to use a single common indices array.

    Keep me updated with your progress! :smiley:

  • Damn. Thank you :) Gonna bite into it soon. Will let you know how it's going!
    Damn I learned a lot using Raylib and haven't even made anything yet!
  • edited February 25
    First GLTF file rendered in Raylib!

    http://u.cubeupload.com/khatake/kfCS83.png

    I reduced cube to triangle because I though I still don't properly use indices but I figured out that since I'm using LoadModelFromMesh() no one calls rlLoadMesh() for me. I had to do it manually. Is it how it's supposed to be? should it be on the user to do that? Is LoadModelFromMesh() designed to be used by people who can trace issue like that or is it something that needs fixing?

    Damn I'm glad it works :D Now to a cube!
  • edited February 25
    For now I just copied the indices from GLTF indices buffer (uint8_t = byte buffer) into uint16_t = unsigned short (which Raylib uses for indices) buffer to get...

    http://u.cubeupload.com/khatake/7C8Iln.png

    GLTF2.0 cube in all its glory!
  • edited February 25
    Ok ok ok. I promise it's the last one for today:

    http://u.cubeupload.com/khatake/wUdrpl.png

    From triangle to a monkey in 2h!
  • amazing! please, don't stop! :D
  • About your previous question, LoadMesh() loads mesh data and uploads data to VRAM to get vaoId/vboId using rlLoadMesh().

    In case you fill the data manually, you must call rlLoadMesh()... but that function should be exposed to user in some other way. Need to review it.
Sign In or Register to comment.