C Wavefront OBJ loader for OpenGL

Related tags

Graphics objgl
Overview

OBJ GL Loader v2

Quite fast .OBJ loader written in C

How to use it

Put objgl2.c and objgl2.h files in Your project and include them.
To put it simply:

//!!!Buffersize must not be smaller than the line length!
//set it to at least 1K bytes, just to be safe
unsigned int bufferSize = 65536;
objgl2StreamInfo strinfo = objgl2_init_filestream("file/name.obj", bufferSize);
objgl2Data objd = objgl2_readobj(&strinfo);

//cleanup
objgl2_deletestream(&strinfo);
objgl2_deleteobj(&objd);

In comparison to the older version, the newer one is based on the idea of streaming - either from a file or a buffer. I'll talk about it in a second. The example above reads the .OBJ file from the file in ~65535 bytes portions but you don't need to read it from file, you can read the .OBJ data from a regular char buffer:

char *rawobj = your_filereading_function("file/name.obj");

objgl2StreamInfo strinfo = objgl2_init_bufferstream(rawobj);
objgl2Data objd = objgl2_readobj(&strinfo);

Here the stream is created from an already existing buffer.
If, for some reason, you don't want to have a default C FILE streaming implementation just #define OBJGL_FSTREAM_IMPL 0 before including the header file but in that case you'll need to implement own file streaming.

Features

  • For some reason the newer version is faster. Not much but a little bit faster!
  • Loads an ~80MB .obj file with 5,626,896 indices in around 2 sec on my machine (AMD Ryzen 5 2600 Six-Core Processor, no compiler optimizations, debug mode, streaming from file, buffer size - 65536 bytes)
  • Uses peak 432.96 MB of memory for loading the said file and 522 allocations/reallocations
  • Puts the data in an OpenGL-friendly way (interleaved vertex attributes, indices)
  • OpenGL-friendly, easy to use material system
  • Uses a hash table to find unique vertices
  • Made for indexed rendering (glDrawElements)
  • Triangulates the faces if needed
  • NEGATIVE INDICES!!! Yaaaay!
  • Smooth shading, flat shading, auto-smooth, it's not a problem, just remember to generate the normals to the file

    The file used for tests was vokselia_spawn.obj from https://casual-effects.com/data/

