Files
raylib/src/models.c
T
2014-03-25 12:40:35 +01:00

989 lines
37 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*********************************************************************************************
*
* raylib.models
*
* Basic functions to draw 3d shapes and load/draw 3d models (.OBJ)
*
* Copyright (c) 2013 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"
#include <GL/gl.h> // OpenGL functions
#include <stdio.h> // Standard input/output functions, used to read model files data
#include <stdlib.h> // Declares malloc() and free() for memory management
#include <math.h> // 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
//----------------------------------------------------------------------------------
#ifdef USE_OPENGL_11
struct Model {
int numVertices;
Vector3 *vertices;
Vector2 *texcoords;
Vector3 *normals;
};
#else
struct Model {
int numVertices;
Vector3 *vertices;
Vector2 *texcoords;
Vector3 *normals;
};
/*
struct Model
{
GLUint vaoId;
Matrix transform;
int polyMode;
}
*/
#endif
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
// It's lonely here...
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
static float GetHeightValue(Color pixel);
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
// Draw cube
// NOTE: Cube position is the center position
void DrawCube(Vector3 position, float width, float height, float lenght, Color color)
{
// THIS WORKS!
/*
Matrix mat = MatrixTranslate(2.0, 0.0, 0.0);
MatrixTranspose(&mat);
VectorTransform(&position, mat);
PrintMatrix(mat);
*/
float x = position.x;
float y = position.y;
float z = position.z;
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();
}
// 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)
{
// TODO: Draw cube using RL_LINES!
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//DrawCube(position, width, height, lenght, color);
//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
// 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)
{
float lat0, z0, zr0;
float lat1, z1, zr1;
float lng, x, y;
// TODO: Review vertex translate/rotate/scale mechanism
rlPushMatrix();
rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
rlRotatef(90, 1, 0, 0);
rlScalef(radius, radius, radius);
rlBegin(GL_QUAD_STRIP);
rlColor4ub(color.r, color.g, color.b, color.a);
for(int i = 0; i <= rings; i++)
{
lat0 = PI * (-0.5 + (float)(i - 1) / rings);
z0 = sin(lat0);
zr0 = cos(lat0);
lat1 = PI * (-0.5 + (float)i / rings);
z1 = sin(lat1);
zr1 = cos(lat1);
for(int j = 0; j <= slices; j++)
{
lng = 2 * PI * (float)(j - 1) / slices;
x = cos(lng);
y = sin(lng);
rlNormal3f(x * zr0, y * zr0, z0);
rlVertex3f(x * zr0, y * zr0, z0);
rlNormal3f(x * zr1, y * zr1, z1);
rlVertex3f(x * zr1, y * zr1, z1);
}
}
rlEnd();
rlPopMatrix();
}
// Draw sphere wires
void DrawSphereWires(Vector3 centerPos, float radius, Color color)
{
// TODO: Draw sphere using RL_LINES!
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//DrawSphere(centerPos, radius, color);
//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
// Draw a cylinder/cone
void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color) // Could be used for pyramid and cone!
{
Vector3 a = { position.x, position.y + height, position.z };
Vector3 d = { 0.0f, 1.0f, 0.0f };
Vector3 p;
Vector3 c = { a.x + (-d.x * height), a.y + (-d.y * height), a.z + (-d.z * height) }; //= a + (-d * h);
Vector3 e0 = VectorPerpendicular(d);
Vector3 e1 = VectorCrossProduct(e0, d);
float angInc = 360.0 / slices * DEG2RAD;
if (radiusTop == 0) // Draw pyramid or cone
{
//d axis defined as a normalized vector from base to apex
//a position of apex (top point)
//h height
//rd radius of directrix
//n number of radial "slices"
// TODO: Review drawing to use RL_TRIANGLES
// Draw cone top
rlBegin(GL_TRIANGLE_FAN);
rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex3f(a.x, a.y, a.z);
for (int i = 0; i <= slices; i++)
{
float rad = angInc * i;
p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusBottom);
p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusBottom);
p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusBottom);
rlVertex3f(p.x, p.y, p.z);
}
rlEnd();
// Draw cone bottom
rlBegin(GL_TRIANGLE_FAN);
rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex3f(c.x, c.y, c.z);
for (int i = slices; i >= 0; i--)
{
float rad = angInc * i;
p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusBottom);
p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusBottom);
p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusBottom);
rlVertex3f(p.x, p.y, p.z);
}
rlEnd();
}
else // Draw cylinder
{
// TODO: Review drawing to use RL_TRIANGLES
// Draw cylinder top (pointed cap)
rlBegin(GL_TRIANGLE_FAN);
rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex3f(c.x, c.y + height, c.z);
for (int i = slices; i >= 0; i--)
{
float rad = angInc * i;
p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusTop);
p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusTop) + height;
p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusTop);
rlVertex3f(p.x, p.y, p.z);
}
rlEnd();
// Draw cylinder sides
rlBegin(GL_TRIANGLE_STRIP);
rlColor4ub(color.r, color.g, color.b, color.a);
for (int i = slices; i >= 0; i--)
{
float rad = angInc * i;
p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusTop);
p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusTop) + height;
p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusTop);
rlVertex3f(p.x, p.y, p.z);
p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusBottom);
p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusBottom);
p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusBottom);
rlVertex3f(p.x, p.y, p.z);
}
rlEnd();
// Draw cylinder bottom
rlBegin(GL_TRIANGLE_FAN);
rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex3f(c.x, c.y, c.z);
for (int i = slices; i >= 0; i--)
{
float rad = angInc * i;
p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusBottom);
p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusBottom);
p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusBottom);
rlVertex3f(p.x, p.y, p.z);
}
rlEnd();
}
}
// Draw a cylinder/cone wires
void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color)
{
// TODO: Draw sphere using RL_LINES!
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
DrawCylinder(position, radiusTop, radiusBottom, height, slices, color);
//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
// Draw a plane
void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color)
{
// TODO: Review vertex translate/rotate/scale mechanism
// NOTE: Plane is always created on XZ ground and then rotated
rlPushMatrix();
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);
rlScalef(size.x, 1.0f, size.y);
rlBegin(GL_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();
}
// Draw a plane with divisions
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;
// TODO: Review vertex translate/rotate/scale mechanism
// 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 (with or without orbits)
void DrawGizmo(Vector3 position, bool orbits)
{
// NOTE: RGB = XYZ
float lenght = 1.0f;
float radius = 1.0f;
//glEnable(GL_LINE_SMOOTH); // Smoothies circle outline (anti-aliasing applied)
//glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // Best quality for line smooth (anti-aliasing best algorithm)
// GL_LINE_SMOOTH is very poorly supported on desktop GL.
// A lot of drivers ignore it, so most people avoid using...
rlPushMatrix();
rlTranslatef(position.x, position.y, position.z);
//glRotatef(rotation, 0, 1, 0);
rlScalef(lenght, lenght, lenght);
rlBegin(GL_LINES);
rlColor3f(1.0f, 0.0f, 0.0f);
rlVertex3f(0.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);
rlVertex3f(0.0f, 1.0f, 0.0f);
rlColor3f(0.0f, 0.0f, 1.0f);
rlVertex3f(0.0f, 0.0f, 0.0f);
rlVertex3f(0.0f, 0.0f, 1.0f);
rlEnd();
if (orbits)
{
rlBegin(GL_LINE_LOOP);
rlColor4f(1.0f, 0.0f, 0.0f, 0.4f);
for (int i=0; i < 360; i++) rlVertex3f(sin(DEG2RAD*i) * radius, 0, cos(DEG2RAD*i) * radius);
rlEnd();
rlBegin(GL_LINE_LOOP);
rlColor4f(0.0f, 1.0f, 0.0f, 0.4f);
for (int i=0; i < 360; i++) rlVertex3f(sin(DEG2RAD*i) * radius, cos(DEG2RAD*i) * radius, 0);
rlEnd();
rlBegin(GL_LINE_LOOP);
rlColor4f(0.0f, 0.0f, 1.0f, 0.4f);
for (int i=0; i < 360; i++) rlVertex3f(0, sin(DEG2RAD*i) * radius, cos(DEG2RAD*i) * radius);
rlEnd();
}
rlPopMatrix();
//glDisable(GL_LINE_SMOOTH);
}
// Load a 3d model (.OBJ)
// TODO: Add comments explaining this function process
Model LoadModel(const char *fileName)
{
struct Model model;
char dataType;
char comments[200];
int numVertex = 0;
int numNormals = 0;
int numTexCoords = 0;
int numTriangles = 0;
FILE* objFile;
objFile = fopen(fileName, "rt");
while(!feof(objFile))
{
fscanf(objFile, "%c", &dataType);
switch(dataType)
{
case '#': // It's a comment
{
fgets(comments, 200, objFile);
} break;
case 'v':
{
fscanf(objFile, "%c", &dataType);
if (dataType == 't') // Read texCoord
{
fgets(comments, 200, objFile);
fscanf(objFile, "%c", &dataType);
while (dataType == 'v')
{
fgets(comments, 200, objFile);
fscanf(objFile, "%c", &dataType);
}
if (dataType == '#')
{
fscanf(objFile, "%i", &numTexCoords);
}
else printf("Ouch! Something was wrong...");
fgets(comments, 200, objFile);
}
else if (dataType == 'n') // Read normals
{
fgets(comments, 200, objFile);
fscanf(objFile, "%c", &dataType);
while (dataType == 'v')
{
fgets(comments, 200, objFile);
fscanf(objFile, "%c", &dataType);
}
if (dataType == '#')
{
fscanf(objFile, "%i", &numNormals);
}
else printf("Ouch! Something was wrong...");
fgets(comments, 200, objFile);
}
else // Read vertex
{
fgets(comments, 200, objFile);
fscanf(objFile, "%c", &dataType);
while (dataType == 'v')
{
fgets(comments, 200, objFile);
fscanf(objFile, "%c", &dataType);
}
if (dataType == '#')
{
fscanf(objFile, "%i", &numVertex);
}
else printf("Ouch! Something was wrong...");
fgets(comments, 200, objFile);
}
} break;
case 'f':
{
fgets(comments, 200, objFile);
fscanf(objFile, "%c", &dataType);
while (dataType == 'f')
{
fgets(comments, 200, objFile);
fscanf(objFile, "%c", &dataType);
}
if (dataType == '#')
{
fscanf(objFile, "%i", &numTriangles);
}
else printf("Ouch! Something was wrong...");
fgets(comments, 200, objFile);
} break;
default: break;
}
}
Vector3 midVertices[numVertex];
Vector3 midNormals[numNormals];
Vector2 midTexCoords[numTexCoords];
model.numVertices = numTriangles*3;
model.vertices = (Vector3 *)malloc(model.numVertices * sizeof(Vector3));
model.normals = (Vector3 *)malloc(model.numVertices * sizeof(Vector3));
model.texcoords = (Vector2 *)malloc(model.numVertices * sizeof(Vector2));
int countVertex = 0;
int countNormals = 0;
int countTexCoords = 0;
int countMaxVertex = 0;
rewind(objFile);
while(!feof(objFile))
{
fscanf(objFile, "%c", &dataType);
switch(dataType)
{
case '#':
{
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;
case 'f':
{
int vNum, vtNum, vnNum;
fscanf(objFile, "%c", &dataType);
fscanf(objFile, "%i/%i/%i", &vNum, &vtNum, &vnNum);
model.vertices[countMaxVertex] = midVertices[vNum-1];
model.normals[countMaxVertex] = midNormals[vnNum-1];
model.texcoords[countMaxVertex].x = midTexCoords[vtNum-1].x;
model.texcoords[countMaxVertex].y = -midTexCoords[vtNum-1].y;
countMaxVertex++;
fscanf(objFile, "%i/%i/%i", &vNum, &vtNum, &vnNum);
model.vertices[countMaxVertex] = midVertices[vNum-1];
model.normals[countMaxVertex] = midNormals[vnNum-1];
model.texcoords[countMaxVertex].x = midTexCoords[vtNum-1].x;
model.texcoords[countMaxVertex].y = -midTexCoords[vtNum-1].y;
countMaxVertex++;
fscanf(objFile, "%i/%i/%i", &vNum, &vtNum, &vnNum);
model.vertices[countMaxVertex] = midVertices[vNum-1];
model.normals[countMaxVertex] = midNormals[vnNum-1];
model.texcoords[countMaxVertex].x = midTexCoords[vtNum-1].x;
model.texcoords[countMaxVertex].y = -midTexCoords[vtNum-1].y;
countMaxVertex++;
} break;
default: break;
}
}
fclose(objFile);
#ifdef USE_OPENGL_33
// TODO: Use loaded data to generate VAO
#endif
return model;
}
// Load a heightmap image as a 3d model
Model LoadHeightmap(Image heightmap, float maxHeight)
{
Model model;
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
model.numVertices = numTriangles*3;
model.vertices = (Vector3 *)malloc(model.numVertices * sizeof(Vector3));
model.normals = (Vector3 *)malloc(model.numVertices * sizeof(Vector3));
model.texcoords = (Vector2 *)malloc(model.numVertices * sizeof(Vector2));
int vCounter = 0;
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
model.vertices[vCounter].x = x;
model.vertices[vCounter].y = GetHeightValue(heightmap.pixels[x + z*mapX])*scaleFactor;
model.vertices[vCounter].z = z;
model.vertices[vCounter+1].x = x;
model.vertices[vCounter+1].y = GetHeightValue(heightmap.pixels[x + (z+1)*mapX])*scaleFactor;
model.vertices[vCounter+1].z = z+1;
model.vertices[vCounter+2].x = x+1;
model.vertices[vCounter+2].y = GetHeightValue(heightmap.pixels[(x+1) + z*mapX])*scaleFactor;
model.vertices[vCounter+2].z = z;
// another triangle - 3 vertex
model.vertices[vCounter+3] = model.vertices[vCounter+2];
model.vertices[vCounter+4] = model.vertices[vCounter+1];
model.vertices[vCounter+5].x = x+1;
model.vertices[vCounter+5].y = GetHeightValue(heightmap.pixels[(x+1) + (z+1)*mapX])*scaleFactor;
model.vertices[vCounter+5].z = z+1;
// Fill texcoords array with data
//--------------------------------------------------------------
model.texcoords[vCounter].x = (float)x / (mapX-1);
model.texcoords[vCounter].y = (float)z / (mapZ-1);
model.texcoords[vCounter+1].x = (float)x / (mapX-1);
model.texcoords[vCounter+1].y = (float)(z+1) / (mapZ-1);
model.texcoords[vCounter+2].x = (float)(x+1) / (mapX-1);
model.texcoords[vCounter+2].y = (float)z / (mapZ-1);
model.texcoords[vCounter+3] = model.texcoords[vCounter+2];
model.texcoords[vCounter+4] = model.texcoords[vCounter+1];
model.texcoords[vCounter+5].x = (float)(x+1) / (mapX-1);
model.texcoords[vCounter+5].y = (float)(z+1) / (mapZ-1);
// Fill normals array with data
//--------------------------------------------------------------
// TODO: Review normals calculation
model.normals[vCounter] = (Vector3){ 0.0f, 1.0f, 0.0f };
model.normals[vCounter+1] = (Vector3){ 0.0f, 1.0f, 0.0f };
model.normals[vCounter+2] = (Vector3){ 0.0f, 1.0f, 0.0f };
model.normals[vCounter+3] = (Vector3){ 0.0f, 1.0f, 0.0f };
model.normals[vCounter+4] = (Vector3){ 0.0f, 1.0f, 0.0f };
model.normals[vCounter+5] = (Vector3){ 0.0f, 1.0f, 0.0f };
vCounter += 6;
trisCounter += 2;
}
}
#ifdef USE_OPENGL_33
// TODO: Use loaded data to generate VAO
#endif
return model;
}
// Unload 3d model from memory
void UnloadModel(Model model)
{
free(model.vertices);
free(model.texcoords);
free(model.normals);
}
// Draw a model
void DrawModel(Model model, Vector3 position, float scale, Color color)
{
// NOTE: For models we use Vertex Arrays (OpenGL 1.1)
//static int rotation = 0;
// NOTE: Add OpenGL 3.3+ VAOs-based drawing! --> Move this stuff to rlgl?
glEnableClientState(GL_VERTEX_ARRAY); // Enable vertex array
glEnableClientState(GL_TEXTURE_COORD_ARRAY); // Enable texture coords array
glEnableClientState(GL_NORMAL_ARRAY); // Enable normals array
glVertexPointer(3, GL_FLOAT, 0, model.vertices); // Pointer to vertex coords array
glTexCoordPointer(2, GL_FLOAT, 0, model.texcoords); // Pointer to texture coords array
glNormalPointer(GL_FLOAT, 0, model.normals); // Pointer to normals array
//glColorPointer(4, GL_UNSIGNED_BYTE, 0, model.colors); // Pointer to colors array (NOT USED)
rlPushMatrix();
rlTranslatef(position.x, position.y, position.z);
//glRotatef(rotation * GetFrameTime(), 0, 1, 0);
rlScalef(scale, scale, scale);
rlColor4ub(color.r, color.g, color.b, color.a);
glDrawArrays(GL_TRIANGLES, 0, model.numVertices);
rlPopMatrix();
glDisableClientState(GL_VERTEX_ARRAY); // Disable vertex array
glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Disable texture coords array
glDisableClientState(GL_NORMAL_ARRAY); // Disable normals array
//rotation += 10;
// Model drawing in OpenGL 3.3+, transform is passed to shader
/*
glUseProgram(shaderProgram); // Use our shader
Matrix modelview = MatrixMultiply(model.transform, view);
glUniformMatrix4fv(projectionMatrixLoc, 1, false, GetMatrixVector(projection));
glUniformMatrix4fv(modelviewMatrixLoc, 1, false, GetMatrixVector(modelview));
glUniform1i(textureLoc, 0);
glBindVertexArray(model.vaoId);
glBindTexture(GL_TEXTURE_2D, model.texId);
glDrawArrays(GL_TRIANGLES, 0, model.numVertices);
glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures
glBindVertexArray(0); // Unbind VAO
*/
}
// Draw a textured model
void DrawModelEx(Model model, Texture2D texture, Vector3 position, float scale, Color tint)
{
rlEnableTexture(texture.glId);
DrawModel(model, position, scale, tint);
rlDisableTexture();
}
// Draw a model wires
void DrawModelWires(Model model, Vector3 position, float scale, Color color)
{
// TODO: Draw model using RL_LINES... or look for a way to deal with polygon mode!
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
DrawModel(model, position, scale, color);
//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
// 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 };
/*
d-------c
| |
| * |
| |
a-------b
*/
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.glId);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
rlNormal3f(0.0f, 1.0f, 0.0f);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(a.x, a.y, a.z);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(b.x, b.y, b.z);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(c.x, c.y, c.z);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(d.x, d.y, d.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 };
/*
d-------c
| |
| * |
| |
a-------b
*/
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.glId);
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);
// 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);
// 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);
// 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);
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);
}