459 lines
13 KiB
C++
459 lines
13 KiB
C++
|
|
#include "log.h"
|
|
#include "engine.h"
|
|
#include "game/IGame.h"
|
|
#include "system/config.h"
|
|
#include "graphics/gl_utils.h"
|
|
#include "graphics/gl_ext.h"
|
|
#include "graphics/glsl_prog.h"
|
|
#include "graphics/texture.h"
|
|
#include "graphics/vao.h"
|
|
#include "renderer/Model.h"
|
|
#include "renderer/mesh.h"
|
|
#include "renderer/camera.h"
|
|
#include "renderer/font.h"
|
|
#include "button.h"
|
|
|
|
#include "mdl_loader.h"
|
|
|
|
#include <mat4x4.hpp>
|
|
#include <gtc/matrix_transform.hpp>
|
|
#include <gtc/type_ptr.hpp>
|
|
#include <gtc/random.hpp>
|
|
using glm::vec3;
|
|
using glm::vec4;
|
|
using glm::mat3;
|
|
using glm::mat4;
|
|
using glm::translate;
|
|
using glm::scale;
|
|
using glm::radians;
|
|
|
|
void VAOFromModel(Model *mdl, VertexArrayObject *out);
|
|
void VAOFromMDL(Model *mdl, VertexArrayObject *out,int a_weight,int a_bones);
|
|
void GenRandomFrame(mat4 *out, mat4 *base, mat4 *inv, Model *mdl);
|
|
void InterpFrames(float a,mat4 *f1, mat4 *f2, mat4 *out,int num);
|
|
void MeshFromModelSkeleton(Mesh *mesh, Model *mdl, mat4 *bonesMtx);
|
|
void UpdateSkeletonMesh(Mesh *mesh, Model *mdl, mat4 *bonesMtx);
|
|
void DrawModel(Model *mdl,VertexArrayObject *vao);
|
|
void DrawMesh(Mesh *mesh);
|
|
|
|
class skinGame: public IGame{
|
|
public:
|
|
skinGame(){}
|
|
void Created();
|
|
void Changed(int w, int h);
|
|
void Draw();
|
|
const char *GetGamedir(){
|
|
return "skinning";
|
|
}
|
|
void OnTouch(float tx, float ty, int ta, int tf);
|
|
|
|
int width;
|
|
int height;
|
|
float aspect;
|
|
|
|
ResourceManager resMan;
|
|
Font *font;
|
|
glslProg progTex;
|
|
glslProg progSkin;
|
|
int a_weight;
|
|
int a_bones;
|
|
int u_bonesMtx;
|
|
|
|
Texture texWhite;
|
|
Model *mdlTest;
|
|
VertexArrayObject vaoTest;
|
|
Camera camera;
|
|
mat4 mvpMtx;
|
|
mat4 modelMtx;
|
|
mat4 *bonesBaseMtx;
|
|
mat4 *bonesInvMtx;
|
|
mat4 *bonesMtx;
|
|
mat4 *frame1Mtx;
|
|
mat4 *frame2Mtx;
|
|
mat4 *skelMeshMtx;
|
|
Mesh meshSkeleton;
|
|
float curTime;
|
|
int animIdx;
|
|
|
|
Joystick joyL;
|
|
Joystick joyM;
|
|
Button bNext;
|
|
Button bPrev;
|
|
|
|
double oldTime;
|
|
};
|
|
|
|
IGame *CreateGame(){
|
|
return new skinGame();
|
|
}
|
|
|
|
void skinGame::Created()
|
|
{
|
|
srand(time(NULL));
|
|
Log("skinning test Created()\n");
|
|
|
|
GLExtensions::Init();
|
|
|
|
resMan.Init();
|
|
resMan.AddModelLoader(new MDLLoader(&resMan));
|
|
|
|
progTex.CreateFromFile("generic","col_tex");
|
|
progTex.u_mvpMtx = progTex.GetUniformLoc("u_mvpMtx");
|
|
progTex.u_color = progTex.GetUniformLoc("u_color");
|
|
|
|
progSkin.CreateFromFile("skinning","skinning");
|
|
progSkin.u_mvpMtx = progSkin.GetUniformLoc("u_mvpMtx");
|
|
progSkin.u_modelMtx = progSkin.GetUniformLoc("u_modelMtx");
|
|
progSkin.u_color = progSkin.GetUniformLoc("u_color");
|
|
u_bonesMtx = progSkin.GetUniformLoc("u_bonesMtx");
|
|
a_weight = progSkin.GetAttribLoc("a_weight");
|
|
a_bones = progSkin.GetAttribLoc("a_bones");
|
|
|
|
CheckGLError("Created shaders", __FILE__, __LINE__);
|
|
|
|
font = new Font();
|
|
font->LoadBMFont("sansation",&resMan);
|
|
|
|
uint8_t texWhiteData[1] = {255};
|
|
texWhite.Create(1, 1);
|
|
texWhite.Upload(0, GL_LUMINANCE, texWhiteData);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
CheckGLError("Created textures", __FILE__, __LINE__);
|
|
|
|
ConfigFile cfg;
|
|
cfg.Load("config.txt");
|
|
animIdx = cfg.GetInt("anim");
|
|
modelMtx = scale(mat4(1),cfg.GetVec3("scale"));
|
|
modelMtx = rotate(modelMtx,radians(-90.0f),vec3(1,0,0));
|
|
|
|
mdlTest = resMan.GetModel(cfg["model"].c_str());
|
|
if(cfg["model"]=="models/player.mdl"){
|
|
mdlTest->materials.Resize(6);
|
|
mdlTest->materials[0].mat=new TexMaterial(resMan.GetTexture("chell/chell_head_diffuse.vtf"));
|
|
mdlTest->materials[1].mat=new TexMaterial(resMan.GetTexture("chell/gambler_eyes.vtf"));
|
|
mdlTest->materials[2].mat=new TexMaterial(resMan.GetTexture("chell/gambler_eyes.vtf"));
|
|
mdlTest->materials[3].mat=new TexMaterial(resMan.GetTexture("chell/chell_torso_diffuse.vtf"));
|
|
mdlTest->materials[4].mat=new TexMaterial(resMan.GetTexture("chell/chell_legs_diffuse.vtf"));
|
|
mdlTest->materials[5].mat=new TexMaterial(resMan.GetTexture("chell/chell_hair.vtf"));
|
|
|
|
modelMtx = rotate(modelMtx,radians(90.0f),vec3(1,0,0));
|
|
}
|
|
if(cfg["model"]=="models/turret.mdl"){
|
|
mdlTest->materials.Resize(4);
|
|
mdlTest->materials[0].mat=new TexMaterial(resMan.GetTexture("turret/turret_frame02.vtf"));
|
|
mdlTest->materials[1].mat=new TexMaterial(resMan.GetTexture("turret/turret_frame01.vtf"));
|
|
mdlTest->materials[2].mat=new TexMaterial(resMan.GetTexture("turret/turret_casing.vtf"));
|
|
mdlTest->materials[3].mat=new TexMaterial(resMan.GetTexture("turret/turret_eye.vtf"));
|
|
}
|
|
if(cfg["model"]=="models/headcrabclassic.mdl"){
|
|
mdlTest->materials.Resize(1);
|
|
mdlTest->materials[0].mat=new TexMaterial(resMan.GetTexture("headcrabsheet.vtf"));
|
|
}
|
|
if(cfg["model"]=="models/male_07.mdl"){
|
|
mdlTest->materials.Resize(8);
|
|
mdlTest->materials[2].mat=new TexMaterial(resMan.GetTexture("group03/mike_facemap.vtf"));
|
|
mdlTest->materials[3].mat=new TexMaterial(resMan.GetTexture("group03/citizen_sheet.vtf"));
|
|
}
|
|
VAOFromMDL(mdlTest,&vaoTest,a_weight,a_bones);
|
|
//mdlTest = resMan.GetModel("cube.nmf");
|
|
//VAOFromModel(mdlTest,&vaoTest);
|
|
|
|
bonesBaseMtx = new mat4[mdlTest->skeleton.size];
|
|
bonesInvMtx = new mat4[mdlTest->skeleton.size];
|
|
bonesMtx = new mat4[mdlTest->skeleton.size];
|
|
frame1Mtx = new mat4[mdlTest->skeleton.size];
|
|
frame2Mtx = new mat4[mdlTest->skeleton.size];
|
|
skelMeshMtx = new mat4[mdlTest->skeleton.size];
|
|
for(int i=0;i<mdlTest->skeleton.size;i++){
|
|
bonesBaseMtx[i] = glm::mat4_cast(mdlTest->skeleton[i].rot);
|
|
bonesBaseMtx[i][3] = vec4(mdlTest->skeleton[i].pos,1.0f);
|
|
if(mdlTest->skeleton[i].parent!=-1)
|
|
bonesBaseMtx[i] = bonesBaseMtx[mdlTest->skeleton[i].parent] * bonesBaseMtx[i];
|
|
bonesInvMtx[i] = glm::inverse(bonesBaseMtx[i]);
|
|
bonesMtx[i] = mat4(1);
|
|
}
|
|
MeshFromModelSkeleton(&meshSkeleton,mdlTest,bonesBaseMtx);
|
|
curTime=0;
|
|
|
|
glClearColor(0.3,0.3,0.3,1.0);
|
|
|
|
camera.pos = vec3(0,1.5,1.6);
|
|
camera.rot = vec3(10,0,0);
|
|
camera.UpdateView();
|
|
|
|
joyL = Joystick(0,0.5,0.5,0.5);
|
|
joyM = Joystick(0.5,0.5,0.5,0.5);
|
|
bNext = Button(0,0,0.1,0.1);
|
|
bPrev = Button(0.2,0,0.1,0.1);
|
|
|
|
oldTime = GetTime();
|
|
}
|
|
|
|
void GenRandomFrame(mat4 *out, mat4 *base, mat4 *inv, Model *mdl){
|
|
for(int i=0;i<mdl->skeleton.size;i++){
|
|
if(i!=0){
|
|
out[i] = base[i]*glm::mat4_cast(glm::quat(glm::linearRand(vec3(-0.2f),vec3(0.2f))))*inv[i];
|
|
if(mdl->skeleton[i].parent!=-1){
|
|
out[i] = out[mdl->skeleton[i].parent] * out[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
void InterpFramesMtx(float a,mat4 *f1, mat4 *f2, mat4 *out,int num){
|
|
for(int i=0;i<num;i++){
|
|
out[i] = glm::mix(f1[i],f2[i],a);
|
|
}
|
|
}
|
|
*/
|
|
void SetupFrame(mat4 *out, Animation_t& anim, float frame){
|
|
int a = floor(frame);
|
|
int b = ceil(frame);
|
|
if(b>=anim.frames.size)
|
|
b = anim.frames.size-1;
|
|
float s = frame-a;
|
|
AnimFrame_t &fr1 = anim.frames[a];
|
|
AnimFrame_t &fr2 = anim.frames[b];
|
|
for(int i=0;i<fr1.bones.size;i++){
|
|
out[i] = glm::mat4_cast(glm::mix(fr1.bones[i].rot,fr2.bones[i].rot,s));
|
|
out[i][3] = vec4(glm::mix(fr1.bones[i].pos,fr2.bones[i].pos,s),1.0f);
|
|
if(fr1.bones[i].parent!=-1)
|
|
out[i] = out[fr1.bones[i].parent] * out[i];
|
|
}
|
|
}
|
|
|
|
void skinGame::Changed(int w, int h){
|
|
width = w;
|
|
height = h;
|
|
aspect = w/(float)h;
|
|
glViewport(0,0,w,h);
|
|
camera.UpdateProj(80,aspect,0.1,100);
|
|
CheckGLError("Changed", __FILE__, __LINE__);
|
|
}
|
|
|
|
void skinGame::Draw()
|
|
{
|
|
double startTime = GetTime();
|
|
float deltaTime = (startTime-oldTime);
|
|
oldTime = startTime;
|
|
|
|
if(bNext.pressed){
|
|
bNext.pressed=false;
|
|
if(mdlTest->animations.size)
|
|
animIdx=(animIdx+1)%mdlTest->animations.size;
|
|
curTime=0;
|
|
/*GenRandomFrame(bonesMtx,bonesBaseMtx,bonesInvMtx,mdlTest);
|
|
for(int i=0;i<mdlTest->skeleton.size;i++){
|
|
skelMeshMtx[i] = bonesMtx[i]*bonesBaseMtx[i];
|
|
}
|
|
UpdateSkeletonMesh(&meshSkeleton,mdlTest,skelMeshMtx);*/
|
|
}
|
|
if(bPrev.pressed){
|
|
bPrev.pressed=false;
|
|
if(mdlTest->animations.size)
|
|
animIdx=(animIdx-1+mdlTest->animations.size)%mdlTest->animations.size;
|
|
curTime=0;
|
|
}
|
|
|
|
curTime+=deltaTime*0.5;
|
|
/*
|
|
if(curTime>=1){
|
|
memcpy(frame1Mtx,frame2Mtx,sizeof(mat4)*mdlTest->skeleton.size);
|
|
GenRandomFrame(frame2Mtx,bonesBaseMtx,bonesInvMtx,mdlTest);
|
|
curTime = 0;
|
|
}
|
|
InterpFramesMtx(curTime,frame1Mtx,frame2Mtx,bonesMtx,mdlTest->skeleton.size);
|
|
for(int i=0;i<mdlTest->skeleton.size;i++){
|
|
skelMeshMtx[i] = bonesMtx[i]*bonesBaseMtx[i];
|
|
}
|
|
UpdateSkeletonMesh(&meshSkeleton,mdlTest,skelMeshMtx);
|
|
*/
|
|
float curFrame = 0;
|
|
if(mdlTest->animations.size){
|
|
curFrame = curTime*mdlTest->animations[animIdx].fps;
|
|
while(curFrame>=mdlTest->animations[animIdx].frames.size)
|
|
curFrame -= mdlTest->animations[animIdx].frames.size;
|
|
SetupFrame(skelMeshMtx,mdlTest->animations[animIdx],curFrame);
|
|
UpdateSkeletonMesh(&meshSkeleton,mdlTest,skelMeshMtx);
|
|
for(int i=0;i<mdlTest->skeleton.size;i++){
|
|
bonesMtx[i] = skelMeshMtx[i]*bonesInvMtx[i];
|
|
}
|
|
}
|
|
|
|
camera.pos += glm::inverse(mat3(camera.viewMtx))*vec3(joyM.vel.x,0,-joyM.vel.y*aspect)*5.0f*deltaTime;
|
|
camera.rot += vec3(-joyL.vel.y*aspect,-joyL.vel.x,0)*90.0f*deltaTime;
|
|
camera.UpdateView();
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
|
|
|
|
mvpMtx = camera.projMtx*camera.viewMtx*modelMtx;
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
glCullFace(GL_FRONT);
|
|
glEnable(GL_CULL_FACE);
|
|
texWhite.Bind();
|
|
glslProg *progCur = &progSkin;
|
|
//progCur = &progTex;
|
|
progCur->Use();
|
|
progCur->UniformMat4(progCur->u_mvpMtx,mvpMtx);
|
|
if(progCur->u_modelMtx>=0)
|
|
progCur->UniformMat4(progCur->u_modelMtx,modelMtx);
|
|
if(progCur->u_color>=0)
|
|
progCur->UniformVec4(progCur->u_color,vec4(1));
|
|
if(u_bonesMtx>=0){
|
|
glUniformMatrix4fv(u_bonesMtx,mdlTest->skeleton.size,false,glm::value_ptr(bonesMtx[0]));
|
|
}
|
|
|
|
DrawModel(mdlTest,&vaoTest);
|
|
|
|
mvpMtx = camera.projMtx*camera.viewMtx*modelMtx;
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDisable(GL_CULL_FACE);
|
|
glEnableVertexAttribArray(0);
|
|
progCur = &progTex;
|
|
progCur->Use();
|
|
progCur->UniformMat4(progCur->u_mvpMtx,mvpMtx);
|
|
if(progCur->u_color>=0)
|
|
progCur->UniformVec4(progCur->u_color,vec4(1));
|
|
texWhite.Bind();
|
|
DrawMesh(&meshSkeleton);
|
|
|
|
glDisableVertexAttribArray(1);
|
|
|
|
glEnableVertexAttribArray(2);
|
|
|
|
mat4 mtx2D = mat4(1);
|
|
mtx2D = glm::translate(mtx2D,glm::vec3(-1,-1,0));
|
|
mtx2D = glm::scale(mtx2D,glm::vec3(2.0f,2.0f*aspect,0));
|
|
progCur->UniformMat4(progCur->u_mvpMtx,mtx2D);
|
|
font->Print("Next",bNext.x,(0.95-bNext.y)/aspect,0.5/aspect);
|
|
font->Print("Prev",bPrev.x,(0.95-bPrev.y)/aspect,0.5/aspect);
|
|
char temp[256]={0};
|
|
if(mdlTest->animations.size){
|
|
snprintf(temp,256,"animation %d/%d: %s\n",animIdx,mdlTest->animations.size,mdlTest->animations[animIdx].name.c_str());
|
|
font->Print(temp,0,0.1/aspect,0.5/aspect);
|
|
snprintf(temp,256,"frame %d/%d\n",(int)curFrame,mdlTest->animations[animIdx].frames.size);
|
|
font->Print(temp,0,0.15/aspect,0.5/aspect);
|
|
}
|
|
glDisableVertexAttribArray(0);
|
|
glDisableVertexAttribArray(2);
|
|
glUseProgram(0);
|
|
CheckGLError("Draw", __FILE__, __LINE__);
|
|
}
|
|
|
|
void skinGame::OnTouch(float tx, float ty, int ta, int tf){
|
|
float x = tx/width;
|
|
#ifdef ANDROID
|
|
float y = (ty-64)/height;
|
|
#else
|
|
float y = ty/height;
|
|
#endif
|
|
if(ta==0){
|
|
bNext.Hit(x,y);
|
|
bPrev.Hit(x,y);
|
|
joyL.Hit(x,y,tf);
|
|
joyM.Hit(x,y,tf);
|
|
}else if(ta==1){
|
|
joyL.Release(tf);
|
|
joyM.Release(tf);
|
|
}else if(ta==2){
|
|
joyL.Move(x,y,tf);
|
|
joyM.Move(x,y,tf);
|
|
}
|
|
}
|
|
|
|
void VAOFromModel(Model *mdl, VertexArrayObject *out){
|
|
out->Create();
|
|
out->Bind();
|
|
//TODO separate function for vbo attribs
|
|
mdl->vbo.Bind();
|
|
for(int i=0; i<mdl->vertexFormat.size; i++){
|
|
vertAttrib_t &va = mdl->vertexFormat[i];
|
|
out->SetAttribute(va.id,va.size,va.type,va.norm,va.stride,(void*)va.offset);
|
|
}
|
|
mdl->vbo.Unbind();
|
|
if(mdl->indexCount){
|
|
mdl->ibo.Bind();
|
|
}
|
|
out->Unbind();
|
|
}
|
|
|
|
void VAOFromMDL(Model *mdl, VertexArrayObject *out,int a_weight,int a_bones){
|
|
out->Create();
|
|
out->Bind();
|
|
mdl->vbo.Bind();
|
|
|
|
for(int i=0; i<mdl->vertexFormat.size; i++){
|
|
vertAttrib_t &va = mdl->vertexFormat[i];
|
|
int id = va.id;
|
|
if(va.id == 3)
|
|
id = a_weight;
|
|
else if(va.id == 4)
|
|
id = a_bones;
|
|
out->SetAttribute(id,va.size,va.type,va.norm,va.stride,(void*)va.offset);
|
|
}
|
|
mdl->vbo.Unbind();
|
|
if(mdl->indexCount){
|
|
mdl->ibo.Bind();
|
|
}
|
|
out->Unbind();
|
|
}
|
|
|
|
void MeshFromModelSkeleton(Mesh *mesh, Model *mdl,mat4 *bonesMtx){
|
|
int nv = mdl->skeleton.size*2;
|
|
float *verts = new float[nv*3];
|
|
*mesh = Mesh(verts,nv,GL_LINES);
|
|
UpdateSkeletonMesh(mesh,mdl,bonesMtx);
|
|
}
|
|
|
|
void UpdateSkeletonMesh(Mesh *mesh, Model *mdl, mat4 *bonesMtx){
|
|
for(int i=0;i<mdl->skeleton.size;i++){
|
|
glm::vec3 *v = (glm::vec3*)(mesh->verts+i*6);
|
|
v[0] = vec3(bonesMtx[i]*vec4(0,0,0,1));
|
|
int p = mdl->skeleton[i].parent;
|
|
v[1] = p!=-1 ? vec3(bonesMtx[p]*vec4(0,0,0,1)) : v[0];
|
|
}
|
|
}
|
|
|
|
void DrawModel(Model *mdl,VertexArrayObject *vao){
|
|
/*
|
|
mdl->vbo.Bind();
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,mdl->vertexStride,0);
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,mdl->vertexStride,(void*)12);
|
|
glEnableVertexAttribArray(2);
|
|
glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,mdl->vertexStride,(void*)24);
|
|
mdl->vbo.Unbind();
|
|
*/
|
|
vao->Bind();
|
|
|
|
for(int i=0; i<mdl->submeshes.size; i++){
|
|
if(mdl->materials.size&&mdl->materials[mdl->submeshes[i].mat].mat)
|
|
mdl->materials[mdl->submeshes[i].mat].mat->Bind(0);
|
|
if(mdl->indexCount){
|
|
//mdl->ibo.Bind();
|
|
glDrawElements(GL_TRIANGLES,mdl->submeshes[i].count,mdl->indexType,(void*)(mdl->submeshes[i].offs*mdl->indexSize));
|
|
//mdl->ibo.Unbind();
|
|
}else{
|
|
glDrawArrays(GL_TRIANGLES, mdl->submeshes[i].offs, mdl->submeshes[i].count);
|
|
}
|
|
}
|
|
//glDrawArrays(GL_POINTS, 0, mdl->vertexCount);
|
|
vao->Unbind();
|
|
}
|
|
|
|
void DrawMesh(Mesh *mesh){
|
|
mesh->Bind();
|
|
mesh->Draw();
|
|
mesh->Unbind();
|
|
}
|
|
|
|
Button::Button(float nx, float ny, float nw, float nh, bool adjust):
|
|
x(nx),y(ny),w(nw),h(nh),text(0),active(true),pressed(false)
|
|
{
|
|
}
|
|
void Button::Update(){}
|
|
bool Button::SetUniform(int loc){return false;}
|