/*
Special thanks to:
libvitaboy - Open source OpenGL TSO character animation library
Renderer.cpp - Copyright (c) 2012 Niotso Project
Author(s): Fatbag
*/
/*
Instructions:
You must have the following files in the same directory as the Renderer binary:
Skeleton:
* skeleton.skel ("adult.skel" in ./avatardata/skeletons/)
Meshes:
* body.mesh (pick one from ./avatardata/bodies/meshes/)
* head.mesh (pick one from ./avatardata/heads/meshes/)
* lhand.mesh (pick one from ./avatardata/hands/meshes/)
* rhand.mesh (pick one from ./avatardata/hands/meshes/)
Textures:
* body.jpg (pick one from ./avatardata/bodies/textures/)
* head.jpg (pick one from ./avatardata/heads/textures/)
* hand.jpg (pick one from ./avatardata/hands/textures/)
Animation:
* animation.anim (pick one from ./avatardata/animations/)
==== Controls ====
1: Toggle skeleton
2: Toggle the mesh
n: Animate the character
*/
#include "raylib.h"
#include "raymath.h" // Required for: Vector3, Quaternion and Matrix functionality
#include "utils.h" // Required for: TRACELOG(), LoadFileData(), LoadFileText(), SaveFileText()
#include "rlgl.h"
#include
#include
#include "../libvitaboy/libvitaboy.hpp"
#define SCREEN_WIDTH (800)
#define SCREEN_HEIGHT (600)
#define WINDOW_TITLE "libvitaboy - Renderer - Ray"
struct BasicVertex_t {
float x, y, z;
};
struct CharacterPlacement_t {
BasicVertex_t Translation;
BasicVertex_t Rotation;
};
static CharacterPlacement_t Character = {{0,-3,0}, {0,0,0}};
static Skeleton_t Skeleton;
static Animation_t Animation;
static float AnimationTime = 0;
static void DisplayFileError(const char * Filename){
const char * Message;
switch(File::Error){
case FERR_NOT_FOUND:
Message = "%s does not exist.";
break;
case FERR_OPEN:
Message = "%s could not be opened for reading.";
break;
case FERR_BLANK:
case FERR_UNRECOGNIZED:
case FERR_INVALIDDATA:
Message = "%s is corrupt or invalid.";
break;
case FERR_MEMORY:
Message = "Memory for %s could not be allocated.";
break;
default:
Message = "%s could not be read.";
break;
}
char Buffer[1024];
sprintf(Buffer, Message, Filename);
}
static bool Read(const char* Filename, uint8_t** InData) {
*InData = File::ReadFile(Filename);
if (*InData != NULL) {
VBFile.set(*InData, File::FileSize);
return true;
}
DisplayFileError(Filename);
return false;
}
//textures
static Texture2D textures[3];
enum { Texture_Body, Texture_Head, Texture_Hand };
static const char* const TexturePaths[] = { "body.jpg", "head.jpg", "hand.jpg" };
static bool LoadTextures()
{
for (int i = 0; i < 3; i++)
{
textures[i] = LoadTexture(TexturePaths[i]);
}
return true;
}
static bool UnloadTextures()
{
for (int i = 0; i < 3; i++)
{
UnloadTexture(textures[i]);
}
return true;
}
//meshes
static const unsigned MeshCount = 4;
static Mesh_t Meshes[4];
enum { Mesh_Body, Mesh_Head, Mesh_LHand, Mesh_RHand };
static const char* const MeshPaths[] = { "body.mesh", "head.mesh", "lhand.mesh", "rhand.mesh" };
static const unsigned Mesh_UseTexture[] = { Texture_Body, Texture_Head, Texture_Hand, Texture_Hand };
static const char* const MeshActivate[] = { NULL, "HEAD", "L_HAND", "R_HAND" };
// Generate a simple triangle mesh from code
static Mesh CreateMesh(const Mesh_t& tso_mesh)
{
Mesh ray_mesh = { 0 };
ray_mesh.vertexCount = tso_mesh.RealVertexCount;
ray_mesh.triangleCount = tso_mesh.FaceCount; // Face count (triangulated)
ray_mesh.vertices = (float*)RL_CALLOC(ray_mesh.vertexCount * 3, sizeof(float));
ray_mesh.texcoords = (float*)RL_CALLOC(ray_mesh.vertexCount * 2, sizeof(float));
ray_mesh.normals = (float*)RL_CALLOC(ray_mesh.vertexCount * 3, sizeof(float));
ray_mesh.indices = (unsigned short*)RL_CALLOC(ray_mesh.triangleCount * 3, sizeof(unsigned short));
// Process all mesh faces
//vertex data
for (unsigned j = 0; j < ray_mesh.vertexCount; j++)
{
//vertices?
ray_mesh.vertices[j * 3 + 0] = tso_mesh.TransformedVertexData[j].Coord.x;
ray_mesh.vertices[j * 3 + 1] = tso_mesh.TransformedVertexData[j].Coord.y;
ray_mesh.vertices[j * 3 + 2] = tso_mesh.TransformedVertexData[j].Coord.z;
//coords
ray_mesh.texcoords[j * 2 + 0] = tso_mesh.TransformedVertexData[j].TextureCoord.u;
ray_mesh.texcoords[j * 2 + 1] = -tso_mesh.TransformedVertexData[j].TextureCoord.v;
//normals
ray_mesh.normals[j * 3 + 0] = tso_mesh.TransformedVertexData[j].NormalCoord.x;
ray_mesh.normals[j * 3 + 1] = tso_mesh.TransformedVertexData[j].NormalCoord.y;
ray_mesh.normals[j * 3 + 2] = tso_mesh.TransformedVertexData[j].NormalCoord.z;
}
//indices
for (unsigned j = 0; j < ray_mesh.triangleCount; j++)
{
ray_mesh.indices[j * 3 + 0] = (unsigned short)tso_mesh.FaceData[j].VertexA;
//counter clock wise
ray_mesh.indices[j * 3 + 1] = (unsigned short)tso_mesh.FaceData[j].VertexC;
ray_mesh.indices[j * 3 + 2] = (unsigned short)tso_mesh.FaceData[j].VertexB;
//Clock wise
//ray_mesh.indices[j * 3 + 1] = (unsigned short)tso_mesh.FaceData[j].VertexB;
//ray_mesh.indices[j * 3 + 2] = (unsigned short)tso_mesh.FaceData[j].VertexC;
}
// Upload mesh data from CPU (RAM) to GPU (VRAM) memory
//UploadMesh(&ray_mesh, true); //check the dynamic flag if we have to keep creating the mesh
return ray_mesh;
}
static bool LoadMeshes()
{
uint8_t* InData;
for (unsigned i = 0; i < MeshCount; i++)
{
if (!Read(MeshPaths[i], &InData))
{
return false;
}
ReadMesh(Meshes[i]);
free(InData);
}
return true;
}
static int ResizeScene(uint16_t width, uint16_t height)
{
rlViewport(0, 0, width, height);
rlMatrixMode(RL_PROJECTION);
rlLoadIdentity();
// Calculate The Aspect Ratio Of The Window
//rluPerspective(45.0f, (rlfloat)width/(rlfloat)height, 0.1f, 100.0f);
// rlScalef(-1.0f, 1.0f, 1.0f);
rlMatrixMode(RL_MODELVIEW);
rlLoadIdentity();
return 1;
}
static bool InitRLGL()
{
rlglInit(SCREEN_WIDTH, SCREEN_HEIGHT);
//rlSetCullFace();
//rlEnable(rl_CULL_FACE);
rlDisableBackfaceCulling();
//rlDisable(rl_BLEND);
rlEnableColorBlend();
//rlDepthFunc(rl_LEQUAL); //default
//rlHint(rl_PERSPECTIVE_CORRECTION_HINT, rl_NICEST); //default
//cant change this
//rlFrontFace(rl_CW);
ResizeScene(SCREEN_WIDTH, SCREEN_HEIGHT);
return true;
}
static void TransformVertices(Bone_t& Bone)
{
rlTranslatef(Bone.Translation.x, Bone.Translation.y, Bone.Translation.z);
float matRotation[16];
FindQuaternionMatrix(matRotation, &Bone.Rotation);
rlMultMatrixf(matRotation);
unsigned MeshIndex = 0;
unsigned BoneIndex;
for(unsigned i=1; i 1){
for(unsigned i=0; ivboId[0] = 0; // Vertex buffer: positions
//mesh->vboId[1] = 0; // Vertex buffer: texcoords
//mesh->vboId[2] = 0; // Vertex buffer: normals
UpdateMeshBuffer(ray_mesh, 0, ray_mesh.vertices, sizeof(float) * ray_mesh.vertexCount * 3, 0);
UpdateMeshBuffer(ray_mesh, 1, ray_mesh.texcoords, sizeof(float) * ray_mesh.vertexCount * 2, 0);
UpdateMeshBuffer(ray_mesh, 2, ray_mesh.normals, sizeof(float) * ray_mesh.vertexCount * 3, 0);
}
rlPopMatrix();
}
static void DrawMeshes()
{
rlPushMatrix();
rlLoadIdentity();
TransformVertices(Skeleton.Bones[0]);
rlPopMatrix();
BlendVertices();
Material material = LoadMaterialDefault();
for (unsigned i = 0; i < MeshCount; i++)
{
material.maps[MATERIAL_MAP_DIFFUSE].texture = textures[Mesh_UseTexture[i]];
DrawMesh(ray_meshes[i], material, MatrixIdentity());
}
rlPopMatrix();
}
static void AdvanceFrame(Skeleton_t& Skeleton, Animation_t& Animation, float TimeDelta)
{
float Duration = (float)Animation.Motions[0].FrameCount/30;
AnimationTime += TimeDelta;
AnimationTime = fmodf(AnimationTime, Duration); //Loop the animation
for(unsigned i=0; i 1){
for(unsigned i=0; i=-10.0f) zoom-=0.05f; }*/ zoom -= 3 * dt;
if (IsKeyDown(KEY_UP)) { if ((Character.Rotation.x -= 60 * dt) <= -360) Character.Rotation.x += 360; }
if (IsKeyDown(KEY_DOWN)) { if ((Character.Rotation.x += 60 * dt) >= 360) Character.Rotation.x -= 360; }
if (IsKeyDown(KEY_LEFT)) { if ((Character.Rotation.y -= 60 * dt) <= -360) Character.Rotation.y += 360; }
if (IsKeyDown(KEY_RIGHT)) { if ((Character.Rotation.y += 60 * dt) >= 360) Character.Rotation.y -= 360; }
if (IsKeyDown(KEY_X)) { if ((Character.Rotation.z -= 60 * dt) <= -360) Character.Rotation.z += 360; }
if (IsKeyDown(KEY_Z)) { if ((Character.Rotation.z += 60 * dt) >= 360) Character.Rotation.z -= 360; }
if (IsKeyDown(KEY_K)) { Character.Translation.y -= 3 * dt; }
if (IsKeyDown(KEY_I)) { Character.Translation.y += 3 * dt; }
if (IsKeyDown(KEY_J)) { Character.Translation.x -= 3 * dt; }
if (IsKeyDown(KEY_L)) { Character.Translation.x += 3 * dt; }
if (IsKeyPressed(KEY_ONE)){ ShowSkeleton = !ShowSkeleton; }
if (IsKeyPressed(KEY_TWO)) { ShowMesh = !ShowMesh; }
if (IsKeyDown(KEY_N)) { AdvanceFrame(Skeleton, Animation, dt); }
}
rlClearScreenBuffers(); //Clear the screen and the depth buffer
UpdateMeshes(model);
BeginDrawing();
{
ClearBackground(BLACK);
BeginMode3D(camera);
{
DrawGrid(10, 5.0f);
//Avatar
{
if (ShowMesh)
{
// draw the model
const Vector3 position{ 0.0f, 0.0f, 0.0f };
const float scale = 1.f;
DrawModel(model, position, scale, WHITE);
}
if (ShowSkeleton)
{
DrawSkeleton();
}
}
}
EndMode3D();
}
EndDrawing();
}
UnloadTextures();
CloseWindow();
return 0;
}