From acdbcc2c091187024b533581f94ca1e1541a539b Mon Sep 17 00:00:00 2001 From: Ulysse Martin Date: Fri, 11 Nov 2016 17:29:36 +0100 Subject: [PATCH] UPBGE: Implement infinite planar maps and refactor cube maps. Infinite planar reflection and refraction is a technique inspired from the martinsh's works. This technique consists to render the scene with the same projection matrix as the scene camera but with a position and an orientation different allowing to render the part wanted for the reflection or the refraction. To accept the planar map, the system rendering the cube maps needed to be refactored. Indeed the system was too detailed and no general to the cube maps and the planar map. This new system work with a base class, the texture renderer. This texture renderer contains some faces to render, these faces are containing a frame buffer attached to the texture using the planar or cube map. Upon the texture renderer, two specialization exists, the cube map KX_CubeMap and KX_PlanarMap, theirs goal is to defines the faces and to modify the camera used for the render. Two categories of texture renderer can be defined, the viewport independents and the viewport dependents. Cube maps are viewport independents because they use their own camera projection, position and rotation, but it is no the case for planar maps. The planar map need to access the setting to create the projection matrix as the current camera rendering the scene, also they need the camera position and orientation. These two categories are stored into KX_TextureRendererMananegr (as KX_CubeMapManager previously) under the list m_renderers[2] accessed with the index VIEWPORT_DEPENDENT and VIEWPORT_INDEPENDENT. Finally two call to render the texture renderers is made in the frame render, one before render all the scenes for the independents and other per scene for the dependents. One of the goal of the specialization of cube maps and planar maps was to define the proper setting for the camera doing the render. To do so, two virtual function were defined: SetupCamera and SetupCameraFace. The first function define the settings shared between all the faces to render (e.g for cube maps the camera position). The second function define the setting unique per faces. After setup the camera settings shared per face, the projection matrix is computed or/and get thanks to the fnction GetProjection, this function take arguments for the viewport dependent renderer as the current scene camera the viewport and area dimension. The attributes shared between the KX_CubeMap and the KX_PlanarMap are exposed into the base python proxy KX_TextureRenderer, only KX_PlanarMap use an extra attribute called "normal" for the mirror normal. In the UI some changes were made. To define a planar map the user have to set the mapping to "Plane" instead of "Cube" in an environment map under "Realtime". As the planar map have to be rendered differently to make refraction a menu is exposed to choose reflection or refraction. In GLSL, only a function to read the planar map texture according to the viewport is added, it is named mtex_image_refl. --- .../startup/bl_ui/properties_texture.py | 2 + source/blender/gpu/intern/gpu_material.c | 19 +- .../gpu/shaders/gpu_shader_material.glsl | 16 + .../gpu/shaders/gpu_shader_vertex_world.glsl | 5 + source/blender/makesdna/DNA_texture_types.h | 7 +- source/blender/makesrna/intern/rna_texture.c | 11 + .../Converter/BL_BlenderDataConversion.cpp | 30 +- source/gameengine/Ketsji/BL_Texture.cpp | 16 +- source/gameengine/Ketsji/BL_Texture.h | 2 +- source/gameengine/Ketsji/CMakeLists.txt | 12 +- source/gameengine/Ketsji/KX_CubeMap.cpp | 226 ++++---------- source/gameengine/Ketsji/KX_CubeMap.h | 76 +---- .../gameengine/Ketsji/KX_CubeMapManager.cpp | 194 ------------ source/gameengine/Ketsji/KX_CubeMapManager.h | 73 ----- source/gameengine/Ketsji/KX_KetsjiEngine.cpp | 15 +- source/gameengine/Ketsji/KX_KetsjiEngine.h | 2 + source/gameengine/Ketsji/KX_PlanarMap.cpp | 285 ++++++++++++++++++ source/gameengine/Ketsji/KX_PlanarMap.h | 78 +++++ .../gameengine/Ketsji/KX_PythonInitTypes.cpp | 3 + source/gameengine/Ketsji/KX_Scene.cpp | 23 +- source/gameengine/Ketsji/KX_Scene.h | 9 +- .../gameengine/Ketsji/KX_TextureRenderer.cpp | 230 ++++++++++++++ source/gameengine/Ketsji/KX_TextureRenderer.h | 117 +++++++ .../Ketsji/KX_TextureRendererManager.cpp | 234 ++++++++++++++ .../Ketsji/KX_TextureRendererManager.h | 95 ++++++ source/gameengine/Rasterizer/CMakeLists.txt | 4 +- .../Rasterizer/RAS_BucketManager.cpp | 3 +- source/gameengine/Rasterizer/RAS_CubeMap.cpp | 222 -------------- source/gameengine/Rasterizer/RAS_CubeMap.h | 87 ------ .../gameengine/Rasterizer/RAS_IRasterizer.cpp | 23 +- .../gameengine/Rasterizer/RAS_IRasterizer.h | 10 +- .../RAS_OpenGLRasterizer.cpp | 13 + .../RAS_OpenGLRasterizer.h | 16 +- source/gameengine/Rasterizer/RAS_Texture.cpp | 24 +- source/gameengine/Rasterizer/RAS_Texture.h | 17 +- .../Rasterizer/RAS_TextureRenderer.cpp | 205 +++++++++++++ .../Rasterizer/RAS_TextureRenderer.h | 99 ++++++ 37 files changed, 1626 insertions(+), 877 deletions(-) delete mode 100644 source/gameengine/Ketsji/KX_CubeMapManager.cpp delete mode 100644 source/gameengine/Ketsji/KX_CubeMapManager.h create mode 100644 source/gameengine/Ketsji/KX_PlanarMap.cpp create mode 100644 source/gameengine/Ketsji/KX_PlanarMap.h create mode 100644 source/gameengine/Ketsji/KX_TextureRenderer.cpp create mode 100644 source/gameengine/Ketsji/KX_TextureRenderer.h create mode 100644 source/gameengine/Ketsji/KX_TextureRendererManager.cpp create mode 100644 source/gameengine/Ketsji/KX_TextureRendererManager.h delete mode 100644 source/gameengine/Rasterizer/RAS_CubeMap.cpp delete mode 100644 source/gameengine/Rasterizer/RAS_CubeMap.h create mode 100644 source/gameengine/Rasterizer/RAS_TextureRenderer.cpp create mode 100644 source/gameengine/Rasterizer/RAS_TextureRenderer.h diff --git a/release/scripts/startup/bl_ui/properties_texture.py b/release/scripts/startup/bl_ui/properties_texture.py index c5d471d8ecb6..07a5a7c71bab 100644 --- a/release/scripts/startup/bl_ui/properties_texture.py +++ b/release/scripts/startup/bl_ui/properties_texture.py @@ -619,6 +619,8 @@ def draw(self, context): layout.template_ID(tex, "image", new="image.new", open="image.open") layout.template_image(tex, "image", tex.image_user, compact=True) layout.prop(env, "filtering") + if env.mapping == 'PLANE': + layout.prop(env, "mode") layout.prop(env, "mapping") if env.mapping == 'PLANE': layout.prop(env, "zoom") diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index d4ddca8f79f1..fabd28891e92 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -1398,14 +1398,27 @@ static void do_material_tex(GPUShadeInput *shi) GPU_link(mat, "mtex_image", texco, GPU_image(tex->ima, &tex->iuser, false), GPU_select_uniform(&mtex->lodbias, GPU_DYNAMIC_TEX_LODBIAS, NULL, ma), &tin, &trgb); } - else { - GPU_link(mat, "mtex_cube_map_refl_refr", - GPU_cube_map(tex->ima, &tex->iuser, false), shi->view, shi->vn, + else if (tex->type == TEX_ENVMAP) { + if (tex->env->type == ENV_PLANE) { + GPU_link(mat, "mtex_image_refl", + GPU_builtin(GPU_VIEW_POSITION), + GPU_builtin(GPU_CAMERA_TEXCO_FACTORS), + texco, + GPU_image(tex->ima, &tex->iuser, false), + GPU_select_uniform(&mtex->lodbias, GPU_DYNAMIC_TEX_LODBIAS, NULL, ma), + GPU_builtin(GPU_OBJECT_MATRIX), + GPU_builtin(GPU_VIEW_MATRIX), + shi->view, shi->vn, &tin, &trgb); + } + else if (tex->env->type == ENV_CUBE) { + GPU_link(mat, "mtex_cube_map_refl_refr", + GPU_cube_map(tex->ima, &tex->iuser, false), shi->view, shi->vn, GPU_select_uniform(&mtex->lodbias, GPU_DYNAMIC_TEX_LODBIAS, NULL, ma), GPU_builtin(GPU_INVERSE_VIEW_MATRIX), GPU_select_uniform(&mtex->ior, GPU_DYNAMIC_TEX_IOR, NULL, ma), GPU_select_uniform(&mtex->refrratio, GPU_DYNAMIC_TEX_REFRRATIO, NULL, ma), &tin, &trgb); + } } rgbnor = TEX_RGB; diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index e8ff862c600f..51055d6f506f 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -1455,6 +1455,22 @@ void mtex_image(vec3 texco, sampler2D ima, float lodbias, out float value, out v value = 1.0; } +void mtex_image_refl(vec3 I, vec4 camerafac, vec3 texco, sampler2D ima, float lodbias, mat4 objectmatrix, mat4 viewmatrix, vec3 vp, vec3 vn, out float value, out vec4 color) +{ + vec4 projvec = gl_ProjectionMatrix * vec4(I, 1.0); + vec3 window = vec3(mtex_2d_mapping(projvec.xyz / projvec.w).xy * camerafac.xy + camerafac.zw, 0.0); + + vec3 Z = normalize(vec3(viewmatrix * objectmatrix * vec4( 0.0, 0.0, 1.0, 0.0))); + + vec3 reflecteddirection = reflect(vp, vn) - reflect(vp, Z); + + // 0.25 is an artistic constant, normal map distortion needs to be scaled down to give proper results + vec2 uv = window.xy + vec2(reflecteddirection.x, reflecteddirection.y) * 0.25; + + color = texture2D(ima, uv, lodbias); + value = 1.0; +} + void mtex_normal(vec3 texco, sampler2D ima, float lodbias, out vec3 normal) { // The invert of the red channel is to make diff --git a/source/blender/gpu/shaders/gpu_shader_vertex_world.glsl b/source/blender/gpu/shaders/gpu_shader_vertex_world.glsl index d45a4b316a83..26c5d20bd118 100644 --- a/source/blender/gpu/shaders/gpu_shader_vertex_world.glsl +++ b/source/blender/gpu/shaders/gpu_shader_vertex_world.glsl @@ -2,6 +2,7 @@ varying vec3 varposition; varying vec3 varnormal; +varying float gl_ClipDistance[6]; /* Color, keep in sync with: gpu_shader_vertex.glsl */ @@ -79,3 +80,7 @@ void main() varnormal = normalize(-varposition); + // Always set clip distance to 1 to disable clipping. + for (int i = 0; i < 6; ++i) { + gl_ClipDistance[i] = 1.0; + } diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h index c6add705633c..4ece48532011 100644 --- a/source/blender/makesdna/DNA_texture_types.h +++ b/source/blender/makesdna/DNA_texture_types.h @@ -142,7 +142,8 @@ typedef struct EnvMap { int ok, lastframe; short recalc, lastsize; int flag, filtering; - float lodfactor, pad; + int mode; + float lodfactor; } EnvMap; typedef struct PointDensity { @@ -481,6 +482,10 @@ typedef struct ColorMapping { /* flag */ #define ENVMAP_AUTO_UPDATE (1 << 0) +/* mode */ +#define ENVMAP_REFLECTION 0 +#define ENVMAP_REFRACTION 1 + /* filtering */ #define ENVMAP_MIPMAP_NONE 0 #define ENVMAP_MIPMAP_LINEAR 1 diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c index 000ffcef2435..a3f3aa9b0c76 100644 --- a/source/blender/makesrna/intern/rna_texture.c +++ b/source/blender/makesrna/intern/rna_texture.c @@ -794,6 +794,12 @@ static void rna_def_environment_map(BlenderRNA *brna) {0, NULL, 0, NULL, NULL} }; + static EnumPropertyItem prop_mode_items[] = { + {ENVMAP_REFLECTION, "REFLECTION", 0, "Reflection", "Reflection rendering"}, + {ENVMAP_REFRACTION, "REFRACTION", 0, "Refraction", "Refraction rendering"}, + {0, NULL, 0, NULL, NULL} + }; + srna = RNA_def_struct(brna, "EnvironmentMap", NULL); RNA_def_struct_sdna(srna, "EnvMap"); RNA_def_struct_ui_text(srna, "EnvironmentMap", @@ -865,6 +871,11 @@ static void rna_def_environment_map(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", ENVMAP_AUTO_UPDATE); RNA_def_property_ui_text(prop, "Auto Update", "True if the cube map is updated every frame"); + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, prop_mode_items); + RNA_def_property_ui_text(prop, "Rendering Mode", "Texture rendering method"); + prop = RNA_def_property(srna, "lod_factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "lodfactor"); RNA_def_property_range(prop, 0.0f, FLT_MAX); diff --git a/source/gameengine/Converter/BL_BlenderDataConversion.cpp b/source/gameengine/Converter/BL_BlenderDataConversion.cpp index 71bded1eacef..6e74c5610fca 100644 --- a/source/gameengine/Converter/BL_BlenderDataConversion.cpp +++ b/source/gameengine/Converter/BL_BlenderDataConversion.cpp @@ -56,6 +56,8 @@ #include "MT_Transform.h" #include "MT_MinMax.h" +#include "GPU_texture.h" + #include "PHY_Pro.h" #include "PHY_IPhysicsEnvironment.h" @@ -86,7 +88,8 @@ #include "RAS_BoundingBoxManager.h" #include "RAS_IPolygonMaterial.h" #include "KX_BlenderMaterial.h" -#include "KX_CubeMapManager.h" +#include "KX_TextureRendererManager.h" +#include "KX_PlanarMap.h" #include "KX_CubeMap.h" #include "BL_Texture.h" @@ -1846,20 +1849,25 @@ void BL_ConvertBlenderObjects(struct Main* maggie, for (unsigned short k = 0; k < RAS_Texture::MaxUnits; ++k) { RAS_Texture *tex = polymat->GetTexture(k); + if (!tex || !tex->Ok()) { + continue; + } - if (tex && tex->Ok() && tex->IsCubeMap() && tex->GetTex()->env->stype == ENV_REALT) { - EnvMap *env = tex->GetTex()->env; - KX_GameObject *viewpoint = gameobj; + EnvMap *env = tex->GetTex()->env; + if (!env || env->stype != ENV_REALT) { + continue; + } - if (env->object) { - KX_GameObject *obj = converter->FindGameObject(env->object); - if (obj) { - viewpoint = obj; - } + KX_GameObject *viewpoint = gameobj; + if (env->object) { + KX_GameObject *obj = converter->FindGameObject(env->object); + if (obj) { + viewpoint = obj; } - - kxscene->GetCubeMapManager()->AddCubeMap(tex, viewpoint); } + + KX_TextureRendererManager::RendererType type = tex->IsCubeMap() ? KX_TextureRendererManager::CUBE : KX_TextureRendererManager::PLANAR; + kxscene->GetTextureRendererManager()->AddRenderer(type, tex, viewpoint); } } } diff --git a/source/gameengine/Ketsji/BL_Texture.cpp b/source/gameengine/Ketsji/BL_Texture.cpp index df04a66dd93f..791d5bafb4f5 100644 --- a/source/gameengine/Ketsji/BL_Texture.cpp +++ b/source/gameengine/Ketsji/BL_Texture.cpp @@ -23,7 +23,7 @@ */ #include "BL_Texture.h" -#include "KX_CubeMap.h" +#include "KX_TextureRenderer.h" #include "DNA_texture_types.h" @@ -42,7 +42,9 @@ BL_Texture::BL_Texture(MTex *mtex) { Tex *tex = m_mtex->tex; EnvMap *env = tex->env; - m_isCubeMap = (env && tex->type == TEX_ENVMAP && (env->stype == ENV_LOAD || env->stype == ENV_REALT)); + m_isCubeMap = (env && tex->type == TEX_ENVMAP && + (env->stype == ENV_LOAD || + (env->stype == ENV_REALT && env->type == ENV_CUBE))); Image *ima = tex->ima; ImageUser& iuser = tex->iuser; @@ -249,7 +251,7 @@ PyAttributeDef BL_Texture::Attributes[] = { KX_PYATTRIBUTE_RW_FUNCTION("parallaxStep", BL_Texture, pyattr_get_parallax_step, pyattr_set_parallax_step), KX_PYATTRIBUTE_RW_FUNCTION("lodBias", BL_Texture, pyattr_get_lod_bias, pyattr_set_lod_bias), KX_PYATTRIBUTE_RW_FUNCTION("bindCode", BL_Texture, pyattr_get_bind_code, pyattr_set_bind_code), - KX_PYATTRIBUTE_RO_FUNCTION("cubeMap", BL_Texture, pyattr_get_cube_map), + KX_PYATTRIBUTE_RO_FUNCTION("renderer", BL_Texture, pyattr_get_renderer), KX_PYATTRIBUTE_RW_FUNCTION("ior", BL_Texture, pyattr_get_ior, pyattr_set_ior), KX_PYATTRIBUTE_RW_FUNCTION("refractionRatio", BL_Texture, pyattr_get_refraction_ratio, pyattr_set_refraction_ratio), KX_PYATTRIBUTE_RW_FUNCTION("uvRotation", BL_Texture, pyattr_get_uv_rotation, pyattr_set_uv_rotation), @@ -519,12 +521,12 @@ int BL_Texture::pyattr_set_bind_code(PyObjectPlus *self_v, const KX_PYATTRIBUTE_ return PY_SET_ATTR_SUCCESS; } -PyObject *BL_Texture::pyattr_get_cube_map(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef) +PyObject *BL_Texture::pyattr_get_renderer(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef) { BL_Texture *self = static_cast(self_v); - KX_CubeMap *cubeMap = (KX_CubeMap *)self->GetCubeMap(); - if (cubeMap) { - return cubeMap->GetProxy(); + KX_TextureRenderer *renderer = static_cast(self->GetRenderer()); + if (renderer) { + return renderer->GetProxy(); } Py_RETURN_NONE; diff --git a/source/gameengine/Ketsji/BL_Texture.h b/source/gameengine/Ketsji/BL_Texture.h index 3e336e4cc25c..0c4ad98861df 100644 --- a/source/gameengine/Ketsji/BL_Texture.h +++ b/source/gameengine/Ketsji/BL_Texture.h @@ -115,7 +115,7 @@ class BL_Texture : public CValue, public RAS_Texture static int pyattr_set_lod_bias(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); static PyObject *pyattr_get_bind_code(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef); static int pyattr_set_bind_code(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); - static PyObject *pyattr_get_cube_map(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef); + static PyObject *pyattr_get_renderer(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef); static PyObject *pyattr_get_ior(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef); static int pyattr_set_ior(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); static PyObject *pyattr_get_refraction_ratio(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef); diff --git a/source/gameengine/Ketsji/CMakeLists.txt b/source/gameengine/Ketsji/CMakeLists.txt index cf3073e8f5da..e2bc5291bb35 100644 --- a/source/gameengine/Ketsji/CMakeLists.txt +++ b/source/gameengine/Ketsji/CMakeLists.txt @@ -82,10 +82,11 @@ set(SRC KX_CameraActuator.cpp KX_CameraIpoSGController.cpp KX_CharacterWrapper.cpp + KX_CollisionEventManager.cpp + KX_CollisionSensor.cpp KX_ConstraintActuator.cpp KX_ConstraintWrapper.cpp KX_CubeMap.cpp - KX_CubeMapManager.cpp KX_EmptyObject.cpp KX_FontObject.cpp KX_GameActuator.cpp @@ -111,6 +112,7 @@ set(SRC KX_ObstacleSimulation.cpp KX_OrientationInterpolator.cpp KX_ParentActuator.cpp + KX_PlanarMap.cpp KX_PolyProxy.cpp KX_PositionInterpolator.cpp KX_PyConstraintBinding.cpp @@ -136,10 +138,10 @@ set(SRC KX_StateActuator.cpp KX_SteeringActuator.cpp KX_TextMaterial.cpp + KX_TextureRenderer.cpp + KX_TextureRendererManager.cpp KX_TimeCategoryLogger.cpp KX_TimeLogger.cpp - KX_CollisionEventManager.cpp - KX_CollisionSensor.cpp KX_TrackToActuator.cpp KX_VehicleWrapper.cpp KX_VertexProxy.cpp @@ -168,7 +170,6 @@ set(SRC KX_ConstraintActuator.h KX_ConstraintWrapper.h KX_CubeMap.h - KX_CubeMapManager.h KX_EmptyObject.h KX_FontObject.h KX_GameActuator.h @@ -200,6 +201,7 @@ set(SRC KX_OrientationInterpolator.h KX_ParentActuator.h KX_PhysicsEngineEnums.h + KX_PlanarMap.h KX_PolyProxy.h KX_PositionInterpolator.h KX_PyConstraintBinding.h @@ -225,6 +227,8 @@ set(SRC KX_StateActuator.h KX_SteeringActuator.h KX_TextMaterial.h + KX_TextureRenderer.h + KX_TextureRendererManager.h KX_TimeCategoryLogger.h KX_TimeLogger.h KX_CollisionEventManager.h diff --git a/source/gameengine/Ketsji/KX_CubeMap.cpp b/source/gameengine/Ketsji/KX_CubeMap.cpp index a40b2e3462f8..58f6616aab75 100644 --- a/source/gameengine/Ketsji/KX_CubeMap.cpp +++ b/source/gameengine/Ketsji/KX_CubeMap.cpp @@ -25,24 +25,57 @@ */ #include "KX_CubeMap.h" -#include "KX_GameObject.h" -#include "KX_Globals.h" - -#include "DNA_texture_types.h" +#include "KX_Camera.h" + +#include "RAS_IRasterizer.h" +#include "RAS_Texture.h" + +static const MT_Matrix3x3 topFaceViewMat( + 1.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.0f, 0.0f, -1.0f); + +static const MT_Matrix3x3 bottomFaceViewMat( + -1.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.0f, 0.0f, 1.0f); + +static const MT_Matrix3x3 frontFaceViewMat( + 0.0f, 0.0f, -1.0f, + 0.0f, -1.0f, 0.0f, + -1.0f, 0.0f, 0.0f); + +static const MT_Matrix3x3 backFaceViewMat( + 0.0f, 0.0f, 1.0f, + 0.0f, -1.0f, 0.0f, + 1.0f, 0.0f, 0.0f); + +static const MT_Matrix3x3 rightFaceViewMat( + 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, -1.0f, + 0.0f, 1.0f, 0.0f); + +static const MT_Matrix3x3 leftFaceViewMat( + 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + 0.0f, -1.0f, 0.0f); + +const MT_Matrix3x3 KX_CubeMap::faceViewMatrices3x3[KX_CubeMap::NUM_FACES] = { + topFaceViewMat, + bottomFaceViewMat, + frontFaceViewMat, + backFaceViewMat, + rightFaceViewMat, + leftFaceViewMat +}; KX_CubeMap::KX_CubeMap(EnvMap *env, KX_GameObject *viewpoint) - :RAS_CubeMap(), - m_viewpointObject(viewpoint), - m_invalidProjection(true), - m_enabled(true), - m_ignoreLayers(env->notlay), - m_clipStart(env->clipsta), - m_clipEnd(env->clipend), - m_lodDistanceFactor(env->lodfactor), - m_autoUpdate(true), - m_forceUpdate(true) + :KX_TextureRenderer(env, viewpoint), + m_invalidProjection(true) { - m_autoUpdate = (env->flag & ENVMAP_AUTO_UPDATE) != 0; + for (int target : RAS_Texture::GetCubeMapTargets()) { + m_faces.emplace_back(target); + } } KX_CubeMap::~KX_CubeMap() @@ -54,82 +87,37 @@ std::string KX_CubeMap::GetName() return "KX_CubeMap"; } -KX_GameObject *KX_CubeMap::GetViewpointObject() const -{ - return m_viewpointObject; -} - -void KX_CubeMap::SetViewpointObject(KX_GameObject *gameobj) -{ - m_viewpointObject = gameobj; -} - -void KX_CubeMap::SetInvalidProjectionMatrix(bool invalid) +void KX_CubeMap::InvalidateProjectionMatrix() { - m_invalidProjection = invalid; + m_invalidProjection = true; } -bool KX_CubeMap::GetInvalidProjectionMatrix() const +const MT_Matrix4x4& KX_CubeMap::GetProjectionMatrix(RAS_IRasterizer *rasty, KX_Scene *UNUSED(scene), KX_Camera *UNUSED(sceneCamera), + const RAS_Rect& UNUSED(viewport), const RAS_Rect& UNUSED(area)) { - return m_invalidProjection; -} - -void KX_CubeMap::SetProjectionMatrix(const MT_Matrix4x4& projection) -{ - m_projection = projection; -} + if (m_invalidProjection) { + m_projection = rasty->GetFrustumMatrix(-m_clipStart, m_clipStart, -m_clipStart, m_clipStart, m_clipStart, m_clipEnd, 1.0f, true); + m_invalidProjection = false; + } -const MT_Matrix4x4& KX_CubeMap::GetProjectionMatrix() const -{ return m_projection; } -bool KX_CubeMap::GetEnabled() const +bool KX_CubeMap::SetupCamera(KX_Camera *sceneCamera, KX_Camera *camera) { - return m_enabled; -} + KX_GameObject *viewpoint = GetViewpointObject(); + const MT_Vector3& position = viewpoint->NodeGetWorldPosition(); -int KX_CubeMap::GetIgnoreLayers() const -{ - return m_ignoreLayers; -} + camera->NodeSetWorldPosition(position); -float KX_CubeMap::GetClipStart() const -{ - return m_clipStart; + return true; } -float KX_CubeMap::GetClipEnd() const +bool KX_CubeMap::SetupCameraFace(KX_Camera *camera, unsigned short index) { - return m_clipEnd; -} - -void KX_CubeMap::SetClipStart(float start) -{ - m_clipStart = start; -} + camera->NodeSetGlobalOrientation(faceViewMatrices3x3[index]); -void KX_CubeMap::SetClipEnd(float end) -{ - m_clipEnd = end; -} - -float KX_CubeMap::GetLodDistanceFactor() const -{ - return m_lodDistanceFactor; -} - -void KX_CubeMap::SetLodDistanceFactor(float lodfactor) -{ - m_lodDistanceFactor = lodfactor; -} - -bool KX_CubeMap::NeedUpdate() -{ - bool result = m_autoUpdate || m_forceUpdate; - m_forceUpdate = false; - - return result; + return true; } #ifdef WITH_PYTHON @@ -151,101 +139,17 @@ PyTypeObject KX_CubeMap::Type = { Methods, 0, 0, - &CValue::Type, + &KX_TextureRenderer::Type, 0, 0, 0, 0, 0, 0, py_base_new }; PyMethodDef KX_CubeMap::Methods[] = { - KX_PYMETHODTABLE_NOARGS(KX_CubeMap, update), {nullptr, nullptr} // Sentinel }; PyAttributeDef KX_CubeMap::Attributes[] = { - KX_PYATTRIBUTE_RW_FUNCTION("viewpointObject", KX_CubeMap, pyattr_get_viewpoint_object, pyattr_set_viewpoint_object), - KX_PYATTRIBUTE_BOOL_RW("autoUpdate", KX_CubeMap, m_autoUpdate), - KX_PYATTRIBUTE_BOOL_RW("enabled", KX_CubeMap, m_enabled), - KX_PYATTRIBUTE_INT_RW("ignoreLayers", 0, (1 << 20) - 1, true, KX_CubeMap, m_ignoreLayers), - KX_PYATTRIBUTE_RW_FUNCTION("clipStart", KX_CubeMap, pyattr_get_clip_start, pyattr_set_clip_start), - KX_PYATTRIBUTE_RW_FUNCTION("clipEnd", KX_CubeMap, pyattr_get_clip_end, pyattr_set_clip_end), - KX_PYATTRIBUTE_FLOAT_RW("lodDistanceFactor", 0.0f, FLT_MAX, KX_CubeMap, m_lodDistanceFactor), KX_PYATTRIBUTE_NULL // Sentinel }; -KX_PYMETHODDEF_DOC_NOARGS(KX_CubeMap, update, "update(): Set the cube map to be updated next frame.\n") -{ - m_forceUpdate = true; - Py_RETURN_NONE; -} - -PyObject *KX_CubeMap::pyattr_get_viewpoint_object(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef) -{ - KX_CubeMap *self = static_cast(self_v); - KX_GameObject *gameobj = self->GetViewpointObject(); - if (gameobj) { - return gameobj->GetProxy(); - } - Py_RETURN_NONE; -} - -int KX_CubeMap::pyattr_set_viewpoint_object(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) -{ - KX_CubeMap *self = static_cast(self_v); - KX_GameObject *gameobj = nullptr; - - SCA_LogicManager *logicmgr = KX_GetActiveScene()->GetLogicManager(); - - if (!ConvertPythonToGameObject(logicmgr, value, &gameobj, true, "cubeMap.object = value: KX_CubeMap")) - return PY_SET_ATTR_FAIL; - - self->SetViewpointObject(gameobj); - return PY_SET_ATTR_SUCCESS; -} - -PyObject *KX_CubeMap::pyattr_get_clip_start(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef) -{ - KX_CubeMap *self = static_cast(self_v); - return PyFloat_FromDouble(self->GetClipStart()); -} - -int KX_CubeMap::pyattr_set_clip_start(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) -{ - KX_CubeMap *self = static_cast(self_v); - - const float val = PyFloat_AsDouble(value); - - if (val <= 0.0f) { - PyErr_SetString(PyExc_AttributeError, "cubeMap.clipStart = float: KX_CubeMap, expected a float grater than zero"); - return PY_SET_ATTR_FAIL; - } - - self->SetClipStart(val); - self->SetInvalidProjectionMatrix(true); - - return PY_SET_ATTR_SUCCESS; -} - -PyObject *KX_CubeMap::pyattr_get_clip_end(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef) -{ - KX_CubeMap *self = static_cast(self_v); - return PyFloat_FromDouble(self->GetClipEnd()); -} - -int KX_CubeMap::pyattr_set_clip_end(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) -{ - KX_CubeMap *self = static_cast(self_v); - - const float val = PyFloat_AsDouble(value); - - if (val <= 0.0f) { - PyErr_SetString(PyExc_AttributeError, "cubeMap.clipEnd = float: KX_CubeMap, expected a float grater than zero"); - return PY_SET_ATTR_FAIL; - } - - self->SetClipEnd(val); - self->SetInvalidProjectionMatrix(true); - - return PY_SET_ATTR_SUCCESS; -} - #endif // WITH_PYTHON diff --git a/source/gameengine/Ketsji/KX_CubeMap.h b/source/gameengine/Ketsji/KX_CubeMap.h index d94f2d413895..7844fe92eb45 100644 --- a/source/gameengine/Ketsji/KX_CubeMap.h +++ b/source/gameengine/Ketsji/KX_CubeMap.h @@ -27,87 +27,37 @@ #ifndef __KX_CUBEMAP_H__ #define __KX_CUBEMAP_H__ -#include "RAS_CubeMap.h" -#include "EXP_Value.h" +#include "KX_TextureRenderer.h" -#include "MT_Matrix4x4.h" - -class KX_GameObject; - -struct EnvMap; - -class KX_CubeMap : public CValue, public RAS_CubeMap +class KX_CubeMap : public KX_TextureRenderer { Py_Header private: - /// The object used to render from its position. - KX_GameObject *m_viewpointObject; - /// The camera projection matrix depending on clip start/end. MT_Matrix4x4 m_projection; - /// True if the projection matrix is invalid and need to be recomputed. bool m_invalidProjection; - /// The cube map is used by the user. - bool m_enabled; - /// Layers to ignore during render. - int m_ignoreLayers; - - /// View clip start. - float m_clipStart; - /// View clip end. - float m_clipEnd; - - /// Distance factor for level of detail. - float m_lodDistanceFactor; +public: + enum { + NUM_FACES = 6 + }; - /// True if the realtime cube map is updated every frame. - bool m_autoUpdate; - /** True if the realtime cube map need to be updated for the next frame. - * Generally used when m_autoUpdate is to false. - */ - bool m_forceUpdate; + /// Face view matrices in 3x3 matrices. + static const MT_Matrix3x3 faceViewMatrices3x3[NUM_FACES]; -public: KX_CubeMap(EnvMap *env, KX_GameObject *viewpoint); virtual ~KX_CubeMap(); virtual std::string GetName(); - KX_GameObject *GetViewpointObject() const; - void SetViewpointObject(KX_GameObject *gameobj); - - float GetClipStart() const; - float GetClipEnd() const; - void SetClipStart(float start); - void SetClipEnd(float end); - - float GetLodDistanceFactor() const; - void SetLodDistanceFactor(float lodfactor); - - void SetInvalidProjectionMatrix(bool invalid); - bool GetInvalidProjectionMatrix() const; - void SetProjectionMatrix(const MT_Matrix4x4& projection); - const MT_Matrix4x4& GetProjectionMatrix() const; - - bool GetEnabled() const; - int GetIgnoreLayers() const; - - // Return true when this cube map need to be updated. - bool NeedUpdate(); - -#ifdef WITH_PYTHON - KX_PYMETHOD_DOC_NOARGS(KX_CubeMap, update); + virtual void InvalidateProjectionMatrix(); + virtual const MT_Matrix4x4& GetProjectionMatrix(RAS_IRasterizer *rasty, KX_Scene *scene, KX_Camera *sceneCamera, + const RAS_Rect& viewport, const RAS_Rect& area); - static PyObject *pyattr_get_viewpoint_object(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef); - static int pyattr_set_viewpoint_object(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); - static PyObject *pyattr_get_clip_start(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef); - static int pyattr_set_clip_start(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); - static PyObject *pyattr_get_clip_end(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef); - static int pyattr_set_clip_end(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); -#endif + virtual bool SetupCamera(KX_Camera *sceneCamera, KX_Camera *camera); + virtual bool SetupCameraFace(KX_Camera *camera, unsigned short index); }; #endif // __KX_CUBEMAP_H__ diff --git a/source/gameengine/Ketsji/KX_CubeMapManager.cpp b/source/gameengine/Ketsji/KX_CubeMapManager.cpp deleted file mode 100644 index a87f919e0680..000000000000 --- a/source/gameengine/Ketsji/KX_CubeMapManager.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/* - * ***** BEGIN GPL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Contributor(s): Ulysse Martin, Tristan Porteries. - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/** \file gameengine/Ketsji/KX_CubeMapManager.cpp - * \ingroup ketsji - */ - -#include "KX_CubeMapManager.h" -#include "KX_Camera.h" -#include "KX_Scene.h" -#include "KX_Globals.h" -#include "KX_CubeMap.h" - -#include "EXP_ListValue.h" - -#include "RAS_IRasterizer.h" -#include "RAS_Texture.h" - -#include "DNA_texture_types.h" - -KX_CubeMapManager::KX_CubeMapManager(KX_Scene *scene) - :m_scene(scene) -{ - const RAS_CameraData& camdata = RAS_CameraData(); - m_camera = new KX_Camera(m_scene, KX_Scene::m_callbacks, camdata, true, true); - m_camera->SetName("__cubemap_cam__"); -} - -KX_CubeMapManager::~KX_CubeMapManager() -{ - for (std::vector::iterator it = m_cubeMaps.begin(), end = m_cubeMaps.end(); it != end; ++it) { - delete *it; - } - - m_camera->Release(); -} - -void KX_CubeMapManager::AddCubeMap(RAS_Texture *texture, KX_GameObject *gameobj) -{ - for (std::vector::iterator it = m_cubeMaps.begin(), end = m_cubeMaps.end(); it != end; ++it) { - KX_CubeMap *cubeMap = *it; - const std::vector& textures = cubeMap->GetTextureUsers(); - for (std::vector::const_iterator it = textures.begin(), end = textures.end(); it != end; ++it) { - if ((*it)->GetTex() == texture->GetTex()) { - cubeMap->AddTextureUser(texture); - return; - } - } - } - - EnvMap *env = texture->GetTex()->env; - KX_CubeMap *cubeMap = new KX_CubeMap(env, gameobj); - cubeMap->AddTextureUser(texture); - texture->SetCubeMap(cubeMap); - m_cubeMaps.push_back(cubeMap); -} - -void KX_CubeMapManager::InvalidateCubeMapViewpoint(KX_GameObject *gameobj) -{ - for (std::vector::iterator it = m_cubeMaps.begin(), end = m_cubeMaps.end(); it != end; ++it) { - KX_CubeMap *cubeMap = *it; - if (cubeMap->GetViewpointObject() == gameobj) { - cubeMap->SetViewpointObject(nullptr); - } - } -} - -void KX_CubeMapManager::RenderCubeMap(RAS_IRasterizer *rasty, KX_CubeMap *cubeMap) -{ - KX_GameObject *viewpoint = cubeMap->GetViewpointObject(); - - // Doesn't need (or can) update. - if (!cubeMap->NeedUpdate() || !cubeMap->GetEnabled() || !viewpoint) { - return; - } - - const MT_Vector3& position = viewpoint->NodeGetWorldPosition(); - - /* We hide the viewpoint object in the case backface culling is disabled -> we can't see through - * the object faces if the camera is inside the gameobject. - */ - viewpoint->SetVisible(false, true); - - // For Culling we need first to set the camera position at the object position. - m_camera->NodeSetWorldPosition(position); - m_camera->SetLodDistanceFactor(cubeMap->GetLodDistanceFactor()); - - /* When we update clipstart or clipend values, - * or if the projection matrix is not computed yet, - * we have to compute projection matrix. - */ - if (cubeMap->GetInvalidProjectionMatrix()) { - const float clipstart = cubeMap->GetClipStart(); - const float clipend = cubeMap->GetClipEnd(); - const MT_Matrix4x4& proj = rasty->GetFrustumMatrix(-clipstart, clipstart, -clipstart, clipstart, clipstart, clipend, 1.0f, true); - cubeMap->SetProjectionMatrix(proj); - cubeMap->SetInvalidProjectionMatrix(false); - } - - // Else we use the projection matrix stored in the cube map. - rasty->SetProjectionMatrix(cubeMap->GetProjectionMatrix()); - m_camera->SetProjectionMatrix(cubeMap->GetProjectionMatrix()); - - cubeMap->BeginRender(); - - for (unsigned short i = 0; i < 6; ++i) { - cubeMap->BindFace(rasty, i); - - // For Culling we need also to set the camera orientation. - m_camera->NodeSetGlobalOrientation(RAS_CubeMap::faceViewMatrices3x3[i]); - m_camera->NodeUpdateGS(0.0f); - - // Setup camera modelview matrix for culling planes. - const MT_Transform trans(m_camera->GetWorldToCamera()); - const MT_Matrix4x4 viewmat(trans); - m_camera->SetModelviewMatrix(viewmat); - - rasty->SetViewMatrix(viewmat, RAS_CubeMap::faceViewMatrices3x3[i], position, MT_Vector3(1.0f, 1.0f, 1.0f), true); - - m_scene->CalculateVisibleMeshes(rasty, m_camera, ~cubeMap->GetIgnoreLayers()); - - /* Updating the lod per face is normally not expensive because a cube map normally show every objects - * but here we update only visible object of a face including the clip end and start. - */ - m_scene->UpdateObjectLods(m_camera); - - /* Update animations to use the culling of each faces, BL_ActionManager avoid redundants - * updates internally. */ - KX_GetActiveEngine()->UpdateAnimations(m_scene); - - // Now the objects are culled and we can render the scene. - m_scene->GetWorldInfo()->RenderBackground(rasty); - // Send a nullptr off screen because we use a set of FBO with shared textures, not an off screen. - m_scene->RenderBuckets(trans, rasty, nullptr); - } - - cubeMap->EndRender(); - - viewpoint->SetVisible(true, true); -} - -void KX_CubeMapManager::Render(RAS_IRasterizer *rasty) -{ - if (m_cubeMaps.size() == 0 || rasty->GetDrawingMode() != RAS_IRasterizer::RAS_TEXTURED) { - return; - } - - const RAS_IRasterizer::DrawType drawmode = rasty->GetDrawingMode(); - rasty->SetDrawingMode(RAS_IRasterizer::RAS_CUBEMAP); - - // Disable scissor to not bother with scissor box. - rasty->Disable(RAS_IRasterizer::RAS_SCISSOR_TEST); - - // Copy current stereo mode. - const RAS_IRasterizer::StereoMode steremode = rasty->GetStereoMode(); - // Disable stereo for realtime cube maps. - rasty->SetStereoMode(RAS_IRasterizer::RAS_STEREO_NOSTEREO); - - for (std::vector::iterator it = m_cubeMaps.begin(), end = m_cubeMaps.end(); it != end; ++it) { - RenderCubeMap(rasty, *it); - } - - // Restore previous stereo mode. - rasty->SetStereoMode(steremode); - - rasty->Enable(RAS_IRasterizer::RAS_SCISSOR_TEST); - - rasty->SetDrawingMode(drawmode); -} - -void KX_CubeMapManager::Merge(KX_CubeMapManager *other) -{ - m_cubeMaps.insert(m_cubeMaps.end(), other->m_cubeMaps.begin(), other->m_cubeMaps.end()); - other->m_cubeMaps.clear(); -} diff --git a/source/gameengine/Ketsji/KX_CubeMapManager.h b/source/gameengine/Ketsji/KX_CubeMapManager.h deleted file mode 100644 index a17cb7f92a23..000000000000 --- a/source/gameengine/Ketsji/KX_CubeMapManager.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * ***** BEGIN GPL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Contributor(s): Ulysse Martin, Tristan Porteries. - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/** \file KX_CubeMapManager.h - * \ingroup ketsji - */ - -#ifndef __KX_CUBEMAPMANAGER_H__ -#define __KX_CUBEMAPMANAGER_H__ - -#include - -class KX_GameObject; -class KX_Camera; -class KX_Scene; -class KX_CubeMap; - -class RAS_IRasterizer; -class RAS_Texture; - -class KX_CubeMapManager -{ -private: - /// All existing realtime cube maps of this scene. - std::vector m_cubeMaps; - - /** The camera used for realtime cube map render. - * This camera is own by the cube map manager. - */ - KX_Camera *m_camera; - - /// The scene we are rendering for. - KX_Scene *m_scene; - - void RenderCubeMap(RAS_IRasterizer *rasty, KX_CubeMap *cubemap); - -public: - KX_CubeMapManager(KX_Scene *scene); - virtual ~KX_CubeMapManager(); - - /** Add and create a cube map if none existing cube map was using the same - * texture containing in the material texture passed. - */ - void AddCubeMap(RAS_Texture *texture, KX_GameObject *gameobj); - /// Invalidate cube map using the given game object as viewpoint object. - void InvalidateCubeMapViewpoint(KX_GameObject *gameobj); - - void Render(RAS_IRasterizer *rasty); - - /// Merge the content of an other cube map manager, used during lib loading. - void Merge(KX_CubeMapManager *other); -}; - -#endif // __KX_CUBEMAPMANAGER_H__ diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp index e2f64649e2be..3687214b9145 100644 --- a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp +++ b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp @@ -535,8 +535,8 @@ void KX_KetsjiEngine::Render() KX_Scene *scene = *sceit; // shadow buffers RenderShadowBuffers(scene); - // cubemaps - scene->RenderCubeMaps(m_rasterizer); + // Render only independent texture renderers here. + scene->RenderTextureRenderers(KX_TextureRendererManager::VIEWPORT_INDEPENDENT, m_rasterizer, nullptr, nullptr, RAS_Rect(), RAS_Rect()); } // Update all off screen to the current canvas size. @@ -729,6 +729,15 @@ void KX_KetsjiEngine::SetCameraOverrideZoom(float camzoom) m_overrideCamZoom = camzoom; } +float KX_KetsjiEngine::GetCameraZoom(KX_Camera *camera) const +{ + KX_Scene *scene = camera->GetScene(); + const bool overrideCamera = m_overrideCam && (scene->GetName() == m_overrideSceneName) && + (camera->GetName() == "__default__cam__"); + + return overrideCamera ? m_overrideCamZoom : m_cameraZoom; +} + void KX_KetsjiEngine::EnableCameraOverride(const std::string& forscene, const MT_CmMatrix4x4& projmat, const MT_CmMatrix4x4& viewmat, const RAS_CameraData& camdata) { @@ -960,6 +969,8 @@ void KX_KetsjiEngine::RenderFrame(KX_Scene *scene, KX_Camera *cam, RAS_OffScreen GetSceneViewport(scene, cam, area, viewport); + scene->RenderTextureRenderers(KX_TextureRendererManager::VIEWPORT_DEPENDENT, m_rasterizer, offScreen, cam, viewport, area); + // set the viewport for this frame and scene const int left = viewport.GetLeft(); const int bottom = viewport.GetBottom(); diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.h b/source/gameengine/Ketsji/KX_KetsjiEngine.h index 616cae9e6525..1920657b123a 100644 --- a/source/gameengine/Ketsji/KX_KetsjiEngine.h +++ b/source/gameengine/Ketsji/KX_KetsjiEngine.h @@ -309,6 +309,8 @@ class KX_KetsjiEngine void SetCameraZoom(float camzoom); /// Sets zoom for default camera, = 2 in embedded mode. void SetCameraOverrideZoom(float camzoom); + /// Get the camera zoom for the passed camera. + float GetCameraZoom(KX_Camera *camera) const; void EnableCameraOverride(const std::string& forscene, const MT_CmMatrix4x4& projmat, const MT_CmMatrix4x4& viewmat, const RAS_CameraData& camdata); diff --git a/source/gameengine/Ketsji/KX_PlanarMap.cpp b/source/gameengine/Ketsji/KX_PlanarMap.cpp new file mode 100644 index 000000000000..745d91cffb4d --- /dev/null +++ b/source/gameengine/Ketsji/KX_PlanarMap.cpp @@ -0,0 +1,285 @@ +/* +* ***** BEGIN GPL LICENSE BLOCK ***** +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +* Contributor(s): Ulysse Martin, Tristan Porteries, Martins Upitis. +* +* ***** END GPL LICENSE BLOCK ***** +*/ + +/** \file KX_PlanarMap.cpp +* \ingroup ketsji +*/ + +#include "KX_PlanarMap.h" +#include "KX_Camera.h" +#include "KX_PyMath.h" +#include "KX_Globals.h" + +#include "RAS_IRasterizer.h" +#include "RAS_Texture.h" + +KX_PlanarMap::KX_PlanarMap(EnvMap *env, KX_GameObject *viewpoint) + :KX_TextureRenderer(env, viewpoint), + m_normal(0.0f, 0.0f, 1.0f) +{ + m_faces.emplace_back(RAS_Texture::GetTexture2DType()); + + switch (env->mode) { + case ENVMAP_REFLECTION: + { + m_type = REFLECTION; + break; + } + case ENVMAP_REFRACTION: + { + m_type = REFRACTION; + break; + } + } +} + +KX_PlanarMap::~KX_PlanarMap() +{ +} + +std::string KX_PlanarMap::GetName() +{ + return "KX_PlanarMap"; +} + +void KX_PlanarMap::ComputeClipPlane(const MT_Vector3& mirrorObjWorldPos, const MT_Matrix3x3& mirrorObjWorldOri) +{ + const MT_Vector3 normal = mirrorObjWorldOri * m_normal; + + m_clipPlane.x() = normal.x(); + m_clipPlane.y() = normal.y(); + m_clipPlane.z() = normal.z(); + m_clipPlane.w() = -(m_clipPlane.x() * mirrorObjWorldPos.x() + + m_clipPlane.y() * mirrorObjWorldPos.y() + + m_clipPlane.z() * mirrorObjWorldPos.z()); +} + +void KX_PlanarMap::InvalidateProjectionMatrix() +{ + m_projections.clear(); +} + +const MT_Matrix4x4& KX_PlanarMap::GetProjectionMatrix(RAS_IRasterizer *rasty, KX_Scene *scene, KX_Camera *sceneCamera, + const RAS_Rect& viewport, const RAS_Rect& area) +{ + std::unordered_map::const_iterator projectionit = m_projections.find(sceneCamera); + if (projectionit != m_projections.end()) { + return projectionit->second; + } + + MT_Matrix4x4& projection = m_projections[sceneCamera]; + + RAS_FrameFrustum frustum; + const bool orthographic = !sceneCamera->GetCameraData()->m_perspective; + + if (orthographic) { + RAS_FramingManager::ComputeOrtho( + scene->GetFramingType(), + area, + viewport, + sceneCamera->GetScale(), + m_clipStart, + m_clipEnd, + sceneCamera->GetSensorFit(), + sceneCamera->GetShiftHorizontal(), + sceneCamera->GetShiftVertical(), + frustum); + } + else { + RAS_FramingManager::ComputeFrustum( + scene->GetFramingType(), + area, + viewport, + sceneCamera->GetLens(), + sceneCamera->GetSensorWidth(), + sceneCamera->GetSensorHeight(), + sceneCamera->GetSensorFit(), + sceneCamera->GetShiftHorizontal(), + sceneCamera->GetShiftVertical(), + m_clipStart, + m_clipEnd, + frustum); + } + + if (!sceneCamera->GetViewport()) { + KX_KetsjiEngine *engine = KX_GetActiveEngine(); + const float camzoom = engine->GetCameraZoom(sceneCamera); + + frustum.x1 *= camzoom; + frustum.x2 *= camzoom; + frustum.y1 *= camzoom; + frustum.y2 *= camzoom; + } + + if (orthographic) { + projection = rasty->GetOrthoMatrix( + frustum.x1, frustum.x2, frustum.y1, frustum.y2, frustum.camnear, frustum.camfar); + } + else { + const float focallength = sceneCamera->GetFocalLength(); + + projection = rasty->GetFrustumMatrix( + frustum.x1, frustum.x2, frustum.y1, frustum.y2, frustum.camnear, frustum.camfar, focallength); + } + + return projection; +} + +void KX_PlanarMap::BeginRenderFace(RAS_IRasterizer *rasty) +{ + KX_TextureRenderer::BeginRenderFace(rasty); + + if (m_type == REFLECTION) { + rasty->SetInvertFrontFace(true); + rasty->EnableClipPlane(0, m_clipPlane); + } + else { + rasty->EnableClipPlane(0, -m_clipPlane); + } +} + +void KX_PlanarMap::EndRenderFace(RAS_IRasterizer *rasty) +{ + if (m_type == REFLECTION) { + rasty->SetInvertFrontFace(false); + } + rasty->DisableClipPlane(0); + + KX_TextureRenderer::EndRenderFace(rasty); +} + +const MT_Vector3& KX_PlanarMap::GetNormal() const +{ + return m_normal; +} + +void KX_PlanarMap::SetNormal(const MT_Vector3& normal) +{ + m_normal = normal.normalized(); +} + +bool KX_PlanarMap::SetupCamera(KX_Camera *sceneCamera, KX_Camera *camera) +{ + KX_GameObject *mirror = GetViewpointObject(); + + // Compute camera position and orientation. + const MT_Matrix3x3& mirrorObjWorldOri = mirror->NodeGetWorldOrientation(); + const MT_Vector3& mirrorObjWorldPos = mirror->NodeGetWorldPosition(); + + MT_Vector3 cameraWorldPos = sceneCamera->NodeGetWorldPosition(); + + // Update clip plane to possible new normal or viewpoint object. + ComputeClipPlane(mirrorObjWorldPos, mirrorObjWorldOri); + + const float d = m_clipPlane.x() * cameraWorldPos.x() + + m_clipPlane.y() * cameraWorldPos.y() + + m_clipPlane.z() * cameraWorldPos.z() + + m_clipPlane.w(); + + // Check if the scene camera is in the right plane side. + if (d < 0.0) { + return false; + } + + const MT_Matrix3x3 mirrorObjWorldOriInverse = mirrorObjWorldOri.inverse(); + MT_Matrix3x3 cameraWorldOri = sceneCamera->NodeGetWorldOrientation(); + + static const MT_Matrix3x3 unmir(1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, -1.0f); + + if (m_type == REFLECTION) { + // Get vector from mirror to camera in mirror space. + cameraWorldPos = (cameraWorldPos - mirrorObjWorldPos) * mirrorObjWorldOri; + + cameraWorldPos = mirrorObjWorldPos + cameraWorldPos * unmir * mirrorObjWorldOriInverse; + cameraWorldOri.transpose(); + cameraWorldOri = cameraWorldOri * mirrorObjWorldOri * unmir * mirrorObjWorldOriInverse; + cameraWorldOri.transpose(); + } + + // Set render camera position and orientation. + camera->NodeSetWorldPosition(cameraWorldPos); + camera->NodeSetGlobalOrientation(cameraWorldOri); + + return true; +} + +bool KX_PlanarMap::SetupCameraFace(KX_Camera *camera, unsigned short index) +{ + return true; +} + +#ifdef WITH_PYTHON + +PyTypeObject KX_PlanarMap::Type = { + PyVarObject_HEAD_INIT(nullptr, 0) + "KX_PlanarMap", + sizeof(PyObjectPlus_Proxy), + 0, + py_base_dealloc, + 0, + 0, + 0, + 0, + py_base_repr, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + 0, 0, 0, 0, 0, 0, 0, + Methods, + 0, + 0, + &KX_TextureRenderer::Type, + 0, 0, 0, 0, 0, 0, + py_base_new +}; + +PyMethodDef KX_PlanarMap::Methods[] = { + {nullptr, nullptr} // Sentinel +}; + +PyAttributeDef KX_PlanarMap::Attributes[] = { + KX_PYATTRIBUTE_RW_FUNCTION("normal", KX_PlanarMap, pyattr_get_normal, pyattr_set_normal), + KX_PYATTRIBUTE_NULL // Sentinel +}; + +PyObject *KX_PlanarMap::pyattr_get_normal(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef) +{ + KX_PlanarMap *self = static_cast(self_v); + return PyObjectFrom(self->GetNormal()); +} + +int KX_PlanarMap::pyattr_set_normal(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) +{ + KX_PlanarMap *self = static_cast(self_v); + + MT_Vector3 normal; + if (!PyVecTo(value, normal)) { + return PY_SET_ATTR_FAIL; + } + + self->SetNormal(normal); + + return PY_SET_ATTR_SUCCESS; +} + +#endif // WITH_PYTHON diff --git a/source/gameengine/Ketsji/KX_PlanarMap.h b/source/gameengine/Ketsji/KX_PlanarMap.h new file mode 100644 index 000000000000..6161e0042cde --- /dev/null +++ b/source/gameengine/Ketsji/KX_PlanarMap.h @@ -0,0 +1,78 @@ +/* +* ***** BEGIN GPL LICENSE BLOCK ***** +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +* Contributor(s): Ulysse Martin, Tristan Porteries, Martins Upitis. +* +* ***** END GPL LICENSE BLOCK ***** +*/ + +/** \file KX_PlanarMap.h +* \ingroup ketsji +*/ + +#ifndef __KX_PLANAR_H__ +#define __KX_PLANAR_H__ + +#include "KX_TextureRenderer.h" + +#include + +class KX_PlanarMap : public KX_TextureRenderer +{ + Py_Header + +private: + /// Mirror normal vector. + MT_Vector3 m_normal; + /// Clip plane equation values. + MT_Vector4 m_clipPlane; + + std::unordered_map m_projections; + + enum Type { + REFLECTION, + REFRACTION + } m_type; + +public: + KX_PlanarMap(EnvMap *env, KX_GameObject *viewpoint); + virtual ~KX_PlanarMap(); + + virtual std::string GetName(); + + void ComputeClipPlane(const MT_Vector3& mirrorObjWorldPos, const MT_Matrix3x3& mirrorObjWorldOri); + + virtual void InvalidateProjectionMatrix(); + virtual const MT_Matrix4x4& GetProjectionMatrix(RAS_IRasterizer *rasty, KX_Scene *scene, KX_Camera *sceneCamera, + const RAS_Rect& viewport, const RAS_Rect& area); + + virtual void BeginRenderFace(RAS_IRasterizer *rasty) override; + virtual void EndRenderFace(RAS_IRasterizer *rasty) override; + + const MT_Vector3& GetNormal() const; + void SetNormal(const MT_Vector3& normal); + + virtual bool SetupCamera(KX_Camera *sceneCamera, KX_Camera *camera); + virtual bool SetupCameraFace(KX_Camera *camera, unsigned short index); + +#ifdef WITH_PYTHON + static PyObject *pyattr_get_normal(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef); + static int pyattr_set_normal(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); +#endif // WITH_PYTHON +}; + +#endif // __KX_PLANAR_H__ diff --git a/source/gameengine/Ketsji/KX_PythonInitTypes.cpp b/source/gameengine/Ketsji/KX_PythonInitTypes.cpp index 55be088f8b31..90818f4b9bac 100644 --- a/source/gameengine/Ketsji/KX_PythonInitTypes.cpp +++ b/source/gameengine/Ketsji/KX_PythonInitTypes.cpp @@ -68,6 +68,7 @@ #include "KX_NetworkMessageSensor.h" #include "KX_ObjectActuator.h" #include "KX_ParentActuator.h" +#include "KX_PlanarMap.h" #include "KX_PolyProxy.h" #include "KX_PythonComponent.h" #include "KX_SCA_AddObjectActuator.h" @@ -246,6 +247,7 @@ PyMODINIT_FUNC initGameTypesPythonBinding(void) PyType_Ready_Attr(dict, KX_NetworkMessageSensor, init_getset); PyType_Ready_Attr(dict, KX_ObjectActuator, init_getset); PyType_Ready_Attr(dict, KX_ParentActuator, init_getset); + PyType_Ready_Attr(dict, KX_PlanarMap, init_getset); PyType_Ready_Attr(dict, KX_PolyProxy, init_getset); PyType_Ready_Attr(dict, KX_PythonComponent, init_getset); PyType_Ready_Attr(dict, KX_RadarSensor, init_getset); @@ -262,6 +264,7 @@ PyMODINIT_FUNC initGameTypesPythonBinding(void) PyType_Ready_Attr(dict, KX_StateActuator, init_getset); PyType_Ready_Attr(dict, KX_SteeringActuator, init_getset); PyType_Ready_Attr(dict, KX_CollisionSensor, init_getset); + PyType_Ready_Attr(dict, KX_TextureRenderer, init_getset); PyType_Ready_Attr(dict, KX_TrackToActuator, init_getset); PyType_Ready_Attr(dict, KX_VehicleWrapper, init_getset); PyType_Ready_Attr(dict, KX_VertexProxy, init_getset); diff --git a/source/gameengine/Ketsji/KX_Scene.cpp b/source/gameengine/Ketsji/KX_Scene.cpp index 8840ed527737..462622148c77 100644 --- a/source/gameengine/Ketsji/KX_Scene.cpp +++ b/source/gameengine/Ketsji/KX_Scene.cpp @@ -62,9 +62,7 @@ #include "RAS_IRasterizer.h" #include "RAS_ICanvas.h" #include "RAS_2DFilterData.h" -#include "RAS_CubeMap.h" #include "KX_2DFilterManager.h" -#include "KX_CubeMapManager.h" #include "RAS_BoundingBoxManager.h" #include "RAS_BucketManager.h" @@ -198,7 +196,7 @@ KX_Scene::KX_Scene(SCA_IInputDevice *inputDevice, m_rootnode = nullptr; - m_cubeMapManager = new KX_CubeMapManager(this); + m_rendererManager = new KX_TextureRendererManager(this); m_bucketmanager=new RAS_BucketManager(); m_boundingBoxManager = new RAS_BoundingBoxManager(); @@ -283,9 +281,9 @@ KX_Scene::~KX_Scene() if (m_networkScene) delete m_networkScene; - - if (m_cubeMapManager) { - delete m_cubeMapManager; + + if (m_rendererManager) { + delete m_rendererManager; } if (m_bucketmanager) @@ -327,9 +325,9 @@ RAS_BucketManager* KX_Scene::GetBucketManager() return m_bucketmanager; } -KX_CubeMapManager *KX_Scene::GetCubeMapManager() +KX_TextureRendererManager *KX_Scene::GetTextureRendererManager() const { - return m_cubeMapManager; + return m_rendererManager; } RAS_BoundingBoxManager *KX_Scene::GetBoundingBoxManager() @@ -1109,7 +1107,7 @@ int KX_Scene::NewRemoveObject(class CValue* gameobj) newobj->RemoveMeshes(); - m_cubeMapManager->InvalidateCubeMapViewpoint(newobj); + m_rendererManager->InvalidateViewpoint(newobj); ret = 1; if (newobj->GetGameObjectType()==SCA_IObject::OBJ_LIGHT && m_lightlist->RemoveValue(newobj)) @@ -1716,9 +1714,10 @@ void KX_Scene::RenderBuckets(const MT_Transform& cameratransform, RAS_IRasterize KX_BlenderMaterial::EndFrame(rasty); } -void KX_Scene::RenderCubeMaps(RAS_IRasterizer *rasty) +void KX_Scene::RenderTextureRenderers(KX_TextureRendererManager::RendererCategory category, RAS_IRasterizer *rasty, RAS_OffScreen *offScreen, KX_Camera *camera, + const RAS_Rect& viewport, const RAS_Rect& area) { - m_cubeMapManager->Render(rasty); + m_rendererManager->Render(category, rasty, offScreen, camera, viewport, area); } void KX_Scene::UpdateObjectLods(KX_Camera *cam) @@ -1981,7 +1980,7 @@ bool KX_Scene::MergeScene(KX_Scene *other) GetBucketManager()->MergeBucketManager(other->GetBucketManager(), this); GetBoundingBoxManager()->Merge(other->GetBoundingBoxManager()); - GetCubeMapManager()->Merge(other->GetCubeMapManager()); + GetTextureRendererManager()->Merge(other->GetTextureRendererManager()); /* active + inactive == all ??? - lets hope so */ for (CListValue::iterator it = other->GetObjectList()->GetBegin(), end = other->GetObjectList()->GetEnd(); it != end; ++it) { diff --git a/source/gameengine/Ketsji/KX_Scene.h b/source/gameengine/Ketsji/KX_Scene.h index 608def61ffe7..c6b6a6d01ff2 100644 --- a/source/gameengine/Ketsji/KX_Scene.h +++ b/source/gameengine/Ketsji/KX_Scene.h @@ -34,6 +34,7 @@ #include "KX_PhysicsEngineEnums.h" +#include "KX_TextureRendererManager.h" // For KX_TextureRendererManager::RendererCategory. #include #include @@ -73,7 +74,6 @@ class KX_WorldInfo; class KX_Camera; class KX_GameObject; class KX_LightObject; -class KX_CubeMapManager; class RAS_BoundingBoxManager; class RAS_BucketManager; class RAS_MaterialBucket; @@ -123,7 +123,7 @@ class KX_Scene : public CValue, public SCA_IScene }; protected: - KX_CubeMapManager *m_cubeMapManager; + KX_TextureRendererManager *m_rendererManager; RAS_BucketManager* m_bucketmanager; /// Manager used to update all the mesh bounding box. @@ -315,11 +315,12 @@ class KX_Scene : public CValue, public SCA_IScene ~KX_Scene(); RAS_BucketManager* GetBucketManager(); - KX_CubeMapManager *GetCubeMapManager(); + KX_TextureRendererManager *GetTextureRendererManager() const; RAS_BoundingBoxManager *GetBoundingBoxManager(); RAS_MaterialBucket* FindBucket(RAS_IPolyMaterial* polymat, bool &bucketCreated); void RenderBuckets(const MT_Transform& cameratransform, RAS_IRasterizer *rasty, RAS_OffScreen *offScreen); - void RenderCubeMaps(RAS_IRasterizer *rasty); + void RenderTextureRenderers(KX_TextureRendererManager::RendererCategory category, RAS_IRasterizer *rasty, RAS_OffScreen *offScreen, + KX_Camera *sceneCamera, const RAS_Rect& viewport, const RAS_Rect& area); /** * Update all transforms according to the scenegraph. diff --git a/source/gameengine/Ketsji/KX_TextureRenderer.cpp b/source/gameengine/Ketsji/KX_TextureRenderer.cpp new file mode 100644 index 000000000000..e09cb37f8b91 --- /dev/null +++ b/source/gameengine/Ketsji/KX_TextureRenderer.cpp @@ -0,0 +1,230 @@ +/* +* ***** BEGIN GPL LICENSE BLOCK ***** +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +* Contributor(s): Ulysse Martin, Tristan Porteries. +* +* ***** END GPL LICENSE BLOCK ***** +*/ + +/** \file KX_TextureRenderer.cpp + * \ingroup ketsji + */ + +#include "KX_TextureRenderer.h" +#include "KX_GameObject.h" +#include "KX_Globals.h" + +#include "DNA_texture_types.h" + +KX_TextureRenderer::KX_TextureRenderer(EnvMap *env, KX_GameObject *viewpoint) + :m_clipStart(env->clipsta), + m_clipEnd(env->clipend), + m_viewpointObject(viewpoint), + m_enabled(true), + m_ignoreLayers(env->notlay), + m_lodDistanceFactor(env->lodfactor), + m_forceUpdate(true) +{ + m_autoUpdate = (env->flag & ENVMAP_AUTO_UPDATE) != 0; +} + +KX_TextureRenderer::~KX_TextureRenderer() +{ +} + +std::string KX_TextureRenderer::GetName() +{ + return "KX_TextureRenderer"; +} + +KX_GameObject *KX_TextureRenderer::GetViewpointObject() const +{ + return m_viewpointObject; +} + +void KX_TextureRenderer::SetViewpointObject(KX_GameObject *gameobj) +{ + m_viewpointObject = gameobj; +} + +bool KX_TextureRenderer::GetEnabled() const +{ + return m_enabled; +} + +int KX_TextureRenderer::GetIgnoreLayers() const +{ + return m_ignoreLayers; +} + +float KX_TextureRenderer::GetClipStart() const +{ + return m_clipStart; +} + +float KX_TextureRenderer::GetClipEnd() const +{ + return m_clipEnd; +} + +void KX_TextureRenderer::SetClipStart(float start) +{ + m_clipStart = start; +} + +void KX_TextureRenderer::SetClipEnd(float end) +{ + m_clipEnd = end; +} + +float KX_TextureRenderer::GetLodDistanceFactor() const +{ + return m_lodDistanceFactor; +} + +void KX_TextureRenderer::SetLodDistanceFactor(float lodfactor) +{ + m_lodDistanceFactor = lodfactor; +} + +bool KX_TextureRenderer::NeedUpdate() +{ + bool result = m_autoUpdate || m_forceUpdate; + // Disable the force update for the next render. + m_forceUpdate = false; + + return result; +} + +#ifdef WITH_PYTHON + +PyTypeObject KX_TextureRenderer::Type = { + PyVarObject_HEAD_INIT(nullptr, 0) + "KX_TextureRenderer", + sizeof(PyObjectPlus_Proxy), + 0, + py_base_dealloc, + 0, + 0, + 0, + 0, + py_base_repr, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + 0, 0, 0, 0, 0, 0, 0, + Methods, + 0, + 0, + &CValue::Type, + 0, 0, 0, 0, 0, 0, + py_base_new +}; + +PyMethodDef KX_TextureRenderer::Methods[] = { + KX_PYMETHODTABLE_NOARGS(KX_TextureRenderer, update), + {nullptr, nullptr} // Sentinel +}; + +PyAttributeDef KX_TextureRenderer::Attributes[] = { + KX_PYATTRIBUTE_RW_FUNCTION("viewpointObject", KX_TextureRenderer, pyattr_get_viewpoint_object, pyattr_set_viewpoint_object), + KX_PYATTRIBUTE_BOOL_RW("autoUpdate", KX_TextureRenderer, m_autoUpdate), + KX_PYATTRIBUTE_BOOL_RW("enabled", KX_TextureRenderer, m_enabled), + KX_PYATTRIBUTE_INT_RW("ignoreLayers", 0, (1 << 20) - 1, true, KX_TextureRenderer, m_ignoreLayers), + KX_PYATTRIBUTE_RW_FUNCTION("clipStart", KX_TextureRenderer, pyattr_get_clip_start, pyattr_set_clip_start), + KX_PYATTRIBUTE_RW_FUNCTION("clipEnd", KX_TextureRenderer, pyattr_get_clip_end, pyattr_set_clip_end), + KX_PYATTRIBUTE_FLOAT_RW("lodDistanceFactor", 0.0f, FLT_MAX, KX_TextureRenderer, m_lodDistanceFactor), + KX_PYATTRIBUTE_NULL // Sentinel +}; + +KX_PYMETHODDEF_DOC_NOARGS(KX_TextureRenderer, update, "update(): Set the texture rendered to be updated next frame.\n") +{ + m_forceUpdate = true; + Py_RETURN_NONE; +} + +PyObject *KX_TextureRenderer::pyattr_get_viewpoint_object(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef) +{ + KX_TextureRenderer *self = static_cast(self_v); + KX_GameObject *gameobj = self->GetViewpointObject(); + if (gameobj) { + return gameobj->GetProxy(); + } + Py_RETURN_NONE; +} + +int KX_TextureRenderer::pyattr_set_viewpoint_object(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) +{ + KX_TextureRenderer *self = static_cast(self_v); + KX_GameObject *gameobj = nullptr; + + SCA_LogicManager *logicmgr = KX_GetActiveScene()->GetLogicManager(); + + if (!ConvertPythonToGameObject(logicmgr, value, &gameobj, true, "renderer.object = value: KX_TextureRenderer")) + return PY_SET_ATTR_FAIL; + + self->SetViewpointObject(gameobj); + return PY_SET_ATTR_SUCCESS; +} + + +PyObject *KX_TextureRenderer::pyattr_get_clip_start(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef) +{ + KX_TextureRenderer *self = static_cast(self_v); + return PyFloat_FromDouble(self->GetClipStart()); +} + +int KX_TextureRenderer::pyattr_set_clip_start(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) +{ + KX_TextureRenderer *self = static_cast(self_v); + + const float val = PyFloat_AsDouble(value); + + if (val <= 0.0f) { + PyErr_SetString(PyExc_AttributeError, "cubeMap.clipStart = float: KX_TextureRenderer, expected a float grater than zero"); + return PY_SET_ATTR_FAIL; + } + + self->SetClipStart(val); + self->InvalidateProjectionMatrix(); + + return PY_SET_ATTR_SUCCESS; +} + +PyObject *KX_TextureRenderer::pyattr_get_clip_end(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef) +{ + KX_TextureRenderer *self = static_cast(self_v); + return PyFloat_FromDouble(self->GetClipEnd()); +} + +int KX_TextureRenderer::pyattr_set_clip_end(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) +{ + KX_TextureRenderer *self = static_cast(self_v); + + const float val = PyFloat_AsDouble(value); + + if (val <= 0.0f) { + PyErr_SetString(PyExc_AttributeError, "cubeMap.clipEnd = float: KX_TextureRenderer, expected a float grater than zero"); + return PY_SET_ATTR_FAIL; + } + + self->SetClipEnd(val); + self->InvalidateProjectionMatrix(); + + return PY_SET_ATTR_SUCCESS; +} + +#endif // WITH_PYTHON diff --git a/source/gameengine/Ketsji/KX_TextureRenderer.h b/source/gameengine/Ketsji/KX_TextureRenderer.h new file mode 100644 index 000000000000..9ff711d0e58c --- /dev/null +++ b/source/gameengine/Ketsji/KX_TextureRenderer.h @@ -0,0 +1,117 @@ +/* +* ***** BEGIN GPL LICENSE BLOCK ***** +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +* Contributor(s): Ulysse Martin, Tristan Porteries. +* +* ***** END GPL LICENSE BLOCK ***** +*/ + +/** \file KX_TextureRenderer.h + * \ingroup ketsji + */ + +#ifndef __KX_TEXTURE_RENDERER_H__ +#define __KX_TEXTURE_RENDERER_H__ + +#include "EXP_Value.h" +#include "RAS_TextureRenderer.h" + +#include "MT_Matrix4x4.h" + +class KX_GameObject; +class KX_Camera; +class KX_Scene; +class RAS_Rect; + +struct EnvMap; + +class KX_TextureRenderer : public CValue, public RAS_TextureRenderer +{ + Py_Header + +protected: + /// View clip start. + float m_clipStart; + /// View clip end. + float m_clipEnd; + +private: + /// The object used to render from its position. + KX_GameObject *m_viewpointObject; + + /// The texture renderer is enabled for render. + bool m_enabled; + /// Layers to ignore during render. + int m_ignoreLayers; + + + /// Distance factor for level of detail. + float m_lodDistanceFactor; + + /// True if the renderer is updated every frame. + bool m_autoUpdate; + /** True if the renderer need to be updated for the next frame. + * Generally used when m_autoUpdate is to false. + */ + bool m_forceUpdate; + +public: + KX_TextureRenderer(EnvMap *env, KX_GameObject *viewpoint); + virtual ~KX_TextureRenderer(); + + virtual std::string GetName(); + + KX_GameObject *GetViewpointObject() const; + void SetViewpointObject(KX_GameObject *gameobj); + + virtual void InvalidateProjectionMatrix() = 0; + + float GetLodDistanceFactor() const; + void SetLodDistanceFactor(float lodfactor); + + virtual const MT_Matrix4x4& GetProjectionMatrix(RAS_IRasterizer *rasty, KX_Scene *scene, KX_Camera *sceneCamera, + const RAS_Rect& viewport, const RAS_Rect& area) = 0; + + bool GetEnabled() const; + int GetIgnoreLayers() const; + + float GetClipStart() const; + float GetClipEnd() const; + void SetClipStart(float start); + void SetClipEnd(float end); + + // Return true when the texture renderer need to be updated. + bool NeedUpdate(); + + /// Setup camera position and orientation shared by all the faces, returns true when the render will be made. + virtual bool SetupCamera(KX_Camera *sceneCamera, KX_Camera *camera) = 0; + /// Setup camera position and orientation unique per faces, returns true when the render will be made. + virtual bool SetupCameraFace(KX_Camera *camera, unsigned short index) = 0; + +#ifdef WITH_PYTHON + KX_PYMETHOD_DOC_NOARGS(KX_TextureRenderer, update); + + static PyObject *pyattr_get_viewpoint_object(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef); + static int pyattr_set_viewpoint_object(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); + static PyObject *pyattr_get_clip_start(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef); + static int pyattr_set_clip_start(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); + static PyObject *pyattr_get_clip_end(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef); + static int pyattr_set_clip_end(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); +#endif +}; + +#endif // __KX_TEXTURE_RENDERER_H__ diff --git a/source/gameengine/Ketsji/KX_TextureRendererManager.cpp b/source/gameengine/Ketsji/KX_TextureRendererManager.cpp new file mode 100644 index 000000000000..63c9871b4a78 --- /dev/null +++ b/source/gameengine/Ketsji/KX_TextureRendererManager.cpp @@ -0,0 +1,234 @@ +/* +* ***** BEGIN GPL LICENSE BLOCK ***** +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +* Contributor(s): Ulysse Martin, Tristan Porteries, Martins Upitis. +* +* ***** END GPL LICENSE BLOCK ***** +*/ + +/** \file gameengine/Ketsji/KX_TextureRendererManager.cpp + * \ingroup ketsji + */ + +#include "KX_TextureRendererManager.h" +#include "KX_Camera.h" +#include "KX_Scene.h" +#include "KX_Globals.h" +#include "KX_CubeMap.h" +#include "KX_PlanarMap.h" + +#include "RAS_IRasterizer.h" +#include "RAS_OffScreen.h" +#include "RAS_Texture.h" + +#include "DNA_texture_types.h" + +#include "CM_Message.h" + +KX_TextureRendererManager::KX_TextureRendererManager(KX_Scene *scene) + :m_scene(scene) +{ + const RAS_CameraData& camdata = RAS_CameraData(); + m_camera = new KX_Camera(m_scene, KX_Scene::m_callbacks, camdata, true, true); + m_camera->SetName("__renderer_cam__"); +} + +KX_TextureRendererManager::~KX_TextureRendererManager() +{ + for (unsigned short i = 0; i < CATEGORY_MAX; ++i) { + for (KX_TextureRenderer *renderer : m_renderers[i]) { + delete renderer; + } + } + + m_camera->Release(); +} + +void KX_TextureRendererManager::InvalidateViewpoint(KX_GameObject *gameobj) +{ + for (unsigned short i = 0; i < CATEGORY_MAX; ++i) { + for (KX_TextureRenderer *renderer : m_renderers[i]) { + if (renderer->GetViewpointObject() == gameobj) { + renderer->SetViewpointObject(nullptr); + } + } + } +} + +void KX_TextureRendererManager::AddRenderer(RendererType type, RAS_Texture *texture, KX_GameObject *viewpoint) +{ + /* Don't Add renderer several times for the same texture. If the texture is shared by several objects, + * we just add a "textureUser" to signal that the renderer texture will be shared by several objects. + */ + for (unsigned short i = 0; i < CATEGORY_MAX; ++i) { + for (KX_TextureRenderer *renderer : m_renderers[i]) { + if (renderer->EqualTextureUser(texture)) { + renderer->AddTextureUser(texture); + + KX_GameObject *origviewpoint = renderer->GetViewpointObject(); + if (viewpoint != origviewpoint) { + CM_Warning("texture renderer (" << texture->GetName() << ") uses different viewpoint objects (" << + (origviewpoint ? origviewpoint->GetName() : "") << " and " << viewpoint->GetName() << ")."); + } + return; + } + } + } + + EnvMap *env = texture->GetTex()->env; + KX_TextureRenderer *renderer; + switch (type) { + case CUBE: + { + renderer = new KX_CubeMap(env, viewpoint); + m_renderers[VIEWPORT_INDEPENDENT].push_back(renderer); + break; + } + case PLANAR: + { + renderer = new KX_PlanarMap(env, viewpoint); + m_renderers[VIEWPORT_DEPENDENT].push_back(renderer); + break; + } + } + + renderer->AddTextureUser(texture); +} + +bool KX_TextureRendererManager::RenderRenderer(RAS_IRasterizer *rasty, KX_TextureRenderer *renderer, + KX_Camera *sceneCamera, const RAS_Rect& viewport, const RAS_Rect& area) +{ + KX_GameObject *viewpoint = renderer->GetViewpointObject(); + // Doesn't need (or can) update. + if (!renderer->NeedUpdate() || !renderer->GetEnabled() || !viewpoint) { + return false; + } + + // Set camera setting shared by all the renderer's faces. + if (!renderer->SetupCamera(sceneCamera, m_camera)) { + return false; + } + + const bool visible = viewpoint->GetVisible(); + /* We hide the viewpoint object in the case backface culling is disabled -> we can't see through + * the object faces if the camera is inside the gameobject. + */ + viewpoint->SetVisible(false, false); + + // Set camera lod distance factor from renderer value. + m_camera->SetLodDistanceFactor(renderer->GetLodDistanceFactor()); + + /* When we update clipstart or clipend values, + * or if the projection matrix is not computed yet, + * we have to compute projection matrix. + */ + const MT_Matrix4x4& projmat = renderer->GetProjectionMatrix(rasty, m_scene, sceneCamera, viewport, area); + m_camera->SetProjectionMatrix(projmat); + rasty->SetProjectionMatrix(projmat); + + // Begin rendering stuff + renderer->BeginRender(rasty); + + for (unsigned short i = 0; i < renderer->GetNumFaces(); ++i) { + // Set camera settings unique per faces. + if (!renderer->SetupCameraFace(m_camera, i)) { + continue; + } + + m_camera->NodeUpdateGS(0.0f); + + renderer->BindFace(i); + + const MT_Transform camtrans(m_camera->GetWorldToCamera()); + const MT_Matrix4x4 viewmat(camtrans); + + rasty->SetViewMatrix(viewmat, m_camera->NodeGetWorldOrientation(), m_camera->NodeGetWorldPosition(), MT_Vector3(1.0f, 1.0f, 1.0f), m_camera->GetCameraData()->m_perspective); + m_camera->SetModelviewMatrix(viewmat); + + m_scene->CalculateVisibleMeshes(rasty, m_camera, ~renderer->GetIgnoreLayers()); + + /* Updating the lod per face is normally not expensive because a cube map normally show every objects + * but here we update only visible object of a face including the clip end and start. + */ + m_scene->UpdateObjectLods(m_camera); + + /* Update animations to use the culling of each faces, BL_ActionManager avoid redundants + * updates internally. */ + KX_GetActiveEngine()->UpdateAnimations(m_scene); + + renderer->BeginRenderFace(rasty); + + // Now the objects are culled and we can render the scene. + m_scene->GetWorldInfo()->RenderBackground(rasty); + // Send a nullptr off screen because we use a set of FBO with shared textures, not an off screen. + m_scene->RenderBuckets(camtrans, rasty, nullptr); + + renderer->EndRenderFace(rasty); + } + + viewpoint->SetVisible(visible, false); + + renderer->EndRender(rasty); + + return true; +} + +void KX_TextureRendererManager::Render(RendererCategory category, RAS_IRasterizer *rasty, RAS_OffScreen *offScreen, + KX_Camera *sceneCamera, const RAS_Rect& viewport, const RAS_Rect& area) +{ + const std::vector& renderers = m_renderers[category]; + if (renderers.size() == 0 || rasty->GetDrawingMode() != RAS_IRasterizer::RAS_TEXTURED) { + return; + } + + const RAS_IRasterizer::DrawType drawmode = rasty->GetDrawingMode(); + rasty->SetDrawingMode(RAS_IRasterizer::RAS_RENDERER); + + // Disable scissor to not bother with scissor box. + rasty->Disable(RAS_IRasterizer::RAS_SCISSOR_TEST); + + // Copy current stereo mode. + const RAS_IRasterizer::StereoMode steremode = rasty->GetStereoMode(); + // Disable stereo for realtime renderer. + rasty->SetStereoMode(RAS_IRasterizer::RAS_STEREO_NOSTEREO); + + // Check if at least one renderer was rendered. + bool rendered = false; + for (KX_TextureRenderer *renderer : renderers) { + rendered |= RenderRenderer(rasty, renderer, sceneCamera, viewport, area); + } + + // Restore previous stereo mode. + rasty->SetStereoMode(steremode); + + rasty->Enable(RAS_IRasterizer::RAS_SCISSOR_TEST); + + rasty->SetDrawingMode(drawmode); + + if (offScreen && rendered) { + // Restore the off screen bound before rendering the texture renderers. + offScreen->Bind(); + } +} + +void KX_TextureRendererManager::Merge(KX_TextureRendererManager *other) +{ + for (unsigned short i = 0; i < CATEGORY_MAX; ++i) { + m_renderers[i].insert(m_renderers[i].end(), other->m_renderers[i].begin(), other->m_renderers[i].end()); + other->m_renderers[i].clear(); + } +} diff --git a/source/gameengine/Ketsji/KX_TextureRendererManager.h b/source/gameengine/Ketsji/KX_TextureRendererManager.h new file mode 100644 index 000000000000..b10de646247d --- /dev/null +++ b/source/gameengine/Ketsji/KX_TextureRendererManager.h @@ -0,0 +1,95 @@ +/* +* ***** BEGIN GPL LICENSE BLOCK ***** +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +* Contributor(s): Ulysse Martin, Tristan Porteries, Martins Upitis. +* +* ***** END GPL LICENSE BLOCK ***** +*/ + +/** \file KX_TextureRendererManager.h +* \ingroup ketsji +*/ + +#ifndef __KX_TEXTURE_RENDERER_MANAGER_H__ +#define __KX_TEXTURE_RENDERER_MANAGER_H__ + +#include + +class KX_GameObject; +class KX_Camera; +class KX_Scene; +class KX_TextureRenderer; + +class RAS_IRasterizer; +class RAS_OffScreen; +class RAS_Texture; +class RAS_Rect; + +class KX_TextureRendererManager +{ +public: + enum RendererCategory { + VIEWPORT_DEPENDENT = 0, + VIEWPORT_INDEPENDENT, + CATEGORY_MAX + }; + +private: + /// All existing renderers of this scene by categories. + std::vector m_renderers[CATEGORY_MAX]; + /// The camera used for renderers render, it's own by the renderer manager. + KX_Camera *m_camera; + /// The scene we are rendering for. + KX_Scene *m_scene; + + /// Render a texture renderer, return true if the render was proceeded. + bool RenderRenderer(RAS_IRasterizer *rasty, KX_TextureRenderer *renderer, + KX_Camera *sceneCamera, const RAS_Rect& viewport, const RAS_Rect& area); + +public: + enum RendererType { + CUBE, + PLANAR + }; + + KX_TextureRendererManager(KX_Scene *scene); + virtual ~KX_TextureRendererManager(); + + /// Invalidate renderers using the given game object as viewpoint object. + void InvalidateViewpoint(KX_GameObject *gameobj); + + /** Add and create a renderer if none existing renderer was using the same + * texture containing in the material texture passed. + */ + void AddRenderer(RendererType type, RAS_Texture *texture, KX_GameObject *viewpoint); + + /** Execute all the texture renderer. + * \param category The category of renderers to render. + * \param offScreen The off screen bound before rendering the texture renderers. + * \param sceneCamera The scene camera currently rendering the scene, used only in case of + * VIEWPORT_DEPENDENT category. + * \param viewport The viewport render area. + * \param area The windows render area. + */ + void Render(RendererCategory category, RAS_IRasterizer *rasty, RAS_OffScreen *offScreen, + KX_Camera *sceneCamera, const RAS_Rect& viewport, const RAS_Rect& area); + + /// Merge the content of an other renderer manager, used during lib loading. + void Merge(KX_TextureRendererManager *other); +}; + +#endif // __KX_TEXTURE_RENDERER_MANAGER_H__ diff --git a/source/gameengine/Rasterizer/CMakeLists.txt b/source/gameengine/Rasterizer/CMakeLists.txt index a0a796992f9f..d951aee404e0 100644 --- a/source/gameengine/Rasterizer/CMakeLists.txt +++ b/source/gameengine/Rasterizer/CMakeLists.txt @@ -60,7 +60,6 @@ set(SRC RAS_BoundingBox.cpp RAS_BoundingBoxManager.cpp RAS_BucketManager.cpp - RAS_CubeMap.cpp RAS_DisplayArrayBucket.cpp RAS_FramingManager.cpp RAS_ICanvas.cpp @@ -78,6 +77,7 @@ set(SRC RAS_Polygon.cpp RAS_Shader.cpp RAS_Texture.cpp + RAS_TextureRenderer.cpp RAS_TextUser.cpp RAS_2DFilterData.h @@ -90,7 +90,6 @@ set(SRC RAS_BoundingBoxManager.h RAS_BucketManager.h RAS_CameraData.h - RAS_CubeMap.h RAS_Deformer.h RAS_DisplayArray.h RAS_DisplayArrayBucket.h @@ -115,6 +114,7 @@ set(SRC RAS_Rect.h RAS_Shader.h RAS_Texture.h + RAS_TextureRenderer.h RAS_TextUser.h RAS_TexVert.h ) diff --git a/source/gameengine/Rasterizer/RAS_BucketManager.cpp b/source/gameengine/Rasterizer/RAS_BucketManager.cpp index 0292bbd22006..3b935d8506df 100644 --- a/source/gameengine/Rasterizer/RAS_BucketManager.cpp +++ b/source/gameengine/Rasterizer/RAS_BucketManager.cpp @@ -273,7 +273,7 @@ void RAS_BucketManager::Renderbuckets(const MT_Transform& cameratrans, RAS_IRast rasty->SetDepthMask(RAS_IRasterizer::RAS_DEPTHMASK_ENABLED); break; } - case RAS_IRasterizer::RAS_CUBEMAP: + case RAS_IRasterizer::RAS_RENDERER: { /* Rendering solid and alpha (regular and instancing) materials * with their shaders. @@ -286,6 +286,7 @@ void RAS_BucketManager::Renderbuckets(const MT_Transform& cameratrans, RAS_IRast rasty->SetDepthMask(RAS_IRasterizer::RAS_DEPTHMASK_DISABLED); + // Don't use depth transparency because the renderer could not offer a depth texture. rasty->ResetGlobalDepthTexture(); RenderBasicBuckets(cameratrans, rasty, ALPHA_DEPTH_INSTANCING_BUCKET); RenderSortedBuckets(cameratrans, rasty, ALPHA_DEPTH_BUCKET); diff --git a/source/gameengine/Rasterizer/RAS_CubeMap.cpp b/source/gameengine/Rasterizer/RAS_CubeMap.cpp deleted file mode 100644 index 2b5b8eafac9d..000000000000 --- a/source/gameengine/Rasterizer/RAS_CubeMap.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/* -* ***** BEGIN GPL LICENSE BLOCK ***** -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* as published by the Free Software Foundation; either version 2 -* of the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software Foundation, -* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -* -* Contributor(s): Ulysse Martin, Tristan Porteries. -* -* ***** END GPL LICENSE BLOCK ***** -*/ - -/** \file RAS_CubeMap.cpp -* \ingroup bgerast -*/ - -#include "RAS_CubeMap.h" -#include "RAS_Texture.h" -#include "RAS_IRasterizer.h" - -#include "GPU_texture.h" -#include "GPU_framebuffer.h" -#include "GPU_draw.h" - -#include "BKE_image.h" - -#include "DNA_texture_types.h" - -#include "glew-mx.h" - -static const MT_Matrix3x3 topFaceViewMat( - 1.0f, 0.0f, 0.0f, - 0.0f, -1.0f, 0.0f, - 0.0f, 0.0f, -1.0f); - -static const MT_Matrix3x3 bottomFaceViewMat( - -1.0f, 0.0f, 0.0f, - 0.0f, -1.0f, 0.0f, - 0.0f, 0.0f, 1.0f); - -static const MT_Matrix3x3 frontFaceViewMat( - 0.0f, 0.0f, -1.0f, - 0.0f, -1.0f, 0.0f, - -1.0f, 0.0f, 0.0f); - -static const MT_Matrix3x3 backFaceViewMat( - 0.0f, 0.0f, 1.0f, - 0.0f, -1.0f, 0.0f, - 1.0f, 0.0f, 0.0f); - -static const MT_Matrix3x3 rightFaceViewMat( - 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, -1.0f, - 0.0f, 1.0f, 0.0f); - -static const MT_Matrix3x3 leftFaceViewMat( - 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, - 0.0f, -1.0f, 0.0f); - -const MT_Matrix3x3 RAS_CubeMap::faceViewMatrices3x3[RAS_CubeMap::NUM_FACES] = { - topFaceViewMat, - bottomFaceViewMat, - frontFaceViewMat, - backFaceViewMat, - rightFaceViewMat, - leftFaceViewMat -}; - -static const GLenum cubeMapTargets[RAS_CubeMap::NUM_FACES] = { - GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, - GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, - GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, - GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, - GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, - GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, -}; - -RAS_CubeMap::RAS_CubeMap() - :m_gpuTex(nullptr), - m_useMipmap(false) -{ - for (unsigned short i = 0; i < NUM_FACES; ++i) { - m_fbos[i] = nullptr; - m_rbs[i] = nullptr; - } -} - -RAS_CubeMap::~RAS_CubeMap() -{ - DetachTexture(); - - /* This call has for side effect to ask regeneration of all textures - * depending of this image. - */ - for (std::vector::iterator it = m_textureUsers.begin(), end = m_textureUsers.end(); it != end; ++it) { - RAS_Texture *texture = *it; - // Invalidate the cube map in each material texture users. - texture->SetCubeMap(nullptr); - /* Use BKE_image_free_buffers to free the bind code and also the cached frames which - * freed by image_free_cached_frames. - */ - BKE_image_free_buffers(texture->GetImage()); - } -} - -void RAS_CubeMap::AttachTexture() -{ - // Increment reference to make sure the gpu texture will not be freed by someone else. - GPU_texture_ref(m_gpuTex); - - for (unsigned short i = 0; i < NUM_FACES; ++i) { - m_fbos[i] = GPU_framebuffer_create(); - m_rbs[i] = GPU_renderbuffer_create(GPU_texture_width(m_gpuTex), GPU_texture_height(m_gpuTex), - 0, GPU_HDR_NONE, GPU_RENDERBUFFER_DEPTH, nullptr); - - GPU_framebuffer_texture_attach_target(m_fbos[i], m_gpuTex, cubeMapTargets[i], 0, nullptr); - GPU_framebuffer_renderbuffer_attach(m_fbos[i], m_rbs[i], 0, nullptr); - } -} - -void RAS_CubeMap::DetachTexture() -{ - if (!m_gpuTex) { - return; - } - - for (unsigned short i = 0; i < NUM_FACES; ++i) { - if (m_fbos[i]) { - GPU_framebuffer_texture_detach_target(m_gpuTex, cubeMapTargets[i]); - } - if (m_rbs[i]) { - GPU_framebuffer_renderbuffer_detach(m_rbs[i]); - } - - if (m_fbos[i]) { - GPU_framebuffer_free(m_fbos[i]); - m_fbos[i] = nullptr; - } - if (m_rbs[i]) { - GPU_renderbuffer_free(m_rbs[i]); - m_rbs[i] = nullptr; - } - } - - GPU_texture_free(m_gpuTex); -} - -void RAS_CubeMap::GetValidTexture() -{ - BLI_assert(m_textureUsers.size() > 0); - - /* The gpu texture returned by all material textures are the same. - * We can so use the first material texture user. - */ - RAS_Texture *texture = m_textureUsers[0]; - texture->CheckValidTexture(); - GPUTexture *gputex = texture->GetGPUTexture(); - - if (m_gpuTex == gputex) { - // The gpu texture is the same. - return; - } - - DetachTexture(); - - m_gpuTex = gputex; - - AttachTexture(); - - EnvMap *env = texture->GetMTex()->tex->env; - m_useMipmap = (env->filtering == ENVMAP_MIPMAP_MIPMAP) && GPU_get_mipmap(); - - if (!m_useMipmap) { - // Disable mipmaping. - GPU_texture_bind(m_gpuTex, 0); - GPU_texture_filter_mode(m_gpuTex, false, (env->filtering == ENVMAP_MIPMAP_LINEAR), false); - GPU_texture_unbind(m_gpuTex); - } -} - -const std::vector& RAS_CubeMap::GetTextureUsers() const -{ - return m_textureUsers; -} - -void RAS_CubeMap::AddTextureUser(RAS_Texture *texture) -{ - m_textureUsers.push_back(texture); - texture->SetCubeMap(this); -} - -void RAS_CubeMap::BeginRender() -{ - GetValidTexture(); -} - -void RAS_CubeMap::EndRender() -{ - if (m_useMipmap) { - GPU_texture_bind(m_gpuTex, 0); - GPU_texture_generate_mipmap(m_gpuTex); - GPU_texture_unbind(m_gpuTex); - } -} - -void RAS_CubeMap::BindFace(RAS_IRasterizer *rasty, unsigned short index) -{ - GPU_framebuffer_bind_no_save(m_fbos[index], 0); - - rasty->Clear(RAS_IRasterizer::RAS_COLOR_BUFFER_BIT | RAS_IRasterizer::RAS_DEPTH_BUFFER_BIT); -} diff --git a/source/gameengine/Rasterizer/RAS_CubeMap.h b/source/gameengine/Rasterizer/RAS_CubeMap.h deleted file mode 100644 index 4ae40513ec76..000000000000 --- a/source/gameengine/Rasterizer/RAS_CubeMap.h +++ /dev/null @@ -1,87 +0,0 @@ -/* -* ***** BEGIN GPL LICENSE BLOCK ***** -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* as published by the Free Software Foundation; either version 2 -* of the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software Foundation, -* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -* -* Contributor(s): Ulysse Martin, Tristan Porteries. -* -* ***** END GPL LICENSE BLOCK ***** -*/ - -/** \file RAS_CubeMap.h -* \ingroup bgerast -*/ - -#ifndef __RAS_CUBEMAP_H__ -#define __RAS_CUBEMAP_H__ - -#include "MT_Matrix3x3.h" - -#include - -class RAS_Texture; -class RAS_IRasterizer; - -struct GPUFrameBuffer; -struct GPURenderBuffer; -struct GPUTexture; - -class RAS_CubeMap -{ -public: - enum { - NUM_FACES = 6 - }; - -private: - /// Cube map texture to attach to frame buffer objects. - GPUTexture *m_gpuTex; - GPUFrameBuffer *m_fbos[NUM_FACES]; - GPURenderBuffer *m_rbs[NUM_FACES]; - - // True if we regenerate mipmap every render. - bool m_useMipmap; - - /// Recreate and attach frame buffer objects and render buffers to the cube map texture. - void AttachTexture(); - /// Free and detach frame buffer objects and render buffer to the cube map texture. - void DetachTexture(); - /** Obtain the latest cube map texture, if it's not the same as before, - * then detach the previous cube map texture and attach the new one. - */ - void GetValidTexture(); - -protected: - /// All the material texture users. - std::vector m_textureUsers; - -public: - /// Face view matrices in 3x3 matrices. - static const MT_Matrix3x3 faceViewMatrices3x3[NUM_FACES]; - - RAS_CubeMap(); - virtual ~RAS_CubeMap(); - - const std::vector& GetTextureUsers() const; - - void AddTextureUser(RAS_Texture *texture); - - void BeginRender(); - void EndRender(); - - void BindFace(RAS_IRasterizer *rasty, unsigned short index); -}; - -#endif // __RAS_CUBEMAP_H__ diff --git a/source/gameengine/Rasterizer/RAS_IRasterizer.cpp b/source/gameengine/Rasterizer/RAS_IRasterizer.cpp index d3c7c6c8deeb..3954d42879e7 100644 --- a/source/gameengine/Rasterizer/RAS_IRasterizer.cpp +++ b/source/gameengine/Rasterizer/RAS_IRasterizer.cpp @@ -31,6 +31,8 @@ #include "RAS_IRasterizer.h" #include "RAS_OpenGLRasterizer.h" +#include "RAS_IPolygonMaterial.h" +#include "RAS_DisplayArrayBucket.h" #include "RAS_ICanvas.h" #include "RAS_OffScreen.h" @@ -232,7 +234,7 @@ RAS_IRasterizer::RAS_IRasterizer() m_auxilaryClientInfo(nullptr), m_drawingmode(RAS_TEXTURED), m_shadowMode(RAS_SHADOW_NONE), - //m_last_alphablend(GPU_BLEND_SOLID), + m_invertFrontFace(false), m_last_frontface(true), m_overrideShader(RAS_OVERRIDE_SHADER_NONE) { @@ -1124,6 +1126,16 @@ void RAS_IRasterizer::SetCullFace(bool enable) } } +void RAS_IRasterizer::EnableClipPlane(unsigned short index, const MT_Vector4& plane) +{ + m_impl->EnableClipPlane(index, plane); +} + +void RAS_IRasterizer::DisableClipPlane(unsigned short index) +{ + m_impl->DisableClipPlane(index); +} + void RAS_IRasterizer::SetLines(bool enable) { m_impl->SetLines(enable); @@ -1200,8 +1212,8 @@ void RAS_IRasterizer::SetAlphaBlend(int alphablend) void RAS_IRasterizer::SetFrontFace(bool ccw) { - if (m_camnegscale) - ccw = !ccw; + // Invert the front face if the camera has a negative scale or if we force to inverse the front face. + ccw ^= (m_camnegscale || m_invertFrontFace); if (m_last_frontface == ccw) { return; @@ -1212,6 +1224,11 @@ void RAS_IRasterizer::SetFrontFace(bool ccw) m_last_frontface = ccw; } +void RAS_IRasterizer::SetInvertFrontFace(bool invert) +{ + m_invertFrontFace = invert; +} + void RAS_IRasterizer::SetAnisotropicFiltering(short level) { GPU_set_anisotropic((float)level); diff --git a/source/gameengine/Rasterizer/RAS_IRasterizer.h b/source/gameengine/Rasterizer/RAS_IRasterizer.h index 44371d51bedf..ab129fab2a49 100644 --- a/source/gameengine/Rasterizer/RAS_IRasterizer.h +++ b/source/gameengine/Rasterizer/RAS_IRasterizer.h @@ -83,7 +83,7 @@ class RAS_IRasterizer RAS_WIREFRAME = 0, RAS_SOLID, RAS_TEXTURED, - RAS_CUBEMAP, + RAS_RENDERER, RAS_SHADOW, RAS_DRAW_MAX, }; @@ -423,6 +423,7 @@ class RAS_IRasterizer StorageAttribs m_storageAttribs; + bool m_invertFrontFace; bool m_last_frontface; OverrideShaderType m_overrideShader; @@ -688,6 +689,11 @@ class RAS_IRasterizer */ void SetCullFace(bool enable); + /// Set and enable clip plane. + void EnableClipPlane(unsigned short index, const MT_Vector4& plane); + /// Disable clip plane + void DisableClipPlane(unsigned short index); + /** * Sets wireframe mode. */ @@ -801,6 +807,8 @@ class RAS_IRasterizer void SetAlphaBlend(int alphablend); void SetFrontFace(bool ccw); + void SetInvertFrontFace(bool invert); + void SetAnisotropicFiltering(short level); short GetAnisotropicFiltering(); diff --git a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp index 8249530e9b4f..abe72618c855 100644 --- a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp +++ b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp @@ -30,6 +30,7 @@ */ #include "RAS_OpenGLRasterizer.h" +#include "RAS_IPolygonMaterial.h" #include "glew-mx.h" @@ -607,6 +608,18 @@ void RAS_OpenGLRasterizer::SetPolygonOffset(float mult, float add) glPolygonOffset(mult, add); } +void RAS_OpenGLRasterizer::EnableClipPlane(unsigned short index, const MT_Vector4& plane) +{ + double planev[4] = {plane.x(), plane.y(), plane.z(), plane.w()}; + glClipPlane(GL_CLIP_PLANE0 + index, planev); + glEnable(GL_CLIP_PLANE0 + index); +} + +void RAS_OpenGLRasterizer::DisableClipPlane(unsigned short index) +{ + glDisable(GL_CLIP_PLANE0 + index); +} + void RAS_OpenGLRasterizer::SetFrontFace(bool ccw) { if (ccw) { diff --git a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.h b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.h index 52d45af4b4fc..29125e823b08 100644 --- a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.h +++ b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.h @@ -36,21 +36,8 @@ # pragma warning (disable:4786) #endif -#include "MT_CmMatrix4x4.h" -#include -#include - -#include "RAS_MaterialBucket.h" -#include "RAS_IPolygonMaterial.h" #include "RAS_IRasterizer.h" -#include "BLI_utildefines.h" - -class RAS_StorageVBO; -class RAS_ICanvas; -class RAS_OpenGLLight; -struct GPUShader; - /** * 3D rendering device context. */ @@ -126,6 +113,9 @@ class RAS_OpenGLRasterizer void FlushDebugShapes(const RAS_IRasterizer::SceneDebugShape& debugShapes); + void EnableClipPlane(unsigned short index, const MT_Vector4& plane); + void DisableClipPlane(unsigned short index); + void SetFrontFace(bool ccw); /** diff --git a/source/gameengine/Rasterizer/RAS_Texture.cpp b/source/gameengine/Rasterizer/RAS_Texture.cpp index 8ddf25ea33e1..4483a770154f 100644 --- a/source/gameengine/Rasterizer/RAS_Texture.cpp +++ b/source/gameengine/Rasterizer/RAS_Texture.cpp @@ -30,7 +30,7 @@ RAS_Texture::RAS_Texture() :m_bindCode(-1), m_name(""), - m_cubeMap(nullptr) + m_renderer(nullptr) { } @@ -43,14 +43,14 @@ std::string& RAS_Texture::GetName() return m_name; } -void RAS_Texture::SetCubeMap(RAS_CubeMap *cubeMap) +void RAS_Texture::SetRenderer(RAS_TextureRenderer *renderer) { - m_cubeMap = cubeMap; + m_renderer = renderer; } -RAS_CubeMap *RAS_Texture::GetCubeMap() const +RAS_TextureRenderer *RAS_Texture::GetRenderer() const { - return m_cubeMap; + return m_renderer; } int RAS_Texture::GetCubeMapTextureType() @@ -63,6 +63,20 @@ int RAS_Texture::GetTexture2DType() return GL_TEXTURE_2D; } +const std::array& RAS_Texture::GetCubeMapTargets() +{ + static std::array targets = { + GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, + GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, + GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB + }; + + return targets; +} + void RAS_Texture::DesactiveTextures() { glActiveTexture(GL_TEXTURE0); diff --git a/source/gameengine/Rasterizer/RAS_Texture.h b/source/gameengine/Rasterizer/RAS_Texture.h index 8c14de0a6465..a3a63a92dc92 100644 --- a/source/gameengine/Rasterizer/RAS_Texture.h +++ b/source/gameengine/Rasterizer/RAS_Texture.h @@ -26,13 +26,14 @@ #define __RAS_TEXTURE_H__ #include +#include struct MTex; struct Tex; struct Image; struct GPUTexture; -class RAS_CubeMap; +class RAS_TextureRenderer; class RAS_Texture { @@ -40,7 +41,7 @@ class RAS_Texture int m_bindCode; std::string m_name; - RAS_CubeMap *m_cubeMap; + RAS_TextureRenderer *m_renderer; public: RAS_Texture(); @@ -55,15 +56,17 @@ class RAS_Texture virtual GPUTexture *GetGPUTexture() const = 0; std::string& GetName(); - void SetCubeMap(RAS_CubeMap *cubeMap); - RAS_CubeMap *GetCubeMap() const; + void SetRenderer(RAS_TextureRenderer *renderer); + RAS_TextureRenderer *GetRenderer() const; virtual unsigned int GetTextureType() = 0; - /// Return GL_TEXTURE_2D + /// Return GL_TEXTURE_2D. static int GetCubeMapTextureType(); - /// Return GL_TEXTURE_CUBE_MAP + /// Return GL_TEXTURE_CUBE_MAP. static int GetTexture2DType(); + /// Return all the OpenGL cube map face target, e.g GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB. + static const std::array& GetCubeMapTargets(); enum {MaxUnits = 8}; @@ -74,7 +77,7 @@ class RAS_Texture /** Set the current active OpenGL texture to the first texture * and bind a null texture in this slot. * This function must be used very carfully, normally only after - * that the user palyed with glActiveTexture and to make sure that + * that the user played with glActiveTexture and to make sure that * it will not break the render. * Only the first slot is affected all texture in greater slot are * not affected but just unused as default. diff --git a/source/gameengine/Rasterizer/RAS_TextureRenderer.cpp b/source/gameengine/Rasterizer/RAS_TextureRenderer.cpp new file mode 100644 index 000000000000..d786ee886c4c --- /dev/null +++ b/source/gameengine/Rasterizer/RAS_TextureRenderer.cpp @@ -0,0 +1,205 @@ +/* +* ***** BEGIN GPL LICENSE BLOCK ***** +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +* Contributor(s): Ulysse Martin, Tristan Porteries, Martins Upitis. +* +* ***** END GPL LICENSE BLOCK ***** +*/ + +/** \file RAS_TextureRenderer.cpp + * \ingroup bgerast + */ + +#include "RAS_TextureRenderer.h" +#include "RAS_Texture.h" +#include "RAS_IRasterizer.h" + +#include "GPU_texture.h" +#include "GPU_framebuffer.h" +#include "GPU_draw.h" + +#include "BKE_image.h" + +#include "DNA_texture_types.h" + +RAS_TextureRenderer::Face::Face(int target) + :m_fbo(nullptr), + m_rb(nullptr), + m_target(target) +{ +} + +RAS_TextureRenderer::Face::~Face() +{ +} + +void RAS_TextureRenderer::Face::AttachTexture(GPUTexture *tex) +{ + m_fbo = GPU_framebuffer_create(); + m_rb = GPU_renderbuffer_create(GPU_texture_width(tex), GPU_texture_height(tex), + 0, GPU_HDR_NONE, GPU_RENDERBUFFER_DEPTH, nullptr); + + GPU_framebuffer_texture_attach_target(m_fbo, tex, m_target, 0, nullptr); + GPU_framebuffer_renderbuffer_attach(m_fbo, m_rb, 0, nullptr); +} + +void RAS_TextureRenderer::Face::DetachTexture(GPUTexture *tex) +{ + if (m_fbo) { + GPU_framebuffer_texture_detach_target(tex, m_target); + } + if (m_rb) { + GPU_framebuffer_renderbuffer_detach(m_rb); + } + + if (m_fbo) { + GPU_framebuffer_free(m_fbo); + m_fbo = nullptr; + } + if (m_rb) { + GPU_renderbuffer_free(m_rb); + m_rb = nullptr; + } +} + +void RAS_TextureRenderer::Face::Bind() +{ + // Set the viewport in the same time. + GPU_framebuffer_bind_no_save(m_fbo, 0); +} + +RAS_TextureRenderer::RAS_TextureRenderer() + :m_gpuTex(nullptr), + m_useMipmap(false) +{ +} + +RAS_TextureRenderer::~RAS_TextureRenderer() +{ + for (Face& face : m_faces) { + face.DetachTexture(m_gpuTex); + } + + if (m_gpuTex) { + GPU_texture_free(m_gpuTex); + } + + /* This call has for side effect to ask regeneration of all textures + * depending of this image. + */ + for (RAS_Texture *texture : m_textureUsers) { + // Invalidate the renderer in each material texture users. + texture->SetRenderer(nullptr); + BKE_image_free_buffers(texture->GetImage()); + } +} + +void RAS_TextureRenderer::GetValidTexture() +{ + BLI_assert(m_textureUsers.size() > 0); + + /* The gpu texture returned by all material textures are the same. + * We can so use the first material texture user. + */ + RAS_Texture *texture = m_textureUsers[0]; + texture->CheckValidTexture(); + GPUTexture *gputex = texture->GetGPUTexture(); + + if (m_gpuTex == gputex) { + // The gpu texture is the same. + return; + } + + if (m_gpuTex) { + for (Face& face : m_faces) { + face.DetachTexture(m_gpuTex); + } + + GPU_texture_free(m_gpuTex); + } + + m_gpuTex = gputex; + + // Increment reference to make sure the gpu texture will not be freed by someone else. + GPU_texture_ref(m_gpuTex); + + for (Face& face : m_faces) { + face.AttachTexture(m_gpuTex); + } + + Tex *tex = texture->GetTex(); + EnvMap *env = tex->env; + m_useMipmap = (env->filtering == ENVMAP_MIPMAP_MIPMAP) && GPU_get_mipmap(); + + if (!m_useMipmap) { + // Disable mipmaping. + GPU_texture_bind(m_gpuTex, 0); + GPU_texture_filter_mode(m_gpuTex, false, (env->filtering == ENVMAP_MIPMAP_LINEAR), false); + GPU_texture_unbind(m_gpuTex); + } +} + +unsigned short RAS_TextureRenderer::GetNumFaces() const +{ + return m_faces.size(); +} + +bool RAS_TextureRenderer::EqualTextureUser(RAS_Texture *texture) const +{ + for (RAS_Texture *user : m_textureUsers) { + if (user->GetMTex() == texture->GetMTex()) { + return true; + } + } + + return false; +} + +void RAS_TextureRenderer::AddTextureUser(RAS_Texture *texture) +{ + m_textureUsers.push_back(texture); + texture->SetRenderer(this); +} + +void RAS_TextureRenderer::BeginRender(RAS_IRasterizer *rasty) +{ + GetValidTexture(); +} + +void RAS_TextureRenderer::EndRender(RAS_IRasterizer *rasty) +{ + if (m_useMipmap) { + GPU_texture_bind(m_gpuTex, 0); + GPU_texture_generate_mipmap(m_gpuTex); + GPU_texture_unbind(m_gpuTex); + } +} + +void RAS_TextureRenderer::BeginRenderFace(RAS_IRasterizer *rasty) +{ + // Clear only the depth texture because the background render will override the color texture. + rasty->Clear(RAS_IRasterizer::RAS_DEPTH_BUFFER_BIT); +} + +void RAS_TextureRenderer::EndRenderFace(RAS_IRasterizer *rasty) +{ +} + +void RAS_TextureRenderer::BindFace(unsigned short index) +{ + m_faces[index].Bind(); +} diff --git a/source/gameengine/Rasterizer/RAS_TextureRenderer.h b/source/gameengine/Rasterizer/RAS_TextureRenderer.h new file mode 100644 index 000000000000..e95772de0c9c --- /dev/null +++ b/source/gameengine/Rasterizer/RAS_TextureRenderer.h @@ -0,0 +1,99 @@ +/* +* ***** BEGIN GPL LICENSE BLOCK ***** +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +* Contributor(s): Ulysse Martin, Tristan Porteries, Martins Upitis. +* +* ***** END GPL LICENSE BLOCK ***** +*/ + +/** \file RAS_TextureRenderer.h +* \ingroup bgerast +*/ + +#ifndef __RAS_TEXTURE_RENDERER_H__ +#define __RAS_TEXTURE_RENDERER_H__ + +#include + +class RAS_Texture; +class RAS_IRasterizer; + +struct GPUFrameBuffer; +struct GPURenderBuffer; +struct GPUTexture; + +/** \brief This class is used to render something into a material texture (RAS_Texture). + * The render is made by faces added in the sub classes of RAS_TextureRenderer. + */ +class RAS_TextureRenderer +{ +protected: + class Face + { + private: + /// Planar FBO and RBO + GPUFrameBuffer *m_fbo; + GPURenderBuffer *m_rb; + int m_target; + + public: + Face(int target); + ~Face(); + + /// Bind the face frame buffer object. + void Bind(); + /// Recreate and attach frame buffer object and render buffer to the texture. + void AttachTexture(GPUTexture *tex); + /// Free and detach frame buffer object and render buffer to the texture. + void DetachTexture(GPUTexture *tex); + }; + + /// All the material texture users. + std::vector m_textureUsers; + + std::vector m_faces; + +private: + /// Planar texture to attach to frame buffer objects. + GPUTexture *m_gpuTex; + /** Obtain the latest planar texture, if it's not the same as before, + * then detach the previous planar texture and attach the new one. + */ + void GetValidTexture(); + + /// Use mipmapping? + bool m_useMipmap; + +public: + RAS_TextureRenderer(); + virtual ~RAS_TextureRenderer(); + + unsigned short GetNumFaces() const; + + /// Return true if the material texture use the same data than one of the texture user. + bool EqualTextureUser(RAS_Texture *texture) const; + void AddTextureUser(RAS_Texture *texture); + + virtual void BeginRender(RAS_IRasterizer *rasty); + virtual void EndRender(RAS_IRasterizer *rasty); + virtual void BeginRenderFace(RAS_IRasterizer *rasty); + virtual void EndRenderFace(RAS_IRasterizer *rasty); + + void BindFace(unsigned short index); +}; + +#endif // __RAS_TEXTURE_RENDERER_H__