/* Copyright (C) 2023 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #ifndef INCLUDED_MODELABSTRACT #define INCLUDED_MODELABSTRACT #include "graphics/Color.h" #include "graphics/RenderableObject.h" #include "maths/BoundingBoxOriented.h" #include "simulation2/helpers/Player.h" #include class CModelDummy; class CModel; class CModelDecal; class CModelParticleEmitter; namespace ModelFlag { static constexpr uint32_t CAST_SHADOWS{1 << 0}; static constexpr uint32_t NO_LOOP_ANIMATION{1 << 1}; static constexpr uint32_t SILHOUETTE_DISPLAY{1 << 2}; static constexpr uint32_t SILHOUETTE_OCCLUDER{1 << 3}; static constexpr uint32_t IGNORE_LOS{1 << 4}; static constexpr uint32_t FLOAT_ON_WATER{1 << 5}; } // namespace ModelFlag /** * Abstract base class for graphical objects that are used by units, * or as props attached to other CModelAbstract objects. * This includes meshes, terrain decals, and sprites. * These objects exist in a tree hierarchy. */ class CModelAbstract : public CRenderableObject { NONCOPYABLE(CModelAbstract); public: /** * Describes a custom selection shape to be used for a model's selection box instead of the default * recursive bounding boxes. */ struct CustomSelectionShape { enum EType { /// The selection shape is determined by an oriented box of custom, user-specified size. BOX, /// The selection shape is determined by a cylinder of custom, user-specified size. CYLINDER }; EType m_Type; ///< Type of shape. float m_Size0; ///< Box width if @ref BOX, or radius if @ref CYLINDER float m_Size1; ///< Box depth if @ref BOX, or radius if @ref CYLINDER float m_Height; ///< Box height if @ref BOX, cylinder height if @ref CYLINDER }; public: CModelAbstract() : m_Parent(NULL), m_PositionValid(false), m_ShadingColor(1, 1, 1, 1), m_PlayerID(INVALID_PLAYER), m_SelectionBoxValid(false), m_CustomSelectionShape(NULL) { } virtual ~CModelAbstract() { delete m_CustomSelectionShape; // allocated and set externally by CCmpVisualActor, but our responsibility to clean up } virtual std::unique_ptr Clone() const = 0; /// Dynamic cast virtual CModelDummy* ToCModelDummy() { return nullptr; } /// Dynamic cast virtual CModel* ToCModel() { return nullptr; } /// Dynamic cast virtual CModelDecal* ToCModelDecal() { return nullptr; } /// Dynamic cast virtual CModelParticleEmitter* ToCModelParticleEmitter() { return nullptr; } // (This dynamic casting is a bit ugly, but we won't have many subclasses // and this seems the easiest way to integrate with other code that wants // type-specific processing) /// Returns world space bounds of this object and all child objects. virtual const CBoundingBoxAligned GetWorldBoundsRec() { return GetWorldBounds(); } // default implementation /** * Returns the world-space selection box of this model. Used primarily for hittesting against against a selection ray. The * returned selection box may be empty to indicate that it does not wish to participate in the selection process. */ virtual const CBoundingBoxOriented& GetSelectionBox(); virtual void InvalidateBounds() { m_BoundsValid = false; // a call to this method usually means that the model's transform has changed, i.e. it has moved or rotated, so we'll also // want to update the selection box accordingly regardless of the shape it is built from. m_SelectionBoxValid = false; } /// Sets a custom selection shape as described by a @p descriptor. Argument may be NULL /// if you wish to keep the default behaviour of using the recursively-calculated bounding boxes. void SetCustomSelectionShape(CustomSelectionShape* descriptor) { if (m_CustomSelectionShape != descriptor) { m_CustomSelectionShape = descriptor; m_SelectionBoxValid = false; // update the selection box when it is next requested } } /** * Returns the (object-space) bounds that should be used to construct a selection box for this model and its children. * May return an empty bound to indicate that this model and its children should not be selectable themselves, or should * not be included in its parent model's selection box. This method is used for constructing the default selection boxes, * as opposed to any boxes of custom shape specified by @ref m_CustomSelectionShape. * * If you wish your model type to be included in selection boxes, override this method and have it return the object-space * bounds of itself, augmented recursively (via this method) with the object-space selection bounds of its children. */ virtual const CBoundingBoxAligned GetObjectSelectionBoundsRec() { return CBoundingBoxAligned::EMPTY; } /** * Called when terrain has changed in the given inclusive bounds. * Might call SetDirty if the change affects this model. */ virtual void SetTerrainDirty(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1) = 0; /** * Called when the entity tries to set some variable to affect the display of this model * and/or its child objects. */ virtual void SetEntityVariable(const std::string& UNUSED(name), float UNUSED(value)) { } /** * Ensure that both the transformation and the bone matrices are correct for this model and all its props. */ virtual void ValidatePosition() = 0; /** * Mark this model's position and bone matrices, and all props' positions as invalid. */ virtual void InvalidatePosition() = 0; virtual void SetPlayerID(player_id_t id) { m_PlayerID = id; } // get the model's player ID; initial default is INVALID_PLAYER virtual player_id_t GetPlayerID() const { return m_PlayerID; } virtual void SetShadingColor(const CColor& color) { m_ShadingColor = color; } virtual const CColor& GetShadingColor() const { return m_ShadingColor; } protected: void CalcSelectionBox(); public: /// If non-null, points to the model that we are attached to. CModelAbstract* m_Parent; /// True if both transform and and bone matrices are valid. bool m_PositionValid; player_id_t m_PlayerID; /// Modulating color CColor m_ShadingColor; protected: /// Selection box for this model. CBoundingBoxOriented m_SelectionBox; /// Is the current selection box valid? bool m_SelectionBoxValid; /// Pointer to a descriptor for a custom-defined selection box shape. If no custom selection box is required, this is NULL /// and the standard recursive-bounding-box-based selection box is used. Otherwise, a custom selection box described by this /// field will be used. /// @see SetCustomSelectionShape CustomSelectionShape* m_CustomSelectionShape; }; #endif // INCLUDED_MODELABSTRACT