openspades/Sources/Client/FallingBlock.cpp
2013-08-18 16:18:06 +09:00

211 lines
5.8 KiB
C++

//
// FallingBlock.cpp
// OpenSpades
//
// Created by yvt on 7/25/13.
// Copyright (c) 2013 yvt.jp. All rights reserved.
//
#include "FallingBlock.h"
#include "IRenderer.h"
#include "Client.h"
#include "IModel.h"
#include <limits.h>
#include "../Core/Debug.h"
#include "../Core/Exception.h"
#include "World.h"
#include "GameMap.h"
#include "SmokeSpriteEntity.h"
#include "ParticleSpriteEntity.h"
namespace spades {
namespace client {
FallingBlock::FallingBlock(Client *client,
std::vector<IntVector3> blocks):
client(client){
if(blocks.empty())
SPRaise("No block given");
// find min/max
int maxX = -1, maxY = -1, maxZ = -1;
int minX = INT_MAX, minY=INT_MAX, minZ = INT_MAX;
uint64_t xSum = 0, ySum = 0, zSum = 0;
numBlocks = (int)blocks.size();
for(size_t i = 0; i < blocks.size(); i++){
IntVector3 v = blocks[i];
if(v.x < minX) minX = v.x;
if(v.y < minY) minY = v.y;
if(v.z < minZ) minZ = v.z;
if(v.x > maxX) maxX = v.x;
if(v.y > maxY) maxY = v.y;
if(v.z > maxZ) maxZ = v.z;
xSum += v.x;
ySum += v.y;
zSum += v.z;
}
GameMap *map = client->GetWorld()->GetMap();
// build voxel model
vmodel = new VoxelModel(maxX - minX + 1, maxY - minY + 1,
maxZ - minZ + 1);
for(size_t i = 0; i < blocks.size(); i++){
IntVector3 v = blocks[i];
uint32_t col = map->GetColor(v.x, v.y, v.z);
vmodel->SetSolid(v.x - minX, v.y - minY, v.z - minZ,
col);
}
// center of gravity
Vector3 origin;
origin.x = (float)minX - (float)xSum / (float)blocks.size();
origin.y = (float)minY - (float)ySum / (float)blocks.size();
origin.z = (float)minZ - (float)zSum / (float)blocks.size();
vmodel->SetOrigin(origin);
Vector3 matTrans = MakeVector3((float)minX, (float)minY,
(float)minZ);
matTrans += .5f; // voxelmodel's (0,0,0) origins on block center
matTrans -= origin; // cancel origin
matrix = Matrix4::Translate(matTrans);
// build renderer model
model = client->GetRenderer()->CreateModel(vmodel);
time = 0.f;
}
FallingBlock::~FallingBlock(){
delete model;
delete vmodel;
}
bool FallingBlock::Update(float dt) {
time += dt;
GameMap *map = client->GetWorld()->GetMap();
Vector3 orig = matrix.GetOrigin();
if(time > 1.f ||
map->ClipBox(orig.x, orig.y, orig.z)){
// destroy
int w = vmodel->GetWidth();
int h = vmodel->GetHeight();
int d = vmodel->GetDepth();
Matrix4 vmat = lastMatrix;
vmat = vmat * Matrix4::Translate(vmodel->GetOrigin());
// block center
Vector3 vmOrigin = vmat.GetOrigin();
Vector3 vmAxis1 = vmat.GetAxis(0);
Vector3 vmAxis2 = vmat.GetAxis(1);
Vector3 vmAxis3 = vmat.GetAxis(2);
IImage *img = client->GetRenderer()->RegisterImage("Gfx/White.tga");
bool usePrecisePhysics = false;
float dist =(client->GetLastSceneDef().viewOrigin - matrix.GetOrigin()).GetLength();
if(dist < 16.f) {
if(numBlocks < 1000){
usePrecisePhysics = true;
}
}
float impact;
if(dist > 170.f)
return false;
impact = (float)numBlocks / 100.f;
client->grenadeVibration += impact / (dist + 5.f);
if(client->grenadeVibration > 1.f)
client->grenadeVibration = 1.f;
for(int x = 0; x < w; x++){
Vector3 p1 = vmOrigin + vmAxis1 * (float)x;
for(int y = 0; y < h; y++){
Vector3 p2 = p1 + vmAxis2 * (float)y;
for(int z = 0; z < d; z++){
if(!vmodel->IsSolid(x, y, z))
continue;
// inner voxel?
if(x > 0 && y > 0 && z > 0 &&
x < w-1 && y < h-1 && z < d-1 &&
vmodel->IsSolid(x-1, y, z) &&
vmodel->IsSolid(x+1, y, z) &&
vmodel->IsSolid(x, y-1, z) &&
vmodel->IsSolid(x, y+1, z) &&
vmodel->IsSolid(x, y, z-1) &&
vmodel->IsSolid(x, y, z+1))
continue;
uint32_t c = vmodel->GetColor(x, y, z);
Vector4 col;
col.x = (float)((uint8_t)(c)) / 255.f;
col.y = (float)((uint8_t)(c>>8)) / 255.f;
col.z = (float)((uint8_t)(c>>16)) / 255.f;
col.w = 1.;
Vector3 p3 = p2 + vmAxis3 * (float)z;
{
ParticleSpriteEntity *ent =
new SmokeSpriteEntity(client, col, 70.f);
ent->SetTrajectory(p3,
(MakeVector3(GetRandom()-GetRandom(),
GetRandom()-GetRandom(),
GetRandom()-GetRandom())) * 0.2f,
1.f, 0.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(1.0f,
0.5f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(1.0f + GetRandom()*0.5f, 0.f, 1.0f);
client->AddLocalEntity(ent);
}
col.w = 1.f;
for(int i = 0; i < 6; i++){
ParticleSpriteEntity *ent =
new ParticleSpriteEntity(client, img, col);
ent->SetTrajectory(p3,
MakeVector3(GetRandom()-GetRandom(),
GetRandom()-GetRandom(),
GetRandom()-GetRandom()) * 13.f,
1.f, .6f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(0.35f + GetRandom()*GetRandom()*0.1f);
ent->SetLifeTime(2.f, 0.f, 1.f);
if(usePrecisePhysics)
ent->SetBlockHitAction(ParticleSpriteEntity::BounceWeak);
client->AddLocalEntity(ent);
}
}
}
}
return false;
}
lastMatrix = matrix;
Matrix4 rot;
rot = Matrix4::Rotate(MakeVector3(1.f, 1.f, 0.f),
time * 1.4f * dt);
matrix = matrix * rot;
Matrix4 trans;
trans = Matrix4::Translate(0, 0, time * dt * 4.f);
matrix = trans * matrix;
return true;
}
void FallingBlock::Render3D() {
ModelRenderParam param;
param.matrix = matrix;
client->GetRenderer()->RenderModel(model, param);
}
}
}