raylib/src/models.c

2050 lines
88 KiB
C
Raw Normal View History

/**********************************************************************************************
*
* raylib.models
*
* Basic functions to draw 3d shapes and load/draw 3d models (.OBJ)
*
2015-07-29 22:43:30 +03:00
* Copyright (c) 2014 Ramon Santamaria (@raysan5)
2014-09-03 18:51:28 +04:00
*
* 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.
*
2014-09-03 18:51:28 +04:00
* 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:
*
2014-09-03 18:51:28 +04:00
* 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 <stdio.h> // Standard input/output functions, used to read model files data
#include <stdlib.h> // Declares malloc() and free() for memory management
#include <string.h> // Required for strcmp()
#include <math.h> // Used for sin, cos, tan
2014-03-25 15:40:35 +04:00
#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
#include "raymath.h" // Required for data type Matrix and Matrix functions
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
#define CUBIC_MAP_HALF_BLOCK_SIZE 0.5
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
// ...
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
extern unsigned int whiteTexture;
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
static Mesh LoadOBJ(const char *fileName); // Load OBJ mesh data
static Material LoadMTL(const char *fileName); // Load MTL material data
static Mesh GenMeshHeightmap(Image image, Vector3 size);
static Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize);
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
// Draw cube
// NOTE: Cube position is the center position
void DrawCube(Vector3 position, float width, float height, float length, Color color)
{
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
2014-03-25 15:40:35 +04:00
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);
2014-09-03 18:51:28 +04:00
rlBegin(RL_TRIANGLES);
2014-09-03 18:51:28 +04:00
rlColor4ub(color.r, color.g, color.b, color.a);
// Front Face -----------------------------------------------------
rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Left
rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right
rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left
2014-09-03 18:51:28 +04:00
rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Right
rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left
rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right
2014-09-03 18:51:28 +04:00
// Back Face ------------------------------------------------------
rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Left
rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left
rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right
2014-09-03 18:51:28 +04:00
rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right
rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right
rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left
2014-09-03 18:51:28 +04:00
// Top Face -------------------------------------------------------
rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left
rlVertex3f(x-width/2, y+height/2, z+length/2); // Bottom Left
rlVertex3f(x+width/2, y+height/2, z+length/2); // Bottom Right
2014-09-03 18:51:28 +04:00
rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right
rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left
rlVertex3f(x+width/2, y+height/2, z+length/2); // Bottom Right
2014-09-03 18:51:28 +04:00
// Bottom Face ----------------------------------------------------
rlVertex3f(x-width/2, y-height/2, z-length/2); // Top Left
rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right
rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Left
2014-09-03 18:51:28 +04:00
rlVertex3f(x+width/2, y-height/2, z-length/2); // Top Right
rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right
rlVertex3f(x-width/2, y-height/2, z-length/2); // Top Left
2014-09-03 18:51:28 +04:00
// Right face -----------------------------------------------------
rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right
rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right
rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Left
2014-09-03 18:51:28 +04:00
rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Left
rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right
rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Left
2014-09-03 18:51:28 +04:00
// Left Face ------------------------------------------------------
rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Right
rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left
rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Right
2014-09-03 18:51:28 +04:00
rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Left
rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left
rlVertex3f(x-width/2, y-height/2, z-length/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 length, Color color)
{
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
2014-09-03 18:51:28 +04:00
rlPushMatrix();
rlTranslatef(position.x, position.y, position.z);
//rlRotatef(45, 0, 1, 0);
2014-09-03 18:51:28 +04:00
rlBegin(RL_LINES);
2014-09-03 18:51:28 +04:00
rlColor4ub(color.r, color.g, color.b, color.a);
// Front Face -----------------------------------------------------
// Bottom Line
rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Left
rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right
2014-09-03 18:51:28 +04:00
// Left Line
rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right
rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Right
2014-09-03 18:51:28 +04:00
// Top Line
rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Right
rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left
2014-09-03 18:51:28 +04:00
// Right Line
rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left
rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Left
2014-09-03 18:51:28 +04:00
// Back Face ------------------------------------------------------
// Bottom Line
rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Left
rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right
2014-09-03 18:51:28 +04:00
// Left Line
rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right
rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right
2014-09-03 18:51:28 +04:00
// Top Line
rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right
rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left
2014-09-03 18:51:28 +04:00
// Right Line
rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left
rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Left
2014-09-03 18:51:28 +04:00
// Top Face -------------------------------------------------------
// Left Line
rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left Front
rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left Back
2014-09-03 18:51:28 +04:00
// Right Line
rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Right Front
rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right Back
// Bottom Face ---------------------------------------------------
// Left Line
rlVertex3f(x-width/2, y-height/2, z+length/2); // Top Left Front
rlVertex3f(x-width/2, y-height/2, z-length/2); // Top Left Back
2014-09-03 18:51:28 +04:00
// Right Line
rlVertex3f(x+width/2, y-height/2, z+length/2); // Top Right Front
rlVertex3f(x+width/2, y-height/2, z-length/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 length, Color color)
{
float x = position.x;
float y = position.y;
float z = position.z;
rlEnableTexture(texture.id);
2014-09-03 18:51:28 +04:00
//rlPushMatrix();
2014-03-25 15:40:35 +04:00
// 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);
2014-09-03 18:51:28 +04:00
2014-03-25 15:40:35 +04:00
rlBegin(RL_QUADS);
2014-09-03 18:51:28 +04:00
rlColor4ub(color.r, color.g, color.b, color.a);
// Front Face
2014-03-25 15:40:35 +04:00
rlNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Left Of The Texture and Quad
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right Of The Texture and Quad
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Right Of The Texture and Quad
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left Of The Texture and Quad
// Back Face
2014-03-25 15:40:35 +04:00
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-length/2); // Bottom Right Of The Texture and Quad
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Right Of The Texture and Quad
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Left Of The Texture and Quad
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Left Of The Texture and Quad
// Top Face
2014-03-25 15:40:35 +04:00
rlNormal3f( 0.0f, 1.0f, 0.0f); // Normal Pointing Up
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left Of The Texture and Quad
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x-width/2, y+height/2, z+length/2); // Bottom Left Of The Texture and Quad
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x+width/2, y+height/2, z+length/2); // Bottom Right Of The Texture and Quad
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right Of The Texture and Quad
// Bottom Face
2014-03-25 15:40:35 +04:00
rlNormal3f( 0.0f,-1.0f, 0.0f); // Normal Pointing Down
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x-width/2, y-height/2, z-length/2); // Top Right Of The Texture and Quad
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x+width/2, y-height/2, z-length/2); // Top Left Of The Texture and Quad
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Left Of The Texture and Quad
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Right Of The Texture and Quad
// Right face
2014-03-25 15:40:35 +04:00
rlNormal3f( 1.0f, 0.0f, 0.0f); // Normal Pointing Right
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right Of The Texture and Quad
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right Of The Texture and Quad
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Left Of The Texture and Quad
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Left Of The Texture and Quad
// Left Face
2014-03-25 15:40:35 +04:00
rlNormal3f(-1.0f, 0.0f, 0.0f); // Normal Pointing Left
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Left Of The Texture and Quad
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Right Of The Texture and Quad
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Right Of The Texture and Quad
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left Of The Texture and Quad
2014-03-25 15:40:35 +04:00
rlEnd();
//rlPopMatrix();
2014-09-03 18:51:28 +04:00
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)
{
2014-03-25 15:40:35 +04:00
rlPushMatrix();
rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
rlScalef(radius, radius, radius);
2014-09-03 18:51:28 +04:00
rlBegin(RL_TRIANGLES);
2014-03-25 15:40:35 +04:00
rlColor4ub(color.r, color.g, color.b, color.a);
2014-09-03 18:51:28 +04:00
2014-06-15 02:50:09 +04:00
for(int i = 0; i < (rings + 2); i++)
{
for(int j = 0; j < slices; j++)
{
2014-09-03 18:51:28 +04:00
rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i)) * sin(DEG2RAD*(j*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*i)),
2014-06-09 18:33:53 +04:00
cos(DEG2RAD*(270+(180/(rings + 1))*i)) * cos(DEG2RAD*(j*360/slices)));
2014-09-03 18:51:28 +04:00
rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*((j+1)*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
2014-06-09 18:33:53 +04:00
cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*((j+1)*360/slices)));
2014-09-03 18:51:28 +04:00
rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*(j*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
2014-06-09 18:33:53 +04:00
cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*(j*360/slices)));
2014-09-03 18:51:28 +04:00
rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i)) * sin(DEG2RAD*(j*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*i)),
2014-06-09 18:33:53 +04:00
cos(DEG2RAD*(270+(180/(rings + 1))*i)) * cos(DEG2RAD*(j*360/slices)));
2014-09-03 18:51:28 +04:00
rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i))) * sin(DEG2RAD*((j+1)*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*(i))),
2014-06-09 18:33:53 +04:00
cos(DEG2RAD*(270+(180/(rings + 1))*(i))) * cos(DEG2RAD*((j+1)*360/slices)));
2014-09-03 18:51:28 +04:00
rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*((j+1)*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
2014-06-09 18:33:53 +04:00
cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*((j+1)*360/slices)));
}
}
2014-03-25 15:40:35 +04:00
rlEnd();
rlPopMatrix();
}
// Draw sphere wires
void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color)
{
rlPushMatrix();
2014-06-09 18:33:53 +04:00
rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
2014-06-15 02:50:09 +04:00
rlScalef(radius, radius, radius);
rlBegin(RL_LINES);
2014-03-25 15:40:35 +04:00
rlColor4ub(color.r, color.g, color.b, color.a);
2014-09-03 18:51:28 +04:00
2014-06-09 18:33:53 +04:00
for(int i = 0; i < (rings + 2); i++)
2014-03-25 15:40:35 +04:00
{
for(int j = 0; j < slices; j++)
{
2014-09-03 18:51:28 +04:00
rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i)) * sin(DEG2RAD*(j*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*i)),
2014-06-15 02:50:09 +04:00
cos(DEG2RAD*(270+(180/(rings + 1))*i)) * cos(DEG2RAD*(j*360/slices)));
2014-09-03 18:51:28 +04:00
rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*((j+1)*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
2014-06-15 02:50:09 +04:00
cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*((j+1)*360/slices)));
2014-09-03 18:51:28 +04:00
rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*((j+1)*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
2014-06-15 02:50:09 +04:00
cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*((j+1)*360/slices)));
2014-09-03 18:51:28 +04:00
rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*(j*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
2014-06-15 02:50:09 +04:00
cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*(j*360/slices)));
2014-09-03 18:51:28 +04:00
rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*(j*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
2014-06-15 02:50:09 +04:00
cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*(j*360/slices)));
2014-09-03 18:51:28 +04:00
rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i)) * sin(DEG2RAD*(j*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*i)),
2014-06-15 02:50:09 +04:00
cos(DEG2RAD*(270+(180/(rings + 1))*i)) * cos(DEG2RAD*(j*360/slices)));
}
2014-03-25 15:40:35 +04:00
}
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;
2014-09-03 18:51:28 +04:00
rlPushMatrix();
rlTranslatef(position.x, position.y, position.z);
rlBegin(RL_TRIANGLES);
2014-03-25 15:40:35 +04:00
rlColor4ub(color.r, color.g, color.b, color.a);
if (radiusTop > 0)
2014-03-25 15:40:35 +04:00
{
// 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
2014-09-03 18:51:28 +04:00
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
}
2014-09-03 18:51:28 +04:00
// 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);
}
2014-03-25 15:40:35 +04:00
}
else
2014-03-25 15:40:35 +04:00
{
// 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);
}
2014-03-25 15:40:35 +04:00
}
2014-09-03 18:51:28 +04:00
// Draw Base -----------------------------------------------------------------------------------------
for(int i = 0; i < 360; i += 360/sides)
2014-03-25 15:40:35 +04:00
{
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);
2014-03-25 15:40:35 +04:00
}
2014-09-03 18:51:28 +04:00
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;
2014-09-03 18:51:28 +04:00
rlPushMatrix();
rlTranslatef(position.x, position.y, position.z);
2014-09-03 18:51:28 +04:00
rlBegin(RL_LINES);
2014-03-25 15:40:35 +04:00
rlColor4ub(color.r, color.g, color.b, color.a);
2014-09-03 18:51:28 +04:00
for(int i = 0; i < 360; i += 360/sides)
2014-03-25 15:40:35 +04:00
{
rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom);
rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom);
2014-09-03 18:51:28 +04:00
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);
2014-09-03 18:51:28 +04:00
rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusTop, height, cos(DEG2RAD*(i+360/sides)) * radiusTop);
rlVertex3f(sin(DEG2RAD*i) * radiusTop, height, cos(DEG2RAD*i) * radiusTop);
2014-09-03 18:51:28 +04:00
rlVertex3f(sin(DEG2RAD*i) * radiusTop, height, cos(DEG2RAD*i) * radiusTop);
rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom);
2014-03-25 15:40:35 +04:00
}
rlEnd();
rlPopMatrix();
}
// Draw a plane
void DrawPlane(Vector3 centerPos, Vector2 size, Color color)
{
// NOTE: Plane is always created on XZ ground
2014-03-25 15:40:35 +04:00
rlPushMatrix();
rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
rlScalef(size.x, 1.0f, size.y);
2014-09-03 18:51:28 +04:00
rlBegin(RL_TRIANGLES);
2014-03-25 15:40:35 +04:00
rlColor4ub(color.r, color.g, color.b, color.a);
2014-09-03 18:51:28 +04:00
rlNormal3f(0.0f, 1.0f, 0.0f);
rlVertex3f(0.5f, 0.0f, -0.5f);
rlVertex3f(-0.5f, 0.0f, -0.5f);
rlVertex3f(-0.5f, 0.0f, 0.5f);
rlVertex3f(-0.5f, 0.0f, 0.5f);
rlVertex3f(0.5f, 0.0f, 0.5f);
rlVertex3f(0.5f, 0.0f, -0.5f);
rlEnd();
rlPopMatrix();
}
// Draw a ray line
void DrawRay(Ray ray, Color color)
{
float scale = 10000;
rlBegin(RL_LINES);
rlColor4ub(color.r, color.g, color.b, color.a);
rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex3f(ray.position.x, ray.position.y, ray.position.z);
rlVertex3f(ray.position.x + ray.direction.x*scale, ray.position.y + ray.direction.y*scale, ray.position.z + ray.direction.z*scale);
rlEnd();
}
// Draw a grid centered at (0, 0, 0)
void DrawGrid(int slices, float spacing)
{
int halfSlices = slices / 2;
2014-03-25 15:40:35 +04:00
rlBegin(RL_LINES);
for(int i = -halfSlices; i <= halfSlices; i++)
{
if (i == 0)
{
2014-03-25 15:40:35 +04:00
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);
}
2014-03-25 15:40:35 +04:00
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);
}
2014-09-03 18:51:28 +04:00
2014-03-25 15:40:35 +04:00
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 length = 1.0f;
2014-03-25 15:40:35 +04:00
rlPushMatrix();
rlTranslatef(position.x, position.y, position.z);
//rlRotatef(rotation, 0, 1, 0);
rlScalef(length, length, length);
2014-09-03 18:51:28 +04:00
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);
2014-09-03 18:51:28 +04:00
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);
2014-09-03 18:51:28 +04:00
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);
2014-09-03 18:51:28 +04:00
rlEnd();
rlPopMatrix();
}
// Load a 3d model (from file)
2014-09-03 18:51:28 +04:00
Model LoadModel(const char *fileName)
{
2016-01-23 15:22:13 +03:00
Model model = { 0 };
2015-08-03 18:27:04 +03:00
// TODO: Initialize default data for model in case loading fails, maybe a cube?
2014-09-03 18:51:28 +04:00
if (strcmp(GetExtension(fileName),"obj") == 0) model.mesh = LoadOBJ(fileName);
2014-09-03 18:51:28 +04:00
else TraceLog(WARNING, "[%s] Model extension not recognized, it can't be loaded", fileName);
if (model.mesh.vertexCount == 0) TraceLog(WARNING, "Model could not be loaded");
2015-08-03 18:27:04 +03:00
else
2015-01-18 12:57:30 +03:00
{
rlglLoadMesh(&model.mesh); // Upload vertex data to GPU
model.transform = MatrixIdentity();
model.material = LoadDefaultMaterial();
2015-01-18 12:57:30 +03:00
}
2014-03-25 15:40:35 +04:00
return model;
}
// Load a 3d model (from vertex data)
Model LoadModelEx(Mesh data)
{
Model model = { 0 };
rlglLoadMesh(&data); // Upload vertex data to GPU
model.transform = MatrixIdentity();
model.material = LoadDefaultMaterial();
return model;
}
// Load a 3d model from rRES file (raylib Resource)
Model LoadModelFromRES(const char *rresName, int resId)
{
Model model = { 0 };
bool found = false;
char id[4]; // rRES file identifier
unsigned char version; // rRES file version and subversion
char useless; // rRES header reserved data
short numRes;
ResInfoHeader infoHeader;
FILE *rresFile = fopen(rresName, "rb");
if (rresFile == NULL)
{
TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName);
}
else
{
// Read rres file (basic file check - id)
fread(&id[0], sizeof(char), 1, rresFile);
fread(&id[1], sizeof(char), 1, rresFile);
fread(&id[2], sizeof(char), 1, rresFile);
fread(&id[3], sizeof(char), 1, rresFile);
fread(&version, sizeof(char), 1, rresFile);
fread(&useless, sizeof(char), 1, rresFile);
if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S'))
{
TraceLog(WARNING, "[%s] This is not a valid raylib resource file", rresName);
}
else
{
// Read number of resources embedded
fread(&numRes, sizeof(short), 1, rresFile);
for (int i = 0; i < numRes; i++)
{
fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile);
if (infoHeader.id == resId)
{
found = true;
// Check data is of valid MODEL type
if (infoHeader.type == 8)
{
// TODO: Load model data
}
else
{
TraceLog(WARNING, "[%s] Required resource do not seem to be a valid MODEL resource", rresName);
}
}
else
{
// Depending on type, skip the right amount of parameters
switch (infoHeader.type)
{
case 0: fseek(rresFile, 6, SEEK_CUR); break; // IMAGE: Jump 6 bytes of parameters
case 1: fseek(rresFile, 6, SEEK_CUR); break; // SOUND: Jump 6 bytes of parameters
case 2: fseek(rresFile, 5, SEEK_CUR); break; // MODEL: Jump 5 bytes of parameters (TODO: Review)
case 3: break; // TEXT: No parameters
case 4: break; // RAW: No parameters
default: break;
}
// Jump DATA to read next infoHeader
fseek(rresFile, infoHeader.size, SEEK_CUR);
}
}
}
fclose(rresFile);
}
if (!found) TraceLog(WARNING, "[%s] Required resource id [%i] could not be found in the raylib resource file", rresName, resId);
return model;
}
// Load a heightmap image as a 3d model
2016-02-11 17:51:04 +03:00
// NOTE: model map size is defined in generic units
Model LoadHeightmap(Image heightmap, Vector3 size)
{
Model model = { 0 };
model.mesh = GenMeshHeightmap(heightmap, size);
rlglLoadMesh(&model.mesh);
model.transform = MatrixIdentity();
model.material = LoadDefaultMaterial();
return model;
}
// Load a map image as a 3d model (cubes based)
Model LoadCubicmap(Image cubicmap)
{
Model model = { 0 };
model.mesh = GenMeshCubicmap(cubicmap, (Vector3){ 1.0, 1.0, 1.5f });
rlglLoadMesh(&model.mesh);
model.transform = MatrixIdentity();
model.material = LoadDefaultMaterial();
return model;
}
// Unload 3d model from memory
void UnloadModel(Model model)
{
// Unload mesh data
free(model.mesh.vertices);
free(model.mesh.texcoords);
if (model.mesh.normals != NULL) free(model.mesh.normals);
if (model.mesh.colors != NULL) free(model.mesh.colors);
if (model.mesh.tangents != NULL) free(model.mesh.tangents);
if (model.mesh.texcoords2 != NULL) free(model.mesh.texcoords2);
TraceLog(INFO, "Unloaded model data from RAM (CPU)");
rlDeleteBuffers(model.mesh.vboId[0]); // vertex
rlDeleteBuffers(model.mesh.vboId[1]); // texcoords
rlDeleteBuffers(model.mesh.vboId[2]); // normals
rlDeleteBuffers(model.mesh.vboId[3]); // colors
rlDeleteBuffers(model.mesh.vboId[4]); // tangents
rlDeleteBuffers(model.mesh.vboId[5]); // texcoords2
rlDeleteVertexArrays(model.mesh.vaoId);
}
// Load material data (from file)
Material LoadMaterial(const char *fileName)
{
Material material = { 0 };
if (strcmp(GetExtension(fileName),"mtl") == 0) material = LoadMTL(fileName);
else TraceLog(WARNING, "[%s] Material extension not recognized, it can't be loaded", fileName);
return material;
}
// Load default material (uses default models shader)
Material LoadDefaultMaterial(void)
{
Material material = { 0 };
material.shader = GetDefaultShader();
material.texDiffuse = GetDefaultTexture(); // White texture (1x1 pixel)
//material.texNormal; // NOTE: By default, not set
//material.texSpecular; // NOTE: By default, not set
material.colDiffuse = WHITE; // Diffuse color
material.colAmbient = WHITE; // Ambient color
material.colSpecular = WHITE; // Specular color
material.glossiness = 100.0f; // Glossiness level
material.normalDepth = 1.0f; // Normal map depth
return material;
}
// Link a texture to a model
void SetModelTexture(Model *model, Texture2D texture)
{
if (texture.id <= 0) model->material.texDiffuse.id = whiteTexture; // Use default white texture
else model->material.texDiffuse = texture;
}
2016-04-03 19:31:42 +03:00
// Generate a mesh from heightmap
static Mesh GenMeshHeightmap(Image heightmap, Vector3 size)
{
2016-02-11 17:51:04 +03:00
#define GRAY_VALUE(c) ((c.r+c.g+c.b)/3)
Mesh mesh = { 0 };
int mapX = heightmap.width;
int mapZ = heightmap.height;
2016-02-11 17:51:04 +03:00
Color *pixels = GetImageData(heightmap);
2014-09-03 18:51:28 +04:00
// NOTE: One vertex per pixel
int numTriangles = (mapX-1)*(mapZ-1)*2; // One quad every four pixels
2014-09-03 18:51:28 +04:00
mesh.vertexCount = numTriangles*3;
mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float));
mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float));
mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float));
mesh.colors = NULL;
2014-09-03 18:51:28 +04:00
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
2014-09-03 18:51:28 +04:00
int trisCounter = 0;
2014-09-03 18:51:28 +04:00
2016-02-11 17:51:04 +03:00
Vector3 scaleFactor = { size.x/mapX, size.y/255.0f, size.z/mapZ };
for(int z = 0; z < mapZ-1; z++)
{
for(int x = 0; x < mapX-1; x++)
{
// Fill vertices array with data
//----------------------------------------------------------
2014-09-03 18:51:28 +04:00
// one triangle - 3 vertex
2016-02-11 17:51:04 +03:00
mesh.vertices[vCounter] = (float)x*scaleFactor.x;
mesh.vertices[vCounter + 1] = (float)GRAY_VALUE(pixels[x + z*mapX])*scaleFactor.y;
mesh.vertices[vCounter + 2] = (float)z*scaleFactor.z;
2014-09-03 18:51:28 +04:00
2016-02-11 17:51:04 +03:00
mesh.vertices[vCounter + 3] = (float)x*scaleFactor.x;
mesh.vertices[vCounter + 4] = (float)GRAY_VALUE(pixels[x + (z + 1)*mapX])*scaleFactor.y;
mesh.vertices[vCounter + 5] = (float)(z + 1)*scaleFactor.z;
2014-09-03 18:51:28 +04:00
2016-02-11 17:51:04 +03:00
mesh.vertices[vCounter + 6] = (float)(x + 1)*scaleFactor.x;
mesh.vertices[vCounter + 7] = (float)GRAY_VALUE(pixels[(x + 1) + z*mapX])*scaleFactor.y;
mesh.vertices[vCounter + 8] = (float)z*scaleFactor.z;
2014-09-03 18:51:28 +04:00
// another triangle - 3 vertex
mesh.vertices[vCounter + 9] = mesh.vertices[vCounter + 6];
mesh.vertices[vCounter + 10] = mesh.vertices[vCounter + 7];
mesh.vertices[vCounter + 11] = mesh.vertices[vCounter + 8];
2014-09-03 18:51:28 +04:00
mesh.vertices[vCounter + 12] = mesh.vertices[vCounter + 3];
mesh.vertices[vCounter + 13] = mesh.vertices[vCounter + 4];
mesh.vertices[vCounter + 14] = mesh.vertices[vCounter + 5];
2014-09-03 18:51:28 +04:00
2016-02-11 17:51:04 +03:00
mesh.vertices[vCounter + 15] = (float)(x + 1)*scaleFactor.x;
mesh.vertices[vCounter + 16] = (float)GRAY_VALUE(pixels[(x + 1) + (z + 1)*mapX])*scaleFactor.y;
mesh.vertices[vCounter + 17] = (float)(z + 1)*scaleFactor.z;
vCounter += 18; // 6 vertex, 18 floats
2014-09-03 18:51:28 +04:00
// Fill texcoords array with data
//--------------------------------------------------------------
2016-02-11 17:51:04 +03:00
mesh.texcoords[tcCounter] = (float)x/(mapX - 1);
mesh.texcoords[tcCounter + 1] = (float)z/(mapZ - 1);
2014-09-03 18:51:28 +04:00
2016-02-11 17:51:04 +03:00
mesh.texcoords[tcCounter + 2] = (float)x/(mapX - 1);
mesh.texcoords[tcCounter + 3] = (float)(z + 1)/(mapZ - 1);
2014-09-03 18:51:28 +04:00
2016-02-11 17:51:04 +03:00
mesh.texcoords[tcCounter + 4] = (float)(x + 1)/(mapX - 1);
mesh.texcoords[tcCounter + 5] = (float)z/(mapZ - 1);
2014-09-03 18:51:28 +04:00
mesh.texcoords[tcCounter + 6] = mesh.texcoords[tcCounter + 4];
mesh.texcoords[tcCounter + 7] = mesh.texcoords[tcCounter + 5];
2014-09-03 18:51:28 +04:00
mesh.texcoords[tcCounter + 8] = mesh.texcoords[tcCounter + 2];
mesh.texcoords[tcCounter + 9] = mesh.texcoords[tcCounter + 3];
2014-09-03 18:51:28 +04:00
2016-02-11 17:51:04 +03:00
mesh.texcoords[tcCounter + 10] = (float)(x + 1)/(mapX - 1);
mesh.texcoords[tcCounter + 11] = (float)(z + 1)/(mapZ - 1);
tcCounter += 12; // 6 texcoords, 12 floats
2014-09-03 18:51:28 +04:00
// Fill normals array with data
//--------------------------------------------------------------
for (int i = 0; i < 18; i += 3)
{
mesh.normals[nCounter + i] = 0.0f;
mesh.normals[nCounter + i + 1] = 1.0f;
mesh.normals[nCounter + i + 2] = 0.0f;
}
2014-09-03 18:51:28 +04:00
// TODO: Calculate normals in an efficient way
2014-09-03 18:51:28 +04:00
nCounter += 18; // 6 vertex, 18 floats
trisCounter += 2;
}
}
2016-02-11 17:51:04 +03:00
free(pixels);
2014-09-03 18:51:28 +04:00
return mesh;
}
static Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
{
Mesh mesh = { 0 };
Color *cubicmapPixels = GetImageData(cubicmap);
int mapWidth = cubicmap.width*(int)cubeSize.x;
int mapHeight = cubicmap.height*(int)cubeSize.z;
2014-09-03 18:51:28 +04:00
// NOTE: Max possible number of triangles numCubes * (12 triangles by cube)
int maxTriangles = cubicmap.width*cubicmap.height*12;
int vCounter = 0; // Used to count vertices
int tcCounter = 0; // Used to count texcoords
int nCounter = 0; // Used to count normals
2014-09-03 18:51:28 +04:00
float w = cubeSize.x;
float h = cubeSize.z;
float h2 = cubeSize.y;
2014-09-03 18:51:28 +04:00
2016-01-13 21:30:35 +03:00
Vector3 *mapVertices = (Vector3 *)malloc(maxTriangles*3*sizeof(Vector3));
Vector2 *mapTexcoords = (Vector2 *)malloc(maxTriangles*3*sizeof(Vector2));
Vector3 *mapNormals = (Vector3 *)malloc(maxTriangles*3*sizeof(Vector3));
// 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 };
2015-02-09 20:29:32 +03:00
// NOTE: We use texture rectangles to define different textures for top-bottom-front-back-right-left (6)
typedef struct RectangleF {
float x;
float y;
float width;
float height;
} RectangleF;
2016-01-13 21:30:35 +03:00
RectangleF rightTexUV = { 0.0f, 0.0f, 0.5f, 0.5f };
RectangleF leftTexUV = { 0.5f, 0.0f, 0.5f, 0.5f };
RectangleF frontTexUV = { 0.0f, 0.0f, 0.5f, 0.5f };
RectangleF backTexUV = { 0.5f, 0.0f, 0.5f, 0.5f };
RectangleF topTexUV = { 0.0f, 0.5f, 0.5f, 0.5f };
RectangleF bottomTexUV = { 0.5f, 0.5f, 0.5f, 0.5f };
for (int z = 0; z < mapHeight; z += cubeSize.z)
{
for (int x = 0; x < mapWidth; x += cubeSize.x)
2014-09-03 18:51:28 +04:00
{
// 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 };
2014-09-03 18:51:28 +04:00
// We check pixel color to be WHITE, we will full cubes
if ((cubicmapPixels[z*cubicmap.width + x].r == 255) &&
(cubicmapPixels[z*cubicmap.width + x].g == 255) &&
(cubicmapPixels[z*cubicmap.width + x].b == 255))
{
// Define triangles (Checking Collateral Cubes!)
//----------------------------------------------
2014-09-03 18:51:28 +04:00
// Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4)
mapVertices[vCounter] = v1;
2014-09-03 18:51:28 +04:00
mapVertices[vCounter + 1] = v2;
mapVertices[vCounter + 2] = v3;
mapVertices[vCounter + 3] = v1;
mapVertices[vCounter + 4] = v3;
mapVertices[vCounter + 5] = v4;
vCounter += 6;
2014-09-03 18:51:28 +04:00
mapNormals[nCounter] = n3;
2014-09-03 18:51:28 +04:00
mapNormals[nCounter + 1] = n3;
mapNormals[nCounter + 2] = n3;
mapNormals[nCounter + 3] = n3;
mapNormals[nCounter + 4] = n3;
mapNormals[nCounter + 5] = n3;
nCounter += 6;
2014-09-03 18:51:28 +04:00
mapTexcoords[tcCounter] = (Vector2){ topTexUV.x, topTexUV.y };
mapTexcoords[tcCounter + 1] = (Vector2){ topTexUV.x, topTexUV.y + topTexUV.height };
mapTexcoords[tcCounter + 2] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
mapTexcoords[tcCounter + 3] = (Vector2){ topTexUV.x, topTexUV.y };
mapTexcoords[tcCounter + 4] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
mapTexcoords[tcCounter + 5] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y };
tcCounter += 6;
2014-09-03 18:51:28 +04:00
// Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8)
mapVertices[vCounter] = v6;
2014-09-03 18:51:28 +04:00
mapVertices[vCounter + 1] = v8;
mapVertices[vCounter + 2] = v7;
mapVertices[vCounter + 3] = v6;
mapVertices[vCounter + 4] = v5;
mapVertices[vCounter + 5] = v8;
vCounter += 6;
2014-09-03 18:51:28 +04:00
mapNormals[nCounter] = n4;
2014-09-03 18:51:28 +04:00
mapNormals[nCounter + 1] = n4;
mapNormals[nCounter + 2] = n4;
mapNormals[nCounter + 3] = n4;
mapNormals[nCounter + 4] = n4;
mapNormals[nCounter + 5] = n4;
nCounter += 6;
2014-09-03 18:51:28 +04:00
mapTexcoords[tcCounter] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
mapTexcoords[tcCounter + 1] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
mapTexcoords[tcCounter + 2] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y + bottomTexUV.height };
mapTexcoords[tcCounter + 3] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
mapTexcoords[tcCounter + 4] = (Vector2){ bottomTexUV.x, bottomTexUV.y };
mapTexcoords[tcCounter + 5] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
tcCounter += 6;
2014-09-03 18:51:28 +04:00
if (((z < cubicmap.height - 1) &&
(cubicmapPixels[(z + 1)*cubicmap.width + x].r == 0) &&
(cubicmapPixels[(z + 1)*cubicmap.width + x].g == 0) &&
(cubicmapPixels[(z + 1)*cubicmap.width + x].b == 0)) || (z == cubicmap.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;
2014-09-03 18:51:28 +04:00
mapVertices[vCounter + 1] = v7;
mapVertices[vCounter + 2] = v3;
mapVertices[vCounter + 3] = v3;
mapVertices[vCounter + 4] = v7;
mapVertices[vCounter + 5] = v8;
vCounter += 6;
2014-09-03 18:51:28 +04:00
mapNormals[nCounter] = n6;
2014-09-03 18:51:28 +04:00
mapNormals[nCounter + 1] = n6;
mapNormals[nCounter + 2] = n6;
mapNormals[nCounter + 3] = n6;
mapNormals[nCounter + 4] = n6;
mapNormals[nCounter + 5] = n6;
nCounter += 6;
2014-09-03 18:51:28 +04:00
mapTexcoords[tcCounter] = (Vector2){ frontTexUV.x, frontTexUV.y };
mapTexcoords[tcCounter + 1] = (Vector2){ frontTexUV.x, frontTexUV.y + frontTexUV.height };
mapTexcoords[tcCounter + 2] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y };
mapTexcoords[tcCounter + 3] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y };
mapTexcoords[tcCounter + 4] = (Vector2){ frontTexUV.x, frontTexUV.y + frontTexUV.height };
mapTexcoords[tcCounter + 5] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y + frontTexUV.height };
tcCounter += 6;
}
2014-09-03 18:51:28 +04:00
if (((z > 0) &&
(cubicmapPixels[(z - 1)*cubicmap.width + x].r == 0) &&
(cubicmapPixels[(z - 1)*cubicmap.width + x].g == 0) &&
(cubicmapPixels[(z - 1)*cubicmap.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;
2014-09-03 18:51:28 +04:00
mapVertices[vCounter + 1] = v5;
mapVertices[vCounter + 2] = v6;
mapVertices[vCounter + 3] = v1;
mapVertices[vCounter + 4] = v4;
mapVertices[vCounter + 5] = v5;
vCounter += 6;
2014-09-03 18:51:28 +04:00
mapNormals[nCounter] = n5;
2014-09-03 18:51:28 +04:00
mapNormals[nCounter + 1] = n5;
mapNormals[nCounter + 2] = n5;
mapNormals[nCounter + 3] = n5;
mapNormals[nCounter + 4] = n5;
mapNormals[nCounter + 5] = n5;
nCounter += 6;
2014-09-03 18:51:28 +04:00
mapTexcoords[tcCounter] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y };
mapTexcoords[tcCounter + 1] = (Vector2){ backTexUV.x, backTexUV.y + backTexUV.height };
mapTexcoords[tcCounter + 2] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y + backTexUV.height };
mapTexcoords[tcCounter + 3] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y };
mapTexcoords[tcCounter + 4] = (Vector2){ backTexUV.x, backTexUV.y };
mapTexcoords[tcCounter + 5] = (Vector2){ backTexUV.x, backTexUV.y + backTexUV.height };
tcCounter += 6;
}
2014-09-03 18:51:28 +04:00
if (((x < cubicmap.width - 1) &&
(cubicmapPixels[z*cubicmap.width + (x + 1)].r == 0) &&
(cubicmapPixels[z*cubicmap.width + (x + 1)].g == 0) &&
(cubicmapPixels[z*cubicmap.width + (x + 1)].b == 0)) || (x == cubicmap.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;
2014-09-03 18:51:28 +04:00
mapVertices[vCounter + 1] = v8;
mapVertices[vCounter + 2] = v4;
mapVertices[vCounter + 3] = v4;
mapVertices[vCounter + 4] = v8;
mapVertices[vCounter + 5] = v5;
vCounter += 6;
2014-09-03 18:51:28 +04:00
mapNormals[nCounter] = n1;
2014-09-03 18:51:28 +04:00
mapNormals[nCounter + 1] = n1;
mapNormals[nCounter + 2] = n1;
mapNormals[nCounter + 3] = n1;
mapNormals[nCounter + 4] = n1;
mapNormals[nCounter + 5] = n1;
nCounter += 6;
2014-09-03 18:51:28 +04:00
mapTexcoords[tcCounter] = (Vector2){ rightTexUV.x, rightTexUV.y };
mapTexcoords[tcCounter + 1] = (Vector2){ rightTexUV.x, rightTexUV.y + rightTexUV.height };
mapTexcoords[tcCounter + 2] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y };
mapTexcoords[tcCounter + 3] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y };
mapTexcoords[tcCounter + 4] = (Vector2){ rightTexUV.x, rightTexUV.y + rightTexUV.height };
mapTexcoords[tcCounter + 5] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y + rightTexUV.height };
tcCounter += 6;
}
2014-09-03 18:51:28 +04:00
if (((x > 0) &&
(cubicmapPixels[z*cubicmap.width + (x - 1)].r == 0) &&
(cubicmapPixels[z*cubicmap.width + (x - 1)].g == 0) &&
(cubicmapPixels[z*cubicmap.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;
2014-09-03 18:51:28 +04:00
mapVertices[vCounter + 1] = v7;
mapVertices[vCounter + 2] = v2;
mapVertices[vCounter + 3] = v1;
mapVertices[vCounter + 4] = v6;
mapVertices[vCounter + 5] = v7;
vCounter += 6;
2014-09-03 18:51:28 +04:00
mapNormals[nCounter] = n2;
2014-09-03 18:51:28 +04:00
mapNormals[nCounter + 1] = n2;
mapNormals[nCounter + 2] = n2;
mapNormals[nCounter + 3] = n2;
mapNormals[nCounter + 4] = n2;
mapNormals[nCounter + 5] = n2;
nCounter += 6;
2014-09-03 18:51:28 +04:00
mapTexcoords[tcCounter] = (Vector2){ leftTexUV.x, leftTexUV.y };
mapTexcoords[tcCounter + 1] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y + leftTexUV.height };
mapTexcoords[tcCounter + 2] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y };
mapTexcoords[tcCounter + 3] = (Vector2){ leftTexUV.x, leftTexUV.y };
mapTexcoords[tcCounter + 4] = (Vector2){ leftTexUV.x, leftTexUV.y + leftTexUV.height };
mapTexcoords[tcCounter + 5] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y + leftTexUV.height };
tcCounter += 6;
}
}
// We check pixel color to be BLACK, we will only draw floor and roof
else if ((cubicmapPixels[z*cubicmap.width + x].r == 0) &&
(cubicmapPixels[z*cubicmap.width + x].g == 0) &&
(cubicmapPixels[z*cubicmap.width + x].b == 0))
{
// Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4)
mapVertices[vCounter] = v1;
mapVertices[vCounter + 1] = v3;
mapVertices[vCounter + 2] = v2;
mapVertices[vCounter + 3] = v1;
mapVertices[vCounter + 4] = v4;
mapVertices[vCounter + 5] = v3;
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] = (Vector2){ topTexUV.x, topTexUV.y };
mapTexcoords[tcCounter + 1] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
mapTexcoords[tcCounter + 2] = (Vector2){ topTexUV.x, topTexUV.y + topTexUV.height };
mapTexcoords[tcCounter + 3] = (Vector2){ topTexUV.x, topTexUV.y };
mapTexcoords[tcCounter + 4] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y };
mapTexcoords[tcCounter + 5] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
tcCounter += 6;
// Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8)
mapVertices[vCounter] = v6;
mapVertices[vCounter + 1] = v7;
mapVertices[vCounter + 2] = v8;
mapVertices[vCounter + 3] = v6;
mapVertices[vCounter + 4] = v8;
mapVertices[vCounter + 5] = v5;
vCounter += 6;
2014-09-03 18:51:28 +04:00
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] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
mapTexcoords[tcCounter + 1] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y + bottomTexUV.height };
mapTexcoords[tcCounter + 2] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
mapTexcoords[tcCounter + 3] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
mapTexcoords[tcCounter + 4] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
mapTexcoords[tcCounter + 5] = (Vector2){ bottomTexUV.x, bottomTexUV.y };
tcCounter += 6;
}
2014-09-03 18:51:28 +04:00
}
}
// Move data from mapVertices temp arays to vertices float array
mesh.vertexCount = vCounter;
2014-09-03 18:51:28 +04:00
mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float));
mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float));
mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float));
mesh.colors = NULL;
2014-09-03 18:51:28 +04:00
int fCounter = 0;
2014-09-03 18:51:28 +04:00
// Move vertices data
for (int i = 0; i < vCounter; i++)
{
mesh.vertices[fCounter] = mapVertices[i].x;
mesh.vertices[fCounter + 1] = mapVertices[i].y;
mesh.vertices[fCounter + 2] = mapVertices[i].z;
fCounter += 3;
}
2014-09-03 18:51:28 +04:00
fCounter = 0;
2014-09-03 18:51:28 +04:00
// Move normals data
for (int i = 0; i < nCounter; i++)
{
mesh.normals[fCounter] = mapNormals[i].x;
mesh.normals[fCounter + 1] = mapNormals[i].y;
mesh.normals[fCounter + 2] = mapNormals[i].z;
fCounter += 3;
}
2014-09-03 18:51:28 +04:00
fCounter = 0;
2014-09-03 18:51:28 +04:00
// Move texcoords data
for (int i = 0; i < tcCounter; i++)
{
mesh.texcoords[fCounter] = mapTexcoords[i].x;
mesh.texcoords[fCounter + 1] = mapTexcoords[i].y;
fCounter += 2;
}
2014-09-03 18:51:28 +04:00
free(mapVertices);
free(mapNormals);
free(mapTexcoords);
free(cubicmapPixels); // Free image pixel data
2015-08-05 20:17:56 +03:00
return mesh;
}
// Draw a model (with texture if set)
void DrawModel(Model model, Vector3 position, float scale, Color tint)
{
Vector3 vScale = { scale, scale, scale };
2016-01-13 21:30:35 +03:00
Vector3 rotationAxis = { 0.0f, 0.0f, 0.0f };
DrawModelEx(model, position, rotationAxis, 0.0f, vScale, tint);
}
// Draw a model with extended parameters
void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
{
// Calculate transformation matrix from function parameters
// Get transform matrix (rotation -> scale -> translation)
Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD);
Matrix matScale = MatrixScale(scale.x, scale.y, scale.z);
Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z);
// Combine model transformation matrix (model.transform) with matrix generated by function parameters (matTransform)
//Matrix matModel = MatrixMultiply(model.transform, matTransform); // Transform to world-space coordinates
model.transform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation);
model.material.colDiffuse = tint;
rlglDrawEx(model.mesh, model.material, model.transform, false);
}
// Draw a model wires (with texture if set)
void DrawModelWires(Model model, Vector3 position, float scale, Color tint)
{
Vector3 vScale = { scale, scale, scale };
2016-01-13 21:30:35 +03:00
Vector3 rotationAxis = { 0.0f, 0.0f, 0.0f };
// Calculate transformation matrix from function parameters
// Get transform matrix (rotation -> scale -> translation)
Matrix matRotation = MatrixRotate(rotationAxis, 0.0f);
Matrix matScale = MatrixScale(vScale.x, vScale.y, vScale.z);
Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z);
model.transform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation);
model.material.colDiffuse = tint;
rlglDrawEx(model.mesh, model.material, model.transform, true);
}
// Draw a model wires (with texture if set) with extended parameters
void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
{
// Calculate transformation matrix from function parameters
// Get transform matrix (rotation -> scale -> translation)
Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD);
Matrix matScale = MatrixScale(scale.x, scale.y, scale.z);
Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z);
model.transform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation);
model.material.colDiffuse = tint;
rlglDrawEx(model.mesh, model.material, model.transform, true);
}
// Draw a billboard
void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint)
{
Rectangle sourceRec = { 0, 0, texture.width, texture.height };
DrawBillboardRec(camera, texture, sourceRec, center, size, tint);
}
// 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);
2014-09-03 18:51:28 +04:00
Vector3 right = { viewMatrix.m0, viewMatrix.m4, viewMatrix.m8 };
//Vector3 up = { viewMatrix.m1, viewMatrix.m5, viewMatrix.m9 };
// NOTE: Billboard locked on axis-Y
Vector3 up = { 0.0f, 1.0f, 0.0f };
2014-09-03 18:51:28 +04:00
/*
2014-11-24 19:23:05 +03:00
a-------b
| |
| * |
| |
2014-11-24 19:23:05 +03:00
d-------c
2014-09-03 18:51:28 +04:00
*/
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);
2014-09-03 18:51:28 +04:00
rlEnableTexture(texture.id);
2014-09-03 18:51:28 +04:00
2014-03-25 15:40:35 +04:00
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
2014-09-03 18:51:28 +04:00
// Bottom-left corner for texture and quad
2014-09-03 18:51:28 +04:00
rlTexCoord2f((float)sourceRec.x / texture.width, (float)sourceRec.y / texture.height);
2014-03-25 15:40:35 +04:00
rlVertex3f(a.x, a.y, a.z);
2015-02-02 02:53:49 +03:00
2014-11-24 19:23:05 +03:00
// 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);
2015-02-02 02:53:49 +03:00
// Top-right corner for texture and quad
2014-09-03 18:51:28 +04:00
rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
2014-03-25 15:40:35 +04:00
rlVertex3f(c.x, c.y, c.z);
2014-09-03 18:51:28 +04:00
2014-11-24 19:23:05 +03:00
// 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);
2014-03-25 15:40:35 +04:00
rlEnd();
2014-09-03 18:51:28 +04:00
2014-03-25 15:40:35 +04:00
rlDisableTexture();
}
2016-01-25 13:12:31 +03:00
// Draw a bounding box with wires
2016-03-01 22:26:01 +03:00
void DrawBoundingBox(BoundingBox box, Color color)
2016-01-25 13:12:31 +03:00
{
Vector3 size;
size.x = fabsf(box.max.x - box.min.x);
size.y = fabsf(box.max.y - box.min.y);
size.z = fabsf(box.max.z - box.min.z);
Vector3 center = { box.min.x + size.x/2.0f, box.min.y + size.y/2.0f, box.min.z + size.z/2.0f };
2016-03-01 22:26:01 +03:00
DrawCubeWires(center, size.x, size.y, size.z, color);
2016-01-25 13:12:31 +03:00
}
// Detect collision between two spheres
2015-02-09 20:35:25 +03:00
bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB)
{
bool collision = false;
float dx = centerA.x - centerB.x; // X distance between centers
float dy = centerA.y - centerB.y; // Y distance between centers
float dz = centerA.z - centerB.z; // Y distance between centers
float distance = sqrt(dx*dx + dy*dy + dz*dz); // Distance between centers
if (distance <= (radiusA + radiusB)) collision = true;
2015-02-09 20:35:25 +03:00
return collision;
}
// Detect collision between two boxes
// NOTE: Boxes are defined by two points minimum and maximum
bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2)
2015-02-09 20:35:25 +03:00
{
bool collision = true;
if ((box1.max.x >= box2.min.x) && (box1.min.x <= box2.max.x))
2015-02-09 20:35:25 +03:00
{
if ((box1.max.y < box2.min.y) || (box1.min.y > box2.max.y)) collision = false;
if ((box1.max.z < box2.min.z) || (box1.min.z > box2.max.z)) collision = false;
2015-02-09 20:35:25 +03:00
}
else collision = false;
return collision;
}
// Detect collision between box and sphere
bool CheckCollisionBoxSphere(BoundingBox box, Vector3 centerSphere, float radiusSphere)
2015-02-09 20:35:25 +03:00
{
bool collision = false;
float dmin = 0;
2015-02-09 20:35:25 +03:00
if (centerSphere.x < box.min.x) dmin += pow(centerSphere.x - box.min.x, 2);
else if (centerSphere.x > box.max.x) dmin += pow(centerSphere.x - box.max.x, 2);
2015-02-09 20:35:25 +03:00
if (centerSphere.y < box.min.y) dmin += pow(centerSphere.y - box.min.y, 2);
else if (centerSphere.y > box.max.y) dmin += pow(centerSphere.y - box.max.y, 2);
2015-02-09 20:35:25 +03:00
if (centerSphere.z < box.min.z) dmin += pow(centerSphere.z - box.min.z, 2);
else if (centerSphere.z > box.max.z) dmin += pow(centerSphere.z - box.max.z, 2);
2015-02-09 20:35:25 +03:00
if (dmin <= (radiusSphere*radiusSphere)) collision = true;
2015-02-09 20:35:25 +03:00
return collision;
}
2016-01-19 22:27:41 +03:00
// Detect collision between ray and sphere
bool CheckCollisionRaySphere(Ray ray, Vector3 spherePosition, float sphereRadius)
{
bool collision = false;
2016-01-20 21:28:47 +03:00
Vector3 raySpherePos = VectorSubtract(spherePosition, ray.position);
float distance = VectorLength(raySpherePos);
float vector = VectorDotProduct(raySpherePos, ray.direction);
float d = sphereRadius*sphereRadius - (distance*distance - vector*vector);
if(d >= 0.0f) collision = true;
return collision;
}
// Detect collision between ray and sphere with extended parameters and collision point detection
bool CheckCollisionRaySphereEx(Ray ray, Vector3 spherePosition, float sphereRadius, Vector3 *collisionPoint)
{
bool collision = false;
Vector3 raySpherePos = VectorSubtract(spherePosition, ray.position);
float distance = VectorLength(raySpherePos);
float vector = VectorDotProduct(raySpherePos, ray.direction);
float d = sphereRadius*sphereRadius - (distance*distance - vector*vector);
if(d >= 0.0f) collision = true;
// Calculate collision point
Vector3 offset = ray.direction;
float collisionDistance = 0;
// Check if ray origin is inside the sphere to calculate the correct collision point
if(distance < sphereRadius) collisionDistance = vector + sqrt(d);
else collisionDistance = vector - sqrt(d);
VectorScale(&offset, collisionDistance);
Vector3 cPoint = VectorAdd(ray.position, offset);
collisionPoint->x = cPoint.x;
collisionPoint->y = cPoint.y;
collisionPoint->z = cPoint.z;
2016-01-19 22:27:41 +03:00
return collision;
}
// Detect collision between ray and bounding box
bool CheckCollisionRayBox(Ray ray, BoundingBox box)
{
bool collision = false;
float t[8];
t[0] = (box.min.x - ray.position.x)/ray.direction.x;
t[1] = (box.max.x - ray.position.x)/ray.direction.x;
t[2] = (box.min.y - ray.position.y)/ray.direction.y;
t[3] = (box.max.y - ray.position.y)/ray.direction.y;
t[4] = (box.min.z - ray.position.z)/ray.direction.z;
t[5] = (box.max.z - ray.position.z)/ray.direction.z;
t[6] = fmax(fmax(fmin(t[0], t[1]), fmin(t[2], t[3])), fmin(t[4], t[5]));
t[7] = fmin(fmin(fmax(t[0], t[1]), fmax(t[2], t[3])), fmax(t[4], t[5]));
collision = !(t[7] < 0 || t[6] > t[7]);
return collision;
}
2016-01-19 22:27:41 +03:00
// Calculate mesh bounding box limits
2016-01-25 13:12:31 +03:00
// NOTE: minVertex and maxVertex should be transformed by model transform matrix (position, scale, rotate)
2016-01-19 22:27:41 +03:00
BoundingBox CalculateBoundingBox(Mesh mesh)
{
// Get min and max vertex to construct bounds (AABB)
Vector3 minVertex = { 0 };
Vector3 maxVertex = { 0 };
2016-01-19 22:27:41 +03:00
if (mesh.vertices != NULL)
2016-01-19 22:27:41 +03:00
{
minVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] };
maxVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] };
for (int i = 1; i < mesh.vertexCount; i++)
{
minVertex = VectorMin(minVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] });
maxVertex = VectorMax(maxVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] });
}
2016-01-19 22:27:41 +03:00
}
2016-01-25 13:12:31 +03:00
2016-01-19 22:27:41 +03:00
// Create the bounding box
BoundingBox box;
box.min = minVertex;
box.max = maxVertex;
return box;
}
2015-02-09 20:35:25 +03:00
// Detect and resolve cubicmap collisions
// NOTE: player position (or camera) is modified inside this function
Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition, float radius)
2015-02-09 20:35:25 +03:00
{
Color *cubicmapPixels = GetImageData(cubicmap);
2015-02-09 20:35:25 +03:00
// Detect the cell where the player is located
2016-01-13 21:30:35 +03:00
Vector3 impactDirection = { 0.0f, 0.0f, 0.0f };
2015-02-09 20:35:25 +03:00
int locationCellX = 0;
int locationCellY = 0;
locationCellX = floor(playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE);
locationCellY = floor(playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE);
if (locationCellX >= 0 && locationCellY >= 0 && locationCellX < cubicmap.width && locationCellY < cubicmap.height)
2015-02-09 20:35:25 +03:00
{
// Multiple Axis --------------------------------------------------------------------------------------------
// Axis x-, y-
if (locationCellX > 0 && locationCellY > 0)
2015-02-09 20:35:25 +03:00
{
if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) &&
(cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0))
{
if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) &&
((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius))
{
playerPosition->x = locationCellX + mapPosition.x - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
playerPosition->z = locationCellY + mapPosition.z - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
2016-01-13 21:30:35 +03:00
impactDirection = (Vector3){ 1.0f, 0.0f, 1.0f };
}
}
2015-02-09 20:35:25 +03:00
}
// Axis x-, y+
if (locationCellX > 0 && locationCellY < cubicmap.height - 1)
2015-02-09 20:35:25 +03:00
{
if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) &&
(cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0))
{
if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) &&
((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius))
{
playerPosition->x = locationCellX + mapPosition.x - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
playerPosition->z = locationCellY + mapPosition.z + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
2016-01-13 21:30:35 +03:00
impactDirection = (Vector3){ 1.0f, 0.0f, 1.0f };
}
}
2015-02-09 20:35:25 +03:00
}
// Axis x+, y-
if (locationCellX < cubicmap.width - 1 && locationCellY > 0)
2015-02-09 20:35:25 +03:00
{
if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) &&
(cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0))
{
if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) &&
((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius))
{
playerPosition->x = locationCellX + mapPosition.x + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
playerPosition->z = locationCellY + mapPosition.z - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
2016-01-13 21:30:35 +03:00
impactDirection = (Vector3){ 1.0f, 0.0f, 1.0f };
}
}
2015-02-09 20:35:25 +03:00
}
// Axis x+, y+
if (locationCellX < cubicmap.width - 1 && locationCellY < cubicmap.height - 1)
2015-02-09 20:35:25 +03:00
{
if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) &&
(cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0))
{
if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) &&
((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius))
{
playerPosition->x = locationCellX + mapPosition.x + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
playerPosition->z = locationCellY + mapPosition.z + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
2016-01-13 21:30:35 +03:00
impactDirection = (Vector3){ 1.0f, 0.0f, 1.0f };
}
}
2015-02-09 20:35:25 +03:00
}
// Single Axis ---------------------------------------------------------------------------------------------------
// Axis x-
if (locationCellX > 0)
2015-02-09 20:35:25 +03:00
{
if (cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0)
{
if ((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius)
{
playerPosition->x = locationCellX + mapPosition.x - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
2016-01-13 21:30:35 +03:00
impactDirection = (Vector3){ 1.0f, 0.0f, 0.0f };
}
}
2015-02-09 20:35:25 +03:00
}
// Axis x+
if (locationCellX < cubicmap.width - 1)
2015-02-09 20:35:25 +03:00
{
if (cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0)
{
if ((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius)
{
playerPosition->x = locationCellX + mapPosition.x + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
2016-01-13 21:30:35 +03:00
impactDirection = (Vector3){ 1.0f, 0.0f, 0.0f };
}
}
2015-02-09 20:35:25 +03:00
}
// Axis y-
if (locationCellY > 0)
2015-02-09 20:35:25 +03:00
{
if (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)
{
if ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)
{
playerPosition->z = locationCellY + mapPosition.z - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
2016-01-13 21:30:35 +03:00
impactDirection = (Vector3){ 0.0f, 0.0f, 1.0f };
}
}
2015-02-09 20:35:25 +03:00
}
// Axis y+
if (locationCellY < cubicmap.height - 1)
2015-02-09 20:35:25 +03:00
{
if (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)
{
if ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)
{
playerPosition->z = locationCellY + mapPosition.z + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
2016-01-13 21:30:35 +03:00
impactDirection = (Vector3){ 0.0f, 0.0f, 1.0f };
}
}
2015-02-09 20:35:25 +03:00
}
// Diagonals -------------------------------------------------------------------------------------------------------
// Axis x-, y-
if (locationCellX > 0 && locationCellY > 0)
2015-02-09 20:35:25 +03:00
{
if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) &&
(cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) &&
(cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX - 1)].r != 0))
{
if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) &&
((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius))
{
if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) > ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY)) playerPosition->x = locationCellX + mapPosition.x - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
else playerPosition->z = locationCellY + mapPosition.z - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
// Return ricochet
if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius / 3) &&
((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius / 3))
{
2016-01-13 21:30:35 +03:00
impactDirection = (Vector3){ 1.0f, 0.0f, 1.0f };
}
}
}
2015-02-09 20:35:25 +03:00
}
// Axis x-, y+
if (locationCellX > 0 && locationCellY < cubicmap.height - 1)
2015-02-09 20:35:25 +03:00
{
if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) &&
(cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) &&
(cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX - 1)].r != 0))
{
if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) &&
((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius))
{
if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) > (1 - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY))) playerPosition->x = locationCellX + mapPosition.x - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
else playerPosition->z = locationCellY + mapPosition.z + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
// Return ricochet
if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius / 3) &&
((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius / 3))
{
2016-01-13 21:30:35 +03:00
impactDirection = (Vector3){ 1.0f, 0.0f, 1.0f };
}
}
}
2015-02-09 20:35:25 +03:00
}
// Axis x+, y-
if (locationCellX < cubicmap.width - 1 && locationCellY > 0)
2015-02-09 20:35:25 +03:00
{
if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) &&
(cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) &&
(cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX + 1)].r != 0))
{
if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) &&
((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius))
{
if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) < (1 - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY))) playerPosition->x = locationCellX + mapPosition.x + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
else playerPosition->z = locationCellY + mapPosition.z - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
// Return ricochet
if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius / 3) &&
((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius / 3))
{
2016-01-13 21:30:35 +03:00
impactDirection = (Vector3){ 1.0f, 0.0f, 1.0f };
}
}
}
2015-02-09 20:35:25 +03:00
}
// Axis x+, y+
if (locationCellX < cubicmap.width - 1 && locationCellY < cubicmap.height - 1)
2015-02-09 20:35:25 +03:00
{
if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) &&
(cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) &&
(cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX + 1)].r != 0))
{
if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) &&
((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius))
{
if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) < ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY)) playerPosition->x = locationCellX + mapPosition.x + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
else playerPosition->z = locationCellY + mapPosition.z + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
// Return ricochet
if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius / 3) &&
((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius / 3))
{
2016-01-13 21:30:35 +03:00
impactDirection = (Vector3){ 1.0f, 0.0f, 1.0f };
}
}
}
2015-02-09 20:35:25 +03:00
}
}
// Floor collision
if (playerPosition->y <= radius)
{
2016-01-13 21:30:35 +03:00
playerPosition->y = radius + 0.01f;
impactDirection = (Vector3) { impactDirection.x, 1, impactDirection.z};
}
// Roof collision
2016-01-13 21:30:35 +03:00
else if (playerPosition->y >= (1.5f - radius))
{
2016-01-13 21:30:35 +03:00
playerPosition->y = (1.5f - radius) - 0.01f;
impactDirection = (Vector3) { impactDirection.x, 1, impactDirection.z};
}
free(cubicmapPixels);
return impactDirection;
2015-02-09 20:35:25 +03:00
}
//----------------------------------------------------------------------------------
// Module specific Functions Definition
//----------------------------------------------------------------------------------
// Load OBJ mesh data
static Mesh LoadOBJ(const char *fileName)
{
Mesh mesh = { 0 };
2014-09-03 18:51:28 +04:00
char dataType;
char comments[200];
2014-09-03 18:51:28 +04:00
int numVertex = 0;
int numNormals = 0;
int numTexCoords = 0;
int numTriangles = 0;
FILE *objFile;
objFile = fopen(fileName, "rt");
2015-02-02 02:53:49 +03:00
2014-12-17 21:32:54 +03:00
if (objFile == NULL)
{
TraceLog(WARNING, "[%s] OBJ file could not be opened", fileName);
return mesh;
2014-12-17 21:32:54 +03:00
}
2014-09-03 18:51:28 +04:00
// 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 (3 vertex per face)
while(!feof(objFile))
{
fscanf(objFile, "%c", &dataType);
2014-09-03 18:51:28 +04:00
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]
{
2014-09-03 18:51:28 +04:00
fgets(comments, 200, objFile);
} break;
case 'v':
{
fscanf(objFile, "%c", &dataType);
2014-09-03 18:51:28 +04:00
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;
}
}
2014-09-03 18:51:28 +04:00
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);
2014-09-03 18:51:28 +04:00
// 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
2014-09-03 18:51:28 +04:00
// 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 different meshes can have different vertex data available (position, texcoords, normals)
while(!feof(objFile))
{
fscanf(objFile, "%c", &dataType);
2014-09-03 18:51:28 +04:00
switch(dataType)
{
2014-09-03 18:51:28 +04:00
case '#': case 'o': case 'g': case 's': case 'm': case 'u': case 'f': fgets(comments, 200, objFile); break;
case 'v':
{
fscanf(objFile, "%c", &dataType);
2014-09-03 18:51:28 +04:00
if (dataType == 't') // Read texCoord
{
float useless = 0;
2014-09-03 18:51:28 +04:00
fscanf(objFile, "%f %f %f", &midTexCoords[countTexCoords].x, &midTexCoords[countTexCoords].y, &useless);
countTexCoords++;
2014-09-03 18:51:28 +04:00
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++;
2014-09-03 18:51:28 +04:00
fscanf(objFile, "%c", &dataType);
}
else // Read vertex
{
fscanf(objFile, "%f %f %f", &midVertices[countVertex].x, &midVertices[countVertex].y, &midVertices[countVertex].z );
countVertex++;
2014-09-03 18:51:28 +04:00
fscanf(objFile, "%c", &dataType);
}
} break;
default: break;
}
}
2014-09-03 18:51:28 +04:00
// At this point all vertex data (v, vt, vn) has been gathered on midVertices, midTexCoords, midNormals
// Now we can organize that data into our Mesh struct
2014-09-03 18:51:28 +04:00
mesh.vertexCount = numTriangles*3;
2014-09-03 18:51:28 +04:00
// Additional arrays to store vertex data as floats
mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float));
mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float));
mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float));
mesh.colors = NULL;
2014-09-03 18:51:28 +04:00
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
2014-09-03 18:51:28 +04:00
int vNum[3], vtNum[3], vnNum[3]; // Used to store triangle indices for v, vt, vn
2014-09-03 18:51:28 +04:00
rewind(objFile); // Return to the beginning of the file, to read again
2014-09-03 18:51:28 +04:00
if (numNormals == 0) TraceLog(INFO, "[%s] No normals data on OBJ, normals will be generated from faces data", fileName);
2014-09-03 18:51:28 +04:00
// Third reading pass: Get faces (triangles) data and fill VertexArray
while(!feof(objFile))
{
fscanf(objFile, "%c", &dataType);
2014-09-03 18:51:28 +04:00
switch(dataType)
{
2014-09-03 18:51:28 +04:00
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!
2014-09-03 18:51:28 +04:00
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]);
2014-09-03 18:51:28 +04:00
mesh.vertices[vCounter] = midVertices[vNum[0]-1].x;
mesh.vertices[vCounter + 1] = midVertices[vNum[0]-1].y;
mesh.vertices[vCounter + 2] = midVertices[vNum[0]-1].z;
vCounter += 3;
mesh.vertices[vCounter] = midVertices[vNum[1]-1].x;
mesh.vertices[vCounter + 1] = midVertices[vNum[1]-1].y;
mesh.vertices[vCounter + 2] = midVertices[vNum[1]-1].z;
vCounter += 3;
mesh.vertices[vCounter] = midVertices[vNum[2]-1].x;
mesh.vertices[vCounter + 1] = midVertices[vNum[2]-1].y;
mesh.vertices[vCounter + 2] = midVertices[vNum[2]-1].z;
vCounter += 3;
2014-09-03 18:51:28 +04:00
if (numNormals > 0)
{
mesh.normals[nCounter] = midNormals[vnNum[0]-1].x;
mesh.normals[nCounter + 1] = midNormals[vnNum[0]-1].y;
mesh.normals[nCounter + 2] = midNormals[vnNum[0]-1].z;
nCounter += 3;
mesh.normals[nCounter] = midNormals[vnNum[1]-1].x;
mesh.normals[nCounter + 1] = midNormals[vnNum[1]-1].y;
mesh.normals[nCounter + 2] = midNormals[vnNum[1]-1].z;
nCounter += 3;
mesh.normals[nCounter] = midNormals[vnNum[2]-1].x;
mesh.normals[nCounter + 1] = midNormals[vnNum[2]-1].y;
mesh.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);
2014-09-03 18:51:28 +04:00
mesh.normals[nCounter] = norm.x;
mesh.normals[nCounter + 1] = norm.y;
mesh.normals[nCounter + 2] = norm.z;
nCounter += 3;
mesh.normals[nCounter] = norm.x;
mesh.normals[nCounter + 1] = norm.y;
mesh.normals[nCounter + 2] = norm.z;
nCounter += 3;
mesh.normals[nCounter] = norm.x;
mesh.normals[nCounter + 1] = norm.y;
mesh.normals[nCounter + 2] = norm.z;
nCounter += 3;
}
2014-09-03 18:51:28 +04:00
if (numTexCoords > 0)
{
// NOTE: If using negative texture coordinates with a texture filter of GL_CLAMP_TO_EDGE doesn't work!
// NOTE: Texture coordinates are Y flipped upside-down
mesh.texcoords[tcCounter] = midTexCoords[vtNum[0]-1].x;
mesh.texcoords[tcCounter + 1] = 1.0f - midTexCoords[vtNum[0]-1].y;
tcCounter += 2;
mesh.texcoords[tcCounter] = midTexCoords[vtNum[1]-1].x;
mesh.texcoords[tcCounter + 1] = 1.0f - midTexCoords[vtNum[1]-1].y;
tcCounter += 2;
mesh.texcoords[tcCounter] = midTexCoords[vtNum[2]-1].x;
mesh.texcoords[tcCounter + 1] = 1.0f - midTexCoords[vtNum[2]-1].y;
tcCounter += 2;
}
} break;
default: break;
}
}
2014-09-03 18:51:28 +04:00
fclose(objFile);
2014-09-03 18:51:28 +04:00
// Security check, just in case no normals or no texcoords defined in OBJ
if (numTexCoords == 0) for (int i = 0; i < (2*mesh.vertexCount); i++) mesh.texcoords[i] = 0.0f;
2014-09-03 18:51:28 +04:00
// Now we can free temp mid* arrays
free(midVertices);
free(midNormals);
free(midTexCoords);
2014-09-03 18:51:28 +04:00
// NOTE: At this point we have all vertex, texcoord, normal data for the model in mesh struct
TraceLog(INFO, "[%s] Model loaded successfully in RAM (CPU)", fileName);
2014-09-03 18:51:28 +04:00
return mesh;
}
// Load MTL material data
static Material LoadMTL(const char *fileName)
{
Material material = { 0 };
2016-05-09 02:18:46 +03:00
// TODO: Load mtl file (multiple variations of .mtl format)
/*
newmtl string Material newmtl (material name). Begins a new material description.
Ka float float float Ambient color Ka (red) (green) (blue)
Kd float float float Diffuse color Kd (red) (green) (blue)
Ks float float float Specular color Ks (red) (green) (blue)
Ke float float float Emmisive color
d float Tr float Dissolve factor. Transparency Tr (alpha). d is inverse of Tr
Ns int Shininess Ns (specular power). Ranges from 0 to 1000. Specular exponent.
Ni int Refraction index.
illum int Illumination model illum (1 / 2); 1 if specular disabled, 2 if specular enabled (lambertian model)
map_Kd string Texture map_Kd (filename)
map_Kd string Diffuse color texture map.
map_Ks string Specular color texture map.
map_Ka string Ambient color texture map.
map_Bump string Bump texture map. Alternative: bump string / map_bump string
map_d string Opacity texture map.
disp string Displacement map
refl Reflection type and map
*/
char dataType;
char comments[200];
FILE *mtlFile;
mtlFile = fopen(fileName, "rt");
if (mtlFile == NULL)
{
TraceLog(WARNING, "[%s] MTL file could not be opened", fileName);
return material;
}
while(!feof(mtlFile))
{
fscanf(mtlFile, "%c", &dataType);
}
fclose(mtlFile);
// NOTE: At this point we have all material data
TraceLog(INFO, "[%s] Material loaded successfully", fileName);
return material;
}