/* 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; }