#include "raylib.h" #include "raymath.h" // Required for: Vector3, Quaternion and Matrix functionality #include "utils.h" // Required for: TRACELOG(), LoadFileData(), LoadFileText(), SaveFileText() #include <string> #include <unordered_map> #include <cassert> #include <FileHandler.h> #include "../libvitaboy/libvitaboy.hpp" #define SCREEN_WIDTH (800) #define SCREEN_HEIGHT (600) #define WINDOW_TITLE "libvitaboy - Renderer - Ray" //we dont look in this mess for now #pragma region TSO //util static bool Read(const char* Filename, uint8_t** InData) { *InData = File::ReadFile(Filename); if (*InData != NULL) { VBFile.set(*InData, File::FileSize); return true; } return false; } //globals //skeleton static Skeleton_t Skeleton; static Model box_model; static void DrawBonesSkeleton(Bone_t& Bone, const Matrix& M) { Vector3 bonePos = Vector3{ Bone.Translation.x, Bone.Translation.y, Bone.Translation.z }; const Vector3 scale = { 1.f, 1.f, 1.f }; Vector3 axis{ Vector3Zero()}; float angle{ 0 }; const Quaternion rotation = Quaternion{ Bone.Rotation.x, Bone.Rotation.y, Bone.Rotation.z, Bone.Rotation.w }; QuaternionToAxisAngle(rotation, &axis, &angle); Matrix matScale = MatrixScale(scale.x, scale.y, scale.z); Matrix matRotation = MatrixRotate(axis, angle); Matrix matTranslation = MatrixTranslate(bonePos.x, bonePos.y, bonePos.z); //this order is correct, see //Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matTranslation), matRotation); Matrix matTransform = MatrixMultiply(matScale, matTranslation); Matrix modelMatrix = MatrixMultiply(M, matTransform); Color color; if(!strcmp(Bone.Name, "ROOT")) { color = RED; } else if(!strcmp(Bone.Name, "HEAD")) { color = YELLOW; } else { color = GREEN; } DrawModel(box_model, Vector3Transform(bonePos, M), 1.f, color); for (unsigned i = 0; i < Bone.ChildrenCount; i++) { DrawBonesSkeleton(*Bone.Children[i], modelMatrix); } } static int counter = 0; static void DrawTest(const Matrix& M) { Vector3 rootPosition = Vector3Transform(Vector3{ 0.f, 0.f, 0.f }, M); DrawModel (box_model, //root rootPosition, 1.f, YELLOW); DrawModel(box_model, //x Vector3Transform(Vector3{ 1.f, 0.f, 0.f }, M), 1.f, RED); DrawModel(box_model, //y Vector3Transform(Vector3{ 0.f, 1.f, 0.f }, M), 1.f, GREEN); DrawModel(box_model, //z Vector3Transform(Vector3{ 0.f, 0.f, 1.f }, M), 1.f, BLUE); Vector3 scale = { 1.f, 1.f, 1.f }; Vector3 rotationAxis = { 0.0f, 0.0f, 1.0f }; Matrix matScale = MatrixScale(scale.x, scale.y, scale.z); Matrix matRotation = MatrixRotate(rotationAxis, 45.f * DEG2RAD); Matrix matTranslation = MatrixTranslate(5.f, 0.f, 0.f); //transform 5 in the X direction // https://github.com/JipBoesenkool/CSE167F17_Project4/blob/master/src/renderer/model/Transform.cpp // SRT = iTRS, for absolute // STR = iRTS, for local transforms Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matTranslation), matRotation); Matrix modelMatrix = MatrixMultiply(M, matTransform); if (counter < 3) { counter++; DrawTest(modelMatrix); } counter = 0; } //animation static Animation_t Animation; static float AnimationTime = 0; 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<Animation.MotionsCount; i++){ unsigned BoneIndex = FindBone(Skeleton, Animation.Motions[i].BoneName, Skeleton.BoneCount); if(BoneIndex == (unsigned)-1) continue; Bone_t& Bone = Skeleton.Bones[BoneIndex]; unsigned Frame = AnimationTime*30; float FractionShown = AnimationTime*30 - Frame; unsigned NextFrame = (Frame+1 != Animation.Motions[0].FrameCount) ? Frame+1 : 0; if(Animation.Motions[i].HasTranslation){ Translation_t& Translation = Animation.Motions[i].Translations[Frame]; Translation_t& NextTranslation = Animation.Motions[i].Translations[NextFrame]; Bone.Translation.x = (1-FractionShown)*Translation.x + FractionShown*NextTranslation.x; Bone.Translation.y = (1-FractionShown)*Translation.y + FractionShown*NextTranslation.y; Bone.Translation.z = (1-FractionShown)*Translation.z + FractionShown*NextTranslation.z; } if(Animation.Motions[i].HasRotation){ Rotation_t& Rotation = Animation.Motions[i].Rotations[Frame]; Rotation_t& NextRotation = Animation.Motions[i].Rotations[NextFrame]; //Use nlerp to interpolate float w1 = 1.0f - FractionShown, w2 = FractionShown; if(DotProduct(&Rotation, &NextRotation) < 0) w1 *= -1; Bone.Rotation.x = w1*Rotation.x + w2*NextRotation.x; Bone.Rotation.y = w1*Rotation.y + w2*NextRotation.y; Bone.Rotation.z = w1*Rotation.z + w2*NextRotation.z; Bone.Rotation.w = w1*Rotation.w + w2*NextRotation.w; Normalize(&Bone.Rotation); } } } //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; } //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"}; 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; } #pragma endregion TSO #pragma region custom_ray namespace CustomRay { static Model LoadModelTSO() { Model model; //meshes model.meshCount = MeshCount; model.meshes = (Mesh*)RL_CALLOC(model.meshCount, sizeof(Mesh)); //textures model.materialCount = 3; model.meshMaterial = (int*)RL_CALLOC(model.meshCount, sizeof(int)); // Material index assigned to each mesh model.materials = (Material*)RL_CALLOC(model.materialCount, sizeof(Material)); model.meshMaterial[0] = 0; // By default, assign material 0 to each mesh //TODO: reassign bone IDS with a map ///load the textures for (int i = 0; i < model.materialCount; i++) { model.materials[i] = LoadMaterialDefault(); model.materials[i].maps[MATERIAL_MAP_DIFFUSE].texture = textures[i]; } //load meshes for (int i = 0; i < model.meshCount; i++) { Mesh_t& tso_mesh = Meshes[i]; Mesh& ray_mesh = model.meshes[i]; 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.VertexData[j].Coord.x; ray_mesh.vertices[j * 3 + 1] = tso_mesh.VertexData[j].Coord.y; ray_mesh.vertices[j * 3 + 2] = tso_mesh.VertexData[j].Coord.z + (i * 2.f); //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 + 0] = tso_mesh.TransformedVertexData[j].NormalCoord.y; ray_mesh.normals[j * 3 + 0] = 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; 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; } //select the textures model.meshMaterial[i] = Mesh_UseTexture[i]; } //upload to gpu // Make sure model transform is set to identity matrix! model.transform = MatrixIdentity(); if ((model.meshCount != 0) && (model.meshes != NULL)) { // Upload vertex data to GPU (static meshes) for (int i = 0; i < model.meshCount; i++) UploadMesh(&model.meshes[i], false); } else TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load model mesh(es) data", fileName); if (model.materialCount == 0) { TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to load model material data, default to white material", fileName); model.materialCount = 1; model.materials = (Material*)RL_CALLOC(model.materialCount, sizeof(Material)); model.materials[0] = LoadMaterialDefault(); if (model.meshMaterial == NULL) { model.meshMaterial = (int*)RL_CALLOC(model.meshCount, sizeof(int)); } } return model; } ModelAnimation LoadModelAnimationsTSO() { ModelAnimation result; return result; } //https://gist.github.com/Gamerfiend/18206474679bf5873925c839d0d6a6d0 void LoadSkeletonTSO(Model& ray_model) { //Load the bonus // map string to ID Skeleton_t& tso_skeleton = Skeleton; const unsigned int boneCount = Skeleton.BoneCount; ray_model.boneCount = boneCount; ray_model.bones = (BoneInfo*)RL_MALLOC(boneCount * sizeof(BoneInfo)); ray_model.bindPose = (Transform*)RL_MALLOC(boneCount * sizeof(Transform)); for (unsigned int i = 0; i < Skeleton.BoneCount; i++) { Bone_t& tso_bone = Skeleton.Bones[i]; BoneInfo& ray_bone = ray_model.bones[i]; Transform& ray_bone_transform = ray_model.bindPose[i]; //fill boneinfo //sims naming might be bigger then 32 chars, assert if so const int length = strlen(tso_bone.Name); assert(strlen(tso_bone.Name) <= 32); strcpy(ray_bone.name, tso_bone.Name); ray_bone.parent = FindBone(Skeleton, tso_bone.ParentsName, boneCount); printf("Bone: %i\n", i); printf("Name: %s\n", ray_bone.name); printf("parentName: %s\n", tso_bone.ParentsName); printf("parentID: %i\n", ray_bone.parent); // Set the transform const Translation_t& tso_bone_position = tso_bone.Translation; const Rotation_t& tso_bone_rotation = tso_bone.Rotation; const Vector3 tso_bone_Scale = { 1.f, 1.f, 1.f, }; //no scale? // sucks writing it out, but safer then pointer casting //position ray_bone_transform.translation.x = tso_bone_position.x; ray_bone_transform.translation.y = tso_bone_position.y; ray_bone_transform.translation.z = tso_bone_position.z; //rotation ray_bone_transform.rotation.x = tso_bone_rotation.x; ray_bone_transform.rotation.y = tso_bone_rotation.y; ray_bone_transform.rotation.z = tso_bone_rotation.z; ray_bone_transform.rotation.w = tso_bone_rotation.w; //scale ray_bone_transform.scale.x = tso_bone_Scale.x; ray_bone_transform.scale.y = tso_bone_Scale.y; ray_bone_transform.scale.z = tso_bone_Scale.z; } for (int i = 0; i < ray_model.meshCount; i++) { Mesh_t& tso_mesh = Meshes[i]; Mesh& ray_mesh = ray_model.meshes[i]; } [[maybe_unused]] unsigned int block = 0; block++; } } #pragma endregion custom_ray static int Startup() { uint8_t * InData; if(!Read("skeleton.skel", &InData)) return 0; ReadSkeleton(Skeleton); free(InData); for(unsigned i=0; i<MeshCount; i++){ if(!Read(MeshPaths[i], &InData)) return 0; ReadMesh(Meshes[i]); free(InData); } if(!Read("animation.anim", &InData)) return 0; ReadAnimation(Animation); free(InData); //AdvanceFrame(Skeleton, Animation, 0); return 1; } //settings static bool ShowTextures = false; static bool ShowMesh = false; static bool ShowSkeleton = true; int main(void) { InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, WINDOW_TITLE); SetTargetFPS(60); // Initialize the camera Camera3D camera = { 0 }; camera.position = Vector3{ 0.f, 5.0f, 5.0f }; // Camera position camera.target = Vector3{ 0.0f, 0.0f, 0.0f }; // Camera looking at point camera.up = Vector3{ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) camera.fovy = 70.0f; // Camera field-of-view Y camera.projection = CAMERA_PERSPECTIVE; // Camera projection type - we will probably need orthographic //doesn't work when ran from VSStudio assert( Startup() ); assert( LoadTextures() ); assert( LoadMeshes() ); printf("======================================\n"); printf("=================RAY==================\n"); printf("======================================\n"); const float size = 0.1f; box_model = LoadModelFromMesh( GenMeshCube(size, size, size) ); Model model = CustomRay::LoadModelTSO(); CustomRay::LoadSkeletonTSO(model); DisableCursor(); while (!WindowShouldClose()) { const float dt{ GetFrameTime() }; //update { UpdateCamera(&camera, CAMERA_FREE); // Play animation when spacebar is held down if (IsKeyDown(KEY_N)) { AdvanceFrame(Skeleton, Animation, dt); } if (IsKeyPressed(KEY_ONE)) { ShowSkeleton = !ShowSkeleton; } } BeginDrawing(); { ClearBackground(BLACK); BeginMode3D(camera); { DrawGrid(10, 5.0f); if(ShowSkeleton) { Matrix M = MatrixIdentity(); DrawBonesSkeleton(Skeleton.Bones[0], M); } if (ShowMesh) { const Vector3 position{ 0.0f, 0.0f, 0.0f }; // Set model position const float scale = 1.f; DrawModel(model, position, scale, WHITE); } Matrix M = MatrixIdentity(); DrawTest(M); } EndMode3D(); //debug textures if(ShowTextures) { for(int i=0; i < 3; i++) { DrawTexture(textures[i], i * 100, 0, WHITE); } } } EndDrawing(); } UnloadModel(model); CloseWindow(); return 0; }