/********************************************************************************************** * * raylib.models * * Basic functions to draw 3d shapes and load/draw 3d models (.OBJ) * * Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) * * 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. * **********************************************************************************************/ #include "raylib.h" #if defined(PLATFORM_ANDROID) #include "utils.h" // Android fopen function map #endif #include // Standard input/output functions, used to read model files data #include // Declares malloc() and free() for memory management #include // Required for strcmp() #include // Used for sin, cos, tan #include "raymath.h" // Required for data type Matrix and Matrix functions #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- // Nop... //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- // ... //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- extern unsigned int whiteTexture; //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- static float GetHeightValue(Color pixel); static VertexData LoadOBJ(const char *fileName); //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- // Draw cube // NOTE: Cube position is the center position void DrawCube(Vector3 position, float width, float height, float lenght, Color color) { float x = 0.0f; float y = 0.0f; float z = 0.0f; rlPushMatrix(); // NOTE: Be careful! Function order matters (rotate -> scale -> translate) rlTranslatef(position.x, position.y, position.z); //rlScalef(2.0f, 2.0f, 2.0f); //rlRotatef(45, 0, 1, 0); rlBegin(RL_TRIANGLES); rlColor4ub(color.r, color.g, color.b, color.a); // Front Face ----------------------------------------------------- rlVertex3f(x-width/2, y-height/2, z+lenght/2); // Bottom Left rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Bottom Right rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Top Left rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Top Right rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Top Left rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Bottom Right // Back Face ------------------------------------------------------ rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Bottom Left rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Bottom Right rlVertex3f(x+width/2, y+height/2, z-lenght/2); // Top Right rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Bottom Right rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left // Top Face ------------------------------------------------------- rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Bottom Left rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Bottom Right rlVertex3f(x+width/2, y+height/2, z-lenght/2); // Top Right rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Bottom Right // Bottom Face ---------------------------------------------------- rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Top Left rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Bottom Right rlVertex3f(x-width/2, y-height/2, z+lenght/2); // Bottom Left rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Top Right rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Bottom Right rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Top Left // Right face ----------------------------------------------------- rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Bottom Right rlVertex3f(x+width/2, y+height/2, z-lenght/2); // Top Right rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Top Left rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Bottom Left rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Bottom Right rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Top Left // Left Face ------------------------------------------------------ rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Bottom Right rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Top Left rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Right rlVertex3f(x-width/2, y-height/2, z+lenght/2); // Bottom Left rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Top Left rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Bottom Right rlEnd(); rlPopMatrix(); } // Draw cube (Vector version) void DrawCubeV(Vector3 position, Vector3 size, Color color) { DrawCube(position, size.x, size.y, size.z, color); } // Draw cube wires void DrawCubeWires(Vector3 position, float width, float height, float lenght, Color color) { float x = 0.0f; float y = 0.0f; float z = 0.0f; rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); //rlRotatef(45, 0, 1, 0); rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); // Front Face ----------------------------------------------------- // Bottom Line rlVertex3f(x-width/2, y-height/2, z+lenght/2); // Bottom Left rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Bottom Right // Left Line rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Bottom Right rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Top Right // Top Line rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Top Right rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Top Left // Right Line rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Top Left rlVertex3f(x-width/2, y-height/2, z+lenght/2); // Bottom Left // Back Face ------------------------------------------------------ // Bottom Line rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Bottom Left rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Bottom Right // Left Line rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Bottom Right rlVertex3f(x+width/2, y+height/2, z-lenght/2); // Top Right // Top Line rlVertex3f(x+width/2, y+height/2, z-lenght/2); // Top Right rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left // Right Line rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Bottom Left // Top Face ------------------------------------------------------- // Left Line rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Top Left Front rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left Back // Right Line rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Top Right Front rlVertex3f(x+width/2, y+height/2, z-lenght/2); // Top Right Back // Bottom Face --------------------------------------------------- // Left Line rlVertex3f(x-width/2, y-height/2, z+lenght/2); // Top Left Front rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Top Left Back // Right Line rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Top Right Front rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Top Right Back rlEnd(); rlPopMatrix(); } // Draw cube // NOTE: Cube position is the center position void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float lenght, Color color) { float x = position.x; float y = position.y; float z = position.z; rlEnableTexture(texture.id); //rlPushMatrix(); // NOTE: Be careful! Function order matters (scale, translate, rotate) //rlScalef(2.0f, 2.0f, 2.0f); //rlTranslatef(2.0f, 0.0f, 0.0f); //rlRotatef(45, 0, 1, 0); rlBegin(RL_QUADS); rlColor4ub(color.r, color.g, color.b, color.a); // Front Face rlNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x-width/2, y-height/2, z+lenght/2); // Bottom Left Of The Texture and Quad rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Bottom Right Of The Texture and Quad rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Top Right Of The Texture and Quad rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Top Left Of The Texture and Quad // Back Face rlNormal3f( 0.0f, 0.0f,-1.0f); // Normal Pointing Away From Viewer rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Bottom Right Of The Texture and Quad rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Right Of The Texture and Quad rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z-lenght/2); // Top Left Of The Texture and Quad rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Bottom Left Of The Texture and Quad // Top Face rlNormal3f( 0.0f, 1.0f, 0.0f); // Normal Pointing Up rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left Of The Texture and Quad rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Bottom Left Of The Texture and Quad rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Bottom Right Of The Texture and Quad rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z-lenght/2); // Top Right Of The Texture and Quad // Bottom Face rlNormal3f( 0.0f,-1.0f, 0.0f); // Normal Pointing Down rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Top Right Of The Texture and Quad rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Top Left Of The Texture and Quad rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Bottom Left Of The Texture and Quad rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x-width/2, y-height/2, z+lenght/2); // Bottom Right Of The Texture and Quad // Right face rlNormal3f( 1.0f, 0.0f, 0.0f); // Normal Pointing Right rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Bottom Right Of The Texture and Quad rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z-lenght/2); // Top Right Of The Texture and Quad rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Top Left Of The Texture and Quad rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Bottom Left Of The Texture and Quad // Left Face rlNormal3f(-1.0f, 0.0f, 0.0f); // Normal Pointing Left rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Bottom Left Of The Texture and Quad rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x-width/2, y-height/2, z+lenght/2); // Bottom Right Of The Texture and Quad rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Top Right Of The Texture and Quad rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left Of The Texture and Quad rlEnd(); //rlPopMatrix(); rlDisableTexture(); } // Draw sphere void DrawSphere(Vector3 centerPos, float radius, Color color) { DrawSphereEx(centerPos, radius, 16, 16, color); } // Draw sphere with extended parameters void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color) { rlPushMatrix(); rlTranslatef(centerPos.x, centerPos.y, centerPos.z); rlScalef(radius, radius, radius); //rlRotatef(rotation, 0, 1, 0); rlBegin(RL_TRIANGLES); rlColor4ub(color.r, color.g, color.b, color.a); for(int i = 0; i < (rings + 2); i++) { for(int j = 0; j < slices; j++) { rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i)) * sin(DEG2RAD*(j*360/slices)), sin(DEG2RAD*(270+(180/(rings + 1))*i)), cos(DEG2RAD*(270+(180/(rings + 1))*i)) * cos(DEG2RAD*(j*360/slices))); rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*((j+1)*360/slices)), sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))), cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*((j+1)*360/slices))); rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*(j*360/slices)), sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))), cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*(j*360/slices))); rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i)) * sin(DEG2RAD*(j*360/slices)), sin(DEG2RAD*(270+(180/(rings + 1))*i)), cos(DEG2RAD*(270+(180/(rings + 1))*i)) * cos(DEG2RAD*(j*360/slices))); rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i))) * sin(DEG2RAD*((j+1)*360/slices)), sin(DEG2RAD*(270+(180/(rings + 1))*(i))), cos(DEG2RAD*(270+(180/(rings + 1))*(i))) * cos(DEG2RAD*((j+1)*360/slices))); rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*((j+1)*360/slices)), sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))), cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*((j+1)*360/slices))); } } rlEnd(); rlPopMatrix(); } // Draw sphere wires void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color) { rlPushMatrix(); rlTranslatef(centerPos.x, centerPos.y, centerPos.z); rlScalef(radius, radius, radius); //rlRotatef(rotation, 0, 1, 0); rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); for(int i = 0; i < (rings + 2); i++) { for(int j = 0; j < slices; j++) { rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i)) * sin(DEG2RAD*(j*360/slices)), sin(DEG2RAD*(270+(180/(rings + 1))*i)), cos(DEG2RAD*(270+(180/(rings + 1))*i)) * cos(DEG2RAD*(j*360/slices))); rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*((j+1)*360/slices)), sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))), cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*((j+1)*360/slices))); rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*((j+1)*360/slices)), sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))), cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*((j+1)*360/slices))); rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*(j*360/slices)), sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))), cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*(j*360/slices))); rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*(j*360/slices)), sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))), cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*(j*360/slices))); rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i)) * sin(DEG2RAD*(j*360/slices)), sin(DEG2RAD*(270+(180/(rings + 1))*i)), cos(DEG2RAD*(270+(180/(rings + 1))*i)) * cos(DEG2RAD*(j*360/slices))); } } rlEnd(); rlPopMatrix(); } // Draw a cylinder // NOTE: It could be also used for pyramid and cone void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color) { if (sides < 3) sides = 3; rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); rlBegin(RL_TRIANGLES); rlColor4ub(color.r, color.g, color.b, color.a); if (radiusTop > 0) { // Draw Body ------------------------------------------------------------------------------------- for(int i = 0; i < 360; i += 360/sides) { rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom); //Bottom Left rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom); //Bottom Right rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusTop, height, cos(DEG2RAD*(i+360/sides)) * radiusTop); //Top Right rlVertex3f(sin(DEG2RAD*i) * radiusTop, height, cos(DEG2RAD*i) * radiusTop); //Top Left rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom); //Bottom Left rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusTop, height, cos(DEG2RAD*(i+360/sides)) * radiusTop); //Top Right } // Draw Cap -------------------------------------------------------------------------------------- for(int i = 0; i < 360; i += 360/sides) { rlVertex3f(0, height, 0); rlVertex3f(sin(DEG2RAD*i) * radiusTop, height, cos(DEG2RAD*i) * radiusTop); rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusTop, height, cos(DEG2RAD*(i+360/sides)) * radiusTop); } } else { // Draw Cone ------------------------------------------------------------------------------------- for(int i = 0; i < 360; i += 360/sides) { rlVertex3f(0, height, 0); rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom); rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom); } } // Draw Base ----------------------------------------------------------------------------------------- for(int i = 0; i < 360; i += 360/sides) { rlVertex3f(0, 0, 0); rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom); rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom); } rlEnd(); rlPopMatrix(); } // Draw a wired cylinder // NOTE: It could be also used for pyramid and cone void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color) { if(sides < 3) sides = 3; rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); for(int i = 0; i < 360; i += 360/sides) { rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom); rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom); rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom); rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusTop, height, cos(DEG2RAD*(i+360/sides)) * radiusTop); rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusTop, height, cos(DEG2RAD*(i+360/sides)) * radiusTop); rlVertex3f(sin(DEG2RAD*i) * radiusTop, height, cos(DEG2RAD*i) * radiusTop); rlVertex3f(sin(DEG2RAD*i) * radiusTop, height, cos(DEG2RAD*i) * radiusTop); rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom); } rlEnd(); rlPopMatrix(); } // Draw a quad void DrawQuad(Vector3 vertices[4], Vector2 textcoords[4], Vector3 normals[4], Color colors[4]) { rlBegin(RL_QUADS); rlColor4ub(colors[0].r, colors[0].g, colors[0].b, colors[0].a); rlNormal3f(normals[0].x, normals[0].y, normals[0].z); rlTexCoord2f(textcoords[0].x, textcoords[0].y); rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z); rlColor4ub(colors[1].r, colors[1].g, colors[1].b, colors[1].a); rlNormal3f(normals[1].x, normals[1].y, normals[1].z); rlTexCoord2f(textcoords[1].x, textcoords[1].y); rlVertex3f(vertices[1].x, vertices[1].y, vertices[1].z); rlColor4ub(colors[2].r, colors[2].g, colors[2].b, colors[2].a); rlNormal3f(normals[2].x, normals[2].y, normals[2].z); rlTexCoord2f(textcoords[2].x, textcoords[2].y); rlVertex3f(vertices[2].x, vertices[2].y, vertices[2].z); rlColor4ub(colors[3].r, colors[3].g, colors[3].b, colors[3].a); rlNormal3f(normals[3].x, normals[3].y, normals[3].z); rlTexCoord2f(textcoords[3].x, textcoords[3].y); rlVertex3f(vertices[3].x, vertices[3].y, vertices[3].z); rlEnd(); } // Draw a plane void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color) { // NOTE: QUADS usage require defining a texture on OpenGL 3.3+ if (rlGetVersion() != OPENGL_11) rlEnableTexture(whiteTexture); // Default white texture // NOTE: Plane is always created on XZ ground and then rotated rlPushMatrix(); rlTranslatef(centerPos.x, centerPos.y, centerPos.z); rlScalef(size.x, 1.0f, size.y); // TODO: Review multiples rotations Gimbal-Lock... use matrix or quaternions... rlRotatef(rotation.x, 1, 0, 0); rlRotatef(rotation.y, 0, 1, 0); rlRotatef(rotation.z, 0, 0, 1); rlBegin(RL_QUADS); rlColor4ub(color.r, color.g, color.b, color.a); rlNormal3f(0.0f, 1.0f, 0.0f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(-0.5f, 0.0f, -0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(-0.5f, 0.0f, 0.5f); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(0.5f, 0.0f, 0.5f); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(0.5f, 0.0f, -0.5f); rlEnd(); rlPopMatrix(); if (rlGetVersion() != OPENGL_11) rlDisableTexture(); } // Draw a plane with divisions // TODO: Test this function void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, int slicesZ, Color color) { float quadWidth = size.x / slicesX; float quadLenght = size.y / slicesZ; float texPieceW = 1 / size.x; float texPieceH = 1 / size.y; // NOTE: Plane is always created on XZ ground and then rotated rlPushMatrix(); rlTranslatef(-size.x / 2, 0.0f, -size.y / 2); rlTranslatef(centerPos.x, centerPos.y, centerPos.z); // TODO: Review multiples rotations Gimbal-Lock... use matrix or quaternions... rlRotatef(rotation.x, 1, 0, 0); rlRotatef(rotation.y, 0, 1, 0); rlRotatef(rotation.z, 0, 0, 1); rlBegin(RL_QUADS); rlColor4ub(color.r, color.g, color.b, color.a); rlNormal3f(0.0f, 1.0f, 0.0f); for (int z = 0; z < slicesZ; z++) { for (int x = 0; x < slicesX; x++) { // Draw the plane quad by quad (with textcoords) rlTexCoord2f((float)x * texPieceW, (float)z * texPieceH); rlVertex3f((float)x * quadWidth, 0.0f, (float)z * quadLenght); rlTexCoord2f((float)x * texPieceW + texPieceW, (float)z * texPieceH); rlVertex3f((float)x * quadWidth + quadWidth, 0.0f, (float)z * quadLenght); rlTexCoord2f((float)x * texPieceW + texPieceW, (float)z * texPieceH + texPieceH); rlVertex3f((float)x * quadWidth + quadWidth, 0.0f, (float)z * quadLenght + quadLenght); rlTexCoord2f((float)x * texPieceW, (float)z * texPieceH + texPieceH); rlVertex3f((float)x * quadWidth, 0.0f, (float)z * quadLenght + quadLenght); } } rlEnd(); rlPopMatrix(); } // Draw a grid centered at (0, 0, 0) void DrawGrid(int slices, float spacing) { int halfSlices = slices / 2; rlBegin(RL_LINES); for(int i = -halfSlices; i <= halfSlices; i++) { if (i == 0) { rlColor3f(0.5f, 0.5f, 0.5f); rlColor3f(0.5f, 0.5f, 0.5f); rlColor3f(0.5f, 0.5f, 0.5f); rlColor3f(0.5f, 0.5f, 0.5f); } else { rlColor3f(0.75f, 0.75f, 0.75f); rlColor3f(0.75f, 0.75f, 0.75f); rlColor3f(0.75f, 0.75f, 0.75f); rlColor3f(0.75f, 0.75f, 0.75f); } rlVertex3f((float)i*spacing, 0.0f, (float)-halfSlices*spacing); rlVertex3f((float)i*spacing, 0.0f, (float)halfSlices*spacing); rlVertex3f((float)-halfSlices*spacing, 0.0f, (float)i*spacing); rlVertex3f((float)halfSlices*spacing, 0.0f, (float)i*spacing); } rlEnd(); } // Draw gizmo void DrawGizmo(Vector3 position) { // NOTE: RGB = XYZ float lenght = 1.0f; rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); //rlRotatef(rotation, 0, 1, 0); rlScalef(lenght, lenght, lenght); rlBegin(RL_LINES); rlColor3f(1.0f, 0.0f, 0.0f); rlVertex3f(0.0f, 0.0f, 0.0f); rlColor3f(1.0f, 0.0f, 0.0f); rlVertex3f(1.0f, 0.0f, 0.0f); rlColor3f(0.0f, 1.0f, 0.0f); rlVertex3f(0.0f, 0.0f, 0.0f); rlColor3f(0.0f, 1.0f, 0.0f); rlVertex3f(0.0f, 1.0f, 0.0f); rlColor3f(0.0f, 0.0f, 1.0f); rlVertex3f(0.0f, 0.0f, 0.0f); rlColor3f(0.0f, 0.0f, 1.0f); rlVertex3f(0.0f, 0.0f, 1.0f); rlEnd(); rlPopMatrix(); } void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale) { // NOTE: RGB = XYZ rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); rlScalef(scale, scale, scale); rlRotatef(rotation.y, 0, 1, 0); rlBegin(RL_LINES); // X Axis rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x, position.y, position.z); rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + 1, position.y, position.z); // ArrowX rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + 1.1, position.y, position.z); rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + .9, position.y, position.z + .1); rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + 1.1, position.y, position.z); rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + .9, position.y, position.z - .1); // Y Axis rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x, position.y, position.z); rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x, position.y + 1, position.z); // ArrowY rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x, position.y + 1.1, position.z); rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x + .1, position.y + .9, position.z); rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x, position.y + 1.1, position.z); rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x - .1, position.y + .9, position.z); // Z Axis rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x, position.y, position.z); rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x, position.y, position.z - 1); // ArrowZ rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x, position.y, position.z - 1.1); rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x + .1, position.y, position.z - .9); rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x, position.y, position.z - 1.1); rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x - .1, position.y, position.z - .9); // Extra int n = 3; // X Axis for (int i=0; i < 360; i += 6) { rlColor4ub(200, 0, 0, 255); rlVertex3f(0, position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n); rlColor4ub(200, 0, 0, 255); rlVertex3f(0, position.x + sin(DEG2RAD*(i+6)) * scale/n, position.y + cos(DEG2RAD*(i+6)) * scale/n); } // Y Axis for (int i=0; i < 360; i += 6) { rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, 0, position.y + cos(DEG2RAD*i) * scale/n); rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x + sin(DEG2RAD*(i+6)) * scale/n, 0, position.y + cos(DEG2RAD*(i+6)) * scale/n); } // Z Axis for (int i=0; i < 360; i += 6) { rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n, 0); rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x + sin(DEG2RAD*(i+6)) * scale/n, position.y + cos(DEG2RAD*(i+6)) * scale/n, 0); } rlEnd(); rlPopMatrix(); } // Load a 3d model Model LoadModel(const char *fileName) { VertexData vData; if (strcmp(GetExtension(fileName),"obj") == 0) vData = LoadOBJ(fileName); else TraceLog(WARNING, "[%s] Model extension not recognized, it can't be loaded", fileName); // NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct Model model = rlglLoadModel(vData); // Upload vertex data to GPU // Now that vertex data is uploaded to GPU, we can free arrays // NOTE: We don't need CPU vertex data on OpenGL 3.3 or ES2 if (rlGetVersion() != OPENGL_11) { free(vData.vertices); free(vData.texcoords); free(vData.normals); } return model; } // Load a heightmap image as a 3d model Model LoadHeightmap(Image heightmap, float maxHeight) { VertexData vData; int mapX = heightmap.width; int mapZ = heightmap.height; // NOTE: One vertex per pixel // TODO: Consider resolution when generating model data? int numTriangles = (mapX-1)*(mapZ-1)*2; // One quad every four pixels vData.vertexCount = numTriangles*3; vData.vertices = (float *)malloc(vData.vertexCount * 3 * sizeof(float)); vData.normals = (float *)malloc(vData.vertexCount * 3 * sizeof(float)); vData.texcoords = (float *)malloc(vData.vertexCount * 2 * sizeof(float)); vData.colors = (unsigned char *)malloc(vData.vertexCount * 4 * sizeof(unsigned char)); // Not used... int vCounter = 0; // Used to count vertices float by float int tcCounter = 0; // Used to count texcoords float by float int nCounter = 0; // Used to count normals float by float int trisCounter = 0; float scaleFactor = maxHeight/255; // TODO: Review scaleFactor calculation for(int z = 0; z < mapZ-1; z++) { for(int x = 0; x < mapX-1; x++) { // Fill vertices array with data //---------------------------------------------------------- // one triangle - 3 vertex vData.vertices[vCounter] = x; vData.vertices[vCounter + 1] = GetHeightValue(heightmap.pixels[x + z*mapX])*scaleFactor; vData.vertices[vCounter + 2] = z; vData.vertices[vCounter + 3] = x; vData.vertices[vCounter + 4] = GetHeightValue(heightmap.pixels[x + (z+1)*mapX])*scaleFactor; vData.vertices[vCounter + 5] = z+1; vData.vertices[vCounter + 6] = x+1; vData.vertices[vCounter + 7] = GetHeightValue(heightmap.pixels[(x+1) + z*mapX])*scaleFactor; vData.vertices[vCounter + 8] = z; // another triangle - 3 vertex vData.vertices[vCounter + 9] = vData.vertices[vCounter + 6]; vData.vertices[vCounter + 10] = vData.vertices[vCounter + 7]; vData.vertices[vCounter + 11] = vData.vertices[vCounter + 8]; vData.vertices[vCounter + 12] = vData.vertices[vCounter + 3]; vData.vertices[vCounter + 13] = vData.vertices[vCounter + 4]; vData.vertices[vCounter + 14] = vData.vertices[vCounter + 5]; vData.vertices[vCounter + 15] = x+1; vData.vertices[vCounter + 16] = GetHeightValue(heightmap.pixels[(x+1) + (z+1)*mapX])*scaleFactor; vData.vertices[vCounter + 17] = z+1; vCounter += 18; // 6 vertex, 18 floats // Fill texcoords array with data //-------------------------------------------------------------- vData.texcoords[tcCounter] = (float)x / (mapX-1); vData.texcoords[tcCounter + 1] = (float)z / (mapZ-1); vData.texcoords[tcCounter + 2] = (float)x / (mapX-1); vData.texcoords[tcCounter + 3] = (float)(z+1) / (mapZ-1); vData.texcoords[tcCounter + 4] = (float)(x+1) / (mapX-1); vData.texcoords[tcCounter + 5] = (float)z / (mapZ-1); vData.texcoords[tcCounter + 6] = vData.texcoords[tcCounter + 4]; vData.texcoords[tcCounter + 7] = vData.texcoords[tcCounter + 5]; vData.texcoords[tcCounter + 8] = vData.texcoords[tcCounter + 2]; vData.texcoords[tcCounter + 9] = vData.texcoords[tcCounter + 3]; vData.texcoords[tcCounter + 10] = (float)(x+1) / (mapX-1); vData.texcoords[tcCounter + 11] = (float)(z+1) / (mapZ-1); tcCounter += 12; // 6 texcoords, 12 floats // Fill normals array with data //-------------------------------------------------------------- // NOTE: Current Model implementation doe not use normals! for (int i = 0; i < 18; i += 3) { vData.normals[nCounter + i] = 0.0f; vData.normals[nCounter + i + 1] = 1.0f; vData.normals[nCounter + i + 2] = 0.0f; } // TODO: Calculate normals in an efficient way nCounter += 18; // 6 vertex, 18 floats trisCounter += 2; } } // Fill color data // NOTE: Not used any more... just one plain color defined at DrawModel() for (int i = 0; i < (4*vData.vertexCount); i++) vData.colors[i] = 255; // NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct Model model = rlglLoadModel(vData); // Now that vertex data is uploaded to GPU, we can free arrays // NOTE: We don't need CPU vertex data on OpenGL 3.3 or ES2 if (rlGetVersion() != OPENGL_11) { free(vData.vertices); free(vData.texcoords); free(vData.normals); } return model; } // Load a map image as a 3d model (cubes based) Model LoadCubicmap(Image cubesmap) { VertexData vData; // Map cube size will be 1.0 float mapCubeSide = 1.0f; int mapWidth = cubesmap.width * (int)mapCubeSide; int mapHeight = cubesmap.height * (int)mapCubeSide; // NOTE: Max possible number of triangles numCubes * (12 triangles by cube) int maxTriangles = cubesmap.width*cubesmap.height*12; int vCounter = 0; // Used to count vertices int tcCounter = 0; // Used to count texcoords int nCounter = 0; // Used to count normals float w = mapCubeSide; float h = mapCubeSide; float h2 = mapCubeSide; Vector3 *mapVertices = (Vector3 *)malloc(maxTriangles * 3 * sizeof(Vector3)); Vector2 *mapTexcoords = (Vector2 *)malloc(maxTriangles * 3 * sizeof(Vector2)); Vector3 *mapNormals = (Vector3 *)malloc(maxTriangles * 3 * sizeof(Vector3)); for (int z = 0; z < mapHeight; z += mapCubeSide) { for (int x = 0; x < mapWidth; x += mapCubeSide) { // Define the 8 vertex of the cube, we will combine them accordingly later... Vector3 v1 = { x - w/2, h2, z - h/2 }; Vector3 v2 = { x - w/2, h2, z + h/2 }; Vector3 v3 = { x + w/2, h2, z + h/2 }; Vector3 v4 = { x + w/2, h2, z - h/2 }; Vector3 v5 = { x + w/2, 0, z - h/2 }; Vector3 v6 = { x - w/2, 0, z - h/2 }; Vector3 v7 = { x - w/2, 0, z + h/2 }; Vector3 v8 = { x + w/2, 0, z + h/2 }; // Define the 6 normals of the cube, we will combine them accordingly later... Vector3 n1 = { 1.0f, 0.0f, 0.0f }; Vector3 n2 = { -1.0f, 0.0f, 0.0f }; Vector3 n3 = { 0.0f, 1.0f, 0.0f }; Vector3 n4 = { 0.0f, -1.0f, 0.0f }; Vector3 n5 = { 0.0f, 0.0f, 1.0f }; Vector3 n6 = { 0.0f, 0.0f, -1.0f }; // Define the 4 texture coordinates of the cube, we will combine them accordingly later... // TODO: Use texture rectangles to define different textures for top-bottom-front-back-right-left (6) Vector2 vt2 = { 0.0f, 0.0f }; Vector2 vt1 = { 0.0f, 1.0f }; Vector2 vt4 = { 1.0f, 0.0f }; Vector2 vt3 = { 1.0f, 1.0f }; // We check pixel color to be WHITE, we will full cubes if ((cubesmap.pixels[z*cubesmap.width + x].r == 255) && (cubesmap.pixels[z*cubesmap.width + x].g == 255) && (cubesmap.pixels[z*cubesmap.width + x].b == 255)) { // Define triangles (Checking Collateral Cubes!) //---------------------------------------------- // Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4) mapVertices[vCounter] = v1; mapVertices[vCounter + 1] = v2; mapVertices[vCounter + 2] = v3; mapVertices[vCounter + 3] = v1; mapVertices[vCounter + 4] = v3; mapVertices[vCounter + 5] = v4; vCounter += 6; mapNormals[nCounter] = n3; mapNormals[nCounter + 1] = n3; mapNormals[nCounter + 2] = n3; mapNormals[nCounter + 3] = n3; mapNormals[nCounter + 4] = n3; mapNormals[nCounter + 5] = n3; nCounter += 6; mapTexcoords[tcCounter] = vt2; mapTexcoords[tcCounter + 1] = vt1; mapTexcoords[tcCounter + 2] = vt3; mapTexcoords[tcCounter + 3] = vt2; mapTexcoords[tcCounter + 4] = vt3; mapTexcoords[tcCounter + 5] = vt4; tcCounter += 6; // Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8) mapVertices[vCounter] = v6; mapVertices[vCounter + 1] = v8; mapVertices[vCounter + 2] = v7; mapVertices[vCounter + 3] = v6; mapVertices[vCounter + 4] = v5; mapVertices[vCounter + 5] = v8; vCounter += 6; mapNormals[nCounter] = n4; mapNormals[nCounter + 1] = n4; mapNormals[nCounter + 2] = n4; mapNormals[nCounter + 3] = n4; mapNormals[nCounter + 4] = n4; mapNormals[nCounter + 5] = n4; nCounter += 6; mapTexcoords[tcCounter] = vt4; mapTexcoords[tcCounter + 1] = vt1; mapTexcoords[tcCounter + 2] = vt3; mapTexcoords[tcCounter + 3] = vt4; mapTexcoords[tcCounter + 4] = vt2; mapTexcoords[tcCounter + 5] = vt1; tcCounter += 6; if (((z < cubesmap.height - 1) && (cubesmap.pixels[(z + 1)*cubesmap.width + x].r == 0) && (cubesmap.pixels[(z + 1)*cubesmap.width + x].g == 0) && (cubesmap.pixels[(z + 1)*cubesmap.width + x].b == 0)) || (z == cubesmap.height - 1)) { // Define front triangles (2 tris, 6 vertex) --> v2 v7 v3, v3 v7 v8 // NOTE: Collateral occluded faces are not generated mapVertices[vCounter] = v2; mapVertices[vCounter + 1] = v7; mapVertices[vCounter + 2] = v3; mapVertices[vCounter + 3] = v3; mapVertices[vCounter + 4] = v7; mapVertices[vCounter + 5] = v8; vCounter += 6; mapNormals[nCounter] = n6; mapNormals[nCounter + 1] = n6; mapNormals[nCounter + 2] = n6; mapNormals[nCounter + 3] = n6; mapNormals[nCounter + 4] = n6; mapNormals[nCounter + 5] = n6; nCounter += 6; mapTexcoords[tcCounter] = vt2; mapTexcoords[tcCounter + 1] = vt1; mapTexcoords[tcCounter + 2] = vt4; mapTexcoords[tcCounter + 3] = vt4; mapTexcoords[tcCounter + 4] = vt1; mapTexcoords[tcCounter + 5] = vt3; tcCounter += 6; } if (((z > 0) && (cubesmap.pixels[(z - 1)*cubesmap.width + x].r == 0) && (cubesmap.pixels[(z - 1)*cubesmap.width + x].g == 0) && (cubesmap.pixels[(z - 1)*cubesmap.width + x].b == 0)) || (z == 0)) { // Define back triangles (2 tris, 6 vertex) --> v1 v5 v6, v1 v4 v5 // NOTE: Collateral occluded faces are not generated mapVertices[vCounter] = v1; mapVertices[vCounter + 1] = v5; mapVertices[vCounter + 2] = v6; mapVertices[vCounter + 3] = v1; mapVertices[vCounter + 4] = v4; mapVertices[vCounter + 5] = v5; vCounter += 6; mapNormals[nCounter] = n5; mapNormals[nCounter + 1] = n5; mapNormals[nCounter + 2] = n5; mapNormals[nCounter + 3] = n5; mapNormals[nCounter + 4] = n5; mapNormals[nCounter + 5] = n5; nCounter += 6; mapTexcoords[tcCounter] = vt4; mapTexcoords[tcCounter + 1] = vt1; mapTexcoords[tcCounter + 2] = vt3; mapTexcoords[tcCounter + 3] = vt4; mapTexcoords[tcCounter + 4] = vt2; mapTexcoords[tcCounter + 5] = vt1; tcCounter += 6; } if (((x < cubesmap.width - 1) && (cubesmap.pixels[z*cubesmap.width + (x + 1)].r == 0) && (cubesmap.pixels[z*cubesmap.width + (x + 1)].g == 0) && (cubesmap.pixels[z*cubesmap.width + (x + 1)].b == 0)) || (x == cubesmap.width - 1)) { // Define right triangles (2 tris, 6 vertex) --> v3 v8 v4, v4 v8 v5 // NOTE: Collateral occluded faces are not generated mapVertices[vCounter] = v3; mapVertices[vCounter + 1] = v8; mapVertices[vCounter + 2] = v4; mapVertices[vCounter + 3] = v4; mapVertices[vCounter + 4] = v8; mapVertices[vCounter + 5] = v5; vCounter += 6; mapNormals[nCounter] = n1; mapNormals[nCounter + 1] = n1; mapNormals[nCounter + 2] = n1; mapNormals[nCounter + 3] = n1; mapNormals[nCounter + 4] = n1; mapNormals[nCounter + 5] = n1; nCounter += 6; mapTexcoords[tcCounter] = vt2; mapTexcoords[tcCounter + 1] = vt1; mapTexcoords[tcCounter + 2] = vt4; mapTexcoords[tcCounter + 3] = vt4; mapTexcoords[tcCounter + 4] = vt1; mapTexcoords[tcCounter + 5] = vt3; tcCounter += 6; } if (((x > 0) && (cubesmap.pixels[z*cubesmap.width + (x - 1)].r == 0) && (cubesmap.pixels[z*cubesmap.width + (x - 1)].g == 0) && (cubesmap.pixels[z*cubesmap.width + (x - 1)].b == 0)) || (x == 0)) { // Define left triangles (2 tris, 6 vertex) --> v1 v7 v2, v1 v6 v7 // NOTE: Collateral occluded faces are not generated mapVertices[vCounter] = v1; mapVertices[vCounter + 1] = v7; mapVertices[vCounter + 2] = v2; mapVertices[vCounter + 3] = v1; mapVertices[vCounter + 4] = v6; mapVertices[vCounter + 5] = v7; vCounter += 6; mapNormals[nCounter] = n2; mapNormals[nCounter + 1] = n2; mapNormals[nCounter + 2] = n2; mapNormals[nCounter + 3] = n2; mapNormals[nCounter + 4] = n2; mapNormals[nCounter + 5] = n2; nCounter += 6; mapTexcoords[tcCounter] = vt2; mapTexcoords[tcCounter + 1] = vt3; mapTexcoords[tcCounter + 2] = vt4; mapTexcoords[tcCounter + 3] = vt2; mapTexcoords[tcCounter + 4] = vt1; mapTexcoords[tcCounter + 5] = vt3; tcCounter += 6; } } // We check pixel color to be BLACK, we will only draw floor and roof else if ((cubesmap.pixels[z*cubesmap.width + x].r == 0) && (cubesmap.pixels[z*cubesmap.width + x].g == 0) && (cubesmap.pixels[z*cubesmap.width + x].b == 0)) { // Define top triangles (2 tris, 6 vertex --> v1-v3-v2, v1-v4-v3) // TODO: ... // Define bottom triangles (2 tris, 6 vertex --> v6-v7-v8, v6-v8-v5) // TODO: ... } } } // Move data from mapVertices temp arays to vertices float array vData.vertexCount = vCounter; vData.vertices = (float *)malloc(vData.vertexCount * 3 * sizeof(float)); vData.normals = (float *)malloc(vData.vertexCount * 3 * sizeof(float)); vData.texcoords = (float *)malloc(vData.vertexCount * 2 * sizeof(float)); vData.colors = (unsigned char *)malloc(vData.vertexCount * 4 * sizeof(unsigned char)); // Not used... // Fill color data // NOTE: Not used any more... just one plain color defined at DrawModel() for (int i = 0; i < (4*vData.vertexCount); i++) vData.colors[i] = 255; int fCounter = 0; // Move vertices data for (int i = 0; i < vCounter; i++) { vData.vertices[fCounter] = mapVertices[i].x; vData.vertices[fCounter + 1] = mapVertices[i].y; vData.vertices[fCounter + 2] = mapVertices[i].z; fCounter += 3; } fCounter = 0; // Move normals data for (int i = 0; i < nCounter; i++) { vData.normals[fCounter] = mapNormals[i].x; vData.normals[fCounter + 1] = mapNormals[i].y; vData.normals[fCounter + 2] = mapNormals[i].z; fCounter += 3; } fCounter = 0; // Move texcoords data for (int i = 0; i < tcCounter; i++) { vData.texcoords[fCounter] = mapTexcoords[i].x; vData.texcoords[fCounter + 1] = mapTexcoords[i].y; fCounter += 2; } free(mapVertices); free(mapNormals); free(mapTexcoords); // NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct Model model = rlglLoadModel(vData); // Now that vertex data is uploaded to GPU, we can free arrays // NOTE: We don't need CPU vertex data on OpenGL 3.3 or ES2 if (rlGetVersion() != OPENGL_11) { free(vData.vertices); free(vData.texcoords); free(vData.normals); } return model; } // Unload 3d model from memory void UnloadModel(Model model) { if (rlGetVersion() == OPENGL_11) { free(model.mesh.vertices); free(model.mesh.texcoords); free(model.mesh.normals); } rlDeleteBuffers(model.vboId[0]); rlDeleteBuffers(model.vboId[1]); rlDeleteBuffers(model.vboId[2]); rlDeleteVertexArrays(model.vaoId); rlDeleteTextures(model.textureId); rlDeleteShader(model.shaderId); } void SetModelTexture(Model *model, Texture2D texture) { if (texture.id <= 0) model->textureId = whiteTexture; // Default white texture (use mesh color) else model->textureId = texture.id; } // Draw a model (with texture if set) void DrawModel(Model model, Vector3 position, float scale, Color tint) { Vector3 vScale = { scale, scale, scale }; Vector3 rotation = { 0, 0, 0 }; rlglDrawModel(model, position, rotation, vScale, tint, false); } // Draw a model with extended parameters void DrawModelEx(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color tint) { // NOTE: Rotation must be provided in degrees, it's converted to radians inside rlglDrawModel() rlglDrawModel(model, position, rotation, scale, tint, false); } // Draw a model wires (with texture if set) void DrawModelWires(Model model, Vector3 position, float scale, Color color) { Vector3 vScale = { scale, scale, scale }; Vector3 rotation = { 0, 0, 0 }; rlglDrawModel(model, position, rotation, vScale, color, true); } // Draw a billboard void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint) { // NOTE: Billboard size will maintain texture aspect ratio, size will be billboard width Vector2 sizeRatio = { size, size * (float)texture.height/texture.width }; Matrix viewMatrix = MatrixLookAt(camera.position, camera.target, camera.up); MatrixTranspose(&viewMatrix); Vector3 right = { viewMatrix.m0, viewMatrix.m4, viewMatrix.m8 }; Vector3 up = { viewMatrix.m1, viewMatrix.m5, viewMatrix.m9 }; /* a-------b | | | * | | | d-------c */ VectorScale(&right, sizeRatio.x/2); VectorScale(&up, sizeRatio.y/2); Vector3 p1 = VectorAdd(right, up); Vector3 p2 = VectorSubtract(right, up); Vector3 a = VectorSubtract(center, p2); Vector3 b = VectorAdd(center, p1); Vector3 c = VectorAdd(center, p2); Vector3 d = VectorSubtract(center, p1); rlEnableTexture(texture.id); rlBegin(RL_QUADS); rlColor4ub(tint.r, tint.g, tint.b, tint.a); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(a.x, a.y, a.z); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(d.x, d.y, d.z); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(c.x, c.y, c.z); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(b.x, b.y, b.z); rlEnd(); rlDisableTexture(); } // Draw a billboard (part of a texture defined by a rectangle) void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint) { // NOTE: Billboard size will maintain sourceRec aspect ratio, size will represent billboard width Vector2 sizeRatio = { size, size * (float)sourceRec.height/sourceRec.width }; Matrix viewMatrix = MatrixLookAt(camera.position, camera.target, camera.up); MatrixTranspose(&viewMatrix); Vector3 right = { viewMatrix.m0, viewMatrix.m4, viewMatrix.m8 }; Vector3 up = { viewMatrix.m1, viewMatrix.m5, viewMatrix.m9 }; /* a-------b | | | * | | | d-------c */ VectorScale(&right, sizeRatio.x/2); VectorScale(&up, sizeRatio.y/2); Vector3 p1 = VectorAdd(right, up); Vector3 p2 = VectorSubtract(right, up); Vector3 a = VectorSubtract(center, p2); Vector3 b = VectorAdd(center, p1); Vector3 c = VectorAdd(center, p2); Vector3 d = VectorSubtract(center, p1); rlEnableTexture(texture.id); rlBegin(RL_QUADS); rlColor4ub(tint.r, tint.g, tint.b, tint.a); // Bottom-left corner for texture and quad rlTexCoord2f((float)sourceRec.x / texture.width, (float)sourceRec.y / texture.height); rlVertex3f(a.x, a.y, a.z); // Top-left corner for texture and quad rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height); rlVertex3f(d.x, d.y, d.z); // Top-right corner for texture and quad rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height); rlVertex3f(c.x, c.y, c.z); // Bottom-right corner for texture and quad rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height); rlVertex3f(b.x, b.y, b.z); rlEnd(); rlDisableTexture(); } // Get current vertex y altitude (proportional to pixel colors in grayscale) static float GetHeightValue(Color pixel) { return (((float)pixel.r + (float)pixel.g + (float)pixel.b)/3); } // Load OBJ mesh data static VertexData LoadOBJ(const char *fileName) { VertexData vData; char dataType; char comments[200]; int numVertex = 0; int numNormals = 0; int numTexCoords = 0; int numTriangles = 0; FILE *objFile; objFile = fopen(fileName, "rt"); if (objFile == NULL) { TraceLog(WARNING, "[%s] OBJ file could not be opened", fileName); return vData; } // First reading pass: Get numVertex, numNormals, numTexCoords, numTriangles // NOTE: vertex, texcoords and normals could be optimized (to be used indexed on faces definition) // NOTE: faces MUST be defined as TRIANGLES, not QUADS while(!feof(objFile)) { fscanf(objFile, "%c", &dataType); switch(dataType) { case '#': // Comments case 'o': // Object name (One OBJ file can contain multible named meshes) case 'g': // Group name case 's': // Smoothing level case 'm': // mtllib [external .mtl file name] case 'u': // usemtl [material name] { fgets(comments, 200, objFile); } break; case 'v': { fscanf(objFile, "%c", &dataType); if (dataType == 't') // Read texCoord { numTexCoords++; fgets(comments, 200, objFile); } else if (dataType == 'n') // Read normals { numNormals++; fgets(comments, 200, objFile); } else // Read vertex { numVertex++; fgets(comments, 200, objFile); } } break; case 'f': { numTriangles++; fgets(comments, 200, objFile); } break; default: break; } } TraceLog(DEBUG, "[%s] Model num vertices: %i", fileName, numVertex); TraceLog(DEBUG, "[%s] Model num texcoords: %i", fileName, numTexCoords); TraceLog(DEBUG, "[%s] Model num normals: %i", fileName, numNormals); TraceLog(DEBUG, "[%s] Model num triangles: %i", fileName, numTriangles); // Once we know the number of vertices to store, we create required arrays Vector3 *midVertices = (Vector3 *)malloc(numVertex*sizeof(Vector3)); Vector3 *midNormals = NULL; if (numNormals > 0) midNormals = (Vector3 *)malloc(numNormals*sizeof(Vector3)); Vector2 *midTexCoords = NULL; if (numTexCoords > 0) midTexCoords = (Vector2 *)malloc(numTexCoords*sizeof(Vector2)); int countVertex = 0; int countNormals = 0; int countTexCoords = 0; rewind(objFile); // Return to the beginning of the file, to read again // Second reading pass: Get vertex data to fill intermediate arrays // NOTE: This second pass is required in case of multiple meshes defined in same OBJ // TODO: Consider that diferent meshes can have different vertex data available (position, texcoords, normals) while(!feof(objFile)) { fscanf(objFile, "%c", &dataType); switch(dataType) { case '#': case 'o': case 'g': case 's': case 'm': case 'u': case 'f': fgets(comments, 200, objFile); break; case 'v': { fscanf(objFile, "%c", &dataType); if (dataType == 't') // Read texCoord { float useless = 0; fscanf(objFile, "%f %f %f", &midTexCoords[countTexCoords].x, &midTexCoords[countTexCoords].y, &useless); countTexCoords++; fscanf(objFile, "%c", &dataType); } else if (dataType == 'n') // Read normals { fscanf(objFile, "%f %f %f", &midNormals[countNormals].x, &midNormals[countNormals].y, &midNormals[countNormals].z ); countNormals++; fscanf(objFile, "%c", &dataType); } else // Read vertex { fscanf(objFile, "%f %f %f", &midVertices[countVertex].x, &midVertices[countVertex].y, &midVertices[countVertex].z ); countVertex++; fscanf(objFile, "%c", &dataType); } } break; default: break; } } // At this point all vertex data (v, vt, vn) has been gathered on midVertices, midTexCoords, midNormals // Now we can organize that data into our VertexData struct vData.vertexCount = numTriangles*3; // Additional arrays to store vertex data as floats vData.vertices = (float *)malloc(vData.vertexCount * 3 * sizeof(float)); vData.texcoords = (float *)malloc(vData.vertexCount * 2 * sizeof(float)); vData.normals = (float *)malloc(vData.vertexCount * 3 * sizeof(float)); vData.colors = (unsigned char *)malloc(vData.vertexCount * 4 * sizeof(unsigned char)); int vCounter = 0; // Used to count vertices float by float int tcCounter = 0; // Used to count texcoords float by float int nCounter = 0; // Used to count normals float by float int vNum[3], vtNum[3], vnNum[3]; rewind(objFile); // Return to the beginning of the file, to read again if (numNormals == 0) TraceLog(INFO, "[%s] No normals data on OBJ, normals will be generated from faces data", fileName); // Third reading pass: Get faces (triangles) data and fill VertexArray while(!feof(objFile)) { fscanf(objFile, "%c", &dataType); switch(dataType) { case '#': case 'o': case 'g': case 's': case 'm': case 'u': case 'v': fgets(comments, 200, objFile); break; case 'f': { // NOTE: It could be that OBJ does not have normals or texcoords defined! if ((numNormals == 0) && (numTexCoords == 0)) fscanf(objFile, "%i %i %i", &vNum[0], &vNum[1], &vNum[2]); else if (numNormals == 0) fscanf(objFile, "%i/%i %i/%i %i/%i", &vNum[0], &vtNum[0], &vNum[1], &vtNum[1], &vNum[2], &vtNum[2]); else fscanf(objFile, "%i/%i/%i %i/%i/%i %i/%i/%i", &vNum[0], &vtNum[0], &vnNum[0], &vNum[1], &vtNum[1], &vnNum[1], &vNum[2], &vtNum[2], &vnNum[2]); vData.vertices[vCounter] = midVertices[vNum[0]-1].x; vData.vertices[vCounter + 1] = midVertices[vNum[0]-1].y; vData.vertices[vCounter + 2] = midVertices[vNum[0]-1].z; vCounter += 3; vData.vertices[vCounter] = midVertices[vNum[1]-1].x; vData.vertices[vCounter + 1] = midVertices[vNum[1]-1].y; vData.vertices[vCounter + 2] = midVertices[vNum[1]-1].z; vCounter += 3; vData.vertices[vCounter] = midVertices[vNum[2]-1].x; vData.vertices[vCounter + 1] = midVertices[vNum[2]-1].y; vData.vertices[vCounter + 2] = midVertices[vNum[2]-1].z; vCounter += 3; if (numNormals > 0) { vData.normals[nCounter] = midNormals[vnNum[0]-1].x; vData.normals[nCounter + 1] = midNormals[vnNum[0]-1].y; vData.normals[nCounter + 2] = midNormals[vnNum[0]-1].z; nCounter += 3; vData.normals[nCounter] = midNormals[vnNum[1]-1].x; vData.normals[nCounter + 1] = midNormals[vnNum[1]-1].y; vData.normals[nCounter + 2] = midNormals[vnNum[1]-1].z; nCounter += 3; vData.normals[nCounter] = midNormals[vnNum[2]-1].x; vData.normals[nCounter + 1] = midNormals[vnNum[2]-1].y; vData.normals[nCounter + 2] = midNormals[vnNum[2]-1].z; nCounter += 3; } else { // If normals not defined, they are calculated from the 3 vertices [N = (V2 - V1) x (V3 - V1)] Vector3 norm = VectorCrossProduct(VectorSubtract(midVertices[vNum[1]-1], midVertices[vNum[0]-1]), VectorSubtract(midVertices[vNum[2]-1], midVertices[vNum[0]-1])); VectorNormalize(&norm); vData.normals[nCounter] = norm.x; vData.normals[nCounter + 1] = norm.y; vData.normals[nCounter + 2] = norm.z; nCounter += 3; vData.normals[nCounter] = norm.x; vData.normals[nCounter + 1] = norm.y; vData.normals[nCounter + 2] = norm.z; nCounter += 3; vData.normals[nCounter] = norm.x; vData.normals[nCounter + 1] = norm.y; vData.normals[nCounter + 2] = norm.z; nCounter += 3; } if (numTexCoords > 0) { vData.texcoords[tcCounter] = midTexCoords[vtNum[0]-1].x; vData.texcoords[tcCounter + 1] = -midTexCoords[vtNum[0]-1].y; tcCounter += 2; vData.texcoords[tcCounter] = midTexCoords[vtNum[1]-1].x; vData.texcoords[tcCounter + 1] = -midTexCoords[vtNum[1]-1].y; tcCounter += 2; vData.texcoords[tcCounter] = midTexCoords[vtNum[2]-1].x; vData.texcoords[tcCounter + 1] = -midTexCoords[vtNum[2]-1].y; tcCounter += 2; } } break; default: break; } } fclose(objFile); // Security check, just in case no normals or no texcoords defined in OBJ if (numTexCoords == 0) for (int i = 0; i < (2*vData.vertexCount); i++) vData.texcoords[i] = 0.0f; // NOTE: We set all vertex colors to white // NOTE: Not used any more... just one plain color defined at DrawModel() for (int i = 0; i < (4*vData.vertexCount); i++) vData.colors[i] = 255; // Now we can free temp mid* arrays free(midVertices); free(midNormals); free(midTexCoords); // NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct TraceLog(INFO, "[%s] Model loaded successfully in RAM (CPU)", fileName); return vData; } bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB) { return false; } bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, Vector3 maxBBox2) { /* // Get min and max vertex to construct bounds (AABB) Vector3 minVertex = tempVertices[0]; Vector3 maxVertex = tempVertices[0]; for (int i = 1; i < tempVertices.Count; i++) { minVertex = Vector3.Min(minVertex, tempVertices[i]); maxVertex = Vector3.Max(maxVertex, tempVertices[i]); } bounds = new BoundingBox(minVertex, maxVertex); */ return false; } bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSphere, Vector3 radiusSphere) { return false; } //BoundingBox GetCollisionArea(BoundingBox box1, BoundingBox box2)