123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- /**************************************************************************/
- /* visual_server_light_culler.h */
- /**************************************************************************/
- /* This file is part of: */
- /* GODOT ENGINE */
- /* https://godotengine.org */
- /**************************************************************************/
- /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
- /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
- /* */
- /* Permission is hereby granted, free of charge, to any person obtaining */
- /* a copy of this software and associated documentation files (the */
- /* "Software"), to deal in the Software without restriction, including */
- /* without limitation the rights to use, copy, modify, merge, publish, */
- /* distribute, sublicense, and/or sell copies of the Software, and to */
- /* permit persons to whom the Software is furnished to do so, subject to */
- /* the following conditions: */
- /* */
- /* The above copyright notice and this permission notice shall be */
- /* included in all copies or substantial portions of the Software. */
- /* */
- /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
- /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
- /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
- /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
- /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
- /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
- /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
- /**************************************************************************/
- #ifndef VISUAL_SERVER_LIGHT_CULLER_H
- #define VISUAL_SERVER_LIGHT_CULLER_H
- #include "core/math/plane.h"
- #include "core/math/vector3.h"
- #include "visual_server_scene.h"
- struct CameraMatrix;
- class Transform;
- // For testing performance improvements from the LightCuller:
- // Uncomment LIGHT_CULLER_DEBUG_FLASH and it will turn the culler
- // on and off every LIGHT_CULLER_DEBUG_FLASH_FREQUENCY camera prepares.
- // Uncomment LIGHT_CULLER_DEBUG_LOGGING to get periodic print of the number of casters culled before / after.
- // #define LIGHT_CULLER_DEBUG_LOGGING
- // #define LIGHT_CULLER_DEBUG_FLASH
- #define LIGHT_CULLER_DEBUG_FLASH_FREQUENCY 1024
- ////////////////////////////////////////////////////////////////////////////////////////////////
- // The code to generate the lookup table is included but commented out.
- // This may be useful for debugging / regenerating the LUT in the future,
- // especially if the order of planes changes.
- // When this define is set, the generated lookup table will be printed to debug output.
- // The generated lookup table can be copy pasted
- // straight to LUT_entry_sizes and LUT_entries.
- // See the referenced article for explanation.
- // #define VISUAL_SERVER_LIGHT_CULLER_CALCULATE_LUT
- ////////////////////////////////////////////////////////////////////////////////////////////////
- // This define will be set automatically depending on earlier defines, you can leave this as is.
- #if defined(LIGHT_CULLER_DEBUG_LOGGING) || defined(VISUAL_SERVER_LIGHT_CULLER_CALCULATE_LUT)
- #define VISUAL_SERVER_LIGHT_CULLER_DEBUG_STRINGS
- #endif
- // Culls shadow casters that can't cast shadows into the camera frustum.
- class VisualServerLightCuller {
- public:
- VisualServerLightCuller();
- private:
- class LightSource {
- public:
- enum SourceType {
- ST_UNKNOWN,
- ST_DIRECTIONAL,
- ST_SPOTLIGHT,
- ST_OMNI,
- };
- LightSource() {
- type = ST_UNKNOWN;
- angle = 0.0f;
- range = FLT_MAX;
- }
- // All in world space, culling done in world space.
- Vector3 pos;
- Vector3 dir;
- SourceType type;
- float angle; // For spotlight.
- float range;
- };
- // Same order as godot.
- enum PlaneOrder {
- PLANE_NEAR,
- PLANE_FAR,
- PLANE_LEFT,
- PLANE_TOP,
- PLANE_RIGHT,
- PLANE_BOTTOM,
- PLANE_TOTAL,
- };
- // Same order as godot.
- enum PointOrder {
- PT_FAR_LEFT_TOP,
- PT_FAR_LEFT_BOTTOM,
- PT_FAR_RIGHT_TOP,
- PT_FAR_RIGHT_BOTTOM,
- PT_NEAR_LEFT_TOP,
- PT_NEAR_LEFT_BOTTOM,
- PT_NEAR_RIGHT_TOP,
- PT_NEAR_RIGHT_BOTTOM,
- };
- // 6 bits, 6 planes.
- enum {
- NUM_CAM_PLANES = 6,
- NUM_CAM_POINTS = 8,
- MAX_CULL_PLANES = 17,
- LUT_SIZE = 64,
- };
- public:
- // Before each pass with a different camera, you must call this so the culler can pre-create
- // the camera frustum planes and corner points in world space which are used for the culling.
- bool prepare_camera(const Transform &p_cam_transform, const CameraMatrix &p_cam_matrix);
- // Returns false if the entire light is culled (i.e. there is no intersection between the light and the view frustum).
- bool prepare_light(const VisualServerScene::Instance &p_instance);
- // Cull according to the planes that were setup in the previous call to prepare_light.
- int cull(int p_count, VisualServerScene::Instance **p_result_array);
- // Can turn on and off from the engine if desired.
- void set_caster_culling_active(bool p_active) { data.caster_culling_active = p_active; }
- void set_light_culling_active(bool p_active) { data.light_culling_active = p_active; }
- private:
- // Internal version uses LightSource.
- bool _add_light_camera_planes(const LightSource &p_light_source);
- // Directional light gives parallel culling planes (as opposed to point lights).
- bool add_light_camera_planes_directional(const LightSource &p_light_source);
- // Avoid adding extra culling planes derived from near colinear triangles.
- // The normals derived from these will be inaccurate, and can lead to false
- // culling of objects that should be within the light volume.
- bool _is_colinear_tri(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c) const {
- // Lengths of sides a, b and c.
- float la = (p_b - p_a).length();
- float lb = (p_c - p_b).length();
- float lc = (p_c - p_a).length();
- // Get longest side into lc.
- if (lb < la) {
- SWAP(la, lb);
- }
- if (lc < lb) {
- SWAP(lb, lc);
- }
- // Prevent divide by zero.
- if (lc > 0.001f) {
- // If the summed length of the smaller two
- // sides is close to the length of the longest side,
- // the points are colinear, and the triangle is near degenerate.
- float ld = ((la + lb) - lc) / lc;
- // ld will be close to zero for colinear tris.
- return ld < 0.001f;
- }
- // Don't create planes from tiny triangles,
- // they won't be accurate.
- return true;
- }
- // Is the light culler active? maybe not in the editor...
- bool is_caster_culling_active() const { return data.caster_culling_active; }
- bool is_light_culling_active() const { return data.light_culling_active; }
- // Do we want to log some debug output?
- bool is_logging() const { return data.debug_count == 0; }
- // Culling planes.
- void add_cull_plane(const Plane &p);
- struct Data {
- // Camera frustum planes (world space) - order ePlane.
- Vector<Plane> frustum_planes;
- // Camera frustum corners (world space) - order ePoint.
- Vector3 frustum_points[NUM_CAM_POINTS];
- // We are storing cull planes in a ye olde style array to prevent needless allocations.
- Plane cull_planes[MAX_CULL_PLANES];
- int num_cull_planes = 0;
- // The whole light can be out of range of the view frustum, in which case all casters should be culled.
- bool out_of_range = false;
- #ifdef VISUAL_SERVER_LIGHT_CULLER_DEBUG_STRINGS
- static String plane_bitfield_to_string(unsigned int BF);
- // Names of the plane and point enums, useful for debugging.
- static const char *string_planes[];
- static const char *string_points[];
- #endif
- // Precalculated look up table.
- static uint8_t LUT_entry_sizes[LUT_SIZE];
- static uint8_t LUT_entries[LUT_SIZE][8];
- bool caster_culling_active = true;
- bool light_culling_active = true;
- // Light culling is a basic on / off switch.
- // Caster culling only works if light culling is also on.
- bool is_active() const { return light_culling_active; }
- // Ideally a frame counter, but for ease of implementation
- // this is just incremented on each prepare_camera.
- // used to turn on and off debugging features.
- int debug_count = -1;
- } data;
- // This functionality is not required in general use (and is compiled out),
- // as the lookup table can normally be hard coded
- // (provided order of planes etc does not change).
- // It is provided for debugging / future maintenance.
- #ifdef VISUAL_SERVER_LIGHT_CULLER_CALCULATE_LUT
- void get_neighbouring_planes(PlaneOrder p_plane, PlaneOrder r_neigh_planes[4]) const;
- void get_corners_of_planes(PlaneOrder p_plane_a, PlaneOrder p_plane_b, PointOrder r_points[2]) const;
- void create_LUT();
- void compact_LUT_entry(uint32_t p_entry_id);
- void debug_print_LUT();
- void debug_print_LUT_as_table();
- void add_LUT(int p_plane_0, int p_plane_1, PointOrder p_pts[2]);
- void add_LUT_entry(uint32_t p_entry_id, PointOrder p_pts[2]);
- String debug_string_LUT_entry(const LocalVector<uint8_t> &p_entry, bool p_pair = false);
- String string_LUT_entry(const LocalVector<uint8_t> &p_entry);
- // Contains a list of points for each combination of plane facing directions.
- LocalVector<uint8_t> _calculated_LUT[LUT_SIZE];
- #endif
- };
- #endif // VISUAL_SERVER_LIGHT_CULLER_H
|