guiCrossHairHud.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "gui/core/guiControl.h"
  24. #include "gui/controls/guiBitmapCtrl.h"
  25. #include "console/consoleTypes.h"
  26. #include "scene/sceneManager.h"
  27. #include "T3D/gameBase/gameConnection.h"
  28. #include "T3D/shapeBase.h"
  29. #include "gfx/gfxDrawUtil.h"
  30. #include "console/engineAPI.h"
  31. #include "gui/core/guiOffscreenCanvas.h"
  32. #include "T3D/tsStatic.h"
  33. #include "materials/baseMatInstance.h"
  34. #include "materials/matInstance.h"
  35. #include "materials/materialDefinition.h"
  36. //-----------------------------------------------------------------------------
  37. /// Vary basic cross hair hud.
  38. /// Uses the base bitmap control to render a bitmap, and decides whether
  39. /// to draw or not depending on the current control object and it's state.
  40. /// If there is ShapeBase object under the cross hair and it's named,
  41. /// then a small health bar is displayed.
  42. class GuiCrossHairHud : public GuiBitmapCtrl
  43. {
  44. typedef GuiBitmapCtrl Parent;
  45. LinearColorF mDamageFillColor;
  46. LinearColorF mDamageFrameColor;
  47. Point2I mDamageRectSize;
  48. Point2I mDamageOffset;
  49. PlatformTimer* mFrameTime;
  50. protected:
  51. void drawDamage(Point2I offset, F32 damage, F32 opacity);
  52. public:
  53. GuiCrossHairHud();
  54. ~GuiCrossHairHud();
  55. void onRender( Point2I, const RectI &) override;
  56. static void initPersistFields();
  57. DECLARE_CONOBJECT( GuiCrossHairHud );
  58. DECLARE_CATEGORY( "Gui Game" );
  59. DECLARE_DESCRIPTION( "Basic cross hair hud. Reacts to state of control object.\n"
  60. "Also displays health bar for named objects under the cross hair." );
  61. };
  62. /// Valid object types for which the cross hair will render, this
  63. /// should really all be script controlled.
  64. static const U32 ObjectMask = PlayerObjectType | VehicleObjectType;
  65. //-----------------------------------------------------------------------------
  66. IMPLEMENT_CONOBJECT( GuiCrossHairHud );
  67. ConsoleDocClass( GuiCrossHairHud,
  68. "@brief Basic cross hair hud. Reacts to state of control object. Also displays health bar for named objects under the cross hair.\n\n"
  69. "Uses the base bitmap control to render a bitmap, and decides whether to draw or not depending "
  70. "on the current control object and it's state. If there is ShapeBase object under the cross hair "
  71. "and it's named, then a small health bar is displayed.\n\n"
  72. "@tsexample\n"
  73. "\n new GuiCrossHairHud()"
  74. "{\n"
  75. " damageFillColor = \"1.0 0.0 0.0 1.0\"; // Fills with a solid red color\n"
  76. " damageFrameColor = \"1.0 1.0 1.0 1.0\"; // Solid white frame color\n"
  77. " damageRect = \"15 5\";\n"
  78. " damageOffset = \"0 -10\";\n"
  79. "};\n"
  80. "@endtsexample\n"
  81. "@ingroup GuiGame\n"
  82. );
  83. GuiCrossHairHud::GuiCrossHairHud()
  84. {
  85. mDamageFillColor.set( 0.0f, 1.0f, 0.0f, 1.0f );
  86. mDamageFrameColor.set( 1.0f, 0.6f, 0.0f, 1.0f );
  87. mDamageRectSize.set(50, 4);
  88. mDamageOffset.set(0,32);
  89. mFrameTime = PlatformTimer::create();
  90. }
  91. GuiCrossHairHud::~GuiCrossHairHud()
  92. {
  93. SAFE_DELETE(mFrameTime);
  94. }
  95. void GuiCrossHairHud::initPersistFields()
  96. {
  97. docsURL;
  98. addGroup("Damage");
  99. addField( "damageFillColor", TypeColorF, Offset( mDamageFillColor, GuiCrossHairHud ), "As the health bar depletes, this color will represent the health loss amount." );
  100. addField( "damageFrameColor", TypeColorF, Offset( mDamageFrameColor, GuiCrossHairHud ), "Color for the health bar's frame." );
  101. addField( "damageRect", TypePoint2I, Offset( mDamageRectSize, GuiCrossHairHud ), "Size for the health bar portion of the control." );
  102. addField( "damageOffset", TypePoint2I, Offset( mDamageOffset, GuiCrossHairHud ), "Offset for drawing the damage portion of the health control." );
  103. endGroup("Damage");
  104. Parent::initPersistFields();
  105. }
  106. //-----------------------------------------------------------------------------
  107. void GuiCrossHairHud::onRender(Point2I offset, const RectI &updateRect)
  108. {
  109. // Must have a connection and player control object
  110. GameConnection* conn = GameConnection::getConnectionToServer();
  111. if (!conn)
  112. return;
  113. GameBase* control = dynamic_cast<GameBase*>(conn->getCameraObject());
  114. if (!control || !(control->getTypeMask() & ObjectMask) || !conn->isFirstPerson())
  115. return;
  116. // Parent render.
  117. Parent::onRender(offset,updateRect);
  118. // Get control camera info
  119. MatrixF cam;
  120. Point3F camPos;
  121. conn->getControlCameraTransform(0,&cam);
  122. cam.getColumn(3, &camPos);
  123. // Extend the camera vector to create an endpoint for our ray
  124. Point3F endPos;
  125. cam.getColumn(1, &endPos);
  126. endPos *= gClientSceneGraph->getVisibleDistance();
  127. endPos += camPos;
  128. // Collision info. We're going to be running LOS tests and we
  129. // don't want to collide with the control object.
  130. static U32 losMask = TerrainObjectType | ShapeBaseObjectType | StaticShapeObjectType;
  131. control->disableCollision();
  132. RayInfo info;
  133. if (gClientContainer.castRay(camPos, endPos, losMask, &info)) {
  134. // is this a tsstatic? then it could be a offscreen canvas, check the list.
  135. if (TSStatic* ts = dynamic_cast<TSStatic*>(info.object))
  136. {
  137. if (mFrameTime->getElapsedMs() > 32)
  138. {
  139. if (GuiOffscreenCanvas::sActiveOffscreenCanvas)
  140. GuiOffscreenCanvas::sActiveOffscreenCanvas->setActive(false);
  141. GuiOffscreenCanvas::sActiveOffscreenCanvas = NULL;
  142. mFrameTime->reset();
  143. Point3F newStart, newEnd;
  144. ts->getWorldTransform().mulP(camPos, &newStart);
  145. ts->getWorldTransform().mulP(endPos, &newEnd);
  146. newStart.convolveInverse(ts->getScale());
  147. newEnd.convolveInverse(ts->getScale());
  148. info.generateTexCoord = true;
  149. if (ts->getShapeInstance()->castRayOpcode(0, newStart, newEnd, &info))
  150. {
  151. MatInstance* matInst = dynamic_cast<MatInstance*>(info.material);
  152. if (matInst)
  153. {
  154. Material* mat = matInst->getMaterial();
  155. if (mat && mat->getDiffuseMapAsset(0).notNull() && mat->getDiffuseMapAsset(0)->isNamedTarget())
  156. {
  157. String canvasName = String(mat->getDiffuseMapAsset(0)->getImageFile()).substr(1, (U32)strlen(mat->getDiffuseMapAsset(0)->getImageFile()) - 1);
  158. for (GuiOffscreenCanvas* canvas : GuiOffscreenCanvas::sList)
  159. {
  160. if (canvas->getTarget()->getName() == canvasName)
  161. {
  162. if (!canvas->canInteract() || canvas->getMaxInteractDistance() < info.distance)
  163. {
  164. break;
  165. }
  166. Point2I canvasSize = canvas->getWindowSize();
  167. Point2I newCursorPos(mRound(mClampF((info.texCoord.x * canvasSize.x), 0.0f, (F32)canvasSize.x)),
  168. mRound(mClampF((info.texCoord.y * canvasSize.y), 0.0f, (F32)canvasSize.y)));
  169. canvas->setCursorPos(newCursorPos);
  170. canvas->markDirty();
  171. GuiOffscreenCanvas::sActiveOffscreenCanvas = canvas;
  172. GuiOffscreenCanvas::sActiveOffscreenCanvas->setActive(true);
  173. break;
  174. }
  175. }
  176. }
  177. }
  178. }
  179. }
  180. }
  181. // Hit something... but we'll only display health for named
  182. // ShapeBase objects. Could mask against the object type here
  183. // and do a static cast if it's a ShapeBaseObjectType, but this
  184. // isn't a performance situation, so I'll just use dynamic_cast.
  185. if (ShapeBase* obj = dynamic_cast<ShapeBase*>(info.object))
  186. if (obj->getShapeName()) {
  187. offset.x = updateRect.point.x + updateRect.extent.x / 2;
  188. offset.y = updateRect.point.y + updateRect.extent.y / 2;
  189. drawDamage(offset + mDamageOffset, obj->getDamageValue(), 1);
  190. }
  191. }
  192. // Restore control object collision
  193. control->enableCollision();
  194. }
  195. //-----------------------------------------------------------------------------
  196. /**
  197. Display a damage bar ubove the shape.
  198. This is a support funtion, called by onRender.
  199. */
  200. void GuiCrossHairHud::drawDamage(Point2I offset, F32 damage, F32 opacity)
  201. {
  202. mDamageFillColor.alpha = mDamageFrameColor.alpha = opacity;
  203. // Damage should be 0->1 (0 being no damage,or healthy), but
  204. // we'll just make sure here as we flip it.
  205. damage = mClampF(1 - damage, 0, 1);
  206. // Center the bar
  207. RectI rect(offset, mDamageRectSize);
  208. rect.point.x -= mDamageRectSize.x / 2;
  209. // Draw the border
  210. GFX->getDrawUtil()->drawRect(rect, mDamageFrameColor.toColorI());
  211. // Draw the damage % fill
  212. rect.point += Point2I(1, 1);
  213. rect.extent -= Point2I(1, 1);
  214. rect.extent.x = (S32)(rect.extent.x * damage);
  215. if (rect.extent.x == 1)
  216. rect.extent.x = 2;
  217. if (rect.extent.x > 0)
  218. GFX->getDrawUtil()->drawRectFill(rect, mDamageFillColor.toColorI());
  219. }