diff --git a/compile b/compile index a85b723c7e..99e50524b3 100755 --- a/compile +++ b/compile @@ -1,9 +1,9 @@ #! /bin/sh # Wrapper for compilers which do not understand '-c -o'. -scriptversion=2012-10-14.11; # UTC +scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify @@ -17,7 +17,7 @@ scriptversion=2012-10-14.11; # UTC # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -255,7 +255,8 @@ EOF echo "compile $scriptversion" exit $? ;; - cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ + icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac @@ -339,9 +340,9 @@ exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff --git a/configure.ac b/configure.ac index 39e5a9b7bd..4f0d6ac96b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([darkradiant], [2.9.0]) +AC_INIT([darkradiant], [2.9.1]) AM_INIT_AUTOMAKE([subdir-objects]) AM_SILENT_RULES([yes]) @@ -392,6 +392,7 @@ fi if test "$reloc_build" != 'no' then CPPFLAGS="-DENABLE_RELOCATION $CPPFLAGS" + LDFLAGS="$LDFLAGS '-Wl,-rpath,\$\$ORIGIN/../lib/darkradiant'" fi AC_SUBST([CPPFLAGS]) diff --git a/debian/changelog b/debian/changelog index 2e5398ca62..d71937edb8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +darkradiant (2.9.1~focal1) focal; urgency=medium + + * 2.9.1 release for Focal. + * Includes the new GameConnection functionality for The Dark Mod, as well as + numerous bug fixes and improvements. + + -- Matthew Mott Tue, 24 Nov 2020 19:36:02 +0000 + darkradiant (2.7.0) bionic; urgency=medium * 2.7.0 release for Bionic. diff --git a/debian/control b/debian/control index 2e6338959a..12682eba15 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: darkradiant Section: editors Priority: extra Maintainer: Matthew Mott -Build-Depends: debhelper (>= 5), autotools-dev, libgtkmm-2.4-dev, libgtkglextmm-x11-1.2-dev, libxml2-dev, libglew-dev, libboost-regex-dev, libboost-filesystem-dev, libboost-serialization-dev, libboost-test-dev, libboost-python-dev, python-dev, libvorbis-dev, libopenal-dev, libalut-dev, libjpeg-dev, libftgl-dev, libwxbase3.0-dev, libwxgtk3.0-dev +Build-Depends: debhelper (>= 5), autotools-dev, libgtkmm-2.4-dev, libgtkglextmm-x11-1.2-dev, libxml2-dev, libglew-dev, libboost-regex-dev, libboost-filesystem-dev, libboost-serialization-dev, libboost-test-dev, libboost-python-dev, python-dev, libvorbis-dev, libopenal-dev, libalut-dev, libjpeg-dev, libftgl-dev, libwxbase3.0-dev, libwxgtk3.0-gtk3-dev Standards-Version: 3.9.1 Package: darkradiant diff --git a/include/irender.h b/include/irender.h index 6d24b77a5e..a08755963e 100644 --- a/include/irender.h +++ b/include/irender.h @@ -4,6 +4,7 @@ #include #include "math/Vector3.h" +#include "math/AABB.h" #include "ShaderLayer.h" #include @@ -231,7 +232,8 @@ typedef std::shared_ptr RendererLightPtr; /// Debug stream insertion for RendererLight inline std::ostream& operator<< (std::ostream& os, const RendererLight& l) { - return os << "RendererLight(origin=" << l.worldOrigin() << ")"; + return os << "RendererLight(origin=" << l.worldOrigin().pp() + << ", lightAABB=" << l.lightAABB() << ")"; } /** @@ -239,16 +241,7 @@ inline std::ostream& operator<< (std::ostream& os, const RendererLight& l) * Interface for an object which can test its intersection with a RendererLight. * * Objects which implement this interface define a intersectsLight() function - * which determines whether the given light intersects the object. They also - * provide methods to allow the renderer to provide the list of lights which - * will be illuminating the object, subsequent to the intersection test. - * - * \todo - * This interface seems to exist because of the design decision that lit objects - * should maintain a list of lights which illuminate them. This is a poor - * design because this should be the responsibility of the renderer. When the - * renderer is refactored to process the scene light-by-light this class will - * not be necessary. + * which determines whether the given light intersects the object. */ class LitObject { @@ -257,12 +250,6 @@ class LitObject /// Test if the given light intersects the LitObject virtual bool intersectsLight(const RendererLight& light) const = 0; - - /// Add a light to the set of lights which do intersect this object - virtual void insertLight(const RendererLight& light) {} - - /// Clear out all lights in the set of lights intersecting this object - virtual void clearLights() {} }; typedef std::shared_ptr LitObjectPtr; @@ -316,92 +303,6 @@ inline std::ostream& operator<< (std::ostream& s, const LightSources* ls) return s << "[no lightsources]"; } -/** - * \brief - * A list of lights which might (but don't necessarily) intersect a LitObject - * - * A LightList is responsible for calculating which lights intersect a - * particular object. Although there is nothing exposed in the interface, the - * LightList holds a reference to a single lit object, and it is the - * intersection with this object which is calculated. - * - * LightLists are constructed by the RenderSystem and returned to each - * LitObject as the result of calling RenderSystem::attachLitObject(). The - * LitObject is then responsible for storing the LightList and passing it (or a - * more optimised LightSources container based on additional tests) to the - * RenderableCollector at render time. - * - * \internal - * As of 2011-01-09/r6927 the calling sequence seems to be as follows: - * - * 1. Illuminated object (e.g. patch, brush) adds itself to the RenderSystem - * with attachLitObject() at construction. - * 2. attachLitObject() returns a reference to a (newly-created) LightList which - * manages the lights intersecting this lit object. The lit object stores this - * reference internally, while the LightList implementation also stores a - * reference to the LitObject. - * 3. When the lit object's renderSolid() method is invoked to set up a render, - * it invokes LightList::calculateIntersectingLights() on the stored LightList - * reference. - * 4. calculateIntersectingLights() first checks to see if the lights need - * updating, which is true if EITHER this LightList's setDirty() method OR the - * RenderSystem's lightChanged() has been called since the last calculation. If - * no update is needed, it returns. - * 5. If an update IS needed, the LightList iterates over all lights in the - * scene, and tests if each one intersects its associated lit object (which is - * the one that just invoked calculateIntersectingLights(), although nothing - * enforces this). This intersection test is performed by passing the light to - * the LitObject::intersectsLight() method. - * 6. For each light which passes the intersection test, the LightList both adds - * it to its internal list of "active" (i.e. intersecting) lights for its - * object, and passes it to the object's insertLight() method. Some object - * classes then use insertLight() to populate another internal LightList subject - * to additional (internal) intersection tests, but this is not required. - * 7. At this point, calculateIntersectingLights() has finished, and returns - * control to its calling renderSolid() method. - * 8. The renderSolid() method (or another method it calls) passes a LightList - * to the RenderableCollector with setLights(). The light list it passes may be - * the original list returned from attachLitObject(), or the additional internal - * list populated in step 6. - * 9. The RenderableCollector state machine stores the LightList as the - * "current" light list. - * 10. Any subsequent renderables submitted with - * RenderableCollector::addRenderable() are associated with the current - * LightList passed in the previous step, and passed to the current Shader. - * 11. The OpenGLShader accepts the renderable and LightList, and adds them to - * its internal OpenGLShaderPasses: once only if RENDER_BUMP is not active, not - * at all if RENDER_BUMP is active but the LightList is NULL, or once for each - * light in the LightList otherwise. - * 12. The OpenGLShaderPass now contains a list of TransformedRenderable - * structures, each associating a single renderable with a single light. - * Multiple TransformedRenderable will exist for the same renderable if there - * were multiple lights illuminating it. - */ -class LightList -: public LightSources -{ -public: - virtual ~LightList() {} - - /** - * \brief - * Trigger the LightList to recalculate which lights intersect its object - * - * For each light, this method will call the LitObject::intersectsLight() - * method to test whether the light intersects the contained lit object. If - * the light does intersect, it will be passed to the object's - * insertLight() method for internal storage (and possibly further - * intersection tests). The LightList will also store all the intersecting - * lights internally, and expose them through its own forEachLight() - * method, for objects which do not wish to perform additional tests but - * just return the LightList to the renderer directly. - */ - virtual void calculateIntersectingLights() const = 0; - - /// Set the dirty flag, informing the LightList that an update is required - virtual void setDirty() = 0; -}; - const int c_attr_TexCoord0 = 1; const int c_attr_Tangent = 3; const int c_attr_Binormal = 4; @@ -665,61 +566,6 @@ class RenderSystem /// Set the shader program to use. virtual void setShaderProgram(ShaderProgram prog) = 0; - /* LIGHT MANAGEMENT */ - - /** - * \brief - * Add a lit object to the renderer. - * - * The renderer will create and return a reference to a LightList associated - * with this particular LitObject. The lit object can use the public - * LightList interface to trigger a recalculation of light intersections, or - * to set the dirty flag indicating to the LightList that a recalculation is - * necessary. - * - * \internal - * When the LightList implementation performs the intersection calculation, - * it will use the LitObject's intersectsLight method to do so, and if the - * intersection is detected, the insertLight method will be invoked on the - * LitObject. This means that (1) the renderer stores a LightList for each - * object, (2) the object itself has a reference to the LightList owned by - * the renderer, and (3) the LitObject interface allows the object to - * maintain ANOTHER list of intersecting lights, added with insertLight(). - * This seems like a lot of indirection, but it might have something to do - * with allowing objects with multiple sub-components to submit only a - * subset of lights for each component. - * - * \param object - * The lit object to add. - * - * \return - * A reference to a LightList which manages the lights that intersect the - * submitted object. - */ - virtual LightList& attachLitObject(LitObject& object) = 0; - - virtual void detachLitObject(LitObject& cullable) = 0; - - virtual void litObjectChanged(LitObject& cullable) = 0; - - /** - * \brief - * Attach a light source to the renderer. - */ - virtual void attachLight(RendererLight& light) = 0; - - /** - * \brief - * Detach a light source from the renderer. - */ - virtual void detachLight(RendererLight& light) = 0; - - /** - * \brief - * Indicate that the scene lights have changed. - */ - virtual void lightChanged() = 0; - virtual void attachRenderable(const Renderable& renderable) = 0; virtual void detachRenderable(const Renderable& renderable) = 0; virtual void forEachRenderable(const RenderableCallback& callback) const = 0; diff --git a/include/irenderable.h b/include/irenderable.h index 68314591e4..032f9e06c1 100644 --- a/include/irenderable.h +++ b/include/irenderable.h @@ -32,35 +32,9 @@ class RenderableCollector public: virtual ~RenderableCollector() {} - /** - * Submit an OpenGLRenderable object for rendering using the given shader. - * - * \param shader - * The Shader object this Renderable will be attached to. - * - * \param renderable - * The renderable object to submit. - * - * \param world - * The local to world transform that should be applied to this object when - * it is rendered. - * - * \param lights - * Optional LightSources containing lights illuminating this Renderable. - * - * \param entity - * Optional IRenderEntity exposing parameters which affect the rendering of - * this Renderable. - */ - virtual void addRenderable(Shader& shader, - const OpenGLRenderable& renderable, - const Matrix4& world, - const LightSources* lights = nullptr, - const IRenderEntity* entity = nullptr) = 0; - /** * \brief - * Submit a renderable object to be illuminated by scene lights. + * Submit a renderable object * * This method allows renderable geometry to be submitted under the control * of a LitObject which will determine whether and how the renderable is @@ -68,22 +42,36 @@ class RenderableCollector * will be considered for lighting by the lights which are submitted to the * same RenderableCollector using addLight(). * - * Most of the parameters have identical meanings to those in - * addRenderable(). + * Objects may be submitted without a LitObject if they are not affected by + * scene lights. * - * \param litObject - * A LitObject determining lighting interactions for this renderable. This - * may or may not be the same actual object as the OpenGLRenderable, - * depending on how the object tree is set up. If a single LitObject - * contains multiple renderables, a separate call to this method must be - * made for each renderable (with the same litObject parameter). + * \param shader + * The Shader object this Renderable will be attached to. + * + * \param renderable + * The renderable object to submit. * + * \param localToWorld + * The local to world transform that should be applied to this object when + * it is rendered. + * + * \param entity + * Optional IRenderEntity exposing parameters which affect the rendering of + * this Renderable. + * + * \param litObject + * Optional LitObject determining lighting interactions for this + * renderable. This may or may not be the same actual object as the + * OpenGLRenderable, depending on how the object class hierarchy is set up. + * If a single LitObject contains multiple renderables, a separate call to + * this method must be made for each renderable (with the same litObject + * parameter). */ - virtual void addLitRenderable(Shader& shader, - OpenGLRenderable& renderable, - const Matrix4& localToWorld, - const LitObject& litObject, - const IRenderEntity* entity = nullptr) = 0; + virtual void addRenderable(Shader& shader, + const OpenGLRenderable& renderable, + const Matrix4& localToWorld, + const LitObject* litObject = nullptr, + const IRenderEntity* entity = nullptr) = 0; /** * \brief diff --git a/include/version.h b/include/version.h index 61a509d3b1..d043b080e6 100644 --- a/include/version.h +++ b/include/version.h @@ -9,11 +9,25 @@ #define RADIANT_BLANK " " #if defined(_M_X64) || defined(__amd64__) || defined(_WIN64) + +// 64 bit architecture names (according to platform convention) +#if defined(__linux__) + #define RADIANT_PLATFORM "amd64" +#else #define RADIANT_PLATFORM "x64" +#endif + +#else + +// 32 bit architecture names (according to platform convention) +#if defined(__linux) + #define RADIANT_PLATFORM "i386" #else #define RADIANT_PLATFORM "x86" #endif +#endif + #include inline std::string RADIANT_APPNAME_FULL() diff --git a/libs/entitylib.h b/libs/entitylib.h index b4066d2a51..cd93e6437d 100644 --- a/libs/entitylib.h +++ b/libs/entitylib.h @@ -15,52 +15,6 @@ #include #include -/* greebo: draws a pyramid defined by 5 vertices - * points[0] is the top of the pyramid - * points[1] to points[4] is the base rectangle - */ -inline void drawPyramid(const Vector3 points[5]) { - typedef unsigned int index_t; - index_t indices[16] = { - 0, 1, // top to first - 0, 2, // top to second - 0, 3, // top to third - 0, 4, // top to fourth - 1, 2, // first to second - 2, 3, // second to third - 3, 4, // third to second - 4, 1, // fourth to first - }; - glVertexPointer(3, GL_DOUBLE, 0, points); - glDrawElements(GL_LINES, sizeof(indices)/sizeof(index_t), GL_UNSIGNED_INT, indices); -} - -/* greebo: draws a frustum defined by 8 vertices - * points[0] to points[3] define the top area vertices (clockwise starting from the "upper right" corner) - * points[4] to points[7] define the base rectangle (clockwise starting from the "upper right" corner) - */ -inline void drawFrustum(const Vector3 points[8]) { - typedef unsigned int index_t; - index_t indices[24] = { - 0, 4, // top up right to bottom up right - 1, 5, // top down right to bottom down right - 2, 6, // top down left to bottom down left - 3, 7, // top up left to bottom up left - - 0, 1, // top up right to top down right - 1, 2, // top down right to top down left - 2, 3, // top down left to top up left - 3, 0, // top up left to top up right - - 4, 5, // bottom up right to bottom down right - 5, 6, // bottom down right to bottom down left - 6, 7, // bottom down left to bottom up left - 7, 4, // bottom up left to bottom up right - }; - glVertexPointer(3, GL_DOUBLE, 0, points); - glDrawElements(GL_LINES, sizeof(indices)/sizeof(index_t), GL_UNSIGNED_INT, indices); -} - inline void arrow_draw(const Vector3& origin, const Vector3& direction) { Vector3 up(0, 0, 1); diff --git a/libs/math/AABB.h b/libs/math/AABB.h index 4ec3fc22d0..eb989f133e 100644 --- a/libs/math/AABB.h +++ b/libs/math/AABB.h @@ -25,16 +25,13 @@ class AABB /// The symmetrical extents in 3 dimensions. Vector3 extents; - /** Construct an AABB with default origin and invalid extents. - */ + /// Construct an AABB with default origin and invalid extents. AABB() : origin(0, 0, 0), extents(-1,-1,-1) {} - /** Construct an AABB with the provided origin and extents - * vectors. - */ + /// Construct an AABB with the provided origin and extents vectors. AABB(const Vector3& origin_, const Vector3& extents_) : origin(origin_), extents(extents_) @@ -97,20 +94,10 @@ class AABB */ float getRadius() const; - /** Expand this AABB in-place to include the given point in - * world space. - * - * @param point - * Vector3 representing the point to include. - */ + /// Expand this AABB in-place to include the given point void includePoint(const Vector3& point); - /** Expand this AABB in-place to include the given AABB in - * world space. - * - * @param other - * The other AABB to include. - */ + /// Expand this AABB in-place to include the given AABB void includeAABB(const AABB& other); /** @@ -294,14 +281,11 @@ inline void AABB::getPlanes(Plane3 planes[6]) const planes[5] = Plane3(-g_vector3_axes[2], -(origin[2] - extents[2])); } -/** - * Stream insertion for AABB class. - */ +/// Stream insertion for AABB class. inline std::ostream& operator<< (std::ostream& os, const AABB& aabb) { - os << "AABB { origin=" << aabb.getOrigin() << ", extents=" << aabb.getExtents() << " }"; - - return os; + return os << "AABB(origin=" << aabb.getOrigin().pp() + << ", extents=" << aabb.getExtents().pp() << ")"; } class AABBExtendByPoint diff --git a/libs/math/Frustum.h b/libs/math/Frustum.h index 3ea9cba32d..46fe41bc1f 100644 --- a/libs/math/Frustum.h +++ b/libs/math/Frustum.h @@ -1,10 +1,13 @@ #pragma once +#include + /// \file /// \brief View-frustum data types and related operations. #include "math/Matrix4.h" #include "math/Segment.h" +#include "math/AABB.h" #include "VolumeIntersectionValue.h" @@ -14,66 +17,122 @@ class Plane3; /** * \brief * Object representing a frustum, defined by six planes. + * + * A frustum is used to represent both the view volume and the volume of a + * projected light. It typically takes the form of a truncated pyramid although + * it need not be symmetrical. + * + * The six planes defining the frustum are named according to their use with an + * OpenGL camera: "front" is the near clip plane (the smaller plane near the + * top of the pyramid) and "back" is the far clip plane (the larger plane at + * the bottom of the pyramid). In the case of a projected light the "front" is + * the plane nearest the light source and the "back" is the far end of the + * light projection. */ class Frustum { public: - Plane3 right, left, bottom, top, back, front; - - Frustum() - {} - - Frustum(const Plane3& _right, const Plane3& _left, - const Plane3& _bottom, const Plane3& _top, - const Plane3& _back, const Plane3& _front) : - right(_right), - left(_left), - bottom(_bottom), - top(_top), - back(_back), - front(_front) - {} - - /** - * Construct the frustum planes from the given projection matrix. - */ - static Frustum createFromViewproj(const Matrix4& viewproj); - - /** - * \brief - * Normalise all planes in the frustum. - */ + Plane3 right, left, bottom, top, back, front; + + Frustum() + {} + + /// Construct a Frustum with six explicit planes + Frustum(const Plane3& _right, const Plane3& _left, + const Plane3& _bottom, const Plane3& _top, + const Plane3& _back, const Plane3& _front) + : right(_right), left(_left), bottom(_bottom), top(_top), + back(_back), front(_front) + {} + + /// Construct the frustum planes from the given projection matrix. + static Frustum createFromViewproj(const Matrix4& viewproj); + + /// Normalise all planes in the frustum. void normalisePlanes(); - /** - * \brief - * Get the projection matrix corresponding to the planes of this frustum. - */ + /// Get the projection matrix corresponding to the planes of this frustum. Matrix4 getProjectionMatrix() const; - /** - * \brief - * Return a copy of this frustum transformed by the given matrix. - */ + /// Return a copy of this frustum transformed by the given matrix. Frustum getTransformedBy(const Matrix4& transform) const; - /** - * \brief - * Test the intersection of this frustum with an AABB. - */ + /// Test the intersection of this frustum with an AABB. VolumeIntersectionValue testIntersection(const AABB& aabb) const; - /** - * Test the intersection of this frustum with a transformed AABB. - */ - VolumeIntersectionValue testIntersection(const AABB& aabb, const Matrix4& localToWorld) const; - - /** - * Returns true if the given point is contained in this frustum. - */ - bool testPoint(const Vector3& point) const; - - bool testLine(const Segment& segment) const; + /// Test the intersection of this frustum with a transformed AABB. + VolumeIntersectionValue testIntersection(const AABB& aabb, const Matrix4& localToWorld) const; + + /// Enum representing the corner points of each end plane + enum Corner + { + TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT + }; + + /// Enum representing each end plane + enum EndPlane + { + FRONT, BACK + }; + + /// Return the position of the given corner point + Vector3 getCornerPoint(EndPlane plane, Corner point) const + { + if (plane == FRONT) + { + switch (point) + { + case TOP_LEFT: + return Plane3::intersect(left, top, front); + case TOP_RIGHT: + return Plane3::intersect(right, top, front); + case BOTTOM_LEFT: + return Plane3::intersect(left, bottom, front); + case BOTTOM_RIGHT: + return Plane3::intersect(right, bottom, front); + } + } + else + { + switch (point) + { + case TOP_LEFT: + return Plane3::intersect(left, top, back); + case TOP_RIGHT: + return Plane3::intersect(right, top, back); + case BOTTOM_LEFT: + return Plane3::intersect(left, bottom, back); + case BOTTOM_RIGHT: + return Plane3::intersect(right, bottom, back); + } + } + + // All cases should be handled above + assert(false); + return Vector3(); + } + + /// Return an AABB enclosing this frustum + AABB getAABB() const + { + // The AABB of a frustum is simply the AABB which includes all eight + // corner points. + AABB result; + result.includePoint(getCornerPoint(FRONT, TOP_LEFT)); + result.includePoint(getCornerPoint(FRONT, BOTTOM_LEFT)); + result.includePoint(getCornerPoint(FRONT, TOP_RIGHT)); + result.includePoint(getCornerPoint(FRONT, BOTTOM_RIGHT)); + result.includePoint(getCornerPoint(BACK, TOP_LEFT)); + result.includePoint(getCornerPoint(BACK, BOTTOM_LEFT)); + result.includePoint(getCornerPoint(BACK, TOP_RIGHT)); + result.includePoint(getCornerPoint(BACK, BOTTOM_RIGHT)); + return result; + } + + /// Returns true if the given point is contained in this frustum. + bool testPoint(const Vector3& point) const; + + bool testLine(const Segment& segment) const; }; inline Frustum Frustum::createFromViewproj(const Matrix4& viewproj) @@ -94,8 +153,8 @@ inline Frustum Frustum::createFromViewproj(const Matrix4& viewproj) inline bool Frustum::testPoint(const Vector3& point) const { - return !right.testPoint(point) && !left.testPoint(point) && - !bottom.testPoint(point) && !top.testPoint(point) && + return !right.testPoint(point) && !left.testPoint(point) && + !bottom.testPoint(point) && !top.testPoint(point) && !back.testPoint(point) && !front.testPoint(point); } @@ -106,11 +165,11 @@ inline bool plane3_test_line(const Plane3& plane, const Segment& segment) inline bool Frustum::testLine(const Segment& segment) const { - return !plane3_test_line(right, segment) && - !plane3_test_line(left, segment) && - !plane3_test_line(bottom, segment) && - !plane3_test_line(top, segment) && - !plane3_test_line(back, segment) && + return !plane3_test_line(right, segment) && + !plane3_test_line(left, segment) && + !plane3_test_line(bottom, segment) && + !plane3_test_line(top, segment) && + !plane3_test_line(back, segment) && !plane3_test_line(front, segment); } diff --git a/libs/math/Matrix4.h b/libs/math/Matrix4.h index 7ca673069b..22ac92a114 100644 --- a/libs/math/Matrix4.h +++ b/libs/math/Matrix4.h @@ -392,7 +392,10 @@ class Matrix4 void invertFull(); /** + * \brief * Returns the given 3-component point transformed by this matrix. + * + * The point is assumed to have a W component of 1. */ template BasicVector3 transformPoint(const BasicVector3& point) const; @@ -677,6 +680,35 @@ class Matrix4 std::size_t clipTriangle(const Vector3& p0, const Vector3& p1, const Vector3& p2, Vector4 clipped[9]) const; }; +// =========================================================================== +// Operators +// =========================================================================== + +/** + * \brief + * Multiply a 4-component vector by this matrix. + * + * Equivalent to m.transform(v). + */ +template +BasicVector4 operator* (const Matrix4& m, const BasicVector4& v) +{ + return m.transform(v); +} + +/** + * \brief + * Multiply a 3-component vector by this matrix. + * + * The vector is upgraded to a 4-component vector with a W component of 1, i.e. + * equivalent to m.transformPoint(v). + */ +template +BasicVector3 operator* (const Matrix4& m, const BasicVector3& v) +{ + return m.transformPoint(v); +} + // ========================================================================================= // Inlined member definitions // ========================================================================================= diff --git a/libs/math/Vector3.h b/libs/math/Vector3.h index 9de7dfe741..0942f5aed6 100644 --- a/libs/math/Vector3.h +++ b/libs/math/Vector3.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "math/pi.h" @@ -98,16 +99,22 @@ class BasicVector3 const Element& y() const { return _v[1]; } const Element& z() const { return _v[2]; } - /** Compare this BasicVector3 against another for equality. - */ + /// Return human readable debug string (pretty print) + std::string pp() const + { + std::stringstream ss; + ss << "[" << x() << ", " << y() << ", " << z() << "]"; + return ss.str(); + } + + /// Compare this BasicVector3 against another for equality. bool operator== (const BasicVector3& other) const { return (other.x() == x() && other.y() == y() && other.z() == z()); } - /** Compare this BasicVector3 against another for inequality. - */ + /// Compare this BasicVector3 against another for inequality. bool operator!= (const BasicVector3& other) const { return !(*this == other); } diff --git a/libs/math/Vector4.h b/libs/math/Vector4.h index eadf675b95..73f7be4f5f 100644 --- a/libs/math/Vector4.h +++ b/libs/math/Vector4.h @@ -1,5 +1,7 @@ #pragma once +#include + /* greebo: This file contains the templated class definition of the three-component vector * * BasicVector4: A vector with three components of type @@ -44,8 +46,9 @@ class BasicVector4 _v[3] = w_; } - // Construct a BasicVector4 out of a Vector3 plus a fourth argument - BasicVector4(const BasicVector3& other, Element w_) { + // Construct a BasicVector4 out of a Vector3 plus a W value (default 1) + BasicVector4(const BasicVector3& other, Element w_ = 1) + { _v[0] = other.x(); _v[1] = other.y(); _v[2] = other.z(); @@ -53,32 +56,16 @@ class BasicVector4 } // Return non-constant references to the components - Element& x() { - return _v[0]; - } - Element& y() { - return _v[1]; - } - Element& z() { - return _v[2]; - } - Element& w() { - return _v[3]; - } + Element& x() { return _v[0]; } + Element& y() { return _v[1]; } + Element& z() { return _v[2]; } + Element& w() { return _v[3]; } // Return constant references to the components - const Element& x() const { - return _v[0]; - } - const Element& y() const { - return _v[1]; - } - const Element& z() const { - return _v[2]; - } - const Element& w() const { - return _v[3]; - } + const Element& x() const { return _v[0]; } + const Element& y() const { return _v[1]; } + const Element& z() const { return _v[2]; } + const Element& w() const { return _v[3]; } Element index(std::size_t i) const { return _v[i]; @@ -87,6 +74,21 @@ class BasicVector4 return _v[i]; } + /** + * \brief + * Return a readable (pretty-printed) string representation of the vector + * + * We need a dedicated function for this because the standard operator<< is + * already used for serialisation to the less readable space-separated text + * format. + */ + std::string pp() const + { + std::stringstream ss; + ss << "(" << x() << ", " << y() << ", " << z() << ", " << w() << ")"; + return ss.str(); + } + /** Compare this BasicVector4 against another for equality. */ bool operator== (const BasicVector4& other) const { diff --git a/libs/render/CamRenderer.h b/libs/render/CamRenderer.h new file mode 100644 index 0000000000..9ff8de2224 --- /dev/null +++ b/libs/render/CamRenderer.h @@ -0,0 +1,183 @@ +#pragma once + +#include "irenderable.h" +#include "ivolumetest.h" + +#include "VectorLightList.h" + +#include + +namespace render +{ + +/// RenderableCollector for use with 3D camera views or preview widgets +class CamRenderer: public RenderableCollector +{ + // The VolumeTest object for object culling + const VolumeTest& _view; + + // Render statistics + int _totalLights = 0; + int _visibleLights = 0; + + // Highlight state + bool _highlightFaces = false; + bool _highlightPrimitives = false; + Shader* _highlightedPrimitiveShader = nullptr; + Shader* _highlightedFaceShader = nullptr; + + // All lights we have received from the scene + std::list _sceneLights; + + // Lit renderable provided via addRenderable(), for which we construct the + // light list with lights received via addLight(). + struct LitRenderable + { + // Renderable information submitted with addLitObject() + const OpenGLRenderable& renderable; + const LitObject* litObject = nullptr; + Matrix4 local2World; + const IRenderEntity* entity = nullptr; + + // Calculated list of intersecting lights (initially empty) + render::lib::VectorLightList lights; + }; + using LitRenderables = std::vector; + + // Renderables added with addLitObject() need to be stored until their + // light lists can be calculated, which can't happen until all the lights + // are submitted too. + std::map _litRenderables; + + // Intersect all received renderables wiith lights + void calculateLightIntersections() + { + // For each shader + for (auto i = _litRenderables.begin(); i != _litRenderables.end(); ++i) + { + // For each renderable associated with this shader + for (auto j = i->second.begin(); j != i->second.end(); ++j) + { + // Test intersection between the LitObject and each light in + // the scene + for (const RendererLight* l: _sceneLights) + { + if (j->litObject && j->litObject->intersectsLight(*l)) + j->lights.addLight(*l); + } + } + } + } + +public: + + // Initialise CamRenderer with optional highlight shaders + CamRenderer(const VolumeTest& view, Shader* primHighlightShader = nullptr, + Shader* faceHighlightShader = nullptr) + : _view(view), + _highlightedPrimitiveShader(primHighlightShader), + _highlightedFaceShader(faceHighlightShader) + {} + + // Instruct the CamRenderer to push its sorted renderables to their + // respective shaders + void submitToShaders() + { + // Calculate intersections between lights and renderables we have received + calculateLightIntersections(); + + // Render objects with calculated light lists + for (auto i = _litRenderables.begin(); i != _litRenderables.end(); ++i) + { + Shader* shader = i->first; + wxASSERT(shader); + for (auto j = i->second.begin(); j != i->second.end(); ++j) + { + const LitRenderable& lr = *j; + shader->addRenderable(lr.renderable, lr.local2World, + &lr.lights, lr.entity); + } + } + } + + /// Obtain the visible light count + int getVisibleLights() const { return _visibleLights; } + + /// Obtain the total light count + int getTotalLights() const { return _totalLights; } + + // RenderableCollector implementation + + bool supportsFullMaterials() const override { return true; } + + void setHighlightFlag(Highlight::Flags flags, bool enabled) override + { + if (flags & Highlight::Faces) + { + _highlightFaces = enabled; + } + + if (flags & Highlight::Primitives) + { + _highlightPrimitives = enabled; + } + } + + void addLight(const RendererLight& light) override + { + // Determine if this light is visible within the view frustum + VolumeIntersectionValue viv = _view.TestAABB(light.lightAABB()); + if (viv != VOLUME_OUTSIDE) + { + // Store the light in our list of scene lights + _sceneLights.push_back(&light); + + // Count the light for the stats display + ++_visibleLights; + } + + // Count total lights + ++_totalLights; + } + + void addRenderable(Shader& shader, + const OpenGLRenderable& renderable, + const Matrix4& localToWorld, + const LitObject* litObject = nullptr, + const IRenderEntity* entity = nullptr) override + { + if (_highlightPrimitives && _highlightedPrimitiveShader) + _highlightedPrimitiveShader->addRenderable(renderable, localToWorld, + nullptr, entity); + + if (_highlightFaces && _highlightedFaceShader) + _highlightedFaceShader->addRenderable(renderable, localToWorld, + nullptr, entity); + + // Construct an entry for this shader in the map if it is the first + // time we've seen it + auto iter = _litRenderables.find(&shader); + if (iter == _litRenderables.end()) + { + // Add an entry for this shader, and pre-allocate some space in the + // vector to avoid too many expansions during scenegraph traversal. + LitRenderables emptyList; + emptyList.reserve(1024); + + auto result = _litRenderables.insert( + std::make_pair(&shader, std::move(emptyList)) + ); + wxASSERT(result.second); + iter = result.first; + } + wxASSERT(iter != _litRenderables.end()); + wxASSERT(iter->first == &shader); + + // Store a LitRenderable object for this renderable + LitRenderable lr { renderable, litObject, localToWorld, entity }; + iter->second.push_back(std::move(lr)); + } +}; + + +} diff --git a/libs/render/SimpleFrontendRenderer.h b/libs/render/SimpleFrontendRenderer.h deleted file mode 100644 index 67a35eca93..0000000000 --- a/libs/render/SimpleFrontendRenderer.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "irenderable.h" -#include "irender.h" -#include - -namespace render -{ - -/** - * greebo: This is a basic front-end renderer (collecting renderables) - * No highlighting support. It is returning FullMaterials as renderer style. - */ -class SimpleFrontendRenderer : - public RenderableCollector -{ -public: - SimpleFrontendRenderer() - {} - - void addRenderable(Shader& shader, - const OpenGLRenderable& renderable, - const Matrix4& world, const LightSources* lights, - const IRenderEntity* entity) override - { - shader.addRenderable(renderable, world, lights, entity); - } - - void addLitRenderable(Shader& shader, - OpenGLRenderable& renderable, - const Matrix4& localToWorld, - const LitObject& litObject, - const IRenderEntity* entity = nullptr) override - { - std::cerr << "FIXME: SimpleFrontendRenderer::addLitRenderable() unimplemented\n"; - shader.addRenderable(renderable, localToWorld, nullptr, entity); - } - - void addLight(const RendererLight&) override {} - - bool supportsFullMaterials() const override - { - return true; - } - - // No support for selection highlighting - void setHighlightFlag(Highlight::Flags flags, bool enabled) override {} -}; - -} // namespace diff --git a/libs/render/VectorLightList.h b/libs/render/VectorLightList.h index 7c1597dcf2..68aad36d83 100644 --- a/libs/render/VectorLightList.h +++ b/libs/render/VectorLightList.h @@ -12,13 +12,8 @@ namespace lib * \brief * A simple container of lights * - * This is used by particular object types to store the list of intersecting - * lights passed into LitObject::insertLight() as a result of an earlier call - * to LightList::calculateIntersectingLights(). - * - * Objects may use their insertLight() method to perform additional - * optimisations to exclude lights for object-specific reasons, then store the - * minimised light list in a VectorLightList for subsequent rendering. + * This is used by the CamRenderer to build a list of lights submitted with + * addLight(). */ class VectorLightList: public LightSources { diff --git a/libs/wxutil/preview/RenderPreview.cpp b/libs/wxutil/preview/RenderPreview.cpp index 9c8718ac27..84d377fe06 100644 --- a/libs/wxutil/preview/RenderPreview.cpp +++ b/libs/wxutil/preview/RenderPreview.cpp @@ -10,6 +10,8 @@ #include "math/AABB.h" #include "util/ScopedBoolLock.h" #include "registry/registry.h" +#include "render/CamRenderer.h" +#include "render/SceneRenderWalker.h" #include "../GLWidget.h" #include @@ -43,7 +45,6 @@ RenderPreview::RenderPreview(wxWindow* parent, bool enableAnimation) : _initialised(false), _renderGrid(registry::getValue(RKEY_RENDERPREVIEW_SHOWGRID)), _renderSystem(GlobalRenderSystemFactory().createRenderSystem()), - _sceneWalker(_renderer, _volumeTest), _viewOrigin(0, 0, 0), _viewAngles(0, 0, 0), _modelView(Matrix4::getIdentity()), @@ -483,14 +484,14 @@ bool RenderPreview::drawPreview() } // Front-end render phase, collect OpenGLRenderable objects from the scene - getScene()->foreachVisibleNodeInVolume(_volumeTest, _sceneWalker); + render::CamRenderer renderer(_volumeTest); + render::SceneRenderWalker sceneWalker(renderer, _volumeTest); + getScene()->foreachVisibleNodeInVolume(_volumeTest, sceneWalker); RenderStateFlags flags = getRenderFlagsFill(); - // Hack-inject the model rotation matrix just before the render pass - //Matrix4 modelView = _volumeTest.GetModelview().getMultipliedBy(_modelRotation); - // Launch the back end rendering + renderer.submitToShaders(); _renderSystem->render(flags, _volumeTest.GetModelview(), projection); // Give subclasses an opportunity to render their own on-screen stuff @@ -510,7 +511,9 @@ void RenderPreview::renderWireFrame() Matrix4 projection = getProjectionMatrix(0.1f, 10000, PREVIEW_FOV, _previewWidth, _previewHeight); // Front-end render phase, collect OpenGLRenderable objects from the scene - getScene()->foreachVisibleNodeInVolume(_volumeTest, _sceneWalker); + render::CamRenderer renderer(_volumeTest); + render::SceneRenderWalker sceneWalker(renderer, _volumeTest); + getScene()->foreachVisibleNodeInVolume(_volumeTest, sceneWalker); // Launch the back end rendering _renderSystem->render(flags, _volumeTest.GetModelview(), projection); diff --git a/libs/wxutil/preview/RenderPreview.h b/libs/wxutil/preview/RenderPreview.h index 5101be63c5..2deae46daf 100644 --- a/libs/wxutil/preview/RenderPreview.h +++ b/libs/wxutil/preview/RenderPreview.h @@ -13,8 +13,6 @@ #include "irender.h" #include "../FreezePointer.h" -#include "render/SimpleFrontendRenderer.h" -#include "render/SceneRenderWalker.h" #include "render/NopVolumeTest.h" class wxToolBarToolBase; @@ -92,13 +90,9 @@ class RenderPreview : // The backend rendersystem instance RenderSystemPtr _renderSystem; - // The front-end renderer, collecting the OpenGLRenderables - render::SimpleFrontendRenderer _renderer; + // Dummy VolumeTest render::NopVolumeTest _volumeTest; - // The scene adaptor passing nodes into our front-end renderer - render::SceneRenderWalker _sceneWalker; - // Current viewer position and view angles Vector3 _viewOrigin; Vector3 _viewAngles; diff --git a/missing b/missing index f62bbae306..625aeb1189 100755 --- a/missing +++ b/missing @@ -1,9 +1,9 @@ #! /bin/sh # Common wrapper for a few potentially missing GNU programs. -scriptversion=2013-10-28.13; # UTC +scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify @@ -17,7 +17,7 @@ scriptversion=2013-10-28.13; # UTC # GNU General Public License for more details. # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -101,9 +101,9 @@ else exit $st fi -perl_URL=http://www.perl.org/ -flex_URL=http://flex.sourceforge.net/ -gnu_software_URL=http://www.gnu.org/software +perl_URL=https://www.perl.org/ +flex_URL=https://github.com/westes/flex +gnu_software_URL=https://www.gnu.org/software program_details () { @@ -207,9 +207,9 @@ give_advice "$1" | sed -e '1s/^/WARNING: /' \ exit $st # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff --git a/radiant/camera/CamWnd.cpp b/radiant/camera/CamWnd.cpp index c674489918..7fa868d44f 100644 --- a/radiant/camera/CamWnd.cpp +++ b/radiant/camera/CamWnd.cpp @@ -35,6 +35,7 @@ #include #include #include "render/VectorLightList.h" +#include "render/CamRenderer.h" namespace ui { @@ -548,242 +549,6 @@ bool CamWnd::freeMoveEnabled() const return _freeMoveEnabled; } -namespace -{ - -// Implementation of RenderableCollector for the 3D camera view. -class CamRenderer: public RenderableCollector -{ - // The View object for object culling - const render::View& _view; - - // Render statistics - render::RenderStatistics& _renderStats; - - // Highlight state - bool _highlightFaces = false; - bool _highlightPrimitives = false; - Shader& _highlightedPrimitiveShader; - Shader& _highlightedFaceShader; - - // All lights we have received from the scene - std::list _sceneLights; - - // Legacy lit renderable which provided its own LightSources to - // addRenderable() - struct LegacyLitRenderable - { - const OpenGLRenderable& renderable; - const LightSources* lights = nullptr; - Matrix4 local2World; - const IRenderEntity* entity = nullptr; - - LegacyLitRenderable(const OpenGLRenderable& r, const LightSources* l, - const Matrix4& l2w, const IRenderEntity* e) - : renderable(r), lights(l), local2World(l2w), entity(e) - {} - }; - using LegacyLitRenderables = std::vector; - - // Legacy renderables with their own light lists. No processing needed; - // just store them until it's time to submit to shaders. - using LegacyRenderablesByShader = std::map; - LegacyRenderablesByShader _legacyRenderables; - - // Lit renderable provided via addLitRenderable(), for which we construct - // the light list with lights received via addLight(). - struct LitRenderable - { - // Renderable information submitted with addLitObject() - const OpenGLRenderable& renderable; - const LitObject& litObject; - Matrix4 local2World; - const IRenderEntity* entity = nullptr; - - // Calculated list of intersecting lights (initially empty) - render::lib::VectorLightList lights; - }; - using LitRenderables = std::vector; - - // Renderables added with addLitObject() need to be stored until their - // light lists can be calculated, which can't happen until all the lights - // are submitted too. - std::map _litRenderables; - - // Intersect all received renderables wiith lights - void calculateLightIntersections() - { - // For each shader - for (auto i = _litRenderables.begin(); i != _litRenderables.end(); ++i) - { - // For each renderable associated with this shader - for (auto j = i->second.begin(); j != i->second.end(); ++j) - { - // Test intersection between the LitObject and each light in - // the scene - for (const RendererLight* l: _sceneLights) - { - if (j->litObject.intersectsLight(*l)) - j->lights.addLight(*l); - } - } - } - } - -public: - - // Initialise CamRenderer with the highlight shaders - CamRenderer(const render::View& view, Shader& primHighlightShader, - Shader& faceHighlightShader, render::RenderStatistics& stats) - : _view(view), - _renderStats(stats), - _highlightedPrimitiveShader(primHighlightShader), - _highlightedFaceShader(faceHighlightShader) - {} - - // Instruct the CamRenderer to push its sorted renderables to their - // respective shaders and perform the actual render - void backendRender(RenderStateFlags allowedFlags, const Matrix4& modelView, - const Matrix4& projection) - { - // Calculate intersections between lights and renderables we have received - calculateLightIntersections(); - - // For each shader in the map - for (auto i = _legacyRenderables.begin(); - i != _legacyRenderables.end(); - ++i) - { - // Iterate over the list of renderables for this shader, submitting - // each one - Shader* shader = i->first; - wxASSERT(shader); - for (auto j = i->second.begin(); j != i->second.end(); ++j) - { - const LegacyLitRenderable& lr = *j; - shader->addRenderable(lr.renderable, lr.local2World, - lr.lights, lr.entity); - } - } - - // Tell the render system to render its shaders and renderables - GlobalRenderSystem().render(allowedFlags, modelView, projection, - _view.getViewer()); - } - - // RenderableCollector implementation - - bool supportsFullMaterials() const override { return true; } - - void setHighlightFlag(Highlight::Flags flags, bool enabled) override - { - if (flags & Highlight::Faces) - { - _highlightFaces = enabled; - } - - if (flags & Highlight::Primitives) - { - _highlightPrimitives = enabled; - } - } - - void addLight(const RendererLight& light) override - { - // Determine if this light is visible within the view frustum - VolumeIntersectionValue viv = _view.TestAABB(light.lightAABB()); - if (viv == VOLUME_OUTSIDE) - { - // Not interested - _renderStats.addLight(false); - } - else - { - // Store the light in our list of scene lights - _sceneLights.push_back(&light); - - // Count the light for the stats display - _renderStats.addLight(true); - } - } - - void addRenderable(Shader& shader, const OpenGLRenderable& renderable, - const Matrix4& world, const LightSources* lights, - const IRenderEntity* entity) override - { - if (_highlightPrimitives) - _highlightedPrimitiveShader.addRenderable(renderable, world, - lights, entity); - - if (_highlightFaces) - _highlightedFaceShader.addRenderable(renderable, world, - lights, entity); - - // Construct an entry for this shader in the map if it is the first - // time we've seen it - auto iter = _legacyRenderables.find(&shader); - if (iter == _legacyRenderables.end()) - { - // Add an entry for this shader, and pre-allocate some space in the - // vector to avoid too many expansions during scenegraph traversal. - LegacyLitRenderables emptyList; - emptyList.reserve(1024); - - auto result = _legacyRenderables.insert( - std::make_pair(&shader, std::move(emptyList)) - ); - wxASSERT(result.second); - iter = result.first; - } - wxASSERT(iter != _legacyRenderables.end()); - - // Add the renderable and its lights to the list of lit renderables for - // this shader - wxASSERT(iter->first == &shader); - iter->second.emplace_back(renderable, lights, world, entity); - } - - void addLitRenderable(Shader& shader, - OpenGLRenderable& renderable, - const Matrix4& localToWorld, - const LitObject& litObject, - const IRenderEntity* entity = nullptr) override - { - if (_highlightPrimitives) - _highlightedPrimitiveShader.addRenderable(renderable, localToWorld, - nullptr, entity); - - if (_highlightFaces) - _highlightedFaceShader.addRenderable(renderable, localToWorld, - nullptr, entity); - - // Construct an entry for this shader in the map if it is the first - // time we've seen it - auto iter = _litRenderables.find(&shader); - if (iter == _litRenderables.end()) - { - // Add an entry for this shader, and pre-allocate some space in the - // vector to avoid too many expansions during scenegraph traversal. - LitRenderables emptyList; - emptyList.reserve(1024); - - auto result = _litRenderables.insert( - std::make_pair(&shader, std::move(emptyList)) - ); - wxASSERT(result.second); - iter = result.first; - } - wxASSERT(iter != _litRenderables.end()); - wxASSERT(iter->first == &shader); - - // Store a LitRenderable object for this renderable - LitRenderable lr { renderable, litObject, localToWorld, entity }; - iter->second.push_back(std::move(lr)); - } -}; - -} - void CamWnd::performFreeMove(int dx, int dy) { int angleSpeed = getCameraSettings()->angleSpeed(); @@ -977,9 +742,13 @@ void CamWnd::Cam_Draw() // Main scene render { // Front end (renderable collection from scene) - CamRenderer renderer(_view, *_primitiveHighlightShader, - *_faceHighlightShader, _renderStats); + render::CamRenderer renderer(_view, _primitiveHighlightShader.get(), + _faceHighlightShader.get()); render::RenderableCollectionWalker::CollectRenderablesInScene(renderer, _view); + + // Accumulate render statistics + _renderStats.setLightCount(renderer.getVisibleLights(), + renderer.getTotalLights()); _renderStats.frontEndComplete(); // Render any active mousetools @@ -988,9 +757,10 @@ void CamWnd::Cam_Draw() i.second->render(GlobalRenderSystem(), renderer, _view); } - // Backend (shader rendering) - renderer.backendRender(allowedRenderFlags, _camera->getModelView(), - _camera->getProjection()); + // Back end (submit to shaders and do the actual render) + renderer.submitToShaders(); + GlobalRenderSystem().render(allowedRenderFlags, _camera->getModelView(), + _camera->getProjection(), _view.getViewer()); } // greebo: Draw the clipper's points (skipping the depth-test) diff --git a/radiant/render/RenderStatistics.h b/radiant/render/RenderStatistics.h index 5464f233bb..55e0e606de 100644 --- a/radiant/render/RenderStatistics.h +++ b/radiant/render/RenderStatistics.h @@ -15,9 +15,9 @@ class RenderStatistics // Time for the render front-end only long _feTime = 0; - // Count of lights (visible and culled) + // Count of lights int _visibleLights = 0; - int _culledLights = 0; + int _totalLights = 0; public: @@ -29,7 +29,7 @@ class RenderStatistics long beTime = totTime - _feTime; return "lights: " + std::to_string(_visibleLights) - + " / " + std::to_string(_visibleLights + _culledLights) + + " / " + std::to_string(_totalLights) + " | f/e: " + std::to_string(_feTime) + " ms" + " | b/e: " + std::to_string(beTime) + " ms" + " | tot: " + std::to_string(totTime) + " ms" @@ -42,19 +42,17 @@ class RenderStatistics _feTime = _timer.Time(); } - /// Increment the light count - void addLight(bool visible) + /// Set the light count + void setLightCount(int visible, int total) { - if (visible) - ++_visibleLights; - else - ++_culledLights; + _visibleLights += visible; + _totalLights += total; } /// Reset statistics at the beginning of a frame render void resetStats() { - _visibleLights = _culledLights = 0; + _visibleLights = _totalLights = 0; _feTime = 0; _timer.Start(); diff --git a/radiant/xyview/XYRenderer.h b/radiant/xyview/XYRenderer.h index bca12cafe6..b7a341b7ca 100644 --- a/radiant/xyview/XYRenderer.h +++ b/radiant/xyview/XYRenderer.h @@ -55,27 +55,19 @@ class XYRenderer: public RenderableCollector void addRenderable(Shader& shader, const OpenGLRenderable& renderable, - const Matrix4& world, const LightSources* lights, - const IRenderEntity* entity) override + const Matrix4& localToWorld, + const LitObject* /* litObject */, + const IRenderEntity* entity = nullptr) override { if (_state.highlightPrimitives) { if (_state.highlightAsGroupMember) - _selectedShaderGroup->addRenderable(renderable, world, - lights, entity); + _selectedShaderGroup->addRenderable(renderable, localToWorld, + nullptr, entity); else - _selectedShader->addRenderable(renderable, world, lights, entity); + _selectedShader->addRenderable(renderable, localToWorld, nullptr, entity); } - shader.addRenderable(renderable, world, lights, entity); - } - - void addLitRenderable(Shader& shader, - OpenGLRenderable& renderable, - const Matrix4& localToWorld, - const LitObject& /* litObject */, - const IRenderEntity* entity = nullptr) override - { shader.addRenderable(renderable, localToWorld, nullptr, entity); } diff --git a/radiantcore/Makefile.am b/radiantcore/Makefile.am index de7724c6d8..f5a0f3199a 100644 --- a/radiantcore/Makefile.am +++ b/radiantcore/Makefile.am @@ -180,9 +180,9 @@ libradiantcore_la_SOURCES = Radiant.cpp \ model/export/WavefrontExporter.cpp \ model/picomodel/PicoModelLoader.cpp \ model/picomodel/PicoModelModule.cpp \ - model/picomodel/PicoModelNode.cpp \ - model/picomodel/RenderablePicoModel.cpp \ - model/picomodel/RenderablePicoSurface.cpp \ + model/picomodel/StaticModelNode.cpp \ + model/picomodel/StaticModel.cpp \ + model/picomodel/StaticModelSurface.cpp \ model/picomodel/lib/pm_md3.c \ model/picomodel/lib/pm_md2.c \ model/picomodel/lib/pm_obj.c \ @@ -233,7 +233,6 @@ libradiantcore_la_SOURCES = Radiant.cpp \ rendersystem/backend/OpenGLShader.cpp \ rendersystem/backend/GLProgramFactory.cpp \ rendersystem/backend/OpenGLShaderPass.cpp \ - rendersystem/LinearLightList.cpp \ rendersystem/OpenGLRenderSystem.cpp \ rendersystem/RenderSystemFactory.cpp \ rendersystem/SharedOpenGLContextModule.cpp \ diff --git a/radiantcore/brush/Brush.cpp b/radiantcore/brush/Brush.cpp index aa94359624..9d856b8f2f 100644 --- a/radiantcore/brush/Brush.cpp +++ b/radiantcore/brush/Brush.cpp @@ -475,7 +475,6 @@ void Brush::onFacePlaneChanged() { m_planeChanged = true; aabbChanged(); - _owner.lightsChanged(); } void Brush::onFaceShaderChanged() diff --git a/radiantcore/brush/BrushNode.cpp b/radiantcore/brush/BrushNode.cpp index a3289a9640..99cee19c14 100644 --- a/radiantcore/brush/BrushNode.cpp +++ b/radiantcore/brush/BrushNode.cpp @@ -12,7 +12,6 @@ // Constructor BrushNode::BrushNode() : scene::SelectableNode(), - m_lightList(&GlobalRenderSystem().attachLitObject(*this)), m_brush(*this), _selectedPoints(GL_POINTS), _faceCentroidPointsCulled(GL_POINTS), @@ -21,8 +20,6 @@ BrushNode::BrushNode() : _untransformedOriginChanged(true) { m_brush.attach(*this); // BrushObserver - - SelectableNode::setTransformChangedCallback(std::bind(&BrushNode::lightsChanged, this)); } // Copy Constructor @@ -39,7 +36,6 @@ BrushNode::BrushNode(const BrushNode& other) : PlaneSelectable(other), LitObject(other), Transformable(other), - m_lightList(&GlobalRenderSystem().attachLitObject(*this)), m_brush(*this, other.m_brush), _selectedPoints(GL_POINTS), _faceCentroidPointsCulled(GL_POINTS), @@ -52,7 +48,6 @@ BrushNode::BrushNode(const BrushNode& other) : BrushNode::~BrushNode() { - GlobalRenderSystem().detachLitObject(*this); m_brush.detach(*this); // BrushObserver } @@ -61,11 +56,6 @@ scene::INode::Type BrushNode::getNodeType() const return Type::Brush; } -void BrushNode::lightsChanged() -{ - m_lightList->setDirty(); -} - const AABB& BrushNode::localAABB() const { return m_brush.localAABB(); } @@ -309,19 +299,6 @@ bool BrushNode::intersectsLight(const RendererLight& light) const { return light.intersectsAABB(worldAABB()); } -void BrushNode::insertLight(const RendererLight& light) { - const Matrix4& l2w = localToWorld(); - for (FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { - i->addLight(l2w, light); - } -} - -void BrushNode::clearLights() { - for (FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { - i->m_lights.clear(); - } -} - void BrushNode::renderComponents(RenderableCollector& collector, const VolumeTest& volume) const { m_brush.evaluateBRep(); @@ -441,21 +418,33 @@ void BrushNode::renderSolid(RenderableCollector& collector, const VolumeTest& volume, const Matrix4& localToWorld) const { - m_lightList->calculateIntersectingLights(); - assert(_renderEntity); // brushes rendered without parent entity - no way! // Check for the override status of this brush bool forceVisible = isForcedVisible(); // Submit the lights and renderable geometry for each face - for (const FaceInstance& face : m_faceInstances) + for (const FaceInstance& faceInst : m_faceInstances) { // Skip invisible faces before traversing further - if (!forceVisible && !face.faceIsVisible()) continue; + if (!forceVisible && !faceInst.faceIsVisible()) continue; - // greebo: BrushNodes have always an identity l2w, don't do any transforms - face.renderSolid(collector, volume, *_renderEntity); + const Face& face = faceInst.getFace(); + if (face.intersectVolume(volume)) + { + bool highlight = faceInst.selectedComponents(); + if (highlight) + collector.setHighlightFlag(RenderableCollector::Highlight::Faces, true); + + // greebo: BrushNodes have always an identity l2w, don't do any transforms + collector.addRenderable( + *face.getFaceShader().getGLShader(), face.getWinding(), + Matrix4::getIdentity(), this, _renderEntity + ); + + if (highlight) + collector.setHighlightFlag(RenderableCollector::Highlight::Faces, false); + } } renderSelectedPoints(collector, volume, localToWorld); diff --git a/radiantcore/brush/BrushNode.h b/radiantcore/brush/BrushNode.h index f8e014796a..a98fa9e915 100644 --- a/radiantcore/brush/BrushNode.h +++ b/radiantcore/brush/BrushNode.h @@ -31,8 +31,6 @@ class BrushNode : public Transformable, public ITraceable { - LightList* m_lightList; - // The actual contained brush (NO reference) Brush m_brush; @@ -84,8 +82,6 @@ class BrushNode : Type getNodeType() const override; - void lightsChanged(); - // Bounded implementation virtual const AABB& localAABB() const override; @@ -138,8 +134,6 @@ class BrushNode : // LitObject implementation bool intersectsLight(const RendererLight& light) const override; - void insertLight(const RendererLight& light) override; - void clearLights() override; // Renderable implementation void renderComponents(RenderableCollector& collector, const VolumeTest& volume) const override; diff --git a/radiantcore/brush/Face.cpp b/radiantcore/brush/Face.cpp index 04b9d3cfb9..c961c635bd 100644 --- a/radiantcore/brush/Face.cpp +++ b/radiantcore/brush/Face.cpp @@ -224,14 +224,6 @@ bool Face::intersectVolume(const VolumeTest& volume, const Matrix4& localToWorld } } -void Face::renderSolid(RenderableCollector& collector, - const Matrix4& localToWorld, const IRenderEntity& entity, - const LightSources& lights) const -{ - collector.addRenderable(*_shader.getGLShader(), m_winding, localToWorld, - &lights, &entity); -} - void Face::renderWireframe(RenderableCollector& collector, const Matrix4& localToWorld, const IRenderEntity& entity) const { diff --git a/radiantcore/brush/Face.h b/radiantcore/brush/Face.h index 31d43fc2f8..befa24d9de 100644 --- a/radiantcore/brush/Face.h +++ b/radiantcore/brush/Face.h @@ -104,8 +104,6 @@ class Face : bool intersectVolume(const VolumeTest& volume, const Matrix4& localToWorld) const; // Frontend render methods for submitting the face winding - void renderSolid(RenderableCollector& collector, const Matrix4& localToWorld, - const IRenderEntity& entity, const LightSources& lights) const; void renderWireframe(RenderableCollector& collector, const Matrix4& localToWorld, const IRenderEntity& entity) const; diff --git a/radiantcore/brush/FaceInstance.cpp b/radiantcore/brush/FaceInstance.cpp index e3e769a56b..3020cc9cfc 100644 --- a/radiantcore/brush/FaceInstance.cpp +++ b/radiantcore/brush/FaceInstance.cpp @@ -163,27 +163,6 @@ bool FaceInstance::intersectVolume(const VolumeTest& volume, const Matrix4& loca return m_face->intersectVolume(volume, localToWorld); } -void FaceInstance::renderSolid(RenderableCollector& collector, const VolumeTest& volume, - const IRenderEntity& entity) const -{ - if (m_face->intersectVolume(volume)) - { - bool highlight = selectedComponents(); - - if (highlight) - { - collector.setHighlightFlag(RenderableCollector::Highlight::Faces, true); - } - - m_face->renderSolid(collector, Matrix4::getIdentity(), entity, m_lights); - - if (highlight) - { - collector.setHighlightFlag(RenderableCollector::Highlight::Faces, false); - } - } -} - void FaceInstance::renderWireframe(RenderableCollector& collector, const VolumeTest& volume, const IRenderEntity& entity) const { @@ -448,19 +427,6 @@ void FaceInstance::connectivityChanged() { m_selectableEdges.setSelected(false); } -void FaceInstance::addLight(const Matrix4& localToWorld, const RendererLight& light) -{ - const Plane3& facePlane = getFace().plane3(); - - Plane3 tmp = Plane3(facePlane.normal(), -facePlane.dist()) - .transformed(localToWorld); - - if (!tmp.testPoint(light.worldOrigin()) || !tmp.testPoint(light.getLightOrigin())) - { - m_lights.addLight(light); - } -} - void FaceInstance::updateFaceVisibility() { getFace().updateFaceVisibility(); diff --git a/radiantcore/brush/FaceInstance.h b/radiantcore/brush/FaceInstance.h index 32fe548801..122b2c113d 100644 --- a/radiantcore/brush/FaceInstance.h +++ b/radiantcore/brush/FaceInstance.h @@ -33,8 +33,6 @@ class FaceInstance static FaceInstanceSet _selectedFaceInstances; public: - mutable render::lib::VectorLightList m_lights; - FaceInstance(Face& face, const SelectionChangedSlot& observer); FaceInstance(const FaceInstance& other); @@ -111,8 +109,6 @@ class FaceInstance bool intersectVolume(const VolumeTest& volume, const Matrix4& localToWorld) const; // Frontend render methods for submitting the face to the given collector - void renderSolid(RenderableCollector& collector, const VolumeTest& volume, - const IRenderEntity& entity) const; void renderWireframe(RenderableCollector& collector, const VolumeTest& volume, const IRenderEntity& entity) const; @@ -146,8 +142,6 @@ class FaceInstance void connectivityChanged(); - void addLight(const Matrix4& localToWorld, const RendererLight& light); - bool faceIsVisible() const { return m_face->isVisible(); diff --git a/radiantcore/entity/light/Light.cpp b/radiantcore/entity/light/Light.cpp index dfa36d541f..341b21b117 100644 --- a/radiantcore/entity/light/Light.cpp +++ b/radiantcore/entity/light/Light.cpp @@ -726,10 +726,14 @@ Matrix4 Light::getLightTextureTransformation() const } } -/* This is needed for the drag manipulator to check the aabb of the light volume only (excl. the light center) - */ +// AABB for light volume only (excluding the light_center which might be +// outside the volume), used for drag manipulator and render culling. AABB Light::lightAABB() const { + if (isProjected()) + // Return Frustum AABB in *world* space + return _frustum.getTransformedBy(_owner.localToParent()).getAABB(); + else return AABB(_originTransformed, m_doom3Radius.m_radiusTransformed); } @@ -967,14 +971,18 @@ void Light::updateProjection() const //rMessage() << " Frustum Plane " << 4 << ": " << _frustum.front.normal() << ", dist: " << _frustum.front.dist() << std::endl; //rMessage() << " Frustum Plane " << 5 << ": " << _frustum.back.normal() << ", dist: " << _frustum.back.dist() << std::endl; + const Vector3& t = _lightTargetTransformed; + const Vector3& u = _lightUpTransformed; + const Vector3& r = _lightRightTransformed; + // Pre-calculate the local2Texture matrix which will be needed in getLightTextureTransformation() // The only thing missing in this matrix will be the world rotation and world translation _localToTexture = Matrix4::getIdentity(); // Scale the light volume such that it is in a [-0.5..0.5] cube, including light origin - Vector3 boundsOrigin = (_lightTargetTransformed - _lightStartTransformed) * 0.5f; - Vector3 boundsExtents = _lightUpTransformed + _lightRightTransformed; - boundsExtents.z() = fabs(_lightTargetTransformed.z() * 0.5f); + Vector3 boundsOrigin = (t - _lightStartTransformed) * 0.5f; + Vector3 boundsExtents = u + r; + boundsExtents.z() = fabs(t.z() * 0.5f); AABB bounds(boundsOrigin, boundsExtents); @@ -1003,6 +1011,22 @@ void Light::updateProjection() const // Now move the cube to [0..1] and we're done _localToTexture.premultiplyBy(Matrix4::getTranslation(Vector3(0.5f, 0.5f, 0))); + +#if defined(DEBUG_LIGHT_MATRIX) + Vector4 t4(t); + Vector4 o(0, 0, 0, 1); + Vector4 topRight = t + u + r; + Vector4 bottomLeft = t - u - r; + + std::cout << "_localToTexture:" + << "\n\tOrigin -> " << (_localToTexture * o).pp() + << "\n\tt: " << t4.pp() << " -> " << (_localToTexture * t4).pp() + << "\n\tt + u + r: " << topRight.pp() << " -> " + << (_localToTexture * topRight).pp() + << "\n\tt - u - r: " << bottomLeft.pp() << " -> " + << (_localToTexture * bottomLeft).pp() + << "\n"; +#endif } const ShaderPtr& Light::getShader() const diff --git a/radiantcore/entity/light/LightNode.cpp b/radiantcore/entity/light/LightNode.cpp index 9ca3cf964e..f23dbecc42 100644 --- a/radiantcore/entity/light/LightNode.cpp +++ b/radiantcore/entity/light/LightNode.cpp @@ -72,7 +72,6 @@ AABB LightNode::getSelectAABB() const } void LightNode::lightChanged() { - GlobalRenderSystem().lightChanged(); } const AABB& LightNode::localAABB() const { @@ -88,8 +87,6 @@ void LightNode::onInsertIntoScene(scene::IMapRootNode& root) { // Call the base class first EntityNode::onInsertIntoScene(root); - - GlobalRenderSystem().attachLight(_light); } void LightNode::onRemoveFromScene(scene::IMapRootNode& root) @@ -97,8 +94,6 @@ void LightNode::onRemoveFromScene(scene::IMapRootNode& root) // Call the base class first EntityNode::onRemoveFromScene(root); - GlobalRenderSystem().detachLight(_light); - // De-select all child components as well setSelectedComponents(false, SelectionSystem::eVertex); setSelectedComponents(false, SelectionSystem::eFace); diff --git a/radiantcore/entity/light/Renderables.cpp b/radiantcore/entity/light/Renderables.cpp index 96c59b4627..ceeefc80af 100644 --- a/radiantcore/entity/light/Renderables.cpp +++ b/radiantcore/entity/light/Renderables.cpp @@ -62,7 +62,7 @@ void drawCenterNormal(const Vector3& a, const Vector3& b, const Vector3& c, cons const Vector3& direction, float length) { Vector3 middle = (a + b + c + d) / 4; - + glBegin(GL_LINES); glVertex3dv(middle); @@ -74,56 +74,104 @@ void drawCenterNormal(const Vector3& a, const Vector3& b, const Vector3& c, cons } #endif -void RenderLightProjection::render(const RenderInfo& info) const { - - // greebo: These four define the base area and are always needed to draw the light - Vector3 backUpperLeft = Plane3::intersect(_frustum.left, _frustum.top, _frustum.back); - Vector3 backLowerLeft = Plane3::intersect(_frustum.left, _frustum.bottom, _frustum.back); - Vector3 backUpperRight = Plane3::intersect(_frustum.right, _frustum.top, _frustum.back); - Vector3 backLowerRight = Plane3::intersect(_frustum.right, _frustum.bottom, _frustum.back); - - // Move all points to world space - backUpperLeft += _origin; - backLowerLeft += _origin; - backUpperRight += _origin; - backLowerRight += _origin; - - if (_start != Vector3(0,0,0)) - { - // Calculate the vertices defining the top area - Vector3 frontUpperLeft = Plane3::intersect(_frustum.left, _frustum.top, _frustum.front); - Vector3 frontLowerLeft = Plane3::intersect(_frustum.left, _frustum.bottom, _frustum.front); - Vector3 frontUpperRight = Plane3::intersect(_frustum.right, _frustum.top, _frustum.front); - Vector3 frontLowerRight = Plane3::intersect(_frustum.right, _frustum.bottom, _frustum.front); - - frontUpperLeft += _origin; - frontLowerLeft += _origin; - frontUpperRight += _origin; - frontLowerRight += _origin; - - Vector3 frustum[8] = { frontUpperLeft, frontLowerLeft, frontLowerRight, frontUpperRight, - backUpperLeft, backLowerLeft, backLowerRight, backUpperRight }; - drawFrustum(frustum); +/* greebo: draws a frustum defined by 8 vertices + * points[0] to points[3] define the top area vertices (clockwise starting from the "upper right" corner) + * points[4] to points[7] define the base rectangle (clockwise starting from the "upper right" corner) + */ +inline void drawFrustum(const Vector3 points[8]) +{ + typedef unsigned int index_t; + index_t indices[24] = { + 0, 4, // top up right to bottom up right + 1, 5, // top down right to bottom down right + 2, 6, // top down left to bottom down left + 3, 7, // top up left to bottom up left + + 0, 1, // top up right to top down right + 1, 2, // top down right to top down left + 2, 3, // top down left to top up left + 3, 0, // top up left to top up right + + 4, 5, // bottom up right to bottom down right + 5, 6, // bottom down right to bottom down left + 6, 7, // bottom down left to bottom up left + 7, 4, // bottom up left to bottom up right + }; + glVertexPointer(3, GL_DOUBLE, 0, points); + glDrawElements(GL_LINES, sizeof(indices)/sizeof(index_t), GL_UNSIGNED_INT, indices); +} + +/* greebo: draws a pyramid defined by 5 vertices + * points[0] is the top of the pyramid + * points[1] to points[4] is the base rectangle + */ +inline void drawPyramid(const Vector3 points[5]) +{ + typedef unsigned int index_t; + index_t indices[16] = { + 0, 1, // top to first + 0, 2, // top to second + 0, 3, // top to third + 0, 4, // top to fourth + 1, 2, // first to second + 2, 3, // second to third + 3, 4, // third to second + 4, 1, // fourth to first + }; + glVertexPointer(3, GL_DOUBLE, 0, points); + glDrawElements(GL_LINES, sizeof(indices)/sizeof(index_t), GL_UNSIGNED_INT, indices); +} + +void RenderLightProjection::render(const RenderInfo& info) const +{ + // greebo: These four define the base area and are always needed to draw the light + Vector3 backUpperLeft = _frustum.getCornerPoint(Frustum::BACK, Frustum::TOP_LEFT); + Vector3 backLowerLeft = _frustum.getCornerPoint(Frustum::BACK, Frustum::BOTTOM_LEFT); + Vector3 backUpperRight = _frustum.getCornerPoint(Frustum::BACK, Frustum::TOP_RIGHT); + Vector3 backLowerRight = _frustum.getCornerPoint(Frustum::BACK, Frustum::BOTTOM_RIGHT); + + // Move all points to world space + backUpperLeft += _origin; + backLowerLeft += _origin; + backUpperRight += _origin; + backLowerRight += _origin; + + if (_start != Vector3(0,0,0)) + { + // Calculate the vertices defining the top area + Vector3 frontUpperLeft = _frustum.getCornerPoint(Frustum::FRONT, Frustum::TOP_LEFT); + Vector3 frontLowerLeft = _frustum.getCornerPoint(Frustum::FRONT, Frustum::BOTTOM_LEFT); + Vector3 frontUpperRight = _frustum.getCornerPoint(Frustum::FRONT, Frustum::TOP_RIGHT); + Vector3 frontLowerRight = _frustum.getCornerPoint(Frustum::FRONT, Frustum::BOTTOM_RIGHT); + + frontUpperLeft += _origin; + frontLowerLeft += _origin; + frontUpperRight += _origin; + frontLowerRight += _origin; + + Vector3 frustum[8] = { frontUpperLeft, frontLowerLeft, frontLowerRight, frontUpperRight, + backUpperLeft, backLowerLeft, backLowerRight, backUpperRight }; + drawFrustum(frustum); #ifdef DRAW_LIGHT_FRUSTUM_NORMALS - float length = 20; - - drawCenterNormal(backUpperLeft, frontUpperLeft, backLowerLeft, frontLowerLeft, _frustum.left.normal(), length); - drawCenterNormal(backLowerLeft, frontLowerLeft, frontLowerRight, backLowerRight, _frustum.bottom.normal(), length); - drawCenterNormal(frontUpperRight, backUpperRight, backLowerRight, frontLowerRight, _frustum.right.normal(), length); - drawCenterNormal(backUpperLeft, backUpperRight, frontUpperRight, frontUpperLeft, _frustum.top.normal(), length); - drawCenterNormal(frontUpperLeft, frontLowerLeft, frontLowerRight, frontUpperRight, _frustum.front.normal(), length); - drawCenterNormal(backUpperLeft, backLowerLeft, backLowerRight, backUpperRight, _frustum.back.normal(), length); + float length = 20; + + drawCenterNormal(backUpperLeft, frontUpperLeft, backLowerLeft, frontLowerLeft, _frustum.left.normal(), length); + drawCenterNormal(backLowerLeft, frontLowerLeft, frontLowerRight, backLowerRight, _frustum.bottom.normal(), length); + drawCenterNormal(frontUpperRight, backUpperRight, backLowerRight, frontLowerRight, _frustum.right.normal(), length); + drawCenterNormal(backUpperLeft, backUpperRight, frontUpperRight, frontUpperLeft, _frustum.top.normal(), length); + drawCenterNormal(frontUpperLeft, frontLowerLeft, frontLowerRight, frontUpperRight, _frustum.front.normal(), length); + drawCenterNormal(backUpperLeft, backLowerLeft, backLowerRight, backUpperRight, _frustum.back.normal(), length); #endif - } - else { - // no light_start, just use the top vertex (doesn't need to be mirrored) - Vector3 top = Plane3::intersect(_frustum.left, _frustum.right, _frustum.top); - top += _origin; - - Vector3 pyramid[5] = { top, backUpperLeft, backLowerLeft, backLowerRight, backUpperRight }; - drawPyramid(pyramid); - } + } + else { + // no light_start, just use the top vertex (doesn't need to be mirrored) + Vector3 top = Plane3::intersect(_frustum.left, _frustum.right, _frustum.top); + top += _origin; + + Vector3 pyramid[5] = { top, backUpperLeft, backLowerLeft, backLowerRight, backUpperRight }; + drawPyramid(pyramid); + } } } // namespace entity diff --git a/radiantcore/model/md5/MD5ModelNode.cpp b/radiantcore/model/md5/MD5ModelNode.cpp index edc3f87eda..be8440898d 100644 --- a/radiantcore/model/md5/MD5ModelNode.cpp +++ b/radiantcore/model/md5/MD5ModelNode.cpp @@ -6,30 +6,12 @@ #include "iscenegraph.h" #include -namespace md5 { - -// Local helper -inline void Surface_addLight(const MD5Surface& surface, - render::lib::VectorLightList& lights, - const Matrix4& localToWorld, - const RendererLight& light) +namespace md5 { - if (light.intersectsAABB( - AABB::createFromOrientedAABB(surface.localAABB(), localToWorld) - ) - ) - { - lights.addLight(light); - } -} MD5ModelNode::MD5ModelNode(const MD5ModelPtr& model) : - _model(new MD5Model(*model)), // create a copy of the incoming model, we need our own instance - _surfaceLightLists(_model->size()) + _model(new MD5Model(*model)) // create a copy of the incoming model, we need our own instance { - _lightList = &GlobalRenderSystem().attachLitObject(*this); - - Node::setTransformChangedCallback((std::bind(&MD5ModelNode::lightsChanged, this))); } const model::IModel& MD5ModelNode::getIModel() const { @@ -50,14 +32,8 @@ Vector3 MD5ModelNode::getModelScale() return Vector3(1, 1, 1); // not supported } -void MD5ModelNode::lightsChanged() -{ - _lightList->setDirty(); -} - MD5ModelNode::~MD5ModelNode() { - GlobalRenderSystem().detachLitObject(*this); } void MD5ModelNode::setModel(const MD5ModelPtr& model) { @@ -96,29 +72,8 @@ bool MD5ModelNode::intersectsLight(const RendererLight& light) const return light.intersectsAABB(worldAABB()); } -void MD5ModelNode::insertLight(const RendererLight& light) { - const Matrix4& l2w = localToWorld(); - - _surfaceLightLists.resize(_model->size()); - - SurfaceLightLists::iterator j = _surfaceLightLists.begin(); - for (MD5Model::const_iterator i = _model->begin(); i != _model->end(); ++i) { - Surface_addLight(*i->surface, *j++, l2w, light); - } -} - -void MD5ModelNode::clearLights() { - for (SurfaceLightLists::iterator i = _surfaceLightLists.begin(); - i != _surfaceLightLists.end(); ++i) - { - i->clear(); - } -} - void MD5ModelNode::renderSolid(RenderableCollector& collector, const VolumeTest& volume) const { - _lightList->calculateIntersectingLights(); - assert(_renderEntity); render(collector, volume, localToWorld(), *_renderEntity); @@ -147,31 +102,21 @@ void MD5ModelNode::render(RenderableCollector& collector, const VolumeTest& volu return; } - SurfaceLightLists::const_iterator j = _surfaceLightLists.begin(); - // greebo: Iterate over all MD5 surfaces and render them - for (MD5Model::const_iterator i = _model->begin(); - i != _model->end(); - ++i, ++j) + for (auto i = _model->begin(); i != _model->end(); ++i) { assert(i->shader); // Get the Material to test the shader name against the filter system const MaterialPtr& surfaceShader = i->shader->getMaterial(); - if (surfaceShader->isVisible()) { - if (collector.supportsFullMaterials()) - { - assert(i->shader); // shader must be captured at this point - collector.addRenderable(*i->shader, *i->surface, localToWorld, - &(*j), &entity); - } - else - { - collector.addRenderable(*entity.getWireShader(), *i->surface, - localToWorld, nullptr, &entity); - } + assert(i->shader); // shader must be captured at this point + collector.addRenderable( + collector.supportsFullMaterials() ? *i->shader + : *entity.getWireShader(), + *i->surface, localToWorld, this, &entity + ); } } diff --git a/radiantcore/model/md5/MD5ModelNode.h b/radiantcore/model/md5/MD5ModelNode.h index 07d3d21f01..28e435a084 100644 --- a/radiantcore/model/md5/MD5ModelNode.h +++ b/radiantcore/model/md5/MD5ModelNode.h @@ -16,14 +16,8 @@ class MD5ModelNode : public SkinnedModel, public ITraceable { -private: MD5ModelPtr _model; - LightList* _lightList; - - typedef std::vector SurfaceLightLists; - SurfaceLightLists _surfaceLightLists; - // The name of this model's skin std::string _skin; @@ -37,8 +31,6 @@ class MD5ModelNode : bool hasModifiedScale() override; Vector3 getModelScale() override; - void lightsChanged(); - // returns the contained model void setModel(const MD5ModelPtr& model); const MD5ModelPtr& getModel() const; @@ -57,8 +49,6 @@ class MD5ModelNode : // LitObject implementation bool intersectsLight(const RendererLight& light) const override; - void insertLight(const RendererLight& light) override; - void clearLights() override; // Renderable implementation void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override; diff --git a/radiantcore/model/picomodel/PicoModelLoader.cpp b/radiantcore/model/picomodel/PicoModelLoader.cpp index 13428e0d36..48e31b101b 100644 --- a/radiantcore/model/picomodel/PicoModelLoader.cpp +++ b/radiantcore/model/picomodel/PicoModelLoader.cpp @@ -8,7 +8,7 @@ #include "os/path.h" -#include "PicoModelNode.h" +#include "StaticModelNode.h" #include "idatastream.h" #include "string/case_conv.h" @@ -57,13 +57,13 @@ scene::INodePtr PicoModelLoader::loadModel(const std::string& modelName) } // The cached model should be an PicoModel, otherwise we're in the wrong movie - RenderablePicoModelPtr picoModel = - std::dynamic_pointer_cast(model); + StaticModelPtr picoModel = + std::dynamic_pointer_cast(model); if (picoModel) { // Load was successful, construct a modelnode using this resource - return std::make_shared(picoModel); + return std::make_shared(picoModel); } else { @@ -105,8 +105,8 @@ IModelPtr PicoModelLoader::loadModelFromPath(const std::string& name) return IModelPtr(); } - RenderablePicoModelPtr modelObj( - new RenderablePicoModel(model, fExt) + StaticModelPtr modelObj( + new StaticModel(model, fExt) ); // Set the filename diff --git a/radiantcore/model/picomodel/PicoModelNode.cpp b/radiantcore/model/picomodel/PicoModelNode.cpp deleted file mode 100644 index 1ee9345bae..0000000000 --- a/radiantcore/model/picomodel/PicoModelNode.cpp +++ /dev/null @@ -1,209 +0,0 @@ -#include "PicoModelNode.h" - -#include "RenderablePicoSurface.h" -#include "ivolumetest.h" -#include "ishaders.h" -#include "iscenegraph.h" -#include "ifilter.h" -#include "imodelcache.h" -#include "math/Frustum.h" -#include "generic/callback.h" -#include - -namespace model { - -// greebo: Construct a new RenderablePicoModel instance, we re-use the surfaces only -PicoModelNode::PicoModelNode(const RenderablePicoModelPtr& picoModel) : - _picoModel(new RenderablePicoModel(*picoModel)), - _name(picoModel->getFilename()), - _lightList(GlobalRenderSystem().attachLitObject(*this)) -{ - Node::setTransformChangedCallback(std::bind(&PicoModelNode::lightsChanged, this)); - - // Update the skin - skinChanged(""); -} - -PicoModelNode::~PicoModelNode() { - GlobalRenderSystem().detachLitObject(*this); -} - -void PicoModelNode::onInsertIntoScene(scene::IMapRootNode& root) -{ - _picoModel->connectUndoSystem(root.getUndoChangeTracker()); - - Node::onInsertIntoScene(root); -} - -void PicoModelNode::onRemoveFromScene(scene::IMapRootNode& root) -{ - _picoModel->disconnectUndoSystem(root.getUndoChangeTracker()); - - Node::onRemoveFromScene(root); -} - -const IModel& PicoModelNode::getIModel() const -{ - return *_picoModel; -} - -IModel& PicoModelNode::getIModel() -{ - return *_picoModel; -} - -bool PicoModelNode::hasModifiedScale() -{ - return _picoModel->getScale() != Vector3(1, 1, 1); -} - -Vector3 PicoModelNode::getModelScale() -{ - return _picoModel->getScale(); -} - -const AABB& PicoModelNode::localAABB() const { - return _picoModel->localAABB(); -} - -// SelectionTestable implementation -void PicoModelNode::testSelect(Selector& selector, SelectionTest& test) { - _picoModel->testSelect(selector, test, localToWorld()); -} - -std::string PicoModelNode::name() const { - return _picoModel->getFilename(); -} - -scene::INode::Type PicoModelNode::getNodeType() const -{ - return Type::Model; -} - -const RenderablePicoModelPtr& PicoModelNode::getModel() const { - return _picoModel; -} - -void PicoModelNode::setModel(const RenderablePicoModelPtr& model) { - _picoModel = model; -} - -// LitObject test function -bool PicoModelNode::intersectsLight(const RendererLight& light) const -{ - return light.intersectsAABB(worldAABB()); -} - -// Add a light to this model instance -void PicoModelNode::insertLight(const RendererLight& light) -{ - // Calculate transform from the superclass - const Matrix4& l2w = localToWorld(); - - // If the light's AABB intersects the oriented AABB of this model instance, - // add the light to our light list - if (light.intersectsAABB(AABB::createFromOrientedAABB(_picoModel->localAABB(), l2w))) - { - _intersectingLights.addLight(light); - } -} - -// Clear all lights from this model instance -void PicoModelNode::clearLights() -{ - _intersectingLights.clear(); -} - -void PicoModelNode::renderSolid(RenderableCollector& collector, const VolumeTest& volume) const -{ - _lightList.calculateIntersectingLights(); - - assert(_renderEntity); - - // Test the model's intersection volume, if it intersects pass on the render call - const Matrix4& l2w = localToWorld(); - - if (volume.TestAABB(_picoModel->localAABB(), l2w) != VOLUME_OUTSIDE) - { - // Submit the model's geometry - _picoModel->renderSolid(collector, l2w, *_renderEntity, - _intersectingLights); - } -} - -void PicoModelNode::renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const -{ - assert(_renderEntity); - - // Test the model's intersection volume, if it intersects pass on the render call - const Matrix4& l2w = localToWorld(); - - if (volume.TestAABB(_picoModel->localAABB(), l2w) != VOLUME_OUTSIDE) - { - // Submit the model's geometry - _picoModel->renderWireframe(collector, l2w, *_renderEntity); - } -} - -void PicoModelNode::setRenderSystem(const RenderSystemPtr& renderSystem) -{ - Node::setRenderSystem(renderSystem); - - _picoModel->setRenderSystem(renderSystem); -} - -// Traceable implementation -bool PicoModelNode::getIntersection(const Ray& ray, Vector3& intersection) -{ - return _picoModel->getIntersection(ray, intersection, localToWorld()); -} - -// Skin changed notify -void PicoModelNode::skinChanged(const std::string& newSkinName) -{ - // The new skin name is stored locally - _skin = newSkinName; - - // greebo: Acquire the ModelSkin reference from the SkinCache - // Note: This always returns a valid reference - ModelSkin& skin = GlobalModelSkinCache().capture(_skin); - _picoModel->applySkin(skin); - - // Refresh the scene (TODO: get rid of that) - GlobalSceneGraph().sceneChanged(); -} - -// Returns the name of the currently active skin -std::string PicoModelNode::getSkin() const -{ - return _skin; -} - -void PicoModelNode::_onTransformationChanged() -{ - // Always revert to our original state before evaluating - if (getTransformationType() & TransformationType::Scale) - { - _picoModel->revertScale(); - _picoModel->evaluateScale(getScale()); - } - else if (getTransformationType() == TransformationType::NoTransform) - { - // Transformation has been changed but no transform mode is set, - // so the reason we got here is a cancelTransform() call, revert everything - _picoModel->revertScale(); - _picoModel->evaluateScale(Vector3(1,1,1)); - } -} - -void PicoModelNode::_applyTransformation() -{ - if (getTransformationType() & TransformationType::Scale) - { - _picoModel->revertScale(); - _picoModel->evaluateScale(getScale()); - _picoModel->freezeScale(); - } -} - -} // namespace model diff --git a/radiantcore/model/picomodel/RenderablePicoModel.cpp b/radiantcore/model/picomodel/StaticModel.cpp similarity index 77% rename from radiantcore/model/picomodel/RenderablePicoModel.cpp rename to radiantcore/model/picomodel/StaticModel.cpp index b3c74ddcc5..5f11818cd9 100644 --- a/radiantcore/model/picomodel/RenderablePicoModel.cpp +++ b/radiantcore/model/picomodel/StaticModel.cpp @@ -1,5 +1,5 @@ -#include "RenderablePicoModel.h" -#include "RenderablePicoSurface.h" +#include "StaticModel.h" +#include "StaticModelSurface.h" #include "ivolumetest.h" #include "iselectiontest.h" @@ -16,7 +16,7 @@ namespace model { // Constructor -RenderablePicoModel::RenderablePicoModel(picoModel_t* mod, const std::string& fExt) : +StaticModel::StaticModel(picoModel_t* mod, const std::string& fExt) : _scaleTransformed(1,1,1), _scale(1,1,1), _undoStateSaver(nullptr), @@ -25,7 +25,7 @@ RenderablePicoModel::RenderablePicoModel(picoModel_t* mod, const std::string& fE // Get the number of surfaces to create int nSurf = PicoGetModelNumSurfaces(mod); - // Create a RenderablePicoSurface for each surface in the structure + // Create a StaticModelSurface for each surface in the structure for (int n = 0; n < nSurf; ++n) { // Retrieve the surface, discarding it if it is null or non-triangulated (?) @@ -37,8 +37,8 @@ RenderablePicoModel::RenderablePicoModel(picoModel_t* mod, const std::string& fE // Fix the normals of the surface (?) PicoFixSurfaceNormals(surf); - // Create the RenderablePicoSurface object and add it to the vector - RenderablePicoSurfacePtr rSurf(new RenderablePicoSurface(surf, fExt)); + // Create the StaticModelSurface object and add it to the vector + StaticModelSurfacePtr rSurf(new StaticModelSurface(surf, fExt)); _surfVec.push_back(Surface(rSurf)); @@ -47,7 +47,7 @@ RenderablePicoModel::RenderablePicoModel(picoModel_t* mod, const std::string& fE } } -RenderablePicoModel::RenderablePicoModel(const RenderablePicoModel& other) : +StaticModel::StaticModel(const StaticModel& other) : _surfVec(other._surfVec.size()), _scaleTransformed(other._scaleTransformed), _scale(other._scale), // use scale of other model @@ -61,13 +61,13 @@ RenderablePicoModel::RenderablePicoModel(const RenderablePicoModel& other) : for (std::size_t i = 0; i < other._surfVec.size(); ++i) { // Copy-construct the other surface, inheriting any applied scale - _surfVec[i].surface = std::make_shared(*(other._surfVec[i].surface)); + _surfVec[i].surface = std::make_shared(*(other._surfVec[i].surface)); _surfVec[i].originalSurface = other._surfVec[i].originalSurface; _surfVec[i].surface->setActiveMaterial(_surfVec[i].surface->getDefaultMaterial()); } } -void RenderablePicoModel::connectUndoSystem(IMapFileChangeTracker& changeTracker) +void StaticModel::connectUndoSystem(IMapFileChangeTracker& changeTracker) { assert(_undoStateSaver == nullptr); @@ -77,7 +77,7 @@ void RenderablePicoModel::connectUndoSystem(IMapFileChangeTracker& changeTracker _undoStateSaver = GlobalUndoSystem().getStateSaver(*this, changeTracker); } -void RenderablePicoModel::disconnectUndoSystem(IMapFileChangeTracker& changeTracker) +void StaticModel::disconnectUndoSystem(IMapFileChangeTracker& changeTracker) { assert(_undoStateSaver != nullptr); @@ -86,7 +86,7 @@ void RenderablePicoModel::disconnectUndoSystem(IMapFileChangeTracker& changeTrac GlobalUndoSystem().releaseStateSaver(*this); } -void RenderablePicoModel::foreachVisibleSurface(const std::function& func) const +void StaticModel::foreachVisibleSurface(const std::function& func) const { for (const Surface& surface : _surfVec) { @@ -102,22 +102,23 @@ void RenderablePicoModel::foreachVisibleSurface(const std::function= 0 && surfaceNum < _surfVec.size()); return *(_surfVec[surfaceNum].surface); } // Apply the given skin to this model -void RenderablePicoModel::applySkin(const ModelSkin& skin) +void StaticModel::applySkin(const ModelSkin& skin) { // Apply the skin to each surface, then try to capture shaders for (SurfaceList::iterator i = _surfVec.begin(); @@ -250,7 +251,7 @@ void RenderablePicoModel::applySkin(const ModelSkin& skin) updateMaterialList(); } -void RenderablePicoModel::captureShaders() +void StaticModel::captureShaders() { RenderSystemPtr renderSystem = _renderSystem.lock(); @@ -269,7 +270,7 @@ void RenderablePicoModel::captureShaders() } // Update the list of active materials -void RenderablePicoModel::updateMaterialList() const +void StaticModel::updateMaterialList() const { _materialList.clear(); @@ -280,7 +281,7 @@ void RenderablePicoModel::updateMaterialList() const } // Return the list of active skins for this model -const StringList& RenderablePicoModel::getActiveMaterials() const +const StringList& StaticModel::getActiveMaterials() const { // If the material list is empty, populate it if (_materialList.empty()) @@ -293,7 +294,7 @@ const StringList& RenderablePicoModel::getActiveMaterials() const } // Perform selection test -void RenderablePicoModel::testSelect(Selector& selector, +void StaticModel::testSelect(Selector& selector, SelectionTest& test, const Matrix4& localToWorld) { @@ -313,7 +314,7 @@ void RenderablePicoModel::testSelect(Selector& selector, } } -bool RenderablePicoModel::getIntersection(const Ray& ray, Vector3& intersection, const Matrix4& localToWorld) +bool StaticModel::getIntersection(const Ray& ray, Vector3& intersection, const Matrix4& localToWorld) { Vector3 bestIntersection = ray.origin; @@ -346,34 +347,34 @@ bool RenderablePicoModel::getIntersection(const Ray& ray, Vector3& intersection, } } -const RenderablePicoModel::SurfaceList& RenderablePicoModel::getSurfaces() const +const StaticModel::SurfaceList& StaticModel::getSurfaces() const { return _surfVec; } -std::string RenderablePicoModel::getModelPath() const +std::string StaticModel::getModelPath() const { return _modelPath; } -void RenderablePicoModel::setModelPath(const std::string& modelPath) +void StaticModel::setModelPath(const std::string& modelPath) { _modelPath = modelPath; } -void RenderablePicoModel::revertScale() +void StaticModel::revertScale() { _scaleTransformed = _scale; } -void RenderablePicoModel::evaluateScale(const Vector3& scale) +void StaticModel::evaluateScale(const Vector3& scale) { _scaleTransformed *= scale; applyScaleToSurfaces(); } -void RenderablePicoModel::applyScaleToSurfaces() +void StaticModel::applyScaleToSurfaces() { _localAABB = AABB(); @@ -385,7 +386,7 @@ void RenderablePicoModel::applyScaleToSurfaces() if (surf.surface == surf.originalSurface) { // Copy-construct the surface - surf.surface = std::make_shared(*surf.originalSurface); + surf.surface = std::make_shared(*surf.originalSurface); } // Apply the scale, on top of the original surface, this should save us from @@ -398,7 +399,7 @@ void RenderablePicoModel::applyScaleToSurfaces() } // Freeze transform, move the applied scale to the original model -void RenderablePicoModel::freezeScale() +void StaticModel::freezeScale() { undoSave(); @@ -406,7 +407,7 @@ void RenderablePicoModel::freezeScale() _scale = _scaleTransformed; } -void RenderablePicoModel::undoSave() +void StaticModel::undoSave() { if (_undoStateSaver != nullptr) { @@ -414,12 +415,12 @@ void RenderablePicoModel::undoSave() } } -IUndoMementoPtr RenderablePicoModel::exportState() const +IUndoMementoPtr StaticModel::exportState() const { return IUndoMementoPtr(new undo::BasicUndoMemento(_scale)); } -void RenderablePicoModel::importState(const IUndoMementoPtr& state) +void StaticModel::importState(const IUndoMementoPtr& state) { undoSave(); @@ -429,7 +430,7 @@ void RenderablePicoModel::importState(const IUndoMementoPtr& state) applyScaleToSurfaces(); } -const Vector3& RenderablePicoModel::getScale() const +const Vector3& StaticModel::getScale() const { return _scale; } diff --git a/radiantcore/model/picomodel/RenderablePicoModel.h b/radiantcore/model/picomodel/StaticModel.h similarity index 84% rename from radiantcore/model/picomodel/RenderablePicoModel.h rename to radiantcore/model/picomodel/StaticModel.h index 03a3524d5a..631555cfbb 100644 --- a/radiantcore/model/picomodel/RenderablePicoModel.h +++ b/radiantcore/model/picomodel/StaticModel.h @@ -14,8 +14,8 @@ class Ray; /* FORWARD DECLS */ namespace model { - class RenderablePicoSurface; - typedef std::shared_ptr RenderablePicoSurfacePtr; + class StaticModelSurface; + typedef std::shared_ptr StaticModelSurfacePtr; } class RenderableCollector; class RendererLight; @@ -26,13 +26,15 @@ namespace model { /** - * Renderable class containing a model loaded via the picomodel library. A - * RenderablePicoModel is made up of one or more RenderablePicoSurface objects, - * each of which contains a number of polygons with the same texture. Rendering - * a RenderablePicoModel involves rendering all of its surfaces, submitting - * their geometry via OpenGL calls. + * \brief + * IModel implementing class representing a static model + * + * A StaticModel is made up of one or more StaticModelSurface + * objects, each of which contains a number of polygons with the same texture. + * Rendering a StaticModel involves rendering all of its surfaces, + * submitting their geometry via OpenGL calls. */ -class RenderablePicoModel : +class StaticModel : public IModel, public IUndoable { @@ -42,10 +44,10 @@ class RenderablePicoModel : struct Surface { // The (shared) surface object - RenderablePicoSurfacePtr surface; + StaticModelSurfacePtr surface; // The (unmodified) surface object - RenderablePicoSurfacePtr originalSurface; + StaticModelSurfacePtr originalSurface; // The shader this surface is using ShaderPtr shader; @@ -54,7 +56,7 @@ class RenderablePicoModel : {} // Constructor - Surface(const RenderablePicoSurfacePtr& surface_) : + Surface(const StaticModelSurfacePtr& surface_) : surface(surface_), originalSurface(surface) {} @@ -113,21 +115,21 @@ class RenderablePicoModel : * loaded from picomodel, and a string filename extension to allow the * correct handling of material paths (which differs between ASE and LWO) */ - RenderablePicoModel(picoModel_t* mod, const std::string& fExt); + StaticModel(picoModel_t* mod, const std::string& fExt); /** * Copy constructor: re-use the surfaces from the other model * but make it possible to assign custom skins to the surfaces. */ - RenderablePicoModel(const RenderablePicoModel& other); + StaticModel(const StaticModel& other); void connectUndoSystem(IMapFileChangeTracker& changeTracker); void disconnectUndoSystem(IMapFileChangeTracker& changeTracker); - // Delegated render methods called by PicoModelNode (not part of any + // Delegated render methods called by StaticModelNode (not part of any // interface) void renderSolid(RenderableCollector& rend, const Matrix4& localToWorld, - const IRenderEntity& entity, const LightSources& lights) const; + const IRenderEntity& entity, const LitObject& litObj) const; void renderWireframe(RenderableCollector& rend, const Matrix4& localToWorld, const IRenderEntity& entity) const; @@ -211,7 +213,7 @@ class RenderablePicoModel : bool getIntersection(const Ray& ray, Vector3& intersection, const Matrix4& localToWorld); /** - * Return the list of RenderablePicoSurface objects. + * Return the list of StaticModelSurface objects. */ const SurfaceList& getSurfaces() const; @@ -231,6 +233,6 @@ class RenderablePicoModel : // Returns the current base scale of this model const Vector3& getScale() const; }; -typedef std::shared_ptr RenderablePicoModelPtr; +typedef std::shared_ptr StaticModelPtr; } diff --git a/radiantcore/model/picomodel/StaticModelNode.cpp b/radiantcore/model/picomodel/StaticModelNode.cpp new file mode 100644 index 0000000000..0e5a46ed16 --- /dev/null +++ b/radiantcore/model/picomodel/StaticModelNode.cpp @@ -0,0 +1,183 @@ +#include "StaticModelNode.h" + +#include "StaticModelSurface.h" +#include "ivolumetest.h" +#include "ishaders.h" +#include "iscenegraph.h" +#include "ifilter.h" +#include "imodelcache.h" +#include "math/Frustum.h" +#include "generic/callback.h" +#include + +namespace model { + +// greebo: Construct a new StaticModel instance, we re-use the surfaces only +StaticModelNode::StaticModelNode(const StaticModelPtr& picoModel) : + _model(new StaticModel(*picoModel)), + _name(picoModel->getFilename()) +{ + // Update the skin + skinChanged(""); +} + +StaticModelNode::~StaticModelNode() +{} + +void StaticModelNode::onInsertIntoScene(scene::IMapRootNode& root) +{ + _model->connectUndoSystem(root.getUndoChangeTracker()); + + Node::onInsertIntoScene(root); +} + +void StaticModelNode::onRemoveFromScene(scene::IMapRootNode& root) +{ + _model->disconnectUndoSystem(root.getUndoChangeTracker()); + + Node::onRemoveFromScene(root); +} + +const IModel& StaticModelNode::getIModel() const +{ + return *_model; +} + +IModel& StaticModelNode::getIModel() +{ + return *_model; +} + +bool StaticModelNode::hasModifiedScale() +{ + return _model->getScale() != Vector3(1, 1, 1); +} + +Vector3 StaticModelNode::getModelScale() +{ + return _model->getScale(); +} + +const AABB& StaticModelNode::localAABB() const { + return _model->localAABB(); +} + +// SelectionTestable implementation +void StaticModelNode::testSelect(Selector& selector, SelectionTest& test) { + _model->testSelect(selector, test, localToWorld()); +} + +std::string StaticModelNode::name() const { + return _model->getFilename(); +} + +scene::INode::Type StaticModelNode::getNodeType() const +{ + return Type::Model; +} + +const StaticModelPtr& StaticModelNode::getModel() const { + return _model; +} + +void StaticModelNode::setModel(const StaticModelPtr& model) { + _model = model; +} + +// LitObject test function +bool StaticModelNode::intersectsLight(const RendererLight& light) const +{ + return light.intersectsAABB(worldAABB()); +} + +void StaticModelNode::renderSolid(RenderableCollector& collector, const VolumeTest& volume) const +{ + assert(_renderEntity); + + const Matrix4& l2w = localToWorld(); + + // Test the model's intersection volume, if it intersects pass on the + // render call + if (volume.TestAABB(_model->localAABB(), l2w) != VOLUME_OUTSIDE) + { + // Submit the model's geometry + _model->renderSolid(collector, l2w, *_renderEntity, *this); + } +} + +void StaticModelNode::renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const +{ + assert(_renderEntity); + + // Test the model's intersection volume, if it intersects pass on the render call + const Matrix4& l2w = localToWorld(); + + if (volume.TestAABB(_model->localAABB(), l2w) != VOLUME_OUTSIDE) + { + // Submit the model's geometry + _model->renderWireframe(collector, l2w, *_renderEntity); + } +} + +void StaticModelNode::setRenderSystem(const RenderSystemPtr& renderSystem) +{ + Node::setRenderSystem(renderSystem); + + _model->setRenderSystem(renderSystem); +} + +// Traceable implementation +bool StaticModelNode::getIntersection(const Ray& ray, Vector3& intersection) +{ + return _model->getIntersection(ray, intersection, localToWorld()); +} + +// Skin changed notify +void StaticModelNode::skinChanged(const std::string& newSkinName) +{ + // The new skin name is stored locally + _skin = newSkinName; + + // greebo: Acquire the ModelSkin reference from the SkinCache + // Note: This always returns a valid reference + ModelSkin& skin = GlobalModelSkinCache().capture(_skin); + _model->applySkin(skin); + + // Refresh the scene (TODO: get rid of that) + GlobalSceneGraph().sceneChanged(); +} + +// Returns the name of the currently active skin +std::string StaticModelNode::getSkin() const +{ + return _skin; +} + +void StaticModelNode::_onTransformationChanged() +{ + // Always revert to our original state before evaluating + if (getTransformationType() & TransformationType::Scale) + { + _model->revertScale(); + _model->evaluateScale(getScale()); + } + else if (getTransformationType() == TransformationType::NoTransform) + { + // Transformation has been changed but no transform mode is set, + // so the reason we got here is a cancelTransform() call, revert everything + _model->revertScale(); + _model->evaluateScale(Vector3(1,1,1)); + } +} + +void StaticModelNode::_applyTransformation() +{ + if (getTransformationType() & TransformationType::Scale) + { + _model->revertScale(); + _model->evaluateScale(getScale()); + _model->freezeScale(); + } +} + +} // namespace model diff --git a/radiantcore/model/picomodel/PicoModelNode.h b/radiantcore/model/picomodel/StaticModelNode.h similarity index 69% rename from radiantcore/model/picomodel/PicoModelNode.h rename to radiantcore/model/picomodel/StaticModelNode.h index fd5d679681..19e0a81fab 100644 --- a/radiantcore/model/picomodel/PicoModelNode.h +++ b/radiantcore/model/picomodel/StaticModelNode.h @@ -8,12 +8,16 @@ #include "irenderable.h" #include "pivot.h" #include "render/VectorLightList.h" -#include "RenderablePicoModel.h" +#include "StaticModel.h" #include "scene/Node.h" namespace model { -class PicoModelNode : +/** + * \brief + * Scenegraph node representing a static model + */ +class StaticModelNode : public scene::Node, public ModelNode, public SelectionTestable, @@ -22,28 +26,22 @@ class PicoModelNode : public ITraceable, public Transformable { -private: // The actual model - RenderablePicoModelPtr _picoModel; + StaticModelPtr _model; std::string _name; - // Vector of RendererLight references which illuminate this instance, set - // with addLight() and clearLights() - render::lib::VectorLightList _intersectingLights; - - // The light list from the shader cache when we attach - LightList& _lightList; - // The name of this model's skin std::string _skin; public: - /** Construct a PicoModelNode with a reference to the loaded picoModel. + typedef std::shared_ptr Ptr; + + /** Construct a StaticModelNode with a reference to the loaded picoModel. */ - PicoModelNode(const RenderablePicoModelPtr& picoModel); + StaticModelNode(const StaticModelPtr& picoModel); - virtual ~PicoModelNode(); + virtual ~StaticModelNode(); virtual void onInsertIntoScene(scene::IMapRootNode& root) override; virtual void onRemoveFromScene(scene::IMapRootNode& root) override; @@ -63,27 +61,17 @@ class PicoModelNode : // Bounded implementation virtual const AABB& localAABB() const override; - // Lights changed function - void lightsChanged() - { - _lightList.setDirty(); - } - // SelectionTestable implementation void testSelect(Selector& selector, SelectionTest& test) override; virtual std::string name() const override; Type getNodeType() const override; - const RenderablePicoModelPtr& getModel() const; - void setModel(const RenderablePicoModelPtr& model); + const StaticModelPtr& getModel() const; + void setModel(const StaticModelPtr& model); // LitObject test function bool intersectsLight(const RendererLight& light) const override; - // Add a light to this model instance - void insertLight(const RendererLight& light) override; - // Clear all lights from this model instance - void clearLights() override; // Renderable implementation void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override; @@ -102,6 +90,5 @@ class PicoModelNode : virtual void _onTransformationChanged() override; virtual void _applyTransformation() override; }; -typedef std::shared_ptr PicoModelNodePtr; } // namespace model diff --git a/radiantcore/model/picomodel/RenderablePicoSurface.cpp b/radiantcore/model/picomodel/StaticModelSurface.cpp similarity index 85% rename from radiantcore/model/picomodel/RenderablePicoSurface.cpp rename to radiantcore/model/picomodel/StaticModelSurface.cpp index 63565bc39c..3bac2ac2ba 100644 --- a/radiantcore/model/picomodel/RenderablePicoSurface.cpp +++ b/radiantcore/model/picomodel/StaticModelSurface.cpp @@ -1,4 +1,4 @@ -#include "RenderablePicoSurface.h" +#include "StaticModelSurface.h" #include "itextstream.h" #include "modelskin.h" @@ -12,7 +12,7 @@ namespace model { // Constructor. Copy the provided picoSurface_t structure into this object -RenderablePicoSurface::RenderablePicoSurface(picoSurface_t* surf, +StaticModelSurface::StaticModelSurface(picoSurface_t* surf, const std::string& fExt) : _defaultMaterial(""), _dlRegular(0), @@ -94,7 +94,7 @@ RenderablePicoSurface::RenderablePicoSurface(picoSurface_t* surf, createDisplayLists(); } -RenderablePicoSurface::RenderablePicoSurface(const RenderablePicoSurface& other) : +StaticModelSurface::StaticModelSurface(const StaticModelSurface& other) : _defaultMaterial(other._defaultMaterial), _vertices(other._vertices), _indices(other._indices), @@ -107,7 +107,7 @@ RenderablePicoSurface::RenderablePicoSurface(const RenderablePicoSurface& other) createDisplayLists(); } -std::string RenderablePicoSurface::cleanupShaderName(const std::string& inName) +std::string StaticModelSurface::cleanupShaderName(const std::string& inName) { const std::string baseFolder = "base"; //FIXME: should be from game.xml std::size_t basePos; @@ -150,7 +150,7 @@ std::string RenderablePicoSurface::cleanupShaderName(const std::string& inName) } // Destructor. Release the GL display lists. -RenderablePicoSurface::~RenderablePicoSurface() +StaticModelSurface::~StaticModelSurface() { glDeleteLists(_dlRegular, 1); glDeleteLists(_dlProgramNoVCol, 1); @@ -158,7 +158,7 @@ RenderablePicoSurface::~RenderablePicoSurface() } // Convert byte pointers to colour vector -Vector3 RenderablePicoSurface::getColourVector(unsigned char* array) { +Vector3 StaticModelSurface::getColourVector(unsigned char* array) { if (array) { return Vector3(array[0] / 255.0f, array[1] / 255.0f, array[2] / 255.0f); } @@ -168,7 +168,7 @@ Vector3 RenderablePicoSurface::getColourVector(unsigned char* array) { } // Tangent calculation -void RenderablePicoSurface::calculateTangents() { +void StaticModelSurface::calculateTangents() { // Calculate the tangents and bitangents using the indices into the vertex // array. @@ -195,7 +195,7 @@ void RenderablePicoSurface::calculateTangents() { } // Back-end render function -void RenderablePicoSurface::render(const RenderInfo& info) const +void StaticModelSurface::render(const RenderInfo& info) const { // Invoke appropriate display list if (info.checkFlag(RENDER_PROGRAM)) @@ -216,7 +216,7 @@ void RenderablePicoSurface::render(const RenderInfo& info) const } // Construct a list for GLProgram mode, either with or without vertex colour -GLuint RenderablePicoSurface::compileProgramList(bool includeColour) +GLuint StaticModelSurface::compileProgramList(bool includeColour) { GLuint list = glGenLists(1); assert(list != 0); // check if we run out of display lists @@ -256,7 +256,7 @@ GLuint RenderablePicoSurface::compileProgramList(bool includeColour) } // Construct the two display lists -void RenderablePicoSurface::createDisplayLists() +void StaticModelSurface::createDisplayLists() { // Generate the lists for lighting mode _dlProgramNoVCol = compileProgramList(false); @@ -286,7 +286,7 @@ void RenderablePicoSurface::createDisplayLists() } // Perform selection test for this surface -void RenderablePicoSurface::testSelect(Selector& selector, +void StaticModelSurface::testSelect(Selector& selector, SelectionTest& test, const Matrix4& localToWorld) const { @@ -310,23 +310,23 @@ void RenderablePicoSurface::testSelect(Selector& selector, } } -int RenderablePicoSurface::getNumVertices() const +int StaticModelSurface::getNumVertices() const { return static_cast(_vertices.size()); } -int RenderablePicoSurface::getNumTriangles() const +int StaticModelSurface::getNumTriangles() const { return static_cast(_indices.size() / 3); // 3 indices per triangle } -const ArbitraryMeshVertex& RenderablePicoSurface::getVertex(int vertexIndex) const +const ArbitraryMeshVertex& StaticModelSurface::getVertex(int vertexIndex) const { assert(vertexIndex >= 0 && vertexIndex < static_cast(_vertices.size())); return _vertices[vertexIndex]; } -ModelPolygon RenderablePicoSurface::getPolygon(int polygonIndex) const +ModelPolygon StaticModelSurface::getPolygon(int polygonIndex) const { assert(polygonIndex >= 0 && polygonIndex*3 < static_cast(_indices.size())); @@ -343,37 +343,37 @@ ModelPolygon RenderablePicoSurface::getPolygon(int polygonIndex) const return poly; } -const std::vector& RenderablePicoSurface::getVertexArray() const +const std::vector& StaticModelSurface::getVertexArray() const { return _vertices; } -const std::vector& RenderablePicoSurface::getIndexArray() const +const std::vector& StaticModelSurface::getIndexArray() const { return _indices; } -const std::string& RenderablePicoSurface::getDefaultMaterial() const +const std::string& StaticModelSurface::getDefaultMaterial() const { return _defaultMaterial; } -void RenderablePicoSurface::setDefaultMaterial(const std::string& defaultMaterial) +void StaticModelSurface::setDefaultMaterial(const std::string& defaultMaterial) { _defaultMaterial = defaultMaterial; } -const std::string& RenderablePicoSurface::getActiveMaterial() const +const std::string& StaticModelSurface::getActiveMaterial() const { return _activeMaterial; } -void RenderablePicoSurface::setActiveMaterial(const std::string& activeMaterial) +void StaticModelSurface::setActiveMaterial(const std::string& activeMaterial) { _activeMaterial = activeMaterial; } -bool RenderablePicoSurface::getIntersection(const Ray& ray, Vector3& intersection, const Matrix4& localToWorld) +bool StaticModelSurface::getIntersection(const Ray& ray, Vector3& intersection, const Matrix4& localToWorld) { Vector3 bestIntersection = ray.origin; Vector3 triIntersection; @@ -414,11 +414,11 @@ bool RenderablePicoSurface::getIntersection(const Ray& ray, Vector3& intersectio } } -void RenderablePicoSurface::applyScale(const Vector3& scale, const RenderablePicoSurface& originalSurface) +void StaticModelSurface::applyScale(const Vector3& scale, const StaticModelSurface& originalSurface) { if (scale.x() == 0 || scale.y() == 0 || scale.z() == 0) { - rMessage() << "RenderablePicoSurface: Cannot apply scale with a zero diagonal element" << std::endl; + rMessage() << "StaticModelSurface: Cannot apply scale with a zero diagonal element" << std::endl; return; } diff --git a/radiantcore/model/picomodel/RenderablePicoSurface.h b/radiantcore/model/picomodel/StaticModelSurface.h similarity index 85% rename from radiantcore/model/picomodel/RenderablePicoSurface.h rename to radiantcore/model/picomodel/StaticModelSurface.h index 5c46f17cbf..5b46000037 100644 --- a/radiantcore/model/picomodel/RenderablePicoSurface.h +++ b/radiantcore/model/picomodel/StaticModelSurface.h @@ -19,12 +19,15 @@ class Ray; namespace model { -/* Renderable class containing a series of polygons textured with the same - * material. RenderablePicoSurface objects are composited into a RenderablePicoModel - * object to create a renderable static mesh. +/** + * \brief + * Renderable class containing a series of polygons textured with the same + * material. + * + * StaticModelSurface objects are composited into a StaticModel object to + * create a renderable static mesh. */ - -class RenderablePicoSurface : +class StaticModelSurface : public IIndexedModelSurface, public OpenGLRenderable { @@ -75,17 +78,17 @@ class RenderablePicoSurface : * Constructor. Accepts a picoSurface_t struct and the file extension to determine * how to assign materials. */ - RenderablePicoSurface(picoSurface_t* surf, const std::string& fExt); + StaticModelSurface(picoSurface_t* surf, const std::string& fExt); /** * Copy-constructor. */ - RenderablePicoSurface(const RenderablePicoSurface& other); + StaticModelSurface(const StaticModelSurface& other); /** * Destructor. */ - ~RenderablePicoSurface(); + ~StaticModelSurface(); /** * Render function from OpenGLRenderable @@ -125,8 +128,8 @@ class RenderablePicoSurface : // the exact point in the given Vector3, returns false if no intersection was found. bool getIntersection(const Ray& ray, Vector3& intersection, const Matrix4& localToWorld); - void applyScale(const Vector3& scale, const RenderablePicoSurface& originalSurface); + void applyScale(const Vector3& scale, const StaticModelSurface& originalSurface); }; -typedef std::shared_ptr RenderablePicoSurfacePtr; +typedef std::shared_ptr StaticModelSurfacePtr; } diff --git a/radiantcore/patch/Patch.cpp b/radiantcore/patch/Patch.cpp index f596d48df3..03fa6800f4 100644 --- a/radiantcore/patch/Patch.cpp +++ b/radiantcore/patch/Patch.cpp @@ -190,20 +190,6 @@ const AABB& Patch::localAABB() const return _localAABB; } -void Patch::renderSolid(RenderableCollector& collector, const VolumeTest& volume, - const Matrix4& localToWorld, const IRenderEntity& entity, const LightList& lights) const -{ - // Defer the tesselation calculation to the last minute - const_cast(*this).updateTesselation(); - - collector.addRenderable(*_shader.getGLShader(), _solidRenderable, - localToWorld, &lights, &entity); - -#if DEBUG_PATCH_NTB_VECTORS - _renderableVectors.render(collector, volume, localToWorld); -#endif -} - void Patch::renderWireframe(RenderableCollector& collector, const VolumeTest& volume, const Matrix4& localToWorld, const IRenderEntity& entity) const { // Defer the tesselation calculation to the last minute @@ -299,7 +285,6 @@ void Patch::transform(const Matrix4& matrix) void Patch::transformChanged() { _transformChanged = true; - _node.lightsChanged(); _tesselationChanged = true; } @@ -1096,7 +1081,6 @@ void Patch::updateAABB() _localAABB = aabb; _node.boundsChanged(); - _node.lightsChanged(); } } diff --git a/radiantcore/patch/Patch.h b/radiantcore/patch/Patch.h index 3539c6df37..24985a0056 100644 --- a/radiantcore/patch/Patch.h +++ b/radiantcore/patch/Patch.h @@ -36,6 +36,7 @@ class Patch : public Snappable, public IUndoable { + friend class PatchNode; PatchNode& _node; typedef std::set Observers; @@ -119,9 +120,7 @@ class Patch : // Return the interally stored AABB const AABB& localAABB() const override; - // Render functions: solid mode, wireframe mode and components - void renderSolid(RenderableCollector& collector, const VolumeTest& volume, - const Matrix4& localToWorld, const IRenderEntity& entity, const LightList& lights) const; + // Render functions: wireframe mode and components void renderWireframe(RenderableCollector& collector, const VolumeTest& volume, const Matrix4& localToWorld, const IRenderEntity& entity) const; diff --git a/radiantcore/patch/PatchNode.cpp b/radiantcore/patch/PatchNode.cpp index 5d16e38c07..e051f234aa 100644 --- a/radiantcore/patch/PatchNode.cpp +++ b/radiantcore/patch/PatchNode.cpp @@ -11,13 +11,10 @@ PatchNode::PatchNode(patch::PatchDefType type) : scene::SelectableNode(), m_dragPlanes(std::bind(&PatchNode::selectedChangedComponent, this, std::placeholders::_1)), m_render_selected(GL_POINTS), - m_lightList(&GlobalRenderSystem().attachLitObject(*this)), m_patch(*this), _untransformedOriginChanged(true) { m_patch.setFixedSubdivisions(type == patch::PatchDefType::Def3, Subdivisions(m_patch.getSubdivisions())); - - SelectableNode::setTransformChangedCallback(Callback(std::bind(&PatchNode::lightsChanged, this))); } // Copy Constructor @@ -35,17 +32,13 @@ PatchNode::PatchNode(const PatchNode& other) : Transformable(other), m_dragPlanes(std::bind(&PatchNode::selectedChangedComponent, this, std::placeholders::_1)), m_render_selected(GL_POINTS), - m_lightList(&GlobalRenderSystem().attachLitObject(*this)), m_patch(other.m_patch, *this), // create the patch out of the one _untransformedOriginChanged(true) { - SelectableNode::setTransformChangedCallback(Callback(std::bind(&PatchNode::lightsChanged, this))); } PatchNode::~PatchNode() -{ - GlobalRenderSystem().detachLitObject(*this); -} +{} scene::INode::Type PatchNode::getNodeType() const { @@ -84,11 +77,6 @@ IPatch& PatchNode::getPatch() { return m_patch; } -void PatchNode::lightsChanged() -{ - m_lightList->setDirty(); -} - // Snappable implementation void PatchNode::snapto(float snap) { m_patch.snapto(snap); @@ -282,12 +270,21 @@ void PatchNode::renderSolid(RenderableCollector& collector, const VolumeTest& vo // Don't render invisible shaders if (!isForcedVisible() && !m_patch.hasVisibleMaterial()) return; + // Defer the tesselation calculation to the last minute const_cast(m_patch).evaluateTransform(); + const_cast(m_patch).updateTesselation(); assert(_renderEntity); // patches rendered without parent - no way! - // Pass the call to the patch instance, it adds the renderable - m_patch.renderSolid(collector, volume, localToWorld(), *_renderEntity, *m_lightList); + // Render the patch itself + collector.addRenderable( + *m_patch._shader.getGLShader(), m_patch._solidRenderable, + localToWorld(), this, _renderEntity + ); + +#if DEBUG_PATCH_NTB_VECTORS + m_patch._renderableVectors.render(collector, volume, localToWorld()); +#endif // Render the selected components renderComponentsSelected(collector, volume); diff --git a/radiantcore/patch/PatchNode.h b/radiantcore/patch/PatchNode.h index 64c7cd4e4c..ed731a923a 100644 --- a/radiantcore/patch/PatchNode.h +++ b/radiantcore/patch/PatchNode.h @@ -33,8 +33,6 @@ class PatchNode : // An array of renderable points mutable RenderablePointVector m_render_selected; - LightList* m_lightList; - Patch m_patch; // An internal AABB variable to calculate the bounding box of the selected components (has to be mutable) @@ -62,8 +60,6 @@ class PatchNode : std::string name() const override; Type getNodeType() const override; - void lightsChanged(); - // Bounded implementation const AABB& localAABB() const override; diff --git a/radiantcore/rendersystem/LinearLightList.cpp b/radiantcore/rendersystem/LinearLightList.cpp deleted file mode 100644 index 77a0909cd5..0000000000 --- a/radiantcore/rendersystem/LinearLightList.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "LinearLightList.h" - -namespace render -{ - -void LinearLightList::calculateIntersectingLights() const -{ - // Get the renderer to tell us whether anything actually needs updating - _testDirtyFunc(); - - if (m_dirty) - { - m_dirty = false; - - _activeLights.clear(); - _litObject.clearLights(); - - // Determine which lights intersect object - for (RendererLight* light : _allLights) - { - if (_litObject.intersectsLight(*light)) - { - _activeLights.push_back(light); - _litObject.insertLight(*light); - } - } - } -} - -void LinearLightList::forEachLight(const RendererLightCallback& callback) const -{ - calculateIntersectingLights(); - - for (RendererLight* light : _activeLights) - { - callback(*light); - } -} - -void LinearLightList::setDirty() -{ - m_dirty = true; -} - -} diff --git a/radiantcore/rendersystem/LinearLightList.h b/radiantcore/rendersystem/LinearLightList.h deleted file mode 100644 index 0561f631e1..0000000000 --- a/radiantcore/rendersystem/LinearLightList.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "irender.h" -#include -#include - -namespace render -{ - -typedef std::set RendererLights; - -/** - * \brief - * Main renderer implementation of LightList interface. - * - * The LinearLightList is reponsible for associating a single lit object with - * all of the lights which currently light it. - */ -class LinearLightList : - public LightList -{ -public: - typedef std::function VoidCallback; - -private: - - // Target object - LitObject& _litObject; - - // Set of all available lights - RendererLights& _allLights; - - // Update callback - VoidCallback _testDirtyFunc; - - // List of lights which are intersecting our lit object - typedef std::list Lights; - mutable Lights _activeLights; - - // Dirty flag indicating recalculation needed - mutable bool m_dirty; - -public: - - /** - * \brief - * Construct and initialise with values. - * - * \param object - * The illuminatable object whose lit status we are tracking. - * - * \param lights - * Entire set of available light sources provided by the renderer. - * - * \param testFunc - * A callback function to request the renderer check if the light list - * needs to recalculate its intersections, and call setDirty() if necessary. - */ - LinearLightList(LitObject& object, - RendererLights& lights, - VoidCallback testFunc) - : _litObject(object), _allLights(lights), _testDirtyFunc(testFunc) - { - m_dirty = true; - } - - // LightList implementation - void calculateIntersectingLights() const; - void forEachLight(const RendererLightCallback& callback) const; - void setDirty(); -}; - -} // namespace render diff --git a/radiantcore/rendersystem/OpenGLRenderSystem.cpp b/radiantcore/rendersystem/OpenGLRenderSystem.cpp index f006456cdc..77077c7c61 100644 --- a/radiantcore/rendersystem/OpenGLRenderSystem.cpp +++ b/radiantcore/rendersystem/OpenGLRenderSystem.cpp @@ -45,7 +45,6 @@ OpenGLRenderSystem::OpenGLRenderSystem() : _glProgramFactory(std::make_shared()), _currentShaderProgram(SHADER_PROGRAM_NONE), _time(0), - m_lightsChanged(true), m_traverseRenderablesMutex(false) { // For the static default rendersystem, the MaterialManager is not existent yet, @@ -349,69 +348,6 @@ void OpenGLRenderSystem::setShaderProgramsAvailable(bool available) _shaderProgramsAvailable = available; } -LightList& OpenGLRenderSystem::attachLitObject(LitObject& object) -{ - return m_lightLists.insert( - LightLists::value_type( - &object, - LinearLightList( - object, - m_lights, - std::bind( - &OpenGLRenderSystem::propagateLightChangedFlagToAllLights, - this - ) - ) - ) - ).first->second; -} - -void OpenGLRenderSystem::detachLitObject(LitObject& object) -{ - m_lightLists.erase(&object); -} - -void OpenGLRenderSystem::litObjectChanged(LitObject& object) -{ - LightLists::iterator i = m_lightLists.find(&object); - assert(i != m_lightLists.end()); - - i->second.setDirty(); -} - -void OpenGLRenderSystem::attachLight(RendererLight& light) -{ - ASSERT_MESSAGE(m_lights.find(&light) == m_lights.end(), "light could not be attached"); - m_lights.insert(&light); - lightChanged(); -} - -void OpenGLRenderSystem::detachLight(RendererLight& light) -{ - ASSERT_MESSAGE(m_lights.find(&light) != m_lights.end(), "light could not be detached"); - m_lights.erase(&light); - lightChanged(); -} - -void OpenGLRenderSystem::lightChanged() -{ - m_lightsChanged = true; -} - -void OpenGLRenderSystem::propagateLightChangedFlagToAllLights() -{ - if (m_lightsChanged) - { - m_lightsChanged = false; - for (LightLists::iterator i = m_lightLists.begin(); - i != m_lightLists.end(); - ++i) - { - i->second.setDirty(); - } - } -} - void OpenGLRenderSystem::insertSortedState(const OpenGLStates::value_type& val) { _state_sorted.insert(val); } diff --git a/radiantcore/rendersystem/OpenGLRenderSystem.h b/radiantcore/rendersystem/OpenGLRenderSystem.h index 62183be075..acde5db919 100644 --- a/radiantcore/rendersystem/OpenGLRenderSystem.h +++ b/radiantcore/rendersystem/OpenGLRenderSystem.h @@ -7,7 +7,6 @@ #include "backend/OpenGLStateManager.h" #include "backend/OpenGLShader.h" #include "backend/OpenGLStateLess.h" -#include "LinearLightList.h" namespace render { @@ -23,7 +22,6 @@ class OpenGLRenderSystem : public RenderSystem, public OpenGLStateManager { -private: // Map of named Shader objects typedef std::map ShaderMap; ShaderMap _shaders; @@ -45,12 +43,6 @@ class OpenGLRenderSystem // Render time std::size_t _time; - // Lights - RendererLights m_lights; - bool m_lightsChanged; - typedef std::map LightLists; - LightLists m_lightLists; - sigc::signal _sigExtensionsInitialised; sigc::connection _materialDefsLoaded; @@ -58,9 +50,6 @@ class OpenGLRenderSystem sigc::connection _sharedContextCreated; sigc::connection _sharedContextDestroyed; -private: - void propagateLightChangedFlagToAllLights(); - public: /** @@ -94,15 +83,6 @@ class OpenGLRenderSystem bool shaderProgramsAvailable() const override; void setShaderProgramsAvailable(bool available) override; - LightList& attachLitObject(LitObject& cullable) override; - void detachLitObject(LitObject& cullable) override; - void litObjectChanged(LitObject& cullable) override; - - // Attach and detach light sources - void attachLight(RendererLight& light) override; - void detachLight(RendererLight& light) override; - void lightChanged() override; - typedef std::set Renderables; Renderables m_renderables; mutable bool m_traverseRenderablesMutex;