diff --git a/include/cpp3ds/Graphics/Billboard.hpp b/include/cpp3ds/Graphics/Billboard.hpp new file mode 100644 index 0000000..e542e57 --- /dev/null +++ b/include/cpp3ds/Graphics/Billboard.hpp @@ -0,0 +1,159 @@ +#ifndef CPP3DS_BILLBOARD_HPP +#define CPP3DS_BILLBOARD_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace cpp3ds +{ +class Camera; + +//////////////////////////////////////////////////////////// +/// \brief A sprite which automatically rotates to +/// face the camera. +/// +//////////////////////////////////////////////////////////// +class Billboard : public Sprite +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates an empty billboard with no source texture. + /// + //////////////////////////////////////////////////////////// + Billboard(); + + //////////////////////////////////////////////////////////// + /// \brief Construct the billboard from a source texture + /// + /// \param texture Source texture + /// + /// \see setTexture + /// + //////////////////////////////////////////////////////////// + explicit Billboard(const Texture& texture); + + //////////////////////////////////////////////////////////// + /// \brief Construct the billboard from a sub-rectangle of a source texture + /// + /// \param texture Source texture + /// \param rectangle Sub-rectangle of the texture to assign to the billboard + /// + /// \see setTexture, setTextureRect + /// + //////////////////////////////////////////////////////////// + Billboard(const Texture& texture, const IntRect& rectangle); + + //////////////////////////////////////////////////////////// + /// \brief Set the camera the billboard should track + /// + /// The \a camera argument refers to a camera that must + /// exist as long as the billboard uses it. Indeed, the billboard + /// doesn't store its own copy of the camera, but rather keeps + /// a pointer to the one that you passed to this function. + /// If the camera is destroyed and the billboard tries to + /// use it, the behaviour is undefined. + /// + /// \param camera Camera the billboard should track + /// + /// \see getCamera + /// + //////////////////////////////////////////////////////////// + void setCamera(const Camera& camera); + + //////////////////////////////////////////////////////////// + /// \brief Get the camera the billboard should track + /// + /// If the billboard has no camera, a NULL pointer is returned. + /// The returned pointer is const, which means that you can't + /// modify the camera when you retrieve it with this function. + /// + /// \return Pointer to the billboard's camera + /// + /// \see setCamera + /// + //////////////////////////////////////////////////////////// + const Camera* getCamera() const; + +private : + + //////////////////////////////////////////////////////////// + /// \brief Draw the billboard to a render target + /// + /// \param target Render target to draw to + /// \param states Current render states + /// + //////////////////////////////////////////////////////////// + virtual void draw(RenderTarget& target, RenderStates states) const; + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + const Camera* m_camera; ///< Camera the billboard should track +}; + +} // namespace cpp3ds + + +#endif // cpp3ds_BILLBOARD_HPP + + +//////////////////////////////////////////////////////////// +/// \class cpp3ds::Billboard +/// \ingroup graphics +/// +/// cpp3ds::Billboard is an cpp3ds::Sprite that rotates to always +/// face a specific camera. +/// +/// It inherits all the functions from cpp3ds::Sprite: +/// position, rotation, scale, origin as well as the sprite-specific +/// properties such as the texture to use, the part of it to display, +/// and the convenience functions to change the overall color of the +/// sprite, or to get its bounding rectangle. +/// +/// For the cpp3ds::Billboard to automatically face a camera whenever +/// it is drawn, a camera needs to be specified using setCamera. +/// When initially constructed, no camera is tracked and the billboard +/// will not automatically rotate. +/// +/// It is important to note that as with cpp3ds::Sprite the cpp3ds::Billboard +/// instance doesn't copy the texture that it uses, it only keeps a +/// reference to it. Thus, a cpp3ds::Texture must not be destroyed while +/// it is used by a cpp3ds::Billboard (i.e. never write a function that +/// uses a local cpp3ds::Texture instance for creating a billboard). +/// +/// The same applies for the camera. It is not copied, but instead +/// a reference to it is stored. +/// +/// See also the note on coordinates and undistorted rendering in cpp3ds::Transformable. +/// +/// Usage example: +/// \code +/// // Declare and use a camera +/// cpp3ds::Camera camera; +/// window.setView(camera); +/// +/// // Declare and load a texture +/// cpp3ds::Texture texture; +/// texture.loadFromFile("texture.png"); +/// +/// // Create a billboard +/// cpp3ds::Billboard billboard; +/// billboard.setTexture(texture); +/// billboard.setTextureRect(cpp3ds::IntRect(10, 10, 50, 30)); +/// billboard.setColor(cpp3ds::Color(255, 255, 255, 200)); +/// billboard.setCamera(camera); +/// billboard.setPosition(100, 25, 50); +/// +/// // Draw it +/// window.draw(billboard); +/// \endcode +/// +/// \see cpp3ds::Sprite, cpp3ds::Texture, cpp3ds::Transformable +/// +//////////////////////////////////////////////////////////// diff --git a/include/cpp3ds/Graphics/Box.hpp b/include/cpp3ds/Graphics/Box.hpp new file mode 100644 index 0000000..938902e --- /dev/null +++ b/include/cpp3ds/Graphics/Box.hpp @@ -0,0 +1,230 @@ +#ifndef CPP3DS_BOX_HPP +#define CPP3DS_BOX_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include + + +namespace cpp3ds +{ +//////////////////////////////////////////////////////////// +/// \brief Utility class for manipulating 3D axis aligned boxes +/// +//////////////////////////////////////////////////////////// +template +class Box +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates an empty box (it is equivalent to calling + /// Box(0, 0, 0, 0, 0, 0)). + /// + //////////////////////////////////////////////////////////// + Box(); + + //////////////////////////////////////////////////////////// + /// \brief Construct the box from its coordinates + /// + /// Be careful, the last three parameters are the width, + /// height and depth, not the right, bottom and back coordinates! + /// + /// \param boxLeft Left coordinate of the box + /// \param boxTop Top coordinate of the box + /// \param boxFront Front coordinate of the box + /// \param boxWidth Width of the box + /// \param boxHeight Height of the box + /// \param boxDepth Depth of the box + /// + //////////////////////////////////////////////////////////// + Box(T boxLeft, T boxTop, T boxFront, T boxWidth, T boxHeight, T boxDepth); + + //////////////////////////////////////////////////////////// + /// \brief Construct the box from position and size + /// + /// Be careful, the last parameter is the size, + /// not the bottom-right-back corner! + /// + /// \param position Position of the top-left-front corner of the box + /// \param size Size of the box + /// + //////////////////////////////////////////////////////////// + Box(const Vector3& position, const Vector3& size); + + //////////////////////////////////////////////////////////// + /// \brief Construct the box from another type of box + /// + /// This constructor doesn't replace the copy constructor, + /// it's called only when U != T. + /// A call to this constructor will fail to compile if U + /// is not convertible to T. + /// + /// \param box Box to convert + /// + //////////////////////////////////////////////////////////// + template + explicit Box(const Box& box); + + //////////////////////////////////////////////////////////// + /// \brief Check if a point is inside the box's area + /// + /// \param x X coordinate of the point to test + /// \param y Y coordinate of the point to test + /// \param z Z coordinate of the point to test + /// + /// \return True if the point is inside, false otherwise + /// + /// \see intersects + /// + //////////////////////////////////////////////////////////// + bool contains(T x, T y, T z) const; + + //////////////////////////////////////////////////////////// + /// \brief Check if a point is inside the box's area + /// + /// \param point Point to test + /// + /// \return True if the point is inside, false otherwise + /// + /// \see intersects + /// + //////////////////////////////////////////////////////////// + bool contains(const Vector3& point) const; + + //////////////////////////////////////////////////////////// + /// \brief Check the intersection between two boxes + /// + /// \param box Box to test + /// + /// \return True if boxes overlap, false otherwise + /// + /// \see contains + /// + //////////////////////////////////////////////////////////// + bool intersects(const Box& box) const; + + //////////////////////////////////////////////////////////// + /// \brief Check the intersection between two boxes + /// + /// This overload returns the overlapped box in the + /// \a intersection parameter. + /// + /// \param box Box to test + /// \param intersection Box to be filled with the intersection + /// + /// \return True if boxes overlap, false otherwise + /// + /// \see contains + /// + //////////////////////////////////////////////////////////// + bool intersects(const Box& box, Box& intersection) const; + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + T left; ///< Left coordinate of the box + T top; ///< Top coordinate of the box + T front; ///< Front coordinate of the box + T width; ///< Width of the box + T height; ///< Height of the box + T depth; ///< Depth of the box +}; + +//////////////////////////////////////////////////////////// +/// \relates Box +/// \brief Overload of binary operator == +/// +/// This operator compares strict equality between two boxes. +/// +/// \param left Left operand (a box) +/// \param right Right operand (a box) +/// +/// \return True if \a left is equal to \a right +/// +//////////////////////////////////////////////////////////// +template +bool operator ==(const Box& left, const Box& right); + +//////////////////////////////////////////////////////////// +/// \relates Box +/// \brief Overload of binary operator != +/// +/// This operator compares strict difference between two boxes. +/// +/// \param left Left operand (a box) +/// \param right Right operand (a box) +/// +/// \return True if \a left is not equal to \a right +/// +//////////////////////////////////////////////////////////// +template +bool operator !=(const Box& left, const Box& right); + +#include + +// Create typedefs for the most common types +typedef Box IntBox; +typedef Box FloatBox; + +} // namespace cpp3ds + + +#endif // CPP3DS_BOX_HPP + + +//////////////////////////////////////////////////////////// +/// \class cpp3ds::Box +/// \ingroup graphics +/// +/// A box is defined by its top-left-front corner and its size. +/// It is a very simple class defined for convenience, so +/// its member variables (left, top, front, width, height +/// and depth) are public and can be accessed directly, just like +/// the vector classes (Vector2 and Vector3) and the Rect class. +/// +/// To keep things simple, cpp3ds::Box doesn't define +/// functions to emulate the properties that are not directly +/// members (such as right, bottom, center, etc.), it rather +/// only provides intersection functions. +/// +/// cpp3ds::Box uses the usual rules for its boundaries: +/// \li The left, top and front edges are included in the box's area +/// \li The right (left + width), bottom (top + height) and back (front + depth) edges are excluded from the box's area +/// +/// This means that cpp3ds::IntBox(0, 0, 0, 1, 1, 1) and +/// cpp3ds::IntBox(1, 1, 1, 1, 1, 1) don't intersect. +/// +/// cpp3ds::Box is a template and may be used with any numeric type, but +/// for simplicity the instanciations used by cpp3ds are typedefed: +/// \li cpp3ds::Box is cpp3ds::IntBox +/// \li cpp3ds::Box is cpp3ds::FloatBox +/// +/// So that you don't have to care about the template syntax. +/// +/// Usage example: +/// \code +/// // Define a box, located at (0, 0, 0) with a size of 20x5x10 +/// cpp3ds::IntBox box1(0, 0, 0, 20, 5, 10); +/// +/// // Define another box, located at (4, 2, 3) with a size of 18x10x14 +/// cpp3ds::Vector3i position(4, 2, 3); +/// cpp3ds::Vector3i size(18, 10, 14); +/// cpp3ds::IntBox box2(position, size); +/// +/// // Test intersections with the point (3, 1) +/// bool b1 = box1.contains(3, 1, 2); // true +/// bool b2 = box2.contains(3, 1, 2); // false +/// +/// // Test the intersection between box1 and box2 +/// cpp3ds::IntBox result; +/// bool b3 = box1.intersects(box2, result); // true +/// // result == (4, 2, 3, 16, 3, 7) +/// \endcode +/// +//////////////////////////////////////////////////////////// diff --git a/include/cpp3ds/Graphics/Box.inl b/include/cpp3ds/Graphics/Box.inl new file mode 100644 index 0000000..4dfb23b --- /dev/null +++ b/include/cpp3ds/Graphics/Box.inl @@ -0,0 +1,153 @@ + + +//////////////////////////////////////////////////////////// +template +Box::Box() : +left (0), +top (0), +front (0), +width (0), +height(0), +depth (0) +{ + +} + + +//////////////////////////////////////////////////////////// +template +Box::Box(T boxLeft, T boxTop, T boxFront, T boxWidth, T boxHeight, T boxDepth) : +left (boxLeft), +top (boxTop), +front (boxFront), +width (boxWidth), +height(boxHeight), +depth (boxDepth) +{ + +} + + +//////////////////////////////////////////////////////////// +template +Box::Box(const Vector3& position, const Vector3& size) : +left (position.x), +top (position.y), +front (position.z), +width (size.x), +height(size.y), +depth (size.z) +{ + +} + + +//////////////////////////////////////////////////////////// +template +template +Box::Box(const Box& box) : +left (static_cast(box.left)), +top (static_cast(box.top)), +front (static_cast(box.front)), +width (static_cast(box.width)), +height(static_cast(box.height)), +depth (static_cast(box.depth)) +{ +} + + +//////////////////////////////////////////////////////////// +template +bool Box::contains(T x, T y, T z) const +{ + // Boxes with negative dimensions are allowed, so we must handle them correctly + + // Compute the real min and max of the box on all axes + T minX = std::min(left, static_cast(left + width)); + T maxX = std::max(left, static_cast(left + width)); + T minY = std::min(top, static_cast(top + height)); + T maxY = std::max(top, static_cast(top + height)); + T minZ = std::min(front, static_cast(front + depth)); + T maxZ = std::max(front, static_cast(front + depth)); + + return (x >= minX) && (x < maxX) && (y >= minY) && (y < maxY) && (z >= minZ) && (z < maxZ); +} + + +//////////////////////////////////////////////////////////// +template +bool Box::contains(const Vector3& point) const +{ + return contains(point.x, point.y, point.z); +} + + +//////////////////////////////////////////////////////////// +template +bool Box::intersects(const Box& box) const +{ + Box intersection; + return intersects(box, intersection); +} + + +//////////////////////////////////////////////////////////// +template +bool Box::intersects(const Box& box, Box& intersection) const +{ + // Boxes with negative dimensions are allowed, so we must handle them correctly + + // Compute the min and max of the first box on all axes + T b1MinX = std::min(left, static_cast(left + width)); + T b1MaxX = std::max(left, static_cast(left + width)); + T b1MinY = std::min(top, static_cast(top + height)); + T b1MaxY = std::max(top, static_cast(top + height)); + T b1MinZ = std::min(front, static_cast(front + depth)); + T b1MaxZ = std::max(front, static_cast(front + depth)); + + // Compute the min and max of the second box on all axes + T b2MinX = std::min(box.left, static_cast(box.left + box.width)); + T b2MaxX = std::max(box.left, static_cast(box.left + box.width)); + T b2MinY = std::min(box.top, static_cast(box.top + box.height)); + T b2MaxY = std::max(box.top, static_cast(box.top + box.height)); + T b2MinZ = std::min(box.front, static_cast(box.front + box.depth)); + T b2MaxZ = std::max(box.front, static_cast(box.front + box.depth)); + + // Compute the intersection boundaries + T interLeft = std::max(b1MinX, b2MinX); + T interTop = std::max(b1MinY, b2MinY); + T interFront = std::max(b1MinZ, b2MinZ); + T interRight = std::min(b1MaxX, b2MaxX); + T interBottom = std::min(b1MaxY, b2MaxY); + T interBack = std::min(b1MaxZ, b2MaxZ); + + // If the intersection is valid (positive non zero area), then there is an intersection + if ((interLeft < interRight) && (interTop < interBottom) && (interFront < interBack)) + { + intersection = Box(interLeft, interTop, interFront, interRight - interLeft, interBottom - interTop, interBack - interFront); + return true; + } + else + { + intersection = Box(0, 0, 0, 0, 0, 0); + return false; + } +} + + +//////////////////////////////////////////////////////////// +template +inline bool operator ==(const Box& left, const Box& right) +{ + return (left.left == right.left) && (left.width == right.width) && + (left.top == right.top) && (left.height == right.height) && + (left.front == right.front) && (left.depth == right.depth); +} + + +//////////////////////////////////////////////////////////// +template +inline bool operator !=(const Box& left, const Box& right) +{ + return !(left == right); +} diff --git a/include/cpp3ds/Graphics/Camera.hpp b/include/cpp3ds/Graphics/Camera.hpp new file mode 100644 index 0000000..5d0d94c --- /dev/null +++ b/include/cpp3ds/Graphics/Camera.hpp @@ -0,0 +1,480 @@ +#ifndef CPP3DS_CAMERA_HPP +#define CPP3DS_CAMERA_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include + + +namespace cpp3ds +{ +//////////////////////////////////////////////////////////// +/// \brief 3D camera that defines what is shown on screen +/// +//////////////////////////////////////////////////////////// +class Camera : public View +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Construct the camera from its field of view and clipping planes + /// + /// This constructor creates a camera with the given + /// field of view, near clipping plane distance and far + /// clipping plane distance. + /// + /// The camera will be positioned at (0, 0, 0) with + /// direction (0, 0, -1) and up (0, 1, 0) and will not + /// be rotated or scaled by default. + /// + /// \param fov Field of view of the camera in degrees + /// \param near Near clipping plane distance of the camera + /// \param far Far clipping plane distance of the camera + /// + //////////////////////////////////////////////////////////// + Camera(float fov, float near, float far); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + virtual ~Camera(); + + //////////////////////////////////////////////////////////// + /// \brief Set the position of the camera + /// + /// This function completely overwrites the previous position. + /// See the move function to apply an offset based on the previous position instead. + /// The default position of a camera is (0, 0, 0). + /// + /// \param x X coordinate of the new position + /// \param y Y coordinate of the new position + /// \param z Z coordinate of the new position + /// + /// \see move, getPosition + /// + //////////////////////////////////////////////////////////// + void setPosition(float x, float y, float z); + + //////////////////////////////////////////////////////////// + /// \brief Set the position of the camera + /// + /// This function completely overwrites the previous position. + /// See the move function to apply an offset based on the previous position instead. + /// The default position of a camera is (0, 0). + /// + /// \param position New position + /// + /// \see move, getPosition + /// + //////////////////////////////////////////////////////////// + void setPosition(const Vector3f& position); + + //////////////////////////////////////////////////////////// + /// \brief Get the position of the camera + /// + /// \return Current position + /// + /// \see setPosition + /// + //////////////////////////////////////////////////////////// + virtual const Vector3f& getPosition() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the forward vector of the camera + /// + /// The direction (also called "at vector") is the vector + /// pointing forward from the camera's perspective. Together + /// with the up vector, it defines the 3D orientation of the + /// camera in the scene. The direction vector doesn't + /// have to be normalized. + /// The default camera's direction is (0, 0, -1). + /// + /// \param x X coordinate of the camera's direction + /// \param y Y coordinate of the camera's direction + /// \param z Z coordinate of the camera's direction + /// + /// \see getDirection, setUpVector, setPosition + /// + //////////////////////////////////////////////////////////// + void setDirection(float x, float y, float z); + + //////////////////////////////////////////////////////////// + /// \brief Set the forward vector of the camera + /// + /// The direction (also called "at vector") is the vector + /// pointing forward from the camera's perspective. Together + /// with the up vector, it defines the 3D orientation of the + /// camera in the scene. The direction vector doesn't + /// have to be normalized. + /// The default camera's direction is (0, 0, -1). + /// + /// \param direction New camera's direction + /// + /// \see getDirection, setUpVector, setPosition + /// + //////////////////////////////////////////////////////////// + void setDirection(const Vector3f& direction); + + //////////////////////////////////////////////////////////// + /// \brief Get the current forward vector of the camera + /// + /// \return Camera's forward vector (not normalized) + /// + /// \see setDirection + /// + //////////////////////////////////////////////////////////// + const Vector3f& getDirection() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the upward vector of the camera + /// + /// The up vector is the vector that points upward from the + /// camera's perspective. Together with the direction, it + /// defines the 3D orientation of the camera in the scene. + /// The up vector doesn't have to be normalized. + /// The default camera's up vector is (0, 1, 0). + /// + /// \param x X coordinate of the camera's up vector + /// \param y Y coordinate of the camera's up vector + /// \param z Z coordinate of the camera's up vector + /// + /// \see getUpVector, setDirection, setPosition + /// + //////////////////////////////////////////////////////////// + void setUpVector(float x, float y, float z); + + //////////////////////////////////////////////////////////// + /// \brief Set the upward vector of the camera + /// + /// The up vector is the vector that points upward from the + /// camera's perspective. Together with the direction, it + /// defines the 3D orientation of the camera in the scene. + /// The up vector doesn't have to be normalized. + /// The default camera's up vector is (0, 1, 0). + /// + /// \param upVector New camera's up vector + /// + /// \see getUpVector, setDirection, setPosition + /// + //////////////////////////////////////////////////////////// + void setUpVector(const Vector3f& upVector); + + //////////////////////////////////////////////////////////// + /// \brief Get the current upward vector of the camera + /// + /// \return Camera's upward vector (not normalized) + /// + /// \see setUpVector + /// + //////////////////////////////////////////////////////////// + const Vector3f& getUpVector() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the scale factors of the camera + /// + /// This function completely overwrites the previous scale. + /// See the scale function to add a factor based on the previous scale instead. + /// The default scale of a camera is (1, 1, 1). + /// + /// \param factorX New horizontal scale factor + /// \param factorY New vertical scale factor + /// \param factorZ New depth scale factor + /// + /// \see scale, getScale + /// + //////////////////////////////////////////////////////////// + void setScale(float factorX, float factorY, float factorZ); + + //////////////////////////////////////////////////////////// + /// \brief Set the scale factors of the camera + /// + /// This function completely overwrites the previous scale. + /// See the scale function to add a factor based on the previous scale instead. + /// The default scale of a transformable camera is (1, 1, 1). + /// + /// \param factors New scale factors + /// + /// \see scale, getScale + /// + //////////////////////////////////////////////////////////// + void setScale(const Vector3f& factors); + + //////////////////////////////////////////////////////////// + /// \brief Get the current scale of the camera + /// + /// \return Current scale factors + /// + /// \see setScale + /// + //////////////////////////////////////////////////////////// + const Vector3f& getScale() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the field of view of this camera + /// + /// The field of view of the camera specifies the opening + /// angle of the frustum that is defined by this camera. + /// Setting it to a larger value will make more geometry + /// visible, whereas setting it to a smaller value will + /// make less geometry visible. + /// + /// \param fov Field of view angle in degrees + /// + /// \see getFieldOfView + /// + //////////////////////////////////////////////////////////// + void setFieldOfView(float fov); + + //////////////////////////////////////////////////////////// + /// \brief Get the field of view of this camera + /// + /// \return Field of view angle in degrees + /// + /// \see setFieldOfView + /// + //////////////////////////////////////////////////////////// + float getFieldOfView() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the distance to the near clipping plane + /// + /// The graphics hardware cannot draw objects that are + /// infinitely far away from the viewer, hence it requires + /// a frustum that defines the viewing volume in which + /// all geometry must be to be rendered. + /// The sides (left, right, top, bottom) are defined + /// by the viewport and the field of view. + /// The distance from the viewer to the "front" of the + /// frustum is defined by the near clipping plane. + /// Objects which are positioned closer to the viewer + /// (or even behind the viewer) than the near clipping plane + /// are clipped and thus not rendered. + /// + /// Because the frustum should always be "in front" of the + /// viewer in order for objects to be rendered to the "screen" + /// that you see, the distance to the near clipping plane + /// must always be positive. + /// + /// Bear in mind that when using depth testing, the depth + /// precision is affected by the distance between the near + /// and far clipping planes. + /// + /// \param distance Distance to the near clipping plane + /// + /// \see getNearClippingPlane, setFarClippingPlane + /// + //////////////////////////////////////////////////////////// + void setNearClippingPlane(float distance); + + //////////////////////////////////////////////////////////// + /// \brief Get the distance to the near clipping plane + /// + /// \return The distance to the near clipping plane + /// + /// \see setNearClippingPlane, getFarClippingPlane + /// + //////////////////////////////////////////////////////////// + float getNearClippingPlane() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the distance to the far clipping plane + /// + /// The graphics hardware cannot draw objects that are + /// infinitely far away from the viewer, hence it requires + /// a frustum that defines the viewing volume in which + /// all geometry must be to be rendered. + /// The sides (left, right, top, bottom) are defined + /// by the viewport and the field of view. + /// The distance from the viewer to the "back" of the + /// frustum is defined by the far clipping plane. + /// Objects which are positioned further away from the + /// viewer than the far clipping plane are clipped + /// and thus not rendered. + /// + /// Because the frustum should always be "in front" of the + /// viewer in order for objects to be rendered to the "screen" + /// that you see, the distance to the far clipping plane + /// must always be positive. + /// + /// Bear in mind that when using depth testing, the depth + /// precision is affected by the distance between the near + /// and far clipping planes. + /// + /// \param distance Distance to the far clipping plane + /// + /// \see getFarClippingPlane, setNearClippingPlane + /// + //////////////////////////////////////////////////////////// + void setFarClippingPlane(float distance); + + //////////////////////////////////////////////////////////// + /// \brief Get the distance to the far clipping plane + /// + /// \return The distance to the far clipping plane + /// + /// \see setFarClippingPlane, getNearClippingPlane + /// + //////////////////////////////////////////////////////////// + float getFarClippingPlane() const; + + //////////////////////////////////////////////////////////// + /// \brief Scale the camera + /// + /// This function multiplies the current scale of the camera, + /// unlike setScale which overwrites it. + /// Thus, it is equivalent to the following code: + /// \code + /// cpp3ds::Vector3f scale = camera.getScale(); + /// camera.setScale(scale.x * factorX, scale.y * factorY, scale.z * factorZ); + /// \endcode + /// + /// \param factorX Horizontal scale factor + /// \param factorY Vertical scale factor + /// \param factorZ Depth scale factor + /// + /// \see setScale + /// + //////////////////////////////////////////////////////////// + void scale(float factorX, float factorY, float factorZ); + + //////////////////////////////////////////////////////////// + /// \brief Scale the camera + /// + /// This function multiplies the current scale of the camera, + /// unlike setScale which overwrites it. + /// Thus, it is equivalent to the following code: + /// \code + /// cpp3ds::Vector3f scale = camera.getScale(); + /// camera.setScale(scale.x * factor.x, scale.y * factor.y, scale.z * factor.z); + /// \endcode + /// + /// \param factor Scale factors + /// + /// \see setScale + /// + //////////////////////////////////////////////////////////// + void scale(const Vector3f& factor); + + //////////////////////////////////////////////////////////// + /// \brief Get the projection transform of the camera + /// + /// This function is meant for internal use only. + /// + /// \return Projection transform defining the camera + /// + /// \see getInverseTransform + /// + //////////////////////////////////////////////////////////// + virtual const Transform& getTransform() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the view transform of the camera + /// + /// This function is meant for internal use only. + /// + /// \return View transform of the camera + /// + /// \see getTransform, getInverseTransform + /// + //////////////////////////////////////////////////////////// + virtual const Transform& getViewTransform() const; + +private: + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + float m_fieldOfView; ///< Field of view of this camera, in degrees + float m_nearPlane; ///< The distance to the near clipping plane + float m_farPlane; ///< The distance to the far clipping plane + Vector3f m_direction; ///< The direction the camera is facing in + Vector3f m_upVector; ///< The up vector of the camera + Vector3f m_scale; ///< The scaling that is applied after the perspective transform +}; + +} // namespace cpp3ds + + +#endif // CPP3DS_VIEW_HPP + + +//////////////////////////////////////////////////////////// +/// \class cpp3ds::Camera +/// \ingroup graphics +/// +/// cpp3ds::Camera defines a camera in the 3D scene. This is a +/// very powerful concept: you can move, rotate or zoom +/// the entire scene without altering the way that your +/// drawable objects are drawn. +/// +/// An cpp3ds::Camera is much like a camera in real life. It has +/// a position, a field of view (different camera lenses) a +/// direction in which it is pointed, a vector denoting +/// the direction that the top of the camera is pointing in +/// (think of a stick stuck to the top of the camera pointing +/// directly away from it) and a per-axis scale that is useful +/// for setting up the proper aspect ratio of the captured image +/// such as 4:3 and 16:9 cameras in real life. +/// +/// Depending on where the camera is, where it is pointing, +/// where its "top" is facing, what its field of view is and +/// what its aspect ratio is, you will get a different final image. +/// +/// Because cpp3ds::Camera inherits from cpp3ds::View, its viewport +/// allows to map the final image to a custom part of the +/// render target, and can be used for split-screen or for +/// creating a rear-view mirror, for example. If the source +/// image does not have the same size as the viewport, its +/// contents will be stretched or shrunk to fit in. +/// +/// To apply a camera, you have to assign it to the render target. +/// Then, every object drawn to this render target will be +/// affected by the camera until you use another camera or view. +/// +/// cpp3ds::Camera can be combined with cpp3ds::View to render a scene +/// using a 3D perspective and then to render 2D elements such as +/// text or a GUI using an cpp3ds::View. Just switch back and forth +/// depending on which you want to affect the following objects +/// to be drawn. +/// +/// Usage example: +/// \code +/// cpp3ds::RenderWindow window; +/// +/// // Construct the camera with a 90 degree field of view +/// // and about 1000 units of space between the clipping planes +/// cpp3ds::Camera camera(90.f, 0.001f, 1000.f); +/// +/// // Set the camera aspect ratio as its scale +/// camera.scale(600.f / 800.f, 1.f, 1.f); +/// +/// // Set its position, direction and up vector +/// camera.setPosition(10, 20, 30); +/// camera.setDirection(0, -1, -1); +/// camera.setUpVector(0, 1, 0); +/// +/// // Set its target viewport to be half of the window +/// camera.setViewport(cpp3ds::FloatRect(0.f, 0.f, 1.f, 0.5f)); +/// +/// // Apply it +/// window.setView(camera); +/// +/// // Render stuff +/// window.draw(someModel); +/// +/// // Set to the default view for 2D rendering +/// window.setView(window.getDefaultView()); +/// +/// // Render 2D elements such as text or a GUI +/// window.draw(someText); +/// \endcode +/// +/// See also the note on coordinates and undistorted rendering in cpp3ds::Transformable. +/// +/// \see cpp3ds::View, cpp3ds::RenderWindow, cpp3ds::RenderTexture +/// +//////////////////////////////////////////////////////////// diff --git a/include/cpp3ds/Graphics/CircleShape.hpp b/include/cpp3ds/Graphics/CircleShape.hpp index 4c60b9f..568e470 100644 --- a/include/cpp3ds/Graphics/CircleShape.hpp +++ b/include/cpp3ds/Graphics/CircleShape.hpp @@ -103,7 +103,7 @@ public : /// \return index-th point of the shape /// //////////////////////////////////////////////////////////// - virtual Vector2f getPoint(unsigned int index) const; + virtual Vector3f getPoint(unsigned int index) const; private : @@ -114,7 +114,7 @@ private : unsigned int m_pointCount; ///< Number of points composing the circle }; -} +} // namespace cpp3ds #endif diff --git a/include/cpp3ds/Graphics/ConvexPolyhedron.hpp b/include/cpp3ds/Graphics/ConvexPolyhedron.hpp new file mode 100644 index 0000000..fc53326 --- /dev/null +++ b/include/cpp3ds/Graphics/ConvexPolyhedron.hpp @@ -0,0 +1,132 @@ +#ifndef CPP3DS_CONVEXPOLYHEDRON_HPP +#define CPP3DS_CONVEXPOLYHEDRON_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace cpp3ds +{ +//////////////////////////////////////////////////////////// +/// \brief Specialized polyhedron representing a convex polyhedron +/// +//////////////////////////////////////////////////////////// +class ConvexPolyhedron : public Polyhedron +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// \param faceCount Number of faces of the polyhedron + /// + //////////////////////////////////////////////////////////// + explicit ConvexPolyhedron(unsigned int faceCount = 0); + + //////////////////////////////////////////////////////////// + /// \brief Set the number of faces of the polyhedron + /// + /// \param count New number of faces of the polyhedron + /// + /// \see getFaceCount + /// + //////////////////////////////////////////////////////////// + void setFaceCount(unsigned int count); + + //////////////////////////////////////////////////////////// + /// \brief Get the number of faces of the polyhedron + /// + /// \return Number of faces of the polyhedron + /// + /// \see setFaceCount + /// + //////////////////////////////////////////////////////////// + virtual unsigned int getFaceCount() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the vertices of a face + /// + /// Don't forget that the face vertices must be specified + /// with counter-clockwise winding or else it will be culled! + /// setFaceCount must be called first in order to set the total + /// number of faces. The result is undefined if \a index is out + /// of the valid range. + /// + /// \param index Index of the face to change, in range [0 .. getFaceCount() - 1] + /// \param v0 First vertex of the face + /// \param v1 Second vertex of the face + /// \param v2 Third vertex of the face + /// + /// \see getFace + /// + //////////////////////////////////////////////////////////// + void setFace(unsigned int index, const Vertex& v0, const Vertex& v1, const Vertex& v2); + + //////////////////////////////////////////////////////////// + /// \brief Get a face + /// + /// The result is undefined if \a index is out of the valid range. + /// + /// \param index Index of the face to get, in range [0 .. getFaceCount() - 1] + /// + /// \return Index-th Face of the polyhedron + /// + /// \see setFace + /// + //////////////////////////////////////////////////////////// + virtual Face getFace(unsigned int index) const; + +private : + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + std::vector m_vertices; ///< Vertices composing the convex polyhedron +}; + +} // namespace cpp3ds + + +#endif // CPP3DS_CONVEXPOLYHEDRON_HPP + + +//////////////////////////////////////////////////////////// +/// \class cpp3ds::ConvexPolyhedron +/// \ingroup graphics +/// +/// This class inherits all the functions of cpp3ds::Transformable +/// (position, rotation, scale, bounds, ...) as well as the +/// functions of cpp3ds::Polyhedron (color, texture, ...). +/// +/// It is important to keep in mind that while specifying faces, +/// faces with clockwise winding (vertices specified in clockwise +/// order from the perspective of the viewer) are culled by default. +/// If you want a face to be facing the "outside" of the polyhedron, +/// specify its vertices in counter-clockwise order. +/// +/// If you want to light your scene, you will either need to +/// specify the vertex normal data yourself, or if you want to +/// automatically generate per-face normals you can call +/// generateNormals() after you are done specifying the faces. +/// +/// Usage example: +/// \code +/// cpp3ds::ConvexPolyhedron polyhedron; +/// polyhedron.setFaceCount(4); +/// polyhedron.setFace(0, cpp3ds::Vector3f(0, -10, -10), cpp3ds::Vector3f(10, -10, 10), cpp3ds::Vector3f(-10, -10, 10)); +/// polyhedron.setFace(1, cpp3ds::Vector3f(10, -10, 10), cpp3ds::Vector3f(0, -10, -10), cpp3ds::Vector3f(0, 10, 0)); +/// polyhedron.setFace(2, cpp3ds::Vector3f(-10, -10, 10), cpp3ds::Vector3f(10, -10, 10), cpp3ds::Vector3f(0, 10, 0)); +/// polyhedron.setFace(3, cpp3ds::Vector3f(0, -10, -10), cpp3ds::Vector3f(-10, -10, 10), cpp3ds::Vector3f(0, 10, 0)); +/// polyhedron.generateNormals(); +/// polyhedron.setColor(cpp3ds::Color::Red); +/// polyhedron.setPosition(10, 20, 30); +/// ... +/// window.draw(polyhedron); +/// \endcode +/// +/// \see cpp3ds::Polyhedron, cpp3ds::Cuboid, cpp3ds::SphericalPolyhedron +/// +//////////////////////////////////////////////////////////// diff --git a/include/cpp3ds/Graphics/ConvexShape.hpp b/include/cpp3ds/Graphics/ConvexShape.hpp index 4dfb75f..5a528d6 100644 --- a/include/cpp3ds/Graphics/ConvexShape.hpp +++ b/include/cpp3ds/Graphics/ConvexShape.hpp @@ -87,7 +87,7 @@ public : /// \see getPoint /// //////////////////////////////////////////////////////////// - void setPoint(unsigned int index, const Vector2f& point); + void setPoint(unsigned int index, const Vector3f& point); //////////////////////////////////////////////////////////// /// \brief Get the position of a point @@ -104,17 +104,17 @@ public : /// \see setPoint /// //////////////////////////////////////////////////////////// - virtual Vector2f getPoint(unsigned int index) const; + virtual Vector3f getPoint(unsigned int index) const; private : //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - std::vector m_points; ///< Points composing the convex polygon + std::vector m_points; ///< Points composing the convex polygon }; -} +} // namespace cpp3ds #endif diff --git a/include/cpp3ds/Graphics/Cuboid.hpp b/include/cpp3ds/Graphics/Cuboid.hpp new file mode 100644 index 0000000..9e77017 --- /dev/null +++ b/include/cpp3ds/Graphics/Cuboid.hpp @@ -0,0 +1,102 @@ +#ifndef CPP3DS_CUBOID_HPP +#define CPP3DS_CUBOID_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace cpp3ds +{ +//////////////////////////////////////////////////////////// +/// \brief Specialized polyhedron representing a cuboid +/// +//////////////////////////////////////////////////////////// +class Cuboid : public Polyhedron +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// \param size Size of the cuboid + /// + //////////////////////////////////////////////////////////// + explicit Cuboid(const Vector3f& size = Vector3f(0, 0, 0)); + + //////////////////////////////////////////////////////////// + /// \brief Set the size of the cuboid + /// + /// \param size New size of the cuboid + /// + /// \see getSize + /// + //////////////////////////////////////////////////////////// + void setSize(const Vector3f& size); + + //////////////////////////////////////////////////////////// + /// \brief Get the size of the cuboid + /// + /// \return Size of the cuboid + /// + /// \see setSize + /// + //////////////////////////////////////////////////////////// + const Vector3f& getSize() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the number of faces defining the polyhedron + /// + /// \return Number of faces defining the polyhedron + /// + //////////////////////////////////////////////////////////// + virtual unsigned int getFaceCount() const; + + //////////////////////////////////////////////////////////// + /// \brief Get a face of the polyhedron + /// + /// The result is undefined if \a index is out of the valid range. + /// + /// \param index Index of the face to get, in range [0 .. getFaceCount() - 1] + /// + /// \return Index-th face of the polyhedron + /// + //////////////////////////////////////////////////////////// + virtual Face getFace(unsigned int index) const; + +private : + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + Vector3f m_size; ///< Size of the cuboid +}; + +} // namespace cpp3ds + + +#endif // CPP3DS_CUBOID_HPP + + +//////////////////////////////////////////////////////////// +/// \class cpp3ds::Cuboid +/// \ingroup graphics +/// +/// This class inherits all the functions of cpp3ds::Transformable +/// (position, rotation, scale, bounds, ...) as well as the +/// functions of cpp3ds::Polyhedron (color, texture, ...). +/// +/// Usage example: +/// \code +/// cpp3ds::Cuboid cuboid; +/// cuboid.setSize(cpp3ds::Vector3f(100, 50, 70)); +/// cuboid.setColor(cpp3ds::Color::Red); +/// cuboid.setPosition(10, 20, 30); +/// ... +/// window.draw(cuboid); +/// \endcode +/// +/// \see cpp3ds::Polyhedron, cpp3ds::SphericalPolyhedron, cpp3ds::ConvexPolyhedron +/// +//////////////////////////////////////////////////////////// diff --git a/include/cpp3ds/Graphics/Light.hpp b/include/cpp3ds/Graphics/Light.hpp new file mode 100644 index 0000000..9e46fff --- /dev/null +++ b/include/cpp3ds/Graphics/Light.hpp @@ -0,0 +1,686 @@ +#ifndef CPP3DS_LIGHT_HPP +#define CPP3DS_LIGHT_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include + + +namespace cpp3ds +{ +class Shader; + +//////////////////////////////////////////////////////////// +/// \brief Light source, either positional or directional +/// +//////////////////////////////////////////////////////////// +class Light : GlResource +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates a default positional light source with + /// position (0, 0, 0), white color, 0.f ambient intensity, + /// 1.f diffuse intensity and 1.f specular intensity. + /// + //////////////////////////////////////////////////////////// + Light(); + + //////////////////////////////////////////////////////////// + /// \brief Copy constructor + /// + /// \param copy instance to copy + /// + //////////////////////////////////////////////////////////// + Light(const Light& copy); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + ~Light(); + + //////////////////////////////////////////////////////////// + /// \brief Set this light to a directional light or positional light + /// + /// Positional light sources have a position within the scene + /// to be rendered. They emit light uniformly in all directions. + /// Directional lights can be considered as if they were positioned + /// infinitely far away from the scene, thus the rays they emit + /// all arrive parallel to one another. + /// + /// \param directional True to set this light source as a directional light, false to set it as a positional light + /// + /// \see isDirectional + /// + //////////////////////////////////////////////////////////// + void setDirectional(bool directional); + + //////////////////////////////////////////////////////////// + /// \brief Set the position of this light source + /// + /// This function completely overwrites the previous position. + /// See the move function to apply an offset based on the previous position instead. + /// The default position of a light source is (0, 0, 0). + /// + /// Keep in mind that this takes into account the current view matrix. + /// + /// \param x X coordinate of the new position + /// \param y Y coordinate of the new position + /// \param z Z coordinate of the new position + /// + /// \see move, getPosition + /// + //////////////////////////////////////////////////////////// + void setPosition(float x, float y, float z); + + //////////////////////////////////////////////////////////// + /// \brief Set the position of this light source + /// + /// This function completely overwrites the previous position. + /// See the move function to apply an offset based on the previous position instead. + /// The default position of a light source is (0, 0, 0). + /// + /// Keep in mind that this takes into account the current view matrix. + /// + /// \param position The new position of this light source + /// + /// \see move, getPosition + /// + //////////////////////////////////////////////////////////// + void setPosition(const Vector3f& position); + + //////////////////////////////////////////////////////////// + /// \brief Get the position of this light source + /// + /// Warning: If this is a directional light source, this will + /// return its direction as they are the same. + /// + /// \return The position of this light source + /// + /// \see setPosition + /// + //////////////////////////////////////////////////////////// + const Vector3f& getPosition() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the direction of this light source + /// + /// This function completely overwrites the previous direction. + /// + /// \param x X coordinate of the new direction + /// \param y Y coordinate of the new direction + /// \param z Z coordinate of the new direction + /// + /// \see getDirection + /// + //////////////////////////////////////////////////////////// + void setDirection(float x, float y, float z); + + //////////////////////////////////////////////////////////// + /// \brief Set the direction of this light source + /// + /// This function completely overwrites the previous direction. + /// + /// \param direction The new direction of this light source + /// + /// \see getDirection + /// + //////////////////////////////////////////////////////////// + void setDirection(const Vector3f& direction); + + //////////////////////////////////////////////////////////// + /// \brief Get the direction of this light source + /// + /// Warning: If this is a positional light source, this will + /// return its position as they are the same. + /// + /// \return The direction of this light source + /// + /// \see setDirection + /// + //////////////////////////////////////////////////////////// + const Vector3f& getDirection() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the color of the light produced by this light source + /// + /// The default color of the light produced by a + /// light source is cpp3ds::Color::White. + /// + /// \param color The new color of the light produced by this light source + /// + /// \see getColor + /// + //////////////////////////////////////////////////////////// + void setColor(const Color& color); + + //////////////////////////////////////////////////////////// + /// \brief Get the color of the light produced by this light source + /// + /// \return The color of the light produced by this light source + /// + /// \see setColor + /// + //////////////////////////////////////////////////////////// + const Color& getColor() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the ambient intensity of this light source + /// + /// The default ambient intensity of a light source is 0.f + /// meaning that when a surface is not illuminated by the light, + /// it will appear black. + /// + /// \param intensity The new ambient intensity of this light source + /// + /// \see getAmbientIntensity + /// + //////////////////////////////////////////////////////////// + void setAmbientIntensity(float intensity); + + //////////////////////////////////////////////////////////// + /// \brief Get the ambient intensity of this light source + /// + /// \return The ambient intensity of this light source + /// + /// \see setAmbientIntensity + /// + //////////////////////////////////////////////////////////// + float getAmbientIntensity() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the diffuse intensity of this light source + /// + /// The default diffuse intensity of a light source is 1.f + /// meaning that when a surface is fully illuminated by the light, + /// it will appear as the color/texture it would have if it + /// was not lit at all. + /// + /// \param intensity The new diffuse intensity of this light source + /// + /// \see getDiffuseIntensity + /// + //////////////////////////////////////////////////////////// + void setDiffuseIntensity(float intensity); + + //////////////////////////////////////////////////////////// + /// \brief Get the diffuse intensity of this light source + /// + /// \return The diffuse intensity of this light source + /// + /// \see setDiffuseIntensity + /// + //////////////////////////////////////////////////////////// + float getDiffuseIntensity() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the specular intensity of this light source + /// + /// The default specular intensity of a light source is 1.f + /// meaning that when a surface is viewed from the same direction + /// as the incident light ray (the light is shining at the surface + /// in the same direction as you are looking at the surface), + /// a specular highlight will appear on the surface. + /// + /// \param intensity The new specular intensity of this light source + /// + /// \see getSpecularIntensity + /// + //////////////////////////////////////////////////////////// + void setSpecularIntensity(float intensity); + + //////////////////////////////////////////////////////////// + /// \brief Get the specular intensity of this light source + /// + /// \return The specular intensity of this light source + /// + /// \see setSpecularIntensity + /// + //////////////////////////////////////////////////////////// + float getSpecularIntensity() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the constant attenuation of this light source + /// + /// The constant attenuation factor of a light source is always + /// applied during lighting computations disregarding the distance + /// of a light source to a vertex. As such it is the same for all + /// vertices. + /// + /// The default constant attenuation is 1, which means no attenuation. + /// + /// Attenuation factor: 1.f / (constant + linear * d + quadratic * d * d) + /// + /// \param attenuation The new constant attenuation factor of this light source + /// + /// \see getConstantAttenuation + /// + //////////////////////////////////////////////////////////// + void setConstantAttenuation(float attenuation); + + //////////////////////////////////////////////////////////// + /// \brief Get the constant attenuation of this light source + /// + /// \return The constant attenuation of this light source + /// + /// \see setConstantAttenuation + /// + //////////////////////////////////////////////////////////// + float getConstantAttenuation() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the linear attenuation of this light source + /// + /// The linear attenuation factor of a light source is applied + /// during lighting computations taking into account the distance + /// of a light source to a vertex. + /// + /// The default linear attenuation is 0, which means no attenuation. + /// + /// Attenuation factor: 1.f / (constant + linear * d + quadratic * d * d) + /// + /// \param attenuation The new linear attenuation factor of this light source + /// + /// \see getLinearAttenuation + /// + //////////////////////////////////////////////////////////// + void setLinearAttenuation(float attenuation); + + //////////////////////////////////////////////////////////// + /// \brief Get the linear attenuation of this light source + /// + /// \return The linear attenuation of this light source + /// + /// \see setLinearAttenuation + /// + //////////////////////////////////////////////////////////// + float getLinearAttenuation() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the quadratic attenuation of this light source + /// + /// The quadratic attenuation factor of a light source is applied + /// during lighting computations taking into account the square of + /// the distance of a light source to a vertex. + /// + /// The default quadratic attenuation is 0, which means no attenuation. + /// + /// Attenuation factor: 1.f / (constant + linear * d + quadratic * d * d) + /// + /// \param attenuation The new quadratic attenuation factor of this light source + /// + /// \see getQuadraticAttenuation + /// + //////////////////////////////////////////////////////////// + void setQuadraticAttenuation(float attenuation); + + //////////////////////////////////////////////////////////// + /// \brief Get the quadratic attenuation of this light source + /// + /// \return The quadratic attenuation of this light source + /// + /// \see setQuadraticAttenuation + /// + //////////////////////////////////////////////////////////// + float getQuadraticAttenuation() const; + + //////////////////////////////////////////////////////////// + /// \brief Move this light source by a given offset + /// + /// This function adds to the current position of the light source, + /// unlike setPosition which overwrites it. + /// Thus, it is equivalent to the following code: + /// \code + /// cpp3ds::Vector3f pos = light.getPosition(); + /// light.setPosition(pos.x + offsetX, pos.y + offsetY, pos.z + offsetZ); + /// \endcode + /// + /// \param offsetX X offset + /// \param offsetY Y offset + /// \param offsetZ Z offset + /// + /// \see setPosition + /// + //////////////////////////////////////////////////////////// + void move(float offsetX, float offsetY, float offsetZ); + + //////////////////////////////////////////////////////////// + /// \brief Move this light source by a given offset + /// + /// This function adds to the current position of the light source, + /// unlike setPosition which overwrites it. + /// Thus, it is equivalent to the following code: + /// \code + /// light.setPosition(light.getPosition() + offset); + /// \endcode + /// + /// \param offset Offset + /// + /// \see setPosition + /// + //////////////////////////////////////////////////////////// + void move(const Vector3f& offset); + + //////////////////////////////////////////////////////////// + /// \brief Tell whether this is a directional light or not + /// + /// \return True if this is a directional light, false if it is a positional light + /// + /// \see setDirectional + /// + //////////////////////////////////////////////////////////// + bool isDirectional() const; + + //////////////////////////////////////////////////////////// + /// \brief Overload of assignment operator + /// + /// \param right Instance to assign + /// + /// \return Reference to self + /// + //////////////////////////////////////////////////////////// + Light& operator =(const Light& right); + + //////////////////////////////////////////////////////////// + /// \brief Enable this light during rendering + /// + /// Enable this light so it will be factored in when objects + /// are rendered in the future. + /// + /// \code + /// cpp3ds::Light l1, l2; + /// ... + /// l1.enable(); + /// // draw stuff taking l1 into account... + /// l2.enable(); + /// // draw stuff taking l2 into account as well... + /// l1.disable(); + /// // draw stuff taking only l2 into account... + /// l2.disable(); + /// // draw stuff taking no lights into account (very dark)... + /// \endcode + /// + /// \see disable + /// + //////////////////////////////////////////////////////////// + void enable(); + + //////////////////////////////////////////////////////////// + /// \brief Disable this light during rendering + /// + /// Disable this light so it is no longer taken into account + /// when rendering objects in the future. + /// + /// \see enable + /// + //////////////////////////////////////////////////////////// + void disable(); + + //////////////////////////////////////////////////////////// + /// \brief Check whether this light is enabled + /// + /// \return true if this light is enabled + /// + /// \see enable, disable + /// + //////////////////////////////////////////////////////////// + bool isEnabled(); + + //////////////////////////////////////////////////////////// + /// \brief Get the maximum number of lights supported + /// + /// This maximum number of lights supported is defined by + /// the graphics driver. A common value on old as well as new + /// hardware is 8. + /// If you require more light sources than is supported + /// by the fixed function pipeline, you will need to resort to + /// using shaders. + /// + /// \return Maximum number of lights supported + /// + //////////////////////////////////////////////////////////// + static unsigned int getMaximumLights(); + + //////////////////////////////////////////////////////////// + /// \brief Enable lighting for objects that are going to be rendered + /// + /// \see disableLighting + /// + //////////////////////////////////////////////////////////// + static void enableLighting(); + + //////////////////////////////////////////////////////////// + /// \brief Disable lighting for objects that are going to be rendered + /// + /// \see enableLighting + /// + //////////////////////////////////////////////////////////// + static void disableLighting(); + + //////////////////////////////////////////////////////////// + /// \brief Check whether lighting is enabled + /// + /// \return true if lighting is enabled + /// + /// \see enableLighting, disableLighting + /// + //////////////////////////////////////////////////////////// + static bool isLightingEnabled(); + + //////////////////////////////////////////////////////////// + /// \brief Check whether shader lighting is supported + /// + /// \return true if shader lighting is supported + /// + //////////////////////////////////////////////////////////// + static bool hasShaderLighting(); + + //////////////////////////////////////////////////////////// + /// \brief Increase the lighting reference count + /// + /// This function is meant for internal use only. + /// + //////////////////////////////////////////////////////////// + static void increaseLightReferences(); + + //////////////////////////////////////////////////////////// + /// \brief Decrease the lighting reference count + /// + /// This function is meant for internal use only. + /// + //////////////////////////////////////////////////////////// + static void decreaseLightReferences(); + + //////////////////////////////////////////////////////////// + /// \brief Add lighting data to the given shader + /// + /// \param shader Shader to add the lighting data to + /// + //////////////////////////////////////////////////////////// + static void addLightsToShader(const Shader& shader); + +private : + + //////////////////////////////////////////////////////////// + /// \brief Get a free identifier for this light + /// + //////////////////////////////////////////////////////////// + void getId(); + + //////////////////////////////////////////////////////////// + /// \brief Set that the lighting data needs to be re-uploaded + /// + //////////////////////////////////////////////////////////// + static void setNeedUniformUpload(); + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + int m_light; ///< Internal light identifier + Vector3f m_position; ///< Position/direction of the light + bool m_directional; ///< Whether the light is a directional light + Color m_color; ///< Color of the light + float m_ambientIntensity; ///< Ambient intensity of the light + float m_diffuseIntensity; ///< Diffuse intensity of the light + float m_specularIntensity; ///< Specular intensity of the light + float m_constantAttenuation; ///< Constant attenuation used during lighting computations + float m_linearAttenuation; ///< Linear attenuation used during lighting computations + float m_quadraticAttenuation; ///< Quadratic attenuation used during lighting computations + bool m_enabled; ///< Whether the light is enabled + mutable std::string m_shaderElement; ///< Cached string containing the element used to access lighting data in a shader +}; + +} // namespace cpp3ds + + +#endif // CPP3DS_LIGHT_HPP + +//////////////////////////////////////////////////////////// +/// \class cpp3ds::Light +/// \ingroup graphics +/// +/// cpp3ds::Light defines a light source in the 3D scene. +/// Without lighting, 3D objects will not be interpreted as +/// such because they lack shading, the difference in color +/// we perceive because of different light intensities. +/// +/// cpp3ds::Light supports 2 different types of lights, positional +/// i.e. point lights and directional lights. +/// +/// Positional light sources have a fixed position within a 3D +/// scene. They emit light in all directions in a sphere outwards +/// from their position. Because of this, the virtual light rays +/// do not travel parallel to each other. This is important to +/// remember when using shadowing techniques. An example of a +/// positional light source would be a simple light bulb. +/// +/// Directional light sources do not have a position. +/// They are defined by the \e direction in which they emit light +/// within the scene, hence their name. They can be seen as positioned +/// infinitely far away from the scene. In contrast to positional +/// light sources, the virtual light rays they emit are all +/// parallel to one another. The best example of a directional +/// light source would be the sun. Although it is a point light +/// at larger scales, once its emitted rays arrive on earth +/// they can be considered to be parallel. The most common use +/// of directional light sources is for outdoor lighting for +/// exactly this reason. +/// +/// As with lights in real life, an cpp3ds::Light emits light of a +/// certain color and with certain properties. The "color" of +/// the light which is emitted is known as its diffuse color. +/// This determines what color an object will be rendered with +/// taking into consideration its own color and the amount +/// of light it is receiving. In the case an object is not +/// illuminated at all, it is still illuminated by a kind of +/// "global" light source known as the ambient light source. +/// The ambient color determines what the object will look like +/// when it is not illuminated by the light source. If the +/// object should not be visible in this case, the ambient +/// color can be set to black. The specular color of a light +/// source determines what highlights on an object will look +/// like. Take a shiny ball as an example: when a high intensity +/// light is shined directly at it, a spot (usually the color +/// of the light itself) can be seen at a certain point on +/// the ball where all of the incident light rays are reflected +/// into the eye. The specular color is normally set to the +/// same hue as the diffuse color although with a much higher +/// intensity depending on the effect to be achieved. +/// +/// As with light in real life, the intensity of the light +/// decreases with increasing distance from the source. This +/// can be modelled in cpp3ds::Light as well using the provided +/// attenuation factors. +/// When the graphics hardware lights a vertex, it determines +/// how much of the source intensity arrives at the vertex +/// using the following formula: +/// +/// factor = 1.f / (constant + linear * d + quadratic * d * d) +/// +/// \li \e d is the distance between the light source and the vertex +/// \li \e constant is the constant attenuation factor +/// \li \e linear is the linear attenuation factor +/// \li \e quadratic is the quadratic attenuation factor +/// +/// Using that factor it can modify the incoming light color +/// to simulate attenuation. +/// +/// To use lights, lighting has to be enabled globally and +/// each light to be used has to be enabled. Objects that are +/// drawn are only affected by enabled lights and only when +/// lighting is enabled. To selectively light certain objects +/// with certain lights, just enable and disable them accordingly. +/// +/// All objects to be lit need to have vertex normals available. +/// If the normals are not set properly, lighting will not function +/// as intended. The various cpp3ds::Polyhedron classes have methods +/// of specifying/generating the required normals. 2D objects have +/// their normals automatically set to point in the correct direction. +/// +/// It is important to note that the position of the light is +/// affected by the current transform on the modelview matrix +/// stack when it is set. This means that when using +/// a moving camera and moving light source, the camera should +/// be updated using setView \e before setting the position of the +/// light to its new value every frame. Failure to do so will +/// result in wrong light positions. +/// +/// A technical detail to keep in mind is that cpp3ds::Light makes +/// use of the fixed-function OpenGL lighting functionality. +/// This means that: +/// \li The number of light sources you can create with cpp3ds::Light is limited +/// \li Lighting is calculated per vertex and not per fragment +/// \li Anything other than the Blinn-Phong shading model is not supported +/// If you want to overcome these limitations, you will have to +/// resort to using shaders to perform your own lighting computation. +/// +/// Usage example: +/// \code +/// cpp3ds::RenderWindow window; +/// +/// // Declare a light +/// cpp3ds::Light light; +/// +/// Set the light's properties +/// light.setDirectional(false); +/// light.setAmbientColor(cpp3ds::Color(50, 50, 50)); +/// light.setLinearAttenuation(0.002f); +/// +/// // Update the camera with its new position if needed +/// window.setView(camera); +/// +/// // Set the light's position +/// light.setPosition(lightPosition); +/// +/// // Enable lighting globally +/// cpp3ds::Light::enableLighting(); +/// +/// // Enable the light for the objects which should be lit by the light +/// light.enable(); +/// +/// // Render lit stuff +/// window.draw(litModel); +/// +/// // Disable the light for the objects which should not be lit the light +/// light.disable(); +/// +/// // Render unlit stuff +/// window.draw(unlitModel); +/// +/// // Disable lighting globally for certain objects e.g. text, GUIs +/// // They will be drawn using their full color intensity +/// cpp3ds::Light::disableLighting(); +/// +/// // Render GUI +/// window.draw(theGUI); +/// \endcode +/// +//////////////////////////////////////////////////////////// diff --git a/include/cpp3ds/Graphics/Model.hpp b/include/cpp3ds/Graphics/Model.hpp new file mode 100644 index 0000000..1915f1a --- /dev/null +++ b/include/cpp3ds/Graphics/Model.hpp @@ -0,0 +1,206 @@ +#ifndef CPP3DS_MODEL_HPP +#define CPP3DS_MODEL_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace cpp3ds +{ +//////////////////////////////////////////////////////////// +/// \brief Base class for 3D models +/// +//////////////////////////////////////////////////////////// +class Model : public Polyhedron +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Virtual destructor + /// + //////////////////////////////////////////////////////////// + virtual ~Model(); + + //////////////////////////////////////////////////////////// + /// \brief Get the total number of faces of the model + /// + /// \return Number of faces of the model + /// + /// \see getFace, addFace, clearFaces + /// + //////////////////////////////////////////////////////////// + virtual unsigned int getFaceCount() const; + + //////////////////////////////////////////////////////////// + /// \brief Get a face of the model + /// + /// The result is undefined if \a index is out of the valid range. + /// + /// \param index Index of the face to get, in range [0 .. getFaceCount() - 1] + /// + /// \return Index-th face of the model + /// + /// \see getFaceCount, addFace, clearFaces + /// + //////////////////////////////////////////////////////////// + virtual Face getFace(unsigned int index) const; + +protected : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + //////////////////////////////////////////////////////////// + Model(); + + //////////////////////////////////////////////////////////// + /// \brief Add a vertex to the model + /// + /// Add a vertex to the model that you can use later + /// during face specification. Vertices are referenced + /// by their index, so make sure to add them in the + /// correct order. + /// + /// \param vertex Vertex to add + /// + /// \see setVertex, getVertex, getVertexCount + /// + //////////////////////////////////////////////////////////// + void addVertex(const Vertex& vertex); + + //////////////////////////////////////////////////////////// + /// \brief Set a vertex at the given index to a new value + /// + /// Set the vertex at the given index to a new value. + /// This can be useful when the geometry of the model + /// changes e.g. when animating it or applying other + /// techniques that do not change the face topology. + /// + /// The result is undefined if \a index is out of the valid range. + /// + /// \param index Index of the vertex to set + /// \param vertex New vertex data + /// + /// \see addVertex, getVertex, getVertexCount + /// + //////////////////////////////////////////////////////////// + void setVertex(unsigned int index, const Vertex& vertex); + + //////////////////////////////////////////////////////////// + /// \brief Get a vertex at the given index + /// + /// Get the vertex at the given index. + /// + /// The result is undefined if \a index is out of the valid range. + /// + /// \param index Index of the vertex to get + /// + /// \return Index-th vertex of the model + /// + /// \see addVertex, setVertex, getVertexCount + /// + //////////////////////////////////////////////////////////// + const Vertex& getVertex(unsigned int index) const; + + //////////////////////////////////////////////////////////// + /// \brief Get the number of vertices in the model + /// + /// \return Number of vertices in the model + /// + /// \see addVertex, setVertex, getVertex + /// + //////////////////////////////////////////////////////////// + unsigned int getVertexCount() const; + + //////////////////////////////////////////////////////////// + /// \brief Add a face to the model + /// + /// Add a triangular face to the model. The vertex indices + /// to be provided are based on the order in which the + /// vertices were added using addVertex. By default + /// back-facing polygons are culled by the graphics + /// hardware, and as such, specify all front-facing faces + /// with counter-clockwise winding. + /// + /// The result is undefined if any indices are out of the valid range. + /// + /// \param index0 Index of the first vertex + /// \param index1 Index of the second vertex + /// \param index2 Index of the third vertex + /// + /// \see getFace, getFaceCount, clearFaces + /// + //////////////////////////////////////////////////////////// + void addFace(unsigned int index0, unsigned int index1, unsigned int index2); + + //////////////////////////////////////////////////////////// + /// \brief Clear the face data + /// + /// \see getFace, getFaceCount, addFace + /// + //////////////////////////////////////////////////////////// + void clearFaces(); + +private : + + //////////////////////////////////////////////////////////// + /// \brief Struct containing the indices that belong to a face + /// + //////////////////////////////////////////////////////////// + struct FaceIndices + { + unsigned int index0; ///< First vertex index + unsigned int index1; ///< Second vertex index + unsigned int index2; ///< Third vertex index + }; + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + std::vector m_vertices; ///< Vertex data + std::vector m_faces; ///< Face data +}; + +} // namespace cpp3ds + + +#endif // CPP3DS_MODEL_HPP + + +//////////////////////////////////////////////////////////// +/// \class cpp3ds::Model +/// \ingroup graphics +/// +/// cpp3ds::Model is a drawable class that acts as an interface +/// between a user-defined class handling the application-specific +/// model data and cpp3ds. Loading of the model data is handled +/// by user code and geometry specification is done using +/// the methods provided by cpp3ds::Model. +/// Once the model is loaded, it can be displayed on a +/// render target like any other drawable. +/// +/// This class inherits all the functions of cpp3ds::Transformable +/// (position, rotation, scale, bounds, ...) as well as the +/// functions of cpp3ds::Polyhedron (color, texture, ...). +/// +/// It is important to keep in mind that while specifying faces, +/// faces with clockwise winding (vertices specified in clockwise +/// order from the perspective of the viewer) are culled by default. +/// If you want a face to be facing the "outside" of the polyhedron, +/// specify its vertices in counter-clockwise order. +/// +/// If you want to light your scene, you will either need to +/// specify the vertex normal data yourself, or if you want to +/// automatically generate per-face normals you can call +/// generateNormals() after you are done specifying the faces. +/// +/// After loading the model or modifying geometry data in any +/// way, call the update method to synchronize the internal +/// data structures with the data you specified. +/// +/// \see cpp3ds::Polyhedron +/// +//////////////////////////////////////////////////////////// diff --git a/include/cpp3ds/Graphics/Polyhedron.hpp b/include/cpp3ds/Graphics/Polyhedron.hpp new file mode 100644 index 0000000..0255ac0 --- /dev/null +++ b/include/cpp3ds/Graphics/Polyhedron.hpp @@ -0,0 +1,241 @@ +#ifndef CPP3DS_POLYHEDRON_HPP +#define CPP3DS_POLYHEDRON_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include + + +namespace cpp3ds +{ +//////////////////////////////////////////////////////////// +/// \brief Base class for textured polyhedra +/// +//////////////////////////////////////////////////////////// +class Polyhedron : public Drawable, public Transformable +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Holds the 3 vertices that make up a face + /// + //////////////////////////////////////////////////////////// + struct Face + { + Vertex v0; ///< The first vertex of the face + Vertex v1; ///< The second vertex of the face + Vertex v2; ///< The third vertex of the face + }; + + //////////////////////////////////////////////////////////// + /// \brief Virtual destructor + /// + //////////////////////////////////////////////////////////// + virtual ~Polyhedron(); + + //////////////////////////////////////////////////////////// + /// \brief Set the color of the polyhedron + /// + /// This color is modulated (multiplied) with the polyhedron's + /// texture if any. It can be used to colorize the polyhedron, + /// or change its global opacity. + /// By default, the polyhedron's color is opaque white. + /// + /// \param color New color of the polyhedron + /// + /// \see getColor + /// + //////////////////////////////////////////////////////////// + void setColor(const Color& color); + + //////////////////////////////////////////////////////////// + /// \brief Get the color of the polyhedron + /// + /// \return Color of the polyhedron + /// + /// \see setColor + /// + //////////////////////////////////////////////////////////// + const Color& getColor() const; + + //////////////////////////////////////////////////////////// + /// \brief Change the source texture of the polyhedron + /// + /// The \a texture argument refers to a texture that must + /// exist as long as the polyhedron uses it. Indeed, the polyhedron + /// doesn't store its own copy of the texture, but rather keeps + /// a pointer to the one that you passed to this function. + /// If the source texture is destroyed and the polyhedron tries to + /// use it, the behaviour is undefined. + /// \a texture can be NULL to disable texturing. + /// + /// \param texture New texture + /// + /// \see getTexture + /// + //////////////////////////////////////////////////////////// + void setTexture(const Texture* texture); + + //////////////////////////////////////////////////////////// + /// \brief Get the source texture of the polyhedron + /// + /// If the polyhedron has no source texture, a NULL pointer is returned. + /// The returned pointer is const, which means that you can't + /// modify the texture when you retrieve it with this function. + /// + /// \return Pointer to the polyhedron's texture + /// + /// \see setTexture + /// + //////////////////////////////////////////////////////////// + const Texture* getTexture() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the total number of faces of the polyhedron + /// + /// \return Number of faces of the polyhedron + /// + /// \see getFace + /// + //////////////////////////////////////////////////////////// + virtual unsigned int getFaceCount() const = 0; + + //////////////////////////////////////////////////////////// + /// \brief Get a face of the polyhedron + /// + /// The result is undefined if \a index is out of the valid range. + /// + /// \param index Index of the face to get, in range [0 .. getFaceCount() - 1] + /// + /// \return Index-th face of the polyhedron + /// + /// \see getFaceCount + /// + //////////////////////////////////////////////////////////// + virtual Face getFace(unsigned int index) const = 0; + + //////////////////////////////////////////////////////////// + /// \brief Get the local bounding box of the entity + /// + /// The returned box is in local coordinates, which means + /// that it ignores the transformations (translation, rotation, + /// scale, ...) that are applied to the entity. + /// In other words, this function returns the bounds of the + /// entity in the entity's coordinate system. + /// + /// \return Local bounding box of the entity + /// + //////////////////////////////////////////////////////////// + FloatBox getLocalBounds() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the global bounding box of the entity + /// + /// The returned box is in global coordinates, which means + /// that it takes in account the transformations (translation, + /// rotation, scale, ...) that are applied to the entity. + /// In other words, this function returns the bounds of the + /// object in the global 3D world's coordinate system. + /// + /// \return Global bounding box of the entity + /// + //////////////////////////////////////////////////////////// + FloatBox getGlobalBounds() const; + + //////////////////////////////////////////////////////////// + /// \brief Generate normals using face data + /// + /// In the case that the input data doesn't contain + /// vertex normals, this method will generate them + /// through a simple cross-product using 2 edges of a face. + /// + /// This will generate the same normal for all 3 vertices + /// of a face, so if smooth lighting or per-pixel lighting + /// is required, you still need to specify your own normal + /// data through other means. + /// + //////////////////////////////////////////////////////////// + virtual void generateNormals(); + +protected : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + //////////////////////////////////////////////////////////// + Polyhedron(); + + //////////////////////////////////////////////////////////// + /// \brief Recompute the internal geometry of the polyhedron + /// + /// This function must be called by the derived class everytime + /// the polyhedron's faces change (ie. the result of either + /// getFaceCount or getFace is different or the vertex data + /// has been modified). + /// + //////////////////////////////////////////////////////////// + void update() const; + +private : + + //////////////////////////////////////////////////////////// + /// \brief Draw the polyhedron to a render target + /// + /// \param target Render target to draw to + /// \param states Current render states + /// + //////////////////////////////////////////////////////////// + virtual void draw(RenderTarget& target, RenderStates states) const; + + //////////////////////////////////////////////////////////// + /// \brief Update the vertices' color + /// + //////////////////////////////////////////////////////////// + void updateColors(); + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + const Texture* m_texture; ///< Texture of the polyhedron + Color m_color; ///< Color + mutable VertexContainer m_vertices; ///< Vertex array containing the geometry + mutable FloatBox m_insideBounds; ///< Bounding rectangle of the inside (fill) +}; + +} // namespace cpp3ds + + +#endif // CPP3DS_POLYHEDRON_HPP + + +//////////////////////////////////////////////////////////// +/// \class cpp3ds::Polyhedron +/// \ingroup graphics +/// +/// cpp3ds::Polyhedron is a drawable class that allows to define and +/// display a custom polyhedron on a render target. +/// It's only an abstract base, it needs to be specialized for +/// concrete types of polyhedra (sphere, cuboid, convex polyhedron, ...). +/// +/// In addition to the attributes provided by the specialized +/// polyhedron classes, a shape always has the following attributes: +/// \li a texture +/// \li a color +/// +/// Each feature is optional, and can be disabled easily: +/// \li the texture can be null +/// \li the color can be cpp3ds::Color::Transparent +/// +/// You can write your own derived polyhedron class, there are only +/// two virtual functions to override: +/// \li getFaceCount must return the number of faces in the polyhedron +/// \li getFace must return the faces of the polyhedron +/// +/// \see cpp3ds::Cuboid, cpp3ds::SphericalPolyhedron, cpp3ds::ConvexPolyhedron, cpp3ds::Transformable +/// +//////////////////////////////////////////////////////////// diff --git a/include/cpp3ds/Graphics/PrimitiveType.hpp b/include/cpp3ds/Graphics/PrimitiveType.hpp index 7daacc7..3e360d2 100644 --- a/include/cpp3ds/Graphics/PrimitiveType.hpp +++ b/include/cpp3ds/Graphics/PrimitiveType.hpp @@ -38,13 +38,9 @@ namespace cpp3ds //////////////////////////////////////////////////////////// enum PrimitiveType { - Points, ///< List of individual points - Lines, ///< List of individual lines - LinesStrip, ///< List of connected lines, a point uses the previous point to form a line - Triangles, ///< List of individual triangles + Triangles, ///< List of individual triangles TrianglesStrip, ///< List of connected triangles, a point uses the two previous points to form a triangle TrianglesFan, ///< List of connected triangles, a point uses the common center and the previous point to form a triangle - Quads ///< List of individual quads (deprecated, don't work with OpenGL ES) }; } diff --git a/include/cpp3ds/Graphics/Rect.hpp b/include/cpp3ds/Graphics/Rect.hpp index 793da71..fd49fb6 100644 --- a/include/cpp3ds/Graphics/Rect.hpp +++ b/include/cpp3ds/Graphics/Rect.hpp @@ -34,6 +34,10 @@ namespace cpp3ds { + +template +class Box; + //////////////////////////////////////////////////////////// /// \brief Utility class for manipulating 2D axis aligned rectangles /// @@ -78,6 +82,14 @@ public : //////////////////////////////////////////////////////////// Rect(const Vector2& position, const Vector2& size); + //////////////////////////////////////////////////////////// + /// \brief Construct the rectangle from a box, discarding front and depth + /// + /// \param box Box to convert + /// + //////////////////////////////////////////////////////////// + Rect(const Box& box); + //////////////////////////////////////////////////////////// /// \brief Construct the rectangle from another type of rectangle /// diff --git a/include/cpp3ds/Graphics/Rect.inl b/include/cpp3ds/Graphics/Rect.inl index 253581d..b2a3599 100644 --- a/include/cpp3ds/Graphics/Rect.inl +++ b/include/cpp3ds/Graphics/Rect.inl @@ -59,6 +59,17 @@ height(size.y) } +//////////////////////////////////////////////////////////// +template +Rect::Rect(const Box& box) : +left (static_cast(box.left)), +top (static_cast(box.top)), +width (static_cast(box.width)), +height(static_cast(box.height)) +{ +} + + //////////////////////////////////////////////////////////// template template diff --git a/include/cpp3ds/Graphics/RectangleShape.hpp b/include/cpp3ds/Graphics/RectangleShape.hpp index d4dc449..3fd7200 100644 --- a/include/cpp3ds/Graphics/RectangleShape.hpp +++ b/include/cpp3ds/Graphics/RectangleShape.hpp @@ -91,7 +91,7 @@ public : /// \return index-th point of the shape /// //////////////////////////////////////////////////////////// - virtual Vector2f getPoint(unsigned int index) const; + virtual Vector3f getPoint(unsigned int index) const; private : @@ -101,7 +101,7 @@ private : Vector2f m_size; ///< Size of the rectangle }; -} +} // namespace cpp3ds #endif diff --git a/include/cpp3ds/Graphics/RenderTarget.hpp b/include/cpp3ds/Graphics/RenderTarget.hpp index 1344daf..c6229e9 100644 --- a/include/cpp3ds/Graphics/RenderTarget.hpp +++ b/include/cpp3ds/Graphics/RenderTarget.hpp @@ -34,11 +34,13 @@ #include #include #include +#include namespace cpp3ds { class Drawable; +class VertexBuffer; //////////////////////////////////////////////////////////// /// \brief Base class for all render targets (window, texture, ...) @@ -65,11 +67,24 @@ public : //////////////////////////////////////////////////////////// void clear(const Color& color = Color(0, 0, 0, 255)); + //////////////////////////////////////////////////////////// + /// \brief Enable or disable depth testing + /// + /// Depth testing removes the need to draw in back to front + /// order. The graphics hardware will make sure objects + /// that are positioned in front of other objects will + /// be seen no matter when they are drawn. + /// + /// \param enable True to enable, false to disable + /// + //////////////////////////////////////////////////////////// + void enableDepthTest(bool enable); + //////////////////////////////////////////////////////////// /// \brief Change the current active view /// - /// The view is like a 2D camera, it controls which part of - /// the 2D scene is visible, and how it is viewed in the + /// The view is like a camera, it controls which part of + /// the scene is visible, and how it is viewed in the /// render-target. /// The new view will affect everything that is drawn, until /// another view is set. @@ -84,7 +99,8 @@ public : /// \see getView, getDefaultView /// //////////////////////////////////////////////////////////// - void setView(const View& view); + template + void setView(const T& view); //////////////////////////////////////////////////////////// /// \brief Get the view currently in use in the render target @@ -193,7 +209,7 @@ public : /// \see mapPixelToCoords /// //////////////////////////////////////////////////////////// - Vector2i mapCoordsToPixel(const Vector2f& point) const; + Vector2i mapCoordsToPixel(const Vector3f& point) const; //////////////////////////////////////////////////////////// /// \brief Convert a point from world coordinates to target coordinates @@ -220,7 +236,7 @@ public : /// \see mapPixelToCoords /// //////////////////////////////////////////////////////////// - Vector2i mapCoordsToPixel(const Vector2f& point, const View& view) const; + Vector2i mapCoordsToPixel(const Vector3f& point, const View& view) const; //////////////////////////////////////////////////////////// /// \brief Draw a drawable object to the render-target @@ -231,6 +247,15 @@ public : //////////////////////////////////////////////////////////// void draw(const Drawable& drawable, const RenderStates& states = RenderStates::Default); + //////////////////////////////////////////////////////////// + /// \brief Draw a vertex buffer to the render-target + /// + /// \param buffer Vertex buffer to draw + /// \param states Render states to use for drawing + /// + //////////////////////////////////////////////////////////// + void draw(const VertexBuffer& buffer, const RenderStates& states = RenderStates::Default); + //////////////////////////////////////////////////////////// /// \brief Draw primitives defined by an array of vertices /// @@ -336,6 +361,11 @@ protected : //////////////////////////////////////////////////////////// void initialize(); + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + bool m_clearDepth; ///< Whether there is a depth buffer to clear + private: //////////////////////////////////////////////////////////// @@ -360,6 +390,12 @@ private: //////////////////////////////////////////////////////////// void applyTransform(const Transform& transform); + //////////////////////////////////////////////////////////// + /// \brief Apply the current view transform + /// + //////////////////////////////////////////////////////////// + void applyViewTransform(); + //////////////////////////////////////////////////////////// /// \brief Apply a new texture /// @@ -376,6 +412,14 @@ private: //////////////////////////////////////////////////////////// void applyShader(const Shader* shader); + //////////////////////////////////////////////////////////// + /// \brief Apply a new vertex buffer + /// + /// \param buffer Vertex buffer to apply + /// + //////////////////////////////////////////////////////////// + void applyVertexBuffer(const VertexBuffer* buffer); + //////////////////////////////////////////////////////////// /// \brief Activate the target for rendering /// @@ -390,6 +434,17 @@ private: //////////////////////////////////////////////////////////// virtual bool activate(bool active) = 0; + //////////////////////////////////////////////////////////// + /// \brief Try to set up the non-legacy rendering pipeline if available + /// + /// This function checks the GLSL version to see if version + /// 1.30 or greater is supported. If that is the case, it will + /// set up the default shader used for rendering that will + /// emulate the legacy pipeline using the non-legacy OpenGL API. + /// + //////////////////////////////////////////////////////////// + void setupNonLegacyPipeline(); + //////////////////////////////////////////////////////////// /// \brief Render states cache /// @@ -398,24 +453,39 @@ private: { enum {VertexCacheSize = 4}; - bool glStatesSet; ///< Are our internal GL states set yet? - bool viewChanged; ///< Has the current view changed since last draw? - BlendMode lastBlendMode; ///< Cached blending mode - Uint64 lastTextureId; ///< Cached texture - bool useVertexCache; ///< Did we previously use the vertex cache? - Vertex* vertexCache; ///< Pre-transformed vertices cache + bool glStatesSet; ///< Are our internal GL states set yet? + bool viewChanged; ///< Has the current view changed since last draw? + BlendMode lastBlendMode; ///< Cached blending mode + Uint64 lastTextureId; ///< Cached texture + Uint64 lastVertexBufferId; ///< Cached vertex buffer + bool useVertexCache; ///< Did we previously use the vertex cache? + Vertex* vertexCache; ///< Pre-transformed vertices cache }; + //////////////////////////////////////////////////////////// + // Types + //////////////////////////////////////////////////////////// + typedef std::map ArrayAgeCount; + //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - View m_defaultView; ///< Default view - View m_view; ///< Current view - StatesCache m_cache; ///< Render states cache + View m_defaultView; ///< Default view + View* m_view; ///< Current view + StatesCache m_cache; ///< Render states cache + bool m_depthTest; ///< Whether depth testing is enabled + Shader* m_defaultShader; ///< Default non-legacy shader, only created if supported + const Shader* m_currentNonLegacyShader; ///< Used during a draw call to set uniforms of the target shader + const Shader* m_lastNonLegacyShader; ///< Used during a draw call to check if shader changed since the last draw + Uint64 m_id; ///< Unique number that identifies the render target + ArrayAgeCount m_arrayAgeCount; ///< Map tracking the age of vertex array objects + IntRect m_previousViewport; ///< Cached viewport + Color m_previousClearColor; ///< Cached clear color }; -} +#include +} // namespace cpp3ds #endif diff --git a/include/cpp3ds/Graphics/RenderTarget.inl b/include/cpp3ds/Graphics/RenderTarget.inl new file mode 100644 index 0000000..1b509dc --- /dev/null +++ b/include/cpp3ds/Graphics/RenderTarget.inl @@ -0,0 +1,17 @@ + + +//////////////////////////////////////////////////////////// +template +void RenderTarget::setView(const T& view) +{ + if (&view != m_view) + { + delete m_view; + m_view = new T(view); + } + + m_cache.viewChanged = true; + + // Update the modelview matrix for any lighting updates + applyViewTransform(); +} diff --git a/include/cpp3ds/Graphics/Shader.hpp b/include/cpp3ds/Graphics/Shader.hpp index f828e0d..1e67845 100644 --- a/include/cpp3ds/Graphics/Shader.hpp +++ b/include/cpp3ds/Graphics/Shader.hpp @@ -45,6 +45,7 @@ namespace cpp3ds { class InputStream; class Texture; +class VertexBuffer; //////////////////////////////////////////////////////////// /// \brief Shader class (vertex and fragment) @@ -99,18 +100,18 @@ public : ~Shader(); //////////////////////////////////////////////////////////// - /// \brief Load either the vertex or fragment shader from a file + /// \brief Load either the vertex, fragment or geometry shader from a file /// - /// This function loads a single shader, either vertex or - /// fragment, identified by the second argument. + /// This function loads a single shader, either vertex, + /// fragment or geometry, identified by the second argument. /// The source must be a text file containing a valid /// shader in GLSL language. GLSL is a C-like language /// dedicated to OpenGL shaders; you'll probably need to /// read a good documentation for it before writing your /// own shaders. /// - /// \param filename Path of the vertex or fragment shader file to load - /// \param type Type of shader (vertex or fragment) + /// \param filename Path of the vertex, fragment or geometry shader file to load + /// \param type Type of shader (vertex, fragment or geometry) /// /// \return True if loading succeeded, false if it failed /// @@ -120,10 +121,11 @@ public : bool loadFromFile(const std::string& filename, Type type); //////////////////////////////////////////////////////////// - /// \brief Load both the vertex and fragment shaders from files + /// \brief Load both the vertex and fragment shaders and optionally a geometry shader from files /// - /// This function loads both the vertex and the fragment - /// shaders. If one of them fails to load, the shader is left + /// This function loads both the vertex and the + /// fragment shaders and optionally a geometry shader. + /// If one of them fails to load, the shader is left /// empty (the valid shader is unloaded). /// The sources must be text files containing valid shaders /// in GLSL language. GLSL is a C-like language dedicated to @@ -132,26 +134,27 @@ public : /// /// \param vertexShaderFilename Path of the vertex shader file to load /// \param fragmentShaderFilename Path of the fragment shader file to load + /// \param geometryShaderFilename Path of the geometry shader file to load (optional) /// /// \return True if loading succeeded, false if it failed /// /// \see loadFromMemory, loadFromStream /// //////////////////////////////////////////////////////////// - bool loadFromFile(const std::string& vertexShaderFilename, const std::string& fragmentShaderFilename); + bool loadFromFile(const std::string& vertexShaderFilename, const std::string& fragmentShaderFilename, const std::string& geometryShaderFilename = ""); - //////////////////////////////////////////////////////////// - /// \brief Load either the vertex or fragment shader from a source code in memory + //////////////////////////////////////////////////////////// + /// \brief Load either the vertex, fragment or geometry shader from a source code in memory /// - /// This function loads a single shader, either vertex or - /// fragment, identified by the second argument. + /// This function loads a single shader, either vertex, + /// fragment or geometry, identified by the second argument. /// The source code must be a valid shader in GLSL language. /// GLSL is a C-like language dedicated to OpenGL shaders; /// you'll probably need to read a good documentation for /// it before writing your own shaders. /// /// \param shader String containing the source code of the shader - /// \param type Type of shader (vertex or fragment) + /// \param type Type of shader (vertex, fragment or geometry) /// /// \return True if loading succeeded, false if it failed /// @@ -161,10 +164,11 @@ public : bool loadFromMemory(const std::string& shader, Type type); //////////////////////////////////////////////////////////// - /// \brief Load both the vertex and fragment shaders from source codes in memory + /// \brief Load both the vertex and fragment shaders and optionally a geometry shader from source codes in memory /// - /// This function loads both the vertex and the fragment - /// shaders. If one of them fails to load, the shader is left + /// This function loads both the vertex and the + /// fragment shaders and optionally a geometry shader. + /// If one of them fails to load, the shader is left /// empty (the valid shader is unloaded). /// The sources must be valid shaders in GLSL language. GLSL is /// a C-like language dedicated to OpenGL shaders; you'll @@ -173,30 +177,31 @@ public : /// /// \param vertexShader String containing the source code of the vertex shader /// \param fragmentShader String containing the source code of the fragment shader + /// \param geometryShader String containing the source code of the geometry shader (optional) /// /// \return True if loading succeeded, false if it failed /// /// \see loadFromFile, loadFromStream /// //////////////////////////////////////////////////////////// - bool loadFromMemory(const std::string& vertexShader, const std::string& fragmentShader); + bool loadFromMemory(const std::string& vertexShader, const std::string& fragmentShader, const std::string& geometryShader = ""); bool loadFromResource(const std::string& shader, Type type, bool compiled = true); bool loadBinary(const Uint8* data, const Uint32 size, Type type); //////////////////////////////////////////////////////////// - /// \brief Load either the vertex or fragment shader from a custom stream + /// \brief Load either the vertex, fragment or geometry shader from a custom stream /// - /// This function loads a single shader, either vertex or - /// fragment, identified by the second argument. + /// This function loads a single shader, either vertex, + /// fragment or geometry, identified by the second argument. /// The source code must be a valid shader in GLSL language. /// GLSL is a C-like language dedicated to OpenGL shaders; /// you'll probably need to read a good documentation for it /// before writing your own shaders. /// /// \param stream Source stream to read from - /// \param type Type of shader (vertex or fragment) + /// \param type Type of shader (vertex, fragment or geometry) /// /// \return True if loading succeeded, false if it failed /// @@ -226,6 +231,160 @@ public : //////////////////////////////////////////////////////////// bool loadFromStream(InputStream& vertexShaderStream, InputStream& fragmentShaderStream); + //////////////////////////////////////////////////////////// + /// \brief Load the vertex, fragment and geometry shaders from custom streams + /// + /// This function loads the vertex, fragment and geometry + /// shaders. If one of them fails to load, the shader is left + /// empty (the valid shader is unloaded). + /// The source codes must be valid shaders in GLSL language. + /// GLSL is a C-like language dedicated to OpenGL shaders; + /// you'll probably need to read a good documentation for + /// it before writing your own shaders. + /// + /// \param vertexShaderStream Source stream to read the vertex shader from + /// \param fragmentShaderStream Source stream to read the fragment shader from + /// \param geometryShaderStream Source stream to read the geometry shader from + /// + /// \return True if loading succeeded, false if it failed + /// + /// \see loadFromFile, loadFromMemory + /// + //////////////////////////////////////////////////////////// + bool loadFromStream(InputStream& vertexShaderStream, InputStream& fragmentShaderStream, InputStream& geometryShaderStream); + + //////////////////////////////////////////////////////////// + /// \brief Change a int parameter of the shader + /// + /// \a name is the name of the variable to change in the shader. + /// The corresponding parameter in the shader must be a int + /// (int GLSL type). + /// + /// Example: + /// \code + /// uniform int myparam; // this is the variable in the shader + /// \endcode + /// \code + /// shader.setParameter("myparam", 5); + /// \endcode + /// + /// \param name Name of the parameter in the shader + /// \param x Value to assign + /// + //////////////////////////////////////////////////////////// + void setParameter(const std::string& name, int x) const; + + //////////////////////////////////////////////////////////// + /// \brief Change a 2-components int vector parameter of the shader + /// + /// \a name is the name of the variable to change in the shader. + /// The corresponding parameter in the shader must be a 2x1 int vector + /// (ivec2 GLSL type). + /// + /// Example: + /// \code + /// uniform ivec2 myparam; // this is the variable in the shader + /// \endcode + /// \code + /// shader.setParameter("myparam", 5, 6); + /// \endcode + /// + /// \param name Name of the parameter in the shader + /// \param x First component of the value to assign + /// \param y Second component of the value to assign + /// + //////////////////////////////////////////////////////////// + void setParameter(const std::string& name, int x, int y) const; + + //////////////////////////////////////////////////////////// + /// \brief Change a 3-components int vector parameter of the shader + /// + /// \a name is the name of the variable to change in the shader. + /// The corresponding parameter in the shader must be a 3x1 int vector + /// (ivec3 GLSL type). + /// + /// Example: + /// \code + /// uniform ivec3 myparam; // this is the variable in the shader + /// \endcode + /// \code + /// shader.setParameter("myparam", 5, 6, -8); + /// \endcode + /// + /// \param name Name of the parameter in the shader + /// \param x First component of the value to assign + /// \param y Second component of the value to assign + /// \param z Third component of the value to assign + /// + //////////////////////////////////////////////////////////// + void setParameter(const std::string& name, int x, int y, int z) const; + + //////////////////////////////////////////////////////////// + /// \brief Change a 4-components int vector parameter of the shader + /// + /// \a name is the name of the variable to change in the shader. + /// The corresponding parameter in the shader must be a 4x1 int vector + /// (ivec4 GLSL type). + /// + /// Example: + /// \code + /// uniform ivec4 myparam; // this is the variable in the shader + /// \endcode + /// \code + /// shader.setParameter("myparam", 5, 6, -8, 0); + /// \endcode + /// + /// \param name Name of the parameter in the shader + /// \param x First component of the value to assign + /// \param y Second component of the value to assign + /// \param z Third component of the value to assign + /// \param w Fourth component of the value to assign + /// + //////////////////////////////////////////////////////////// + void setParameter(const std::string& name, int x, int y, int z, int w) const; + + //////////////////////////////////////////////////////////// + /// \brief Change a 2-components int vector parameter of the shader + /// + /// \a name is the name of the variable to change in the shader. + /// The corresponding parameter in the shader must be a 2x1 int vector + /// (ivec2 GLSL type). + /// + /// Example: + /// \code + /// uniform ivec2 myparam; // this is the variable in the shader + /// \endcode + /// \code + /// shader.setParameter("myparam", sf::Vector2i(5, 6)); + /// \endcode + /// + /// \param name Name of the parameter in the shader + /// \param vector Vector to assign + /// + //////////////////////////////////////////////////////////// + void setParameter(const std::string& name, const Vector2i& vector) const; + + //////////////////////////////////////////////////////////// + /// \brief Change a 3-components int vector parameter of the shader + /// + /// \a name is the name of the variable to change in the shader. + /// The corresponding parameter in the shader must be a 3x1 int vector + /// (ivec3 GLSL type). + /// + /// Example: + /// \code + /// uniform ivec3 myparam; // this is the variable in the shader + /// \endcode + /// \code + /// shader.setParameter("myparam", sf::Vector3f(5, 6, -8)); + /// \endcode + /// + /// \param name Name of the parameter in the shader + /// \param vector Vector to assign + /// + //////////////////////////////////////////////////////////// + void setParameter(const std::string& name, const Vector3i& vector) const; + //////////////////////////////////////////////////////////// /// \brief Change a float parameter of the shader /// @@ -245,7 +404,7 @@ public : /// \param x Value to assign /// //////////////////////////////////////////////////////////// - void setParameter(const std::string& name, float x); + void setParameter(const std::string& name, float x) const; //////////////////////////////////////////////////////////// /// \brief Change a 2-components vector parameter of the shader @@ -267,7 +426,7 @@ public : /// \param y Second component of the value to assign /// //////////////////////////////////////////////////////////// - void setParameter(const std::string& name, float x, float y); + void setParameter(const std::string& name, float x, float y) const; //////////////////////////////////////////////////////////// /// \brief Change a 3-components vector parameter of the shader @@ -290,7 +449,7 @@ public : /// \param z Third component of the value to assign /// //////////////////////////////////////////////////////////// - void setParameter(const std::string& name, float x, float y, float z); + void setParameter(const std::string& name, float x, float y, float z) const; //////////////////////////////////////////////////////////// /// \brief Change a 4-components vector parameter of the shader @@ -314,7 +473,7 @@ public : /// \param w Fourth component of the value to assign /// //////////////////////////////////////////////////////////// - void setParameter(const std::string& name, float x, float y, float z, float w); + void setParameter(const std::string& name, float x, float y, float z, float w) const; //////////////////////////////////////////////////////////// /// \brief Change a 2-components vector parameter of the shader @@ -335,7 +494,7 @@ public : /// \param vector Vector to assign /// //////////////////////////////////////////////////////////// - void setParameter(const std::string& name, const Vector2f& vector); + void setParameter(const std::string& name, const Vector2f& vector) const; //////////////////////////////////////////////////////////// /// \brief Change a 3-components vector parameter of the shader @@ -356,7 +515,7 @@ public : /// \param vector Vector to assign /// //////////////////////////////////////////////////////////// - void setParameter(const std::string& name, const Vector3f& vector); + void setParameter(const std::string& name, const Vector3f& vector) const; //////////////////////////////////////////////////////////// /// \brief Change a color parameter of the shader @@ -383,7 +542,7 @@ public : /// \param color Color to assign /// //////////////////////////////////////////////////////////// - void setParameter(const std::string& name, const Color& color); + void setParameter(const std::string& name, const Color& color) const; //////////////////////////////////////////////////////////// /// \brief Change a matrix parameter of the shader @@ -406,7 +565,7 @@ public : /// \param transform Transform to assign /// //////////////////////////////////////////////////////////// - void setParameter(const std::string& name, const cpp3ds::Transform& transform); + void setParameter(const std::string& name, const Transform& transform) const; //////////////////////////////////////////////////////////// /// \brief Change a texture parameter of the shader @@ -438,7 +597,7 @@ public : /// \param texture Texture to assign /// //////////////////////////////////////////////////////////// - void setParameter(const std::string& name, const Texture& texture); + void setParameter(const std::string& name, const Texture& texture) const; //////////////////////////////////////////////////////////// /// \brief Change a texture parameter of the shader @@ -461,7 +620,7 @@ public : /// \param name Name of the texture in the shader /// //////////////////////////////////////////////////////////// - void setParameter(const std::string& name, CurrentTextureType); + void setParameter(const std::string& name, CurrentTextureType) const; //////////////////////////////////////////////////////////// /// \brief Get the underlying OpenGL handle of the shader. @@ -475,6 +634,107 @@ public : //////////////////////////////////////////////////////////// unsigned int getNativeHandle() const; + //////////////////////////////////////////////////////////// + /// \brief Bind a VertexBuffer to a uniform block in the shader + /// + /// This method maps a VertexBuffer to a uniform block in + /// the shader. It is assumed the block has layout (std140) + /// and only occupies a single binding. + /// + /// Example: + /// \code + /// uniform layout (std140) someBlock // this is the block in the shader + /// { + /// vec3 param1; // Aligned to vec4 because of std140 + /// vec4 param2; + /// }; // Make sure block only occupies a single binding (no arrays of blocks) + /// ... + /// vec3 someVariable = param1; + /// vec2 someOtherVariable = param2.xy; + /// \endcode + /// \code + /// sf::VertexBuffer buffer; + /// ... + /// // Set buffer data... + /// ... + /// shader.setBlock("someBlock", buffer); // Upload + /// ... + /// // Set buffer data again... + /// ... + /// shader.setBlock("someBlock", buffer); // Upload + /// \endcode + /// + /// \param name Name of the uniform block in the shader + /// \param buffer Buffer to bind to the uniform block + /// + //////////////////////////////////////////////////////////// + void setBlock(const std::string& name, const VertexBuffer& buffer) const; + + //////////////////////////////////////////////////////////// + /// \brief Get the location ID of a shader vertex attribute + /// + /// \param name Name of the vertex attribute to search + /// + /// \return Location ID of the vertex attribute, or -1 if not found + /// + //////////////////////////////////////////////////////////// + int getVertexAttributeLocation(const std::string& name) const; + + //////////////////////////////////////////////////////////// + /// \brief Set/Get whether the shader warns about missing variables + /// + /// When setting certain parameters is optional, you can use + /// this method to disable warnings about them not being set + /// in the shader. + /// + /// This will also return the previous warning setting for + /// easy resetting back to the previous state when required. + /// + /// \param warn true to enable warnings about missing variables, false to disable + /// + /// \return true if warnings were previously enabled, false if not + /// + //////////////////////////////////////////////////////////// + bool warnMissing(bool warn) const; + + //////////////////////////////////////////////////////////// + /// \brief Begin setting a parameter block + /// + /// When setting a lot of variables at a time on the + /// same shader, performance can be increased by batching + /// them together into a parameter block. That way, the + /// shader does not have to be activated and deactivated + /// every time a parameter is set which will increase + /// performance by reducing the number of OpenGL calls. + /// + /// When done setting a parameter block, don't forget + /// to call endParameterBlock. + /// + /// \code + /// sf::Shader shader; + /// ... + /// shader.beginParameterBlock(); + /// shader.setParameter("color1", sf::Color(255, 128, 0, 255)); + /// shader.setParameter("color2", sf::Color(255, 255, 128, 255)); + /// shader.setParameter("color3", sf::Color(0, 128, 255, 255)); + /// shader.setParameter("color4", sf::Color(0, 255, 0, 255)); + /// shader.endParameterBlock(); + /// ... + /// \endcode + /// + /// \see endParameterBlock + /// + //////////////////////////////////////////////////////////// + void beginParameterBlock() const; + + //////////////////////////////////////////////////////////// + /// \brief End setting a parameter block + /// + /// \see beginParameterBlock + /// + //////////////////////////////////////////////////////////// + void endParameterBlock() const; + //////////////////////////////////////////////////////////// /// \brief Bind a shader for rendering /// @@ -507,11 +767,94 @@ public : /// /// \return True if shaders are supported, false otherwise /// + /// \see getSupportedVersion, isGeometryShaderAvailable, isUniformBufferAvailable + /// //////////////////////////////////////////////////////////// static bool isAvailable(); + //////////////////////////////////////////////////////////// + /// \brief Tell whether or not the system supports geometry shaders + /// + /// This function should always be called before trying + /// to load a geometry shader. If it returns false, then + /// any attempt to load a geometry shader will fail. + /// + /// This will check for \e core support of geometry shaders, + /// \e not ARB or EXT support. As such, it can only return + /// true if OpenGL 3.2 or later is supported. This does + /// \e not mean you need to have a version 3.2 or later + /// context to use geometry shaders. 3.2 merely has to + /// be \e supported by the driver/hardware. + /// + /// The non-core functionality isn't exposed through the + /// Shader API. All required information such as input + /// primitive, output primitive and vertex count need + /// to be specified using the layout() GLSL syntax so that + /// a Shader object, when compiled, is self-contained. + /// + /// \return True if geometry shaders are supported, false otherwise + /// + /// \see isAvailable, getSupportedVersion + /// + //////////////////////////////////////////////////////////// + static bool isGeometryShaderAvailable(); + + //////////////////////////////////////////////////////////// + /// \brief Tell whether or not the system supports uniform buffers + /// + /// Uniform buffers are simple buffer objects, such as those + /// used in VertexBuffer. Uniform buffers however, store + /// shader uniform data instead of vertex data. They can + /// be used to upload large amounts of uniform data + /// with a single API call and that data can be shared + /// among multiple shaders as well. + /// + /// Because uniform buffers rely on shaders and buffer + /// objects being available, this will only return true + /// if Shader::isAvailable() and VertexBuffer::isAvailable() + /// both return true and uniform buffer support itself + /// is present. + /// + /// \return True if uniform buffers are supported, false otherwise + /// + /// \see isAvailable + /// + //////////////////////////////////////////////////////////// +// static bool isUniformBufferAvailable(); + + //////////////////////////////////////////////////////////// + /// \brief Get the string identifying the supported GLSL version + /// + /// In the desktop implementation (not ES), the string returned + /// is guaranteed to begin with the version number. In the ES + /// implementation, the returned string is prefixed with "ES ". + /// + /// \return std::string containing the supported GLSL version or an empty string if unsupported + /// + /// \see isAvailable + /// + //////////////////////////////////////////////////////////// + static std::string getSupportedVersion(); + + //////////////////////////////////////////////////////////// + /// \brief Get the maximum number of uniform components supported + /// + /// A uniform component is typically a single 32-bit data type + /// such as float or int. Vectors of those data types take up + /// the corresponding multiple of space, i.e. vec4 takes up + /// 4 floats worth of space and counts as 4 components. To + /// guarantee alignment, vec3 might also be packed to take up + /// the same amount of space as a vec4. + /// + /// \return Maximum number of uniform components supported + /// + //////////////////////////////////////////////////////////// + static unsigned int getMaximumUniformComponents(); + private : + friend class RenderTarget; + //////////////////////////////////////////////////////////// /// \brief Compile the shader(s) and create the program /// @@ -520,11 +863,12 @@ private : /// /// \param vertexShaderCode Source code of the vertex shader /// \param fragmentShaderCode Source code of the fragment shader + /// \param geometryShaderCode Source code of the geometry shader /// /// \return True on success, false if any error happened /// //////////////////////////////////////////////////////////// - bool compile(const char* vertexShaderCode, const char* fragmentShaderCode); + bool compile(const char* vertexShaderCode, const char* fragmentShaderCode, const char* geometryShaderCode); //////////////////////////////////////////////////////////// /// \brief Bind all the textures used by the shader @@ -543,30 +887,42 @@ private : /// \return Location ID of the parameter, or -1 if not found /// //////////////////////////////////////////////////////////// - int getParamLocation(const std::string& name); + int getParamLocation(const std::string& name) const; + + //////////////////////////////////////////////////////////// + /// \brief Get the binding ID of a shader uniform block + /// + /// \param name Name of the uniform block to search + /// + /// \return Binding ID of the uniform block, or -1 if not found + /// + //////////////////////////////////////////////////////////// +// int getBlockBinding(const std::string& name) const; //////////////////////////////////////////////////////////// // Types //////////////////////////////////////////////////////////// typedef std::map TextureTable; - typedef std::map ParamTable; + typedef std::map LocationTable; + typedef std::map BufferTable; //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - GLuint m_shaderProgram; ///< OpenGL identifier for the program - int m_currentTexture; ///< Location of the current texture in the shader - TextureTable m_textures; ///< Texture variables in the shader, mapped to their location - ParamTable m_params; ///< Parameters location cache - - #ifndef EMULATION - //shader structure - DVLB_s* dvlb; - shaderProgram_s shader; - #endif + GLuint m_shaderProgram; ///< OpenGL identifier for the program + mutable int m_currentTexture; ///< Location of the current texture in the shader + mutable TextureTable m_textures; ///< Texture variables in the shader, mapped to their location + mutable LocationTable m_params; ///< Parameters location cache + mutable LocationTable m_attributes; ///< Attributes location cache + mutable LocationTable m_blockBindings; ///< Block binding cache + mutable BufferTable m_boundBuffers; ///< Buffers bound to this shader + mutable bool m_warnMissing; ///< Whether to warn the user that variables could not be found. + Uint64 m_id; ///< Unique number that identifies the compiled and linked program + mutable bool m_parameterBlock; ///< Whether we are in a parameter block + mutable unsigned int m_blockProgram; ///< The program to restore after a parameter block }; -} +} // namespace cpp3ds #endif diff --git a/include/cpp3ds/Graphics/Shape.hpp b/include/cpp3ds/Graphics/Shape.hpp index 6bd2d3c..c9477e1 100644 --- a/include/cpp3ds/Graphics/Shape.hpp +++ b/include/cpp3ds/Graphics/Shape.hpp @@ -30,8 +30,8 @@ //////////////////////////////////////////////////////////// #include #include -#include -#include +#include +#include namespace cpp3ds @@ -209,7 +209,7 @@ public : /// \see getPointCount /// //////////////////////////////////////////////////////////// - virtual Vector2f getPoint(unsigned int index) const = 0; + virtual Vector3f getPoint(unsigned int index) const = 0; //////////////////////////////////////////////////////////// /// \brief Get the local bounding rectangle of the entity @@ -297,18 +297,18 @@ private : //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - const Texture* m_texture; ///< Texture of the shape - IntRect m_textureRect; ///< Rectangle defining the area of the source texture to display - Color m_fillColor; ///< Fill color - Color m_outlineColor; ///< Outline color - float m_outlineThickness; ///< Thickness of the shape's outline - VertexArray m_vertices; ///< Vertex array containing the fill geometry - VertexArray m_outlineVertices; ///< Vertex array containing the outline geometry - FloatRect m_insideBounds; ///< Bounding rectangle of the inside (fill) - FloatRect m_bounds; ///< Bounding rectangle of the whole shape (outline + fill) + const Texture* m_texture; ///< Texture of the shape + IntRect m_textureRect; ///< Rectangle defining the area of the source texture to display + Color m_fillColor; ///< Fill color + Color m_outlineColor; ///< Outline color + float m_outlineThickness; ///< Thickness of the shape's outline + VertexContainer m_vertices; ///< Vertex array containing the fill geometry + VertexContainer m_outlineVertices; ///< Vertex array containing the outline geometry + FloatRect m_insideBounds; ///< Bounding rectangle of the inside (fill) + FloatRect m_bounds; ///< Bounding rectangle of the whole shape (outline + fill) }; -} +} // namespace cpp3ds #endif diff --git a/include/cpp3ds/Graphics/SphericalPolyhedron.hpp b/include/cpp3ds/Graphics/SphericalPolyhedron.hpp new file mode 100644 index 0000000..388f065 --- /dev/null +++ b/include/cpp3ds/Graphics/SphericalPolyhedron.hpp @@ -0,0 +1,155 @@ +#ifndef CPP3DS_SPHERICALPOLYHEDRON_HPP +#define CPP3DS_SPHERICALPOLYHEDRON_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace cpp3ds +{ +//////////////////////////////////////////////////////////// +/// \brief Specialized polyhedron representing a spherical polyhedron +/// +//////////////////////////////////////////////////////////// +class SphericalPolyhedron : public Polyhedron +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates a spherical polyhedron positioned at (0, 0, 0) + /// with radius 0 and 5 subdivisions. + /// + /// \param radius Radius of the spherical polyhedron + /// \param subdivisions Number of times to subdivide faces of the base geometry + /// + //////////////////////////////////////////////////////////// + explicit SphericalPolyhedron(float radius = 0, unsigned int subdivisions = 5); + + //////////////////////////////////////////////////////////// + /// \brief Set the radius of the spherical polyhedron + /// + /// \param radius New radius of the spherical polyhedron + /// + /// \see getRadius + /// + //////////////////////////////////////////////////////////// + void setRadius(float radius); + + //////////////////////////////////////////////////////////// + /// \brief Get the radius of the spherical polyhedron + /// + /// \return Radius of the spherical polyhedron + /// + /// \see setRadius + /// + //////////////////////////////////////////////////////////// + float getRadius() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the number of times the base polyhedron is subdivided + /// + /// \param subdivisions Number of times the base polyhedron is subdivided + /// + /// \see getSubdivisions + /// + //////////////////////////////////////////////////////////// + void setSubdivisions(unsigned int subdivisions); + + //////////////////////////////////////////////////////////// + /// \brief Get the number of times the base polyhedron is subdivided + /// + /// \return The number of times the base polyhedron is subdivided + /// + /// \see setSubdivisions + /// + //////////////////////////////////////////////////////////// + unsigned int getSubdivisions() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the number of faces of the polyhedron + /// + /// \return Number of faces of the polyhedron + /// + /// \see setFaceCount + /// + //////////////////////////////////////////////////////////// + virtual unsigned int getFaceCount() const; + + //////////////////////////////////////////////////////////// + /// \brief Get a face of the polyhedron + /// + /// The result is undefined if \a index is out of the valid range. + /// + /// \param index Index of the face to get, in range [0 .. getFaceCount() - 1] + /// + /// \return Index-th face of the polyhedron + /// + //////////////////////////////////////////////////////////// + virtual Face getFace(unsigned int index) const; + +private : + + //////////////////////////////////////////////////////////// + /// \brief Construct the geometry from the base polyhedron and given subdivisions + /// + //////////////////////////////////////////////////////////// + void construct() const; + + //////////////////////////////////////////////////////////// + /// \brief Subdivide a face of the current geometry + /// + /// \param a First corner position + /// \param b Second corner position + /// \param c Third corner position + /// \param s Current subdivision + /// + //////////////////////////////////////////////////////////// + void subdivide(const Vector3f& a, const Vector3f& b, const Vector3f& c, unsigned int s) const; + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + float m_radius; ///< Radius of the spherical polyhedron + unsigned int m_subdivisions; ///< Radius of the spherical polyhedron + + mutable std::vector m_geometry; ///< Constructed geometry +}; + +} // namespace cpp3ds + + +#endif // CPP3DS_SPHERICALPOLYHEDRON_HPP + + +//////////////////////////////////////////////////////////// +/// \class cpp3ds::SphericalPolyhedron +/// \ingroup graphics +/// +/// This class inherits all the functions of cpp3ds::Transformable +/// (position, rotation, scale, bounds, ...) as well as the +/// functions of cpp3ds::Polyhedron (color, texture, ...). +/// +/// Usage example: +/// \code +/// cpp3ds::SphericalPolyhedron sphere; +/// sphere.setRadius(150); +/// sphere.setColor(cpp3ds::Color::Red); +/// sphere.setPosition(10, 20, 30); +/// ... +/// window.draw(sphere); +/// \endcode +/// +/// Since the graphics card can't draw perfect spheres, we have to +/// fake them through tessellation of a base icosahedron. The +/// "subdivisions" property of cpp3ds::SphericalPolyhedron defines how many +/// subdivisions to perform on the faces of the base primitive, +/// and therefore defines the quality of the sphere. +/// +/// \see cpp3ds::Polyhedron, cpp3ds::Cuboid, cpp3ds::ConvexPolyhedron +/// +//////////////////////////////////////////////////////////// diff --git a/include/cpp3ds/Graphics/Sprite.hpp b/include/cpp3ds/Graphics/Sprite.hpp index 596eaed..18ce3af 100644 --- a/include/cpp3ds/Graphics/Sprite.hpp +++ b/include/cpp3ds/Graphics/Sprite.hpp @@ -30,7 +30,7 @@ //////////////////////////////////////////////////////////// #include #include -#include +#include #include @@ -76,8 +76,6 @@ public : //////////////////////////////////////////////////////////// Sprite(const Texture& texture, const IntRect& rectangle); - ~Sprite(); - //////////////////////////////////////////////////////////// /// \brief Change the source texture of the sprite /// @@ -190,7 +188,7 @@ public : //////////////////////////////////////////////////////////// FloatRect getGlobalBounds() const; -private : +protected : //////////////////////////////////////////////////////////// /// \brief Draw the sprite to a render target @@ -201,6 +199,8 @@ private : //////////////////////////////////////////////////////////// virtual void draw(RenderTarget& target, RenderStates states) const; +private : + //////////////////////////////////////////////////////////// /// \brief Update the vertices' positions /// @@ -216,12 +216,12 @@ private : //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - Vertex* m_vertices; ///< Vertices defining the sprite's geometry - const Texture* m_texture; ///< Texture of the sprite - IntRect m_textureRect; ///< Rectangle defining the area of the source texture to display + VertexContainer m_vertices; ///< Vertices defining the sprite's geometry + const Texture* m_texture; ///< Texture of the sprite + IntRect m_textureRect; ///< Rectangle defining the area of the source texture to display }; -} +} // namespace cpp3ds #endif diff --git a/include/cpp3ds/Graphics/Text.hpp b/include/cpp3ds/Graphics/Text.hpp index 4a34e4e..b3207d9 100644 --- a/include/cpp3ds/Graphics/Text.hpp +++ b/include/cpp3ds/Graphics/Text.hpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include @@ -232,7 +232,7 @@ public : /// \return Position of the character /// //////////////////////////////////////////////////////////// - Vector2f findCharacterPos(std::size_t index) const; + Vector3f findCharacterPos(std::size_t index) const; //////////////////////////////////////////////////////////// /// \brief Get the local bounding rectangle of the entity @@ -285,14 +285,14 @@ private : //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - String m_string; ///< String to display - const Font* m_font; ///< Font used to display the string - unsigned int m_characterSize; ///< Base size of characters, in pixels - Uint32 m_style; ///< Text style (see Style enum) - Color m_color; ///< Text color - mutable VertexArray m_vertices; ///< Vertex array containing the text's geometry - mutable FloatRect m_bounds; ///< Bounding rectangle of the text (in local coordinates) - mutable bool m_geometryNeedUpdate; ///< Does the geometry need to be recomputed? + String m_string; ///< String to display + const Font* m_font; ///< Font used to display the string + unsigned int m_characterSize; ///< Base size of characters, in pixels + Uint32 m_style; ///< Text style (see Style enum) + Color m_color; ///< Text color + mutable VertexContainer m_vertices; ///< Vertex array containing the text's geometry + mutable FloatRect m_bounds; ///< Bounding rectangle of the text (in local coordinates) + mutable bool m_geometryNeedUpdate; ///< Does the geometry need to be recomputed? }; } // namespace cpp3ds diff --git a/include/cpp3ds/Graphics/Transform.hpp b/include/cpp3ds/Graphics/Transform.hpp index 2e3fc6d..94a2132 100644 --- a/include/cpp3ds/Graphics/Transform.hpp +++ b/include/cpp3ds/Graphics/Transform.hpp @@ -29,13 +29,14 @@ // Headers //////////////////////////////////////////////////////////// #include -#include +#include +#include namespace cpp3ds { //////////////////////////////////////////////////////////// -/// \brief Define a 3x3 transform matrix +/// \brief Define a 4x4 transform matrix /// //////////////////////////////////////////////////////////// class Transform @@ -56,17 +57,25 @@ public : /// \param a00 Element (0, 0) of the matrix /// \param a01 Element (0, 1) of the matrix /// \param a02 Element (0, 2) of the matrix + /// \param a03 Element (0, 3) of the matrix /// \param a10 Element (1, 0) of the matrix /// \param a11 Element (1, 1) of the matrix /// \param a12 Element (1, 2) of the matrix + /// \param a13 Element (1, 3) of the matrix /// \param a20 Element (2, 0) of the matrix /// \param a21 Element (2, 1) of the matrix /// \param a22 Element (2, 2) of the matrix + /// \param a23 Element (2, 3) of the matrix + /// \param a30 Element (3, 0) of the matrix + /// \param a31 Element (3, 1) of the matrix + /// \param a32 Element (3, 2) of the matrix + /// \param a33 Element (3, 3) of the matrix /// //////////////////////////////////////////////////////////// - Transform(float a00, float a01, float a02, - float a10, float a11, float a12, - float a20, float a21, float a22); + Transform(float a00, float a01, float a02, float a03, + float a10, float a11, float a12, float a13, + float a20, float a21, float a22, float a23, + float a30, float a31, float a32, float a33); //////////////////////////////////////////////////////////// /// \brief Return the transform as a 4x4 matrix @@ -97,25 +106,34 @@ public : Transform getInverse() const; //////////////////////////////////////////////////////////// - /// \brief Transform a 2D point + /// \brief Return the transpose of the transform + /// + /// \return A new transform which is the transpose of self + /// + //////////////////////////////////////////////////////////// + Transform getTranspose() const; + + //////////////////////////////////////////////////////////// + /// \brief Transform a 3D point /// /// \param x X coordinate of the point to transform /// \param y Y coordinate of the point to transform + /// \param z Z coordinate of the point to transform /// /// \return Transformed point /// //////////////////////////////////////////////////////////// - Vector2f transformPoint(float x, float y) const; + Vector3f transformPoint(float x, float y, float z = 0) const; //////////////////////////////////////////////////////////// - /// \brief Transform a 2D point + /// \brief Transform a 3D point /// /// \param point Point to transform /// /// \return Transformed point /// //////////////////////////////////////////////////////////// - Vector2f transformPoint(const Vector2f& point) const; + Vector3f transformPoint(const Vector3f& point) const; //////////////////////////////////////////////////////////// /// \brief Transform a rectangle @@ -133,6 +151,22 @@ public : //////////////////////////////////////////////////////////// FloatRect transformRect(const FloatRect& rectangle) const; + //////////////////////////////////////////////////////////// + /// \brief Transform a box + /// + /// Since SFML doesn't provide support for oriented boxes, + /// the result of this function is always an axis-aligned + /// box. Which means that if the transform contains a + /// rotation, the bounding box of the transformed box + /// is returned. + /// + /// \param box Box to transform + /// + /// \return Transformed box + /// + //////////////////////////////////////////////////////////// + FloatBox transformBox(const FloatBox& box) const; + //////////////////////////////////////////////////////////// /// \brief Combine the current transform with another one /// @@ -154,18 +188,19 @@ public : /// can be chained. /// \code /// cpp3ds::Transform transform; - /// transform.translate(100, 200).rotate(45); + /// transform.translate(100, 200, 300).rotate(45); /// \endcode /// /// \param x Offset to apply on X axis /// \param y Offset to apply on Y axis + /// \param z Offset to apply on Z axis /// /// \return Reference to *this /// /// \see rotate, scale /// //////////////////////////////////////////////////////////// - Transform& translate(float x, float y); + Transform& translate(float x, float y, float z = 0); //////////////////////////////////////////////////////////// /// \brief Combine the current transform with a translation @@ -174,7 +209,7 @@ public : /// can be chained. /// \code /// cpp3ds::Transform transform; - /// transform.translate(cpp3ds::Vector2f(100, 200)).rotate(45); + /// transform.translate(cpp3ds::Vector3f(100, 200, 300)).rotate(45); /// \endcode /// /// \param offset Translation offset to apply @@ -184,7 +219,7 @@ public : /// \see rotate, scale /// //////////////////////////////////////////////////////////// - Transform& translate(const Vector2f& offset); + Transform& translate(const Vector3f& offset); //////////////////////////////////////////////////////////// /// \brief Combine the current transform with a rotation @@ -256,6 +291,29 @@ public : //////////////////////////////////////////////////////////// Transform& rotate(float angle, const Vector2f& center); + //////////////////////////////////////////////////////////// + /// \brief Combine the current transform with a rotation + /// + /// The axis of rotation is provided for convenience as a second + /// argument, so that you can build rotations around arbitrary axes. + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// \code + /// sf::Transform transform; + /// transform.rotate(90, sf::Vector3f(8, 3, 5)).translate(sf::Vector2f(50, 20)); + /// \endcode + /// + /// \param angle Rotation angle, in degrees + /// \param axis Axis of rotation + /// + /// \return Reference to *this + /// + /// \see translate, scale + /// + //////////////////////////////////////////////////////////// + Transform& rotate(float angle, const Vector3f& axis); + //////////////////////////////////////////////////////////// /// \brief Combine the current transform with a scaling /// @@ -268,13 +326,14 @@ public : /// /// \param scaleX Scaling factor on the X axis /// \param scaleY Scaling factor on the Y axis + /// \param scaleZ Scaling factor on the Z axis /// /// \return Reference to *this /// /// \see translate, rotate /// //////////////////////////////////////////////////////////// - Transform& scale(float scaleX, float scaleY); + Transform& scale(float scaleX, float scaleY, float scaleZ = 1); //////////////////////////////////////////////////////////// /// \brief Combine the current transform with a scaling @@ -306,11 +365,40 @@ public : //////////////////////////////////////////////////////////// /// \brief Combine the current transform with a scaling /// + /// The center of scaling is provided for convenience as a second + /// argument, so that you can build scaling around arbitrary points + /// more easily (and efficiently) than the usual + /// translate(-center).scale(factors).translate(center). + /// /// This function returns a reference to *this, so that calls /// can be chained. /// \code /// cpp3ds::Transform transform; - /// transform.scale(cpp3ds::Vector2f(2, 1)).rotate(45); + /// transform.scale(2, 1, 8, 3, 2, 6).rotate(45); + /// \endcode + /// + /// \param scaleX Scaling factor on X axis + /// \param scaleY Scaling factor on Y axis + /// \param scaleZ Scaling factor on Z axis + /// \param centerX X coordinate of the center of scaling + /// \param centerY Y coordinate of the center of scaling + /// \param centerZ Z coordinate of the center of scaling + /// + /// \return Reference to *this + /// + /// \see translate, rotate + /// + //////////////////////////////////////////////////////////// + Transform& scale(float scaleX, float scaleY, float scaleZ, float centerX, float centerY, float centerZ); + + //////////////////////////////////////////////////////////// + /// \brief Combine the current transform with a scaling + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// \code + /// cpp3ds::Transform transform; + /// transform.scale(cpp3ds::Vector3f(2, 1, 3)).rotate(45); /// \endcode /// /// \param factors Scaling factors @@ -320,7 +408,7 @@ public : /// \see translate, rotate /// //////////////////////////////////////////////////////////// - Transform& scale(const Vector2f& factors); + Transform& scale(const Vector3f& factors); //////////////////////////////////////////////////////////// /// \brief Combine the current transform with a scaling @@ -345,7 +433,7 @@ public : /// \see translate, rotate /// //////////////////////////////////////////////////////////// - Transform& scale(const Vector2f& factors, const Vector2f& center); + Transform& scale(const Vector3f& factors, const Vector3f& center); //////////////////////////////////////////////////////////// // Static member data @@ -400,12 +488,12 @@ Transform& operator *=(Transform& left, const Transform& right); /// \return New transformed point /// //////////////////////////////////////////////////////////// -Vector2f operator *(const Transform& left, const Vector2f& right); +Vector3f operator *(const Transform& left, const Vector3f& right); -} // namespace sf +} // namespace cpp3ds -#endif // SFML_TRANSFORM_HPP +#endif // CPP3DS_TRANSFORM_HPP //////////////////////////////////////////////////////////// diff --git a/include/cpp3ds/Graphics/Transformable.hpp b/include/cpp3ds/Graphics/Transformable.hpp index eec49eb..2a99f22 100644 --- a/include/cpp3ds/Graphics/Transformable.hpp +++ b/include/cpp3ds/Graphics/Transformable.hpp @@ -58,15 +58,16 @@ public : /// /// This function completely overwrites the previous position. /// See the move function to apply an offset based on the previous position instead. - /// The default position of a transformable object is (0, 0). + /// The default position of a transformable object is (0, 0, 0). /// /// \param x X coordinate of the new position /// \param y Y coordinate of the new position + /// \param z Z coordinate of the new position /// /// \see move, getPosition /// //////////////////////////////////////////////////////////// - void setPosition(float x, float y); + void setPosition(float x, float y, float z = 0); //////////////////////////////////////////////////////////// /// \brief set the position of the object @@ -80,7 +81,7 @@ public : /// \see move, getPosition /// //////////////////////////////////////////////////////////// - void setPosition(const Vector2f& position); + void setPosition(const Vector3f& position); //////////////////////////////////////////////////////////// /// \brief set the orientation of the object @@ -96,6 +97,21 @@ public : //////////////////////////////////////////////////////////// void setRotation(float angle); + //////////////////////////////////////////////////////////// + /// \brief Set the orientation of the object + /// + /// This function completely overwrites the previous rotation. + /// See the rotate function to add an angle based on the previous rotation instead. + /// The default rotation of a transformable object is 0. + /// + /// \param angle New rotation, in degrees + /// \param axis Axis of rotation + /// + /// \see rotate, getRotation + /// + //////////////////////////////////////////////////////////// + void setRotation(float angle, const Vector3f& axis); + //////////////////////////////////////////////////////////// /// \brief set the scale factors of the object /// @@ -105,11 +121,12 @@ public : /// /// \param factorX New horizontal scale factor /// \param factorY New vertical scale factor + /// \param factorZ New depth scale factor /// /// \see scale, getScale /// //////////////////////////////////////////////////////////// - void setScale(float factorX, float factorY); + void setScale(float factorX, float factorY, float factorZ = 1); //////////////////////////////////////////////////////////// /// \brief set the scale factors of the object @@ -123,7 +140,7 @@ public : /// \see scale, getScale /// //////////////////////////////////////////////////////////// - void setScale(const Vector2f& factors); + void setScale(const Vector3f& factors); //////////////////////////////////////////////////////////// /// \brief set the local origin of the object @@ -133,15 +150,16 @@ public : /// The coordinates of this point must be relative to the /// top-left corner of the object, and ignore all /// transformations (position, scale, rotation). - /// The default origin of a transformable object is (0, 0). + /// The default origin of a transformable object is (0, 0, 0). /// /// \param x X coordinate of the new origin /// \param y Y coordinate of the new origin + /// \param z Z coordinate of the new origin /// /// \see getOrigin /// //////////////////////////////////////////////////////////// - void setOrigin(float x, float y); + void setOrigin(float x, float y, float z = 0); //////////////////////////////////////////////////////////// /// \brief set the local origin of the object @@ -158,7 +176,7 @@ public : /// \see getOrigin /// //////////////////////////////////////////////////////////// - void setOrigin(const Vector2f& origin); + void setOrigin(const Vector3f& origin); //////////////////////////////////////////////////////////// /// \brief get the position of the object @@ -168,7 +186,7 @@ public : /// \see setPosition /// //////////////////////////////////////////////////////////// - const Vector2f& getPosition() const; + const Vector3f& getPosition() const; //////////////////////////////////////////////////////////// /// \brief get the orientation of the object @@ -190,7 +208,7 @@ public : /// \see setScale /// //////////////////////////////////////////////////////////// - const Vector2f& getScale() const; + const Vector3f& getScale() const; //////////////////////////////////////////////////////////// /// \brief get the local origin of the object @@ -200,7 +218,7 @@ public : /// \see setOrigin /// //////////////////////////////////////////////////////////// - const Vector2f& getOrigin() const; + const Vector3f& getOrigin() const; //////////////////////////////////////////////////////////// /// \brief Move the object by a given offset @@ -209,17 +227,18 @@ public : /// unlike setPosition which overwrites it. /// Thus, it is equivalent to the following code: /// \code - /// cpp3ds::Vector2f pos = object.getPosition(); - /// object.setPosition(pos.x + offsetX, pos.y + offsetY); + /// cpp3ds::Vector3f pos = object.getPosition(); + /// object.setPosition(pos.x + offsetX, pos.y + offsetY, pos.z + offsetZ); /// \endcode /// /// \param offsetX X offset /// \param offsetY Y offset + /// \param offsetZ Z offset /// /// \see setPosition /// //////////////////////////////////////////////////////////// - void move(float offsetX, float offsetY); + void move(float offsetX, float offsetY, float offsetZ = 0); //////////////////////////////////////////////////////////// /// \brief Move the object by a given offset @@ -236,7 +255,7 @@ public : /// \see setPosition /// //////////////////////////////////////////////////////////// - void move(const Vector2f& offset); + void move(const Vector3f& offset); //////////////////////////////////////////////////////////// /// \brief Rotate the object @@ -254,23 +273,20 @@ public : void rotate(float angle); //////////////////////////////////////////////////////////// - /// \brief Scale the object + /// \brief Rotate the object /// - /// This function multiplies the current scale of the object, - /// unlike setScale which overwrites it. + /// This function adds to the current rotation of the object, + /// unlike setRotation which overwrites it. /// Thus, it is equivalent to the following code: /// \code - /// cpp3ds::Vector2f scale = object.getScale(); - /// object.setScale(scale.x * factorX, scale.y * factorY); + /// object.setRotation(object.getRotation() + angle); /// \endcode /// - /// \param factorX Horizontal scale factor - /// \param factorY Vertical scale factor - /// - /// \see setScale + /// \param angle Angle of rotation, in degrees + /// \param axis Axis of rotation /// //////////////////////////////////////////////////////////// - void scale(float factorX, float factorY); + void rotate(float angle, const Vector3f& axis); //////////////////////////////////////////////////////////// /// \brief Scale the object @@ -279,8 +295,28 @@ public : /// unlike setScale which overwrites it. /// Thus, it is equivalent to the following code: /// \code - /// cpp3ds::Vector2f scale = object.getScale(); - /// object.setScale(scale.x * factor.x, scale.y * factor.y); + /// cpp3ds::Vector3f scale = object.getScale(); + /// object.setScale(scale.x * factorX, scale.y * factorY, scale.z * factorZ); + /// \endcode + /// + /// \param factorX Horizontal scale factor + /// \param factorY Vertical scale factor + /// \param factorZ Depth scale factor + /// + /// \see setScale + /// + //////////////////////////////////////////////////////////// + void scale(float factorX, float factorY, float factorZ = 1); + + //////////////////////////////////////////////////////////// + /// \brief Scale the object + /// + /// This function multiplies the current scale of the object, + /// unlike setScale which overwrites it. + /// Thus, it is equivalent to the following code: + /// \code + /// cpp3ds::Vector3f scale = object.getScale(); + /// object.setScale(scale.x * factor.x, scale.y * factor.y, scale.z * factor.z); /// \endcode /// /// \param factor Scale factors @@ -288,7 +324,7 @@ public : /// \see setScale /// //////////////////////////////////////////////////////////// - void scale(const Vector2f& factor); + void scale(const Vector3f& factor); //////////////////////////////////////////////////////////// /// \brief get the combined transform of the object @@ -315,10 +351,11 @@ private : //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - Vector2f m_origin; ///< Origin of translation/rotation/scaling of the object - Vector2f m_position; ///< Position of the object in the 2D world + Vector3f m_origin; ///< Origin of translation/rotation/scaling of the object + Vector3f m_position; ///< Position of the object in the 3D world float m_rotation; ///< Orientation of the object, in degrees - Vector2f m_scale; ///< Scale of the object + Vector3f m_scale; ///< Scale of the object + mutable Transform m_rotation_transform; ///< Combined transformation of the object mutable Transform m_transform; ///< Combined transformation of the object mutable bool m_transformNeedUpdate; ///< Does the transform need to be recomputed? mutable Transform m_inverseTransform; ///< Combined transformation of the object @@ -328,7 +365,7 @@ private : } // namespace sf -#endif // SFML_TRANSFORMABLE_HPP +#endif // CPP3DS_TRANSFORMABLE_HPP //////////////////////////////////////////////////////////// @@ -371,7 +408,7 @@ private : /// center, for example. To do such things, use cpp3ds::Transform directly. /// /// cpp3ds::Transformable can be used as a base class. It is often -/// combined with cpp3ds::Drawable -- that's what SFML's sprites, +/// combined with cpp3ds::Drawable -- that's what cpp3ds's sprites, /// texts and shapes do. /// \code /// class MyEntity : public cpp3ds::Transformable, public cpp3ds::Drawable @@ -412,7 +449,7 @@ private : /// \endcode /// /// A note on coordinates and undistorted rendering: \n -/// By default, SFML (or more exactly, OpenGL) may interpolate drawable objects +/// By default, cpp3ds (or more exactly, OpenGL) may interpolate drawable objects /// such as sprites or texts when rendering. While this allows transitions /// like slow movements or rotations to appear smoothly, it can lead to /// unwanted results in some cases, for example blurred or distorted objects. diff --git a/include/cpp3ds/Graphics/Vertex.hpp b/include/cpp3ds/Graphics/Vertex.hpp index 5089fa2..fcabc00 100644 --- a/include/cpp3ds/Graphics/Vertex.hpp +++ b/include/cpp3ds/Graphics/Vertex.hpp @@ -30,12 +30,13 @@ //////////////////////////////////////////////////////////// #include #include +#include #include namespace cpp3ds { //////////////////////////////////////////////////////////// -/// \brief Define a point with color and texture coordinates +/// \brief Define a point with a color, texture coordinate and normal /// //////////////////////////////////////////////////////////// class Vertex @@ -56,7 +57,7 @@ public : /// \param thePosition Vertex position /// //////////////////////////////////////////////////////////// - Vertex(const Vector2f& thePosition); + Vertex(const Vector3f& thePosition); //////////////////////////////////////////////////////////// /// \brief Construct the vertex from its position and color @@ -67,7 +68,7 @@ public : /// \param theColor Vertex color /// //////////////////////////////////////////////////////////// - Vertex(const Vector2f& thePosition, const Color& theColor); + Vertex(const Vector3f& thePosition, const Color& theColor); //////////////////////////////////////////////////////////// /// \brief Construct the vertex from its position and texture coordinates @@ -78,7 +79,7 @@ public : /// \param theTexCoords Vertex texture coordinates /// //////////////////////////////////////////////////////////// - Vertex(const Vector2f& thePosition, const Vector2f& theTexCoords); + Vertex(const Vector3f& thePosition, const Vector2f& theTexCoords); //////////////////////////////////////////////////////////// /// \brief Construct the vertex from its position, color and texture coordinates @@ -88,7 +89,7 @@ public : /// \param theTexCoords Vertex texture coordinates /// //////////////////////////////////////////////////////////// - Vertex(const Vector2f& thePosition, const Color& theColor, const Vector2f& theTexCoords); + Vertex(const Vector3f& thePosition, const Color& theColor, const Vector2f& theTexCoords); #ifndef EMULATION static void* operator new (std::size_t size); @@ -97,12 +98,24 @@ public : static void operator delete[] (void *p); #endif + //////////////////////////////////////////////////////////// + /// \brief Construct the vertex from its position, color, texture coordinates and lighting normal + /// + /// \param thePosition Vertex position + /// \param theColor Vertex color + /// \param theTexCoords Vertex texture coordinates + /// \param theNormal Lighting normal + /// + //////////////////////////////////////////////////////////// + Vertex(const Vector3f& thePosition, const Color& theColor, const Vector2f& theTexCoords, const Vector3f& theNormal); + //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - Vector2f position; ///< 2D position of the vertex + Vector3f position; ///< 3D position of the vertex Color color; ///< Color of the vertex Vector2f texCoords; ///< Coordinates of the texture's pixel to map to the vertex + Vector3f normal; ///< Lighting normal }; } @@ -116,7 +129,7 @@ public : /// \ingroup graphics /// /// A vertex is an improved point. It has a position and other -/// extra attributes that will be used for drawing: in SFML, +/// extra attributes that will be used for drawing: in cpp3ds, /// vertices also have a color and a pair of texture coordinates. /// /// The vertex is the building block of drawing. Everything which @@ -125,7 +138,7 @@ public : /// are grouped to create even more complex 2D entities such as /// sprites, texts, etc. /// -/// If you use the graphical entities of SFML (sprite, text, shape) +/// If you use the graphical entities of cpp3ds (sprite, text, shape) /// you won't have to deal with vertices directly. But if you want /// to define your own 2D entities, such as tiled maps or particle /// systems, using vertices will allow you to get maximum performances. @@ -135,20 +148,22 @@ public : /// // define a 100x100 square, red, with a 10x10 texture mapped on it /// cpp3ds::Vertex vertices[] = /// { -/// cpp3ds::Vertex(cpp3ds::Vector2f( 0, 0), cpp3ds::Color::Red, cpp3ds::Vector2f( 0, 0)), -/// cpp3ds::Vertex(cpp3ds::Vector2f( 0, 100), cpp3ds::Color::Red, cpp3ds::Vector2f( 0, 10)), -/// cpp3ds::Vertex(cpp3ds::Vector2f(100, 100), cpp3ds::Color::Red, cpp3ds::Vector2f(10, 10)), -/// cpp3ds::Vertex(cpp3ds::Vector2f(100, 0), cpp3ds::Color::Red, cpp3ds::Vector2f(10, 0)) +/// cpp3ds::Vertex(cpp3ds::Vector3f( 0, 0, 0), cpp3ds::Color::Red, cpp3ds::Vector2f( 0, 0)), +/// cpp3ds::Vertex(cpp3ds::Vector3f( 0, 100, 0), cpp3ds::Color::Red, cpp3ds::Vector2f( 0, 10)), +/// cpp3ds::Vertex(cpp3ds::Vector3f(100, 100, 0), cpp3ds::Color::Red, cpp3ds::Vector2f(10, 10)), +/// cpp3ds::Vertex(cpp3ds::Vector3f( 0, 0, 0), cpp3ds::Color::Red, cpp3ds::Vector2f( 0, 0)), +/// cpp3ds::Vertex(cpp3ds::Vector3f(100, 100, 0), cpp3ds::Color::Red, cpp3ds::Vector2f(10, 10)), +/// cpp3ds::Vertex(cpp3ds::Vector3f(100, 0, 0), cpp3ds::Color::Red, cpp3ds::Vector2f(10, 0)) /// }; /// /// // draw it -/// window.draw(vertices, 4, cpp3ds::Quads); +/// window.draw(vertices, 6, cpp3ds::Triangles); /// \endcode /// /// Note: although texture coordinates are supposed to be an integer /// amount of pixels, their type is float because of some buggy graphics /// drivers that are not able to process integer coordinates correctly. /// -/// \see cpp3ds::VertexArray +/// \see cpp3ds::VertexArray, cpp3ds::VertexBuffer, cpp3ds::VertexContainer /// //////////////////////////////////////////////////////////// diff --git a/include/cpp3ds/Graphics/VertexArray.hpp b/include/cpp3ds/Graphics/VertexArray.hpp index 0be15a4..761e969 100644 --- a/include/cpp3ds/Graphics/VertexArray.hpp +++ b/include/cpp3ds/Graphics/VertexArray.hpp @@ -31,7 +31,8 @@ #include #include #include -#include +#include +#include #ifndef EMULATION #include #endif @@ -44,7 +45,7 @@ namespace cpp3ds /// \brief Define a set of one or more 2D primitives /// //////////////////////////////////////////////////////////// -class VertexArray : public Drawable +class VertexArray : public VertexContainer { public : @@ -163,15 +164,15 @@ public : PrimitiveType getPrimitiveType() const; //////////////////////////////////////////////////////////// - /// \brief Compute the bounding rectangle of the vertex array + /// \brief Compute the bounding box of the vertex array /// - /// This function returns the axis-aligned rectangle that + /// This function returns the axis-aligned box that /// contains all the vertices of the array. /// - /// \return Bounding rectangle of the vertex array + /// \return Bounding box of the vertex array /// //////////////////////////////////////////////////////////// - FloatRect getBounds() const; + FloatBox getBounds() const; private : @@ -197,7 +198,7 @@ private: PrimitiveType m_primitiveType; ///< Type of primitives to draw }; -} +} // namespace cpp3ds #endif @@ -213,17 +214,22 @@ private: /// It inherits cpp3ds::Drawable, but unlike other drawables it /// is not transformable. /// +/// Be aware of the order when specifying vertices. By default, +/// outward facing faces have counter-clockwise winding and as +/// such any faces specified in clockwise order might not be +/// displayed. +/// /// Example: /// \code /// cpp3ds::VertexArray lines(cpp3ds::LinesStrip, 4); -/// lines[0].position = cpp3ds::Vector2f(10, 0); -/// lines[1].position = cpp3ds::Vector2f(20, 0); -/// lines[2].position = cpp3ds::Vector2f(30, 5); -/// lines[3].position = cpp3ds::Vector2f(40, 2); +/// lines[0].position = cpp3ds::Vector3f(10, 0, 0); +/// lines[1].position = cpp3ds::Vector3f(20, 0, 0); +/// lines[2].position = cpp3ds::Vector3f(30, 5, 0); +/// lines[3].position = cpp3ds::Vector3f(40, 2, 0); /// /// window.draw(lines); /// \endcode /// -/// \see cpp3ds::Vertex +/// \see cpp3ds::Vertex, cpp3ds::VertexBuffer, cpp3ds::VertexContainer /// //////////////////////////////////////////////////////////// diff --git a/include/cpp3ds/Graphics/VertexBuffer.hpp b/include/cpp3ds/Graphics/VertexBuffer.hpp new file mode 100644 index 0000000..7b18583 --- /dev/null +++ b/include/cpp3ds/Graphics/VertexBuffer.hpp @@ -0,0 +1,373 @@ +#ifndef CPP3DS_VERTEXBUFFER_HPP +#define CPP3DS_VERTEXBUFFER_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#ifndef EMULATION +#include +#endif +#include +#include + + +namespace cpp3ds +{ +class Shader; + +//////////////////////////////////////////////////////////// +/// \brief Define a set of one or more primitives +/// +//////////////////////////////////////////////////////////// +class VertexBuffer : public VertexContainer, GlResource +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates an empty vertex buffer. + /// + //////////////////////////////////////////////////////////// + VertexBuffer(); + + //////////////////////////////////////////////////////////// + /// \brief Construct the vertex buffer with a type and an initial number of vertices + /// + /// \param type Type of primitives + /// \param vertexCount Initial number of vertices in the buffer + /// + //////////////////////////////////////////////////////////// + explicit VertexBuffer(PrimitiveType type, unsigned int vertexCount = 0); + + //////////////////////////////////////////////////////////// + /// \brief Copy constructor + /// + /// \param copy instance to copy + /// + //////////////////////////////////////////////////////////// + VertexBuffer(const VertexBuffer& copy); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + virtual ~VertexBuffer(); + + //////////////////////////////////////////////////////////// + /// \brief Create the vertex buffer + /// + /// If this function fails, the vertex buffer is left unchanged. + /// + /// \return True if creation was successful + /// + //////////////////////////////////////////////////////////// + bool create(); + + //////////////////////////////////////////////////////////// + /// \brief Return the vertex count + /// + /// \return Number of vertices in the buffer + /// + //////////////////////////////////////////////////////////// + unsigned int getVertexCount() const; + + //////////////////////////////////////////////////////////// + /// \brief Get a read-write access to a vertex by its index + /// + /// This function doesn't check \a index, it must be in range + /// [0, getVertexCount() - 1]. The behaviour is undefined + /// otherwise. + /// + /// \param index Index of the vertex to get + /// + /// \return Reference to the index-th vertex + /// + /// \see getVertexCount + /// + //////////////////////////////////////////////////////////// + Vertex& operator [](unsigned int index); + + //////////////////////////////////////////////////////////// + /// \brief Get a read-only access to a vertex by its index + /// + /// This function doesn't check \a index, it must be in range + /// [0, getVertexCount() - 1]. The behaviour is undefined + /// otherwise. + /// + /// \param index Index of the vertex to get + /// + /// \return Const reference to the index-th vertex + /// + /// \see getVertexCount + /// + //////////////////////////////////////////////////////////// + const Vertex& operator [](unsigned int index) const; + + //////////////////////////////////////////////////////////// + /// \brief Clear the vertex buffer + /// + /// This function removes all the vertices from the buffer. + /// It doesn't deallocate the corresponding memory, so that + /// adding new vertices after clearing doesn't involve + /// reallocating all the memory. + /// + //////////////////////////////////////////////////////////// + void clear(); + + //////////////////////////////////////////////////////////// + /// \brief Resize the vertex buffer + /// + /// If \a vertexCount is greater than the current size, the previous + /// vertices are kept and new (default-constructed) vertices are + /// added. + /// If \a vertexCount is less than the current size, existing vertices + /// are removed from the buffer. + /// + /// \param vertexCount New size of the buffer (number of vertices) + /// + //////////////////////////////////////////////////////////// + void resize(unsigned int vertexCount); + + //////////////////////////////////////////////////////////// + /// \brief Add a vertex to the buffer + /// + /// \param vertex Vertex to add + /// + //////////////////////////////////////////////////////////// + void append(const Vertex& vertex); + + //////////////////////////////////////////////////////////// + /// \brief Set the type of primitives to draw + /// + /// This function defines how the vertices must be interpreted + /// when it's time to draw them: + /// \li As points + /// \li As lines + /// \li As triangles + /// \li As quads + /// The default primitive type is cpp3ds::Points. + /// + /// \param type Type of primitive + /// + //////////////////////////////////////////////////////////// + void setPrimitiveType(PrimitiveType type); + + //////////////////////////////////////////////////////////// + /// \brief Get the type of primitives drawn by the vertex buffer + /// + /// \return Primitive type + /// + //////////////////////////////////////////////////////////// + PrimitiveType getPrimitiveType() const; + + //////////////////////////////////////////////////////////// + /// \brief Compute the bounding box of the vertex buffer + /// + /// This function returns the axis-aligned box that + /// contains all the vertices of the buffer. + /// + /// \return Bounding box of the vertex buffer + /// + //////////////////////////////////////////////////////////// + FloatBox getBounds() const; + + //////////////////////////////////////////////////////////// + /// \brief Get a pointer (non-const) to the data + /// + /// This function returns a non-const pointer to the data + /// block allocated by this VertexBuffer. It can be used + /// for transferring arbitrary data between host and graphics + /// memory. The number of bytes available can be computed + /// with getVertexCount() * sizeof(cpp3ds::Vertex). + /// + /// \return Non-const pointer to the data + /// + //////////////////////////////////////////////////////////// + void* getPointer(); + + //////////////////////////////////////////////////////////// + /// \brief Get a pointer (const) to the data + /// + /// This function returns a const pointer to the data + /// block allocated by this VertexBuffer. The number + /// of bytes available can be computed with + /// getVertexCount() * sizeof(cpp3ds::Vertex). + /// + /// \return Const pointer to the data + /// + //////////////////////////////////////////////////////////// + const void* getPointer() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the name of the underlying buffer object + /// + /// This function returns the name of the underlying + /// OpenGL buffer object, i.e. the identifier returned + /// by glGenBuffers. + /// + /// \return Name of the underlying buffer object + /// + //////////////////////////////////////////////////////////// + unsigned int getBufferObjectName() const; + + //////////////////////////////////////////////////////////// + /// \brief Overload of assignment operator + /// + /// \param right Instance to assign + /// + /// \return Reference to self + /// + //////////////////////////////////////////////////////////// + VertexBuffer& operator =(const VertexBuffer& right); + + //////////////////////////////////////////////////////////// + /// \brief Bind a vertex buffer for rendering + /// + /// This function is not part of the graphics API, it mustn't be + /// used when drawing cpp3ds entities. It must be used only if you + /// mix cpp3ds::VertexBuffer with OpenGL code. + /// + /// \code + /// cpp3ds::VertexBuffer buffer1, buffer2; + /// ... + /// cpp3ds::VertexBuffer::bind(&buffer1); + /// // draw OpenGL stuff that use buffer1... + /// cpp3ds::VertexBuffer::bind(&buffer2); + /// // draw OpenGL stuff that use buffer2... + /// cpp3ds::VertexBuffer::bind(NULL); + /// // draw OpenGL stuff that use no vertex buffer... + /// \endcode + /// + /// \param buffer Pointer to the vertex buffer to bind, can be null to use no vertex buffer + /// + //////////////////////////////////////////////////////////// + static void bind(const VertexBuffer* buffer); + + //////////////////////////////////////////////////////////// + /// \brief Bind a vertex buffer to a specific target + /// + /// This function is not part of the graphics API, it mustn't be + /// used when drawing cpp3ds entities. It must be used only if you + /// mix cpp3ds::VertexBuffer with OpenGL code. + /// + /// \code + /// cpp3ds::VertexBuffer buffer1, buffer2; + /// ... + /// cpp3ds::VertexBuffer::bind(&buffer1, GL_ELEMENT_ARRAY_ARB); + /// // draw OpenGL stuff that use buffer1 as an index buffer... + /// cpp3ds::VertexBuffer::bind(&buffer2, GL_ELEMENT_ARRAY_ARB); + /// // draw OpenGL stuff that use buffer2 as an index buffer... + /// cpp3ds::VertexBuffer::bind(NULL, GL_ELEMENT_ARRAY_ARB); + /// // draw OpenGL stuff that use no index buffer... + /// \endcode + /// + /// \param buffer Pointer to the vertex buffer to bind, can be null to use no vertex buffer + /// \param target Target to bind to + /// + //////////////////////////////////////////////////////////// + static void bind(const VertexBuffer* buffer, unsigned int target); + + //////////////////////////////////////////////////////////// + /// \brief Tell whether or not the system supports vertex buffers + /// + /// This function should always be called before using + /// the vertex buffer features. If it returns false, then + /// any attempt to use cpp3ds::VertexBuffer will fail. + /// + /// \return True if vertex buffers are supported, false otherwise + /// + //////////////////////////////////////////////////////////// + static bool isAvailable(); + + //////////////////////////////////////////////////////////// + /// \brief Tell whether or not the system supports vertex array objects + /// + /// This function should always be called before using + /// any vertex array object features. + /// + /// \return True if vertex array objects are supported, false otherwise + /// + //////////////////////////////////////////////////////////// + static bool hasVertexArrayObjects(); + +private : + + friend class RenderTarget; + friend class Shader; + + //////////////////////////////////////////////////////////// + /// \brief Draw the vertex buffer to a render target + /// + /// \param target Render target to draw to + /// \param states Current render states + /// + //////////////////////////////////////////////////////////// + virtual void draw(RenderTarget& target, RenderStates states) const; + + //////////////////////////////////////////////////////////// + // Types + //////////////////////////////////////////////////////////// + typedef std::map, unsigned int> ArrayObjects; + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + #ifdef EMULATION + std::vector m_vertices; ///< Vertices contained in the buffer + #else + std::vector> m_vertices; + #endif + PrimitiveType m_primitiveType; ///< Type of primitives to draw + unsigned int m_bufferObject; ///< OpenGL identifier for the buffer object + Uint64 m_cacheId; ///< Unique number that identifies the vertex buffer to the render target's cache + mutable bool m_needUpload; ///< Whether the buffer data needs to be re-uploaded + mutable ArrayObjects m_arrayObjects; ///< Map of (render target, shader) pairs to array object identifiers +}; + +} // namespace cpp3ds + + +#endif // CPP3DS_VERTEXBUFFER_HPP + + +//////////////////////////////////////////////////////////// +/// \class cpp3ds::VertexBuffer +/// \ingroup graphics +/// +/// cpp3ds::VertexBuffer is a very simple wrapper around a dynamic +/// buffer of vertices and a primitives type. +/// +/// It inherits cpp3ds::Drawable, but unlike other drawables it +/// is not transformable. +/// +/// An cpp3ds::VertexBuffer functions exactly like an cpp3ds::VertexArray +/// except that vertex data is stored in GPU memory and only +/// resynchronized with system memory when necessary. This is +/// analog to cpp3ds::Image and cpp3ds::Texture. +/// +/// Be aware of the order when specifying vertices. By default, +/// outward facing faces have counter-clockwise winding and as +/// such any faces specified in clockwise order might not be +/// displayed. +/// +/// Example: +/// \code +/// cpp3ds::VertexBuffer lines(cpp3ds::LinesStrip, 4); +/// lines[0].position = cpp3ds::Vector3f(10, 0, 0); +/// lines[1].position = cpp3ds::Vector3f(20, 0, 0); +/// lines[2].position = cpp3ds::Vector3f(30, 5, 0); +/// lines[3].position = cpp3ds::Vector3f(40, 2, 0); +/// +/// window.draw(lines); +/// \endcode +/// +/// \see cpp3ds::Vertex, cpp3ds::VertexArray, cpp3ds::VertexContainer +/// +//////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/include/cpp3ds/Graphics/VertexContainer.hpp b/include/cpp3ds/Graphics/VertexContainer.hpp new file mode 100644 index 0000000..4f4b8a0 --- /dev/null +++ b/include/cpp3ds/Graphics/VertexContainer.hpp @@ -0,0 +1,240 @@ +#ifndef CPP3DS_VERTEXCONTAINER_HPP +#define CPP3DS_VERTEXCONTAINER_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include + + +namespace cpp3ds +{ +//////////////////////////////////////////////////////////// +/// \brief Define a set of one or more primitives +/// +//////////////////////////////////////////////////////////// +class VertexContainer : public Drawable +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates an empty vertex container. + /// + //////////////////////////////////////////////////////////// + VertexContainer(); + + //////////////////////////////////////////////////////////// + /// \brief Construct the vertex container with a type and an initial number of vertices + /// + /// \param type Type of primitives + /// \param vertexCount Initial number of vertices in the container + /// + //////////////////////////////////////////////////////////// + explicit VertexContainer(PrimitiveType type, unsigned int vertexCount = 0); + + //////////////////////////////////////////////////////////// + /// \brief Copy constructor + /// + /// \param copy instance to copy + /// + //////////////////////////////////////////////////////////// + VertexContainer(const VertexContainer& copy); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + ~VertexContainer(); + + //////////////////////////////////////////////////////////// + /// \brief Return the vertex count + /// + /// \return Number of vertices in the container + /// + //////////////////////////////////////////////////////////// + virtual unsigned int getVertexCount() const; + + //////////////////////////////////////////////////////////// + /// \brief Get a read-write access to a vertex by its index + /// + /// This function doesn't check \a index, it must be in range + /// [0, getVertexCount() - 1]. The behaviour is undefined + /// otherwise. + /// + /// \param index Index of the vertex to get + /// + /// \return Reference to the index-th vertex + /// + /// \see getVertexCount + /// + //////////////////////////////////////////////////////////// + virtual Vertex& operator [](unsigned int index); + + //////////////////////////////////////////////////////////// + /// \brief Get a read-only access to a vertex by its index + /// + /// This function doesn't check \a index, it must be in range + /// [0, getVertexCount() - 1]. The behaviour is undefined + /// otherwise. + /// + /// \param index Index of the vertex to get + /// + /// \return Const reference to the index-th vertex + /// + /// \see getVertexCount + /// + //////////////////////////////////////////////////////////// + virtual const Vertex& operator [](unsigned int index) const; + + //////////////////////////////////////////////////////////// + /// \brief Clear the vertex container + /// + /// This function removes all the vertices from the container. + /// It doesn't deallocate the corresponding memory, so that + /// adding new vertices after clearing doesn't involve + /// reallocating all the memory. + /// + //////////////////////////////////////////////////////////// + virtual void clear(); + + //////////////////////////////////////////////////////////// + /// \brief Resize the vertex container + /// + /// If \a vertexCount is greater than the current size, the previous + /// vertices are kept and new (default-constructed) vertices are + /// added. + /// If \a vertexCount is less than the current size, existing vertices + /// are removed from the container. + /// + /// \param vertexCount New size of the container (number of vertices) + /// + //////////////////////////////////////////////////////////// + virtual void resize(unsigned int vertexCount); + + //////////////////////////////////////////////////////////// + /// \brief Add a vertex to the container + /// + /// \param vertex Vertex to add + /// + //////////////////////////////////////////////////////////// + virtual void append(const Vertex& vertex); + + //////////////////////////////////////////////////////////// + /// \brief Set the type of primitives to draw + /// + /// This function defines how the vertices must be interpreted + /// when it's time to draw them: + /// \li As points + /// \li As lines + /// \li As triangles + /// \li As quads + /// The default primitive type is cpp3ds::Points. + /// + /// \param type Type of primitive + /// + //////////////////////////////////////////////////////////// + virtual void setPrimitiveType(PrimitiveType type); + + //////////////////////////////////////////////////////////// + /// \brief Get the type of primitives drawn by the vertex container + /// + /// \return Primitive type + /// + //////////////////////////////////////////////////////////// + virtual PrimitiveType getPrimitiveType() const; + + //////////////////////////////////////////////////////////// + /// \brief Compute the bounding box of the vertex container + /// + /// This function returns the axis-aligned box that + /// contains all the vertices of the container. + /// + /// \return Bounding box of the vertex container + /// + //////////////////////////////////////////////////////////// + virtual FloatBox getBounds() const; + + //////////////////////////////////////////////////////////// + /// \brief Overload of assignment operator + /// + /// \param right Instance to assign + /// + /// \return Reference to self + /// + //////////////////////////////////////////////////////////// + VertexContainer& operator =(const VertexContainer& right); + +protected : + + //////////////////////////////////////////////////////////// + /// \brief Special constructor for derived classes + /// + /// This function is meant for internal use only. + /// + //////////////////////////////////////////////////////////// + VertexContainer(int); + +private : + + //////////////////////////////////////////////////////////// + /// \brief Draw the vertex container to a render target + /// + /// \param target Render target to draw to + /// \param states Current render states + /// + //////////////////////////////////////////////////////////// + virtual void draw(RenderTarget& target, RenderStates states) const; + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + VertexContainer* m_impl; ///< Platform/hardware specific implementation +}; + +} // namespace cpp3ds + + +#endif // CPP3DS_VERTEXCONTAINER_HPP + + +//////////////////////////////////////////////////////////// +/// \class cpp3ds::VertexContainer +/// \ingroup graphics +/// +/// cpp3ds::VertexContainer is a very simple wrapper around a dynamic +/// container of vertices and a primitives type. +/// +/// It inherits cpp3ds::Drawable, but unlike other drawables it +/// is not transformable. +/// +/// cpp3ds::VertexContainer is a simple proxy class that automatically +/// selects the best method of vertex storage. It will make use +/// of an cpp3ds::VertexBuffer if it is available and fall back +/// to cpp3ds::VertexArray if not. +/// +/// Be aware of the order when specifying vertices. By default, +/// outward facing faces have counter-clockwise winding and as +/// such any faces specified in clockwise order might not be +/// displayed. +/// +/// Example: +/// \code +/// cpp3ds::VertexContainer lines(cpp3ds::LinesStrip, 4); +/// lines[0].position = cpp3ds::Vector3f(10, 0, 0); +/// lines[1].position = cpp3ds::Vector3f(20, 0, 0); +/// lines[2].position = cpp3ds::Vector3f(30, 5, 0); +/// lines[3].position = cpp3ds::Vector3f(40, 2, 0); +/// +/// window.draw(lines); +/// \endcode +/// +/// \see cpp3ds::Vertex, cpp3ds::VertexArray, cpp3ds::VertexBuffer +/// +//////////////////////////////////////////////////////////// diff --git a/include/cpp3ds/Graphics/View.hpp b/include/cpp3ds/Graphics/View.hpp index b12a6c1..e4cdd9f 100644 --- a/include/cpp3ds/Graphics/View.hpp +++ b/include/cpp3ds/Graphics/View.hpp @@ -30,7 +30,7 @@ //////////////////////////////////////////////////////////// #include #include -#include +#include namespace cpp3ds @@ -66,7 +66,13 @@ public : /// \param size Size of zone to display /// //////////////////////////////////////////////////////////// - View(const Vector2f& center, const Vector2f& size); + View(const Vector3f& center, const Vector2f& size); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + virtual ~View(); //////////////////////////////////////////////////////////// /// \brief Set the center of the view @@ -77,7 +83,7 @@ public : /// \see setSize, getCenter /// //////////////////////////////////////////////////////////// - void setCenter(float x, float y); + void setCenter(float x, float y, float z = 0); //////////////////////////////////////////////////////////// /// \brief Set the center of the view @@ -87,7 +93,7 @@ public : /// \see setSize, getCenter /// //////////////////////////////////////////////////////////// - void setCenter(const Vector2f& center); + void setCenter(const Vector3f& center); //////////////////////////////////////////////////////////// /// \brief Set the size of the view @@ -159,7 +165,7 @@ public : /// \see getSize, setCenter /// //////////////////////////////////////////////////////////// - const Vector2f& getCenter() const; + const Vector3f& getCenter() const; //////////////////////////////////////////////////////////// /// \brief Get the size of the view @@ -196,11 +202,12 @@ public : /// /// \param offsetX X coordinate of the move offset /// \param offsetY Y coordinate of the move offset + /// \param offsetZ Z coordinate of the move offset /// /// \see setCenter, rotate, zoom /// //////////////////////////////////////////////////////////// - void move(float offsetX, float offsetY); + void move(float offsetX, float offsetY, float offsetZ = 0); //////////////////////////////////////////////////////////// /// \brief Move the view relatively to its current position @@ -210,7 +217,7 @@ public : /// \see setCenter, rotate, zoom /// //////////////////////////////////////////////////////////// - void move(const Vector2f& offset); + void move(const Vector3f& offset); //////////////////////////////////////////////////////////// /// \brief Rotate the view relatively to its current orientation @@ -246,10 +253,10 @@ public : /// /// \return Projection transform defining the view /// - /// \see getInverseTransform + /// \see getInverseTransform, getViewTransform, getInverseViewTransform /// //////////////////////////////////////////////////////////// - const Transform& getTransform() const; + virtual const Transform& getTransform() const; //////////////////////////////////////////////////////////// /// \brief Get the inverse projection transform of the view @@ -258,30 +265,72 @@ public : /// /// \return Inverse of the projection transform defining the view /// - /// \see getTransform + /// \see getTransform, getViewTransform, getInverseViewTransform /// //////////////////////////////////////////////////////////// const Transform& getInverseTransform() const; + //////////////////////////////////////////////////////////// + /// \brief Get the view transform of the view + /// + /// This function is meant for internal use only. + /// + /// \return View transform of the view + /// + /// \see getTransform, getInverseTransform, getInverseViewTransform + /// + //////////////////////////////////////////////////////////// + virtual const Transform& getViewTransform() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the inverse view transform of the view + /// + /// This function is meant for internal use only. + /// + /// \return Inverse of the view transform + /// + /// \see getTransform, getInverseTransform, getViewTransform + /// + //////////////////////////////////////////////////////////// + const Transform& getInverseViewTransform() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the position of the viewer + /// + /// \return Position of the viewer + /// + //////////////////////////////////////////////////////////// + virtual const Vector3f& getPosition() const; + +protected: + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + mutable Transform m_transform; ///< Precomputed projection transform corresponding to the view + mutable bool m_transformUpdated; ///< Internal state telling if the transform needs to be updated + mutable Transform m_inverseTransform; ///< Precomputed inverse projection transform corresponding to the view + mutable bool m_invTransformUpdated; ///< Internal state telling if the inverse transform needs to be updated + mutable Transform m_viewTransform; ///< Precomputed view transform + mutable bool m_viewTransformUpdated; ///< Internal state telling if the view transform needs to be updated + mutable Transform m_inverseViewTransform; ///< Precomputed inverse view transform + mutable bool m_invViewTransformUpdated; ///< Internal state telling if the inverse view transform needs to be updated + private : //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - Vector2f m_center; ///< Center of the view, in scene coordinates - Vector2f m_size; ///< Size of the view, in scene coordinates - float m_rotation; ///< Angle of rotation of the view rectangle, in degrees - FloatRect m_viewport; ///< Viewport rectangle, expressed as a factor of the render-target's size - mutable Transform m_transform; ///< Precomputed projection transform corresponding to the view - mutable Transform m_inverseTransform; ///< Precomputed inverse projection transform corresponding to the view - mutable bool m_transformUpdated; ///< Internal state telling if the transform needs to be updated - mutable bool m_invTransformUpdated; ///< Internal state telling if the inverse transform needs to be updated + Vector3f m_position; ///< Center of the view, in scene coordinates + Vector2f m_size; ///< Size of the view, in scene coordinates + float m_rotation; ///< Angle of rotation of the view rectangle, in degrees + FloatRect m_viewport; ///< Viewport rectangle, expressed as a factor of the render-target's size }; -} // namespace sf +} // namespace cpp3ds -#endif // SFML_VIEW_HPP +#endif // CPP3DS_VIEW_HPP //////////////////////////////////////////////////////////// diff --git a/include/cpp3ds/System/Vector2.hpp b/include/cpp3ds/System/Vector2.hpp index 12bdfc7..1ffb10c 100644 --- a/include/cpp3ds/System/Vector2.hpp +++ b/include/cpp3ds/System/Vector2.hpp @@ -28,6 +28,10 @@ namespace cpp3ds { + +template +class Vector3; + //////////////////////////////////////////////////////////// /// \brief Utility template class for manipulating /// 2-dimensional vectors @@ -55,6 +59,14 @@ public : //////////////////////////////////////////////////////////// Vector2(T X, T Y); + //////////////////////////////////////////////////////////// + /// \brief Construct the vector by truncating a Vector3, discarding z + /// + /// \param vector Vector to convert + /// + //////////////////////////////////////////////////////////// + Vector2(const Vector3& vector); + //////////////////////////////////////////////////////////// /// \brief Construct the vector from another type of vector /// diff --git a/include/cpp3ds/System/Vector2.inl b/include/cpp3ds/System/Vector2.inl index 07b96f5..ac9a3ca 100644 --- a/include/cpp3ds/System/Vector2.inl +++ b/include/cpp3ds/System/Vector2.inl @@ -43,6 +43,15 @@ y(Y) } +//////////////////////////////////////////////////////////// +template +inline Vector2::Vector2(const Vector3& vector) : +x(vector.x), +y(vector.y) +{ +} + + //////////////////////////////////////////////////////////// template template diff --git a/include/cpp3ds/System/Vector3.hpp b/include/cpp3ds/System/Vector3.hpp index 7fcc053..b0b9f2a 100644 --- a/include/cpp3ds/System/Vector3.hpp +++ b/include/cpp3ds/System/Vector3.hpp @@ -28,6 +28,10 @@ namespace cpp3ds { + +template +class Vector2; + //////////////////////////////////////////////////////////// /// \brief Utility template class for manipulating /// 3-dimensional vectors @@ -46,6 +50,15 @@ public : //////////////////////////////////////////////////////////// Vector3(); + //////////////////////////////////////////////////////////// + /// \brief Construct the vector from its x and y coordinates, setting z to 0 + /// + /// \param X X coordinate + /// \param Y Y coordinate + /// + //////////////////////////////////////////////////////////// + Vector3(T X, T Y); + //////////////////////////////////////////////////////////// /// \brief Construct the vector from its coordinates /// @@ -56,6 +69,14 @@ public : //////////////////////////////////////////////////////////// Vector3(T X, T Y, T Z); + //////////////////////////////////////////////////////////// + /// \brief Construct the vector by expanding a Vector2, setting z to 0 + /// + /// \param vector Vector to expand from + /// + //////////////////////////////////////////////////////////// + Vector3(const Vector2& vector); + //////////////////////////////////////////////////////////// /// \brief Construct the vector from another type of vector /// @@ -252,8 +273,9 @@ bool operator !=(const Vector3& left, const Vector3& right); #include // Define the most common types -typedef Vector3 Vector3i; -typedef Vector3 Vector3f; +typedef Vector3 Vector3i; +typedef Vector3 Vector3u; +typedef Vector3 Vector3f; } // namespace cpp3ds diff --git a/include/cpp3ds/System/Vector3.inl b/include/cpp3ds/System/Vector3.inl index af8dae3..b0c016a 100644 --- a/include/cpp3ds/System/Vector3.inl +++ b/include/cpp3ds/System/Vector3.inl @@ -34,6 +34,17 @@ z(0) } +//////////////////////////////////////////////////////////// +template +inline Vector3::Vector3(T X, T Y) : +x(X), +y(Y), +z(0) +{ + +} + + //////////////////////////////////////////////////////////// template inline Vector3::Vector3(T X, T Y, T Z) : @@ -45,6 +56,16 @@ z(Z) } +//////////////////////////////////////////////////////////// +template +inline Vector3::Vector3(const Vector2& vector) : +x(vector.x), +y(vector.y), +z(0) +{ +} + + //////////////////////////////////////////////////////////// template template diff --git a/src/cpp3ds/Graphics/Billboard.cpp b/src/cpp3ds/Graphics/Billboard.cpp new file mode 100644 index 0000000..28a0301 --- /dev/null +++ b/src/cpp3ds/Graphics/Billboard.cpp @@ -0,0 +1,88 @@ +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include + + +namespace cpp3ds +{ +//////////////////////////////////////////////////////////// +Billboard::Billboard() : +m_camera(NULL) +{ +} + + +//////////////////////////////////////////////////////////// +Billboard::Billboard(const Texture& texture) : +Sprite (texture), +m_camera(NULL) +{ +} + + +//////////////////////////////////////////////////////////// +Billboard::Billboard(const Texture& texture, const IntRect& rectangle) : +Sprite (texture, rectangle), +m_camera(NULL) +{ +} + + +//////////////////////////////////////////////////////////// +void Billboard::draw(RenderTarget& target, RenderStates states) const +{ + if (m_camera) + { + Vector3f pos = getPosition(); + + // Construct the basis of our rotation matrix + Vector3f zAxis = m_camera->getPosition() - pos; + float zAxisNorm = std::sqrt(zAxis.x * zAxis.x + + zAxis.y * zAxis.y + + zAxis.z * zAxis.z); + zAxis /= zAxisNorm; + + Vector3f xAxis(zAxis.z, 0.f, -zAxis.x); + float xAxisNorm = std::sqrt(xAxis.x * xAxis.x + + xAxis.y * xAxis.y + + xAxis.z * xAxis.z); + xAxis /= xAxisNorm; + + Vector3f yAxis(zAxis.y * xAxis.z - zAxis.z * xAxis.y, + zAxis.z * xAxis.x - zAxis.x * xAxis.z, + zAxis.x * xAxis.y - zAxis.y * xAxis.x); + + // No need to normalize y axis, x and z orthogonal + + // Combine a translation, rotation and translation into 1 transform + pos -= xAxis * pos.x + yAxis * pos.y + zAxis * pos.z; + + Transform billboardTransform(xAxis.x, yAxis.x, zAxis.x, pos.x, + xAxis.y, yAxis.y, zAxis.y, pos.y, + xAxis.z, yAxis.z, zAxis.z, pos.z, + 0.f, 0.f, 0.f, 1.f); + + states.transform *= billboardTransform; + } + + Sprite::draw(target, states); +} + + +//////////////////////////////////////////////////////////// +void Billboard::setCamera(const Camera& camera) +{ + m_camera = &camera; +} + + +//////////////////////////////////////////////////////////// +const Camera* Billboard::getCamera() const +{ + return m_camera; +} + +} // namespace cpp3ds diff --git a/src/cpp3ds/Graphics/Camera.cpp b/src/cpp3ds/Graphics/Camera.cpp new file mode 100644 index 0000000..449d8a8 --- /dev/null +++ b/src/cpp3ds/Graphics/Camera.cpp @@ -0,0 +1,256 @@ +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace cpp3ds +{ +//////////////////////////////////////////////////////////// +Camera::Camera(float fov, float near, float far) : +m_fieldOfView(fov), +m_nearPlane (near), +m_farPlane (far), +m_direction (0, 0, -1), +m_upVector (0, 1, 0), +m_scale (1, 1, 1) +{ + setPosition(0, 0, 0); +} + + +//////////////////////////////////////////////////////////// +Camera::~Camera() +{ + +} + + +//////////////////////////////////////////////////////////// +void Camera::setFieldOfView(float fov) +{ + m_fieldOfView = fov; + m_transformUpdated = false; + m_invTransformUpdated = false; +} + + +//////////////////////////////////////////////////////////// +float Camera::getFieldOfView() const +{ + return m_fieldOfView; +} + + +//////////////////////////////////////////////////////////// +void Camera::setNearClippingPlane(float distance) +{ + m_nearPlane = distance; + m_transformUpdated = false; + m_invTransformUpdated = false; +} + + +//////////////////////////////////////////////////////////// +float Camera::getNearClippingPlane() const +{ + return m_nearPlane; +} + + +//////////////////////////////////////////////////////////// +void Camera::setFarClippingPlane(float distance) +{ + m_farPlane = distance; + m_transformUpdated = false; + m_invTransformUpdated = false; +} + + +//////////////////////////////////////////////////////////// +float Camera::getFarClippingPlane() const +{ + return m_farPlane; +} + + +//////////////////////////////////////////////////////////// +void Camera::setPosition(float x, float y, float z) +{ + setPosition(Vector3f(x, y, z)); +} + + +//////////////////////////////////////////////////////////// +void Camera::setPosition(const Vector3f& position) +{ + setCenter(position); +} + + +//////////////////////////////////////////////////////////// +const Vector3f& Camera::getPosition() const +{ + return getCenter(); +} + + +//////////////////////////////////////////////////////////// +void Camera::setDirection(float x, float y, float z) +{ + setDirection(Vector3f(x, y, z)); +} + + +//////////////////////////////////////////////////////////// +void Camera::setDirection(const Vector3f& direction) +{ + m_direction = direction; + m_viewTransformUpdated = false; + m_invViewTransformUpdated = false; +} + + +//////////////////////////////////////////////////////////// +const Vector3f& Camera::getDirection() const +{ + return m_direction; +} + + +//////////////////////////////////////////////////////////// +void Camera::setUpVector(float x, float y, float z) +{ + setUpVector(Vector3f(x, y, z)); +} + + +//////////////////////////////////////////////////////////// +void Camera::setUpVector(const Vector3f& upVector) +{ + m_upVector = upVector; + m_viewTransformUpdated = false; + m_invViewTransformUpdated = false; +} + + +//////////////////////////////////////////////////////////// +const Vector3f& Camera::getUpVector() const +{ + return m_upVector; +} + + +//////////////////////////////////////////////////////////// +void Camera::setScale(float factorX, float factorY, float factorZ) +{ + setScale(Vector3f(factorX, factorY, factorZ)); +} + + +//////////////////////////////////////////////////////////// +void Camera::setScale(const Vector3f& factors) +{ + m_scale = factors; + m_transformUpdated = false; + m_invTransformUpdated = false; +} + + +//////////////////////////////////////////////////////////// +const Vector3f& Camera::getScale() const +{ + return m_scale; +} + + +//////////////////////////////////////////////////////////// +void Camera::scale(float factorX, float factorY, float factorZ) +{ + m_scale.x *= factorX; + m_scale.y *= factorY; + m_scale.z *= factorZ; + m_transformUpdated = false; + m_invTransformUpdated = false; +} + + +//////////////////////////////////////////////////////////// +void Camera::scale(const Vector3f& factor) +{ + scale(factor.x, factor.y, factor.z); +} + + +//////////////////////////////////////////////////////////// +const Transform& Camera::getTransform() const +{ + // Recompute the perspective projection matrix if needed + if (!m_transformUpdated) + { + float radians = m_fieldOfView / 2.f * 3.141592654f / 180.f; + + // Projection components + float f = 1.f / std::tan(radians / 2.f); + float g = (m_farPlane + m_nearPlane) / (m_nearPlane - m_farPlane); + float h = 2.f * (m_farPlane * m_nearPlane) / (m_nearPlane - m_farPlane); + + // Rebuild the projection matrix + m_transform = Transform(m_scale.x * f, 0.f, 0.f, 0.f, + 0.f, m_scale.y * f, 0.f, 0.f, + 0.f, 0.f, m_scale.z * g, h, + 0.f, 0.f, -m_scale.z, 0.f); + + m_transformUpdated = true; + } + + return m_transform; +} + + +//////////////////////////////////////////////////////////// +const Transform& Camera::getViewTransform() const +{ + // Recompute the view matrix if needed + if (!m_viewTransformUpdated) + { + // View components + float directionNorm = std::sqrt(m_direction.x * m_direction.x + + m_direction.y * m_direction.y + + m_direction.z * m_direction.z); + Vector3f d = m_direction / directionNorm; + + float upNorm = std::sqrt(m_upVector.x * m_upVector.x + + m_upVector.y * m_upVector.y + + m_upVector.z * m_upVector.z); + Vector3f un = m_upVector / upNorm; + + Vector3f s(d.y * un.z - d.z * un.y, + d.z * un.x - d.x * un.z, + d.x * un.y - d.y * un.x); + + float sNorm = std::sqrt(s.x * s.x + + s.y * s.y + + s.z * s.z); + Vector3f sn = s / sNorm; + + Vector3f u(sn.y * d.z - sn.z * d.y, + sn.z * d.x - sn.x * d.z, + sn.x * d.y - sn.y * d.x); + + // Rebuild the view matrix + m_viewTransform = Transform( s.x, s.y, s.z, 0.f, + u.x, u.y, u.z, 0.f, + -d.x, -d.y, -d.z, 0.f, + 0.f, 0.f, 0.f, 1.f); + + m_viewTransform.translate(-getCenter()); + + m_viewTransformUpdated = true; + } + + return m_viewTransform; +} + +} // namespace cpp3ds diff --git a/src/cpp3ds/Graphics/CircleShape.cpp b/src/cpp3ds/Graphics/CircleShape.cpp index c2e05f5..e3e4736 100644 --- a/src/cpp3ds/Graphics/CircleShape.cpp +++ b/src/cpp3ds/Graphics/CircleShape.cpp @@ -71,7 +71,7 @@ unsigned int CircleShape::getPointCount() const //////////////////////////////////////////////////////////// -Vector2f CircleShape::getPoint(unsigned int index) const +Vector3f CircleShape::getPoint(unsigned int index) const { static const float pi = 3.141592654f; @@ -79,7 +79,7 @@ Vector2f CircleShape::getPoint(unsigned int index) const float x = std::cos(angle) * m_radius; float y = std::sin(angle) * m_radius; - return Vector2f(m_radius + x, m_radius + y); + return Vector3f(m_radius + x, m_radius + y); } } // namespace cpp3ds diff --git a/src/cpp3ds/Graphics/ConvexPolyhedron.cpp b/src/cpp3ds/Graphics/ConvexPolyhedron.cpp new file mode 100644 index 0000000..1ae6d16 --- /dev/null +++ b/src/cpp3ds/Graphics/ConvexPolyhedron.cpp @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace cpp3ds +{ +//////////////////////////////////////////////////////////// +ConvexPolyhedron::ConvexPolyhedron(unsigned int faceCount) +{ + setFaceCount(faceCount); +} + + +//////////////////////////////////////////////////////////// +void ConvexPolyhedron::setFaceCount(unsigned int count) +{ + m_vertices.resize(count * 3); + update(); +} + + +//////////////////////////////////////////////////////////// +unsigned int ConvexPolyhedron::getFaceCount() const +{ + return static_cast(m_vertices.size() / 3); +} + + +//////////////////////////////////////////////////////////// +void ConvexPolyhedron::setFace(unsigned int index, const Vertex& v0, const Vertex& v1, const Vertex& v2) +{ + m_vertices[index * 3 + 0] = v0; + m_vertices[index * 3 + 1] = v1; + m_vertices[index * 3 + 2] = v2; + update(); +} + + +//////////////////////////////////////////////////////////// +ConvexPolyhedron::Face ConvexPolyhedron::getFace(unsigned int index) const +{ + ConvexPolyhedron::Face face = { m_vertices[index * 3 + 0], m_vertices[index * 3 + 1], m_vertices[index * 3 + 2] }; + return face; +} + +} // namespace cpp3ds diff --git a/src/cpp3ds/Graphics/ConvexShape.cpp b/src/cpp3ds/Graphics/ConvexShape.cpp index f21ef54..af75ff4 100644 --- a/src/cpp3ds/Graphics/ConvexShape.cpp +++ b/src/cpp3ds/Graphics/ConvexShape.cpp @@ -53,7 +53,7 @@ unsigned int ConvexShape::getPointCount() const //////////////////////////////////////////////////////////// -void ConvexShape::setPoint(unsigned int index, const Vector2f& point) +void ConvexShape::setPoint(unsigned int index, const Vector3f& point) { m_points[index] = point; update(); @@ -61,7 +61,7 @@ void ConvexShape::setPoint(unsigned int index, const Vector2f& point) //////////////////////////////////////////////////////////// -Vector2f ConvexShape::getPoint(unsigned int index) const +Vector3f ConvexShape::getPoint(unsigned int index) const { return m_points[index]; } diff --git a/src/cpp3ds/Graphics/Cuboid.cpp b/src/cpp3ds/Graphics/Cuboid.cpp new file mode 100644 index 0000000..443b4a6 --- /dev/null +++ b/src/cpp3ds/Graphics/Cuboid.cpp @@ -0,0 +1,81 @@ +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace cpp3ds +{ +//////////////////////////////////////////////////////////// +Cuboid::Cuboid(const Vector3f& size) +{ + setSize(size); +} + + +//////////////////////////////////////////////////////////// +void Cuboid::setSize(const Vector3f& size) +{ + m_size = size; + update(); + generateNormals(); +} + + +//////////////////////////////////////////////////////////// +const Vector3f& Cuboid::getSize() const +{ + return m_size; +} + + +//////////////////////////////////////////////////////////// +unsigned int Cuboid::getFaceCount() const +{ + return 12; +} + + +//////////////////////////////////////////////////////////// +Polyhedron::Face Cuboid::getFace(unsigned int index) const +{ + float left = m_size.x / -2.f; + float top = m_size.y / 2.f; + float front = m_size.z / 2.f; + float right = m_size.x / 2.f; + float bottom = m_size.y / -2.f; + float back = m_size.z / -2.f; + + const Color& color = getColor(); + + switch (index) + { + default: + // Front 1 + case 0: { Face face = {Vertex(Vector3f(left, top, front), color), Vertex(Vector3f(left, bottom, front), color), Vertex(Vector3f(right, bottom, front), color)}; return face; } + // Front 2 + case 1: { Face face = {Vertex(Vector3f(left, top, front), color), Vertex(Vector3f(right, bottom, front), color), Vertex(Vector3f(right, top, front), color)}; return face; } + // Right 1 + case 2: { Face face = {Vertex(Vector3f(right, top, front), color), Vertex(Vector3f(right, bottom, front), color), Vertex(Vector3f(right, bottom, back), color)}; return face; } + // Right 2 + case 3: { Face face = {Vertex(Vector3f(right, top, front), color), Vertex(Vector3f(right, bottom, back), color), Vertex(Vector3f(right, top, back), color)}; return face; } + // Back 1 + case 4: { Face face = {Vertex(Vector3f(right, top, back), color), Vertex(Vector3f(right, bottom, back), color), Vertex(Vector3f(left, bottom, back), color)}; return face; } + // Back 2 + case 5: { Face face = {Vertex(Vector3f(right, top, back), color), Vertex(Vector3f(left, bottom, back), color), Vertex(Vector3f(left, top, back), color)}; return face; } + // Left 1 + case 6: { Face face = {Vertex(Vector3f(left, top, back), color), Vertex(Vector3f(left, bottom, back), color), Vertex(Vector3f(left, bottom, front), color)}; return face; } + // Left 2 + case 7: { Face face = {Vertex(Vector3f(left, top, back), color), Vertex(Vector3f(left, bottom, front), color), Vertex(Vector3f(left, top, front), color)}; return face; } + // Top 1 + case 8: { Face face = {Vertex(Vector3f(left, top, back), color), Vertex(Vector3f(left, top, front), color), Vertex(Vector3f(right, top, front), color)}; return face; } + // Top 2 + case 9: { Face face = {Vertex(Vector3f(left, top, back), color), Vertex(Vector3f(right, top, front), color), Vertex(Vector3f(right, top, back), color)}; return face; } + // Bottom 1 + case 10: { Face face = {Vertex(Vector3f(left, bottom, front), color), Vertex(Vector3f(left, bottom, back), color), Vertex(Vector3f(right, bottom, back), color)}; return face; } + // Bottom 2 + case 11: { Face face = {Vertex(Vector3f(left, bottom, front), color), Vertex(Vector3f(right, bottom, back), color), Vertex(Vector3f(right, bottom, front), color)}; return face; } + } +} + +} // namespace cpp3ds diff --git a/src/cpp3ds/Graphics/Light.cpp b/src/cpp3ds/Graphics/Light.cpp new file mode 100644 index 0000000..ff0251b --- /dev/null +++ b/src/cpp3ds/Graphics/Light.cpp @@ -0,0 +1,775 @@ +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace +{ + // Static light data and its mutex + cpp3ds::Mutex mutex; + unsigned int count = 0; + std::vector usedIds; + std::set enabledLights; + bool lightingEnabled = false; + cpp3ds::VertexBuffer* lightUniformBuffer = NULL; + bool lightUniformBufferNeedUpload = true; +} + + +namespace cpp3ds +{ + +//////////////////////////////////////////////////////////// +Light::Light() : +m_light (-1), +m_position (0, 0, 0), +m_directional (false), +m_color (Color::White), +m_ambientIntensity (0.f), +m_diffuseIntensity (1.f), +m_specularIntensity (1.f), +m_constantAttenuation (1.f), +m_linearAttenuation (0.f), +m_quadraticAttenuation(0.f), +m_enabled (false) +{ + getId(); + + if (m_light < 0) + return; + + setNeedUniformUpload(); + + if (hasShaderLighting()) + return; + + GLfloat position[] = {0.0f, 0.0f, 0.0f, 1.0f}; + glCheck(glLightfv(GL_LIGHT0 + m_light, GL_POSITION, position)); + + GLfloat ambientColor[] = {0.0f, 0.0f, 0.0f, 1.0f}; + glCheck(glLightfv(GL_LIGHT0 + m_light, GL_AMBIENT, ambientColor)); + + GLfloat diffuseColor[] = {1.0f, 1.0f, 1.0f, 1.0f}; + glCheck(glLightfv(GL_LIGHT0 + m_light, GL_DIFFUSE, diffuseColor)); + + GLfloat specularColor[] = {1.0f, 1.0f, 1.0f, 1.0f}; + glCheck(glLightfv(GL_LIGHT0 + m_light, GL_SPECULAR, specularColor)); + + glCheck(glLightf(GL_LIGHT0 + m_light, GL_CONSTANT_ATTENUATION, 1.0f)); + glCheck(glLightf(GL_LIGHT0 + m_light, GL_LINEAR_ATTENUATION, 0.0f)); + glCheck(glLightf(GL_LIGHT0 + m_light, GL_QUADRATIC_ATTENUATION, 0.0f)); + + glCheck(glDisable(GL_LIGHT0 + m_light)); +} + + +//////////////////////////////////////////////////////////// +Light::Light(const Light& copy) : +m_light (-1), +m_position (copy.m_position), +m_directional (copy.m_directional), +m_color (copy.m_color), +m_ambientIntensity (copy.m_ambientIntensity), +m_diffuseIntensity (copy.m_diffuseIntensity), +m_specularIntensity (copy.m_specularIntensity), +m_constantAttenuation (copy.m_constantAttenuation), +m_linearAttenuation (copy.m_linearAttenuation), +m_quadraticAttenuation(copy.m_quadraticAttenuation), +m_enabled (false) +{ + getId(); + + if (m_light < 0) + return; + + setNeedUniformUpload(); + + // If this is a directional light source, normalize the direction vector + if (m_directional) + { + float norm = std::sqrt(m_position.x * m_position.x + m_position.y * m_position.y + m_position.z * m_position.z); + + m_position /= norm; + } + + if (hasShaderLighting()) + return; + + GLfloat position[] = {m_position.x, m_position.y, m_position.z, m_directional ? 0.0f : 1.0f}; + glCheck(glLightfv(GL_LIGHT0 + m_light, GL_POSITION, position)); + + GLfloat ambientColor[] = {static_cast(m_color.r) / 255.f * m_ambientIntensity, + static_cast(m_color.g) / 255.f * m_ambientIntensity, + static_cast(m_color.b) / 255.f * m_ambientIntensity, + static_cast(m_color.a) / 255.f * m_ambientIntensity}; + glCheck(glLightfv(GL_LIGHT0 + m_light, GL_AMBIENT, ambientColor)); + + GLfloat diffuseColor[] = {static_cast(m_color.r) / 255.f * m_diffuseIntensity, + static_cast(m_color.g) / 255.f * m_diffuseIntensity, + static_cast(m_color.b) / 255.f * m_diffuseIntensity, + static_cast(m_color.a) / 255.f * m_diffuseIntensity}; + glCheck(glLightfv(GL_LIGHT0 + m_light, GL_DIFFUSE, diffuseColor)); + + GLfloat specularColor[] = {static_cast(m_color.r) / 255.f * m_specularIntensity, + static_cast(m_color.g) / 255.f * m_specularIntensity, + static_cast(m_color.b) / 255.f * m_specularIntensity, + static_cast(m_color.a) / 255.f * m_specularIntensity}; + glCheck(glLightfv(GL_LIGHT0 + m_light, GL_SPECULAR, specularColor)); + + glCheck(glLightf(GL_LIGHT0 + m_light, GL_CONSTANT_ATTENUATION, m_constantAttenuation)); + glCheck(glLightf(GL_LIGHT0 + m_light, GL_LINEAR_ATTENUATION, m_linearAttenuation)); + glCheck(glLightf(GL_LIGHT0 + m_light, GL_QUADRATIC_ATTENUATION, m_quadraticAttenuation)); + + glCheck(glDisable(GL_LIGHT0 + m_light)); +} + + +//////////////////////////////////////////////////////////// +Light::~Light() +{ + disable(); + + if (m_light >= 0) + { + Lock lock(mutex); + usedIds[m_light] = false; + } +} + + +//////////////////////////////////////////////////////////// +void Light::setDirectional(bool directional) +{ + m_directional = directional; + + if (m_light < 0) + return; + + setNeedUniformUpload(); + + // If this becomes a directional light source, normalize the direction vector + if (m_directional) + { + float norm = std::sqrt(m_position.x * m_position.x + m_position.y * m_position.y + m_position.z * m_position.z); + + m_position /= norm; + } + + if (hasShaderLighting()) + return; + + GLfloat position[] = {m_position.x, m_position.y, m_position.z, m_directional ? 0.0f : 1.0f}; + glCheck(glLightfv(GL_LIGHT0 + m_light, GL_POSITION, position)); +} + + +//////////////////////////////////////////////////////////// +void Light::setPosition(float x, float y, float z) +{ + setPosition(Vector3f(x, y, z)); +} + + +//////////////////////////////////////////////////////////// +void Light::setPosition(const Vector3f& position) +{ + m_position = position; + + if (m_light < 0) + return; + + setNeedUniformUpload(); + + // If this is a directional light source, normalize the direction vector + if (m_directional) + { + float norm = std::sqrt(m_position.x * m_position.x + m_position.y * m_position.y + m_position.z * m_position.z); + + m_position /= norm; + } + + if (hasShaderLighting()) + return; + + GLfloat glPosition[] = {m_position.x, m_position.y, m_position.z, m_directional ? 0.0f : 1.0f}; + glCheck(glLightfv(GL_LIGHT0 + m_light, GL_POSITION, glPosition)); +} + + +//////////////////////////////////////////////////////////// +const Vector3f& Light::getPosition() const +{ + return m_position; +} + + +//////////////////////////////////////////////////////////// +void Light::setDirection(float x, float y, float z) +{ + setPosition(Vector3f(x, y, z)); +} + + +//////////////////////////////////////////////////////////// +void Light::setDirection(const Vector3f& position) +{ + setPosition(position); +} + + +//////////////////////////////////////////////////////////// +const Vector3f& Light::getDirection() const +{ + return m_position; +} + + +//////////////////////////////////////////////////////////// +void Light::setColor(const Color& color) +{ + m_color = color; + + if (m_light < 0) + return; + + setNeedUniformUpload(); + + if (hasShaderLighting()) + return; + + GLfloat ambientColor[] = {static_cast(m_color.r) / 255.f * m_ambientIntensity, + static_cast(m_color.g) / 255.f * m_ambientIntensity, + static_cast(m_color.b) / 255.f * m_ambientIntensity, + static_cast(m_color.a) / 255.f * m_ambientIntensity}; + glCheck(glLightfv(GL_LIGHT0 + m_light, GL_AMBIENT, ambientColor)); + + GLfloat diffuseColor[] = {static_cast(m_color.r) / 255.f * m_diffuseIntensity, + static_cast(m_color.g) / 255.f * m_diffuseIntensity, + static_cast(m_color.b) / 255.f * m_diffuseIntensity, + static_cast(m_color.a) / 255.f * m_diffuseIntensity}; + glCheck(glLightfv(GL_LIGHT0 + m_light, GL_DIFFUSE, diffuseColor)); + + GLfloat specularColor[] = {static_cast(m_color.r) / 255.f * m_specularIntensity, + static_cast(m_color.g) / 255.f * m_specularIntensity, + static_cast(m_color.b) / 255.f * m_specularIntensity, + static_cast(m_color.a) / 255.f * m_specularIntensity}; + glCheck(glLightfv(GL_LIGHT0 + m_light, GL_SPECULAR, specularColor)); +} + + +//////////////////////////////////////////////////////////// +const Color& Light::getColor() const +{ + return m_color; +} + + +//////////////////////////////////////////////////////////// +void Light::setAmbientIntensity(float intensity) +{ + m_ambientIntensity = intensity; + + if (m_light < 0) + return; + + setNeedUniformUpload(); + + if (hasShaderLighting()) + return; + + GLfloat ambientColor[] = {static_cast(m_color.r) / 255.f * m_ambientIntensity, + static_cast(m_color.g) / 255.f * m_ambientIntensity, + static_cast(m_color.b) / 255.f * m_ambientIntensity, + static_cast(m_color.a) / 255.f * m_ambientIntensity}; + glCheck(glLightfv(GL_LIGHT0 + m_light, GL_AMBIENT, ambientColor)); +} + + +//////////////////////////////////////////////////////////// +float Light::getAmbientIntensity() const +{ + return m_ambientIntensity; +} + + +//////////////////////////////////////////////////////////// +void Light::setDiffuseIntensity(float intensity) +{ + m_diffuseIntensity = intensity; + + if (m_light < 0) + return; + + setNeedUniformUpload(); + + if (hasShaderLighting()) + return; + + GLfloat diffuseColor[] = {static_cast(m_color.r) / 255.f * m_diffuseIntensity, + static_cast(m_color.g) / 255.f * m_diffuseIntensity, + static_cast(m_color.b) / 255.f * m_diffuseIntensity, + static_cast(m_color.a) / 255.f * m_diffuseIntensity}; + glCheck(glLightfv(GL_LIGHT0 + m_light, GL_DIFFUSE, diffuseColor)); +} + + +//////////////////////////////////////////////////////////// +float Light::getDiffuseIntensity() const +{ + return m_diffuseIntensity; +} + + +//////////////////////////////////////////////////////////// +void Light::setSpecularIntensity(float intensity) +{ + m_specularIntensity = intensity; + + if (m_light < 0) + return; + + setNeedUniformUpload(); + + if (hasShaderLighting()) + return; + + GLfloat specularColor[] = {static_cast(m_color.r) / 255.f * m_specularIntensity, + static_cast(m_color.g) / 255.f * m_specularIntensity, + static_cast(m_color.b) / 255.f * m_specularIntensity, + static_cast(m_color.a) / 255.f * m_specularIntensity}; + glCheck(glLightfv(GL_LIGHT0 + m_light, GL_SPECULAR, specularColor)); +} + + +//////////////////////////////////////////////////////////// +float Light::getSpecularIntensity() const +{ + return m_specularIntensity; +} + + +//////////////////////////////////////////////////////////// +void Light::setConstantAttenuation(float attenuation) +{ + m_constantAttenuation = attenuation; + + if (m_light < 0) + return; + + setNeedUniformUpload(); + + if (hasShaderLighting()) + return; + + glCheck(glLightf(GL_LIGHT0 + m_light, GL_CONSTANT_ATTENUATION, m_constantAttenuation)); +} + + +//////////////////////////////////////////////////////////// +float Light::getConstantAttenuation() const +{ + return m_constantAttenuation; +} + + +//////////////////////////////////////////////////////////// +void Light::setLinearAttenuation(float attenuation) +{ + m_linearAttenuation = attenuation; + + if (m_light < 0) + return; + + setNeedUniformUpload(); + + if (hasShaderLighting()) + return; + + glCheck(glLightf(GL_LIGHT0 + m_light, GL_LINEAR_ATTENUATION, m_linearAttenuation)); +} + + +//////////////////////////////////////////////////////////// +float Light::getLinearAttenuation() const +{ + return m_linearAttenuation; +} + + +//////////////////////////////////////////////////////////// +void Light::setQuadraticAttenuation(float attenuation) +{ + m_quadraticAttenuation = attenuation; + + if (m_light < 0) + return; + + setNeedUniformUpload(); + + if (hasShaderLighting()) + return; + + glCheck(glLightf(GL_LIGHT0 + m_light, GL_QUADRATIC_ATTENUATION, m_quadraticAttenuation)); +} + + +//////////////////////////////////////////////////////////// +float Light::getQuadraticAttenuation() const +{ + return m_quadraticAttenuation; +} + + +//////////////////////////////////////////////////////////// +void Light::move(float offsetX, float offsetY, float offsetZ) +{ + move(Vector3f(offsetX, offsetY, offsetZ)); +} + + +//////////////////////////////////////////////////////////// +void Light::move(const Vector3f& offset) +{ + setPosition(m_position + offset); +} + + +//////////////////////////////////////////////////////////// +bool Light::isDirectional() const +{ + return m_directional; +} + + +//////////////////////////////////////////////////////////// +void Light::enable() +{ + if (m_light < 0) + return; + + setNeedUniformUpload(); + + Lock lock(mutex); + enabledLights.insert(this); + + if (hasShaderLighting()) + return; + + glCheck(glEnable(GL_LIGHT0 + m_light)); +} + + +//////////////////////////////////////////////////////////// +void Light::disable() +{ + if (m_light < 0) + return; + + setNeedUniformUpload(); + + Lock lock(mutex); + enabledLights.erase(this); + + if (hasShaderLighting()) + return; + + glCheck(glDisable(GL_LIGHT0 + m_light)); +} + + +//////////////////////////////////////////////////////////// +unsigned int Light::getMaximumLights() +{ + ensureGlContext(); + + if (hasShaderLighting()) + { + // hasShaderLighting() guarantees this will be a sane value + return (Shader::getMaximumUniformComponents() - 256) / 128; + } + + GLint maxLights = 0; + glCheck(glGetIntegerv(GL_MAX_LIGHTS, &maxLights)); + + return static_cast(maxLights); +} + + +//////////////////////////////////////////////////////////// +void Light::enableLighting() +{ + ensureGlContext(); + + Lock lock(mutex); + lightingEnabled = true; + + if (hasShaderLighting()) + return; + + glCheck(glEnable(GL_LIGHTING)); +} + + +//////////////////////////////////////////////////////////// +void Light::disableLighting() +{ + ensureGlContext(); + + Lock lock(mutex); + lightingEnabled = false; + + if (hasShaderLighting()) + return; + + glCheck(glDisable(GL_LIGHTING)); +} + + +//////////////////////////////////////////////////////////// +bool Light::isLightingEnabled() +{ + Lock lock(mutex); + return lightingEnabled; +} + + +//////////////////////////////////////////////////////////// +bool Light::hasShaderLighting() +{ + Lock lock(mutex); + + static bool checked = false; + static bool shaderLightingSupported = false; + if (!checked) + { + checked = true; + + double versionNumber = 0.0; + std::istringstream versionStringStream(Shader::getSupportedVersion()); + versionStringStream >> versionNumber; + +// Disable non-legacy pipeline if requested +#if defined(SFML_LEGACY_GL) + versionNumber = 0.0; +#endif + + // This will only succeed if the supported version is not GLSL ES + if (versionNumber > 1.29) + { + unsigned int maxUniformComponents = Shader::getMaximumUniformComponents(); + + GLint maxLegacyLights = 0; + glCheck(glGetIntegerv(GL_MAX_LIGHTS, &maxLegacyLights)); + + unsigned int requiredUniformComponents = maxLegacyLights * 128 + 256; + + if (maxUniformComponents >= requiredUniformComponents) + shaderLightingSupported = true; + } + } + + return shaderLightingSupported; +} + + +//////////////////////////////////////////////////////////// +void Light::increaseLightReferences() +{ + ensureGlContext(); + + Lock lock(mutex); + +// if (!count && Shader::isUniformBufferAvailable()) +// { +// lightUniformBuffer = new VertexBuffer; +// setNeedUniformUpload(); +// } + + count++; +} + + +//////////////////////////////////////////////////////////// +void Light::decreaseLightReferences() +{ + ensureGlContext(); + + Lock lock(mutex); + count--; + + if (!count) + { + delete lightUniformBuffer; + lightUniformBuffer = NULL; + } +} + + +//////////////////////////////////////////////////////////// +Light& Light::operator =(const Light& right) +{ + Light temp(right); + + std::swap(m_light, temp.m_light); + std::swap(m_position, temp.m_position); + std::swap(m_directional, temp.m_directional); + std::swap(m_color, temp.m_color); + std::swap(m_ambientIntensity, temp.m_ambientIntensity); + std::swap(m_diffuseIntensity, temp.m_diffuseIntensity); + std::swap(m_specularIntensity, temp.m_specularIntensity); + std::swap(m_constantAttenuation, temp.m_constantAttenuation); + std::swap(m_linearAttenuation, temp.m_linearAttenuation); + std::swap(m_quadraticAttenuation, temp.m_quadraticAttenuation); + std::swap(m_enabled, temp.m_enabled); + + return *this; +} + + +//////////////////////////////////////////////////////////// +void Light::getId() +{ + Lock lock(mutex); + + if (usedIds.empty()) + usedIds.resize(getMaximumLights(), false); + + for (std::size_t i = 0; i < usedIds.size(); ++i) + { + if (!usedIds[i]) + { + m_light = static_cast(i); + usedIds[i] = true; + return; + } + } + +#ifdef SFML_DEBUG + // Inform the user that they created too many lights + // for the fixed function pipeline to handle + err() << "Not enough OpenGL lights to support creating " + << "more cpp3ds::Light objects." + << std::endl; +#endif +} + + +//////////////////////////////////////////////////////////// +void Light::addLightsToShader(const Shader& shader) +{ + if (!lightingEnabled) + { + shader.setParameter("sf_LightingEnabled", 0); + return; + } + + shader.setParameter("sf_LightingEnabled", 1); + +// if (!Shader::isUniformBufferAvailable()) +// { + for (std::set::const_iterator i = enabledLights.begin(); i != enabledLights.end(); ++i) + { + const Light& light = *(*i); + + if (light.m_shaderElement.empty()) + { + std::ostringstream shaderElement; + shaderElement << "sf_Lights[" << light.m_light << "]"; + light.m_shaderElement = shaderElement.str(); + } + + shader.setParameter(light.m_shaderElement + ".ambientColor", light.m_color.r * light.m_ambientIntensity / 255.f, + light.m_color.g * light.m_ambientIntensity / 255.f, + light.m_color.b * light.m_ambientIntensity / 255.f, + light.m_color.a * light.m_ambientIntensity / 255.f); + shader.setParameter(light.m_shaderElement + ".diffuseColor", light.m_color.r * light.m_diffuseIntensity / 255.f, + light.m_color.g * light.m_diffuseIntensity / 255.f, + light.m_color.b * light.m_diffuseIntensity / 255.f, + light.m_color.a * light.m_diffuseIntensity / 255.f); + shader.setParameter(light.m_shaderElement + ".specularColor", light.m_color.r * light.m_specularIntensity / 255.f, + light.m_color.g * light.m_specularIntensity / 255.f, + light.m_color.b * light.m_specularIntensity / 255.f, + light.m_color.a * light.m_specularIntensity / 255.f); + shader.setParameter(light.m_shaderElement + ".positionDirection", light.m_position.x, + light.m_position.y, + light.m_position.z, + light.m_directional ? 0.f : 1.f); + shader.setParameter(light.m_shaderElement + ".attenuation", light.m_constantAttenuation, + light.m_linearAttenuation, + light.m_quadraticAttenuation, + 1.f); + } +// } +// else if (lightUniformBuffer) +// { +// { +// Lock lock(mutex); +// +// if (lightUniformBufferNeedUpload) +// { +// lightUniformBufferNeedUpload = false; +// +// std::size_t bytesNeeded = enabledLights.size() * 20 * sizeof(float); +// +// // Make sure we have enough space for our lighting data +// lightUniformBuffer->resize((bytesNeeded / sizeof(Vertex)) + 1); +// float* dataPointer = static_cast(lightUniformBuffer->getPointer()); +// std::size_t index = 0; +// +// for (std::set::const_iterator i = enabledLights.begin(); i != enabledLights.end(); ++i, ++index) +// { +// const Light& light = *(*i); +// +// dataPointer[index * 20 + 0] = light.m_color.r * light.m_ambientIntensity / 255.f; +// dataPointer[index * 20 + 1] = light.m_color.g * light.m_ambientIntensity / 255.f; +// dataPointer[index * 20 + 2] = light.m_color.b * light.m_ambientIntensity / 255.f; +// dataPointer[index * 20 + 3] = light.m_color.a * light.m_ambientIntensity / 255.f; +// dataPointer[index * 20 + 4] = light.m_color.r * light.m_diffuseIntensity / 255.f; +// dataPointer[index * 20 + 5] = light.m_color.g * light.m_diffuseIntensity / 255.f; +// dataPointer[index * 20 + 6] = light.m_color.b * light.m_diffuseIntensity / 255.f; +// dataPointer[index * 20 + 7] = light.m_color.a * light.m_diffuseIntensity / 255.f; +// dataPointer[index * 20 + 8] = light.m_color.r * light.m_specularIntensity / 255.f; +// dataPointer[index * 20 + 9] = light.m_color.g * light.m_specularIntensity / 255.f; +// dataPointer[index * 20 + 10] = light.m_color.b * light.m_specularIntensity / 255.f; +// dataPointer[index * 20 + 11] = light.m_color.a * light.m_specularIntensity / 255.f; +// dataPointer[index * 20 + 12] = light.m_position.x; +// dataPointer[index * 20 + 13] = light.m_position.y; +// dataPointer[index * 20 + 14] = light.m_position.z; +// dataPointer[index * 20 + 15] = light.m_directional ? 0.f : 1.f; +// dataPointer[index * 20 + 16] = light.m_constantAttenuation; +// dataPointer[index * 20 + 17] = light.m_linearAttenuation; +// dataPointer[index * 20 + 18] = light.m_quadraticAttenuation; +// dataPointer[index * 20 + 19] = 1.f; +// } +// } +// } +// +// shader.setBlock("Lights", *lightUniformBuffer); +// } + + shader.setParameter("cpp3ds_LightCount", static_cast(enabledLights.size())); +} + + +//////////////////////////////////////////////////////////// +void Light::setNeedUniformUpload() +{ +// if (!Shader::isUniformBufferAvailable()) + return; + + Lock lock(mutex); + + lightUniformBufferNeedUpload = true; +} + +} // namespace sf diff --git a/src/cpp3ds/Graphics/Model.cpp b/src/cpp3ds/Graphics/Model.cpp new file mode 100644 index 0000000..f146042 --- /dev/null +++ b/src/cpp3ds/Graphics/Model.cpp @@ -0,0 +1,81 @@ +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace cpp3ds +{ +//////////////////////////////////////////////////////////// +Model::~Model() +{ +} + + +//////////////////////////////////////////////////////////// +unsigned int Model::getFaceCount() const +{ + return m_faces.size(); +} + + +//////////////////////////////////////////////////////////// +Polyhedron::Face Model::getFace(unsigned int index) const +{ + Face face = {m_vertices[m_faces[index].index0], + m_vertices[m_faces[index].index1], + m_vertices[m_faces[index].index2]}; + + return face; +} + + +//////////////////////////////////////////////////////////// +Model::Model() +{ +} + + +//////////////////////////////////////////////////////////// +void Model::addVertex(const Vertex& vertex) +{ + m_vertices.push_back(vertex); +} + + +//////////////////////////////////////////////////////////// +void Model::setVertex(unsigned int index, const Vertex& vertex) +{ + m_vertices[index] = vertex; +} + + +//////////////////////////////////////////////////////////// +const Vertex& Model::getVertex(unsigned int index) const +{ + return m_vertices[index]; +} + + +//////////////////////////////////////////////////////////// +unsigned int Model::getVertexCount() const +{ + return m_vertices.size(); +} + + +//////////////////////////////////////////////////////////// +void Model::addFace(unsigned int index0, unsigned int index1, unsigned int index2) +{ + FaceIndices face = {index0, index1, index2}; + m_faces.push_back(face); +} + + +//////////////////////////////////////////////////////////// +void Model::clearFaces() +{ + m_faces.clear(); +} + +} // namespace cpp3ds diff --git a/src/cpp3ds/Graphics/Polyhedron.cpp b/src/cpp3ds/Graphics/Polyhedron.cpp new file mode 100644 index 0000000..aaccc93 --- /dev/null +++ b/src/cpp3ds/Graphics/Polyhedron.cpp @@ -0,0 +1,156 @@ +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include + + +namespace +{ + // Compute the normal of a face given 2 of its edges + cpp3ds::Vector3f computeNormal(const cpp3ds::Vector3f& v1, const cpp3ds::Vector3f& v2) + { + cpp3ds::Vector3f normal(v1.y * v2.z - v1.z * v2.y, + v1.z * v2.x - v1.x * v2.z, + v1.x * v2.y - v1.y * v2.x); + float length = std::sqrt(normal.x * normal.x + + normal.y * normal.y + + normal.z * normal.z); + if (length != 0.f) + normal /= length; + return normal; + } +} + + +namespace cpp3ds +{ +//////////////////////////////////////////////////////////// +Polyhedron::~Polyhedron() +{ +} + + +//////////////////////////////////////////////////////////// +void Polyhedron::setColor(const Color& color) +{ + m_color = color; + + updateColors(); +} + + +//////////////////////////////////////////////////////////// +const Color& Polyhedron::getColor() const +{ + return m_color; +} + + +//////////////////////////////////////////////////////////// +void Polyhedron::setTexture(const Texture* texture) +{ + m_texture = texture; +} + + +//////////////////////////////////////////////////////////// +const Texture* Polyhedron::getTexture() const +{ + return m_texture; +} + + +//////////////////////////////////////////////////////////// +FloatBox Polyhedron::getLocalBounds() const +{ + return m_insideBounds; +} + + +//////////////////////////////////////////////////////////// +FloatBox Polyhedron::getGlobalBounds() const +{ + return getTransform().transformBox(getLocalBounds()); +} + + +//////////////////////////////////////////////////////////// +Polyhedron::Polyhedron() : +m_texture (NULL), +m_color (Color::White), +m_vertices (Triangles), +m_insideBounds() +{ +} + + +//////////////////////////////////////////////////////////// +void Polyhedron::update() const +{ + // Get the total number of faces of the polyhedron + unsigned int count = getFaceCount(); + if (!count) + { + m_vertices.resize(0); + return; + } + + m_vertices.resize(count * 3); + + // Vertices + for (unsigned int i = 0; i < count; ++i) + { + Face face = getFace(i); + + m_vertices[i * 3 + 0] = face.v0; + m_vertices[i * 3 + 1] = face.v1; + m_vertices[i * 3 + 2] = face.v2; + } + + // Update the bounding rectangle + m_insideBounds = m_vertices.getBounds(); +} + + +//////////////////////////////////////////////////////////// +void Polyhedron::draw(RenderTarget& target, RenderStates states) const +{ + states.transform *= getTransform(); + + // Render the inside + states.texture = m_texture; + target.draw(m_vertices, states); +} + + +//////////////////////////////////////////////////////////// +void Polyhedron::generateNormals() +{ + // Get the total number of faces of the polyhedron + unsigned int count = m_vertices.getVertexCount() / 3; + + // Vertices + for (unsigned int i = 0; i < count; ++i) + { + Vector3f normal = computeNormal(m_vertices[i * 3 + 2].position - m_vertices[i * 3 + 1].position, + m_vertices[i * 3 + 0].position - m_vertices[i * 3 + 1].position); + + m_vertices[i * 3 + 0].normal = normal; + m_vertices[i * 3 + 1].normal = normal; + m_vertices[i * 3 + 2].normal = normal; + } +} + + +//////////////////////////////////////////////////////////// +void Polyhedron::updateColors() +{ + for (unsigned int i = 0; i < m_vertices.getVertexCount(); ++i) + m_vertices[i].color = m_color; +} + +} // namespace cpp3ds diff --git a/src/cpp3ds/Graphics/RectangleShape.cpp b/src/cpp3ds/Graphics/RectangleShape.cpp index 85a4b3e..4798b53 100644 --- a/src/cpp3ds/Graphics/RectangleShape.cpp +++ b/src/cpp3ds/Graphics/RectangleShape.cpp @@ -61,15 +61,15 @@ unsigned int RectangleShape::getPointCount() const //////////////////////////////////////////////////////////// -Vector2f RectangleShape::getPoint(unsigned int index) const +Vector3f RectangleShape::getPoint(unsigned int index) const { switch (index) { default: - case 0: return Vector2f(0, 0); - case 1: return Vector2f(m_size.x, 0); - case 2: return Vector2f(m_size.x, m_size.y); - case 3: return Vector2f(0, m_size.y); + case 0: return Vector3f(0, 0); + case 1: return Vector3f(0, m_size.y); + case 2: return Vector3f(m_size.x, m_size.y); + case 3: return Vector3f(m_size.x, 0); } } diff --git a/src/cpp3ds/Graphics/RenderTarget.cpp b/src/cpp3ds/Graphics/RenderTarget.cpp index b551f9a..70317d8 100644 --- a/src/cpp3ds/Graphics/RenderTarget.cpp +++ b/src/cpp3ds/Graphics/RenderTarget.cpp @@ -30,8 +30,14 @@ #include #include #include +#include +#include #include +#include +#include #include +#include +#include namespace @@ -66,6 +72,17 @@ namespace case cpp3ds::BlendMode::Subtract: return GL_FUNC_SUBTRACT; } } + + // Thread-safe unique identifier generator, + // is used for id + cpp3ds::Uint64 getUniqueId() + { + static cpp3ds::Uint64 id = 1; + static cpp3ds::Mutex mutex; + + cpp3ds::Lock lock(mutex); + return id++; + } } @@ -73,18 +90,30 @@ namespace cpp3ds { //////////////////////////////////////////////////////////// RenderTarget::RenderTarget() : -m_defaultView(), -m_view (), -m_cache () +m_defaultView (), +m_view (NULL), +m_cache (), +m_depthTest (false), +m_clearDepth (false), +m_defaultShader (NULL), +m_currentNonLegacyShader(NULL), +m_lastNonLegacyShader (NULL), +m_id (getUniqueId()), +m_previousViewport (-1, -1, -1, -1), +m_previousClearColor (0, 0, 0, 0) { m_cache.vertexCache = new Vertex[StatesCache::VertexCacheSize]; m_cache.glStatesSet = false; + Light::increaseLightReferences(); } //////////////////////////////////////////////////////////// RenderTarget::~RenderTarget() { + Light::decreaseLightReferences(); + delete m_defaultShader; + delete m_view; delete[] m_cache.vertexCache; } @@ -97,29 +126,40 @@ void RenderTarget::clear(const Color& color) // Unbind texture to fix RenderTexture preventing clear applyTexture(NULL); + if (color != m_previousClearColor) + { #ifdef EMULATION glCheck(glClearColor(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f)); #else // Use glClearColorIiEXT to avoid unnecessary float conversion glCheck(glClearColorIiEXT(color.r, color.g, color.b, color.a)); #endif - glCheck(glClear(GL_COLOR_BUFFER_BIT)); + m_previousClearColor = color; + } + + glCheck(glClear(GL_COLOR_BUFFER_BIT | (m_clearDepth ? GL_DEPTH_BUFFER_BIT : 0))); } } //////////////////////////////////////////////////////////// -void RenderTarget::setView(const View& view) +void RenderTarget::enableDepthTest(bool enable) { - m_view = view; - m_cache.viewChanged = true; + m_depthTest = enable; + + if(enable) + { + glCheck(glEnable(GL_DEPTH_TEST)); + } + else + glCheck(glDisable(GL_DEPTH_TEST)); } //////////////////////////////////////////////////////////// const View& RenderTarget::getView() const { - return m_view; + return *m_view; } @@ -166,17 +206,17 @@ Vector2f RenderTarget::mapPixelToCoords(const Vector2i& point, const View& view) //////////////////////////////////////////////////////////// -Vector2i RenderTarget::mapCoordsToPixel(const Vector2f& point) const +Vector2i RenderTarget::mapCoordsToPixel(const Vector3f& point) const { return mapCoordsToPixel(point, getView()); } //////////////////////////////////////////////////////////// -Vector2i RenderTarget::mapCoordsToPixel(const Vector2f& point, const View& view) const +Vector2i RenderTarget::mapCoordsToPixel(const Vector3f& point, const View& view) const { - // First, transform the point by the view matrix - Vector2f normalized = view.getTransform().transformPoint(point); + // First, transform the point by the modelview and projection matrix + Vector3f normalized = (view.getTransform() * view.getViewTransform()).transformPoint(point); // Then convert to viewport coordinates Vector2i pixel; @@ -195,6 +235,273 @@ void RenderTarget::draw(const Drawable& drawable, const RenderStates& states) } +//////////////////////////////////////////////////////////// +void RenderTarget::draw(const VertexBuffer& buffer, const RenderStates& states) +{ + // Nothing to draw? + if (!buffer.getVertexCount()) + return; + + draw(&buffer.m_vertices[0], buffer.getVertexCount(), buffer.m_primitiveType, states); + return; + + if (activate(true)) + { + // First set the persistent OpenGL states if it's the very first call + if (!m_cache.glStatesSet) + resetGLStates(); + + // Track if we need to set uniforms again for current shader + bool shaderChanged = false; + + bool previousShaderWarnSetting = true; + +// if (m_defaultShader) +// { +// // Non-legacy rendering, need to set uniforms +// if (states.shader) +// { +// m_currentNonLegacyShader = states.shader; +// previousShaderWarnSetting = states.shader->warnMissing(false); +// } +// else +// m_currentNonLegacyShader = m_defaultShader; +// +// shaderChanged = (m_currentNonLegacyShader != m_lastNonLegacyShader); +// +// m_currentNonLegacyShader->beginParameterBlock(); +// } + + applyTransform(states.transform); + + // Apply the view + if (shaderChanged || m_cache.viewChanged) + applyCurrentView(); + + // Apply the blend mode + if (states.blendMode != m_cache.lastBlendMode) + applyBlendMode(states.blendMode); + + // Apply the texture + Uint64 textureId = states.texture ? states.texture->m_cacheId : 0; + if (shaderChanged || (textureId != m_cache.lastTextureId)) + applyTexture(states.texture); + + // Apply the shader +// if (states.shader) +// applyShader(states.shader); +// else if (m_defaultShader) +// applyShader(m_defaultShader); + + // Find the OpenGL primitive type + static const GLenum modes[] = {GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN}; + GLenum mode = modes[buffer.getPrimitiveType()]; + + // Setup the pointers to the vertices' components + if (!m_defaultShader) + { +#ifdef EMULATION + // Apply the vertex buffer + Uint64 vertexBufferId = buffer.m_cacheId; + if (vertexBufferId != m_cache.lastVertexBufferId) + applyVertexBuffer(&buffer); + + glCheck(glVertexPointer(3, GL_FLOAT, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, position)))); + glCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, color)))); + glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, texCoords)))); + glCheck(glNormalPointer(GL_FLOAT, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, normal)))); +#else + // Temorary workaround until gl3ds can get VAO gl*Pointer functions working + u32 bufferOffsets[] = {0}; + u64 bufferPermutations[] = {0x3210}; + u8 bufferAttribCounts[] = {4}; + GPU_SetAttributeBuffers( + 4, // number of attributes + (u32 *) osConvertVirtToPhys((u32) &buffer.m_vertices[0]), + GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE) | + GPU_ATTRIBFMT(2, 2, GPU_FLOAT) | GPU_ATTRIBFMT(3, 3, GPU_FLOAT), + 0xFF8, //0b1100 + 0x3210, + 1, //number of buffers + bufferOffsets, + bufferPermutations, + bufferAttribCounts // number of attributes for each buffer + ); +#endif + // Draw the primitives + glCheck(glDrawArrays(mode, 0, buffer.getVertexCount())); + } + else + { + Light::addLightsToShader(*m_currentNonLegacyShader); + + unsigned int arrayObject = 0; + bool newArray = true; + bool needUpload = false; + + if (VertexBuffer::hasVertexArrayObjects()) + { + // Lookup the current context (id, shader id) in the VertexBuffer + std::pair contextIdentifier(m_id, m_currentNonLegacyShader->m_id); + VertexBuffer::ArrayObjects::iterator arrayObjectIter = buffer.m_arrayObjects.find(contextIdentifier); + + if (arrayObjectIter == buffer.m_arrayObjects.end()) + { + // VertexBuffer doesn't have a VAO in this context + + // Create a new VAO + glCheck(glGenVertexArrays(1, &arrayObject)); + + // Register the VAO with the VertexBuffer + buffer.m_arrayObjects[contextIdentifier] = arrayObject; + + // Mark the VAO age as 0 + m_arrayAgeCount[arrayObject] = 0; + } + else + { + // VertexBuffer has/had a VAO in this context + + // Grab the VAO identifier from the VertexBuffer + arrayObject = arrayObjectIter->second; + + // Still need to check if it still exists + ArrayAgeCount::iterator arrayAge = m_arrayAgeCount.find(arrayObject); + + if (arrayAge != m_arrayAgeCount.end()) + { + // VAO still exists in this context + newArray = false; + + // Check if the VertexBuffer data needs to be re-uploaded + needUpload = buffer.m_needUpload; + + // Mark the VAO age as 0 + arrayAge->second = 0; + } + else + { + // VAO needs to be recreated in this context + + // Create a new VAO + glCheck(glGenVertexArrays(1, &arrayObject)); + + // Register the VAO with the VertexBuffer + arrayObjectIter->second = arrayObject; + + // Mark the VAO age as 0 + m_arrayAgeCount[arrayObject] = 0; + } + } + + glBindVertexArray(arrayObject); + + // Maximum array object age in draw calls before being purged + // If an array object was not used to draw this many + // calls, it will be considered expired and purged + // from the context owned by this RenderTarget + const static unsigned int maxArrayObjectAge = 10000; + + // Increment age counters and purge all expired VAOs + for (ArrayAgeCount::iterator arrayAge = m_arrayAgeCount.begin(); arrayAge != m_arrayAgeCount.end();) + { + arrayAge->second++; + + if (arrayAge->second > maxArrayObjectAge) + { + glCheck(glDeleteVertexArrays(1, &(arrayAge->first))); + m_arrayAgeCount.erase(arrayAge++); + continue; + } + + ++arrayAge; + } + } + + int vertexLocation = -1; + int colorLocation = -1; + int texCoordLocation = -1; + int normalLocation = -1; + + // If we are creating a new array object or buffer data + // needs to be re-uploaded, we need to rebind even if + // it is still currently bound + if (newArray || needUpload) + { + // Apply the vertex buffer + Uint64 vertexBufferId = buffer.m_cacheId; + applyVertexBuffer(&buffer); + } + + if (newArray) + { + vertexLocation = m_currentNonLegacyShader->getVertexAttributeLocation("sf_Vertex"); + colorLocation = m_currentNonLegacyShader->getVertexAttributeLocation("sf_Color"); + texCoordLocation = m_currentNonLegacyShader->getVertexAttributeLocation("sf_MultiTexCoord0"); + normalLocation = m_currentNonLegacyShader->getVertexAttributeLocation("sf_Normal"); + + if (vertexLocation >= 0) + { + glCheck(glEnableVertexAttribArray(vertexLocation)); + glCheck(glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, position)))); + } + + if (colorLocation >= 0) + { + glCheck(glEnableVertexAttribArray(colorLocation)); + glCheck(glVertexAttribPointer(colorLocation, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, color)))); + } + + if (texCoordLocation >= 0) + { + glCheck(glEnableVertexAttribArray(texCoordLocation)); + glCheck(glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, texCoords)))); + } + + if (normalLocation >= 0) + { + glCheck(glEnableVertexAttribArray(normalLocation)); + glCheck(glVertexAttribPointer(normalLocation, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, normal)))); + } + } + + // Draw the primitives + glCheck(glDrawArrays(mode, 0, buffer.getVertexCount())); + + if (arrayObject) + glBindVertexArray(0); + + if (vertexLocation >= 0) + glCheck(glDisableVertexAttribArray(vertexLocation)); + + if (colorLocation >= 0) + glCheck(glDisableVertexAttribArray(colorLocation)); + + if (texCoordLocation >= 0) + glCheck(glDisableVertexAttribArray(texCoordLocation)); + + if (normalLocation >= 0) + glCheck(glDisableVertexAttribArray(normalLocation)); + } + + // Unbind the shader, if any was bound in legacy mode +// if (states.shader && !m_defaultShader) +// applyShader(NULL); + +// if (m_defaultShader) +// { +// m_currentNonLegacyShader->endParameterBlock(); +// +// if (states.shader) +// states.shader->warnMissing(previousShaderWarnSetting); +// +// m_lastNonLegacyShader = m_currentNonLegacyShader; +// m_currentNonLegacyShader = NULL; +// } + } +} + + //////////////////////////////////////////////////////////// void RenderTarget::draw(const Vertex* vertices, unsigned int vertexCount, PrimitiveType type, const RenderStates& states) @@ -212,20 +519,33 @@ void RenderTarget::draw(const Vertex* vertices, unsigned int vertexCount, } #endif - // GL_QUADS is unavailable on OpenGL ES - if (type == Quads) - { - err() << "cpp3ds::Quads primitive type is not supported on OpenGL ES platforms, drawing skipped" << std::endl; - return; - } - #define GL_QUADS 0 - if (activate(true)) { // First set the persistent OpenGL states if it's the very first call if (!m_cache.glStatesSet) resetGLStates(); + // Track if we need to set uniforms again for current shader + bool shaderChanged = false; + + bool previousShaderWarnSetting = true; + +// if (m_defaultShader) +// { +// // Non-legacy rendering, need to set uniforms +// if (states.shader) +// { +// m_currentNonLegacyShader = states.shader; +// previousShaderWarnSetting = states.shader->warnMissing(false); +// } +// else +// m_currentNonLegacyShader = m_defaultShader; +// +// shaderChanged = (m_currentNonLegacyShader != m_lastNonLegacyShader); +// +// m_currentNonLegacyShader->beginParameterBlock(); +// } + // Check if the vertex count is low enough so that we can pre-transform them bool useVertexCache = (vertexCount <= StatesCache::VertexCacheSize); if (useVertexCache) @@ -249,7 +569,7 @@ void RenderTarget::draw(const Vertex* vertices, unsigned int vertexCount, } // Apply the view - if (m_cache.viewChanged) + if (shaderChanged || m_cache.viewChanged) applyCurrentView(); // Apply the blend mode @@ -258,12 +578,18 @@ void RenderTarget::draw(const Vertex* vertices, unsigned int vertexCount, // Apply the texture Uint64 textureId = states.texture ? states.texture->m_cacheId : 0; - if (textureId != m_cache.lastTextureId) + if (shaderChanged || (textureId != m_cache.lastTextureId)) applyTexture(states.texture); // Apply the shader // if (states.shader) // applyShader(states.shader); +// else if (m_defaultShader) +// applyShader(m_defaultShader); + + // Unbind any bound vertex buffer + if (m_cache.lastVertexBufferId) + applyVertexBuffer(NULL); // If we pre-transform the vertices, we must use our internal vertex cache if (useVertexCache) @@ -275,44 +601,103 @@ void RenderTarget::draw(const Vertex* vertices, unsigned int vertexCount, vertices = NULL; } - // Setup the pointers to the vertices' components - if (vertices) - { - #ifdef EMULATION + // Find the OpenGL primitive type + static const GLenum modes[] = {GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN}; + GLenum mode = modes[type]; + + // Setup the pointers to the vertices' components + if (!m_defaultShader) + { + if (vertices) { +#ifdef EMULATION const char* data = reinterpret_cast(vertices); - glCheck(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), data + 0)); - glCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), data + 8)); // 8 = sizeof(Vector2f) - glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), data + 12)); // 12 = 8 + sizeof(Color) - #else + glCheck(glVertexPointer(3, GL_FLOAT, sizeof(Vertex), data + offsetof(Vertex, position))); + glCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), data + offsetof(Vertex, color))); + glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), data + offsetof(Vertex, texCoords))); + glCheck(glNormalPointer(GL_FLOAT, sizeof(Vertex), data + offsetof(Vertex, normal))); +#else // Temorary workaround until gl3ds can get VAO gl*Pointer functions working u32 bufferOffsets[] = {0}; - u64 bufferPermutations[] = {0x210}; - u8 bufferAttribCounts[] = {3}; + u64 bufferPermutations[] = {0x3210}; + u8 bufferAttribCounts[] = {4}; GPU_SetAttributeBuffers( - 3, // number of attributes - (u32*)osConvertVirtToPhys((u32)vertices), - GPU_ATTRIBFMT(0, 2, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE) | GPU_ATTRIBFMT(2, 2, GPU_FLOAT), - 0xFF8, //0b1100 - 0x210, - 1, //number of buffers - bufferOffsets, - bufferPermutations, - bufferAttribCounts // number of attributes for each buffer + 4, // number of attributes + (u32 *) osConvertVirtToPhys((u32) vertices), + GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE) | + GPU_ATTRIBFMT(2, 2, GPU_FLOAT) | GPU_ATTRIBFMT(3, 3, GPU_FLOAT), + 0xFF8, //0b1100 + 0x3210, + 1, //number of buffers + bufferOffsets, + bufferPermutations, + bufferAttribCounts // number of attributes for each buffer ); - #endif - } +#endif + } + // Draw the primitives + glCheck(glDrawArrays(mode, 0, vertexCount)); - // Find the OpenGL primitive type - static const GLenum modes[] = {GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, - GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS}; - GLenum mode = modes[type]; + } else { + Light::addLightsToShader(*m_currentNonLegacyShader); - // Draw the primitives - glCheck(glDrawArrays(mode, 0, vertexCount)); + int vertexLocation = m_currentNonLegacyShader->getVertexAttributeLocation("sf_Vertex"); + int colorLocation = m_currentNonLegacyShader->getVertexAttributeLocation("sf_Color"); + int texCoordLocation = m_currentNonLegacyShader->getVertexAttributeLocation("sf_MultiTexCoord0"); + int normalLocation = m_currentNonLegacyShader->getVertexAttributeLocation("sf_Normal"); - // Unbind the shader, if any -// if (states.shader) -// applyShader(NULL); + const char* data = reinterpret_cast(vertices); + + if (vertexLocation >= 0) { + glCheck(glEnableVertexAttribArray(vertexLocation)); + glCheck(glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), data + offsetof(Vertex, position))); + } + + if (colorLocation >= 0) { + glCheck(glEnableVertexAttribArray(colorLocation)); + glCheck(glVertexAttribPointer(colorLocation, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), data + offsetof(Vertex, color))); + } + + if (texCoordLocation >= 0) { + glCheck(glEnableVertexAttribArray(texCoordLocation)); + glCheck(glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), data + offsetof(Vertex, texCoords))); + } + + if (normalLocation >= 0) { + glCheck(glEnableVertexAttribArray(normalLocation)); + glCheck(glVertexAttribPointer(normalLocation, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), data + offsetof(Vertex, normal))); + } + + // Draw the primitives + glCheck(glDrawArrays(mode, 0, vertexCount)); + + if (vertexLocation >= 0) + glCheck(glDisableVertexAttribArray(vertexLocation)); + + if (colorLocation >= 0) + glCheck(glDisableVertexAttribArray(colorLocation)); + + if (texCoordLocation >= 0) + glCheck(glDisableVertexAttribArray(texCoordLocation)); + + if (normalLocation >= 0) + glCheck(glDisableVertexAttribArray(normalLocation)); + } + + + // Unbind the shader, if any was bound in legacy mode +// if (states.shader && !m_defaultShader) +// applyShader(NULL); + +// if (m_defaultShader) +// { +// m_currentNonLegacyShader->endParameterBlock(); +// +// if (states.shader) +// states.shader->warnMissing(previousShaderWarnSetting); +// +// m_lastNonLegacyShader = m_currentNonLegacyShader; +// m_currentNonLegacyShader = NULL; +// } // Update the cache m_cache.useVertexCache = useVertexCache; @@ -325,27 +710,31 @@ void RenderTarget::pushGLStates() { if (activate(true)) { - #ifdef CPP3DS_DEBUG - // make sure that the user didn't leave an unchecked OpenGL error - GLenum error = glGetError(); - if (error != GL_NO_ERROR) - { - err() << "OpenGL error (" << error << ") detected in user code, " - << "you should check for errors with glGetError()" - << std::endl; - } - #endif +#ifdef CPP3DS_DEBUG + // make sure that the user didn't leave an unchecked OpenGL error + GLenum error = glGetError(); + if (error != GL_NO_ERROR) + { + err() << "OpenGL error (" << error << ") detected in user code, " + << "you should check for errors with glGetError()" + << std::endl; + } +#endif - glCheck(glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS)); - glCheck(glPushAttrib(GL_ALL_ATTRIB_BITS)); + glCheck(glPushAttrib(GL_ALL_ATTRIB_BITS)); - glCheck(glMatrixMode(GL_MODELVIEW)); - glCheck(glPushMatrix()); - glCheck(glMatrixMode(GL_PROJECTION)); - glCheck(glPushMatrix()); - glCheck(glMatrixMode(GL_TEXTURE)); - glCheck(glPushMatrix()); + if (!m_defaultShader) + { + glCheck(glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS)); + glCheck(glMatrixMode(GL_MODELVIEW)); + glCheck(glPushMatrix()); + glCheck(glMatrixMode(GL_PROJECTION)); + glCheck(glPushMatrix()); + glCheck(glMatrixMode(GL_TEXTURE)); + glCheck(glPushMatrix()); + } } + resetGLStates(); } @@ -355,15 +744,21 @@ void RenderTarget::popGLStates() { if (activate(true)) { - glCheck(glMatrixMode(GL_PROJECTION)); - glCheck(glPopMatrix()); - glCheck(glMatrixMode(GL_MODELVIEW)); - glCheck(glPopMatrix()); - glCheck(glMatrixMode(GL_TEXTURE)); - glCheck(glPopMatrix()); + if (m_defaultShader) + applyShader(NULL); - glCheck(glPopClientAttrib()); - glCheck(glPopAttrib()); + if (!m_defaultShader) + { + glCheck(glMatrixMode(GL_PROJECTION)); + glCheck(glPopMatrix()); + glCheck(glMatrixMode(GL_MODELVIEW)); + glCheck(glPopMatrix()); + glCheck(glMatrixMode(GL_TEXTURE)); + glCheck(glPopMatrix()); + glCheck(glPopClientAttrib()); + } + + glCheck(glPopAttrib()); } } @@ -384,31 +779,53 @@ void RenderTarget::resetGLStates() } // Define the default OpenGL states - glCheck(glDisable(GL_CULL_FACE)); - glCheck(glDisable(GL_DEPTH_TEST)); + glCheck(glDisable(GL_LIGHTING)); + if(!m_depthTest) + glCheck(glDisable(GL_DEPTH_TEST)); glCheck(glDisable(GL_ALPHA_TEST)); - glCheck(glEnable(GL_TEXTURE_2D)); - glCheck(glEnable(GL_BLEND)); - glCheck(glMatrixMode(GL_MODELVIEW)); - #ifdef EMULATION - glCheck(glDisable(GL_LIGHTING)); - #endif - glCheck(glEnableClientState(GL_VERTEX_ARRAY)); - glCheck(glEnableClientState(GL_COLOR_ARRAY)); - glCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + glCheck(glEnable(GL_CULL_FACE)); + glCheck(glEnable(GL_BLEND)); + glCheck(glMatrixMode(GL_MODELVIEW)); + + glCheck(glDepthFunc(GL_GEQUAL)); + glCheck(glClearDepth(0.f)); + glCheck(glDepthRangef(1.f, 0.f)); + + if (!m_defaultShader) + { + glCheck(glEnable(GL_TEXTURE_2D)); + glCheck(glEnable(GL_COLOR_MATERIAL)); + glCheck(glEnable(GL_NORMALIZE)); + glCheck(glMatrixMode(GL_MODELVIEW)); + glCheck(glEnableClientState(GL_VERTEX_ARRAY)); + glCheck(glEnableClientState(GL_COLOR_ARRAY)); + glCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + glCheck(glEnableClientState(GL_NORMAL_ARRAY)); + } + + glCheck(glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)); m_cache.glStatesSet = true; // Apply the default SFML states applyBlendMode(BlendAlpha); applyTransform(Transform::Identity); applyTexture(NULL); -// if (shaderAvailable) -// applyShader(NULL); + + if (shaderAvailable) + { + if (!m_defaultShader) + applyShader(NULL); + else + applyShader(m_defaultShader); + } + + if (VertexBuffer::isAvailable()) + applyVertexBuffer(NULL); m_cache.useVertexCache = false; // Set the default view - setView(getView()); + setView(m_defaultView); } } @@ -418,27 +835,53 @@ void RenderTarget::initialize() { // Setup the default and current views m_defaultView.reset(FloatRect(0, 0, static_cast(getSize().x), static_cast(getSize().y))); - m_view = m_defaultView; + + delete m_view; + m_view = new View(m_defaultView); // Set GL states only on first draw, so that we don't pollute user's states m_cache.glStatesSet = false; + + // Try to set up non-legacy pipeline if available + setupNonLegacyPipeline(); } //////////////////////////////////////////////////////////// void RenderTarget::applyCurrentView() { - // Set the viewport - IntRect viewport = getViewport(m_view); - int top = getSize().y - (viewport.top + viewport.height); - glCheck(glViewport(viewport.left, top, viewport.width, viewport.height)); + // Set the viewport + IntRect viewport = getViewport(*m_view); - // Set the projection matrix - glCheck(glMatrixMode(GL_PROJECTION)); - glCheck(glLoadMatrixf(m_view.getTransform().getMatrix())); + if (viewport != m_previousViewport) + { + int top = getSize().y - (viewport.top + viewport.height); + glCheck(glViewport(viewport.left, top, viewport.width, viewport.height)); + m_previousViewport = viewport; + } - // Go back to model-view mode - glCheck(glMatrixMode(GL_MODELVIEW)); + if (m_defaultShader) + { + const Shader* shader = NULL; + + if (m_currentNonLegacyShader) + shader = m_currentNonLegacyShader; + else + shader = m_defaultShader; + + shader->setParameter("sf_ProjectionMatrix", m_view->getTransform()); + shader->setParameter("sf_ViewMatrix", m_view->getViewTransform()); + shader->setParameter("sf_ViewerPosition", m_view->getPosition()); + } + else + { + // Set the projection matrix + glCheck(glMatrixMode(GL_PROJECTION)); + glCheck(glLoadMatrixf(m_view->getTransform().getMatrix())); + + // Go back to model-view mode + glCheck(glMatrixMode(GL_MODELVIEW)); + } m_cache.viewChanged = false; } @@ -463,16 +906,86 @@ void RenderTarget::applyBlendMode(const BlendMode& mode) //////////////////////////////////////////////////////////// void RenderTarget::applyTransform(const Transform& transform) { - // No need to call glMatrixMode(GL_MODELVIEW), it is always the - // current mode (for optimization purpose, since it's the most used) - glCheck(glLoadMatrixf(transform.getMatrix())); + if (m_defaultShader) + { + const Shader* shader = NULL; + + if (m_currentNonLegacyShader) + shader = m_currentNonLegacyShader; + else + shader = m_defaultShader; + + shader->setParameter("sf_ModelMatrix", transform); + + const float* modelMatrix = transform.getMatrix(); + Transform normalMatrix(modelMatrix[0], modelMatrix[4], modelMatrix[8], 0.f, + modelMatrix[1], modelMatrix[5], modelMatrix[9], 0.f, + modelMatrix[2], modelMatrix[6], modelMatrix[10], 0.f, + 0.f, 0.f, 0.f, 1.f); + + if (Light::isLightingEnabled()) + shader->setParameter("sf_NormalMatrix", normalMatrix.getInverse().getTranspose()); + } + else + // No need to call glMatrixMode(GL_MODELVIEW), it is always the + // current mode (for optimization purpose, since it's the most used) + glCheck(glLoadMatrixf((m_view->getViewTransform() * transform).getMatrix())); +} + + +//////////////////////////////////////////////////////////// +void RenderTarget::applyViewTransform() +{ + if (!m_defaultShader) + // No need to call glMatrixMode(GL_MODELVIEW), it is always the + // current mode (for optimization purpose, since it's the most used) + glCheck(glLoadMatrixf(m_view->getViewTransform().getMatrix())); } //////////////////////////////////////////////////////////// void RenderTarget::applyTexture(const Texture* texture) { - Texture::bind(texture, Texture::Pixels); + if (m_defaultShader) + { + const Shader* shader = NULL; + + if (m_currentNonLegacyShader) + shader = m_currentNonLegacyShader; + else + shader = m_defaultShader; + + float xScale = 1.f; + float yScale = 1.f; + float yFlip = 0.f; + + if (texture) + { + // Setup scale factors that convert the range [0 .. size] to [0 .. 1] + xScale = 1.f / texture->m_actualSize.x; + yScale = 1.f / texture->m_actualSize.y; + + // If pixels are flipped we must invert the Y axis + if (texture->m_pixelsFlipped) + { + yScale = -yScale; + yFlip = static_cast(texture->m_size.y) / texture->m_actualSize.y; + } + + Transform textureMatrix(xScale, 0.f, 0.f, 0.f, + 0.f, yScale, 0.f, yFlip, + 0.f, 0.f, 1.f, 0.f, + 0.f, 0.f, 0.f, 1.f); + + shader->setParameter("sf_TextureMatrix", textureMatrix); + shader->setParameter("sf_Texture0", *texture); + shader->setParameter("sf_TextureEnabled", 1); + } + else + shader->setParameter("sf_TextureEnabled", 0); + } + else + Texture::bind(texture, Texture::Pixels); m_cache.lastTextureId = texture ? texture->m_cacheId : 0; } @@ -484,6 +997,196 @@ void RenderTarget::applyShader(const Shader* shader) Shader::bind(shader); } + +//////////////////////////////////////////////////////////// +void RenderTarget::applyVertexBuffer(const VertexBuffer* buffer) +{ + VertexBuffer::bind(buffer); + + m_cache.lastVertexBufferId = buffer ? buffer->m_cacheId : 0; +} + + +//////////////////////////////////////////////////////////// +void RenderTarget::setupNonLegacyPipeline() +{ + // Setup the default shader if non-legacy rendering is supported + delete m_defaultShader; + m_defaultShader = NULL; + + // Check if our shader lighting implementation is supported + if (!Light::hasShaderLighting()) + return; + + double versionNumber = 0.0; + std::istringstream versionStringStream(Shader::getSupportedVersion()); + versionStringStream >> versionNumber; + +// Disable non-legacy pipeline if requested +#if defined(SFML_LEGACY_GL) +versionNumber = 0.0; +#endif + + // This will only succeed if the supported version is not GLSL ES + if (versionNumber > 1.29) + { + m_defaultShader = new Shader; + + std::stringstream vertexShaderSource; + vertexShaderSource << "#version 130\n" + "\n" + "// Uniforms\n" + "uniform mat4 sf_ModelMatrix;\n" + "uniform mat4 sf_ViewMatrix;\n" + "uniform mat4 sf_ProjectionMatrix;\n" + "uniform mat4 sf_TextureMatrix;\n" + "uniform int sf_TextureEnabled;\n" + "uniform int sf_LightingEnabled;\n" + "\n" + "// Vertex attributes\n" + "in vec3 sf_Vertex;\n" + "in vec4 sf_Color;\n" + "in vec2 sf_MultiTexCoord0;\n" + "in vec3 sf_Normal;\n" + "\n" + "// Vertex shader outputs\n" + "out vec4 sf_FrontColor;\n" + "out vec2 sf_TexCoord0;\n" + "out vec3 sf_FragWorldPosition;\n" + "out vec3 sf_FragNormal;\n" + "\n" + "void main()\n" + "{\n" + " // Vertex position\n" + " gl_Position = sf_ProjectionMatrix * sf_ViewMatrix * sf_ModelMatrix * vec4(sf_Vertex, 1.0);\n" + "\n" + " // Vertex color\n" + " sf_FrontColor = sf_Color;\n" + "\n" + " // Texture data\n" + " if (sf_TextureEnabled == 1)\n" + " sf_TexCoord0 = (sf_TextureMatrix * vec4(sf_MultiTexCoord0, 0.0, 1.0)).st;\n" + "\n" + " // Lighting data\n" + " if (sf_LightingEnabled > 0)\n" + " {\n" + " sf_FragNormal = sf_Normal;\n" + " sf_FragWorldPosition = vec3(sf_ModelMatrix * vec4(sf_Vertex, 1.0));\n" + " }\n" + "}\n"; + + std::stringstream fragmentShaderSource; + fragmentShaderSource << "#version 130\n"; + +// if (Shader::isUniformBufferAvailable()) +// fragmentShaderSource << "#extension GL_ARB_uniform_buffer_object : enable\n"; + + fragmentShaderSource << "\n" + "// Light structure\n" + "struct Light\n" + "{\n" + " vec4 ambientColor;\n" + " vec4 diffuseColor;\n" + " vec4 specularColor;\n" + " vec4 positionDirection;\n" + " vec4 attenuation;\n" + "};\n" + "\n" + "// Uniforms\n" + "uniform mat4 sf_ModelMatrix;\n" + "uniform mat4 sf_NormalMatrix;\n" + "uniform sampler2D sf_Texture0;\n" + "uniform int sf_TextureEnabled;\n" + "uniform int sf_LightCount;\n" + "uniform int sf_LightingEnabled;\n" + "uniform vec3 sf_ViewerPosition;\n" + "\n"; + +// if (Shader::isUniformBufferAvailable()) +// fragmentShaderSource << "layout (std140) uniform Lights\n" +// "{\n" +// " Light sf_Lights[" << Light::getMaximumLights() << "];\n" +// "};\n"; +// else + fragmentShaderSource << "uniform Light sf_Lights[" << Light::getMaximumLights() << "];\n"; + + fragmentShaderSource << "\n" + "// Fragment attributes\n" + "in vec4 sf_FrontColor;\n" + "in vec2 sf_TexCoord0;\n" + "in vec3 sf_FragWorldPosition;\n" + "in vec3 sf_FragNormal;\n" + "\n" + "// Fragment shader outputs\n" + "out vec4 sf_FragColor;\n" + "\n" + "vec4 computeLighting()\n" + "{\n" + " // Early return in case lighting disabled\n" + " if (sf_LightingEnabled == 0)\n" + " return vec4(1.0, 1.0, 1.0, 1.0);\n" + "\n" + " // TODO: Implement way to manipulate materials\n" + " const float materialShininess = 1.0;\n" + " const vec4 materialSpecularColor = vec4(0.0001, 0.0001, 0.0001, 1.0);\n" + "\n" + " vec3 fragmentNormal = normalize((sf_NormalMatrix * vec4(sf_FragNormal, 1.0)).xyz);\n" + " vec3 fragmentDistanceToViewer = normalize(sf_ViewerPosition - sf_FragWorldPosition);" + "\n" + " vec4 totalIntensity = vec4(0.0, 0.0, 0.0, 0.0);\n" + "\n" + " for (int index = 0; index < sf_LightCount; ++index)\n" + " {\n" + " vec3 rayDirection = normalize(sf_Lights[index].positionDirection.xyz);\n" + " float attenuationFactor = 1.0;" + "\n" + " if (sf_Lights[index].positionDirection.w > 0.0)\n" + " {\n" + " rayDirection = normalize(sf_FragWorldPosition - sf_Lights[index].positionDirection.xyz);\n" + " float rayLength = length(sf_Lights[index].positionDirection.xyz - sf_FragWorldPosition);" + " vec4 attenuationCoefficients = vec4(1.0, rayLength, rayLength * rayLength, 0.0);" + " attenuationFactor = dot(sf_Lights[index].attenuation, attenuationCoefficients);\n" + " }\n" + "\n" + " vec4 ambientIntensity = sf_Lights[index].ambientColor;\n" + "\n" + " float diffuseCoefficient = max(0.0, dot(fragmentNormal, -rayDirection));\n" + " vec4 diffuseIntensity = sf_Lights[index].diffuseColor * diffuseCoefficient;\n" + "\n" + " float specularCoefficient = 0.0;\n" + " if(diffuseCoefficient > 0.0)" + " specularCoefficient = pow(max(0.0, dot(fragmentDistanceToViewer, reflect(rayDirection, fragmentNormal))), materialShininess);" + " vec4 specularIntensity = specularCoefficient * materialSpecularColor * sf_Lights[index].specularColor;" + "\n" + " totalIntensity += ambientIntensity + (diffuseIntensity + specularIntensity) / attenuationFactor;\n" + " }\n" + "\n" + " return vec4(totalIntensity.rgb, 1.0);\n" + "}\n" + "\n" + "vec4 computeTexture()\n" + "{\n" + " if (sf_TextureEnabled == 0)\n" + " return vec4(1.0, 1.0, 1.0, 1.0);\n" + "\n" + " return texture2D(sf_Texture0, sf_TexCoord0);\n" + "}\n" + "\n" + "void main()\n" + "{\n" + " // Fragment color\n" + " sf_FragColor = sf_FrontColor * computeTexture() * computeLighting();\n" + "}\n"; + + if (!m_defaultShader->loadFromMemory(vertexShaderSource.str(), fragmentShaderSource.str())) + { + err() << "Compiling default shader failed. Falling back to legacy pipeline..." << std::endl; + delete m_defaultShader; + m_defaultShader = NULL; + } + } +} + } // namespace cpp3ds diff --git a/src/cpp3ds/Graphics/Shader.cpp b/src/cpp3ds/Graphics/Shader.cpp index 8f46a43..87cd907 100644 --- a/src/cpp3ds/Graphics/Shader.cpp +++ b/src/cpp3ds/Graphics/Shader.cpp @@ -28,15 +28,80 @@ //////////////////////////////////////////////////////////// #include #include +#include #include #include #include +#include +#include #include #include <3ds.h> #include #include +namespace +{ + // Thread-safe unique identifier generator, + // is used for id + cpp3ds::Uint64 getUniqueId() + { + static cpp3ds::Uint64 id = 1; // start at 1, zero is "no program" + static cpp3ds::Mutex mutex; + + cpp3ds::Lock lock(mutex); + return id++; + } + + // Retrieve the maximum number of texture units available + GLint getMaxTextureUnits() + { + GLint maxUnits; + glCheck(glGetIntegerv(GL_MAX_TEXTURE_COORDS_ARB, &maxUnits)); + return maxUnits; + } + + // Read the contents of a file into an array of char + bool getFileContents(const std::string& filename, std::vector& buffer) + { + std::ifstream file(filename.c_str(), std::ios_base::binary); + if (file) + { + file.seekg(0, std::ios_base::end); + std::streamsize size = file.tellg(); + if (size > 0) + { + file.seekg(0, std::ios_base::beg); + buffer.resize(static_cast(size)); + file.read(&buffer[0], size); + } + buffer.push_back('\0'); + return true; + } + else + { + return false; + } + } + + // Read the contents of a stream into an array of char + bool getStreamContents(cpp3ds::InputStream& stream, std::vector& buffer) + { + bool success = true; + cpp3ds::Int64 size = stream.getSize(); + if (size > 0) + { + buffer.resize(static_cast(size)); + stream.seek(0); + cpp3ds::Int64 read = stream.read(&buffer[0], size); + success = (read == size); + } + buffer.push_back('\0'); + return success; + } +} + + namespace cpp3ds { //////////////////////////////////////////////////////////// @@ -48,7 +113,13 @@ Shader::Shader() : m_shaderProgram (0), m_currentTexture(-1), m_textures (), -m_params () +m_params (), +m_attributes (), +m_blockBindings (), +m_warnMissing (true), +m_id (0), +m_parameterBlock(false), +m_blockProgram (0) { } @@ -56,6 +127,7 @@ m_params () //////////////////////////////////////////////////////////// Shader::~Shader() { + // Destroy effect program if (m_shaderProgram) glDeleteProgram(m_shaderProgram); } @@ -69,7 +141,7 @@ bool Shader::loadFromFile(const std::string& filename, Type type) //////////////////////////////////////////////////////////// -bool Shader::loadFromFile(const std::string& vertexShaderFilename, const std::string& fragmentShaderFilename) +bool Shader::loadFromFile(const std::string& vertexShaderFilename, const std::string& fragmentShaderFilename, const std::string& geometryShaderFilename) { return false; } @@ -83,9 +155,9 @@ bool Shader::loadFromMemory(const std::string& shader, Type type) //////////////////////////////////////////////////////////// -bool Shader::loadFromMemory(const std::string& vertexShader, const std::string& fragmentShader) +bool Shader::loadFromMemory(const std::string& vertexShader, const std::string& fragmentShader, const std::string& geometryShader) { - return false; + return false; } @@ -95,7 +167,7 @@ bool Shader::loadFromResource(const std::string& shader, Type type, bool compile if (compiled) { return loadBinary(priv::resources[shader].data, priv::resources[shader].size, type); } else { - return compile(shader.c_str(), nullptr); + return compile(shader.c_str(), nullptr, nullptr); } } @@ -115,7 +187,120 @@ bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& fragme //////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, float x) +bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& fragmentShaderStream, InputStream& geometryShaderStream) +{ + return false; +} + + +//////////////////////////////////////////////////////////// +void Shader::setParameter(const std::string& name, int x) const +{ + if (m_shaderProgram) + { + // Enable program + GLint program; + glCheck(glGetIntegerv(GL_CURRENT_PROGRAM, &program)); + glCheck(glUseProgram(m_shaderProgram)); + + // Get parameter location and assign it new values + GLint location = getParamLocation(name); + if (location != -1) + { + glCheck(glUniform1i(location, x)); + } + + // Disable program + glCheck(glUseProgram(program)); + } +} + + +//////////////////////////////////////////////////////////// +void Shader::setParameter(const std::string& name, int x, int y) const +{ + if (m_shaderProgram) + { + // Enable program + GLint program; + glCheck(glGetIntegerv(GL_CURRENT_PROGRAM, &program)); + glCheck(glUseProgram(m_shaderProgram)); + + // Get parameter location and assign it new values + GLint location = getParamLocation(name); + if (location != -1) + { + glCheck(glUniform2i(location, x, y)); + } + + // Disable program + glCheck(glUseProgram(program)); + } +} + + +//////////////////////////////////////////////////////////// +void Shader::setParameter(const std::string& name, int x, int y, int z) const +{ + if (m_shaderProgram) + { + // Enable program + GLint program; + glCheck(glGetIntegerv(GL_CURRENT_PROGRAM, &program)); + glCheck(glUseProgram(m_shaderProgram)); + + // Get parameter location and assign it new values + GLint location = getParamLocation(name); + if (location != -1) + { + glCheck(glUniform3i(location, x, y, z)); + } + + // Disable program + glCheck(glUseProgram(program)); + } +} + + +//////////////////////////////////////////////////////////// +void Shader::setParameter(const std::string& name, int x, int y, int z, int w) const +{ + if (m_shaderProgram) + { + // Enable program + GLint program; + glCheck(glGetIntegerv(GL_CURRENT_PROGRAM, &program)); + glCheck(glUseProgram(m_shaderProgram)); + + // Get parameter location and assign it new values + GLint location = getParamLocation(name); + if (location != -1) + { + glCheck(glUniform4i(location, x, y, z, w)); + } + + // Disable program + glCheck(glUseProgram(program)); + } +} + + +//////////////////////////////////////////////////////////// +void Shader::setParameter(const std::string& name, const Vector2i& v) const +{ + setParameter(name, v.x, v.y); +} + + +//////////////////////////////////////////////////////////// +void Shader::setParameter(const std::string& name, const Vector3i& v) const +{ + setParameter(name, v.x, v.y, v.z); +} + + +//////////////////////////////////////////////////////////// +void Shader::setParameter(const std::string& name, float x) const { if (m_shaderProgram) { @@ -138,7 +323,7 @@ void Shader::setParameter(const std::string& name, float x) //////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, float x, float y) +void Shader::setParameter(const std::string& name, float x, float y) const { if (m_shaderProgram) { @@ -161,7 +346,7 @@ void Shader::setParameter(const std::string& name, float x, float y) //////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, float x, float y, float z) +void Shader::setParameter(const std::string& name, float x, float y, float z) const { if (m_shaderProgram) { @@ -184,7 +369,7 @@ void Shader::setParameter(const std::string& name, float x, float y, float z) //////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, float x, float y, float z, float w) +void Shader::setParameter(const std::string& name, float x, float y, float z, float w) const { if (m_shaderProgram) { @@ -207,28 +392,28 @@ void Shader::setParameter(const std::string& name, float x, float y, float z, fl //////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, const Vector2f& v) +void Shader::setParameter(const std::string& name, const Vector2f& v) const { setParameter(name, v.x, v.y); } //////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, const Vector3f& v) +void Shader::setParameter(const std::string& name, const Vector3f& v) const { setParameter(name, v.x, v.y, v.z); } //////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, const Color& color) +void Shader::setParameter(const std::string& name, const Color& color) const { setParameter(name, color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f); } //////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, const cpp3ds::Transform& transform) +void Shader::setParameter(const std::string& name, const cpp3ds::Transform& transform) const { if (m_shaderProgram) { @@ -251,7 +436,7 @@ void Shader::setParameter(const std::string& name, const cpp3ds::Transform& tran //////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, const Texture& texture) +void Shader::setParameter(const std::string& name, const Texture& texture) const { if (m_shaderProgram) { @@ -286,7 +471,7 @@ void Shader::setParameter(const std::string& name, const Texture& texture) //////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, CurrentTextureType) +void Shader::setParameter(const std::string& name, CurrentTextureType) const { if (m_shaderProgram) { @@ -296,6 +481,65 @@ void Shader::setParameter(const std::string& name, CurrentTextureType) } +//////////////////////////////////////////////////////////// +int Shader::getVertexAttributeLocation(const std::string& name) const +{ + // Check the cache + LocationTable::const_iterator it = m_attributes.find(name); + if (it != m_attributes.end()) + { + // Already in cache, return it + return it->second; + } + else + { + // Not in cache, request the location from OpenGL + int location = -1; +// int location = glGetAttribLocationARB(m_shaderProgram, name.c_str()); + if (location == -1) + { + // Error: location not found + if (m_warnMissing) + err() << "Vertex attribute \"" << name << "\" not found in shader" << std::endl; + } + + m_attributes.insert(std::make_pair(name, location)); + + return location; + } +} + + +//////////////////////////////////////////////////////////// +bool Shader::warnMissing(bool warn) const +{ + bool previousWarn = m_warnMissing; + m_warnMissing = warn; + return previousWarn; +} + + +////////////////////////////////////////////////////////////// +//void Shader::beginParameterBlock() const +//{ +// m_parameterBlock = true; +// +// m_blockProgram = static_cast(glGetHandleARB(GL_PROGRAM_OBJECT_ARB)); +// +// if (m_blockProgram != m_shaderProgram) +// glCheck(glUseProgramObjectARB(m_shaderProgram)); +//} +// +// +////////////////////////////////////////////////////////////// +//void Shader::endParameterBlock() const +//{ +// m_parameterBlock = false; +// +// if (m_blockProgram != m_shaderProgram) +// glCheck(glUseProgramObjectARB(m_blockProgram)); +//} + //////////////////////////////////////////////////////////// unsigned int Shader::getNativeHandle() const @@ -325,8 +569,8 @@ void Shader::bind(const Shader* shader) shader->bindTextures(); // Bind the current texture -// if (shader->m_currentTexture != -1) -// glCheck(GLEXT_glUniform1i(shader->m_currentTexture, 0)); + if (shader->m_currentTexture != -1) + glCheck(glUniform1i(shader->m_currentTexture, 0)); } else { @@ -344,7 +588,7 @@ bool Shader::isAvailable() //////////////////////////////////////////////////////////// -bool Shader::compile(const char* vertexShaderCode, const char* fragmentShaderCode) +bool Shader::compile(const char* vertexShaderCode, const char* fragmentShaderCode, const char* geometryShaderCode) { return false; } @@ -370,16 +614,56 @@ bool Shader::loadBinary(const Uint8* data, const Uint32 size, Type type) } +//////////////////////////////////////////////////////////// +bool Shader::isGeometryShaderAvailable() +{ + return isAvailable(); +// return isAvailable() && GLEW_VERSION_3_2; +} + + +//////////////////////////////////////////////////////////// +std::string Shader::getSupportedVersion() +{ + return ""; +} + + +//////////////////////////////////////////////////////////// +unsigned int Shader::getMaximumUniformComponents() +{ + if (!isAvailable()) + return 0; + + GLint maxVertexUniformComponents = 10; +// GLint maxVertexUniformComponents = 0; +// glCheck(glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &maxVertexUniformComponents)); + return maxVertexUniformComponents; +} + + //////////////////////////////////////////////////////////// void Shader::bindTextures() const { + TextureTable::const_iterator it = m_textures.begin(); + for (std::size_t i = 0; i < m_textures.size(); ++i) + { + GLint index = static_cast(i + 1); + glCheck(glUniform1i(it->first, index)); + glCheck(GLEXT_glActiveTexture(GLEXT_GL_TEXTURE0 + index)); + Texture::bind(it->second); + ++it; + } + + // Make sure that the texture unit which is left active is the number 0 + glCheck(GLEXT_glActiveTexture(GLEXT_GL_TEXTURE0)); } //////////////////////////////////////////////////////////// -int Shader::getParamLocation(const std::string& name) +int Shader::getParamLocation(const std::string& name) const { // Check the cache - ParamTable::const_iterator it = m_params.find(name); + LocationTable::const_iterator it = m_params.find(name); if (it != m_params.end()) { // Already in cache, return it return it->second; diff --git a/src/cpp3ds/Graphics/Shape.cpp b/src/cpp3ds/Graphics/Shape.cpp index acae8ff..bb5d7a4 100644 --- a/src/cpp3ds/Graphics/Shape.cpp +++ b/src/cpp3ds/Graphics/Shape.cpp @@ -259,13 +259,13 @@ void Shape::updateOutline() unsigned int index = i + 1; // Get the two segments shared by the current point - Vector2f p0 = (i == 0) ? m_vertices[count].position : m_vertices[index - 1].position; - Vector2f p1 = m_vertices[index].position; - Vector2f p2 = m_vertices[index + 1].position; + Vector3f p0 = (i == 0) ? m_vertices[count].position : m_vertices[index - 1].position; + Vector3f p1 = m_vertices[index].position; + Vector3f p2 = m_vertices[index + 1].position; // Compute their normal - Vector2f n1 = computeNormal(p0, p1); - Vector2f n2 = computeNormal(p1, p2); + Vector3f n1 = computeNormal(p0, p1); + Vector3f n2 = computeNormal(p1, p2); // Make sure that the normals point towards the outside of the shape // (this depends on the order in which the points were defined) @@ -276,7 +276,7 @@ void Shape::updateOutline() // Combine them to get the extrusion direction float factor = 1.f + (n1.x * n2.x + n1.y * n2.y); - Vector2f normal = (n1 + n2) / factor; + Vector3f normal = (n1 + n2) / factor; // Update the outline points m_outlineVertices[i * 2 + 0].position = p1; diff --git a/src/cpp3ds/Graphics/SphericalPolyhedron.cpp b/src/cpp3ds/Graphics/SphericalPolyhedron.cpp new file mode 100644 index 0000000..5877bd7 --- /dev/null +++ b/src/cpp3ds/Graphics/SphericalPolyhedron.cpp @@ -0,0 +1,175 @@ +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace cpp3ds +{ +//////////////////////////////////////////////////////////// +SphericalPolyhedron::SphericalPolyhedron(float radius, unsigned int subdivisions) : +m_radius (radius), +m_subdivisions(subdivisions) +{ + update(); +} + + +//////////////////////////////////////////////////////////// +void SphericalPolyhedron::setRadius(float radius) +{ + m_radius = radius; + + std::vector().swap(m_geometry); + + update(); +} + + +//////////////////////////////////////////////////////////// +float SphericalPolyhedron::getRadius() const +{ + return m_radius; +} + + +//////////////////////////////////////////////////////////// +void SphericalPolyhedron::setSubdivisions(unsigned int subdivisions) +{ + m_subdivisions = subdivisions; + + std::vector().swap(m_geometry); + + update(); +} + + +//////////////////////////////////////////////////////////// +unsigned int SphericalPolyhedron::getSubdivisions() const +{ + return m_subdivisions; +} + +//////////////////////////////////////////////////////////// +unsigned int SphericalPolyhedron::getFaceCount() const +{ + if (m_geometry.empty()) + construct(); + + return static_cast(m_geometry.size()) / 3; +} + + +//////////////////////////////////////////////////////////// +Polyhedron::Face SphericalPolyhedron::getFace(unsigned int index) const +{ + if (m_geometry.empty()) + construct(); + + Face face = {m_geometry[index * 3 + 0], + m_geometry[index * 3 + 1], + m_geometry[index * 3 + 2]}; + + // This is probably the last time we will need the + // geometry for a while so clear it to save memory. + if (index == (m_geometry.size() / 3 - 1)) + std::vector().swap(m_geometry); + + return face; +} + + +//////////////////////////////////////////////////////////// +void SphericalPolyhedron::construct() const +{ + // Icosahedron radii + static const float a = 0.525731112119133606f; + static const float b = 0.850650808352039932f; + + static Vector3f vertices[] = + { + Vector3f( a, 0, -b), + Vector3f(-a, 0, -b), + Vector3f( a, 0, b), + Vector3f(-a, 0, b), + Vector3f( 0, -b, -a), + Vector3f( 0, -b, a), + Vector3f( 0, b, -a), + Vector3f( 0, b, a), + Vector3f(-b, -a, 0), + Vector3f( b, -a, 0), + Vector3f(-b, a, 0), + Vector3f( b, a, 0) + }; + + static unsigned int indices[] = + { + 0, 1, 6, + 0, 4, 1, + 0, 9, 4, + 2, 7, 3, + 4, 5, 8, + 4, 8, 1, + 5, 2, 3, + 5, 3, 8, + 6, 1, 10, + 7, 2, 11, + 7, 6, 10, + 7, 10, 3, + 7, 11, 6, + 8, 3, 10, + 8, 10, 1, + 9, 0, 11, + 9, 2, 5, + 9, 5, 4, + 9, 11, 2, + 11, 0, 6 + }; + + m_geometry.clear(); + m_geometry.reserve(60 * (1 << (m_subdivisions * 2))); + + for (int i = 0; i < 20; ++i) + subdivide(vertices[indices[i * 3 + 0]], vertices[indices[i * 3 + 1]], vertices[indices[i * 3 + 2]], m_subdivisions); +} + + +//////////////////////////////////////////////////////////// +void SphericalPolyhedron::subdivide(const Vector3f& a, const Vector3f& b, const Vector3f& c, unsigned int s) const +{ + if (!s) + { + Vertex va(a * m_radius); + va.color = getColor(); + va.normal = a; + Vertex vb(b * m_radius); + vb.color = getColor(); + vb.normal = b; + Vertex vc(c * m_radius); + vc.color = getColor(); + vc.normal = c; + + m_geometry.push_back(va); + m_geometry.push_back(vb); + m_geometry.push_back(vc); + } + else + { + Vector3f ab((a + b) / 2.f); + Vector3f bc((b + c) / 2.f); + Vector3f ca((c + a) / 2.f); + + // Normalize all positions + ab /= std::sqrt(ab.x * ab.x + ab.y * ab.y + ab.z * ab.z); + bc /= std::sqrt(bc.x * bc.x + bc.y * bc.y + bc.z * bc.z); + ca /= std::sqrt(ca.x * ca.x + ca.y * ca.y + ca.z * ca.z); + + subdivide(a, ab, ca, s - 1); + subdivide(b, bc, ab, s - 1); + subdivide(c, ca, bc, s - 1); + subdivide(ab, bc, ca, s - 1); + } +} + +} // namespace cpp3ds diff --git a/src/cpp3ds/Graphics/Sprite.cpp b/src/cpp3ds/Graphics/Sprite.cpp index 900d86b..3252b2d 100644 --- a/src/cpp3ds/Graphics/Sprite.cpp +++ b/src/cpp3ds/Graphics/Sprite.cpp @@ -38,41 +38,34 @@ namespace cpp3ds { //////////////////////////////////////////////////////////// Sprite::Sprite() : +m_vertices (TrianglesStrip, 4), m_texture (NULL), m_textureRect() { - m_vertices = new Vertex[4]; } //////////////////////////////////////////////////////////// Sprite::Sprite(const Texture& texture) : +m_vertices (TrianglesStrip, 4), m_texture (NULL), m_textureRect() { - m_vertices = new Vertex[4]; setTexture(texture); } //////////////////////////////////////////////////////////// Sprite::Sprite(const Texture& texture, const IntRect& rectangle) : +m_vertices (TrianglesStrip, 4), m_texture (NULL), m_textureRect() { - m_vertices = new Vertex[4]; setTexture(texture); setTextureRect(rectangle); } -//////////////////////////////////////////////////////////// -Sprite::~Sprite() -{ - delete[] m_vertices; -} - - //////////////////////////////////////////////////////////// void Sprite::setTexture(const Texture& texture, bool resetRect) { @@ -153,7 +146,7 @@ void Sprite::draw(RenderTarget& target, RenderStates states) const { states.transform *= getTransform(); states.texture = m_texture; - target.draw(m_vertices, 4, TrianglesStrip, states); + target.draw(m_vertices, states); } } diff --git a/src/cpp3ds/Graphics/Text.cpp b/src/cpp3ds/Graphics/Text.cpp index 5c78b0b..d51afe7 100644 --- a/src/cpp3ds/Graphics/Text.cpp +++ b/src/cpp3ds/Graphics/Text.cpp @@ -169,7 +169,7 @@ const Color& Text::getColor() const //////////////////////////////////////////////////////////// -Vector2f Text::findCharacterPos(std::size_t index) const +Vector3f Text::findCharacterPos(std::size_t index) const { // Make sure that we have a valid font if (!m_font) @@ -201,6 +201,7 @@ Vector2f Text::findCharacterPos(std::size_t index) const case ' ' : position.x += hspace; continue; case '\t' : position.x += hspace * 4; continue; case '\n' : position.y += vspace; position.x = 0; continue; + case '\v' : position.y += vspace * 4; continue; } // For regular characters, add the advance offset of the glyph @@ -304,16 +305,16 @@ void Text::ensureGeometryUpdate() const float top = y + underlineOffset; float bottom = top + underlineThickness; - m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1))); - m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1))); - m_vertices.append(Vertex(Vector2f(0, bottom), m_color, Vector2f(1, 1))); - m_vertices.append(Vertex(Vector2f(0, bottom), m_color, Vector2f(1, 1))); - m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1))); - m_vertices.append(Vertex(Vector2f(x, bottom), m_color, Vector2f(1, 1))); - } + m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1))); + m_vertices.append(Vertex(Vector2f(0, bottom), m_color, Vector2f(1, 1))); + m_vertices.append(Vertex(Vector2f(x, bottom), m_color, Vector2f(1, 1))); + m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1))); + m_vertices.append(Vertex(Vector2f(x, bottom), m_color, Vector2f(1, 1))); + m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1))); + } // Handle special characters - if ((curChar == ' ') || (curChar == '\t') || (curChar == '\n')) + if ((curChar == ' ') || (curChar == '\t') || (curChar == '\n') || (curChar == '\v')) { // Update the current bounds (min coordinates) minX = std::min(minX, x); @@ -324,6 +325,7 @@ void Text::ensureGeometryUpdate() const case ' ' : x += hspace; break; case '\t' : x += hspace * 4; break; case '\n' : y += vspace; x = 0; break; + case '\v' : y += vspace * 4; break; } // Update the current bounds (max coordinates) @@ -348,12 +350,12 @@ void Text::ensureGeometryUpdate() const float v2 = static_cast(glyph.textureRect.top + glyph.textureRect.height); // Add a quad for the current character - m_vertices.append(Vertex(Vector2f(x + left - italic * top, y + top), m_color, Vector2f(u1, v1))); - m_vertices.append(Vertex(Vector2f(x + right - italic * top, y + top), m_color, Vector2f(u2, v1))); - m_vertices.append(Vertex(Vector2f(x + left - italic * bottom, y + bottom), m_color, Vector2f(u1, v2))); - m_vertices.append(Vertex(Vector2f(x + left - italic * bottom, y + bottom), m_color, Vector2f(u1, v2))); - m_vertices.append(Vertex(Vector2f(x + right - italic * top, y + top), m_color, Vector2f(u2, v1))); - m_vertices.append(Vertex(Vector2f(x + right - italic * bottom, y + bottom), m_color, Vector2f(u2, v2))); + m_vertices.append(Vertex(Vector2f(x + left - italic * top, y + top), m_color, Vector2f(u1, v1))); + m_vertices.append(Vertex(Vector2f(x + left - italic * bottom, y + bottom), m_color, Vector2f(u1, v2))); + m_vertices.append(Vertex(Vector2f(x + right - italic * bottom, y + bottom), m_color, Vector2f(u2, v2))); + m_vertices.append(Vertex(Vector2f(x + left - italic * top, y + top), m_color, Vector2f(u1, v1))); + m_vertices.append(Vertex(Vector2f(x + right - italic * bottom, y + bottom), m_color, Vector2f(u2, v2))); + m_vertices.append(Vertex(Vector2f(x + right - italic * top, y + top), m_color, Vector2f(u2, v1))); // Update the current bounds minX = std::min(minX, x + left - italic * bottom); @@ -371,13 +373,13 @@ void Text::ensureGeometryUpdate() const float top = y + underlineOffset; float bottom = top + underlineThickness; - m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1))); - m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1))); - m_vertices.append(Vertex(Vector2f(0, bottom), m_color, Vector2f(1, 1))); - m_vertices.append(Vertex(Vector2f(0, bottom), m_color, Vector2f(1, 1))); - m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1))); - m_vertices.append(Vertex(Vector2f(x, bottom), m_color, Vector2f(1, 1))); - } + m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1))); + m_vertices.append(Vertex(Vector2f(0, bottom), m_color, Vector2f(1, 1))); + m_vertices.append(Vertex(Vector2f(x, bottom), m_color, Vector2f(1, 1))); + m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1))); + m_vertices.append(Vertex(Vector2f(x, bottom), m_color, Vector2f(1, 1))); + m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1))); + } // Update the bounding rectangle m_bounds.left = minX; diff --git a/src/cpp3ds/Graphics/Texture.cpp b/src/cpp3ds/Graphics/Texture.cpp index c621991..b9422df 100644 --- a/src/cpp3ds/Graphics/Texture.cpp +++ b/src/cpp3ds/Graphics/Texture.cpp @@ -63,8 +63,8 @@ namespace // initializing the shared context // cpp3ds::Context context; - GLint size; - glCheck(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size)); + GLint size = 1024; +// glCheck(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size)); return static_cast(size); } diff --git a/src/cpp3ds/Graphics/Transform.cpp b/src/cpp3ds/Graphics/Transform.cpp index 4ab215e..0b159a0 100644 --- a/src/cpp3ds/Graphics/Transform.cpp +++ b/src/cpp3ds/Graphics/Transform.cpp @@ -47,14 +47,15 @@ Transform::Transform() //////////////////////////////////////////////////////////// -Transform::Transform(float a00, float a01, float a02, - float a10, float a11, float a12, - float a20, float a21, float a22) +Transform::Transform(float a00, float a01, float a02, float a03, + float a10, float a11, float a12, float a13, + float a20, float a21, float a22, float a23, + float a30, float a31, float a32, float a33) { - m_matrix[0] = a00; m_matrix[4] = a01; m_matrix[8] = 0.f; m_matrix[12] = a02; - m_matrix[1] = a10; m_matrix[5] = a11; m_matrix[9] = 0.f; m_matrix[13] = a12; - m_matrix[2] = 0.f; m_matrix[6] = 0.f; m_matrix[10] = 1.f; m_matrix[14] = 0.f; - m_matrix[3] = a20; m_matrix[7] = a21; m_matrix[11] = 0.f; m_matrix[15] = a22; + m_matrix[0] = a00; m_matrix[4] = a01; m_matrix[8] = a02; m_matrix[12] = a03; + m_matrix[1] = a10; m_matrix[5] = a11; m_matrix[9] = a12; m_matrix[13] = a13; + m_matrix[2] = a20; m_matrix[6] = a21; m_matrix[10] = a22; m_matrix[14] = a23; + m_matrix[3] = a30; m_matrix[7] = a31; m_matrix[11] = a32; m_matrix[15] = a33; } @@ -68,24 +69,140 @@ const float* Transform::getMatrix() const //////////////////////////////////////////////////////////// Transform Transform::getInverse() const { + // Compute the inverse + float inverted_matrix[16]; + + inverted_matrix[0] = m_matrix[5] * m_matrix[10] * m_matrix[15] - + m_matrix[5] * m_matrix[11] * m_matrix[14] - + m_matrix[9] * m_matrix[6] * m_matrix[15] + + m_matrix[9] * m_matrix[7] * m_matrix[14] + + m_matrix[13] * m_matrix[6] * m_matrix[11] - + m_matrix[13] * m_matrix[7] * m_matrix[10]; + + inverted_matrix[1] = -m_matrix[1] * m_matrix[10] * m_matrix[15] + + m_matrix[1] * m_matrix[11] * m_matrix[14] + + m_matrix[9] * m_matrix[2] * m_matrix[15] - + m_matrix[9] * m_matrix[3] * m_matrix[14] - + m_matrix[13] * m_matrix[2] * m_matrix[11] + + m_matrix[13] * m_matrix[3] * m_matrix[10]; + + inverted_matrix[2] = m_matrix[1] * m_matrix[6] * m_matrix[15] - + m_matrix[1] * m_matrix[7] * m_matrix[14] - + m_matrix[5] * m_matrix[2] * m_matrix[15] + + m_matrix[5] * m_matrix[3] * m_matrix[14] + + m_matrix[13] * m_matrix[2] * m_matrix[7] - + m_matrix[13] * m_matrix[3] * m_matrix[6]; + + inverted_matrix[3] = -m_matrix[1] * m_matrix[6] * m_matrix[11] + + m_matrix[1] * m_matrix[7] * m_matrix[10] + + m_matrix[5] * m_matrix[2] * m_matrix[11] - + m_matrix[5] * m_matrix[3] * m_matrix[10] - + m_matrix[9] * m_matrix[2] * m_matrix[7] + + m_matrix[9] * m_matrix[3] * m_matrix[6]; + + inverted_matrix[4] = -m_matrix[4] * m_matrix[10] * m_matrix[15] + + m_matrix[4] * m_matrix[11] * m_matrix[14] + + m_matrix[8] * m_matrix[6] * m_matrix[15] - + m_matrix[8] * m_matrix[7] * m_matrix[14] - + m_matrix[12] * m_matrix[6] * m_matrix[11] + + m_matrix[12] * m_matrix[7] * m_matrix[10]; + + inverted_matrix[5] = m_matrix[0] * m_matrix[10] * m_matrix[15] - + m_matrix[0] * m_matrix[11] * m_matrix[14] - + m_matrix[8] * m_matrix[2] * m_matrix[15] + + m_matrix[8] * m_matrix[3] * m_matrix[14] + + m_matrix[12] * m_matrix[2] * m_matrix[11] - + m_matrix[12] * m_matrix[3] * m_matrix[10]; + + inverted_matrix[6] = -m_matrix[0] * m_matrix[6] * m_matrix[15] + + m_matrix[0] * m_matrix[7] * m_matrix[14] + + m_matrix[4] * m_matrix[2] * m_matrix[15] - + m_matrix[4] * m_matrix[3] * m_matrix[14] - + m_matrix[12] * m_matrix[2] * m_matrix[7] + + m_matrix[12] * m_matrix[3] * m_matrix[6]; + + inverted_matrix[7] = m_matrix[0] * m_matrix[6] * m_matrix[11] - + m_matrix[0] * m_matrix[7] * m_matrix[10] - + m_matrix[4] * m_matrix[2] * m_matrix[11] + + m_matrix[4] * m_matrix[3] * m_matrix[10] + + m_matrix[8] * m_matrix[2] * m_matrix[7] - + m_matrix[8] * m_matrix[3] * m_matrix[6]; + + inverted_matrix[8] = m_matrix[4] * m_matrix[9] * m_matrix[15] - + m_matrix[4] * m_matrix[11] * m_matrix[13] - + m_matrix[8] * m_matrix[5] * m_matrix[15] + + m_matrix[8] * m_matrix[7] * m_matrix[13] + + m_matrix[12] * m_matrix[5] * m_matrix[11] - + m_matrix[12] * m_matrix[7] * m_matrix[9]; + + inverted_matrix[9] = -m_matrix[0] * m_matrix[9] * m_matrix[15] + + m_matrix[0] * m_matrix[11] * m_matrix[13] + + m_matrix[8] * m_matrix[1] * m_matrix[15] - + m_matrix[8] * m_matrix[3] * m_matrix[13] - + m_matrix[12] * m_matrix[1] * m_matrix[11] + + m_matrix[12] * m_matrix[3] * m_matrix[9]; + + inverted_matrix[10] = m_matrix[0] * m_matrix[5] * m_matrix[15] - + m_matrix[0] * m_matrix[7] * m_matrix[13] - + m_matrix[4] * m_matrix[1] * m_matrix[15] + + m_matrix[4] * m_matrix[3] * m_matrix[13] + + m_matrix[12] * m_matrix[1] * m_matrix[7] - + m_matrix[12] * m_matrix[3] * m_matrix[5]; + + inverted_matrix[11] = -m_matrix[0] * m_matrix[5] * m_matrix[11] + + m_matrix[0] * m_matrix[7] * m_matrix[9] + + m_matrix[4] * m_matrix[1] * m_matrix[11] - + m_matrix[4] * m_matrix[3] * m_matrix[9] - + m_matrix[8] * m_matrix[1] * m_matrix[7] + + m_matrix[8] * m_matrix[3] * m_matrix[5]; + + inverted_matrix[12] = -m_matrix[4] * m_matrix[9] * m_matrix[14] + + m_matrix[4] * m_matrix[10] * m_matrix[13] + + m_matrix[8] * m_matrix[5] * m_matrix[14] - + m_matrix[8] * m_matrix[6] * m_matrix[13] - + m_matrix[12] * m_matrix[5] * m_matrix[10] + + m_matrix[12] * m_matrix[6] * m_matrix[9]; + + inverted_matrix[13] = m_matrix[0] * m_matrix[9] * m_matrix[14] - + m_matrix[0] * m_matrix[10] * m_matrix[13] - + m_matrix[8] * m_matrix[1] * m_matrix[14] + + m_matrix[8] * m_matrix[2] * m_matrix[13] + + m_matrix[12] * m_matrix[1] * m_matrix[10] - + m_matrix[12] * m_matrix[2] * m_matrix[9]; + + inverted_matrix[14] = -m_matrix[0] * m_matrix[5] * m_matrix[14] + + m_matrix[0] * m_matrix[6] * m_matrix[13] + + m_matrix[4] * m_matrix[1] * m_matrix[14] - + m_matrix[4] * m_matrix[2] * m_matrix[13] - + m_matrix[12] * m_matrix[1] * m_matrix[6] + + m_matrix[12] * m_matrix[2] * m_matrix[5]; + + inverted_matrix[15] = m_matrix[0] * m_matrix[5] * m_matrix[10] - + m_matrix[0] * m_matrix[6] * m_matrix[9] - + m_matrix[4] * m_matrix[1] * m_matrix[10] + + m_matrix[4] * m_matrix[2] * m_matrix[9] + + m_matrix[8] * m_matrix[1] * m_matrix[6] - + m_matrix[8] * m_matrix[2] * m_matrix[5]; + // Compute the determinant - float det = m_matrix[0] * (m_matrix[15] * m_matrix[5] - m_matrix[7] * m_matrix[13]) - - m_matrix[1] * (m_matrix[15] * m_matrix[4] - m_matrix[7] * m_matrix[12]) + - m_matrix[3] * (m_matrix[13] * m_matrix[4] - m_matrix[5] * m_matrix[12]); + float det = m_matrix[0] * inverted_matrix[0] + + m_matrix[1] * inverted_matrix[4] + + m_matrix[2] * inverted_matrix[8] + + m_matrix[3] * inverted_matrix[12]; // Compute the inverse if the determinant is not zero // (don't use an epsilon because the determinant may *really* be tiny) if (det != 0.f) { - return Transform( (m_matrix[15] * m_matrix[5] - m_matrix[7] * m_matrix[13]) / det, - -(m_matrix[15] * m_matrix[4] - m_matrix[7] * m_matrix[12]) / det, - (m_matrix[13] * m_matrix[4] - m_matrix[5] * m_matrix[12]) / det, - -(m_matrix[15] * m_matrix[1] - m_matrix[3] * m_matrix[13]) / det, - (m_matrix[15] * m_matrix[0] - m_matrix[3] * m_matrix[12]) / det, - -(m_matrix[13] * m_matrix[0] - m_matrix[1] * m_matrix[12]) / det, - (m_matrix[7] * m_matrix[1] - m_matrix[3] * m_matrix[5]) / det, - -(m_matrix[7] * m_matrix[0] - m_matrix[3] * m_matrix[4]) / det, - (m_matrix[5] * m_matrix[0] - m_matrix[1] * m_matrix[4]) / det); + det = 1.f / det; + + for (int i = 0; i < 16; ++i) + inverted_matrix[i] = inverted_matrix[i] * det; + + return Transform(inverted_matrix[0], inverted_matrix[4], inverted_matrix[8], inverted_matrix[12], + inverted_matrix[1], inverted_matrix[5], inverted_matrix[8], inverted_matrix[13], + inverted_matrix[2], inverted_matrix[6], inverted_matrix[10], inverted_matrix[14], + inverted_matrix[3], inverted_matrix[7], inverted_matrix[11], inverted_matrix[15]); } else { @@ -95,17 +212,28 @@ Transform Transform::getInverse() const //////////////////////////////////////////////////////////// -Vector2f Transform::transformPoint(float x, float y) const +Transform Transform::getTranspose() const { - return Vector2f(m_matrix[0] * x + m_matrix[4] * y + m_matrix[12], - m_matrix[1] * x + m_matrix[5] * y + m_matrix[13]); + return Transform(m_matrix[0], m_matrix[1], m_matrix[2], m_matrix[3], + m_matrix[4], m_matrix[5], m_matrix[6], m_matrix[7], + m_matrix[8], m_matrix[9], m_matrix[10], m_matrix[11], + m_matrix[12], m_matrix[13], m_matrix[14], m_matrix[15]); } //////////////////////////////////////////////////////////// -Vector2f Transform::transformPoint(const Vector2f& point) const +Vector3f Transform::transformPoint(float x, float y, float z) const { - return transformPoint(point.x, point.y); + return Vector3f(m_matrix[0] * x + m_matrix[4] * y + m_matrix[8] * z + m_matrix[12], + m_matrix[1] * x + m_matrix[5] * y + m_matrix[9] * z + m_matrix[13], + m_matrix[2] * x + m_matrix[6] * y + m_matrix[10] * z + m_matrix[14]); +} + + +//////////////////////////////////////////////////////////// +Vector3f Transform::transformPoint(const Vector3f& point) const +{ + return transformPoint(point.x, point.y, point.z); } @@ -113,7 +241,7 @@ Vector2f Transform::transformPoint(const Vector2f& point) const FloatRect Transform::transformRect(const FloatRect& rectangle) const { // Transform the 4 corners of the rectangle - const Vector2f points[] = + const Vector3f points[] = { transformPoint(rectangle.left, rectangle.top), transformPoint(rectangle.left, rectangle.top + rectangle.height), @@ -138,41 +266,86 @@ FloatRect Transform::transformRect(const FloatRect& rectangle) const } +//////////////////////////////////////////////////////////// +FloatBox Transform::transformBox(const FloatBox& box) const +{ + // Transform the 8 corners of the box + const Vector3f points[] = + { + transformPoint(box.left, box.top, box.front), + transformPoint(box.left, box.top + box.height, box.front), + transformPoint(box.left + box.width, box.top, box.front), + transformPoint(box.left + box.width, box.top + box.height, box.front), + transformPoint(box.left, box.top, box.front + box.depth), + transformPoint(box.left, box.top + box.height, box.front + box.depth), + transformPoint(box.left + box.width, box.top, box.front + box.depth), + transformPoint(box.left + box.width, box.top + box.height, box.front + box.depth) + }; + + // Compute the bounding box of the transformed points + float left = points[0].x; + float top = points[0].y; + float front = points[0].z; + float right = points[0].x; + float bottom = points[0].y; + float back = points[0].z; + for (int i = 1; i < 4; ++i) + { + if (points[i].x < left) left = points[i].x; + else if (points[i].x > right) right = points[i].x; + if (points[i].y < top) top = points[i].y; + else if (points[i].y > bottom) bottom = points[i].y; + if (points[i].z < front) front = points[i].z; + else if (points[i].z > back) back = points[i].z; + } + + return FloatBox(left, top, front, right - left, bottom - top, back - front); +} + + //////////////////////////////////////////////////////////// Transform& Transform::combine(const Transform& transform) { const float* a = m_matrix; const float* b = transform.m_matrix; - *this = Transform(a[0] * b[0] + a[4] * b[1] + a[12] * b[3], - a[0] * b[4] + a[4] * b[5] + a[12] * b[7], - a[0] * b[12] + a[4] * b[13] + a[12] * b[15], - a[1] * b[0] + a[5] * b[1] + a[13] * b[3], - a[1] * b[4] + a[5] * b[5] + a[13] * b[7], - a[1] * b[12] + a[5] * b[13] + a[13] * b[15], - a[3] * b[0] + a[7] * b[1] + a[15] * b[3], - a[3] * b[4] + a[7] * b[5] + a[15] * b[7], - a[3] * b[12] + a[7] * b[13] + a[15] * b[15]); + *this = Transform(a[0] * b[0] + a[4] * b[1] + a[8] * b[2] + a[12] * b[3], + a[0] * b[4] + a[4] * b[5] + a[8] * b[6] + a[12] * b[7], + a[0] * b[8] + a[4] * b[9] + a[8] * b[10] + a[12] * b[11], + a[0] * b[12] + a[4] * b[13] + a[8] * b[14] + a[12] * b[15], + a[1] * b[0] + a[5] * b[1] + a[9] * b[2] + a[13] * b[3], + a[1] * b[4] + a[5] * b[5] + a[9] * b[6] + a[13] * b[7], + a[1] * b[8] + a[5] * b[9] + a[9] * b[10] + a[13] * b[11], + a[1] * b[12] + a[5] * b[13] + a[9] * b[14] + a[13] * b[15], + a[2] * b[0] + a[6] * b[1] + a[10] * b[2] + a[14] * b[3], + a[2] * b[4] + a[6] * b[5] + a[10] * b[6] + a[14] * b[7], + a[2] * b[8] + a[6] * b[9] + a[10] * b[10] + a[14] * b[11], + a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14] * b[15], + a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + a[15] * b[3], + a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + a[15] * b[7], + a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + a[15] * b[11], + a[3] * b[12] + a[7] * b[13] + a[11] * b[14] + a[15] * b[15]); return *this; } //////////////////////////////////////////////////////////// -Transform& Transform::translate(float x, float y) +Transform& Transform::translate(float x, float y, float z) { - Transform translation(1, 0, x, - 0, 1, y, - 0, 0, 1); + Transform translation(1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1); return combine(translation); } //////////////////////////////////////////////////////////// -Transform& Transform::translate(const Vector2f& offset) +Transform& Transform::translate(const Vector3f& offset) { - return translate(offset.x, offset.y); + return translate(offset.x, offset.y, offset.z); } @@ -183,9 +356,10 @@ Transform& Transform::rotate(float angle) float cos = std::cos(rad); float sin = std::sin(rad); - Transform rotation(cos, -sin, 0, - sin, cos, 0, - 0, 0, 1); + Transform rotation(cos, -sin, 0, 0, + sin, cos, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); return combine(rotation); } @@ -198,9 +372,10 @@ Transform& Transform::rotate(float angle, float centerX, float centerY) float cos = std::cos(rad); float sin = std::sin(rad); - Transform rotation(cos, -sin, centerX * (1 - cos) + centerY * sin, - sin, cos, centerY * (1 - cos) - centerX * sin, - 0, 0, 1); + Transform rotation(cos, -sin, 0, centerX * (1 - cos) + centerY * sin, + sin, cos, 0, centerY * (1 - cos) - centerX * sin, + 0, 0, 1, 0, + 0, 0, 0, 1); return combine(rotation); } @@ -214,11 +389,38 @@ Transform& Transform::rotate(float angle, const Vector2f& center) //////////////////////////////////////////////////////////// -Transform& Transform::scale(float scaleX, float scaleY) +Transform& Transform::rotate(float angle, const Vector3f& axis) { - Transform scaling(scaleX, 0, 0, - 0, scaleY, 0, - 0, 0, 1); + // Normalize axis + float norm = std::sqrt(axis.x * axis.x + axis.y * axis.y + axis.z * axis.z); + + if(norm == 0.f) + return *this; + + float x = axis.x / norm; + float y = axis.y / norm; + float z = axis.z / norm; + + float rad = angle * 3.141592654f / 180.f; + float cos = std::cos(rad); + float sin = std::sin(rad); + + Transform rotation(x * x * (1 - cos) + cos, x * y * (1 - cos) - z * sin, x * z * (1 - cos) + y * sin, 0, + y * x * (1 - cos) + z * sin, y * y * (1 - cos) + cos, y * z * (1 - cos) - x * sin, 0, + x * z * (1 - cos) - y * sin, y * z * (1 - cos) + x * sin, z * z * (1 - cos) + cos, 0, + 0, 0, 0, 1); + + return combine(rotation); +} + + +//////////////////////////////////////////////////////////// +Transform& Transform::scale(float scaleX, float scaleY, float scaleZ) +{ + Transform scaling(scaleX, 0, 0, 0, + 0, scaleY, 0, 0, + 0, 0, scaleZ, 0, + 0, 0, 0, 1); return combine(scaling); } @@ -227,25 +429,38 @@ Transform& Transform::scale(float scaleX, float scaleY) //////////////////////////////////////////////////////////// Transform& Transform::scale(float scaleX, float scaleY, float centerX, float centerY) { - Transform scaling(scaleX, 0, centerX * (1 - scaleX), - 0, scaleY, centerY * (1 - scaleY), - 0, 0, 1); + Transform scaling(scaleX, 0, 0, centerX * (1 - scaleX), + 0, scaleY, 0, centerY * (1 - scaleY), + 0, 0, 1, 0, + 0, 0, 0, 1); return combine(scaling); } //////////////////////////////////////////////////////////// -Transform& Transform::scale(const Vector2f& factors) +Transform& Transform::scale(float scaleX, float scaleY, float scaleZ, float centerX, float centerY, float centerZ) { - return scale(factors.x, factors.y); + Transform scaling(scaleX, 0, 0, centerX * (1 - scaleX), + 0, scaleY, 0, centerY * (1 - scaleY), + 0, 0, scaleZ, centerZ * (1 - scaleZ), + 0, 0, 0, 1); + + return combine(scaling); } //////////////////////////////////////////////////////////// -Transform& Transform::scale(const Vector2f& factors, const Vector2f& center) +Transform& Transform::scale(const Vector3f& factors) { - return scale(factors.x, factors.y, center.x, center.y); + return scale(factors.x, factors.y, factors.z); +} + + +//////////////////////////////////////////////////////////// +Transform& Transform::scale(const Vector3f& factors, const Vector3f& center) +{ + return scale(factors.x, factors.y, factors.z, center.x, center.y, center.z); } @@ -264,9 +479,9 @@ Transform& operator *=(Transform& left, const Transform& right) //////////////////////////////////////////////////////////// -Vector2f operator *(const Transform& left, const Vector2f& right) +Vector3f operator *(const Transform& left, const Vector3f& right) { return left.transformPoint(right); } -} +} // namespace cpp3ds diff --git a/src/cpp3ds/Graphics/Transformable.cpp b/src/cpp3ds/Graphics/Transformable.cpp index 04e4b7a..23c21ad 100644 --- a/src/cpp3ds/Graphics/Transformable.cpp +++ b/src/cpp3ds/Graphics/Transformable.cpp @@ -33,10 +33,11 @@ namespace cpp3ds { //////////////////////////////////////////////////////////// Transformable::Transformable() : -m_origin (0, 0), -m_position (0, 0), +m_origin (0, 0, 0), +m_position (0, 0, 0), m_rotation (0), -m_scale (1, 1), +m_scale (1, 1, 1), +m_rotation_transform (), m_transform (), m_transformNeedUpdate (true), m_inverseTransform (), @@ -52,19 +53,20 @@ Transformable::~Transformable() //////////////////////////////////////////////////////////// -void Transformable::setPosition(float x, float y) +void Transformable::setPosition(float x, float y, float z) { m_position.x = x; m_position.y = y; + m_position.z = z; m_transformNeedUpdate = true; m_inverseTransformNeedUpdate = true; } //////////////////////////////////////////////////////////// -void Transformable::setPosition(const Vector2f& position) +void Transformable::setPosition(const Vector3f& position) { - setPosition(position.x, position.y); + setPosition(position.x, position.y, position.z); } @@ -75,47 +77,67 @@ void Transformable::setRotation(float angle) if (m_rotation < 0) m_rotation += 360.f; + m_rotation_transform = Transform::Identity; + m_rotation_transform.rotate(m_rotation, Vector3f(0.f, 0.f, 1.f)); + m_transformNeedUpdate = true; m_inverseTransformNeedUpdate = true; } //////////////////////////////////////////////////////////// -void Transformable::setScale(float factorX, float factorY) +void Transformable::setRotation(float angle, const Vector3f& axis) +{ + m_rotation = static_cast(fmod(angle, 360)); + if (m_rotation < 0) + m_rotation += 360.f; + + m_rotation_transform = Transform::Identity; + m_rotation_transform.rotate(m_rotation, axis); + + m_transformNeedUpdate = true; + m_inverseTransformNeedUpdate = true; +} + + +//////////////////////////////////////////////////////////// +void Transformable::setScale(float factorX, float factorY, float factorZ) { m_scale.x = factorX; m_scale.y = factorY; + m_scale.z = factorZ; m_transformNeedUpdate = true; m_inverseTransformNeedUpdate = true; } //////////////////////////////////////////////////////////// -void Transformable::setScale(const Vector2f& factors) +void Transformable::setScale(const Vector3f& factors) { - setScale(factors.x, factors.y); + setScale(factors.x, factors.y, factors.z); } //////////////////////////////////////////////////////////// -void Transformable::setOrigin(float x, float y) +void Transformable::setOrigin(float x, float y, float z) { m_origin.x = x; m_origin.y = y; + m_origin.z = z; m_transformNeedUpdate = true; m_inverseTransformNeedUpdate = true; } //////////////////////////////////////////////////////////// -void Transformable::setOrigin(const Vector2f& origin) +void Transformable::setOrigin(const Vector3f& origin) { - setOrigin(origin.x, origin.y); + setOrigin(origin.x, origin.y, origin.z); } //////////////////////////////////////////////////////////// -const Vector2f& Transformable::getPosition() const +const Vector3f& Transformable::getPosition() const { return m_position; } @@ -129,30 +151,30 @@ float Transformable::getRotation() const //////////////////////////////////////////////////////////// -const Vector2f& Transformable::getScale() const +const Vector3f& Transformable::getScale() const { return m_scale; } //////////////////////////////////////////////////////////// -const Vector2f& Transformable::getOrigin() const +const Vector3f& Transformable::getOrigin() const { return m_origin; } //////////////////////////////////////////////////////////// -void Transformable::move(float offsetX, float offsetY) +void Transformable::move(float offsetX, float offsetY, float offsetZ) { - setPosition(m_position.x + offsetX, m_position.y + offsetY); + setPosition(m_position.x + offsetX, m_position.y + offsetY, m_position.z + offsetZ); } //////////////////////////////////////////////////////////// -void Transformable::move(const Vector2f& offset) +void Transformable::move(const Vector3f& offset) { - setPosition(m_position.x + offset.x, m_position.y + offset.y); + setPosition(m_position.x + offset.x, m_position.y + offset.y, m_position.z + offset.z); } @@ -164,16 +186,27 @@ void Transformable::rotate(float angle) //////////////////////////////////////////////////////////// -void Transformable::scale(float factorX, float factorY) +void Transformable::rotate(float angle, const Vector3f& axis) { - setScale(m_scale.x * factorX, m_scale.y * factorY); + Transform new_rotation = Transform::Identity; + new_rotation.rotate(angle, axis); + m_rotation_transform = new_rotation * m_rotation_transform; + + m_transformNeedUpdate = true; } //////////////////////////////////////////////////////////// -void Transformable::scale(const Vector2f& factor) +void Transformable::scale(float factorX, float factorY, float factorZ) { - setScale(m_scale.x * factor.x, m_scale.y * factor.y); + setScale(m_scale.x * factorX, m_scale.y * factorY, m_scale.z * factorZ); +} + + +//////////////////////////////////////////////////////////// +void Transformable::scale(const Vector3f& factor) +{ + setScale(m_scale.x * factor.x, m_scale.y * factor.y, m_scale.z * factor.z); } @@ -190,12 +223,24 @@ const Transform& Transformable::getTransform() const float syc = m_scale.y * cosine; float sxs = m_scale.x * sine; float sys = m_scale.y * sine; + float sz = m_scale.z; float tx = -m_origin.x * sxc - m_origin.y * sys + m_position.x; float ty = m_origin.x * sxs - m_origin.y * syc + m_position.y; + float tz = -m_origin.z + m_position.z; - m_transform = Transform( sxc, sys, tx, - -sxs, syc, ty, - 0.f, 0.f, 1.f); + // Translation into origin + Transform origin_transform = Transform::Identity; + origin_transform.translate(-m_origin); + + // Scale + Transform scale_transform = Transform::Identity; + scale_transform.scale(m_scale); + + // Translate to position + Transform position_transform = Transform::Identity; + position_transform.translate(m_position); + + m_transform = position_transform * scale_transform * m_rotation_transform * origin_transform; m_transformNeedUpdate = false; } @@ -216,4 +261,4 @@ const Transform& Transformable::getInverseTransform() const return m_inverseTransform; } -} +} // namespace cpp3ds diff --git a/src/cpp3ds/Graphics/Vertex.cpp b/src/cpp3ds/Graphics/Vertex.cpp index 5a13e52..95be666 100644 --- a/src/cpp3ds/Graphics/Vertex.cpp +++ b/src/cpp3ds/Graphics/Vertex.cpp @@ -38,46 +38,62 @@ namespace cpp3ds Vertex::Vertex() : position (0, 0), color (255, 255, 255), -texCoords(0, 0) +texCoords(0, 0), +normal (0, 0, 1) { } //////////////////////////////////////////////////////////// -Vertex::Vertex(const Vector2f& thePosition) : +Vertex::Vertex(const Vector3f& thePosition) : position (thePosition), color (255, 255, 255), -texCoords(0, 0) +texCoords(0, 0), +normal (0, 0, 1) { } //////////////////////////////////////////////////////////// -Vertex::Vertex(const Vector2f& thePosition, const Color& theColor) : +Vertex::Vertex(const Vector3f& thePosition, const Color& theColor) : position (thePosition), color (theColor), -texCoords(0, 0) +texCoords(0, 0), +normal (0, 0, 1) { } //////////////////////////////////////////////////////////// -Vertex::Vertex(const Vector2f& thePosition, const Vector2f& theTexCoords) : +Vertex::Vertex(const Vector3f& thePosition, const Vector2f& theTexCoords) : position (thePosition), color (255, 255, 255), -texCoords(theTexCoords) +texCoords(theTexCoords), +normal (0, 0, 1) { } //////////////////////////////////////////////////////////// -Vertex::Vertex(const Vector2f& thePosition, const Color& theColor, const Vector2f& theTexCoords) : +Vertex::Vertex(const Vector3f& thePosition, const Color& theColor, const Vector2f& theTexCoords) : position (thePosition), color (theColor), -texCoords(theTexCoords) +texCoords(theTexCoords), +normal (0, 0, 1) { } + +//////////////////////////////////////////////////////////// +Vertex::Vertex(const Vector3f& thePosition, const Color& theColor, const Vector2f& theTexCoords, const Vector3f& theNormal) : +position (thePosition), +color (theColor), +texCoords(theTexCoords), +normal (theNormal) +{ +} + + #ifndef EMULATION //////////////////////////////////////////////////////////// void* Vertex::operator new (std::size_t size) @@ -110,4 +126,4 @@ void Vertex::operator delete[] (void *p) } #endif -} +} // namespace cpp3ds diff --git a/src/cpp3ds/Graphics/VertexArray.cpp b/src/cpp3ds/Graphics/VertexArray.cpp index 1f93eba..2d2e7c2 100644 --- a/src/cpp3ds/Graphics/VertexArray.cpp +++ b/src/cpp3ds/Graphics/VertexArray.cpp @@ -33,14 +33,16 @@ namespace cpp3ds { //////////////////////////////////////////////////////////// VertexArray::VertexArray() : +VertexContainer(0), m_vertices (), -m_primitiveType(Points) +m_primitiveType(Triangles) { } //////////////////////////////////////////////////////////// VertexArray::VertexArray(PrimitiveType type, unsigned int vertexCount) : +VertexContainer(0), m_vertices (vertexCount), m_primitiveType(type) { @@ -104,18 +106,20 @@ PrimitiveType VertexArray::getPrimitiveType() const //////////////////////////////////////////////////////////// -FloatRect VertexArray::getBounds() const +FloatBox VertexArray::getBounds() const { if (!m_vertices.empty()) { float left = m_vertices[0].position.x; float top = m_vertices[0].position.y; + float front = m_vertices[0].position.z; float right = m_vertices[0].position.x; float bottom = m_vertices[0].position.y; + float back = m_vertices[0].position.z; for (std::size_t i = 1; i < m_vertices.size(); ++i) { - Vector2f position = m_vertices[i].position; + Vector3f position = m_vertices[i].position; // Update left and right if (position.x < left) @@ -128,14 +132,20 @@ FloatRect VertexArray::getBounds() const top = position.y; else if (position.y > bottom) bottom = position.y; + + // Update front and back + if (position.z < front) + front = position.z; + else if (position.z > back) + back = position.z; } - return FloatRect(left, top, right - left, bottom - top); + return FloatBox(left, top, front, right - left, bottom - top, back - front); } else { // Array is empty - return FloatRect(); + return FloatBox(); } } @@ -147,4 +157,4 @@ void VertexArray::draw(RenderTarget& target, RenderStates states) const target.draw(&m_vertices[0], static_cast(m_vertices.size()), m_primitiveType, states); } -} +} // namespace cpp3ds diff --git a/src/cpp3ds/Graphics/VertexBuffer.cpp b/src/cpp3ds/Graphics/VertexBuffer.cpp new file mode 100644 index 0000000..a7a4d57 --- /dev/null +++ b/src/cpp3ds/Graphics/VertexBuffer.cpp @@ -0,0 +1,337 @@ +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include + + +namespace +{ + // Thread-safe unique identifier generator, + // is used for states cache (see RenderTarget) + cpp3ds::Uint64 getUniqueId() + { + static cpp3ds::Uint64 id = 1; // start at 1, zero is "no buffer" + static cpp3ds::Mutex mutex; + + cpp3ds::Lock lock(mutex); + return id++; + } +} + + +namespace cpp3ds +{ +//////////////////////////////////////////////////////////// +VertexBuffer::VertexBuffer() : +VertexContainer(0), +m_vertices (), +m_primitiveType(Triangles), +m_bufferObject (0), +m_cacheId (getUniqueId()), +m_needUpload (true) +{ + create(); +} + + +//////////////////////////////////////////////////////////// +VertexBuffer::VertexBuffer(PrimitiveType type, unsigned int vertexCount) : +VertexContainer(0), +m_vertices (vertexCount), +m_primitiveType(type), +m_bufferObject (0), +m_cacheId (getUniqueId()), +m_needUpload (true) +{ + create(); +} + + +//////////////////////////////////////////////////////////// +VertexBuffer::VertexBuffer(const VertexBuffer& copy) : +VertexContainer(0), +m_vertices (copy.m_vertices), +m_primitiveType(copy.m_primitiveType), +m_bufferObject (0), +m_cacheId (getUniqueId()), +m_needUpload (true) +{ + create(); +} + + +//////////////////////////////////////////////////////////// +VertexBuffer::~VertexBuffer() +{ + // Destroy buffer object + if (m_bufferObject) + { + ensureGlContext(); + + GLuint bufferObject = static_cast(m_bufferObject); + glCheck(glDeleteBuffers(1, &bufferObject)); + } +} + + +//////////////////////////////////////////////////////////// +bool VertexBuffer::create() +{ + // First make sure that we can use vertex buffers + if (!isAvailable()) + { + err() << "Failed to create a vertex buffer: your system doesn't support vertex buffers " + << "(you should test VertexBuffer::isAvailable() before trying to create a VertexBuffer object)" << std::endl; + return false; + } + + // Create the OpenGL buffer object if it doesn't exist yet + if (!m_bufferObject) + { + GLuint bufferObject; + glCheck(glGenBuffers(1, &bufferObject)); + m_bufferObject = static_cast(bufferObject); + } + + m_needUpload = true; + + return true; +} + + +//////////////////////////////////////////////////////////// +unsigned int VertexBuffer::getVertexCount() const +{ + return static_cast(m_vertices.size()); +} + + +//////////////////////////////////////////////////////////// +Vertex& VertexBuffer::operator [](unsigned int index) +{ + m_needUpload = true; + + return m_vertices[index]; +} + + +//////////////////////////////////////////////////////////// +const Vertex& VertexBuffer::operator [](unsigned int index) const +{ + return m_vertices[index]; +} + + +//////////////////////////////////////////////////////////// +void VertexBuffer::clear() +{ + if (!m_vertices.empty()) + m_needUpload = true; + + m_vertices.clear(); +} + + +//////////////////////////////////////////////////////////// +void VertexBuffer::resize(unsigned int vertexCount) +{ + if (m_vertices.size() != vertexCount) + m_needUpload = true; + + m_vertices.resize(vertexCount); +} + + +//////////////////////////////////////////////////////////// +void VertexBuffer::append(const Vertex& vertex) +{ + m_needUpload = true; + + m_vertices.push_back(vertex); +} + + +//////////////////////////////////////////////////////////// +void VertexBuffer::setPrimitiveType(PrimitiveType type) +{ + m_primitiveType = type; +} + + +//////////////////////////////////////////////////////////// +PrimitiveType VertexBuffer::getPrimitiveType() const +{ + return m_primitiveType; +} + + +//////////////////////////////////////////////////////////// +FloatBox VertexBuffer::getBounds() const +{ + if (!m_vertices.empty()) + { + float left = m_vertices[0].position.x; + float top = m_vertices[0].position.y; + float front = m_vertices[0].position.z; + float right = m_vertices[0].position.x; + float bottom = m_vertices[0].position.y; + float back = m_vertices[0].position.z; + + for (std::size_t i = 1; i < m_vertices.size(); ++i) + { + Vector3f position = m_vertices[i].position; + + // Update left and right + if (position.x < left) + left = position.x; + else if (position.x > right) + right = position.x; + + // Update top and bottom + if (position.y < top) + top = position.y; + else if (position.y > bottom) + bottom = position.y; + + // Update front and back + if (position.z < front) + front = position.z; + else if (position.z > back) + back = position.z; + } + + return FloatBox(left, top, front, right - left, bottom - top, back - front); + } + else + { + // Buffer is empty + return FloatBox(); + } +} + + +//////////////////////////////////////////////////////////// +void* VertexBuffer::getPointer() +{ + m_needUpload = true; + + return &m_vertices[0]; +} + + +//////////////////////////////////////////////////////////// +const void* VertexBuffer::getPointer() const +{ + return &m_vertices[0]; +} + + +//////////////////////////////////////////////////////////// +unsigned int VertexBuffer::getBufferObjectName() const +{ + return m_bufferObject; +} + + +//////////////////////////////////////////////////////////// +VertexBuffer& VertexBuffer::operator =(const VertexBuffer& right) +{ + VertexBuffer temp(right); + + std::swap(m_vertices, temp.m_vertices); + std::swap(m_primitiveType, temp.m_primitiveType); + m_cacheId = getUniqueId(); + + m_needUpload = true; + + return *this; +} + + +//////////////////////////////////////////////////////////// +void VertexBuffer::draw(RenderTarget& target, RenderStates states) const +{ + target.draw(*this, states); +} + + +//////////////////////////////////////////////////////////// +void VertexBuffer::bind(const VertexBuffer* buffer) +{ + bind(buffer, GL_ARRAY_BUFFER); +} + + +//////////////////////////////////////////////////////////// +void VertexBuffer::bind(const VertexBuffer* buffer, unsigned int target) +{ + ensureGlContext(); + + if (buffer && buffer->m_bufferObject) + { + // Bind the buffer + glCheck(glBindBuffer(target, buffer->m_bufferObject)); + + if (buffer->m_needUpload) + { + // Orphan the buffer first to give the driver an opportunity to optimize streaming + glCheck(glBufferData(target, buffer->m_vertices.size() * sizeof(Vertex), NULL, GL_DYNAMIC_DRAW)); + glCheck(glBufferData(target, buffer->m_vertices.size() * sizeof(Vertex), &(buffer->m_vertices[0]), GL_DYNAMIC_DRAW)); + buffer->m_needUpload = false; + } + } + else + { + // Bind no buffer + glCheck(glBindBuffer(target, 0)); + } +} + + +//////////////////////////////////////////////////////////// +bool VertexBuffer::isAvailable() +{ + static bool checked = false; + static bool bufferObjectsSupported = false; + if (!checked) + { + checked = true; + + ensureGlContext(); + + // Make sure that GLEW is initialized +// priv::ensureGlewInit(); + + bufferObjectsSupported = true; //(GLEW_ARB_vertex_buffer_object != 0); + } + + return bufferObjectsSupported; +} + + +//////////////////////////////////////////////////////////// +bool VertexBuffer::hasVertexArrayObjects() +{ + static bool checked = false; + static bool vertexArrayObjectsSupported = false; + if (!checked) + { + checked = true; + + ensureGlContext(); + + // Make sure that GLEW is initialized +// priv::ensureGlewInit(); + + vertexArrayObjectsSupported = true; //(GLEW_ARB_vertex_array_object != 0); + } + + return vertexArrayObjectsSupported; +} + +} // namespace cpp3ds diff --git a/src/cpp3ds/Graphics/VertexContainer.cpp b/src/cpp3ds/Graphics/VertexContainer.cpp new file mode 100644 index 0000000..1f72341 --- /dev/null +++ b/src/cpp3ds/Graphics/VertexContainer.cpp @@ -0,0 +1,164 @@ +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include + + +namespace cpp3ds +{ +//////////////////////////////////////////////////////////// +VertexContainer::VertexContainer() : +m_impl(NULL) +{ + if (VertexBuffer::isAvailable()) + { + m_impl = new VertexBuffer; + } + else + { + m_impl = new VertexArray; + } +} + + +//////////////////////////////////////////////////////////// +VertexContainer::VertexContainer(PrimitiveType type, unsigned int vertexCount) : +m_impl(NULL) +{ + if (VertexBuffer::isAvailable()) + { + m_impl = new VertexBuffer(type, vertexCount); + } + else + { + m_impl = new VertexArray(type, vertexCount); + } +} + + +//////////////////////////////////////////////////////////// +VertexContainer::VertexContainer(int) : +m_impl(NULL) +{ +} + + +//////////////////////////////////////////////////////////// +VertexContainer::VertexContainer(const VertexContainer& copy) : +m_impl(NULL) +{ + if (copy.m_impl) + { + // copy is a VertexContainer + if (VertexBuffer::isAvailable()) + { + m_impl = new VertexBuffer(*static_cast(copy.m_impl)); + } + else + { + m_impl = new VertexArray(*static_cast(copy.m_impl)); + } + } + + // If copy is not a VertexContainer, derived classes will handle copying +} + + +//////////////////////////////////////////////////////////// +VertexContainer::~VertexContainer() +{ + delete m_impl; +} + + +//////////////////////////////////////////////////////////// +unsigned int VertexContainer::getVertexCount() const +{ + return m_impl->getVertexCount(); +} + + +//////////////////////////////////////////////////////////// +Vertex& VertexContainer::operator [](unsigned int index) +{ + return (*m_impl)[index]; +} + + +//////////////////////////////////////////////////////////// +const Vertex& VertexContainer::operator [](unsigned int index) const +{ + return (*m_impl)[index]; +} + + +//////////////////////////////////////////////////////////// +void VertexContainer::clear() +{ + m_impl->clear(); +} + + +//////////////////////////////////////////////////////////// +void VertexContainer::resize(unsigned int vertexCount) +{ + m_impl->resize(vertexCount); +} + + +//////////////////////////////////////////////////////////// +void VertexContainer::append(const Vertex& vertex) +{ + m_impl->append(vertex); +} + + +//////////////////////////////////////////////////////////// +void VertexContainer::setPrimitiveType(PrimitiveType type) +{ + m_impl->setPrimitiveType(type); +} + + +//////////////////////////////////////////////////////////// +PrimitiveType VertexContainer::getPrimitiveType() const +{ + return m_impl->getPrimitiveType(); +} + + +//////////////////////////////////////////////////////////// +FloatBox VertexContainer::getBounds() const +{ + return m_impl->getBounds(); +} + + +//////////////////////////////////////////////////////////// +VertexContainer& VertexContainer::operator =(const VertexContainer& right) +{ + VertexContainer temp(right); + + std::swap(m_impl, temp.m_impl); + + return *this; +} + + +//////////////////////////////////////////////////////////// +void VertexContainer::draw(RenderTarget& target, RenderStates states) const +{ + if (VertexBuffer::isAvailable()) + { + target.draw(*static_cast(m_impl), states); + } + else + { + m_impl->draw(target, states); + } +} + +} // namespace cpp3ds diff --git a/src/cpp3ds/Graphics/View.cpp b/src/cpp3ds/Graphics/View.cpp index 6e89ffd..ef1f56d 100644 --- a/src/cpp3ds/Graphics/View.cpp +++ b/src/cpp3ds/Graphics/View.cpp @@ -33,12 +33,14 @@ namespace cpp3ds { //////////////////////////////////////////////////////////// View::View() : -m_center (), -m_size (), -m_rotation (0), -m_viewport (0, 0, 1, 1), -m_transformUpdated (false), -m_invTransformUpdated(false) +m_transformUpdated (false), +m_invTransformUpdated (false), +m_viewTransformUpdated (false), +m_invViewTransformUpdated(false), +m_position (), +m_size (), +m_rotation (0), +m_viewport (0, 0, 1, 1) { reset(FloatRect(0, 0, 1000, 1000)); } @@ -46,44 +48,57 @@ m_invTransformUpdated(false) //////////////////////////////////////////////////////////// View::View(const FloatRect& rectangle) : -m_center (), -m_size (), -m_rotation (0), -m_viewport (0, 0, 1, 1), -m_transformUpdated (false), -m_invTransformUpdated(false) +m_transformUpdated (false), +m_invTransformUpdated (false), +m_viewTransformUpdated (false), +m_invViewTransformUpdated(false), +m_position (), +m_size (), +m_rotation (0), +m_viewport (0, 0, 1, 1) { reset(rectangle); } //////////////////////////////////////////////////////////// -View::View(const Vector2f& center, const Vector2f& size) : -m_center (center), -m_size (size), -m_rotation (0), -m_viewport (0, 0, 1, 1), -m_transformUpdated (false), -m_invTransformUpdated(false) +View::View(const Vector3f& center, const Vector2f& size) : +m_transformUpdated (false), +m_invTransformUpdated (false), +m_viewTransformUpdated (false), +m_invViewTransformUpdated(false), +m_position (center), +m_size (size), +m_rotation (0), +m_viewport (0, 0, 1, 1) { } -//////////////////////////////////////////////////////////// -void View::setCenter(float x, float y) -{ - m_center.x = x; - m_center.y = y; - m_transformUpdated = false; - m_invTransformUpdated = false; +//////////////////////////////////////////////////////////// +View::~View() +{ + } //////////////////////////////////////////////////////////// -void View::setCenter(const Vector2f& center) +void View::setCenter(float x, float y, float z) { - setCenter(center.x, center.y); + m_position.x = x; + m_position.y = y; + m_position.z = z; + + m_viewTransformUpdated = false; + m_invViewTransformUpdated = false; +} + + +//////////////////////////////////////////////////////////// +void View::setCenter(const Vector3f& center) +{ + setCenter(center.x, center.y, center.z); } @@ -93,8 +108,10 @@ void View::setSize(float width, float height) m_size.x = width; m_size.y = height; - m_transformUpdated = false; - m_invTransformUpdated = false; + m_transformUpdated = false; + m_invTransformUpdated = false; + m_viewTransformUpdated = false; + m_invViewTransformUpdated = false; } @@ -112,8 +129,8 @@ void View::setRotation(float angle) if (m_rotation < 0) m_rotation += 360.f; - m_transformUpdated = false; - m_invTransformUpdated = false; + m_viewTransformUpdated = false; + m_invViewTransformUpdated = false; } @@ -127,21 +144,23 @@ void View::setViewport(const FloatRect& viewport) //////////////////////////////////////////////////////////// void View::reset(const FloatRect& rectangle) { - m_center.x = rectangle.left + rectangle.width / 2.f; - m_center.y = rectangle.top + rectangle.height / 2.f; + m_position.x = rectangle.left + rectangle.width / 2.f; + m_position.y = rectangle.top + rectangle.height / 2.f; m_size.x = rectangle.width; m_size.y = rectangle.height; m_rotation = 0; - m_transformUpdated = false; - m_invTransformUpdated = false; + m_transformUpdated = false; + m_invTransformUpdated = false; + m_viewTransformUpdated = false; + m_invViewTransformUpdated = false; } //////////////////////////////////////////////////////////// -const Vector2f& View::getCenter() const +const Vector3f& View::getCenter() const { - return m_center; + return m_position; } @@ -167,16 +186,16 @@ const FloatRect& View::getViewport() const //////////////////////////////////////////////////////////// -void View::move(float offsetX, float offsetY) +void View::move(float offsetX, float offsetY, float offsetZ) { - setCenter(m_center.x + offsetX, m_center.y + offsetY); + setCenter(m_position.x + offsetX, m_position.y + offsetY, m_position.z + offsetZ); } //////////////////////////////////////////////////////////// -void View::move(const Vector2f& offset) +void View::move(const Vector3f& offset) { - setCenter(m_center + offset); + setCenter(m_position + offset); } @@ -197,30 +216,23 @@ void View::zoom(float factor) //////////////////////////////////////////////////////////// const Transform& View::getTransform() const { - // Recompute the matrix if needed - if (!m_transformUpdated) - { - // Rotation components - float angle = m_rotation * 3.141592654f / 180.f; - float cosine = static_cast(std::cos(angle)); - float sine = static_cast(std::sin(angle)); - float tx = -m_center.x * cosine - m_center.y * sine + m_center.x; - float ty = m_center.x * sine - m_center.y * cosine + m_center.y; + // Recompute the matrix if needed + if (!m_transformUpdated) + { + // Projection components + float a = 2.f / m_size.x; + float b = -2.f / m_size.y; - // Projection components - float a = 2.f / m_size.x; - float b = -2.f / m_size.y; - float c = -a * m_center.x; - float d = -b * m_center.y; + // Rebuild the projection matrix + m_transform = Transform(a, 0.f, 0.f, -1.f, + 0.f, b, 0.f, 1.f, + 0.f, 0.f, -2.f, -1.f, + 0.f, 0.f, 0.f, 1.f); - // Rebuild the projection matrix - m_transform = Transform( a * cosine, a * sine, a * tx + c, - -b * sine, b * cosine, b * ty + d, - 0.f, 0.f, 1.f); - m_transformUpdated = true; - } + m_transformUpdated = true; + } - return m_transform; + return m_transform; } @@ -237,4 +249,52 @@ const Transform& View::getInverseTransform() const return m_inverseTransform; } + +//////////////////////////////////////////////////////////// +const Transform& View::getViewTransform() const +{ + // Recompute the view matrix if needed + if (!m_viewTransformUpdated) + { + // Rotation components + float angle = m_rotation * 3.141592654f / 180.f; + float cosine = static_cast(std::cos(angle)); + float sine = static_cast(std::sin(angle)); + + // Translation components + float x = m_size.x / 2.f - m_position.x; + float y = m_size.y / 2.f - m_position.y; + + m_viewTransform = Transform( cosine, sine, 0.f, -x * cosine - y * sine, + -sine, cosine, 0.f, x * sine - y * cosine, + 0.f, 0.f, 1.f, 0.f, + 0.f, 0.f, 0.f, 1.f); + + m_viewTransformUpdated = true; + } + + return m_viewTransform; } + + +//////////////////////////////////////////////////////////// +const Transform& View::getInverseViewTransform() const +{ + // Recompute the matrix if needed + if (!m_invViewTransformUpdated) + { + m_inverseViewTransform = getViewTransform().getInverse(); + m_invViewTransformUpdated = true; + } + + return m_inverseViewTransform; +} + + +//////////////////////////////////////////////////////////// +const Vector3f& View::getPosition() const +{ + return getCenter(); +} + +} // namespace sf