515 lines
11 KiB
C++
515 lines
11 KiB
C++
|
|
#include "log.h"
|
|
#include "graphics/platform_gl.h"
|
|
#include "button.h"
|
|
#include "renderer/renderer.h"
|
|
#include "renderer/mesh.h"
|
|
#include "resource/ResourceManager.h"
|
|
|
|
#include "game/IGame.h"
|
|
|
|
#include <vector>
|
|
#include <vec2.hpp>
|
|
#include <gtc/random.hpp>
|
|
#include <gtc/matrix_transform.hpp>
|
|
#include <common.hpp>
|
|
#include <geometric.hpp>
|
|
using glm::vec2;
|
|
using glm::vec3;
|
|
using glm::linearRand;
|
|
using glm::clamp;
|
|
using glm::abs;
|
|
using glm::normalize;
|
|
using glm::translate;
|
|
using glm::scale;
|
|
|
|
class vec3_queue{
|
|
public:
|
|
vec3_queue():_data(){}
|
|
void push(vec3 v){
|
|
_data.push_back(v);
|
|
for(int i=_data.size()-2;i>=0;i--){
|
|
_data[i+1]=_data[i];
|
|
}
|
|
_data[0] = v;
|
|
}
|
|
vec3 pop(){
|
|
if(!_data.size())
|
|
return vec3(0);
|
|
vec3 v = _data[_data.size()-1];
|
|
_data.erase(_data.end()-1);
|
|
return v;
|
|
}
|
|
vec3& operator[] (int n){
|
|
return _data[n];
|
|
}
|
|
void *data(){
|
|
return _data.data();
|
|
}
|
|
int size(){
|
|
return _data.size();
|
|
}
|
|
void clear(){
|
|
_data.clear();
|
|
}
|
|
private:
|
|
std::vector<vec3> _data;
|
|
};
|
|
|
|
class Entity
|
|
{
|
|
public:
|
|
virtual ~Entity(){}
|
|
virtual void Update(float dt){}
|
|
virtual void UpdateTail(){}
|
|
virtual void Respawn(){}
|
|
|
|
vec3 pos;
|
|
vec3 vel;
|
|
vec2 size;
|
|
float mass;
|
|
int hp;
|
|
|
|
vec3_queue trail;
|
|
};
|
|
|
|
class Unit: public Entity
|
|
{
|
|
public:
|
|
Unit();
|
|
virtual ~Unit(){}
|
|
virtual void Update(float dt);
|
|
virtual void UpdateTail();
|
|
virtual void Respawn();
|
|
vec3 target;
|
|
};
|
|
|
|
Unit::Unit():Entity(),target(0){
|
|
vel = vec3(0);
|
|
size = vec2(0.02f);
|
|
mass = 0.5f;
|
|
hp = 100;
|
|
};
|
|
|
|
Entity* Collide(Unit *u,vec3 orig, vec3 vel);
|
|
vec3 GetGravity(Unit *u);
|
|
|
|
vec3 planetPos(0.0f,0.0f,0.0f);
|
|
const int asteroidsCount = 2048;
|
|
float planetRadius = 1;
|
|
float planetMass = 2000;
|
|
float G = 0.000066;
|
|
const float timeScale = 10;
|
|
|
|
float aspect=1;
|
|
|
|
float RadiusFromMass(float m)
|
|
{
|
|
const float p = 5515.3;
|
|
float V = m/p;
|
|
return glm::pow((3*V)/(4* M_PI ),1.0/3.0);
|
|
//return glm::pow(m,0.5f)*0.02f;
|
|
}
|
|
|
|
void Unit::Update(float dt)
|
|
{
|
|
if(hp<=0)
|
|
return;
|
|
|
|
float d=glm::distance(pos,planetPos);
|
|
if(d<planetRadius+size.x||d>59)
|
|
{
|
|
planetMass += mass;
|
|
planetRadius = RadiusFromMass(planetMass);
|
|
Respawn();
|
|
}
|
|
/*
|
|
if(pos.x<0||pos.x>aspect-size.x){
|
|
vel.x*=-1;
|
|
//vel+=linearRand(vec3(-0.02,-0.06,0),vec3(0.02,0.06,0));
|
|
pos.x=clamp(pos.x,0.0f,aspect-size.x);
|
|
}
|
|
if(pos.y<0||pos.y>1-size.y){
|
|
vel.y*=-1;
|
|
//vel+=linearRand(vec3(-0.06,-0.02,0),vec3(0.06,0.02,0));
|
|
pos.y=clamp(pos.y,0.0f,1-size.y);
|
|
}
|
|
*/
|
|
//vel+=vec3(0,0.5f*dt,0);
|
|
vec3 gravity = GetGravity(this);
|
|
vel += gravity/mass*dt;
|
|
|
|
if(Entity *e = Collide(this,pos,vel*dt))
|
|
{
|
|
/*if(abs(pos.x-e->pos.x)>abs(pos.y-e->pos.y)){
|
|
vel.x*=-1;
|
|
e->vel.x*=-1;
|
|
}else{
|
|
vel.y*=-1;
|
|
e->vel.y*=-1;
|
|
}*/
|
|
|
|
vel = (mass*vel + e->mass*e->vel)/(mass+e->mass);
|
|
pos = (mass*pos + e->mass*e->pos)/(mass+e->mass);
|
|
mass+=e->mass;
|
|
//e->Respawn();
|
|
e->hp = 0;
|
|
}
|
|
|
|
pos+=vel*dt;
|
|
if(trail.size())
|
|
trail[0] = pos;
|
|
|
|
size = vec2(RadiusFromMass(mass));
|
|
}
|
|
void Unit::UpdateTail()
|
|
{
|
|
if(hp<=0)
|
|
return;
|
|
|
|
if(trail.size()>64){
|
|
trail.pop();
|
|
}
|
|
trail.push(pos);
|
|
}
|
|
|
|
void Unit::Respawn()
|
|
{
|
|
hp = 100;
|
|
|
|
mass = linearRand(1.0f, 4.0f);
|
|
|
|
size = vec2(RadiusFromMass(mass));
|
|
const float rr = 12;
|
|
//pos = linearRand(vec3(-rr,-rr,0.0),vec3(rr,rr,0));
|
|
pos = vec3(glm::diskRand(rr),0.0f);
|
|
pos += normalize(pos-planetPos)*planetRadius*1.2f;
|
|
//vel = linearRand(vec3(-0.6,-0.6,0),vec3(0.6,0.6,0));
|
|
//vel = vec3(0);
|
|
|
|
//vel = glm::cross(normalize(planetPos-pos),vec3(0,0,1.0f))*linearRand(-1.6f,1.6f);
|
|
vel = glm::cross(normalize(planetPos-pos),vec3(0,0,1.0f)) * glm::sqrt(G*(planetMass/(glm::length(planetPos-pos))));
|
|
//vel *= (rand()&1 ? 1.0f : -1.0f);
|
|
vel *= linearRand(0.9f,1.1f);
|
|
|
|
vel.z = 0;
|
|
trail.clear();
|
|
}
|
|
|
|
std::vector<Entity*> entities;
|
|
|
|
Entity * Collide(Unit *u, vec3 orig, vec3 vel)
|
|
{
|
|
//vec3 dst = orig+vel;
|
|
for(uint32_t i=0;i<entities.size();i++){
|
|
Entity *e = entities[i];
|
|
if(e==u || e->hp<=0)
|
|
continue;
|
|
/*if(e->pos.x>dst.x+u->size.x||e->pos.x<dst.x-u->size.x)
|
|
continue;
|
|
if(e->pos.y>dst.y+u->size.y||e->pos.y<dst.y-u->size.y)
|
|
continue;*/
|
|
float d = glm::distance(e->pos,u->pos);
|
|
if(d > e->size.x+u->size.x)
|
|
continue;
|
|
return e;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
vec3 GetGravity(Unit *u)
|
|
{
|
|
vec3 g=vec3(0);
|
|
g += normalize(planetPos-u->pos)*G*((planetMass*u->mass)/glm::pow(glm::distance(u->pos,planetPos),2.0f));
|
|
for(uint32_t i=0;i<entities.size();i++){
|
|
Entity *e = entities[i];
|
|
if(e==u || e->hp<=0)
|
|
continue;
|
|
vec3 d = e->pos-u->pos;
|
|
g += normalize(d)*G*((e->mass*u->mass)/glm::pow(glm::length(d),2.0f));
|
|
}
|
|
return g;
|
|
}
|
|
|
|
Mesh meshCircle;
|
|
|
|
class GravityGame: public IGame{
|
|
public:
|
|
GravityGame(){}
|
|
void Created();
|
|
void Changed(int w, int h);
|
|
void Draw();
|
|
const char *GetGamedir(){
|
|
return "base";
|
|
}
|
|
void OnTouch(float tx, float ty, int ta, int tf);
|
|
void DrawCircle(vec3 pos, float size);
|
|
IRenderer *rend;
|
|
ResourceManager *resMan;
|
|
Camera cam;
|
|
//vec2 pos;
|
|
|
|
bool drawTrails;
|
|
Button bAdd;
|
|
Button bTrails;
|
|
Scroll bScroll;
|
|
};
|
|
|
|
IGame *CreateGame(){
|
|
return new GravityGame();
|
|
}
|
|
|
|
Unit *CreateSatellite(float mass, float height, Unit *parent)
|
|
{
|
|
Unit *un = new Unit();
|
|
un->mass = mass;
|
|
un->size = vec2(RadiusFromMass(un->mass));
|
|
if(parent)
|
|
un->pos = parent->pos + vec3(0, height, 0);
|
|
else
|
|
un->pos = vec3(0, planetRadius*1.2f+height, 0);
|
|
if(parent)
|
|
un->vel = parent->vel + glm::cross(normalize(parent->pos-un->pos),vec3(0,0,1.0f)) * glm::sqrt(G*(parent->mass/(glm::length(parent->pos-un->pos))));
|
|
else
|
|
un->vel = glm::cross(normalize(planetPos-un->pos),vec3(0,0,1.0f)) * glm::sqrt(G*(planetMass/(glm::length(planetPos-un->pos))));
|
|
un->vel.z = 0;
|
|
un->trail.clear();
|
|
|
|
//un2->vel = glm::cross(normalize(planetPos-un2->pos),vec3(0,0,1.0f)) * glm::sqrt(G*(planetMass/(glm::length(planetPos-un2->pos))));
|
|
//un2->vel += glm::cross(normalize(un->pos-un2->pos),vec3(0,0,1.0f)) * glm::sqrt(G*(un->mass/(glm::length(un->pos-un2->pos))));
|
|
|
|
return un;
|
|
}
|
|
|
|
void GravityGame::Created()
|
|
{
|
|
srand(time(0));
|
|
|
|
Log("GravityGame Created()\n");
|
|
resMan = new ResourceManager();
|
|
resMan->Init();
|
|
rend = CreateRenderer();
|
|
rend->Init(RENDERER_GUI,resMan);
|
|
|
|
int nv = 32*2;
|
|
float *v = new float[nv*2];
|
|
int cv=0;
|
|
for(int i=0;i<32;i++){
|
|
float a = i/32.0f*M_PI*2;
|
|
float b = (i+1)/32.0f*M_PI*2;
|
|
v[cv++] = sin(a);
|
|
v[cv++] = cos(a);
|
|
v[cv++] = sin(b);
|
|
v[cv++] = cos(b);
|
|
}
|
|
meshCircle = Mesh(v, nv, GL_LINES);
|
|
|
|
planetRadius = RadiusFromMass(planetMass);
|
|
|
|
for(int i=0;i<4;i++){
|
|
Unit *un = new Unit();
|
|
un->Respawn();
|
|
entities.push_back(un);
|
|
}
|
|
|
|
//
|
|
{
|
|
Unit *un1 = CreateSatellite(35, 10, NULL);
|
|
entities.push_back(un1);
|
|
|
|
Unit *un2 = CreateSatellite(1, 0.8, un1);
|
|
entities.push_back(un2);
|
|
|
|
Unit *un3 = new Unit();
|
|
entities.push_back(un3);
|
|
un3->mass = 0.04f;
|
|
un3->size = vec2(RadiusFromMass(un3->mass));
|
|
un3->pos = un2->pos+vec3(0,0.09,0);
|
|
un3->vel = glm::cross(normalize(planetPos-un3->pos),vec3(0,0,1.0f)) * glm::sqrt(G*(planetMass/(glm::length(planetPos-un3->pos))));
|
|
un3->vel += glm::cross(normalize(un1->pos-un3->pos),vec3(0,0,1.0f)) * glm::sqrt(G*(un1->mass/(glm::length(un1->pos-un3->pos))));
|
|
un3->vel += glm::cross(normalize(un2->pos-un3->pos),vec3(0,0,1.0f)) * glm::sqrt(G*(un2->mass/(glm::length(un2->pos-un3->pos))));
|
|
un3->vel.z = 0;
|
|
un3->trail.clear();
|
|
}
|
|
|
|
{
|
|
const float ir = 19;
|
|
const float cw = 2;
|
|
for(int i=0; i<asteroidsCount; i++){
|
|
Unit *un = new Unit();
|
|
un->mass = linearRand(0.04f, 2.0f);
|
|
un->size = vec2(RadiusFromMass(un->mass));
|
|
un->pos = vec3(glm::circularRand(ir), 0.0f);
|
|
un->pos += normalize(un->pos - planetPos)*linearRand(0.0f, cw);
|
|
un->vel = glm::cross(normalize(planetPos-un->pos), vec3(0,0,1.0f)) * glm::sqrt(G*(planetMass/(glm::length(planetPos-un->pos))));
|
|
//un->vel *= linearRand(0.9f,1.1f);
|
|
un->vel.z = 0;
|
|
un->trail.clear();
|
|
entities.push_back(un);
|
|
}
|
|
}
|
|
//
|
|
|
|
cam = Camera();
|
|
|
|
drawTrails = true;
|
|
|
|
bAdd = Button(0.02,0.1,0.2,0.08,"Add");
|
|
bScroll = Scroll(0.8,0.1,0.2,0.9);
|
|
bScroll.pos = 10;
|
|
bTrails = Button(0.25,0.1,0.3,0.08, "Draw trails");
|
|
rend->AddButton(&bAdd);
|
|
rend->AddButton(&bTrails);
|
|
|
|
|
|
}
|
|
|
|
void GravityGame::Changed(int w, int h)
|
|
{
|
|
rend->Resize(w,h);
|
|
aspect = rend->aspect;
|
|
//planetPos.x = 0.5f*aspect;
|
|
cam.SetOrtho(aspect,bScroll.pos,1);
|
|
}
|
|
|
|
void GravityGame::DrawCircle(vec3 pos, float size)
|
|
{
|
|
mat4 mtx = translate(mat4(1.0f),pos);
|
|
mtx = scale(mtx,vec3(size));
|
|
rend->SetModelMtx(mtx);
|
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 8, meshCircle.verts);
|
|
glDrawArrays(meshCircle.mode,0,meshCircle.numVerts);
|
|
}
|
|
|
|
void GravityGame::Draw()
|
|
{
|
|
if(bAdd.pressed)
|
|
{
|
|
bAdd.pressed=0;
|
|
Unit *un = new Unit();
|
|
un->Respawn();
|
|
entities.push_back(un);
|
|
}
|
|
if(bTrails.pressed)
|
|
{
|
|
bTrails.pressed = false;
|
|
drawTrails = !drawTrails;
|
|
if(drawTrails)
|
|
{
|
|
for(size_t i = 0;i<entities.size();i++){
|
|
entities[i]->trail.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
cam.SetOrtho(aspect,bScroll.pos*2,1);
|
|
|
|
float deltaTime = 0.02;
|
|
int subSteps = 10;
|
|
for(int i=0;i<subSteps;i++){
|
|
for(size_t i = 0;i<entities.size();i++){
|
|
entities[i]->Update(deltaTime/subSteps * timeScale);
|
|
}
|
|
|
|
for(size_t i = 0;i<entities.size();i++)
|
|
{
|
|
Entity *e = entities[i];
|
|
if(e->hp<=0){
|
|
entities.erase(entities.begin()+i);
|
|
delete e;
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
static int t=0;
|
|
if(drawTrails){
|
|
t++;
|
|
if(t%10==0){
|
|
for(size_t i = 0;i<entities.size();i++){
|
|
entities[i]->UpdateTail();
|
|
}
|
|
}
|
|
}
|
|
rend->Draw();
|
|
//rend->DrawRect(0.1,0.1,0.1/rend->aspect,0.1);
|
|
//rend->DrawText("test",0.45*rend->aspect,0.1,0.6*rend->aspect);
|
|
|
|
/*rend->DrawRect(pos.x,pos.y,0.1,0.1*rend->aspect);
|
|
char temp[256];
|
|
snprintf(temp,256,"pos: (%.3f, %.3f)",pos.x,pos.y);
|
|
rend->DrawText(temp,0.1,0.2,0.2*rend->aspect);
|
|
*/
|
|
|
|
char temp[256];
|
|
snprintf(temp,256,"objects count: %d\nplanet mass %.3f",entities.size(), planetMass);
|
|
rend->DrawText(temp,0.02,0.9,0.2*rend->aspect);
|
|
|
|
//mat4 mtx(1.0f);
|
|
rend->Set2DMode();
|
|
rend->SetCamera(&cam);
|
|
glDisableVertexAttribArray(2);
|
|
for(size_t i = 0;i<entities.size();i++){
|
|
Entity *e = entities[i];
|
|
//rend->DrawRect(e->pos.x/aspect,e->pos.y,e->size.x/rend->aspect,e->size.y);
|
|
DrawCircle(e->pos,e->size.x);
|
|
}
|
|
|
|
rend->SetModelMtx(mat4(1));
|
|
if(drawTrails)
|
|
{
|
|
for(size_t i = 0;i<entities.size();i++){
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, entities[i]->trail.data());
|
|
glDrawArrays(GL_LINE_STRIP,0,entities[i]->trail.size());
|
|
}
|
|
}
|
|
/*
|
|
mtx = translate(mtx,planetPos);
|
|
rend->SetModelMtx(mtx);
|
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 8, meshPlanet.verts);
|
|
glDrawArrays(meshPlanet.mode,0,meshPlanet.numVerts);
|
|
*/
|
|
DrawCircle(planetPos,planetRadius);
|
|
}
|
|
|
|
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)
|
|
{
|
|
/*if(adjust){
|
|
if(aspect>1)
|
|
h*=aspect;
|
|
else
|
|
w/=aspect;
|
|
}*/
|
|
}
|
|
|
|
Button::Button(float nx, float ny, float nw, float nh, const char *t, bool adjust):
|
|
x(nx),y(ny),w(nw),h(nh),text(t),active(true),pressed(false)
|
|
{
|
|
//if(adjust)
|
|
// w/=aspect;
|
|
}
|
|
void Button::Update(){}
|
|
bool Button::SetUniform(int loc){return false;}
|
|
|
|
void GravityGame::OnTouch(float tx, float ty, int ta, int tf){
|
|
float x = tx/rend->width;
|
|
#ifdef ANDROID
|
|
float y = (ty-64)/rend->height;
|
|
#else
|
|
float y = ty/rend->height;
|
|
#endif
|
|
|
|
if(ta==0){
|
|
bAdd.Hit(x,y);
|
|
bTrails.Hit(x,y);
|
|
bScroll.Hit(x,y,tf);
|
|
}else if(ta==1){
|
|
bScroll.Release(tf);
|
|
}else if(ta==2){
|
|
bScroll.Move(x,y,tf);
|
|
}
|
|
//pos = vec2(x,y);
|
|
}
|