Not-so-much features

  • Does not support multiple objects in one file (at the moment, I'll fix it)
  • Uses "triangle fan" triangulation algorithm (glitches may appear if the face is not convex)
  • Does not generate the normals if not present in file
  • Does not care about smoothing groups, flat shading, smooth shading, if no normals are present in the file

Streams and buffers

As I said above, there are two ways of "feeding" the parser with a data - a file stream or a buffer stream. It's important to note that the buffer stream is not really a stream.

When to use file stream over the buffer stream? Sometimes you deal with a large file, hundreds of megabytes. Malloc might not be able to allocate that much memory or there are other reasons not to allocate that much at once, so the idea of streaming comes in handy. Instead of reading the entire file at once and storing the result in one big buffer the program reads n bytes of data from a file, puts it into a buffer and then the OBJ Loader processes that buffer. Once it gets to the end of the buffer it requests more data, and the stream reader gets more bytes from the file into the buffer.

Reading from an already existing buffer supplied by the programmer is a little bit different - here the "streaming" mechanism is just a wrapper and there's no streaming at all. The OBJ Loader requests the data from the streamer and it gives it an entire buffer at once, so there's no overhead of fetching small portions of data.

And what about the buffer size? I recommend you to set it as big as possible - the bigger the buffer the less fetching is done. The OBJ Loader also requires the streamer to return whole lines and if your buffer size is smaller than the length of the line - it won't work! During the tests 10 bytes were too small but 100 bytes did the job, however as I said, set the buffer size as big as possible, 10K, 65K would be optimal.

Implementing your own file streamer

If you think you can do better than me (yes, probably you can) or you just don't want to use the C way of reading files, you can make your own stream reader. The stream reading function pointer looks like that: uint_least32_t (*objgl2_streamreader_ptr)(objgl2StreamInfo*) and the declaration of the default file streamer is uint_least32_t objgl2_filestreamreader(objgl2StreamInfo* info). Only four requirements are:

  • The streamer must return the buffer length (buffer length is not necessarily equal to bufferLen)
  • The streamer must not read more bytes than the bufferLen
  • The streamer must read whole lines, thus it will most likely read fewer bytes than the bufferLen, because it cannot read more than that
  • The streamer must set the eof field to a non-zero value if the end of the file is reached (set the eof to true basicaly). My implementation does it this way: it reads as many bytes (= bufferLen) as it can with fread. It goes back from the end of the buffer until it sees the \n. If the buffer already ends with \n then the length does not change. It returns the length of the buffer (distance from the start of the buffer to the last \n). It's as simple as that. The buffer reader is even simple - it does nothing.

`objgl2_init_bufferstream` and `objgl2_init_filestream` are there just to facilitate the creation of the stream info struct. You'll need to initialize your stream info struct yourself or just change the `objgl2_streamreader_ptr function` after initializing the struct. The code speaks louder than my convoluted descriptions, so please look it up.

How do materials work?

Really simply - let's say we have a function drawIndices(float *vertexData, unsigned int *indices, unsigned int numIndices) and our model has 3 materials: First we read the OBJ.
And now let's draw the entire model: drawIndices(objdata.data, objdata.indices, objdata.numIndices) and here it is! Now let's just render the first material: drawIndices(objdata.data, objdata.materials[0].indices, objdata.materials[0].len) once again, here it is, however now we see only the parts with the first material.

Why is it like that?

It feels intuitive to me - if you're using an OpenGL OBJ loader, probably you need a data suitable for OpenGL, thus vertex parameter interleaving. Probably you want to use different shader for different materials, thus such material system and if you don't care about materials, you just don't care about them in code and just render the model in it's entirety.

Can I use it for...

No, you cannot. Use it however you want as long as it's compliant with the ObjGL2 Loader (modified MIT) license, so go ahead and read it's "rules". The best reward for me is someone actually using my code, not gotoshaming me for QUESTIONABLE coding style. I just want it to be fast. I always use this type of parsing whenever I need to parse a file, because it's quick, easy for me to understand, no string-splitting bloat, just you and code. Harmony.

Data structures

objgl2Data is a structure for holding the OBJ Data - indices, vertices etc.

  • float *data - pointer to an interleaved buffer of vertices (ex. pos texcoord normal pos texcoord normal pos texcoord normal)
  • uint_least32_t *indices - pointer to the indices buffer. Each index is at least 32 bit long unsigned integer
  • objgl2Material *materials - pointer to the materials
  • uint_least32_t numIndices - how many indices there are in the OBJ
  • uint_least32_t numVertices - how many vertices there are
  • uint_least32_t vertSize - one vertex' size in bytes, can be 12 (only positions), 20 (positions and texcoords), 24 (positions and normals) or 32 (all three attributes)
  • uint_least32_t numMaterials - how many materials there are
  • unsigned char hasNormals - (boolean) whether the normals are present in the vertex attributes
  • unsigned char hasTexCoords - (boolean) whether the texcoords are present in the vertex attributes
  • char *name - (might be deleted in the future) the OBJ name, taken from the first o name declaration in the file, it's not important

objgl2StreamInfo is a structure for holding the stream data

  • uint_least64_t fOffset - offset from the beginning of the file, used by fread for fetching the chunks of data
  • uint_least32_t bufferLen - the malloced size of a buffer, maximal amount of bytes to read from the file to the buffer at once
  • uint_least32_t buffOffset - offset from the beginning of the buffer, not used at the moment
  • objgl2_streamreader_ptr function - pointer to a streaming function, by default can be objgl2_filestreamreader or objgl2_bufferstreamreader
  • char* filename - null terminated file name and path, ex. /home/obj/vokselia_spawn.obj
  • char eof - End of the file flag. Needs to be set by the stream reader if the EOF is reached
  • char type - type of the stream, can be OBJGL_FSTREAM or OBJGL_BSTREAM. Used by objgl2_deletestream

objgl2Material is a structure for holding the material data

  • uint_least32_t *indices - pointer to the face indices with that material, it's some offset of objgl2Data's *indices, so the memory is shared
  • uint_least32_t len - how many indices there are in that material. Indices of a specific material are contiguous.
  • char *name - null terminated name of the character. It's malloced and gets freed on objgl2_deleteobj
OpenGL®-Starter is a template for your upcoming OpenGL Projects which has been compiled to run the most basic Hello World OpenGL Program from LearnOpenGL.com.

OpenGL®-Starter OpenGL®-Starter is a template for your upcoming OpenGL Projects which has been compiled to run the most basic Hello World OpenGL Progr

Kushagra 9 Jul 18, 2022
A multi-platform library for OpenGL, OpenGL ES, Vulkan, window and input

GLFW Introduction GLFW is an Open Source, multi-platform library for OpenGL, OpenGL ES and Vulkan application development. It provides a simple, platf

GLFW 9.4k Aug 6, 2022
A legacy OpenGL simulator for OpenGL 4.4, written in C++.

the-ancient-tri A legacy OpenGL simulator for OpenGL 4.4, written in C++. Why? My Uni forces us to use legacy OpenGL (eww!), and I didn't want to lear

Mohammad Issawi 4 Feb 10, 2022
A set of open c++ game development tools that are lightweight, easy-to-integrate and free to use. Currently hosting a magicavoxel .vox full scene loader.

open game tools Open game tools is a set of unencumbered, free, lightweight, easy-to-integrate tools for use in game development. So far it contains:

null 275 Jul 28, 2022
Low Level Graphics Library (LLGL) is a thin abstraction layer for the modern graphics APIs OpenGL, Direct3D, Vulkan, and Metal

Low Level Graphics Library (LLGL) Documentation NOTE: This repository receives bug fixes only, but no major updates. Pull requests may still be accept

Lukas Hermanns 1.4k Aug 13, 2022
Antialiased 2D vector drawing library on top of OpenGL for UI and visualizations.

This project is not actively maintained. NanoVG NanoVG is small antialiased vector graphics rendering library for OpenGL. It has lean API modeled afte

Mikko Mononen 4.4k Aug 11, 2022
Epoxy is a library for handling OpenGL function pointer management for you

Epoxy is a library for handling OpenGL function pointer management for you. It hides the complexity of dlopen(), dlsym(), glXGetProcAddress(), eglGetP

Eric Anholt 554 Aug 9, 2022
3D engine from scratch (without OpenGL or any other 3D graphics library)

Simple 3d engine based on SFML library. I tried to make this engine powerful and easy to understand.

Vectozavr 53 Aug 7, 2022
RGL - 3D visualization device system for R using OpenGL

RGL - 3D visualization device system for R using OpenGL INTRODUCTION The RGL package is a visualization device system for R, using OpenGL or WebGL as

null 59 Jul 27, 2022
OpenGL Demo: Simulating Ocean Waves with FFT

OceanFFT Realistic ocean wave simulation, primarily based on J. Tessendorf's paper, using OpenGL compute shaders. Checkout the demo video here. In the

Achal Pandey 60 Jul 12, 2022
OBS Linux Vulkan/OpenGL game capture

OBS Linux Vulkan/OpenGL game capture OBS plugin for Vulkan/OpenGL game capture on Linux. Requires OBS with EGL support (currently unreleased, you need

David Rosca 216 Aug 12, 2022
NodeEditor basiced on Qt and OpenGL/CV

Vapour 基于Qt的轻量级的节点式几何建模工具与shader材质生成工具 可能加入图像处理(合成)的功能 日志: 2021-5-17: 完成背景绘制,右键菜单,节点连线,节点删除,初步拓扑排序 (细节)连线位于节点层级之下,使用lambda处理右键菜单slot,节点创建生成在鼠标位置处 2021

Cuimi 15 Aug 11, 2022
Orbit is a multiplatform-focus graphical engine build on top of OpenGl, ImGui

Orbit Engine Orbit is a multiplatform-focus graphical engine build on top of OpenGl, ImGui and more... The development of the engine is documented via

Madsycode 11 Jul 3, 2021
Axel Gneiting 1.4k Aug 8, 2022
A 2d Graphing Calculator using OpenGL

glGraph A 2d Graphing Calculator using Modern OpenGL Demo glGraph.mp4 Information This has only been tested on Fedora 34, it should work on other OS's

Nathan Medros 16 Apr 19, 2022
Tetris written with C++ and OpenGL.

Tetrec This is yet another Tetris game, which is in 3D, written using C++ and OpenGL 2.1, aiming at being lightweight towards not-so-beefy computers (

Lê Duy Quang 14 Jan 17, 2022
OpenGL made easy.

SmartGL OpenGL made easy. Demo video: https://youtu.be/zDuNxg4LJ18 (sorry for low-quality recording) For an example of how my engine is used, please r

null 16 Jun 21, 2022
This is a openGL cube demo program. It was made as a tech demo using PVR_PSP2 Driver layer GPU libraries.

OpenGL Cube Demo using PVR_PSP2 Driver layer GPU libraries This is a openGL cube demo program. It was made as a tech demo using PVR_PSP2 Driver layer

David Cantu 5 Oct 31, 2021
A simple single point light shadow mapping with OpenGL 3.3 and C++

omni-directional-light-example Using OpenGL 3.3 with C++ Basically a single light map, no lighting model was used Usage Build the executable outside A

Mohammad Issawi 4 Feb 10, 2022