// // 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 #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 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); } } }