1 /* 2 Copyright (c) 2013 yvt 3 based on code of pysnip (c) Mathias Kaerlev 2011-2012. 4 5 This file is part of OpenSpades. 6 7 OpenSpades is free software: you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation, either version 3 of the License, or 10 (at your option) any later version. 11 12 OpenSpades is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with OpenSpades. If not, see <http://www.gnu.org/licenses/>. 19 20 */ 21 22 #include "Client.h" 23 24 #include <Core/ConcurrentDispatch.h> 25 #include <Core/Settings.h> 26 #include <Core/Strings.h> 27 28 #include "CTFGameMode.h" 29 #include "Corpse.h" 30 #include "GameMap.h" 31 #include "Grenade.h" 32 #include "IGameMode.h" 33 #include "Player.h" 34 #include "TCGameMode.h" 35 #include "Weapon.h" 36 #include "World.h" 37 38 #include "ClientPlayer.h" 39 #include "ILocalEntity.h" 40 41 #include "NetClient.h" 42 43 DEFINE_SPADES_SETTING(cg_fov, "68"); 44 DEFINE_SPADES_SETTING(cg_thirdperson, "0"); 45 DEFINE_SPADES_SETTING(cg_manualFocus, "0"); 46 DEFINE_SPADES_SETTING(cg_depthOfFieldAmount, "1"); 47 DEFINE_SPADES_SETTING(cg_shake, "1"); 48 49 namespace spades { 50 namespace client { 51 52 #pragma mark - Drawing 53 GetCameraMode()54 ClientCameraMode Client::GetCameraMode() { 55 if (!world) { 56 return ClientCameraMode::None; 57 } 58 59 Player *p = world->GetLocalPlayer(); 60 if (!p) { 61 return ClientCameraMode::NotJoined; 62 } 63 64 if (p->IsAlive() && !p->IsSpectator()) { 65 // There exists an alive (non-spectator) local player 66 if ((int)cg_thirdperson != 0 && world->GetNumPlayers() <= 1) { 67 return ClientCameraMode::ThirdPersonLocal; 68 } 69 return ClientCameraMode::FirstPersonLocal; 70 } else { 71 // The local player is dead or a spectator 72 if (followCameraState.enabled) { 73 bool isAlive = world->GetPlayer(followedPlayerId)->IsAlive(); 74 if (followCameraState.firstPerson && isAlive) { 75 return ClientCameraMode::FirstPersonFollow; 76 } else { 77 return ClientCameraMode::ThirdPersonFollow; 78 } 79 } else { 80 if (p->IsSpectator()) { 81 return ClientCameraMode::Free; 82 } else { 83 // Look at your own cadaver! 84 return ClientCameraMode::ThirdPersonLocal; 85 } 86 } 87 } 88 } 89 GetCameraTargetPlayerId()90 int Client::GetCameraTargetPlayerId() { 91 switch (GetCameraMode()) { 92 case ClientCameraMode::None: 93 case ClientCameraMode::NotJoined: 94 case ClientCameraMode::Free: SPUnreachable(); 95 case ClientCameraMode::FirstPersonLocal: 96 case ClientCameraMode::ThirdPersonLocal: 97 SPAssert(world); 98 return world->GetLocalPlayerIndex(); 99 case ClientCameraMode::FirstPersonFollow: 100 case ClientCameraMode::ThirdPersonFollow: return followedPlayerId; 101 } 102 SPUnreachable(); 103 } 104 GetCameraTargetPlayer()105 Player &Client::GetCameraTargetPlayer() { 106 Player *p = world->GetPlayer(GetCameraTargetPlayerId()); 107 SPAssert(p); 108 return *p; 109 } 110 GetLocalFireVibration()111 float Client::GetLocalFireVibration() { 112 float localFireVibration = 0.f; 113 localFireVibration = time - localFireVibrationTime; 114 localFireVibration = 1.f - localFireVibration / 0.1f; 115 if (localFireVibration < 0.f) 116 localFireVibration = 0.f; 117 return localFireVibration; 118 } 119 GetAimDownZoomScale()120 float Client::GetAimDownZoomScale() { 121 Player &player = GetCameraTargetPlayer(); 122 if (!player.IsToolWeapon() || !player.IsAlive()) { 123 return 1.f; 124 } 125 126 ClientPlayer* clientPlayer = clientPlayers[player.GetId()]; 127 SPAssert(clientPlayer); 128 129 float delta = .8f; 130 switch (player.GetWeapon()->GetWeaponType()) { 131 case SMG_WEAPON: delta = .8f; break; 132 case RIFLE_WEAPON: delta = 1.4f; break; 133 case SHOTGUN_WEAPON: delta = .4f; break; 134 } 135 136 float aimDownState = clientPlayer->GetAimDownState(); 137 138 return 1.f + (3.f - 2.f * powf(aimDownState, 1.5f)) * powf(aimDownState, 3.f) * delta; 139 } 140 CreateSceneDefinition()141 SceneDefinition Client::CreateSceneDefinition() { 142 SPADES_MARK_FUNCTION(); 143 144 int shakeLevel = cg_shake; 145 146 SceneDefinition def; 147 def.time = (unsigned int)(time * 1000.f); 148 def.denyCameraBlur = true; 149 def.zFar = 200.f; 150 151 // Limit the range of cg_fov 152 // (note: comparsion with a NaN always results in false) 153 if (!((float)cg_fov < 90.0f)) { 154 cg_fov = 90.0f; 155 } 156 if (!((float)cg_fov > 45.0f)) { 157 cg_fov = 45.0f; 158 } 159 160 if (world) { 161 IntVector3 fogColor = world->GetFogColor(); 162 renderer->SetFogColor( 163 MakeVector3(fogColor.x / 255.f, fogColor.y / 255.f, fogColor.z / 255.f)); 164 165 def.blurVignette = 0.0f; 166 167 float roll = 0.f; 168 float scale = 1.f; 169 float vibPitch = 0.f; 170 float vibYaw = 0.f; 171 172 switch (GetCameraMode()) { 173 case ClientCameraMode::None: SPUnreachable(); 174 175 case ClientCameraMode::NotJoined: 176 def.viewOrigin = MakeVector3(256, 256, 4); 177 def.viewAxis[0] = MakeVector3(-1, 0, 0); 178 def.viewAxis[1] = MakeVector3(0, 1, 0); 179 def.viewAxis[2] = MakeVector3(0, 0, 1); 180 181 def.fovY = (float)cg_fov * static_cast<float>(M_PI) / 180.f; 182 def.fovX = atanf(tanf(def.fovY * .5f) * renderer->ScreenWidth() / 183 renderer->ScreenHeight()) * 2.f; 184 185 def.zNear = 0.05f; 186 187 def.skipWorld = false; 188 break; 189 190 case ClientCameraMode::FirstPersonLocal: 191 case ClientCameraMode::FirstPersonFollow: { 192 Player &player = GetCameraTargetPlayer(); 193 194 Matrix4 eyeMatrix = clientPlayers[player.GetId()]->GetEyeMatrix(); 195 196 def.viewOrigin = eyeMatrix.GetOrigin(); 197 def.viewAxis[0] = -eyeMatrix.GetAxis(0); 198 def.viewAxis[1] = -eyeMatrix.GetAxis(2); 199 def.viewAxis[2] = eyeMatrix.GetAxis(1); 200 201 if (shakeLevel >= 1) { 202 float localFireVibration = GetLocalFireVibration(); 203 localFireVibration *= localFireVibration; 204 205 if (player.GetTool() == Player::ToolSpade) { 206 localFireVibration *= 0.4f; 207 } 208 209 roll += (SampleRandomFloat() - SampleRandomFloat()) * 0.03f * localFireVibration; 210 scale += SampleRandomFloat() * 0.04f * localFireVibration; 211 212 vibPitch += localFireVibration * (1.f - localFireVibration) * 0.01f; 213 vibYaw += sinf(localFireVibration * (float)M_PI * 2.f) * 0.001f; 214 215 def.radialBlur += localFireVibration * 0.05f; 216 217 // sprint bob 218 { 219 float sp = SmoothStep(GetSprintState()); 220 vibYaw += sinf(player.GetWalkAnimationProgress() * 221 static_cast<float>(M_PI) * 2.f) * 222 0.01f * sp; 223 roll -= sinf(player.GetWalkAnimationProgress() * 224 static_cast<float>(M_PI) * 2.f) * 225 0.005f * (sp); 226 float p = cosf(player.GetWalkAnimationProgress() * 227 static_cast<float>(M_PI) * 2.f); 228 p = p * p; 229 p *= p; 230 p *= p; 231 vibPitch += p * 0.01f * sp; 232 233 if (shakeLevel >= 2) { 234 vibYaw += coherentNoiseSamplers[0].Sample( 235 player.GetWalkAnimationProgress() * 2.5f) * 236 0.005f * sp; 237 vibPitch += coherentNoiseSamplers[1].Sample( 238 player.GetWalkAnimationProgress() * 2.5f) * 239 0.01f * sp; 240 roll += coherentNoiseSamplers[2].Sample( 241 player.GetWalkAnimationProgress() * 2.5f) * 242 0.008f * sp; 243 244 scale += sp * 0.1f; 245 } 246 } 247 } 248 249 def.fovY = (float)cg_fov * static_cast<float>(M_PI) / 180.f; 250 def.fovX = atanf(tanf(def.fovY * .5f) * renderer->ScreenWidth() / 251 renderer->ScreenHeight()) * 2.f; 252 253 // for 1st view, camera blur can be used 254 def.denyCameraBlur = false; 255 256 // DoF when doing ADS 257 float aimDownState = GetAimDownState(); 258 float per = aimDownState; 259 per *= per * per; 260 def.depthOfFieldFocalLength = per * 13.f + .054f; 261 262 // Hurt effect 263 { 264 float wTime = world->GetTime(); 265 if (wTime < lastHurtTime + .15f && wTime >= lastHurtTime) { 266 float per = 1.f - (wTime - lastHurtTime) / .15f; 267 per *= .5f - player.GetHealth() / 100.f * .3f; 268 def.blurVignette += per * 6.f; 269 } 270 if (wTime < lastHurtTime + .2f && wTime >= lastHurtTime) { 271 float per = 1.f - (wTime - lastHurtTime) / .2f; 272 per *= .5f - player.GetHealth() / 100.f * .3f; 273 def.saturation *= std::max(0.f, 1.f - per * 4.f); 274 } 275 } 276 277 // Apply ADS zoom 278 scale /= GetAimDownZoomScale(); 279 280 // Update initial floating camera pos 281 freeCameraState.position = def.viewOrigin; 282 freeCameraState.velocity = MakeVector3(0, 0, 0); 283 break; 284 } 285 286 case ClientCameraMode::ThirdPersonLocal: 287 case ClientCameraMode::ThirdPersonFollow: { 288 Player &player = GetCameraTargetPlayer(); 289 Vector3 center = player.GetEye(); 290 291 if (!player.IsAlive() && lastMyCorpse && 292 &player == world->GetLocalPlayer()) { 293 center = lastMyCorpse->GetCenter(); 294 } 295 if (map->IsSolidWrapped((int)floorf(center.x), (int)floorf(center.y), 296 (int)floorf(center.z))) { 297 float z = center.z; 298 while (z > center.z - 5.f) { 299 if (!map->IsSolidWrapped((int)floorf(center.x), 300 (int)floorf(center.y), (int)floorf(z))) { 301 center.z = z; 302 break; 303 } else { 304 z -= 1.f; 305 } 306 } 307 } 308 309 float distance = 5.f; 310 if (&player == world->GetLocalPlayer() && 311 world->GetLocalPlayer()->GetTeamId() < 2 && 312 !world->GetLocalPlayer()->IsAlive()) { 313 // deathcam. 314 float elapsedTime = time - lastAliveTime; 315 distance -= 3.f * expf(-elapsedTime * 1.f); 316 } 317 318 auto &state = followAndFreeCameraState; 319 Vector3 eye = center; 320 eye.x += cosf(state.yaw) * cosf(state.pitch) * distance; 321 eye.y += sinf(state.yaw) * cosf(state.pitch) * distance; 322 eye.z -= sinf(state.pitch) * distance; 323 324 // Prevent the camera from being behind a wall 325 GameMap::RayCastResult result; 326 result = map->CastRay2(center, (eye - center).Normalize(), 256); 327 if (result.hit) { 328 float dist = (result.hitPos - center).GetLength(); 329 float curDist = (eye - center).GetLength(); 330 dist -= 0.3f; // near clip plane 331 if (curDist > dist) { 332 float diff = curDist - dist; 333 eye += (center - eye).Normalize() * diff; 334 } 335 } 336 337 Vector3 front = center - eye; 338 front = front.Normalize(); 339 340 Vector3 up = MakeVector3(0, 0, -1); 341 342 def.viewOrigin = eye; 343 def.viewAxis[0] = -Vector3::Cross(up, front).Normalize(); 344 def.viewAxis[1] = -Vector3::Cross(front, def.viewAxis[0]).Normalize(); 345 def.viewAxis[2] = front; 346 347 def.fovY = (float)cg_fov * static_cast<float>(M_PI) / 180.f; 348 def.fovX = atanf(tanf(def.fovY * .5f) * renderer->ScreenWidth() / 349 renderer->ScreenHeight()) * 2.f; 350 351 // Update initial floating camera pos 352 freeCameraState.position = def.viewOrigin; 353 freeCameraState.velocity = MakeVector3(0, 0, 0); 354 break; 355 } 356 case ClientCameraMode::Free: { 357 // spectator view (noclip view) 358 Vector3 center = freeCameraState.position; 359 Vector3 front; 360 Vector3 up = {0, 0, -1}; 361 362 auto &state = followAndFreeCameraState; 363 front.x = -cosf(state.yaw) * cosf(state.pitch); 364 front.y = -sinf(state.yaw) * cosf(state.pitch); 365 front.z = sinf(state.pitch); 366 367 def.viewOrigin = center; 368 def.viewAxis[0] = -Vector3::Cross(up, front).Normalize(); 369 def.viewAxis[1] = -Vector3::Cross(front, def.viewAxis[0]).Normalize(); 370 def.viewAxis[2] = front; 371 372 def.fovY = (float)cg_fov * static_cast<float>(M_PI) / 180.f; 373 def.fovX = atanf(tanf(def.fovY * .5f) * renderer->ScreenWidth() / 374 renderer->ScreenHeight()) * 2.f; 375 376 // for 1st view, camera blur can be used 377 def.denyCameraBlur = false; 378 break; 379 } 380 } 381 382 // Add vibration effects 383 { 384 float grenVib = grenadeVibration; 385 if (grenVib > 0.f) { 386 if (shakeLevel >= 1) { 387 grenVib *= 10.f; 388 if (grenVib > 1.f) 389 grenVib = 1.f; 390 roll += (SampleRandomFloat() - SampleRandomFloat()) * 0.2f * grenVib; 391 vibPitch += (SampleRandomFloat() - SampleRandomFloat()) * 0.1f * grenVib; 392 vibYaw += (SampleRandomFloat() - SampleRandomFloat()) * 0.1f * grenVib; 393 scale -= (SampleRandomFloat() - SampleRandomFloat()) * 0.1f * grenVib; 394 395 def.radialBlur += grenVib * 0.1f; 396 } 397 } 398 } 399 { 400 float grenVib = grenadeVibrationSlow; 401 if (grenVib > 0.f) { 402 if (shakeLevel >= 2) { 403 grenVib *= 4.f; 404 if (grenVib > 1.f) 405 grenVib = 1.f; 406 grenVib *= grenVib; 407 408 roll += coherentNoiseSamplers[0].Sample(time * 8.f) * 0.2f * grenVib; 409 vibPitch += 410 coherentNoiseSamplers[1].Sample(time * 12.f) * 0.1f * grenVib; 411 vibYaw += coherentNoiseSamplers[2].Sample(time * 11.f) * 0.1f * grenVib; 412 } 413 } 414 } 415 416 // Add roll / scale 417 { 418 Vector3 right = def.viewAxis[0]; 419 Vector3 up = def.viewAxis[1]; 420 421 def.viewAxis[0] = right * cosf(roll) - up * sinf(roll); 422 def.viewAxis[1] = up * cosf(roll) + right * sinf(roll); 423 424 def.fovX = atanf(tanf(def.fovX * .5f) * scale) * 2.f; 425 def.fovY = atanf(tanf(def.fovY * .5f) * scale) * 2.f; 426 } 427 { 428 Vector3 u = def.viewAxis[1]; 429 Vector3 v = def.viewAxis[2]; 430 431 def.viewAxis[1] = u * cosf(vibPitch) - v * sinf(vibPitch); 432 def.viewAxis[2] = v * cosf(vibPitch) + u * sinf(vibPitch); 433 } 434 { 435 Vector3 u = def.viewAxis[0]; 436 Vector3 v = def.viewAxis[2]; 437 438 def.viewAxis[0] = u * cosf(vibYaw) - v * sinf(vibYaw); 439 def.viewAxis[2] = v * cosf(vibYaw) + u * sinf(vibYaw); 440 } 441 442 if (def.viewOrigin.z < 0.f) { 443 // Need to move the far plane because there's no vertical fog 444 def.zFar -= def.viewOrigin.z; 445 } 446 447 if ((int)cg_manualFocus) { 448 // Depth of field is manually controlled 449 def.depthOfFieldNearBlurStrength = def.depthOfFieldFarBlurStrength = 450 0.5f * (float)cg_depthOfFieldAmount; 451 def.depthOfFieldFocalLength = focalLength; 452 } else { 453 def.depthOfFieldNearBlurStrength = cg_depthOfFieldAmount; 454 def.depthOfFieldFarBlurStrength = 0.f; 455 } 456 457 def.zNear = 0.05f; 458 459 def.skipWorld = false; 460 } else { 461 SPAssert(GetCameraMode() == ClientCameraMode::None); 462 463 // Let there be darkness 464 def.viewOrigin = MakeVector3(0, 0, 0); 465 def.viewAxis[0] = MakeVector3(1, 0, 0); 466 def.viewAxis[1] = MakeVector3(0, 0, -1); 467 def.viewAxis[2] = MakeVector3(0, 0, 1); 468 469 def.fovY = (float)cg_fov * static_cast<float>(M_PI) / 180.f; 470 def.fovX = 471 atanf(tanf(def.fovY * .5f) * renderer->ScreenWidth() / renderer->ScreenHeight()) * 472 2.f; 473 474 def.zNear = 0.05f; 475 476 def.skipWorld = true; 477 478 renderer->SetFogColor(MakeVector3(0, 0, 0)); 479 } 480 481 SPAssert(!std::isnan(def.viewOrigin.x)); 482 SPAssert(!std::isnan(def.viewOrigin.y)); 483 SPAssert(!std::isnan(def.viewOrigin.z)); 484 485 def.radialBlur = std::min(def.radialBlur, 1.f); 486 487 return def; 488 } 489 AddGrenadeToScene(spades::client::Grenade * g)490 void Client::AddGrenadeToScene(spades::client::Grenade *g) { 491 SPADES_MARK_FUNCTION(); 492 493 IModel *model; 494 model = renderer->RegisterModel("Models/Weapons/Grenade/Grenade.kv6"); 495 496 if (g->GetPosition().z > 63.f) { 497 // work-around for water refraction problem 498 return; 499 } 500 501 // Move the grenade slightly so that it doesn't look like sinking in 502 // the ground 503 Vector3 position = g->GetPosition(); 504 position.z -= 0.03f * 3.0f; 505 506 ModelRenderParam param; 507 Matrix4 mat = g->GetOrientation().ToRotationMatrix() * Matrix4::Scale(0.03f); 508 mat = Matrix4::Translate(position) * mat; 509 param.matrix = mat; 510 511 renderer->RenderModel(model, param); 512 } 513 AddDebugObjectToScene(const spades::OBB3 & obb,const Vector4 & color)514 void Client::AddDebugObjectToScene(const spades::OBB3 &obb, const Vector4 &color) { 515 const Matrix4 &mat = obb.m; 516 Vector3 v[2][2][2]; 517 v[0][0][0] = (mat * MakeVector3(0, 0, 0)).GetXYZ(); 518 v[0][0][1] = (mat * MakeVector3(0, 0, 1)).GetXYZ(); 519 v[0][1][0] = (mat * MakeVector3(0, 1, 0)).GetXYZ(); 520 v[0][1][1] = (mat * MakeVector3(0, 1, 1)).GetXYZ(); 521 v[1][0][0] = (mat * MakeVector3(1, 0, 0)).GetXYZ(); 522 v[1][0][1] = (mat * MakeVector3(1, 0, 1)).GetXYZ(); 523 v[1][1][0] = (mat * MakeVector3(1, 1, 0)).GetXYZ(); 524 v[1][1][1] = (mat * MakeVector3(1, 1, 1)).GetXYZ(); 525 526 renderer->AddDebugLine(v[0][0][0], v[1][0][0], color); 527 renderer->AddDebugLine(v[0][0][1], v[1][0][1], color); 528 renderer->AddDebugLine(v[0][1][0], v[1][1][0], color); 529 renderer->AddDebugLine(v[0][1][1], v[1][1][1], color); 530 531 renderer->AddDebugLine(v[0][0][0], v[0][1][0], color); 532 renderer->AddDebugLine(v[0][0][1], v[0][1][1], color); 533 renderer->AddDebugLine(v[1][0][0], v[1][1][0], color); 534 renderer->AddDebugLine(v[1][0][1], v[1][1][1], color); 535 536 renderer->AddDebugLine(v[0][0][0], v[0][0][1], color); 537 renderer->AddDebugLine(v[0][1][0], v[0][1][1], color); 538 renderer->AddDebugLine(v[1][0][0], v[1][0][1], color); 539 renderer->AddDebugLine(v[1][1][0], v[1][1][1], color); 540 } 541 DrawCTFObjects()542 void Client::DrawCTFObjects() { 543 SPADES_MARK_FUNCTION(); 544 CTFGameMode *mode = static_cast<CTFGameMode *>(world->GetMode()); 545 int tId; 546 IModel *base = renderer->RegisterModel("Models/MapObjects/CheckPoint.kv6"); 547 IModel *intel = renderer->RegisterModel("Models/MapObjects/Intel.kv6"); 548 for (tId = 0; tId < 2; tId++) { 549 CTFGameMode::Team &team = mode->GetTeam(tId); 550 IntVector3 col = world->GetTeam(tId).color; 551 Vector3 color = {col.x / 255.f, col.y / 255.f, col.z / 255.f}; 552 553 ModelRenderParam param; 554 param.customColor = color; 555 556 // draw base 557 param.matrix = Matrix4::Translate(team.basePos); 558 param.matrix = param.matrix * Matrix4::Scale(.3f); 559 renderer->RenderModel(base, param); 560 561 // draw flag 562 if (!mode->GetTeam(1 - tId).hasIntel) { 563 param.matrix = Matrix4::Translate(team.flagPos); 564 param.matrix = param.matrix * Matrix4::Scale(.1f); 565 renderer->RenderModel(intel, param); 566 } 567 } 568 } 569 DrawTCObjects()570 void Client::DrawTCObjects() { 571 SPADES_MARK_FUNCTION(); 572 TCGameMode *mode = static_cast<TCGameMode *>(world->GetMode()); 573 int tId; 574 IModel *base = renderer->RegisterModel("Models/MapObjects/CheckPoint.kv6"); 575 int cnt = mode->GetNumTerritories(); 576 for (tId = 0; tId < cnt; tId++) { 577 TCGameMode::Territory *t = mode->GetTerritory(tId); 578 IntVector3 col; 579 if (t->ownerTeamId == 2) { 580 col = IntVector3::Make(255, 255, 255); 581 } else { 582 col = world->GetTeam(t->ownerTeamId).color; 583 } 584 Vector3 color = {col.x / 255.f, col.y / 255.f, col.z / 255.f}; 585 586 ModelRenderParam param; 587 param.customColor = color; 588 589 // draw base 590 param.matrix = Matrix4::Translate(t->pos); 591 param.matrix = param.matrix * Matrix4::Scale(.3f); 592 renderer->RenderModel(base, param); 593 } 594 } 595 DrawScene()596 void Client::DrawScene() { 597 SPADES_MARK_FUNCTION(); 598 599 renderer->StartScene(lastSceneDef); 600 601 if (world) { 602 Player *p = world->GetLocalPlayer(); 603 604 for (size_t i = 0; i < world->GetNumPlayerSlots(); i++) 605 if (world->GetPlayer(static_cast<unsigned int>(i))) { 606 SPAssert(clientPlayers[i]); 607 clientPlayers[i]->AddToScene(); 608 } 609 std::vector<Grenade *> nades = world->GetAllGrenades(); 610 for (size_t i = 0; i < nades.size(); i++) { 611 AddGrenadeToScene(nades[i]); 612 } 613 614 { 615 for (auto &c : corpses) { 616 Vector3 center = c->GetCenter(); 617 if ((center - lastSceneDef.viewOrigin).GetPoweredLength() > 150.f * 150.f) 618 continue; 619 c->AddToScene(); 620 } 621 } 622 623 if (IGameMode::m_CTF == world->GetMode()->ModeType()) { 624 DrawCTFObjects(); 625 } else if (IGameMode::m_TC == world->GetMode()->ModeType()) { 626 DrawTCObjects(); 627 } 628 629 { 630 for (auto &ent : localEntities) { 631 ent->Render3D(); 632 } 633 } 634 635 // Draw block cursor 636 // FIXME: don't use debug line 637 if (p) { 638 if (p->IsReadyToUseTool() && p->GetTool() == Player::ToolBlock && 639 p->IsAlive()) { 640 std::vector<IntVector3> blocks; 641 if (p->IsBlockCursorDragging()) { 642 blocks = world->CubeLine(p->GetBlockCursorDragPos(), 643 p->GetBlockCursorPos(), 256); 644 } else { 645 blocks.push_back(p->GetBlockCursorPos()); 646 } 647 648 bool active = p->IsBlockCursorActive() && CanLocalPlayerUseToolNow(); 649 650 Vector4 color = {1, 1, 1, 1}; 651 if (!active) 652 color = Vector4(1, 1, 0, 1); 653 if ((int)blocks.size() > p->GetNumBlocks()) 654 color = MakeVector4(1, 0, 0, 1); 655 if (!active) 656 color.w *= 0.5f; 657 658 for (size_t i = 0; i < blocks.size(); i++) { 659 IntVector3 &v = blocks[i]; 660 661 if (active) { 662 663 renderer->AddDebugLine(MakeVector3(v.x, v.y, v.z), 664 MakeVector3(v.x + 1, v.y, v.z), color); 665 renderer->AddDebugLine(MakeVector3(v.x, v.y + 1, v.z), 666 MakeVector3(v.x + 1, v.y + 1, v.z), color); 667 renderer->AddDebugLine(MakeVector3(v.x, v.y, v.z + 1), 668 MakeVector3(v.x + 1, v.y, v.z + 1), color); 669 renderer->AddDebugLine(MakeVector3(v.x, v.y + 1, v.z + 1), 670 MakeVector3(v.x + 1, v.y + 1, v.z + 1), 671 color); 672 renderer->AddDebugLine(MakeVector3(v.x, v.y, v.z), 673 MakeVector3(v.x + 1, v.y, v.z), color); 674 675 renderer->AddDebugLine(MakeVector3(v.x, v.y, v.z), 676 MakeVector3(v.x, v.y + 1, v.z), color); 677 renderer->AddDebugLine(MakeVector3(v.x, v.y, v.z + 1), 678 MakeVector3(v.x, v.y + 1, v.z + 1), color); 679 renderer->AddDebugLine(MakeVector3(v.x + 1, v.y, v.z), 680 MakeVector3(v.x + 1, v.y + 1, v.z), color); 681 renderer->AddDebugLine(MakeVector3(v.x + 1, v.y, v.z + 1), 682 MakeVector3(v.x + 1, v.y + 1, v.z + 1), 683 color); 684 685 renderer->AddDebugLine(MakeVector3(v.x, v.y, v.z), 686 MakeVector3(v.x, v.y, v.z + 1), color); 687 renderer->AddDebugLine(MakeVector3(v.x, v.y + 1, v.z), 688 MakeVector3(v.x, v.y + 1, v.z + 1), color); 689 renderer->AddDebugLine(MakeVector3(v.x + 1, v.y, v.z), 690 MakeVector3(v.x + 1, v.y, v.z + 1), color); 691 renderer->AddDebugLine(MakeVector3(v.x + 1, v.y + 1, v.z), 692 MakeVector3(v.x + 1, v.y + 1, v.z + 1), 693 color); 694 } else { 695 // not active 696 697 const float ln = 0.1f; 698 { 699 float xx = v.x, yy = v.y, zz = v.z; 700 renderer->AddDebugLine(Vector3(xx, yy, zz), 701 Vector3(xx + ln, yy, zz), color); 702 renderer->AddDebugLine(Vector3(xx, yy, zz), 703 Vector3(xx, yy + ln, zz), color); 704 renderer->AddDebugLine(Vector3(xx, yy, zz), 705 Vector3(xx, yy, zz + ln), color); 706 } 707 { 708 float xx = v.x + 1, yy = v.y, zz = v.z; 709 renderer->AddDebugLine(Vector3(xx, yy, zz), 710 Vector3(xx - ln, yy, zz), color); 711 renderer->AddDebugLine(Vector3(xx, yy, zz), 712 Vector3(xx, yy + ln, zz), color); 713 renderer->AddDebugLine(Vector3(xx, yy, zz), 714 Vector3(xx, yy, zz + ln), color); 715 } 716 { 717 float xx = v.x, yy = v.y + 1, zz = v.z; 718 renderer->AddDebugLine(Vector3(xx, yy, zz), 719 Vector3(xx + ln, yy, zz), color); 720 renderer->AddDebugLine(Vector3(xx, yy, zz), 721 Vector3(xx, yy - ln, zz), color); 722 renderer->AddDebugLine(Vector3(xx, yy, zz), 723 Vector3(xx, yy, zz + ln), color); 724 } 725 { 726 float xx = v.x + 1, yy = v.y + 1, zz = v.z; 727 renderer->AddDebugLine(Vector3(xx, yy, zz), 728 Vector3(xx - ln, yy, zz), color); 729 renderer->AddDebugLine(Vector3(xx, yy, zz), 730 Vector3(xx, yy - ln, zz), color); 731 renderer->AddDebugLine(Vector3(xx, yy, zz), 732 Vector3(xx, yy, zz + ln), color); 733 } 734 { 735 float xx = v.x, yy = v.y, zz = v.z + 1; 736 renderer->AddDebugLine(Vector3(xx, yy, zz), 737 Vector3(xx + ln, yy, zz), color); 738 renderer->AddDebugLine(Vector3(xx, yy, zz), 739 Vector3(xx, yy + ln, zz), color); 740 renderer->AddDebugLine(Vector3(xx, yy, zz), 741 Vector3(xx, yy, zz - ln), color); 742 } 743 { 744 float xx = v.x + 1, yy = v.y, zz = v.z + 1; 745 renderer->AddDebugLine(Vector3(xx, yy, zz), 746 Vector3(xx - ln, yy, zz), color); 747 renderer->AddDebugLine(Vector3(xx, yy, zz), 748 Vector3(xx, yy + ln, zz), color); 749 renderer->AddDebugLine(Vector3(xx, yy, zz), 750 Vector3(xx, yy, zz - ln), color); 751 } 752 { 753 float xx = v.x, yy = v.y + 1, zz = v.z + 1; 754 renderer->AddDebugLine(Vector3(xx, yy, zz), 755 Vector3(xx + ln, yy, zz), color); 756 renderer->AddDebugLine(Vector3(xx, yy, zz), 757 Vector3(xx, yy - ln, zz), color); 758 renderer->AddDebugLine(Vector3(xx, yy, zz), 759 Vector3(xx, yy, zz - ln), color); 760 } 761 { 762 float xx = v.x + 1, yy = v.y + 1, zz = v.z + 1; 763 renderer->AddDebugLine(Vector3(xx, yy, zz), 764 Vector3(xx - ln, yy, zz), color); 765 renderer->AddDebugLine(Vector3(xx, yy, zz), 766 Vector3(xx, yy - ln, zz), color); 767 renderer->AddDebugLine(Vector3(xx, yy, zz), 768 Vector3(xx, yy, zz - ln), color); 769 } 770 } 771 // --- one block drawn 772 } // end for 773 774 } // p->IsReadyToUseTool 775 } 776 } 777 778 for (size_t i = 0; i < flashDlights.size(); i++) 779 renderer->AddLight(flashDlights[i]); 780 flashDlightsOld.clear(); 781 flashDlightsOld.swap(flashDlights); 782 783 // draw player hottrack 784 // FIXME: don't use debug line 785 { 786 hitTag_t tag = hit_None; 787 Player *hottracked = HotTrackedPlayer(&tag); 788 if (hottracked) { 789 IntVector3 col = world->GetTeam(hottracked->GetTeamId()).color; 790 Vector4 color = Vector4::Make(col.x / 255.f, col.y / 255.f, col.z / 255.f, 1.f); 791 Vector4 color2 = Vector4::Make(1, 1, 1, 1); 792 793 Player::HitBoxes hb = hottracked->GetHitBoxes(); 794 AddDebugObjectToScene(hb.head, (tag & hit_Head) ? color2 : color); 795 AddDebugObjectToScene(hb.torso, (tag & hit_Torso) ? color2 : color); 796 AddDebugObjectToScene(hb.limbs[0], (tag & hit_Legs) ? color2 : color); 797 AddDebugObjectToScene(hb.limbs[1], (tag & hit_Legs) ? color2 : color); 798 AddDebugObjectToScene(hb.limbs[2], (tag & hit_Arms) ? color2 : color); 799 } 800 } 801 802 renderer->EndScene(); 803 } 804 Project(spades::Vector3 v)805 Vector3 Client::Project(spades::Vector3 v) { 806 v -= lastSceneDef.viewOrigin; 807 808 // transform to NDC 809 Vector3 v2; 810 v2.x = Vector3::Dot(v, lastSceneDef.viewAxis[0]); 811 v2.y = Vector3::Dot(v, lastSceneDef.viewAxis[1]); 812 v2.z = Vector3::Dot(v, lastSceneDef.viewAxis[2]); 813 814 float tanX = tanf(lastSceneDef.fovX * .5f); 815 float tanY = tanf(lastSceneDef.fovY * .5f); 816 817 v2.x /= tanX * v2.z; 818 v2.y /= tanY * v2.z; 819 820 // transform to IRenderer 2D coord 821 v2.x = (v2.x + 1.f) / 2.f * renderer->ScreenWidth(); 822 v2.y = (-v2.y + 1.f) / 2.f * renderer->ScreenHeight(); 823 824 return v2; 825 } 826 } 827 } 828