Created
December 10, 2024 10:06
-
-
Save maskrosen/f0ae3a0e39026375814e69542df2e56d to your computer and use it in GitHub Desktop.
Some extended Raylib rendering functions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
raylib-render-extras - Some render functions that avoids extra overhead when rendering multiple instances. | |
Tested with Raylib 4.2 might work with other versions | |
See https://youtu.be/2EOjGwhYXds?si=fcJzO1VjbLQ1dzRW&t=319 for a brief explanation of the functions | |
Copyright (c) 2024 Lingon Studios | |
The code is derived from Raylib Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) | |
This software is provided "as-is", without any express or implied warranty. In no event | |
will the authors be held liable for any damages arising from the use of this software. | |
Permission is granted to anyone to use this software for any purpose, including commercial | |
applications, and to alter it and redistribute it freely, subject to the following restrictions: | |
1. The origin of this software must not be misrepresented; you must not claim that you | |
wrote the original software. If you use this software in a product, an acknowledgment | |
in the product documentation would be appreciated but is not required. | |
2. Altered source versions must be plainly marked as such, and must not be misrepresented | |
as being the original software. | |
3. This notice may not be removed or altered from any source distribution. | |
*/ | |
#ifndef RAYLIB_RENDER_EXTRAS_H | |
#define RAYLIB_RENDER_EXTRAS_H | |
inline void BeginDrawMeshesWithSameMaterial(Material material) | |
{ | |
// Bind shader program | |
rlEnableShader(material.shader.id); | |
// Send required data to shader (matrices, values) | |
//----------------------------------------------------- | |
// Upload to shader material.colDiffuse | |
if (material.shader.locs[SHADER_LOC_COLOR_DIFFUSE] != -1) | |
{ | |
float values[4] = { | |
(float)material.maps[MATERIAL_MAP_DIFFUSE].color.r/255.0f, | |
(float)material.maps[MATERIAL_MAP_DIFFUSE].color.g/255.0f, | |
(float)material.maps[MATERIAL_MAP_DIFFUSE].color.b/255.0f, | |
(float)material.maps[MATERIAL_MAP_DIFFUSE].color.a/255.0f | |
}; | |
rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_DIFFUSE], values, SHADER_UNIFORM_VEC4, 1); | |
} | |
// Upload to shader material.colSpecular (if location available) | |
if (material.shader.locs[SHADER_LOC_COLOR_SPECULAR] != -1) | |
{ | |
float values[4] = { | |
(float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.r/255.0f, | |
(float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.g/255.0f, | |
(float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.b/255.0f, | |
(float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.a/255.0f | |
}; | |
rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_SPECULAR], values, SHADER_UNIFORM_VEC4, 1); | |
} | |
Matrix matView = rlGetMatrixModelview(); | |
Matrix matProjection = rlGetMatrixProjection(); | |
// Upload view and projection matrices (if locations available) | |
if (material.shader.locs[SHADER_LOC_MATRIX_VIEW] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_VIEW], matView); | |
if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], matProjection); | |
// Bind active texture maps (if available) | |
for (i32 i = 0; i < MAX_MATERIAL_MAPS; i++) | |
{ | |
if (material.maps[i].texture.id > 0) | |
{ | |
// Select current shader texture slot | |
rlActiveTextureSlot(i); | |
// Enable texture for active slot | |
if ((i == MATERIAL_MAP_IRRADIANCE) || | |
(i == MATERIAL_MAP_PREFILTER) || | |
(i == MATERIAL_MAP_CUBEMAP)) rlEnableTextureCubemap(material.maps[i].texture.id); | |
else rlEnableTexture(material.maps[i].texture.id); | |
rlSetUniform(material.shader.locs[SHADER_LOC_MAP_DIFFUSE + i], &i, SHADER_UNIFORM_INT, 1); | |
} | |
} | |
} | |
inline void DrawMeshesWithSameMaterial(Mesh mesh, Material material, Matrix transform) | |
{ | |
// Get a copy of current matrices to work with, | |
// just in case stereo render is required and we need to modify them | |
// NOTE: At this point the modelview matrix just contains the view matrix (camera) | |
// That's because BeginMode3D() sets it and there is no model-drawing function | |
// that modifies it, all use rlPushMatrix() and rlPopMatrix() | |
Matrix matModel = MatrixIdentity(); | |
Matrix matView = rlGetMatrixModelview(); | |
Matrix matModelView = MatrixIdentity(); | |
Matrix matProjection = rlGetMatrixProjection(); | |
// Model transformation matrix is send to shader uniform location: SHADER_LOC_MATRIX_MODEL | |
if (material.shader.locs[SHADER_LOC_MATRIX_MODEL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MODEL], transform); | |
// Accumulate several model transformations: | |
// transform: model transformation provided (includes DrawModel() params combined with model.transform) | |
// rlGetMatrixTransform(): rlgl internal transform matrix due to push/pop matrix stack | |
matModel = MatrixMultiply(transform, rlGetMatrixTransform()); | |
// Get model-view matrix | |
matModelView = MatrixMultiply(matModel, matView); | |
// Upload model normal matrix (if locations available) | |
if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel))); | |
//----------------------------------------------------- | |
// Try binding vertex array objects (VAO) or use VBOs if not possible | |
// WARNING: UploadMesh() enables all vertex attributes available in mesh and sets default attribute values | |
// for shader expected vertex attributes that are not provided by the mesh (i.e. colors) | |
// This could be a dangerous approach because different meshes with different shaders can enable/disable some attributes | |
if (!rlEnableVertexArray(mesh.vaoId)) | |
{ | |
// Bind mesh VBO data: vertex position (shader-location = 0) | |
rlEnableVertexBuffer(mesh.vboId[0]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION], 3, RL_FLOAT, 0, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION]); | |
// Bind mesh VBO data: vertex texcoords (shader-location = 1) | |
rlEnableVertexBuffer(mesh.vboId[1]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, RL_FLOAT, 0, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01]); | |
if (material.shader.locs[SHADER_LOC_VERTEX_NORMAL] != -1) | |
{ | |
// Bind mesh VBO data: vertex normals (shader-location = 2) | |
rlEnableVertexBuffer(mesh.vboId[2]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL], 3, RL_FLOAT, 0, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL]); | |
} | |
// Bind mesh VBO data: vertex colors (shader-location = 3, if available) | |
if (material.shader.locs[SHADER_LOC_VERTEX_COLOR] != -1) | |
{ | |
if (mesh.vboId[3] != 0) | |
{ | |
rlEnableVertexBuffer(mesh.vboId[3]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR], 4, RL_UNSIGNED_BYTE, 1, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); | |
} | |
else | |
{ | |
// Set default value for defined vertex attribute in shader but not provided by mesh | |
// WARNING: It could result in GPU undefined behaviour | |
float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; | |
rlSetVertexAttributeDefault(material.shader.locs[SHADER_LOC_VERTEX_COLOR], value, SHADER_ATTRIB_VEC4, 4); | |
rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); | |
} | |
} | |
// Bind mesh VBO data: vertex tangents (shader-location = 4, if available) | |
if (material.shader.locs[SHADER_LOC_VERTEX_TANGENT] != -1) | |
{ | |
rlEnableVertexBuffer(mesh.vboId[4]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT], 4, RL_FLOAT, 0, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT]); | |
} | |
// Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available) | |
if (material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] != -1) | |
{ | |
rlEnableVertexBuffer(mesh.vboId[5]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02], 2, RL_FLOAT, 0, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]); | |
} | |
if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[6]); | |
} | |
// WARNING: Disable vertex attribute color input if mesh can not provide that data (despite location being enabled in shader) | |
if (mesh.vboId[3] == 0 && material.shader.locs[SHADER_LOC_VERTEX_COLOR] != -1) | |
{ | |
rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); | |
} | |
i32 eyeCount = 1; | |
if (rlIsStereoRenderEnabled()) eyeCount = 2; | |
for (i32 eye = 0; eye < eyeCount; eye++) | |
{ | |
// Calculate model-view-projection matrix (MVP) | |
Matrix matModelViewProjection = MatrixIdentity(); | |
if (eyeCount == 1) matModelViewProjection = MatrixMultiply(matModelView, matProjection); | |
else | |
{ | |
// Setup current eye viewport (half screen width) | |
rlViewport(eye*rlGetFramebufferWidth()/2, 0, rlGetFramebufferWidth()/2, rlGetFramebufferHeight()); | |
matModelViewProjection = MatrixMultiply(MatrixMultiply(matModelView, rlGetMatrixViewOffsetStereo(eye)), rlGetMatrixProjectionStereo(eye)); | |
} | |
// Send combined model-view-projection matrix to shader | |
rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MVP], matModelViewProjection); | |
// Draw mesh | |
if (mesh.indices != NULL) rlDrawVertexArrayElements(0, mesh.triangleCount*3, 0); | |
else rlDrawVertexArray(0, mesh.vertexCount); | |
} | |
} | |
void EndDrawMeshesWithSameMaterial(Material material, Matrix matView, Matrix matProjection) | |
{ | |
// Unbind all binded texture maps | |
for (i32 i = 0; i < MAX_MATERIAL_MAPS; i++) | |
{ | |
if (material.maps[i].texture.id > 0) | |
{ | |
// Select current shader texture slot | |
rlActiveTextureSlot(i); | |
// Disable texture for active slot | |
if ((i == MATERIAL_MAP_IRRADIANCE) || | |
(i == MATERIAL_MAP_PREFILTER) || | |
(i == MATERIAL_MAP_CUBEMAP)) rlDisableTextureCubemap(); | |
else rlDisableTexture(); | |
} | |
} | |
// Disable all possible vertex array objects (or VBOs) | |
rlDisableVertexArray(); | |
rlDisableVertexBuffer(); | |
rlDisableVertexBufferElement(); | |
// Disable shader program | |
rlDisableShader(); | |
// Restore rlgl internal modelview and projection matrices | |
rlSetMatrixModelview(matView); | |
rlSetMatrixProjection(matProjection); | |
} | |
// Draw multiple mesh instances with material and different transforms without allocating new memory | |
inline void DrawMeshInstancedNoAlloc(Mesh mesh, Material material, float16* instanceTransforms, i32 instances) | |
{ | |
// Instancing required variables | |
u32 instancesVboId = 0; | |
// Bind shader program | |
rlEnableShader(material.shader.id); | |
// Send required data to shader (matrices, values) | |
//----------------------------------------------------- | |
// Upload to shader material.colDiffuse | |
if (material.shader.locs[SHADER_LOC_COLOR_DIFFUSE] != -1) | |
{ | |
float values[4] = { | |
(float)material.maps[MATERIAL_MAP_DIFFUSE].color.r / 255.0f, | |
(float)material.maps[MATERIAL_MAP_DIFFUSE].color.g / 255.0f, | |
(float)material.maps[MATERIAL_MAP_DIFFUSE].color.b / 255.0f, | |
(float)material.maps[MATERIAL_MAP_DIFFUSE].color.a / 255.0f | |
}; | |
rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_DIFFUSE], values, SHADER_UNIFORM_VEC4, 1); | |
} | |
// Upload to shader material.colSpecular (if location available) | |
if (material.shader.locs[SHADER_LOC_COLOR_SPECULAR] != -1) | |
{ | |
float values[4] = { | |
(float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.r / 255.0f, | |
(float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.g / 255.0f, | |
(float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.b / 255.0f, | |
(float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.a / 255.0f | |
}; | |
rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_SPECULAR], values, SHADER_UNIFORM_VEC4, 1); | |
} | |
// Get a copy of current matrices to work with, | |
// just in case stereo render is required, and we need to modify them | |
// NOTE: At this point the modelview matrix just contains the view matrix (camera) | |
// That's because BeginMode3D() sets it and there is no model-drawing function | |
// that modifies it, all use rlPushMatrix() and rlPopMatrix() | |
Matrix matModel = MatrixIdentity(); | |
Matrix matView = rlGetMatrixModelview(); | |
Matrix matModelView = MatrixIdentity(); | |
Matrix matProjection = rlGetMatrixProjection(); | |
// Upload view and projection matrices (if locations available) | |
if (material.shader.locs[SHADER_LOC_MATRIX_VIEW] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_VIEW], matView); | |
if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], matProjection); | |
// Enable mesh VAO to attach new buffer | |
rlEnableVertexArray(mesh.vaoId); | |
// This could alternatively use a static VBO and either glMapBuffer() or glBufferSubData(). | |
// It isn't clear which would be reliably faster in all cases and on all platforms, | |
// anecdotally glMapBuffer() seems very slow (syncs) while glBufferSubData() seems | |
// no faster, since we're transferring all the transform matrices anyway | |
instancesVboId = rlLoadVertexBuffer(instanceTransforms, instances * sizeof(float16), false); | |
// Instances transformation matrices are send to shader attribute location: SHADER_LOC_MATRIX_MODEL | |
for (u32 i = 0; i < 4; i++) | |
{ | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i, 4, RL_FLOAT, 0, sizeof(Matrix), (void*)(i * sizeof(Vector4))); | |
rlSetVertexAttributeDivisor(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i, 1); | |
} | |
rlDisableVertexBuffer(); | |
rlDisableVertexArray(); | |
// Accumulate internal matrix transform (push/pop) and view matrix | |
// NOTE: In this case, model instance transformation must be computed in the shader | |
matModelView = MatrixMultiply(rlGetMatrixTransform(), matView); | |
// Upload model normal matrix (if locations available) | |
if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel))); | |
//----------------------------------------------------- | |
// Bind active texture maps (if available) | |
for (i32 i = 0; i < MAX_MATERIAL_MAPS; i++) | |
{ | |
if (material.maps[i].texture.id > 0) | |
{ | |
// Select current shader texture slot | |
rlActiveTextureSlot(i); | |
// Enable texture for active slot | |
if ((i == MATERIAL_MAP_IRRADIANCE) || | |
(i == MATERIAL_MAP_PREFILTER) || | |
(i == MATERIAL_MAP_CUBEMAP)) rlEnableTextureCubemap(material.maps[i].texture.id); | |
else rlEnableTexture(material.maps[i].texture.id); | |
rlSetUniform(material.shader.locs[SHADER_LOC_MAP_DIFFUSE + i], &i, SHADER_UNIFORM_INT, 1); | |
} | |
} | |
// Try binding vertex array objects (VAO) | |
// or use VBOs if not possible | |
if (!rlEnableVertexArray(mesh.vaoId)) | |
{ | |
// Bind mesh VBO data: vertex position (shader-location = 0) | |
rlEnableVertexBuffer(mesh.vboId[0]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION], 3, RL_FLOAT, 0, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION]); | |
// Bind mesh VBO data: vertex texcoords (shader-location = 1) | |
rlEnableVertexBuffer(mesh.vboId[1]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, RL_FLOAT, 0, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01]); | |
if (material.shader.locs[SHADER_LOC_VERTEX_NORMAL] != -1) | |
{ | |
// Bind mesh VBO data: vertex normals (shader-location = 2) | |
rlEnableVertexBuffer(mesh.vboId[2]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL], 3, RL_FLOAT, 0, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL]); | |
} | |
// Bind mesh VBO data: vertex colors (shader-location = 3, if available) | |
if (material.shader.locs[SHADER_LOC_VERTEX_COLOR] != -1) | |
{ | |
if (mesh.vboId[3] != 0) | |
{ | |
rlEnableVertexBuffer(mesh.vboId[3]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR], 4, RL_UNSIGNED_BYTE, 1, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); | |
} | |
else | |
{ | |
// Set default value for unused attribute | |
// NOTE: Required when using default shader and no VAO support | |
float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; | |
rlSetVertexAttributeDefault(material.shader.locs[SHADER_LOC_VERTEX_COLOR], value, SHADER_ATTRIB_VEC4, 4); | |
rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); | |
} | |
} | |
// Bind mesh VBO data: vertex tangents (shader-location = 4, if available) | |
if (material.shader.locs[SHADER_LOC_VERTEX_TANGENT] != -1) | |
{ | |
rlEnableVertexBuffer(mesh.vboId[4]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT], 4, RL_FLOAT, 0, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT]); | |
} | |
// Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available) | |
if (material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] != -1) | |
{ | |
rlEnableVertexBuffer(mesh.vboId[5]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02], 2, RL_FLOAT, 0, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]); | |
} | |
if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[6]); | |
} | |
// WARNING: Disable vertex attribute color input if mesh can not provide that data (despite location being enabled in shader) | |
if (mesh.vboId[3] == 0 && material.shader.locs[SHADER_LOC_VERTEX_COLOR] != -1) rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); | |
i32 eyeCount = 1; | |
if (rlIsStereoRenderEnabled()) eyeCount = 2; | |
for (i32 eye = 0; eye < eyeCount; eye++) | |
{ | |
// Calculate model-view-projection matrix (MVP) | |
Matrix matModelViewProjection = MatrixIdentity(); | |
if (eyeCount == 1) matModelViewProjection = MatrixMultiply(matModelView, matProjection); | |
else | |
{ | |
// Setup current eye viewport (half screen width) | |
rlViewport(eye * rlGetFramebufferWidth() / 2, 0, rlGetFramebufferWidth() / 2, rlGetFramebufferHeight()); | |
matModelViewProjection = MatrixMultiply(MatrixMultiply(matModelView, rlGetMatrixViewOffsetStereo(eye)), rlGetMatrixProjectionStereo(eye)); | |
} | |
// Send combined model-view-projection matrix to shader | |
rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MVP], matModelViewProjection); | |
// Draw mesh instanced | |
if (mesh.indices != NULL) rlDrawVertexArrayElementsInstanced(0, mesh.triangleCount * 3, 0, instances); | |
else rlDrawVertexArrayInstanced(0, mesh.vertexCount, instances); | |
} | |
// Unbind all bound texture maps | |
for (i32 i = 0; i < MAX_MATERIAL_MAPS; i++) | |
{ | |
if (material.maps[i].texture.id > 0) | |
{ | |
// Select current shader texture slot | |
rlActiveTextureSlot(i); | |
// Disable texture for active slot | |
if ((i == MATERIAL_MAP_IRRADIANCE) || | |
(i == MATERIAL_MAP_PREFILTER) || | |
(i == MATERIAL_MAP_CUBEMAP)) rlDisableTextureCubemap(); | |
else rlDisableTexture(); | |
} | |
} | |
// Disable all possible vertex array objects (or VBOs) | |
rlDisableVertexArray(); | |
rlDisableVertexBuffer(); | |
rlDisableVertexBufferElement(); | |
// Disable shader program | |
rlDisableShader(); | |
// Remove instance transforms buffer | |
rlUnloadVertexBuffer(instancesVboId); | |
} | |
// Draw multiple mesh instances with material and different transforms and colours without allocating new memory | |
inline void DrawMeshInstancedWithColorNoAlloc(Mesh mesh, Material material, float16* instanceTransforms, Color* instanceColors, u32 colorBufferId, i32 instances) | |
{ | |
// Instancing required variables | |
u32 instancesVboId = 0; | |
u32 instancesVboColorId = 0; | |
// Bind shader program | |
rlEnableShader(material.shader.id); | |
// Send required data to shader (matrices, values) | |
//----------------------------------------------------- | |
// Upload to shader material.colDiffuse | |
if (material.shader.locs[SHADER_LOC_COLOR_DIFFUSE] != -1) | |
{ | |
float values[4] = { | |
(float)material.maps[MATERIAL_MAP_DIFFUSE].color.r / 255.0f, | |
(float)material.maps[MATERIAL_MAP_DIFFUSE].color.g / 255.0f, | |
(float)material.maps[MATERIAL_MAP_DIFFUSE].color.b / 255.0f, | |
(float)material.maps[MATERIAL_MAP_DIFFUSE].color.a / 255.0f | |
}; | |
rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_DIFFUSE], values, SHADER_UNIFORM_VEC4, 1); | |
} | |
// Upload to shader material.colSpecular (if location available) | |
if (material.shader.locs[SHADER_LOC_COLOR_SPECULAR] != -1) | |
{ | |
float values[4] = { | |
(float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.r / 255.0f, | |
(float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.g / 255.0f, | |
(float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.b / 255.0f, | |
(float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.a / 255.0f | |
}; | |
rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_SPECULAR], values, SHADER_UNIFORM_VEC4, 1); | |
} | |
// Get a copy of current matrices to work with, | |
// just in case stereo render is required, and we need to modify them | |
// NOTE: At this point the modelview matrix just contains the view matrix (camera) | |
// That's because BeginMode3D() sets it and there is no model-drawing function | |
// that modifies it, all use rlPushMatrix() and rlPopMatrix() | |
Matrix matModel = MatrixIdentity(); | |
Matrix matView = rlGetMatrixModelview(); | |
Matrix matModelView = MatrixIdentity(); | |
Matrix matProjection = rlGetMatrixProjection(); | |
// Upload view and projection matrices (if locations available) | |
if (material.shader.locs[SHADER_LOC_MATRIX_VIEW] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_VIEW], matView); | |
if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], matProjection); | |
// Enable mesh VAO to attach new buffer | |
rlEnableVertexArray(mesh.vaoId); | |
// This could alternatively use a static VBO and either glMapBuffer() or glBufferSubData(). | |
// It isn't clear which would be reliably faster in all cases and on all platforms, | |
// anecdotally glMapBuffer() seems very slow (syncs) while glBufferSubData() seems | |
// no faster, since we're transferring all the transform matrices anyway | |
instancesVboId = rlLoadVertexBuffer(instanceTransforms, instances * sizeof(float16), false); | |
// Instances transformation matrices are send to shader attribute location: SHADER_LOC_MATRIX_MODEL | |
for (u32 i = 0; i < 4; i++) | |
{ | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i, 4, GL_HALF_FLOAT, 0, sizeof(float16), (void*)(i * sizeof(Float16b) * 4)); | |
rlSetVertexAttributeDivisor(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i, 1); | |
} | |
instancesVboColorId = rlLoadVertexBuffer(instanceColors, instances * sizeof(Color), false); | |
// Instances colors are send to shader attribute location: | |
rlEnableVertexAttribute(colorBufferId); | |
glVertexAttribIPointer(colorBufferId, 1, RL_INT, sizeof(Color), (void*)(0 * sizeof(Color))); | |
rlSetVertexAttributeDivisor(colorBufferId, 1); | |
rlDisableVertexBuffer(); | |
rlDisableVertexArray(); | |
// Accumulate internal matrix transform (push/pop) and view matrix | |
// NOTE: In this case, model instance transformation must be computed in the shader | |
matModelView = MatrixMultiply(rlGetMatrixTransform(), matView); | |
// Upload model normal matrix (if locations available) | |
if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel))); | |
//----------------------------------------------------- | |
// Bind active texture maps (if available) | |
for (i32 i = 0; i < MAX_MATERIAL_MAPS; i++) | |
{ | |
if (material.maps[i].texture.id > 0) | |
{ | |
// Select current shader texture slot | |
rlActiveTextureSlot(i); | |
// Enable texture for active slot | |
if ((i == MATERIAL_MAP_IRRADIANCE) || | |
(i == MATERIAL_MAP_PREFILTER) || | |
(i == MATERIAL_MAP_CUBEMAP)) rlEnableTextureCubemap(material.maps[i].texture.id); | |
else rlEnableTexture(material.maps[i].texture.id); | |
rlSetUniform(material.shader.locs[SHADER_LOC_MAP_DIFFUSE + i], &i, SHADER_UNIFORM_INT, 1); | |
} | |
} | |
// Try binding vertex array objects (VAO) | |
// or use VBOs if not possible | |
if (!rlEnableVertexArray(mesh.vaoId)) | |
{ | |
// Bind mesh VBO data: vertex position (shader-location = 0) | |
rlEnableVertexBuffer(mesh.vboId[0]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION], 3, RL_FLOAT, 0, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION]); | |
// Bind mesh VBO data: vertex texcoords (shader-location = 1) | |
rlEnableVertexBuffer(mesh.vboId[1]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, RL_FLOAT, 0, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01]); | |
if (material.shader.locs[SHADER_LOC_VERTEX_NORMAL] != -1) | |
{ | |
// Bind mesh VBO data: vertex normals (shader-location = 2) | |
rlEnableVertexBuffer(mesh.vboId[2]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL], 3, RL_FLOAT, 0, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL]); | |
} | |
// Bind mesh VBO data: vertex colors (shader-location = 3, if available) | |
if (material.shader.locs[SHADER_LOC_VERTEX_COLOR] != -1) | |
{ | |
if (mesh.vboId[3] != 0) | |
{ | |
rlEnableVertexBuffer(mesh.vboId[3]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR], 4, RL_UNSIGNED_BYTE, 1, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); | |
} | |
else | |
{ | |
// Set default value for unused attribute | |
// NOTE: Required when using default shader and no VAO support | |
float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; | |
rlSetVertexAttributeDefault(material.shader.locs[SHADER_LOC_VERTEX_COLOR], value, SHADER_ATTRIB_VEC4, 4); | |
rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); | |
} | |
} | |
// Bind mesh VBO data: vertex tangents (shader-location = 4, if available) | |
if (material.shader.locs[SHADER_LOC_VERTEX_TANGENT] != -1) | |
{ | |
rlEnableVertexBuffer(mesh.vboId[4]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT], 4, RL_FLOAT, 0, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT]); | |
} | |
// Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available) | |
if (material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] != -1) | |
{ | |
rlEnableVertexBuffer(mesh.vboId[5]); | |
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02], 2, RL_FLOAT, 0, 0, 0); | |
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]); | |
} | |
if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[6]); | |
} | |
// WARNING: Disable vertex attribute color input if mesh can not provide that data (despite location being enabled in shader) | |
if (mesh.vboId[3] == 0) rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); | |
int eyeCount = 1; | |
if (rlIsStereoRenderEnabled()) eyeCount = 2; | |
for (i32 eye = 0; eye < eyeCount; eye++) | |
{ | |
// Calculate model-view-projection matrix (MVP) | |
Matrix matModelViewProjection = MatrixIdentity(); | |
if (eyeCount == 1) matModelViewProjection = MatrixMultiply(matModelView, matProjection); | |
else | |
{ | |
// Setup current eye viewport (half screen width) | |
rlViewport(eye * rlGetFramebufferWidth() / 2, 0, rlGetFramebufferWidth() / 2, rlGetFramebufferHeight()); | |
matModelViewProjection = MatrixMultiply(MatrixMultiply(matModelView, rlGetMatrixViewOffsetStereo(eye)), rlGetMatrixProjectionStereo(eye)); | |
} | |
// Send combined model-view-projection matrix to shader | |
rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MVP], matModelViewProjection); | |
// Draw mesh instanced | |
if (mesh.indices != NULL) rlDrawVertexArrayElementsInstanced(0, mesh.triangleCount * 3, 0, instances); | |
else rlDrawVertexArrayInstanced(0, mesh.vertexCount, instances); | |
} | |
// Unbind all bound texture maps | |
for (i32 i = 0; i < MAX_MATERIAL_MAPS; i++) | |
{ | |
if (material.maps[i].texture.id > 0) | |
{ | |
// Select current shader texture slot | |
rlActiveTextureSlot(i); | |
// Disable texture for active slot | |
if ((i == MATERIAL_MAP_IRRADIANCE) || | |
(i == MATERIAL_MAP_PREFILTER) || | |
(i == MATERIAL_MAP_CUBEMAP)) rlDisableTextureCubemap(); | |
else rlDisableTexture(); | |
} | |
} | |
// Disable all possible vertex array objects (or VBOs) | |
rlDisableVertexArray(); | |
rlDisableVertexBuffer(); | |
rlDisableVertexBufferElement(); | |
// Disable shader program | |
rlDisableShader(); | |
// Remove instance transforms buffer | |
rlUnloadVertexBuffer(instancesVboId); | |
rlUnloadVertexBuffer(instancesVboColorId); | |
} | |
#endif RAYLIB_RENDER_EXTRAS_H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
Used together with DrawMeshInstancedWithColorNoAlloc to render instanced meshes with different colours | |
Copyright (c) 2024 Lingon Studios | |
The code is derived from raylib Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) | |
This software is provided "as-is", without any express or implied warranty. In no event | |
will the authors be held liable for any damages arising from the use of this software. | |
Permission is granted to anyone to use this software for any purpose, including commercial | |
applications, and to alter it and redistribute it freely, subject to the following restrictions: | |
1. The origin of this software must not be misrepresented; you must not claim that you | |
wrote the original software. If you use this software in a product, an acknowledgment | |
in the product documentation would be appreciated but is not required. | |
2. Altered source versions must be plainly marked as such, and must not be misrepresented | |
as being the original software. | |
3. This notice may not be removed or altered from any source distribution. | |
*/ | |
#version 330 | |
// Input vertex attributes | |
in vec3 vertexPosition; | |
in vec2 vertexTexCoord; | |
in vec3 vertexNormal; | |
in vec4 vertexColor; | |
in int instanceColor; | |
in mat4 instanceTransform; | |
// Input uniform values | |
uniform mat4 mvp; | |
uniform mat4 matNormal; | |
uniform mat4 matLight; | |
// Output vertex attributes (to fragment shader) | |
out vec3 fragPosition; | |
out vec2 fragTexCoord; | |
out vec4 fragColor; | |
out vec3 fragNormal; | |
out vec4 shadowPos; | |
// NOTE: Add here your custom variables | |
void main() | |
{ | |
// Compute MVP for current instance | |
mat4 mvpi = mvp*instanceTransform; | |
// Send vertex attributes to fragment shader | |
vec3 fragPositionLocal = vec3(instanceTransform*vec4(vertexPosition, 1.0)); | |
fragTexCoord = vertexTexCoord; | |
float iRed = instanceColor & 0xff; | |
float iGreen = (instanceColor >> 8) & 0xff; | |
float iBlue = (instanceColor >> 16) & 0xff; | |
float iAlpha = (instanceColor >> 24) & 0xff; | |
vec4 instanceColorUnpacked = vec4(iRed / 255.0, iGreen / 255.0, iBlue / 255.0, iAlpha / 255.0); | |
//instanceColorUnpacked.a = 1.0; | |
fragColor = vertexColor * instanceColorUnpacked; | |
mat3 normalMatrix = transpose(inverse(mat3(instanceTransform))); | |
fragNormal = normalize(normalMatrix * vertexNormal); | |
shadowPos = matLight * vec4(fragPositionLocal, 1.0); | |
fragPosition = fragPositionLocal; | |
// Calculate final vertex position | |
gl_Position = mvpi*vec4(vertexPosition, 1.0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment