diff options
author | Lukas Tönne <lukas.toenne@gmail.com> | 2015-03-21 19:58:52 +0300 |
---|---|---|
committer | Lukas Tönne <lukas.toenne@gmail.com> | 2015-03-21 19:58:52 +0300 |
commit | 6db876bef6325b81d598da0324fc48e71c52ba0f (patch) | |
tree | 8c0ee82e55ac42b079f5b51c9243368528222e1b | |
parent | 3e44712895e021b5040aede67783338c0f700f5d (diff) | |
parent | 2744ce77dea394026bc524e68c687050bc8e0c28 (diff) |
Merge branch 'master' into alembic_pointcache
109 files changed, 3962 insertions, 1325 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 7909f2465d2..d1aeef8a010 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -684,6 +684,7 @@ if(WITH_GHOST_SDL OR WITH_HEADLESS) set(WITH_X11_XINPUT OFF) set(WITH_X11_XF86VMODE OFF) set(WITH_GHOST_XDND OFF) + set(WITH_INPUT_IME OFF) endif() if(WITH_CPU_SSE) diff --git a/GNUmakefile b/GNUmakefile index 775d0ae532b..5a3a0b7848d 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -65,7 +65,7 @@ ifneq "$(findstring cycles, $(MAKECMDGOALS))" "" BUILD_CMAKE_ARGS:=$(BUILD_CMAKE_ARGS) -C"$(BLENDER_DIR)/build_files/cmake/config/cycles_standalone.cmake" endif ifneq "$(findstring headless, $(MAKECMDGOALS))" "" - BUILD_DIR:=$(BUILD_DIR)_bpy + BUILD_DIR:=$(BUILD_DIR)_headless BUILD_CMAKE_ARGS:=$(BUILD_CMAKE_ARGS) -C"$(BLENDER_DIR)/build_files/cmake/config/blender_headless.cmake" endif ifneq "$(findstring bpy, $(MAKECMDGOALS))" "" diff --git a/SConstruct b/SConstruct index 00a95b0114b..52fef2f3b87 100644 --- a/SConstruct +++ b/SConstruct @@ -771,6 +771,9 @@ if B.targets != ['cudakernels']: data_to_c_simple("source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl") data_to_c_simple("source/blender/gpu/shaders/gpu_shader_fx_dof_frag.glsl") data_to_c_simple("source/blender/gpu/shaders/gpu_shader_fx_dof_vert.glsl") + data_to_c_simple("source/blender/gpu/shaders/gpu_shader_fx_dof_hq_frag.glsl") + data_to_c_simple("source/blender/gpu/shaders/gpu_shader_fx_dof_hq_vert.glsl") + data_to_c_simple("source/blender/gpu/shaders/gpu_shader_fx_dof_hq_geo.glsl") data_to_c_simple("source/blender/gpu/shaders/gpu_shader_fx_lib.glsl") data_to_c_simple("source/blender/gpu/shaders/gpu_shader_fx_depth_resolve.glsl") data_to_c_simple("source/blender/gpu/shaders/gpu_shader_fx_vert.glsl") diff --git a/build_files/cmake/Modules/FindPCRE.cmake b/build_files/cmake/Modules/FindPCRE.cmake index 9d73e9200d9..ff4b7261555 100644 --- a/build_files/cmake/Modules/FindPCRE.cmake +++ b/build_files/cmake/Modules/FindPCRE.cmake @@ -40,7 +40,6 @@ FIND_PATH(PCRE_INCLUDE_DIR pcre.h ${_pcre_SEARCH_DIRS} PATH_SUFFIXES include - include ) FIND_LIBRARY(PCRE_LIBRARY diff --git a/build_files/cmake/buildinfo.cmake b/build_files/cmake/buildinfo.cmake index 8ef29b06213..74c1f8f1f8e 100644 --- a/build_files/cmake/buildinfo.cmake +++ b/build_files/cmake/buildinfo.cmake @@ -60,7 +60,8 @@ if(EXISTS ${SOURCE_DIR}/.git) execute_process(COMMAND git log HEAD..@{u} WORKING_DIRECTORY ${SOURCE_DIR} OUTPUT_VARIABLE _git_below_check - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET) if(NOT _git_below_check STREQUAL "") # If there're commits between HEAD and upstream this means # that we're reset-ed to older revision. Use it's hash then. diff --git a/build_files/cmake/cmake_netbeans_project.py b/build_files/cmake/cmake_netbeans_project.py index 81b1a460f67..25d07373569 100755 --- a/build_files/cmake/cmake_netbeans_project.py +++ b/build_files/cmake/cmake_netbeans_project.py @@ -29,18 +29,19 @@ Example linux usage Windows not supported so far """ -from project_info import (SIMPLE_PROJECTFILE, - SOURCE_DIR, - CMAKE_DIR, - PROJECT_DIR, - source_list, - is_project_file, - is_c_header, - # is_py, - cmake_advanced_info, - cmake_compiler_defines, - project_name_get, - ) +from project_info import ( + SIMPLE_PROJECTFILE, + SOURCE_DIR, + CMAKE_DIR, + PROJECT_DIR, + source_list, + is_project_file, + is_c_header, + # is_py, + cmake_advanced_info, + cmake_compiler_defines, + project_name_get, + ) import os diff --git a/build_files/cmake/cmake_qtcreator_project.py b/build_files/cmake/cmake_qtcreator_project.py index 1676d634cf2..67302c89a68 100755 --- a/build_files/cmake/cmake_qtcreator_project.py +++ b/build_files/cmake/cmake_qtcreator_project.py @@ -22,26 +22,27 @@ # <pep8 compliant> -""" +r""" +Example Linux usage: + python ~/blender-git/blender/build_files/cmake/cmake_qtcreator_project.py ~/blender-git/cmake + Example Win32 usage: c:\Python32\python.exe c:\blender_dev\blender\build_files\cmake\cmake_qtcreator_project.py c:\blender_dev\cmake_build - -example linux usage - python ~/blender-git/blender/build_files/cmake/cmake_qtcreator_project.py ~/blender-git/cmake """ -from project_info import (SIMPLE_PROJECTFILE, - SOURCE_DIR, - # CMAKE_DIR, - PROJECT_DIR, - source_list, - is_project_file, - is_c_header, - is_py, - cmake_advanced_info, - cmake_compiler_defines, - project_name_get, - ) +from project_info import ( + SIMPLE_PROJECTFILE, + SOURCE_DIR, + # CMAKE_DIR, + PROJECT_DIR, + source_list, + is_project_file, + is_c_header, + is_py, + cmake_advanced_info, + cmake_compiler_defines, + project_name_get, + ) import os import sys @@ -63,18 +64,19 @@ def create_qtc_project_main(): if SIMPLE_PROJECTFILE: # --- qtcreator specific, simple format PROJECT_NAME = "Blender" - with open(os.path.join(PROJECT_DIR, "%s.files" % PROJECT_NAME), 'w') as f: + FILE_NAME = PROJECT_NAME.lower() + with open(os.path.join(PROJECT_DIR, "%s.files" % FILE_NAME), 'w') as f: f.write("\n".join(files_rel)) - with open(os.path.join(PROJECT_DIR, "%s.includes" % PROJECT_NAME), 'w') as f: + with open(os.path.join(PROJECT_DIR, "%s.includes" % FILE_NAME), 'w') as f: f.write("\n".join(sorted(list(set(os.path.dirname(f) for f in files_rel if is_c_header(f)))))) - qtc_prj = os.path.join(PROJECT_DIR, "%s.creator" % PROJECT_NAME) + qtc_prj = os.path.join(PROJECT_DIR, "%s.creator" % FILE_NAME) with open(qtc_prj, 'w') as f: f.write("[General]\n") - qtc_cfg = os.path.join(PROJECT_DIR, "%s.config" % PROJECT_NAME) + qtc_cfg = os.path.join(PROJECT_DIR, "%s.config" % FILE_NAME) if not os.path.exists(qtc_cfg): with open(qtc_cfg, 'w') as f: f.write("// ADD PREDEFINED MACROS HERE!\n") diff --git a/build_files/cmake/packaging.cmake b/build_files/cmake/packaging.cmake index 95bbb2b59a8..f0dd0482d4d 100644 --- a/build_files/cmake/packaging.cmake +++ b/build_files/cmake/packaging.cmake @@ -27,7 +27,8 @@ if(EXISTS ${CMAKE_SOURCE_DIR}/.git/) execute_process(COMMAND git rev-parse --short @{u} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE MY_WC_HASH - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET) endif() endif() set(BUILD_REV ${MY_WC_HASH}) diff --git a/build_files/scons/tools/Blender.py b/build_files/scons/tools/Blender.py index 710041dc5e6..d78f82588f7 100644 --- a/build_files/scons/tools/Blender.py +++ b/build_files/scons/tools/Blender.py @@ -474,7 +474,7 @@ def buildinfo(lenv, build_type): no_upstream = False try : - build_hash = btools.get_command_output(['git', 'rev-parse', '--short', '@{u}']).strip() + build_hash = btools.get_command_output(['git', 'rev-parse', '--short', '@{u}'], stderr=subprocess.STDOUT).strip() except subprocess.CalledProcessError: # assume branch has no upstream configured build_hash = btools.get_command_output(['git', 'rev-parse', '--short', 'HEAD']).strip() diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 63518d7fdb6..1097b79ffa5 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -457,7 +457,10 @@ class CyclesCamera_PT_dof(CyclesButtonsPanel, Panel): sub.active = cam.dof_object is None sub.prop(cam, "dof_distance", text="Distance") col.prop(dof_options, "fstop") - + col.prop(dof_options, "use_high_quality") + if dof_options.use_high_quality: + col.prop(dof_options, "blades") + col = split.column() col.label("Aperture:") diff --git a/intern/cycles/bvh/bvh_params.h b/intern/cycles/bvh/bvh_params.h index 99bfd9449da..af8d8eeb3ee 100644 --- a/intern/cycles/bvh/bvh_params.h +++ b/intern/cycles/bvh/bvh_params.h @@ -113,7 +113,9 @@ public: __forceinline int prim_type() const { return type; } BVHReference& operator=(const BVHReference &arg) { - memcpy(this, &arg, sizeof(BVHReference)); + if(&arg != this) { + memcpy(this, &arg, sizeof(BVHReference)); + } return *this; } diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index dc55a81b0d8..761bcb4dd27 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -297,11 +297,6 @@ elseif(WIN32) intern/GHOST_NDOFManagerWin32.h ) endif() - - list(APPEND INC - ../utfconv - ) - endif() if(WITH_GL_EGL AND NOT (WITH_HEADLESS OR WITH_GHOST_SDL)) @@ -334,6 +329,10 @@ elseif(WIN32) intern/GHOST_SystemPathsWin32.h ) + list(APPEND INC + ../utfconv + ) + endif() add_definitions(${GL_DEFINITIONS}) diff --git a/intern/ghost/intern/GHOST_ContextCGL.mm b/intern/ghost/intern/GHOST_ContextCGL.mm index 0b290c617a5..ae7e7a69854 100644 --- a/intern/ghost/intern/GHOST_ContextCGL.mm +++ b/intern/ghost/intern/GHOST_ContextCGL.mm @@ -292,14 +292,16 @@ GHOST_TSuccess GHOST_ContextCGL::initializeDrawingContext() [m_openGLView setPixelFormat:pixelFormat]; - m_openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:s_sharedOpenGLContext]; + m_openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:s_sharedOpenGLContext]; // +1 refCount to pixelFormat if (m_openGLContext == nil) goto error; if (s_sharedCount == 0) s_sharedOpenGLContext = m_openGLContext; - + + [pixelFormat release]; // -1 refCount to pixelFormat + s_sharedCount++; #ifdef GHOST_MULTITHREADED_OPENGL @@ -332,6 +334,7 @@ GHOST_TSuccess GHOST_ContextCGL::initializeDrawingContext() error: [m_openGLView setOpenGLContext:prev_openGLContext]; + [pixelFormat release]; [pool drain]; diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py index 106e31ea89a..6a0d680df53 100644 --- a/release/scripts/startup/bl_ui/properties_data_camera.py +++ b/release/scripts/startup/bl_ui/properties_data_camera.py @@ -184,13 +184,18 @@ class DATA_PT_camera_dof(CameraButtonsPanel, Panel): col = split.column() col.label(text="Focus:") col.prop(cam, "dof_object", text="") + col.prop(dof_options, "use_high_quality") col = split.column() col.prop(dof_options, "fstop") + sub = col.column() - sub.active = cam.dof_object is None + sub.active = (cam.dof_object is None) sub.prop(cam, "dof_distance", text="Distance") + if dof_options.use_high_quality: + col.prop(dof_options, "blades") + class DATA_PT_camera_display(CameraButtonsPanel, Panel): bl_label = "Display" diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h index 57ba6fd55ca..f51e0fe6979 100644 --- a/source/blender/blenkernel/BKE_action.h +++ b/source/blender/blenkernel/BKE_action.h @@ -161,6 +161,9 @@ void extract_pose_from_pose(struct bPose *pose, const struct bPose *src); /* sets constraint flags */ void BKE_pose_update_constraint_flags(struct bPose *pose); +/* tag constraint flags for update */ +void BKE_pose_tag_update_constraint_flags(struct bPose *pose); + /* return the name of structure pointed by pose->ikparam */ const char *BKE_pose_ikparam_get_name(struct bPose *pose); diff --git a/source/blender/blenkernel/BKE_camera.h b/source/blender/blenkernel/BKE_camera.h index 77296920ee2..4849c66df1b 100644 --- a/source/blender/blenkernel/BKE_camera.h +++ b/source/blender/blenkernel/BKE_camera.h @@ -104,24 +104,35 @@ typedef struct CameraParams { float winmat[4][4]; } CameraParams; +/* values for CameraParams.zoom, need to be taken into account for some operations */ +#define CAMERA_PARAM_ZOOM_INIT_CAMOB 1.0f +#define CAMERA_PARAM_ZOOM_INIT_PERSP 2.0f + void BKE_camera_params_init(CameraParams *params); -void BKE_camera_params_from_object(CameraParams *params, struct Object *camera); -void BKE_camera_params_from_view3d(CameraParams *params, struct View3D *v3d, struct RegionView3D *rv3d); +void BKE_camera_params_from_object(CameraParams *params, const struct Object *camera); +void BKE_camera_params_from_view3d(CameraParams *params, const struct View3D *v3d, const struct RegionView3D *rv3d); void BKE_camera_params_compute_viewplane(CameraParams *params, int winx, int winy, float aspx, float aspy); void BKE_camera_params_compute_matrix(CameraParams *params); /* Camera View Frame */ -void BKE_camera_view_frame_ex(struct Scene *scene, struct Camera *camera, float drawsize, const bool do_clip, const float scale[3], - float r_asp[2], float r_shift[2], float *r_drawsize, float r_vec[4][3]); - -void BKE_camera_view_frame(struct Scene *scene, struct Camera *camera, float r_vec[4][3]); - -bool BKE_camera_view_frame_fit_to_scene(struct Scene *scene, struct View3D *v3d, struct Object *camera_ob, - float r_co[3], float *r_scale); -bool BKE_camera_view_frame_fit_to_coords(struct Scene *scene, float (*cos)[3], int num_cos, - struct Object *camera_ob, float r_co[3], float *r_scale); +void BKE_camera_view_frame_ex( + const struct Scene *scene, const struct Camera *camera, + const float drawsize, const bool do_clip, const float scale[3], + float r_asp[2], float r_shift[2], float *r_drawsize, float r_vec[4][3]); +void BKE_camera_view_frame( + const struct Scene *scene, const struct Camera *camera, + float r_vec[4][3]); + +bool BKE_camera_view_frame_fit_to_scene( + struct Scene *scene, struct View3D *v3d, struct Object *camera_ob, + float r_co[3], float *r_scale); +bool BKE_camera_view_frame_fit_to_coords( + const struct Scene *scene, + const float (*cos)[3], int num_cos, + const struct Object *camera_ob, + float r_co[3], float *r_scale); void BKE_camera_to_gpu_dof(struct Object *camera, struct GPUFXSettings *r_fx_settings); diff --git a/source/blender/blenkernel/BKE_dynamicpaint.h b/source/blender/blenkernel/BKE_dynamicpaint.h index a8e152fd301..1aba72e8cd8 100644 --- a/source/blender/blenkernel/BKE_dynamicpaint.h +++ b/source/blender/blenkernel/BKE_dynamicpaint.h @@ -70,8 +70,8 @@ void dynamicPaint_Modifier_copy(struct DynamicPaintModifierData *pmd, struct Dyn bool dynamicPaint_createType(struct DynamicPaintModifierData *pmd, int type, struct Scene *scene); struct DynamicPaintSurface *dynamicPaint_createNewSurface(struct DynamicPaintCanvasSettings *canvas, struct Scene *scene); -void dynamicPaint_clearSurface(struct Scene *scene, struct DynamicPaintSurface *surface); -bool dynamicPaint_resetSurface(struct Scene *scene, struct DynamicPaintSurface *surface); +void dynamicPaint_clearSurface(const struct Scene *scene, struct DynamicPaintSurface *surface); +bool dynamicPaint_resetSurface(const struct Scene *scene, struct DynamicPaintSurface *surface); void dynamicPaint_freeSurface(struct DynamicPaintSurface *surface); void dynamicPaint_freeCanvas(struct DynamicPaintModifierData *pmd); void dynamicPaint_freeBrush(struct DynamicPaintModifierData *pmd); diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h index 38c8cf12969..f80e03fda9b 100644 --- a/source/blender/blenkernel/BKE_editmesh.h +++ b/source/blender/blenkernel/BKE_editmesh.h @@ -97,7 +97,7 @@ void BKE_editmesh_color_ensure(BMEditMesh *em, const char htype); /* editderivedmesh.c */ /* should really be defined in editmesh.c, but they use 'EditDerivedBMesh' */ void BKE_editmesh_statvis_calc(BMEditMesh *em, struct DerivedMesh *dm, - struct MeshStatVis *statvis); + const struct MeshStatVis *statvis); float (*BKE_editmesh_vertexCos_get(struct BMEditMesh *em, struct Scene *scene, int *r_numVerts))[3]; diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index c88b102c191..003d3ffd677 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -131,6 +131,9 @@ bool BKE_mesh_uv_cdlayer_rename(struct Mesh *me, const char *old_name, const cha float (*BKE_mesh_vertexCos_get(struct Mesh *me, int *r_numVerts))[3]; +void BKE_mesh_calc_normals_split(struct Mesh *mesh); +void BKE_mesh_split_faces(struct Mesh *mesh); + struct Mesh *BKE_mesh_new_from_object(struct Main *bmain, struct Scene *sce, struct Object *ob, int apply_modifiers, int settings, int calc_tessface, int calc_undeformed); struct Mesh *BKE_mesh_new_from_dupli_data(struct Main *bmain, struct DupliObjectData *data, bool calc_tessface, bool calc_undeformed); diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 58c0b495062..9076eada312 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -102,9 +102,7 @@ void BKE_palette_free(struct Palette *palette); struct Palette *BKE_palette_add(struct Main *bmain, const char *name); struct PaletteColor *BKE_palette_color_add(struct Palette *palette); bool BKE_palette_is_empty(const struct Palette *palette); -void BKE_palette_color_remove_ex(struct Palette *palette, struct PaletteColor *color, bool use_free); void BKE_palette_color_remove(struct Palette *palette, struct PaletteColor *color); -void BKE_palette_cleanup(struct Palette *palette); void BKE_palette_clear(struct Palette *palette); /* paint curves */ diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index 149472db8fa..c191f3805a6 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -113,8 +113,8 @@ char *BKE_scene_find_last_marker_name(struct Scene *scene, int frame); /* checks for cycle, returns 1 if it's all OK */ bool BKE_scene_validate_setscene(struct Main *bmain, struct Scene *sce); -float BKE_scene_frame_get(struct Scene *scene); -float BKE_scene_frame_get_from_ctime(struct Scene *scene, const float frame); +float BKE_scene_frame_get(const struct Scene *scene); +float BKE_scene_frame_get_from_ctime(const struct Scene *scene, const float frame); void BKE_scene_frame_set(struct Scene *scene, double cfra); /* ** Scene evaluation ** */ @@ -126,15 +126,15 @@ struct SceneRenderLayer *BKE_scene_add_render_layer(struct Scene *sce, const cha bool BKE_scene_remove_render_layer(struct Main *main, struct Scene *scene, struct SceneRenderLayer *srl); /* render profile */ -int get_render_subsurf_level(struct RenderData *r, int level); -int get_render_child_particle_number(struct RenderData *r, int num); -int get_render_shadow_samples(struct RenderData *r, int samples); -float get_render_aosss_error(struct RenderData *r, float error); +int get_render_subsurf_level(const struct RenderData *r, int level); +int get_render_child_particle_number(const struct RenderData *r, int num); +int get_render_shadow_samples(const struct RenderData *r, int samples); +float get_render_aosss_error(const struct RenderData *r, float error); -bool BKE_scene_use_new_shading_nodes(struct Scene *scene); +bool BKE_scene_use_new_shading_nodes(const struct Scene *scene); -bool BKE_scene_uses_blender_internal(struct Scene *scene); -bool BKE_scene_uses_blender_game(struct Scene *scene); +bool BKE_scene_uses_blender_internal(const struct Scene *scene); +bool BKE_scene_uses_blender_game(const struct Scene *scene); void BKE_scene_disable_color_management(struct Scene *scene); bool BKE_scene_check_color_management_enabled(const struct Scene *scene); diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index c936ce5284d..5649e76efe6 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -449,7 +449,9 @@ void BKE_sequence_modifier_list_copy(struct Sequence *seqn, struct Sequence *seq int BKE_sequence_supports_modifiers(struct Sequence *seq); /* internal filters */ -struct ImBuf *BKE_sequencer_render_mask_input(const SeqRenderData *context, int mask_input_type, struct Sequence *mask_sequence, struct Mask *mask_id, int cfra, bool make_float); +struct ImBuf *BKE_sequencer_render_mask_input( + const SeqRenderData *context, int mask_input_type, struct Sequence *mask_sequence, struct Mask *mask_id, + int cfra, int fra_offset, bool make_float); void BKE_sequencer_color_balance_apply(struct StripColorBalance *cb, struct ImBuf *ibuf, float mul, bool make_float, struct ImBuf *mask_input); #endif /* __BKE_SEQUENCER_H__ */ diff --git a/source/blender/blenkernel/BKE_texture.h b/source/blender/blenkernel/BKE_texture.h index ebf85ff51d1..03ddbb706d7 100644 --- a/source/blender/blenkernel/BKE_texture.h +++ b/source/blender/blenkernel/BKE_texture.h @@ -134,7 +134,9 @@ struct OceanTex *BKE_copy_oceantex(struct OceanTex *ot); bool BKE_texture_dependsOnTime(const struct Tex *texture); bool BKE_texture_is_image_user(const struct Tex *tex); -void BKE_texture_get_value(struct Scene *scene, struct Tex *texture, float *tex_co, struct TexResult *texres, bool use_color_management); +void BKE_texture_get_value( + const struct Scene *scene, struct Tex *texture, + float *tex_co, struct TexResult *texres, bool use_color_management); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 0bfa3628967..bfb5098c5ae 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -1131,7 +1131,7 @@ typedef struct DMWeightColorInfo { } DMWeightColorInfo; -static int dm_drawflag_calc(ToolSettings *ts) +static int dm_drawflag_calc(const ToolSettings *ts) { return ((ts->multipaint ? CALC_WP_MULTIPAINT : /* CALC_WP_GROUP_USER_ACTIVE or CALC_WP_GROUP_USER_ALL*/ @@ -1368,7 +1368,7 @@ void DM_update_weight_mcol(Object *ob, DerivedMesh *dm, int const draw_flag, } } -static void DM_update_statvis_color(Scene *scene, Object *ob, DerivedMesh *dm) +static void DM_update_statvis_color(const Scene *scene, Object *ob, DerivedMesh *dm) { BMEditMesh *em = BKE_editmesh_from_object(ob); @@ -2340,7 +2340,7 @@ static void editbmesh_build_data(Scene *scene, Object *obedit, BMEditMesh *em, C BLI_assert(!(em->derivedFinal->dirty & DM_DIRTY_NORMALS)); } -static CustomDataMask object_get_datamask(Scene *scene, Object *ob) +static CustomDataMask object_get_datamask(const Scene *scene, Object *ob) { Object *actob = scene->basact ? scene->basact->object : NULL; CustomDataMask mask = ob->customdata_mask; diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index f9556bf39ab..9ce198bd687 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -929,6 +929,12 @@ void BKE_pose_update_constraint_flags(bPose *pose) pchan->constflag |= PCHAN_HAS_CONST; } } + pose->flag &= ~POSE_CONSTRAINTS_NEED_UPDATE_FLAGS; +} + +void BKE_pose_tag_update_constraint_flags(bPose *pose) +{ + pose->flag |= POSE_CONSTRAINTS_NEED_UPDATE_FLAGS; } /* Clears all BONE_UNKEYED flags for every pose channel in every pose diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 451656a9bca..886df16d0ee 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -213,7 +213,7 @@ void BKE_camera_params_init(CameraParams *params) params->clipend = 100.0f; } -void BKE_camera_params_from_object(CameraParams *params, Object *ob) +void BKE_camera_params_from_object(CameraParams *params, const Object *ob) { if (!ob) return; @@ -255,7 +255,7 @@ void BKE_camera_params_from_object(CameraParams *params, Object *ob) } } -void BKE_camera_params_from_view3d(CameraParams *params, View3D *v3d, RegionView3D *rv3d) +void BKE_camera_params_from_view3d(CameraParams *params, const View3D *v3d, const RegionView3D *rv3d) { /* common */ params->lens = v3d->lens; @@ -274,7 +274,7 @@ void BKE_camera_params_from_view3d(CameraParams *params, View3D *v3d, RegionView params->shiftx *= params->zoom; params->shifty *= params->zoom; - params->zoom = 1.0f / params->zoom; + params->zoom = CAMERA_PARAM_ZOOM_INIT_CAMOB / params->zoom; } else if (rv3d->persp == RV3D_ORTHO) { /* orthographic view */ @@ -283,13 +283,13 @@ void BKE_camera_params_from_view3d(CameraParams *params, View3D *v3d, RegionView params->clipsta = -params->clipend; params->is_ortho = true; - /* make sure any changes to this match ED_view3d_radius_to_ortho_dist() */ + /* make sure any changes to this match ED_view3d_radius_to_dist_ortho() */ params->ortho_scale = rv3d->dist * sensor_size / v3d->lens; - params->zoom = 2.0f; + params->zoom = CAMERA_PARAM_ZOOM_INIT_PERSP; } else { /* perspective view */ - params->zoom = 2.0f; + params->zoom = CAMERA_PARAM_ZOOM_INIT_PERSP; } } @@ -384,8 +384,10 @@ void BKE_camera_params_compute_matrix(CameraParams *params) /***************************** Camera View Frame *****************************/ -void BKE_camera_view_frame_ex(Scene *scene, Camera *camera, float drawsize, const bool do_clip, const float scale[3], - float r_asp[2], float r_shift[2], float *r_drawsize, float r_vec[4][3]) +void BKE_camera_view_frame_ex( + const Scene *scene, const Camera *camera, + const float drawsize, const bool do_clip, const float scale[3], + float r_asp[2], float r_shift[2], float *r_drawsize, float r_vec[4][3]) { float facx, facy; float depth; @@ -456,7 +458,7 @@ void BKE_camera_view_frame_ex(Scene *scene, Camera *camera, float drawsize, cons r_vec[3][0] = r_shift[0] - facx; r_vec[3][1] = r_shift[1] + facy; r_vec[3][2] = depth; } -void BKE_camera_view_frame(Scene *scene, Camera *camera, float r_vec[4][3]) +void BKE_camera_view_frame(const Scene *scene, const Camera *camera, float r_vec[4][3]) { float dummy_asp[2]; float dummy_shift[2]; @@ -502,7 +504,9 @@ static void camera_to_frame_view_cb(const float co[3], void *user_data) data->tot++; } -static void camera_frame_fit_data_init(Scene *scene, Object *ob, CameraParams *params, CameraViewFrameData *data) +static void camera_frame_fit_data_init( + const Scene *scene, const Object *ob, + CameraParams *params, CameraViewFrameData *data) { float camera_rotmat_transposed_inversed[4][4]; unsigned int i; @@ -521,7 +525,7 @@ static void camera_frame_fit_data_init(Scene *scene, Object *ob, CameraParams *p BKE_camera_params_compute_matrix(params); /* initialize callback data */ - copy_m3_m4(data->camera_rotmat, ob->obmat); + copy_m3_m4(data->camera_rotmat, (float (*)[4])ob->obmat); normalize_m3(data->camera_rotmat); /* To transform a plane which is in its homogeneous representation (4d vector), * we need the inverse of the transpose of the transform matrix... */ @@ -672,7 +676,8 @@ bool BKE_camera_view_frame_fit_to_scene( } bool BKE_camera_view_frame_fit_to_coords( - Scene *scene, float (*cos)[3], int num_cos, Object *camera_ob, float r_co[3], float *r_scale) + const Scene *scene, const float (*cos)[3], int num_cos, const Object *camera_ob, + float r_co[3], float *r_scale) { CameraParams params; CameraViewFrameData data_cb; diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 762b24dfdf8..7b7f024d2dc 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -579,7 +579,7 @@ static void scene_setSubframe(Scene *scene, float subframe) scene->r.subframe = subframe; } -static int surface_getBrushFlags(DynamicPaintSurface *surface, Scene *scene) +static int surface_getBrushFlags(DynamicPaintSurface *surface, const Scene *scene) { Base *base = NULL; GroupObject *go = NULL; @@ -1447,7 +1447,7 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, int for MEM_freeN(temp_data); } -static void dynamicPaint_setInitialColor(Scene *scene, DynamicPaintSurface *surface) +static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface *surface) { PaintSurfaceData *sData = surface->data; PaintPoint *pPoint = (PaintPoint *)sData->type_data; @@ -1595,7 +1595,7 @@ static void dynamicPaint_setInitialColor(Scene *scene, DynamicPaintSurface *surf } /* clears surface data back to zero */ -void dynamicPaint_clearSurface(Scene *scene, DynamicPaintSurface *surface) +void dynamicPaint_clearSurface(const Scene *scene, DynamicPaintSurface *surface) { PaintSurfaceData *sData = surface->data; if (sData && sData->type_data) { @@ -1620,7 +1620,7 @@ void dynamicPaint_clearSurface(Scene *scene, DynamicPaintSurface *surface) } /* completely (re)initializes surface (only for point cache types)*/ -bool dynamicPaint_resetSurface(Scene *scene, DynamicPaintSurface *surface) +bool dynamicPaint_resetSurface(const Scene *scene, DynamicPaintSurface *surface) { int numOfPoints = dynamicPaint_surfaceNumOfPoints(surface); /* free existing data */ @@ -1647,7 +1647,7 @@ bool dynamicPaint_resetSurface(Scene *scene, DynamicPaintSurface *surface) } /* make sure allocated surface size matches current requirements */ -static bool dynamicPaint_checkSurfaceData(Scene *scene, DynamicPaintSurface *surface) +static bool dynamicPaint_checkSurfaceData(const Scene *scene, DynamicPaintSurface *surface) { if (!surface->data || ((dynamicPaint_surfaceNumOfPoints(surface) != surface->data->total_points))) { return dynamicPaint_resetSurface(scene, surface); @@ -4696,7 +4696,7 @@ static int dynamicPaint_surfaceHasMoved(DynamicPaintSurface *surface, Object *ob return ret; } -static int surface_needsVelocityData(DynamicPaintSurface *surface, Scene *scene) +static int surface_needsVelocityData(DynamicPaintSurface *surface, const Scene *scene) { if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) return 1; @@ -4716,7 +4716,7 @@ static int surface_needsAccelerationData(DynamicPaintSurface *surface) } /* Prepare for surface step by creating PaintBakeNormal data */ -static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, Scene *scene, Object *ob) +static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Scene *scene, Object *ob) { PaintSurfaceData *sData = surface->data; PaintAdjData *adj_data = sData->adj_data; diff --git a/source/blender/blenkernel/intern/editderivedmesh.c b/source/blender/blenkernel/intern/editderivedmesh.c index 082edb01efd..40f3022c192 100644 --- a/source/blender/blenkernel/intern/editderivedmesh.c +++ b/source/blender/blenkernel/intern/editderivedmesh.c @@ -2253,7 +2253,7 @@ static void statvis_calc_sharp( } void BKE_editmesh_statvis_calc(BMEditMesh *em, DerivedMesh *dm, - MeshStatVis *statvis) + const MeshStatVis *statvis) { EditDerivedBMesh *bmdm = (EditDerivedBMesh *)dm; BLI_assert(dm == NULL || dm->type == DM_TYPE_EDITBMESH); diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index fa62308ec53..11409297a85 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -725,8 +725,9 @@ static bool calc_curve_deform(Scene *scene, Object *par, float co[3], return false; } -void curve_deform_verts(Scene *scene, Object *cuOb, Object *target, DerivedMesh *dm, float (*vertexCos)[3], - int numVerts, const char *vgroup, short defaxis) +void curve_deform_verts( + Scene *scene, Object *cuOb, Object *target, DerivedMesh *dm, float (*vertexCos)[3], + int numVerts, const char *vgroup, short defaxis) { Curve *cu; int a; diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 5f75d00eb78..65a092fe6a4 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -2124,6 +2124,146 @@ void BKE_mesh_mselect_active_set(Mesh *me, int index, int type) (me->mselect[me->totselect - 1].type == type)); } +void BKE_mesh_calc_normals_split(Mesh *mesh) +{ + float (*r_loopnors)[3]; + float (*polynors)[3]; + short (*clnors)[2] = NULL; + bool free_polynors = false; + + if (CustomData_has_layer(&mesh->ldata, CD_NORMAL)) { + r_loopnors = CustomData_get_layer(&mesh->ldata, CD_NORMAL); + memset(r_loopnors, 0, sizeof(float[3]) * mesh->totloop); + } + else { + r_loopnors = CustomData_add_layer(&mesh->ldata, CD_NORMAL, CD_CALLOC, NULL, mesh->totloop); + CustomData_set_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY); + } + + /* may be NULL */ + clnors = CustomData_get_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); + + if (CustomData_has_layer(&mesh->pdata, CD_NORMAL)) { + /* This assume that layer is always up to date, not sure this is the case (esp. in Edit mode?)... */ + polynors = CustomData_get_layer(&mesh->pdata, CD_NORMAL); + free_polynors = false; + } + else { + polynors = MEM_mallocN(sizeof(float[3]) * mesh->totpoly, __func__); + BKE_mesh_calc_normals_poly(mesh->mvert, mesh->totvert, mesh->mloop, mesh->mpoly, mesh->totloop, mesh->totpoly, + polynors, false); + free_polynors = true; + } + + BKE_mesh_normals_loop_split( + mesh->mvert, mesh->totvert, mesh->medge, mesh->totedge, + mesh->mloop, r_loopnors, mesh->totloop, mesh->mpoly, (const float (*)[3])polynors, mesh->totpoly, + (mesh->flag & ME_AUTOSMOOTH) != 0, mesh->smoothresh, NULL, clnors, NULL); + + if (free_polynors) { + MEM_freeN(polynors); + } +} + +/* Spli faces based on the edge angle. + * Matches behavior of face splitting in render engines. + */ +void BKE_mesh_split_faces(Mesh *mesh) +{ + const int num_verts = mesh->totvert; + const int num_edges = mesh->totedge; + const int num_polys = mesh->totpoly; + MVert *mvert = mesh->mvert; + MEdge *medge = mesh->medge; + MLoop *mloop = mesh->mloop; + MPoly *mpoly = mesh->mpoly; + float (*lnors)[3]; + int poly, num_new_verts = 0; + if ((mesh->flag & ME_AUTOSMOOTH) == 0) { + return; + } + BKE_mesh_tessface_clear(mesh); + /* Compute loop normals if needed. */ + if (!CustomData_has_layer(&mesh->ldata, CD_NORMAL)) { + BKE_mesh_calc_normals_split(mesh); + } + lnors = CustomData_get_layer(&mesh->ldata, CD_NORMAL); + /* Count. */ + for (poly = 0; poly < num_polys; poly++) { + MPoly *mp = &mpoly[poly]; + int loop; + for (loop = 0; loop < mp->totloop; loop++) { + MLoop *ml = &mloop[mp->loopstart + loop]; + MVert *mv = &mvert[ml->v]; + float vn[3]; + normal_short_to_float_v3(vn, mv->no); + if (!equals_v3v3(vn, lnors[mp->loopstart + loop])) { + num_new_verts++; + } + } + } + if (num_new_verts == 0) { + /* No new vertices are to be added, can do early exit. */ + return; + } + /* Actual split. */ + mesh->totvert += num_new_verts; + mesh->totedge += 2 * num_new_verts; + mvert = mesh->mvert = MEM_reallocN(mesh->mvert, + sizeof(MVert) * mesh->totvert); + medge = mesh->medge = MEM_reallocN(mesh->medge, + sizeof(MEdge) * mesh->totedge); + CustomData_set_layer(&mesh->vdata, CD_MVERT, mesh->mvert); + CustomData_set_layer(&mesh->edata, CD_MEDGE, mesh->medge); + num_new_verts = 0; + for (poly = 0; poly < num_polys; poly++) { + MPoly *mp = &mpoly[poly]; + int loop; + for (loop = 0; loop < mp->totloop; loop++) { + int poly_loop = mp->loopstart + loop; + MLoop *ml = &mloop[poly_loop]; + MVert *mv = &mvert[ml->v]; + float vn[3]; + normal_short_to_float_v3(vn, mv->no); + if (!equals_v3v3(vn, lnors[mp->loopstart + loop])) { + int poly_loop_prev = mp->loopstart + (loop + mp->totloop - 1) % mp->totloop; + MLoop *ml_prev = &mloop[poly_loop_prev]; + int new_edge_prev, new_edge; + /* Cretae new vertex. */ + int new_vert = num_verts + num_new_verts; + CustomData_copy_data(&mesh->vdata, &mesh->vdata, + ml->v, new_vert, 1); + normal_float_to_short_v3(mvert[new_vert].no, + lnors[poly_loop]); + /* Create new edges. */ + new_edge_prev = num_edges + 2 * num_new_verts; + new_edge = num_edges + 2 * num_new_verts + 1; + CustomData_copy_data(&mesh->edata, &mesh->edata, + ml_prev->e, new_edge_prev, 1); + CustomData_copy_data(&mesh->edata, &mesh->edata, + ml->e, new_edge, 1); + if (medge[new_edge_prev].v1 == ml->v) { + medge[new_edge_prev].v1 = new_vert; + } + else { + medge[new_edge_prev].v2 = new_vert; + } + if (medge[new_edge].v1 == ml->v) { + medge[new_edge].v1 = new_vert; + } + else { + medge[new_edge].v2 = new_vert; + } + + ml->v = new_vert; + ml_prev->e = new_edge_prev; + ml->e = new_edge; + num_new_verts++; + } + } + } +} + /* settings: 1 - preview, 2 - render */ Mesh *BKE_mesh_new_from_object( Main *bmain, Scene *sce, Object *ob, diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 202e1b9cce8..9ce6c93c163 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -3035,8 +3035,12 @@ void BKE_object_handle_update_ex(EvaluationContext *eval_ctx, { if (ob->recalc & OB_RECALC_ALL) { /* speed optimization for animation lookups */ - if (ob->pose) + if (ob->pose) { BKE_pose_channels_hash_make(ob->pose); + if (ob->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { + BKE_pose_update_constraint_flags(ob->pose); + } + } if (ob->recalc & OB_RECALC_DATA) { if (ob->type == OB_ARMATURE) { diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 2082066d395..c3c88389e11 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -314,7 +314,7 @@ void BKE_paint_curve_set(Brush *br, PaintCurve *pc) } /* remove colour from palette. Must be certain color is inside the palette! */ -void BKE_palette_color_remove_ex(Palette *palette, PaletteColor *color, bool use_free) +void BKE_palette_color_remove(Palette *palette, PaletteColor *color) { if (BLI_listbase_count_ex(&palette->colors, palette->active_color) == palette->active_color) { palette->active_color--; @@ -326,32 +326,15 @@ void BKE_palette_color_remove_ex(Palette *palette, PaletteColor *color, bool use palette->active_color = 0; } - if (use_free) { - MEM_freeN(color); - } - else { - BLI_addhead(&palette->deleted, color); - } -} - -void BKE_palette_color_remove(Palette *palette, PaletteColor *color) -{ - BKE_palette_color_remove_ex(palette, color, false); + MEM_freeN(color); } void BKE_palette_clear(Palette *palette) { BLI_freelistN(&palette->colors); - BLI_freelistN(&palette->deleted); palette->active_color = 0; } -void BKE_palette_cleanup(Palette *palette) -{ - BLI_freelistN(&palette->deleted); -} - - Palette *BKE_palette_add(Main *bmain, const char *name) { Palette *palette; diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 0de68f0088e..98831cb699c 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -1141,13 +1141,13 @@ bool BKE_scene_validate_setscene(Main *bmain, Scene *sce) /* This function is needed to cope with fractional frames - including two Blender rendering features * mblur (motion blur that renders 'subframes' and blurs them together), and fields rendering. */ -float BKE_scene_frame_get(Scene *scene) +float BKE_scene_frame_get(const Scene *scene) { return BKE_scene_frame_get_from_ctime(scene, scene->r.cfra); } /* This function is used to obtain arbitrary fractional frames */ -float BKE_scene_frame_get_from_ctime(Scene *scene, const float frame) +float BKE_scene_frame_get_from_ctime(const Scene *scene, const float frame) { float ctime = frame; ctime += scene->r.subframe; @@ -1895,7 +1895,7 @@ bool BKE_scene_remove_render_layer(Main *bmain, Scene *scene, SceneRenderLayer * /* render simplification */ -int get_render_subsurf_level(RenderData *r, int lvl) +int get_render_subsurf_level(const RenderData *r, int lvl) { if (r->mode & R_SIMPLIFY) return min_ii(r->simplify_subsurf, lvl); @@ -1903,7 +1903,7 @@ int get_render_subsurf_level(RenderData *r, int lvl) return lvl; } -int get_render_child_particle_number(RenderData *r, int num) +int get_render_child_particle_number(const RenderData *r, int num) { if (r->mode & R_SIMPLIFY) return (int)(r->simplify_particles * num); @@ -1911,7 +1911,7 @@ int get_render_child_particle_number(RenderData *r, int num) return num; } -int get_render_shadow_samples(RenderData *r, int samples) +int get_render_shadow_samples(const RenderData *r, int samples) { if ((r->mode & R_SIMPLIFY) && samples > 0) return min_ii(r->simplify_shadowsamples, samples); @@ -1919,7 +1919,7 @@ int get_render_shadow_samples(RenderData *r, int samples) return samples; } -float get_render_aosss_error(RenderData *r, float error) +float get_render_aosss_error(const RenderData *r, float error) { if (r->mode & R_SIMPLIFY) return ((1.0f - r->simplify_aosss) * 10.0f + 1.0f) * error; @@ -1951,18 +1951,18 @@ Base *_setlooper_base_step(Scene **sce_iter, Base *base) return NULL; } -bool BKE_scene_use_new_shading_nodes(Scene *scene) +bool BKE_scene_use_new_shading_nodes(const Scene *scene) { - RenderEngineType *type = RE_engines_find(scene->r.engine); + const RenderEngineType *type = RE_engines_find(scene->r.engine); return (type && type->flag & RE_USE_SHADING_NODES); } -bool BKE_scene_uses_blender_internal(struct Scene *scene) +bool BKE_scene_uses_blender_internal(const Scene *scene) { return STREQ(scene->r.engine, RE_engine_id_BLENDER_RENDER); } -bool BKE_scene_uses_blender_game(struct Scene *scene) +bool BKE_scene_uses_blender_game(const Scene *scene) { return STREQ(scene->r.engine, RE_engine_id_BLENDER_GAME); } diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index a6ac4551f4a..3c43bfa2920 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -510,8 +510,8 @@ void BKE_screen_view3d_sync(View3D *v3d, struct Scene *scene) if ((v3d->lay & v3d->layact) == 0) { for (bit = 0; bit < 32; bit++) { - if (v3d->lay & (1 << bit)) { - v3d->layact = 1 << bit; + if (v3d->lay & (1u << bit)) { + v3d->layact = (1u << bit); break; } } diff --git a/source/blender/blenkernel/intern/seqmodifier.c b/source/blender/blenkernel/intern/seqmodifier.c index edd3d8efd55..b9d096f1346 100644 --- a/source/blender/blenkernel/intern/seqmodifier.c +++ b/source/blender/blenkernel/intern/seqmodifier.c @@ -77,9 +77,9 @@ typedef struct ModifierThread { } ModifierThread; -static ImBuf *modifier_mask_get(SequenceModifierData *smd, const SeqRenderData *context, int cfra, bool make_float) +static ImBuf *modifier_mask_get(SequenceModifierData *smd, const SeqRenderData *context, int cfra, int fra_offset, bool make_float) { - return BKE_sequencer_render_mask_input(context, smd->mask_input_type, smd->mask_sequence, smd->mask_id, cfra, make_float); + return BKE_sequencer_render_mask_input(context, smd->mask_input_type, smd->mask_sequence, smd->mask_id, cfra, fra_offset, make_float); } static void modifier_init_handle(void *handle_v, int start_line, int tot_line, void *init_data_v) @@ -667,7 +667,7 @@ ImBuf *BKE_sequence_modifier_apply_stack(const SeqRenderData *context, Sequence continue; if (smti->apply) { - ImBuf *mask = modifier_mask_get(smd, context, cfra, ibuf->rect_float != NULL); + ImBuf *mask = modifier_mask_get(smd, context, cfra, seq->start, ibuf->rect_float != NULL); if (processed_ibuf == ibuf) processed_ibuf = IMB_dupImBuf(ibuf); diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index cb23b1eb88e..c45db303467 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -1920,7 +1920,10 @@ static void *color_balance_do_thread(void *thread_data_v) return NULL; } -ImBuf *BKE_sequencer_render_mask_input(const SeqRenderData *context, int mask_input_type, Sequence *mask_sequence, Mask *mask_id, int cfra, bool make_float) +/* cfra is offset by fra_offset only in case we are using a real mask. */ +ImBuf *BKE_sequencer_render_mask_input( + const SeqRenderData *context, int mask_input_type, Sequence *mask_sequence, Mask *mask_id, + int cfra, int fra_offset, bool make_float) { ImBuf *mask_input = NULL; @@ -1939,7 +1942,7 @@ ImBuf *BKE_sequencer_render_mask_input(const SeqRenderData *context, int mask_in } } else if (mask_input_type == SEQUENCE_MASK_INPUT_ID) { - mask_input = seq_render_mask(context, mask_id, cfra, make_float); + mask_input = seq_render_mask(context, mask_id, cfra - fra_offset, make_float); } return mask_input; diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 3293cca76fe..07b1e9e30ac 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -1651,7 +1651,9 @@ bool BKE_texture_dependsOnTime(const struct Tex *texture) /* ------------------------------------------------------------------------- */ -void BKE_texture_get_value(Scene *scene, Tex *texture, float *tex_co, TexResult *texres, bool use_color_management) +void BKE_texture_get_value( + const Scene *scene, Tex *texture, + float *tex_co, TexResult *texres, bool use_color_management) { int result_type; bool do_color_manage = false; diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index 0037002f6d8..83db0639468 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -547,7 +547,7 @@ bool BKE_tracking_track_has_enabled_marker_at_frame(MovieTrackingTrack *track, i * - If action is TRACK_CLEAR_UPTO path from the beginning up to * ref_frame-1 will be clear. * - * - If action is TRACK_CLEAR_ALL only mareker at frame ref_frame will remain. + * - If action is TRACK_CLEAR_ALL only marker at frame ref_frame will remain. * * NOTE: frame number should be in clip space, not scene space */ diff --git a/source/blender/blenlib/BLI_ghash.h b/source/blender/blenlib/BLI_ghash.h index bf2b4126453..16d18ef1315 100644 --- a/source/blender/blenlib/BLI_ghash.h +++ b/source/blender/blenlib/BLI_ghash.h @@ -44,6 +44,8 @@ typedef unsigned int (*GHashHashFP) (const void *key); typedef bool (*GHashCmpFP) (const void *a, const void *b); typedef void (*GHashKeyFreeFP) (void *key); typedef void (*GHashValFreeFP) (void *val); +typedef void *(*GHashKeyCopyFP) (void *key); +typedef void *(*GHashValCopyFP) (void *val); typedef struct GHash GHash; @@ -54,7 +56,13 @@ typedef struct GHashIterator { } GHashIterator; enum { - GHASH_FLAG_ALLOW_DUPES = (1 << 0), /* only checked for in debug mode */ + GHASH_FLAG_ALLOW_DUPES = (1 << 0), /* Only checked for in debug mode */ + GHASH_FLAG_ALLOW_SHRINK = (1 << 1), /* Allow to shrink buckets' size. */ + +#ifdef GHASH_INTERNAL_API + /* Internal usage only */ + GHASH_FLAG_IS_GSET = (1 << 16), /* Whether the GHash is actually used as GSet (no value storage). */ +#endif }; /* *** */ @@ -62,7 +70,10 @@ enum { GHash *BLI_ghash_new_ex(GHashHashFP hashfp, GHashCmpFP cmpfp, const char *info, const unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT; GHash *BLI_ghash_new(GHashHashFP hashfp, GHashCmpFP cmpfp, const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT; +GHash *BLI_ghash_copy(GHash *gh, GHashKeyCopyFP keycopyfp, + GHashValCopyFP valcopyfp) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT; void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp); +void BLI_ghash_reserve(GHash *gh, const unsigned int nentries_reserve); void BLI_ghash_insert(GHash *gh, void *key, void *val); bool BLI_ghash_reinsert(GHash *gh, void *key, void *val, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp); void *BLI_ghash_lookup(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT; @@ -127,23 +138,37 @@ unsigned int BLI_ghashutil_strhash_n(const char *key, size_t n); CHECK_TYPE_ANY(key, char *, const char *, const char * const), \ BLI_ghashutil_strhash_p(key)) unsigned int BLI_ghashutil_strhash_p(const void *key); +unsigned int BLI_ghashutil_strhash_p_murmur(const void *key); bool BLI_ghashutil_strcmp(const void *a, const void *b); #define BLI_ghashutil_inthash(key) ( \ CHECK_TYPE_ANY(&(key), int *, const int *), \ BLI_ghashutil_uinthash((unsigned int)key)) unsigned int BLI_ghashutil_uinthash(unsigned int key); +unsigned int BLI_ghashutil_inthash_p(const void *ptr); +unsigned int BLI_ghashutil_inthash_p_murmur(const void *ptr); +bool BLI_ghashutil_intcmp(const void *a, const void *b); + + +unsigned int BLI_ghashutil_uinthash_v4(const unsigned int key[4]); #define BLI_ghashutil_inthash_v4(key) ( \ CHECK_TYPE_ANY(key, int *, const int *), \ BLI_ghashutil_uinthash_v4((const unsigned int *)key)) -unsigned int BLI_ghashutil_uinthash_v4(const unsigned int key[4]); #define BLI_ghashutil_inthash_v4_p \ ((GSetHashFP)BLI_ghashutil_uinthash_v4) +#define BLI_ghashutil_uinthash_v4_p \ + ((GSetHashFP)BLI_ghashutil_uinthash_v4) +unsigned int BLI_ghashutil_uinthash_v4_murmur(const unsigned int key[4]); +#define BLI_ghashutil_inthash_v4_murmur(key) ( \ + CHECK_TYPE_ANY(key, int *, const int *), \ + BLI_ghashutil_uinthash_v4_murmur((const unsigned int *)key)) +#define BLI_ghashutil_inthash_v4_p_murmur \ + ((GSetHashFP)BLI_ghashutil_uinthash_v4_murmur) +#define BLI_ghashutil_uinthash_v4_p_murmur \ + ((GSetHashFP)BLI_ghashutil_uinthash_v4_murmur) bool BLI_ghashutil_uinthash_v4_cmp(const void *a, const void *b); #define BLI_ghashutil_inthash_v4_cmp \ BLI_ghashutil_uinthash_v4_cmp -unsigned int BLI_ghashutil_inthash_p(const void *ptr); -bool BLI_ghashutil_intcmp(const void *a, const void *b); /** \} */ @@ -178,6 +203,7 @@ typedef struct GSet GSet; typedef GHashHashFP GSetHashFP; typedef GHashCmpFP GSetCmpFP; typedef GHashKeyFreeFP GSetKeyFreeFP; +typedef GHashKeyCopyFP GSetKeyCopyFP; /* so we can cast but compiler sees as different */ typedef struct GSetIterator { @@ -191,6 +217,7 @@ typedef struct GSetIterator { GSet *BLI_gset_new_ex(GSetHashFP hashfp, GSetCmpFP cmpfp, const char *info, const unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT; GSet *BLI_gset_new(GSetHashFP hashfp, GSetCmpFP cmpfp, const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT; +GSet *BLI_gset_copy(GSet *gs, GSetKeyCopyFP keycopyfp) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT; int BLI_gset_size(GSet *gs) ATTR_WARN_UNUSED_RESULT; void BLI_gset_flag_set(GSet *gs, unsigned int flag); void BLI_gset_flag_clear(GSet *gs, unsigned int flag); @@ -202,7 +229,7 @@ bool BLI_gset_haskey(GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT; bool BLI_gset_remove(GSet *gs, void *key, GSetKeyFreeFP keyfreefp); void BLI_gset_clear_ex(GSet *gs, GSetKeyFreeFP keyfreefp, const unsigned int nentries_reserve); -void BLI_gset_clear(GSet *gs, GSetKeyFreeFP keyfreefp); +void BLI_gset_clear(GSet *gs, GSetKeyFreeFP keyfreefp); GSet *BLI_gset_ptr_new_ex(const char *info, const unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT; @@ -229,10 +256,22 @@ BLI_INLINE bool BLI_gsetIterator_done(GSetIterator *gsi) { return BLI_ghashItera BLI_gsetIterator_done(&gs_iter_) == false; \ BLI_gsetIterator_step(&gs_iter_), i_++) -#ifdef DEBUG + +/* For testing, debugging only */ +#ifdef GHASH_INTERNAL_API +int BLI_ghash_buckets_size(GHash *gh); +int BLI_gset_buckets_size(GSet *gs); + +double BLI_ghash_calc_quality_ex( + GHash *gh, double *r_load, double *r_variance, + double *r_prop_empty_buckets, double *r_prop_overloaded_buckets, int *r_biggest_bucket); +double BLI_gset_calc_quality_ex( + GSet *gs, double *r_load, double *r_variance, + double *r_prop_empty_buckets, double *r_prop_overloaded_buckets, int *r_biggest_bucket); double BLI_ghash_calc_quality(GHash *gh); double BLI_gset_calc_quality(GSet *gs); -#endif +#endif /* GHASH_INTERNAL_API */ + #ifdef __cplusplus } diff --git a/source/blender/blenlib/BLI_hash_mm2a.h b/source/blender/blenlib/BLI_hash_mm2a.h index 007dec4f4d6..6beaf50ae8f 100644 --- a/source/blender/blenlib/BLI_hash_mm2a.h +++ b/source/blender/blenlib/BLI_hash_mm2a.h @@ -42,4 +42,6 @@ void BLI_hash_mm2a_add_int(BLI_HashMurmur2A *mm2, int data); uint32_t BLI_hash_mm2a_end(BLI_HashMurmur2A *mm2); +uint32_t BLI_hash_mm2(const unsigned char *data, size_t len, uint32_t seed); + #endif /* __BLI_HASH_MM2A_H__ */ diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h index 1e8440e55e9..2ddc8faa8b1 100644 --- a/source/blender/blenlib/BLI_utildefines.h +++ b/source/blender/blenlib/BLI_utildefines.h @@ -252,7 +252,7 @@ extern "C" { #define FTOCHAR(val) ((CHECK_TYPE_INLINE(val, float)), \ (char)(((val) <= 0.0f) ? 0 : (((val) > (1.0f - 0.5f / 255.0f)) ? 255 : ((255.0f * (val)) + 0.5f)))) #define FTOUSHORT(val) ((CHECK_TYPE_INLINE(val, float)), \ - ((val >= 1.0f - 0.5f / 65535) ? 65535 : (val <= 0.0f) ? 0 : (unsigned short)(val * 65535.0f + 0.5f))) + (unsigned short)((val >= 1.0f - 0.5f / 65535) ? 65535 : (val <= 0.0f) ? 0 : (val * 65535.0f + 0.5f))) #define USHORTTOUCHAR(val) ((unsigned char)(((val) >= 65535 - 128) ? 255 : ((val) + 128) >> 8)) #define F3TOCHAR3(v2, v1) { \ (v1)[0] = FTOCHAR((v2[0])); \ diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c index 5360ea744a1..49d3cecafed 100644 --- a/source/blender/blenlib/intern/BLI_ghash.c +++ b/source/blender/blenlib/intern/BLI_ghash.c @@ -36,17 +36,23 @@ #include <string.h> #include <stdlib.h> +#include <stdarg.h> #include <limits.h> #include "MEM_guardedalloc.h" #include "BLI_sys_types.h" /* for intptr_t support */ #include "BLI_utildefines.h" +#include "BLI_hash_mm2a.h" #include "BLI_mempool.h" + +#define GHASH_INTERNAL_API #include "BLI_ghash.h" #include "BLI_strict_flags.h" +#define GHASH_USE_MODULO_BUCKETS +/* Also used by smallhash! */ const unsigned int hashsizes[] = { 5, 11, 17, 37, 67, 131, 257, 521, 1031, 2053, 4099, 8209, 16411, 32771, 65537, 131101, 262147, 524309, 1048583, 2097169, @@ -54,24 +60,43 @@ const unsigned int hashsizes[] = { 268435459 }; -/* internal flag to ensure sets values aren't used */ -#ifndef NDEBUG -# define GHASH_FLAG_IS_SET (1 << 8) -# define IS_GHASH_ASSERT(gh) BLI_assert((gh->flag & GHASH_FLAG_IS_SET) == 0) -// # define IS_GSET_ASSERT(gs) BLI_assert((gs->flag & GHASH_FLAG_IS_SET) != 0) +#ifdef GHASH_USE_MODULO_BUCKETS +# define GHASH_MAX_SIZE 27 #else -# define IS_GHASH_ASSERT(gh) -// # define IS_GSET_ASSERT(eh) +# define GHASH_BUCKET_BIT_MIN 2 +# define GHASH_BUCKET_BIT_MAX 28 /* About 268M of buckets... */ #endif +/** + * \note Max load #GHASH_LIMIT_GROW used to be 3. (pre 2.74). + * Python uses 0.6666, tommyhaslib even goes down to 0.5. + * Reducing our from 3 to 0.75 gives huge speedup (about twice quicker pure GHash insertions/lookup, + * about 25% - 30% quicker 'dynamic-topology' stroke drawing e.g.). + * Min load #GHASH_LIMIT_SHRINK is a quarter of max load, to avoid resizing to quickly. + */ +#define GHASH_LIMIT_GROW(_nbkt) (((_nbkt) * 3) / 4) +#define GHASH_LIMIT_SHRINK(_nbkt) (((_nbkt) * 3) / 16) + /***/ +/* WARNING! Keep in sync with ugly _gh_Entry in header!!! */ typedef struct Entry { struct Entry *next; - void *key, *val; + void *key; } Entry; +typedef struct GHashEntry { + Entry e; + + void *val; +} GHashEntry; + +typedef Entry GSetEntry; + +#define GHASH_ENTRY_SIZE(_is_gset) \ + ((_is_gset) ? sizeof(GSetEntry) : sizeof(GHashEntry)) + struct GHash { GHashHashFP hashfp; GHashCmpFP cmpfp; @@ -79,11 +104,34 @@ struct GHash { Entry **buckets; struct BLI_mempool *entrypool; unsigned int nbuckets; + unsigned int limit_grow, limit_shrink; +#ifdef GHASH_USE_MODULO_BUCKETS + unsigned int cursize, size_min; +#else + unsigned int bucket_mask, bucket_bit, bucket_bit_min; +#endif + unsigned int nentries; - unsigned int cursize, flag; + unsigned int flag; }; +BLI_INLINE void ghash_entry_copy( + GHash *gh_dst, Entry *dst, GHash *gh_src, Entry *src, + GHashKeyCopyFP keycopyfp, GHashValCopyFP valcopyfp) +{ + dst->key = (keycopyfp) ? keycopyfp(src->key) : src->key; + + if ((gh_dst->flag & GHASH_FLAG_IS_GSET) == 0) { + if ((gh_src->flag & GHASH_FLAG_IS_GSET) == 0) { + ((GHashEntry *)dst)->val = (valcopyfp) ? valcopyfp(((GHashEntry *)src)->val) : ((GHashEntry *)src)->val; + } + else { + ((GHashEntry *)dst)->val = NULL; + } + } +} + /* -------------------------------------------------------------------- */ /* GHash API */ @@ -91,25 +139,37 @@ struct GHash { * \{ */ /** - * Get the hash for a key. + * Get the full hash for a key. */ BLI_INLINE unsigned int ghash_keyhash(GHash *gh, const void *key) { - return gh->hashfp(key) % gh->nbuckets; + return gh->hashfp(key); +} + +/** + * Get the full hash for an entry. + */ +BLI_INLINE unsigned int ghash_entryhash(GHash *gh, const Entry *e) +{ + return gh->hashfp(e->key); } /** - * Check if the number of items in the GHash is large enough to require more buckets. + * Get the bucket-hash for an already-computed full hash. */ -BLI_INLINE bool ghash_test_expand_buckets(const unsigned int nentries, const unsigned int nbuckets) +BLI_INLINE unsigned int ghash_bucket_index(GHash *gh, const unsigned int hash) { - return (nentries > nbuckets * 3); +#ifdef GHASH_USE_MODULO_BUCKETS + return hash % gh->nbuckets; +#else + return full_hash & gh->bucket_mask; +#endif } /** - * Expand buckets to the next size up. + * Expand buckets to the next size up or down. */ -BLI_INLINE void ghash_resize_buckets(GHash *gh, const unsigned int nbuckets) +static void ghash_buckets_resize(GHash *gh, const unsigned int nbuckets) { Entry **buckets_old = gh->buckets; Entry **buckets_new; @@ -117,49 +177,223 @@ BLI_INLINE void ghash_resize_buckets(GHash *gh, const unsigned int nbuckets) unsigned int i; Entry *e; - BLI_assert(gh->nbuckets != nbuckets); + BLI_assert((gh->nbuckets != nbuckets) || !gh->buckets); +// printf("%s: %d -> %d\n", __func__, nbuckets_old, nbuckets); gh->nbuckets = nbuckets; - buckets_new = (Entry **)MEM_callocN(gh->nbuckets * sizeof(*gh->buckets), "buckets"); - - for (i = 0; i < nbuckets_old; i++) { - Entry *e_next; - for (e = buckets_old[i]; e; e = e_next) { - const unsigned hash = ghash_keyhash(gh, e->key); - e_next = e->next; - e->next = buckets_new[hash]; - buckets_new[hash] = e; +#ifdef GHASH_USE_MODULO_BUCKETS +#else + gh->bucket_mask = nbuckets - 1; +#endif + + buckets_new = (Entry **)MEM_callocN(sizeof(*gh->buckets) * gh->nbuckets, __func__); + + if (buckets_old) { + if (nbuckets > nbuckets_old) { + for (i = 0; i < nbuckets_old; i++) { + Entry *e_next; + for (e = buckets_old[i]; e; e = e_next) { + const unsigned hash = ghash_entryhash(gh, e); + const unsigned bucket_index = ghash_bucket_index(gh, hash); + e_next = e->next; + e->next = buckets_new[bucket_index]; + buckets_new[bucket_index] = e; + } + } + } + else { + for (i = 0; i < nbuckets_old; i++) { +#ifdef GHASH_USE_MODULO_BUCKETS + Entry *e_next; + for (e = buckets_old[i]; e; e = e_next) { + const unsigned hash = ghash_entryhash(gh, e); + const unsigned bucket_index = ghash_bucket_index(gh, hash); + e_next = e->next; + e->next = buckets_new[bucket_index]; + buckets_new[bucket_index] = e; + } +#else + /* No need to recompute hashes in this case, since our mask is just smaller, all items in old bucket i + * will go in same new bucket (i & new_mask)! */ + const unsigned bucket_index = ghash_bucket_index(gh, i); + BLI_assert(!buckets_old[i] || (bucket_index == ghash_bucket_index(gh, ghash_entryhash(gh, buckets_old[i])))); + for (e = buckets_old[i]; e && e->next; e = e->next); + if (e) { + e->next = buckets_new[bucket_index]; + buckets_new[bucket_index] = buckets_old[i]; + } +#endif + } } } gh->buckets = buckets_new; - MEM_freeN(buckets_old); + if (buckets_old) { + MEM_freeN(buckets_old); + } } /** - * Increase initial bucket size to match a reserved amount. + * Check if the number of items in the GHash is large enough to require more buckets, + * or small enough to require less buckets, and resize \a gh accordingly. */ -BLI_INLINE void ghash_buckets_reserve(GHash *gh, const unsigned int nentries_reserve) +static void ghash_buckets_expand( + GHash *gh, const unsigned int nentries, const bool user_defined) { - while (ghash_test_expand_buckets(nentries_reserve, gh->nbuckets)) { - gh->nbuckets = hashsizes[++gh->cursize]; + unsigned int new_nbuckets; + + if (LIKELY(gh->buckets && (nentries < gh->limit_grow))) { + return; + } + + new_nbuckets = gh->nbuckets; + +#ifdef GHASH_USE_MODULO_BUCKETS + while ((nentries > gh->limit_grow) && + (gh->cursize < GHASH_MAX_SIZE - 1)) + { + new_nbuckets = hashsizes[++gh->cursize]; + gh->limit_grow = GHASH_LIMIT_GROW(new_nbuckets); + } +#else + while ((nentries > gh->limit_grow) && + (gh->bucket_bit < GHASH_BUCKET_BIT_MAX)) + { + new_nbuckets = 1u << ++gh->bucket_bit; + gh->limit_grow = GHASH_LIMIT_GROW(new_nbuckets); + } +#endif + + if (user_defined) { +#ifdef GHASH_USE_MODULO_BUCKETS + gh->size_min = gh->cursize; +#else + gh->bucket_bit_min = gh->bucket_bit; +#endif + } + + if ((new_nbuckets == gh->nbuckets) && gh->buckets) { + return; + } + + gh->limit_grow = GHASH_LIMIT_GROW(new_nbuckets); + gh->limit_shrink = GHASH_LIMIT_SHRINK(new_nbuckets); + ghash_buckets_resize(gh, new_nbuckets); +} + +static void ghash_buckets_contract( + GHash *gh, const unsigned int nentries, const bool user_defined, const bool force_shrink) +{ + unsigned int new_nbuckets; + + if (!(force_shrink || (gh->flag & GHASH_FLAG_ALLOW_SHRINK))) { + return; + } + + if (LIKELY(gh->buckets && (nentries > gh->limit_shrink))) { + return; + } + + new_nbuckets = gh->nbuckets; + +#ifdef GHASH_USE_MODULO_BUCKETS + while ((nentries < gh->limit_shrink) && + (gh->cursize > gh->size_min)) + { + new_nbuckets = hashsizes[--gh->cursize]; + gh->limit_shrink = GHASH_LIMIT_SHRINK(new_nbuckets); + } +#else + while ((nentries < gh->limit_shrink) && + (gh->bucket_bit > gh->bucket_bit_min)) + { + new_nbuckets = 1u << --gh->bucket_bit; + gh->limit_shrink = GHASH_LIMIT_SHRINK(new_nbuckets); + } +#endif + + if (user_defined) { +#ifdef GHASH_USE_MODULO_BUCKETS + gh->size_min = gh->cursize; +#else + gh->bucket_bit_min = gh->bucket_bit; +#endif } + + if ((new_nbuckets == gh->nbuckets) && gh->buckets) { + return; + } + + gh->limit_grow = GHASH_LIMIT_GROW(new_nbuckets); + gh->limit_shrink = GHASH_LIMIT_SHRINK(new_nbuckets); + ghash_buckets_resize(gh, new_nbuckets); +} + +/** + * Clear and reset \a gh buckets, reserve again buckets for given number of entries. + */ +BLI_INLINE void ghash_buckets_reset(GHash *gh, const unsigned int nentries) +{ + MEM_SAFE_FREE(gh->buckets); + +#ifdef GHASH_USE_MODULO_BUCKETS + gh->cursize = 0; + gh->size_min = 0; + gh->nbuckets = hashsizes[gh->cursize]; +#else + gh->bucket_bit = GHASH_BUCKET_BIT_MIN; + gh->bucket_bit_min = GHASH_BUCKET_BIT_MIN; + gh->nbuckets = 1u << gh->bucket_bit; + gh->bucket_mask = gh->nbuckets - 1; +#endif + + gh->limit_grow = GHASH_LIMIT_GROW(gh->nbuckets); + gh->limit_shrink = GHASH_LIMIT_SHRINK(gh->nbuckets); + + gh->nentries = 0; + + ghash_buckets_expand(gh, nentries, (nentries != 0)); } /** * Internal lookup function. - * Takes a hash argument to avoid calling #ghash_keyhash multiple times. + * Takes hash and bucket_index arguments to avoid calling #ghash_keyhash and #ghash_bucket_index multiple times. */ -BLI_INLINE Entry *ghash_lookup_entry_ex(GHash *gh, const void *key, - const unsigned int hash) +BLI_INLINE Entry *ghash_lookup_entry_ex( + GHash *gh, const void *key, const unsigned int bucket_index) { Entry *e; + /* If we do not store GHash, not worth computing it for each entry here! + * Typically, comparison function will be quicker, and since it's needed in the end anyway... */ + for (e = gh->buckets[bucket_index]; e; e = e->next) { + if (UNLIKELY(gh->cmpfp(key, e->key) == false)) { + return e; + } + } + + return NULL; +} + +/** + * Internal lookup function, returns previous entry of target one too. + * Takes hash and bucket_index arguments to avoid calling #ghash_keyhash and #ghash_bucket_index multiple times. + * Useful when modifying buckets somehow (like removing an entry...). + */ +BLI_INLINE Entry *ghash_lookup_entry_prev_ex( + GHash *gh, const void *key, Entry **r_e_prev, const unsigned int bucket_index) +{ + Entry *e, *e_prev = NULL; - for (e = gh->buckets[hash]; e; e = e->next) { + /* If we do not store GHash, not worth computing it for each entry here! + * Typically, comparison function will be quicker, and since it's needed in the end anyway... */ + for (e = gh->buckets[bucket_index]; e; e_prev = e, e = e->next) { if (UNLIKELY(gh->cmpfp(key, e->key) == false)) { + *r_e_prev = e_prev; return e; } } + + *r_e_prev = NULL; return NULL; } @@ -169,105 +403,141 @@ BLI_INLINE Entry *ghash_lookup_entry_ex(GHash *gh, const void *key, BLI_INLINE Entry *ghash_lookup_entry(GHash *gh, const void *key) { const unsigned int hash = ghash_keyhash(gh, key); - return ghash_lookup_entry_ex(gh, key, hash); + const unsigned int bucket_index = ghash_bucket_index(gh, hash); + return ghash_lookup_entry_ex(gh, key, bucket_index); } static GHash *ghash_new(GHashHashFP hashfp, GHashCmpFP cmpfp, const char *info, - const unsigned int nentries_reserve, - const unsigned int entry_size) + const unsigned int nentries_reserve, const unsigned int flag) { GHash *gh = MEM_mallocN(sizeof(*gh), info); gh->hashfp = hashfp; gh->cmpfp = cmpfp; - gh->nbuckets = hashsizes[0]; /* gh->cursize */ - gh->nentries = 0; - gh->cursize = 0; - gh->flag = 0; - - /* if we have reserved the number of elements that this hash will contain */ - if (nentries_reserve) { - ghash_buckets_reserve(gh, nentries_reserve); - } + gh->buckets = NULL; + gh->flag = flag; - gh->buckets = MEM_callocN(gh->nbuckets * sizeof(*gh->buckets), "buckets"); - gh->entrypool = BLI_mempool_create(entry_size, 64, 64, BLI_MEMPOOL_NOP); + ghash_buckets_reset(gh, nentries_reserve); + gh->entrypool = BLI_mempool_create(GHASH_ENTRY_SIZE(flag & GHASH_FLAG_IS_GSET), 64, 64, BLI_MEMPOOL_NOP); return gh; } /** * Internal insert function. - * Takes a hash argument to avoid calling #ghash_keyhash multiple times. + * Takes hash and bucket_index arguments to avoid calling #ghash_keyhash and #ghash_bucket_index multiple times. */ -BLI_INLINE void ghash_insert_ex(GHash *gh, void *key, void *val, - unsigned int hash) +BLI_INLINE void ghash_insert_ex( + GHash *gh, void *key, void *val, const unsigned int bucket_index) { - Entry *e = (Entry *)BLI_mempool_alloc(gh->entrypool); + GHashEntry *e = BLI_mempool_alloc(gh->entrypool); + BLI_assert((gh->flag & GHASH_FLAG_ALLOW_DUPES) || (BLI_ghash_haskey(gh, key) == 0)); - IS_GHASH_ASSERT(gh); + BLI_assert(!(gh->flag & GHASH_FLAG_IS_GSET)); - e->next = gh->buckets[hash]; - e->key = key; + e->e.next = gh->buckets[bucket_index]; + e->e.key = key; e->val = val; - gh->buckets[hash] = e; + gh->buckets[bucket_index] = (Entry *)e; - if (UNLIKELY(ghash_test_expand_buckets(++gh->nentries, gh->nbuckets))) { - ghash_resize_buckets(gh, hashsizes[++gh->cursize]); - } + ghash_buckets_expand(gh, ++gh->nentries, false); } /** * Insert function that doesn't set the value (use for GSet) */ -BLI_INLINE void ghash_insert_ex_keyonly(GHash *gh, void *key, - unsigned int hash) +BLI_INLINE void ghash_insert_ex_keyonly( + GHash *gh, void *key, const unsigned int bucket_index) { - Entry *e = (Entry *)BLI_mempool_alloc(gh->entrypool); + GSetEntry *e = BLI_mempool_alloc(gh->entrypool); + BLI_assert((gh->flag & GHASH_FLAG_ALLOW_DUPES) || (BLI_ghash_haskey(gh, key) == 0)); - e->next = gh->buckets[hash]; + BLI_assert((gh->flag & GHASH_FLAG_IS_GSET) != 0); + + e->next = gh->buckets[bucket_index]; e->key = key; - /* intentionally leave value unset */ - gh->buckets[hash] = e; + gh->buckets[bucket_index] = (Entry *)e; - if (UNLIKELY(ghash_test_expand_buckets(++gh->nentries, gh->nbuckets))) { - ghash_resize_buckets(gh, hashsizes[++gh->cursize]); - } + ghash_buckets_expand(gh, ++gh->nentries, false); } BLI_INLINE void ghash_insert(GHash *gh, void *key, void *val) { const unsigned int hash = ghash_keyhash(gh, key); - ghash_insert_ex(gh, key, val, hash); + const unsigned int bucket_index = ghash_bucket_index(gh, hash); + + ghash_insert_ex(gh, key, val, bucket_index); +} + +BLI_INLINE bool ghash_insert_safe( + GHash *gh, void *key, void *val, const bool override, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp) +{ + const unsigned int hash = ghash_keyhash(gh, key); + const unsigned int bucket_index = ghash_bucket_index(gh, hash); + GHashEntry *e = (GHashEntry *)ghash_lookup_entry_ex(gh, key, bucket_index); + + BLI_assert(!(gh->flag & GHASH_FLAG_IS_GSET)); + + if (e) { + if (override) { + if (keyfreefp) keyfreefp(e->e.key); + if (valfreefp) valfreefp(e->val); + e->e.key = key; + e->val = val; + } + return false; + } + else { + ghash_insert_ex(gh, key, val, bucket_index); + return true; + } +} + +BLI_INLINE bool ghash_insert_safe_keyonly(GHash *gh, void *key, const bool override, GHashKeyFreeFP keyfreefp) +{ + const unsigned int hash = ghash_keyhash(gh, key); + const unsigned int bucket_index = ghash_bucket_index(gh, hash); + GSetEntry *e = ghash_lookup_entry_ex(gh, key, bucket_index); + + BLI_assert((gh->flag & GHASH_FLAG_IS_GSET) != 0); + + if (e) { + if (override) { + if (keyfreefp) keyfreefp(e->key); + e->key = key; + } + return false; + } + else { + ghash_insert_ex_keyonly(gh, key, bucket_index); + return true; + } } /** * Remove the entry and return it, caller must free from gh->entrypool. */ -static Entry *ghash_remove_ex(GHash *gh, void *key, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp, - unsigned int hash) +static Entry *ghash_remove_ex( + GHash *gh, void *key, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp, + const unsigned int bucket_index) { - Entry *e; - Entry *e_prev = NULL; + Entry *e_prev; + Entry *e = ghash_lookup_entry_prev_ex(gh, key, &e_prev, bucket_index); - for (e = gh->buckets[hash]; e; e = e->next) { - if (UNLIKELY(gh->cmpfp(key, e->key) == false)) { - Entry *e_next = e->next; + BLI_assert(!valfreefp|| !(gh->flag & GHASH_FLAG_IS_GSET)); - if (keyfreefp) keyfreefp(e->key); - if (valfreefp) valfreefp(e->val); + if (e) { + if (keyfreefp) keyfreefp(e->key); + if (valfreefp) valfreefp(((GHashEntry *)e)->val); - if (e_prev) e_prev->next = e_next; - else gh->buckets[hash] = e_next; + if (e_prev) e_prev->next = e->next; + else gh->buckets[bucket_index] = e->next; - gh->nentries--; - return e; - } - e_prev = e; + ghash_buckets_contract(gh, --gh->nentries, false, false); } - return NULL; + return e; } /** @@ -277,21 +547,57 @@ static void ghash_free_cb(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP va { unsigned int i; - BLI_assert(keyfreefp || valfreefp); + BLI_assert(keyfreefp || valfreefp); + BLI_assert(!valfreefp || !(gh->flag & GHASH_FLAG_IS_GSET)); for (i = 0; i < gh->nbuckets; i++) { Entry *e; - for (e = gh->buckets[i]; e; ) { - Entry *e_next = e->next; - + for (e = gh->buckets[i]; e; e = e->next) { if (keyfreefp) keyfreefp(e->key); - if (valfreefp) valfreefp(e->val); + if (valfreefp) valfreefp(((GHashEntry *)e)->val); + } + } +} + +/** + * Copy the GHash. + */ +static GHash *ghash_copy(GHash *gh, GHashKeyCopyFP keycopyfp, GHashValCopyFP valcopyfp) +{ + GHash *gh_new; + unsigned int i; + /* This allows us to be sure to get the same number of buckets in gh_new as in ghash. */ + const unsigned int reserve_nentries_new = MAX2(GHASH_LIMIT_GROW(gh->nbuckets) - 1, gh->nentries); + + BLI_assert(!valcopyfp || !(gh->flag & GHASH_FLAG_IS_GSET)); + + gh_new = ghash_new(gh->hashfp, gh->cmpfp, __func__, 0, gh->flag); + ghash_buckets_expand(gh_new, reserve_nentries_new, false); + + BLI_assert(gh_new->nbuckets == gh->nbuckets); + + for (i = 0; i < gh->nbuckets; i++) { + Entry *e; + + for (e = gh->buckets[i]; e; e = e->next) { + Entry *e_new = BLI_mempool_alloc(gh_new->entrypool); + ghash_entry_copy(gh_new, e_new, gh, e, keycopyfp, valcopyfp); - e = e_next; + /* Warning! + * This means entries in buckets in new copy will be in reversed order! + * This shall not be an issue though, since order should never be assumed in ghash. */ + + /* Note: We can use 'i' here, since we are sure that 'gh' and 'gh_new' have the same number of buckets! */ + e_new->next = gh_new->buckets[i]; + gh_new->buckets[i] = e_new; } } + gh_new->nentries = gh->nentries; + + return gh_new; } + /** \} */ @@ -311,9 +617,7 @@ static void ghash_free_cb(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP va GHash *BLI_ghash_new_ex(GHashHashFP hashfp, GHashCmpFP cmpfp, const char *info, const unsigned int nentries_reserve) { - return ghash_new(hashfp, cmpfp, info, - nentries_reserve, - (unsigned int)sizeof(Entry)); + return ghash_new(hashfp, cmpfp, info, nentries_reserve, 0); } /** @@ -325,6 +629,23 @@ GHash *BLI_ghash_new(GHashHashFP hashfp, GHashCmpFP cmpfp, const char *info) } /** + * Copy given GHash. Keys and values are also copied if relevant callback is provided, else pointers remain the same. + */ +GHash *BLI_ghash_copy(GHash *gh, GHashKeyCopyFP keycopyfp, GHashValCopyFP valcopyfp) +{ + return ghash_copy(gh, keycopyfp, valcopyfp); +} + +/** + * Reverve given ammount of entries (resize \a gh accordingly if needed). + */ +void BLI_ghash_reserve(GHash *gh, const unsigned int nentries_reserve) +{ + ghash_buckets_expand(gh, nentries_reserve, true); + ghash_buckets_contract(gh, nentries_reserve, true, false); +} + +/** * \return size of the GHash. */ int BLI_ghash_size(GHash *gh) @@ -353,19 +674,7 @@ void BLI_ghash_insert(GHash *gh, void *key, void *val) */ bool BLI_ghash_reinsert(GHash *gh, void *key, void *val, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp) { - const unsigned int hash = ghash_keyhash(gh, key); - Entry *e = ghash_lookup_entry_ex(gh, key, hash); - if (e) { - if (keyfreefp) keyfreefp(e->key); - if (valfreefp) valfreefp(e->val); - e->key = key; - e->val = val; - return false; - } - else { - ghash_insert_ex(gh, key, val, hash); - return true; - } + return ghash_insert_safe(gh, key, val, true, keyfreefp, valfreefp); } /** @@ -379,8 +688,8 @@ bool BLI_ghash_reinsert(GHash *gh, void *key, void *val, GHashKeyFreeFP keyfreef */ void *BLI_ghash_lookup(GHash *gh, const void *key) { - Entry *e = ghash_lookup_entry(gh, key); - IS_GHASH_ASSERT(gh); + GHashEntry *e = (GHashEntry *)ghash_lookup_entry(gh, key); + BLI_assert(!(gh->flag & GHASH_FLAG_IS_GSET)); return e ? e->val : NULL; } @@ -389,8 +698,8 @@ void *BLI_ghash_lookup(GHash *gh, const void *key) */ void *BLI_ghash_lookup_default(GHash *gh, const void *key, void *val_default) { - Entry *e = ghash_lookup_entry(gh, key); - IS_GHASH_ASSERT(gh); + GHashEntry *e = (GHashEntry *)ghash_lookup_entry(gh, key); + BLI_assert(!(gh->flag & GHASH_FLAG_IS_GSET)); return e ? e->val : val_default; } @@ -406,8 +715,8 @@ void *BLI_ghash_lookup_default(GHash *gh, const void *key, void *val_default) */ void **BLI_ghash_lookup_p(GHash *gh, const void *key) { - Entry *e = ghash_lookup_entry(gh, key); - IS_GHASH_ASSERT(gh); + GHashEntry *e = (GHashEntry *)ghash_lookup_entry(gh, key); + BLI_assert(!(gh->flag & GHASH_FLAG_IS_GSET)); return e ? &e->val : NULL; } @@ -422,7 +731,8 @@ void **BLI_ghash_lookup_p(GHash *gh, const void *key) bool BLI_ghash_remove(GHash *gh, void *key, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp) { const unsigned int hash = ghash_keyhash(gh, key); - Entry *e = ghash_remove_ex(gh, key, keyfreefp, valfreefp, hash); + const unsigned int bucket_index = ghash_bucket_index(gh, hash); + Entry *e = ghash_remove_ex(gh, key, keyfreefp, valfreefp, bucket_index); if (e) { BLI_mempool_free(gh->entrypool, e); return true; @@ -444,8 +754,9 @@ bool BLI_ghash_remove(GHash *gh, void *key, GHashKeyFreeFP keyfreefp, GHashValFr void *BLI_ghash_popkey(GHash *gh, void *key, GHashKeyFreeFP keyfreefp) { const unsigned int hash = ghash_keyhash(gh, key); - Entry *e = ghash_remove_ex(gh, key, keyfreefp, NULL, hash); - IS_GHASH_ASSERT(gh); + const unsigned int bucket_index = ghash_bucket_index(gh, hash); + GHashEntry *e = (GHashEntry *)ghash_remove_ex(gh, key, keyfreefp, NULL, bucket_index); + BLI_assert(!(gh->flag & GHASH_FLAG_IS_GSET)); if (e) { void *val = e->val; BLI_mempool_free(gh->entrypool, e); @@ -477,17 +788,7 @@ void BLI_ghash_clear_ex(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valf if (keyfreefp || valfreefp) ghash_free_cb(gh, keyfreefp, valfreefp); - gh->nbuckets = hashsizes[0]; /* gh->cursize */ - gh->nentries = 0; - gh->cursize = 0; - - if (nentries_reserve) { - ghash_buckets_reserve(gh, nentries_reserve); - } - - MEM_freeN(gh->buckets); - gh->buckets = MEM_callocN(gh->nbuckets * sizeof(*gh->buckets), "buckets"); - + ghash_buckets_reset(gh, nentries_reserve); BLI_mempool_clear_ex(gh->entrypool, nentries_reserve ? (int)nentries_reserve : -1); } @@ -701,6 +1002,10 @@ unsigned int BLI_ghashutil_uinthash_v4(const unsigned int key[4]) hash += key[3]; return hash; } +unsigned int BLI_ghashutil_uinthash_v4_murmur(const unsigned int key[4]) +{ + return BLI_hash_mm2((const unsigned char *)key, sizeof(key), 0); +} bool BLI_ghashutil_uinthash_v4_cmp(const void *a, const void *b) { @@ -733,6 +1038,13 @@ unsigned int BLI_ghashutil_inthash_p(const void *ptr) return (unsigned int)(key & 0xffffffff); } +unsigned int BLI_ghashutil_inthash_p_murmur(const void *ptr) +{ + uintptr_t key = (uintptr_t)ptr; + + return BLI_hash_mm2((const unsigned char *)&key, sizeof(key), 0); +} + bool BLI_ghashutil_intcmp(const void *a, const void *b) { return (a != b); @@ -769,9 +1081,15 @@ unsigned int BLI_ghashutil_strhash_p(const void *ptr) return h; } +unsigned int BLI_ghashutil_strhash_p_murmur(const void *ptr) +{ + const unsigned char *key = ptr; + + return BLI_hash_mm2(key, strlen((const char *)key) + 1, 0); +} bool BLI_ghashutil_strcmp(const void *a, const void *b) { - return (!STREQ(a, b)); + return (a == b) ? false : !STREQ(a, b); } GHashPair *BLI_ghashutil_pairalloc(const void *first, const void *second) @@ -809,44 +1127,36 @@ void BLI_ghashutil_pairfree(void *ptr) /** \name Convenience GHash Creation Functions * \{ */ -GHash *BLI_ghash_ptr_new_ex(const char *info, - const unsigned int nentries_reserve) +GHash *BLI_ghash_ptr_new_ex(const char *info, const unsigned int nentries_reserve) { - return BLI_ghash_new_ex(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, info, - nentries_reserve); + return BLI_ghash_new_ex(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, info, nentries_reserve); } GHash *BLI_ghash_ptr_new(const char *info) { return BLI_ghash_ptr_new_ex(info, 0); } -GHash *BLI_ghash_str_new_ex(const char *info, - const unsigned int nentries_reserve) +GHash *BLI_ghash_str_new_ex(const char *info, const unsigned int nentries_reserve) { - return BLI_ghash_new_ex(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, info, - nentries_reserve); + return BLI_ghash_new_ex(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, info, nentries_reserve); } GHash *BLI_ghash_str_new(const char *info) { return BLI_ghash_str_new_ex(info, 0); } -GHash *BLI_ghash_int_new_ex(const char *info, - const unsigned int nentries_reserve) +GHash *BLI_ghash_int_new_ex(const char *info, const unsigned int nentries_reserve) { - return BLI_ghash_new_ex(BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, info, - nentries_reserve); + return BLI_ghash_new_ex(BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, info, nentries_reserve); } GHash *BLI_ghash_int_new(const char *info) { return BLI_ghash_int_new_ex(info, 0); } -GHash *BLI_ghash_pair_new_ex(const char *info, - const unsigned int nentries_reserve) +GHash *BLI_ghash_pair_new_ex(const char *info, const unsigned int nentries_reserve) { - return BLI_ghash_new_ex(BLI_ghashutil_pairhash, BLI_ghashutil_paircmp, info, - nentries_reserve); + return BLI_ghash_new_ex(BLI_ghashutil_pairhash, BLI_ghashutil_paircmp, info, nentries_reserve); } GHash *BLI_ghash_pair_new(const char *info) { @@ -861,21 +1171,12 @@ GHash *BLI_ghash_pair_new(const char *info) /* Use ghash API to give 'set' functionality */ -/* TODO: typical set functions - * isdisjoint/issubset/issuperset/union/intersection/difference etc */ - /** \name GSet Functions * \{ */ GSet *BLI_gset_new_ex(GSetHashFP hashfp, GSetCmpFP cmpfp, const char *info, const unsigned int nentries_reserve) { - GSet *gs = (GSet *)ghash_new(hashfp, cmpfp, info, - nentries_reserve, - sizeof(Entry) - sizeof(void *)); -#ifndef NDEBUG - ((GHash *)gs)->flag |= GHASH_FLAG_IS_SET; -#endif - return gs; + return (GSet *)ghash_new(hashfp, cmpfp, info, nentries_reserve, GHASH_FLAG_IS_GSET); } GSet *BLI_gset_new(GSetHashFP hashfp, GSetCmpFP cmpfp, const char *info) @@ -883,6 +1184,14 @@ GSet *BLI_gset_new(GSetHashFP hashfp, GSetCmpFP cmpfp, const char *info) return BLI_gset_new_ex(hashfp, cmpfp, info, 0); } +/** + * Copy given GSet. Keys are also copied if callback is provided, else pointers remain the same. + */ +GSet *BLI_gset_copy(GSet *gs, GHashKeyCopyFP keycopyfp) +{ + return (GSet *)ghash_copy((GHash *)gs, keycopyfp, NULL); +} + int BLI_gset_size(GSet *gs) { return (int)((GHash *)gs)->nentries; @@ -895,7 +1204,8 @@ int BLI_gset_size(GSet *gs) void BLI_gset_insert(GSet *gs, void *key) { const unsigned int hash = ghash_keyhash((GHash *)gs, key); - ghash_insert_ex_keyonly((GHash *)gs, key, hash); + const unsigned int bucket_index = ghash_bucket_index((GHash *)gs, hash); + ghash_insert_ex_keyonly((GHash *)gs, key, bucket_index); } /** @@ -906,15 +1216,7 @@ void BLI_gset_insert(GSet *gs, void *key) */ bool BLI_gset_add(GSet *gs, void *key) { - const unsigned int hash = ghash_keyhash((GHash *)gs, key); - Entry *e = ghash_lookup_entry_ex((GHash *)gs, key, hash); - if (e) { - return false; - } - else { - ghash_insert_ex_keyonly((GHash *)gs, key, hash); - return true; - } + return ghash_insert_safe_keyonly((GHash *)gs, key, false, NULL); } /** @@ -925,17 +1227,7 @@ bool BLI_gset_add(GSet *gs, void *key) */ bool BLI_gset_reinsert(GSet *gs, void *key, GSetKeyFreeFP keyfreefp) { - const unsigned int hash = ghash_keyhash((GHash *)gs, key); - Entry *e = ghash_lookup_entry_ex((GHash *)gs, key, hash); - if (e) { - if (keyfreefp) keyfreefp(e->key); - e->key = key; - return false; - } - else { - ghash_insert_ex_keyonly((GHash *)gs, key, hash); - return true; - } + return ghash_insert_safe_keyonly((GHash *)gs, key, true, keyfreefp); } bool BLI_gset_remove(GSet *gs, void *key, GSetKeyFreeFP keyfreefp) @@ -982,22 +1274,18 @@ void BLI_gset_flag_clear(GSet *gs, unsigned int flag) /** \name Convenience GSet Creation Functions * \{ */ -GSet *BLI_gset_ptr_new_ex(const char *info, - const unsigned int nentries_reserve) +GSet *BLI_gset_ptr_new_ex(const char *info, const unsigned int nentries_reserve) { - return BLI_gset_new_ex(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, info, - nentries_reserve); + return BLI_gset_new_ex(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, info, nentries_reserve); } GSet *BLI_gset_ptr_new(const char *info) { return BLI_gset_ptr_new_ex(info, 0); } -GSet *BLI_gset_pair_new_ex(const char *info, - const unsigned int nentries_reserve) +GSet *BLI_gset_pair_new_ex(const char *info, const unsigned int nentries_reserve) { - return BLI_gset_new_ex(BLI_ghashutil_pairhash, BLI_ghashutil_paircmp, info, - nentries_reserve); + return BLI_gset_new_ex(BLI_ghashutil_pairhash, BLI_ghashutil_paircmp, info, nentries_reserve); } GSet *BLI_gset_pair_new(const char *info) { @@ -1009,37 +1297,126 @@ GSet *BLI_gset_pair_new(const char *info) /** \name Debugging & Introspection * \{ */ -#ifdef DEBUG + +#include "BLI_math.h" /** - * Measure how well the hash function performs - * (1.0 is approx as good as random distribution). + * \return number of buckets in the GHash. + */ +int BLI_ghash_buckets_size(GHash *gh) +{ + return (int)gh->nbuckets; +} +int BLI_gset_buckets_size(GSet *gs) +{ + return BLI_ghash_buckets_size((GHash *)gs); +} + +/** + * Measure how well the hash function performs (1.0 is approx as good as random distribution), + * and return a few other stats like load, variance of the distribution of the entries in the buckets, etc. * * Smaller is better! */ -double BLI_ghash_calc_quality(GHash *gh) +double BLI_ghash_calc_quality_ex( + GHash *gh, double *r_load, double *r_variance, + double *r_prop_empty_buckets, double *r_prop_overloaded_buckets, int *r_biggest_bucket) { - uint64_t sum = 0; + double mean; unsigned int i; - if (gh->nentries == 0) - return -1.0; + if (gh->nentries == 0) { + if (r_load) { + *r_load = 0.0; + } + if (r_variance) { + *r_variance = 0.0; + } + if (r_prop_empty_buckets) { + *r_prop_empty_buckets = 1.0; + } + if (r_prop_overloaded_buckets) { + *r_prop_overloaded_buckets = 0.0; + } + if (r_biggest_bucket) { + *r_biggest_bucket = 0; + } + + return 0.0; + } - for (i = 0; i < gh->nbuckets; i++) { - uint64_t count = 0; - Entry *e; - for (e = gh->buckets[i]; e; e = e->next) { - count += 1; + mean = (double)gh->nentries / (double)gh->nbuckets; + if (r_load) { + *r_load = mean; + } + if (r_biggest_bucket) { + *r_biggest_bucket = 0; + } + + if (r_variance) { + /* We already know our mean (i.e. load factor), easy to compute variance. + * See http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Two-pass_algorithm + */ + double sum = 0.0; + for (i = 0; i < gh->nbuckets; i++) { + int count = 0; + Entry *e; + for (e = gh->buckets[i]; e; e = e->next) { + count++; + } + sum += ((double)count - mean) * ((double)count - mean); + } + *r_variance = sum / (double)(gh->nbuckets - 1); + } + + { + uint64_t sum = 0; + uint64_t overloaded_buckets_threshold = (uint64_t)max_ii(GHASH_LIMIT_GROW(1), 1); + uint64_t sum_overloaded = 0; + uint64_t sum_empty = 0; + + for (i = 0; i < gh->nbuckets; i++) { + uint64_t count = 0; + Entry *e; + for (e = gh->buckets[i]; e; e = e->next) { + count++; + } + if (r_biggest_bucket) { + *r_biggest_bucket = max_ii(*r_biggest_bucket, (int)count); + } + if (r_prop_overloaded_buckets && (count > overloaded_buckets_threshold)) { + sum_overloaded++; + } + if (r_prop_empty_buckets && !count) { + sum_empty++; + } + sum += count * (count + 1); + } + if (r_prop_overloaded_buckets) { + *r_prop_overloaded_buckets = (double)sum_overloaded / (double)gh->nbuckets; + } + if (r_prop_empty_buckets) { + *r_prop_empty_buckets = (double)sum_empty / (double)gh->nbuckets; } - sum += count * (count + 1); + return ((double)sum * (double)gh->nbuckets / + ((double)gh->nentries * (gh->nentries + 2 * gh->nbuckets - 1))); } - return ((double)sum * (double)gh->nbuckets / - ((double)gh->nentries * (gh->nentries + 2 * gh->nbuckets - 1))); +} +double BLI_gset_calc_quality_ex( + GSet *gs, double *r_load, double *r_variance, + double *r_prop_empty_buckets, double *r_prop_overloaded_buckets, int *r_biggest_bucket) +{ + return BLI_ghash_calc_quality_ex((GHash *)gs, r_load, r_variance, + r_prop_empty_buckets, r_prop_overloaded_buckets, r_biggest_bucket); +} + +double BLI_ghash_calc_quality(GHash *gh) +{ + return BLI_ghash_calc_quality_ex(gh, NULL, NULL, NULL, NULL, NULL); } double BLI_gset_calc_quality(GSet *gs) { - return BLI_ghash_calc_quality((GHash *)gs); + return BLI_ghash_calc_quality_ex((GHash *)gs, NULL, NULL, NULL, NULL, NULL); } -#endif /** \} */ diff --git a/source/blender/blenlib/intern/hash_mm2a.c b/source/blender/blenlib/intern/hash_mm2a.c index bae098ae96b..af6ef4f355f 100644 --- a/source/blender/blenlib/intern/hash_mm2a.c +++ b/source/blender/blenlib/intern/hash_mm2a.c @@ -49,6 +49,13 @@ (h) = ((h) * MM2A_M) ^ (k); \ } (void)0 +#define MM2A_MIX_FINALIZE(h) \ +{ \ + (h) ^= (h) >> 13; \ + (h) *= MM2A_M; \ + (h) ^= (h) >> 15; \ +} (void)0 + static void mm2a_mix_tail(BLI_HashMurmur2A *mm2, const unsigned char **data, size_t *len) { while (*len && ((*len < 4) || mm2->count)) { @@ -99,9 +106,40 @@ uint32_t BLI_hash_mm2a_end(BLI_HashMurmur2A *mm2) MM2A_MIX(mm2->hash, mm2->tail); MM2A_MIX(mm2->hash, mm2->size); - mm2->hash ^= mm2->hash >> 13; - mm2->hash *= MM2A_M; - mm2->hash ^= mm2->hash >> 15; + MM2A_MIX_FINALIZE(mm2->hash); return mm2->hash; } + +/* Non-incremental version, quicker for small keys. */ +uint32_t BLI_hash_mm2(const unsigned char *data, size_t len, uint32_t seed) +{ + /* Initialize the hash to a 'random' value */ + uint32_t h = seed ^ len; + + /* Mix 4 bytes at a time into the hash */ + for (; len >= 4; data += 4, len -= 4) { + uint32_t k = *(uint32_t *)data; + + MM2A_MIX(h, k); + } + + /* Handle the last few bytes of the input array */ + switch (len) { + case 3: + h ^= data[2] << 16; + /* fall through */ + case 2: + h ^= data[1] << 8; + /* fall through */ + case 1: + h ^= data[0]; + h *= MM2A_M; + } + + /* Do a few final mixes of the hash to ensure the last few bytes are well-incorporated. */ + MM2A_MIX_FINALIZE(h); + + return h; +} + diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 8c8b576456b..8d2063c2afc 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1952,7 +1952,6 @@ static void direct_link_palette(FileData *fd, Palette *palette) { /* palette itself has been read */ link_list(fd, &palette->colors); - BLI_listbase_clear(&palette->deleted); } static void lib_link_paint_curve(FileData *UNUSED(fd), Main *main) diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index ee015fa5652..adf5f93ae16 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -616,18 +616,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } } } - - if (!DNA_struct_elem_find(fd->filesdna, "bSteeringActuator", "float", "acceleration")) { - for (ob = main->object.first; ob; ob = ob->id.next) { - bActuator *act; - for (act = ob->actuators.first; act; act = act->next) { - if (act->type == ACT_STEERING) { - bSteeringActuator *sact = act->data; - sact->acceleration = 1000.f; - } - } - } - } } if (!MAIN_VERSION_ATLEAST(main, 273, 9)) { diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cpp b/source/blender/compositor/intern/COM_NodeOperationBuilder.cpp index 74c05c3e62e..45105bade08 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cpp +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cpp @@ -101,12 +101,12 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) } } + resolve_proxies(); + add_datatype_conversions(); add_operation_input_constants(); - resolve_proxies(); - determineResolutions(); /* surround complex ops with read/write buffer */ diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index 5cc2101650e..8074182c395 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -90,7 +90,7 @@ EditBone *ED_armature_edit_bone_add_primitive(Object *obedit_arm, float length, bArmature *arm = obedit_arm->data; EditBone *bone; - ED_armature_deselect_all(obedit_arm, 0); + ED_armature_deselect_all(obedit_arm); /* Create a bone */ bone = ED_armature_edit_bone_add(arm, "Bone"); @@ -145,7 +145,7 @@ static int armature_click_extrude_exec(bContext *C, wmOperator *UNUSED(op)) to_root = 1; } - ED_armature_deselect_all(obedit, 0); + ED_armature_deselect_all(obedit); /* we re-use code for mirror editing... */ flipbone = NULL; @@ -639,6 +639,7 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op) * Set the duplicate->parent to NULL */ ebone->parent = NULL; + ebone->flag &= ~BONE_CONNECTED; } else { /* the parent may have been duplicated, if not lookup the mirror parent */ @@ -652,6 +653,7 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op) * So just use the same parent for both. */ ebone_parent = ebone_iter->parent; + ebone->flag &= ~BONE_CONNECTED; } ebone->parent = ebone_parent; @@ -912,7 +914,7 @@ static int armature_bone_primitive_add_exec(bContext *C, wmOperator *op) mul_m3_m3m3(totmat, obmat, viewmat); invert_m3_m3(imat, totmat); - ED_armature_deselect_all(obedit, 0); + ED_armature_deselect_all(obedit); /* Create a bone */ bone = ED_armature_edit_bone_add(obedit->data, name); diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c index 88c52989c07..29b7872f304 100644 --- a/source/blender/editors/armature/armature_edit.c +++ b/source/blender/editors/armature/armature_edit.c @@ -578,6 +578,7 @@ static int armature_fill_bones_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); ListBase points = {NULL, NULL}; + EditBone *newbone = NULL; int count; /* sanity checks */ @@ -610,94 +611,97 @@ static int armature_fill_bones_exec(bContext *C, wmOperator *op) float curs[3]; /* Get Points - selected joint */ - ebp = (EditBonePoint *)points.first; + ebp = points.first; /* Get points - cursor (tail) */ invert_m4_m4(obedit->imat, obedit->obmat); mul_v3_m4v3(curs, obedit->imat, ED_view3d_cursor3d_get(scene, v3d)); /* Create a bone */ - /* newbone = */ add_points_bone(obedit, ebp->vec, curs); + newbone = add_points_bone(obedit, ebp->vec, curs); } else if (count == 2) { - EditBonePoint *ebp, *ebp2; + EditBonePoint *ebp_a, *ebp_b; float head[3], tail[3]; short headtail = 0; /* check that the points don't belong to the same bone */ - ebp = (EditBonePoint *)points.first; - ebp2 = ebp->next; + ebp_a = (EditBonePoint *)points.first; + ebp_b = ebp_a->next; - if ((ebp->head_owner == ebp2->tail_owner) && (ebp->head_owner != NULL)) { - BKE_report(op->reports, RPT_ERROR, "Same bone selected..."); - BLI_freelistN(&points); - return OPERATOR_CANCELLED; - } - if ((ebp->tail_owner == ebp2->head_owner) && (ebp->tail_owner != NULL)) { + if (((ebp_a->head_owner == ebp_b->tail_owner) && (ebp_a->head_owner != NULL)) || + ((ebp_a->tail_owner == ebp_b->head_owner) && (ebp_a->tail_owner != NULL))) + { BKE_report(op->reports, RPT_ERROR, "Same bone selected..."); BLI_freelistN(&points); return OPERATOR_CANCELLED; } /* find which one should be the 'head' */ - if ((ebp->head_owner && ebp2->head_owner) || (ebp->tail_owner && ebp2->tail_owner)) { - /* rule: whichever one is closer to 3d-cursor */ - float curs[3]; - float vecA[3], vecB[3]; - float distA, distB; - - /* get cursor location */ - invert_m4_m4(obedit->imat, obedit->obmat); - mul_v3_m4v3(curs, obedit->imat, ED_view3d_cursor3d_get(scene, v3d)); - - /* get distances */ - sub_v3_v3v3(vecA, ebp->vec, curs); - sub_v3_v3v3(vecB, ebp2->vec, curs); - distA = len_v3(vecA); - distB = len_v3(vecB); - - /* compare distances - closer one therefore acts as direction for bone to go */ - headtail = (distA < distB) ? 2 : 1; + if ((ebp_a->head_owner && ebp_b->head_owner) || (ebp_a->tail_owner && ebp_b->tail_owner)) { + /* use active, nice predictable */ + if (arm->act_edbone && ELEM(arm->act_edbone, ebp_a->head_owner, ebp_a->tail_owner)) { + headtail = 1; + } + else if (arm->act_edbone && ELEM(arm->act_edbone, ebp_b->head_owner, ebp_b->tail_owner)) { + headtail = 2; + } + else { + /* rule: whichever one is closer to 3d-cursor */ + float curs[3]; + float dist_sq_a, dist_sq_b; + + /* get cursor location */ + invert_m4_m4(obedit->imat, obedit->obmat); + mul_v3_m4v3(curs, obedit->imat, ED_view3d_cursor3d_get(scene, v3d)); + + /* get distances */ + dist_sq_a = len_squared_v3v3(ebp_a->vec, curs); + dist_sq_b = len_squared_v3v3(ebp_b->vec, curs); + + /* compare distances - closer one therefore acts as direction for bone to go */ + headtail = (dist_sq_a < dist_sq_b) ? 2 : 1; + } } - else if (ebp->head_owner) { + else if (ebp_a->head_owner) { headtail = 1; } - else if (ebp2->head_owner) { + else if (ebp_b->head_owner) { headtail = 2; } /* assign head/tail combinations */ if (headtail == 2) { - copy_v3_v3(head, ebp->vec); - copy_v3_v3(tail, ebp2->vec); + copy_v3_v3(head, ebp_a->vec); + copy_v3_v3(tail, ebp_b->vec); } else if (headtail == 1) { - copy_v3_v3(head, ebp2->vec); - copy_v3_v3(tail, ebp->vec); + copy_v3_v3(head, ebp_b->vec); + copy_v3_v3(tail, ebp_a->vec); } /* add new bone and parent it to the appropriate end */ if (headtail) { - EditBone *newbone = add_points_bone(obedit, head, tail); + newbone = add_points_bone(obedit, head, tail); /* do parenting (will need to set connected flag too) */ if (headtail == 2) { /* ebp tail or head - tail gets priority */ - if (ebp->tail_owner) - newbone->parent = ebp->tail_owner; + if (ebp_a->tail_owner) + newbone->parent = ebp_a->tail_owner; else - newbone->parent = ebp->head_owner; + newbone->parent = ebp_a->head_owner; } else { - /* ebp2 tail or head - tail gets priority */ - if (ebp2->tail_owner) - newbone->parent = ebp2->tail_owner; + /* ebp_b tail or head - tail gets priority */ + if (ebp_b->tail_owner) + newbone->parent = ebp_b->tail_owner; else - newbone->parent = ebp2->head_owner; + newbone->parent = ebp_b->head_owner; } /* don't set for bone connecting two head points of bones */ - if (ebp->tail_owner || ebp2->tail_owner) { + if (ebp_a->tail_owner || ebp_b->tail_owner) { newbone->flag |= BONE_CONNECTED; } } @@ -708,6 +712,12 @@ static int armature_fill_bones_exec(bContext *C, wmOperator *op) BLI_freelistN(&points); return OPERATOR_CANCELLED; } + + if (newbone) { + ED_armature_deselect_all(obedit); + arm->act_edbone = newbone; + newbone->flag |= BONE_TIPSEL; + } /* updates */ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit); diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 581dd00e285..720b9b7821a 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -388,61 +388,14 @@ static EditBone *get_nearest_editbonepoint(ViewContext *vc, const int mval[2], return NULL; } - - -/* toggle==0: deselect - * toggle==1: swap (based on test) - * toggle==2: swap (no test), CURRENTLY UNUSED - */ -void ED_armature_deselect_all(Object *obedit, int toggle) +void ED_armature_deselect_all(Object *obedit) { bArmature *arm = obedit->data; - EditBone *eBone; - int sel = 1; - - if (toggle == 1) { - /* Determine if there are any selected bones - * and therefore whether we are selecting or deselecting */ - for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { - // if (arm->layer & eBone->layer) { - if (eBone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) { - sel = 0; - break; - } - // } - } - } - else { - sel = toggle; - } + EditBone *ebone; - /* Set the flags */ - for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { - if (sel == 2) { - /* invert selection of bone */ - if (EBONE_VISIBLE(arm, eBone)) { - eBone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - if (arm->act_edbone == eBone) - arm->act_edbone = NULL; - } - } - else if (sel == 1) { - /* select bone */ - if (EBONE_VISIBLE(arm, eBone)) { - eBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - if (eBone->parent) - eBone->parent->flag |= (BONE_TIPSEL); - } - } - else { - /* deselect bone */ - eBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - if (arm->act_edbone == eBone) - arm->act_edbone = NULL; - } + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); } - - ED_armature_sync_selection(arm->edbo); } void ED_armature_deselect_all_visible(Object *obedit) @@ -489,8 +442,9 @@ bool mouse_armature(bContext *C, const int mval[2], bool extend, bool deselect, nearBone = get_nearest_editbonepoint(&vc, mval, arm->edbo, 1, &selmask); if (nearBone) { - if (!extend && !deselect && !toggle) - ED_armature_deselect_all(obedit, 0); + if (!extend && !deselect && !toggle) { + ED_armature_deselect_all(obedit); + } /* by definition the non-root connected bones have no root point drawn, * so a root selection needs to be delivered to the parent tip */ @@ -962,75 +916,78 @@ void ARMATURE_OT_select_similar(wmOperatorType *ot) /* ********************* select hierarchy operator ************** */ -/* Get the first available child of an editbone */ -static EditBone *editbone_get_child(bArmature *arm, EditBone *pabone, short use_visibility) -{ - EditBone *curbone, *chbone = NULL; - - for (curbone = arm->edbo->first; curbone; curbone = curbone->next) { - if (curbone->parent == pabone) { - if (use_visibility) { - if ((arm->layer & curbone->layer) && !(pabone->flag & BONE_HIDDEN_A)) { - chbone = curbone; - } - } - else - chbone = curbone; - } - } - - return chbone; -} - static int armature_select_hierarchy_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); Object *ob; bArmature *arm; - EditBone *curbone, *pabone, *chbone; + EditBone *ebone_active; int direction = RNA_enum_get(op->ptr, "direction"); const bool add_to_sel = RNA_boolean_get(op->ptr, "extend"); + bool changed = false; ob = obedit; arm = (bArmature *)ob->data; - - for (curbone = arm->edbo->first; curbone; curbone = curbone->next) { - /* only work on bone if it is visible and its selection can change */ - if (EBONE_SELECTABLE(arm, curbone)) { - if (curbone == arm->act_edbone) { - if (direction == BONE_SELECT_PARENT) { - if (curbone->parent == NULL) continue; - else pabone = curbone->parent; - - if (EBONE_VISIBLE(arm, pabone)) { - pabone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_edbone = pabone; - if (pabone->parent) pabone->parent->flag |= BONE_TIPSEL; - - if (!add_to_sel) curbone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - break; - } - + + ebone_active = arm->act_edbone; + if (ebone_active == NULL) { + return OPERATOR_CANCELLED; + } + + if (direction == BONE_SELECT_PARENT) { + if (ebone_active->parent) { + EditBone *ebone_parent; + + ebone_parent = ebone_active->parent; + + if (EBONE_SELECTABLE(arm, ebone_parent)) { + arm->act_edbone = ebone_parent; + + if (!add_to_sel) { + ED_armature_ebone_select_set(ebone_active, false); } - else { // BONE_SELECT_CHILD - chbone = editbone_get_child(arm, curbone, 1); - if (chbone == NULL) continue; - - if (EBONE_SELECTABLE(arm, chbone)) { - chbone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_edbone = chbone; - - if (!add_to_sel) { - curbone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL); - if (curbone->parent) curbone->parent->flag &= ~BONE_TIPSEL; + ED_armature_ebone_select_set(ebone_parent, true); + + changed = true; + } + } + + } + else { /* BONE_SELECT_CHILD */ + EditBone *ebone_iter, *ebone_child = NULL; + int pass; + + /* first pass, only connected bones (the logical direct child) */ + for (pass = 0; pass < 2 && (ebone_child == NULL); pass++) { + for (ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { + /* possible we have multiple children, some invisible */ + if (EBONE_SELECTABLE(arm, ebone_iter)) { + if (ebone_iter->parent == ebone_active) { + if ((pass == 1) || (ebone_iter->flag & BONE_CONNECTED)) { + ebone_child = ebone_iter; + break; } - break; } } } } + + if (ebone_child) { + arm->act_edbone = ebone_child; + + if (!add_to_sel) { + ED_armature_ebone_select_set(ebone_active, false); + } + ED_armature_ebone_select_set(ebone_child, true); + + changed = true; + } } + if (changed == false) { + return OPERATOR_CANCELLED; + } + ED_armature_sync_selection(arm->edbo); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c index 549a3854bac..bc900315247 100644 --- a/source/blender/editors/armature/pose_edit.c +++ b/source/blender/editors/armature/pose_edit.c @@ -862,7 +862,7 @@ static int pose_bone_layers_invoke(bContext *C, wmOperator *op, const wmEvent *e /* loop over the bits for this pchan's layers, adding layers where they're needed */ for (bit = 0; bit < 32; bit++) { - layers[bit] = (pchan->bone->layer & (1 << bit)) != 0; + layers[bit] = (pchan->bone->layer & (1u << bit)) != 0; } } CTX_DATA_END; @@ -936,8 +936,9 @@ static int armature_bone_layers_invoke(bContext *C, wmOperator *op, const wmEven /* loop over the bits for this pchan's layers, adding layers where they're needed */ for (bit = 0; bit < 32; bit++) { - if (ebone->layer & (1 << bit)) + if (ebone->layer & (1u << bit)) { layers[bit] = 1; + } } } CTX_DATA_END; diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index 5d5a9bf363c..a4a21a0a225 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -518,71 +518,67 @@ static int pose_select_hierarchy_exec(bContext *C, wmOperator *op) { Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); bArmature *arm = ob->data; - Bone *curbone, *pabone, *chbone; + bPoseChannel *pchan_act; int direction = RNA_enum_get(op->ptr, "direction"); const bool add_to_sel = RNA_boolean_get(op->ptr, "extend"); - bool found = false; + bool changed = false; - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) - { - curbone = pchan->bone; - - if ((curbone->flag & BONE_UNSELECTABLE) == 0) { - if (curbone == arm->act_bone) { - if (direction == BONE_SELECT_PARENT) { - if (pchan->parent == NULL) continue; - else pabone = pchan->parent->bone; - - if (PBONE_SELECTABLE(arm, pabone)) { - if (!add_to_sel) curbone->flag &= ~BONE_SELECTED; - pabone->flag |= BONE_SELECTED; - arm->act_bone = pabone; - - found = 1; - break; - } + pchan_act = BKE_pose_channel_active(ob); + if (pchan_act == NULL) { + return OPERATOR_CANCELLED; + } + + if (direction == BONE_SELECT_PARENT) { + if (pchan_act->parent) { + Bone *bone_parent; + bone_parent = pchan_act->parent->bone; + + if (PBONE_SELECTABLE(arm, bone_parent)) { + if (!add_to_sel) { + pchan_act->bone->flag &= ~BONE_SELECTED; } - else { /* direction == BONE_SELECT_CHILD */ - /* the child member is only assigned to connected bones, see [#30340] */ -#if 0 - if (pchan->child == NULL) continue; - else chbone = pchan->child->bone; -#else - /* instead. find _any_ visible child bone, using the first one is a little arbitrary - campbell */ - chbone = pchan->child ? pchan->child->bone : NULL; - if (chbone == NULL) { - bPoseChannel *pchan_child; - - for (pchan_child = ob->pose->chanbase.first; pchan_child; pchan_child = pchan_child->next) { - /* possible we have multiple children, some invisible */ - if (PBONE_SELECTABLE(arm, pchan_child->bone)) { - if (pchan_child->parent == pchan) { - chbone = pchan_child->bone; - break; - } - } - } - } + bone_parent->flag |= BONE_SELECTED; + arm->act_bone = bone_parent; - if (chbone == NULL) continue; -#endif - - if (PBONE_SELECTABLE(arm, chbone)) { - if (!add_to_sel) curbone->flag &= ~BONE_SELECTED; - chbone->flag |= BONE_SELECTED; - arm->act_bone = chbone; - - found = 1; - break; + changed = true; + } + } + } + else { /* direction == BONE_SELECT_CHILD */ + bPoseChannel *pchan_iter; + Bone *bone_child = NULL; + int pass; + + /* first pass, only connected bones (the logical direct child) */ + for (pass = 0; pass < 2 && (bone_child == NULL); pass++) { + for (pchan_iter = ob->pose->chanbase.first; pchan_iter; pchan_iter = pchan_iter->next) { + /* possible we have multiple children, some invisible */ + if (PBONE_SELECTABLE(arm, pchan_iter->bone)) { + if (pchan_iter->parent == pchan_act) { + if ((pass == 1) || (pchan_iter->bone->flag & BONE_CONNECTED)) { + bone_child = pchan_iter->bone; + break; + } } } } } + + if (bone_child) { + arm->act_bone = bone_child; + + if (!add_to_sel) { + pchan_act->bone->flag &= ~BONE_SELECTED; + } + bone_child->flag |= BONE_SELECTED; + + changed = true; + } } - CTX_DATA_END; - if (found == 0) + if (changed == false) { return OPERATOR_CANCELLED; + } /* updates */ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index b08cc12dc3e..da756422bdb 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -119,7 +119,7 @@ void ED_keymap_armature(struct wmKeyConfig *keyconf); void ED_armature_from_edit(struct bArmature *arm); void ED_armature_to_edit(struct bArmature *arm); void ED_armature_edit_free(struct bArmature *arm); -void ED_armature_deselect_all(struct Object *obedit, int toggle); +void ED_armature_deselect_all(struct Object *obedit); void ED_armature_deselect_all_visible(struct Object *obedit); int ED_do_pose_selectbuffer(struct Scene *scene, struct Base *base, unsigned int *buffer, diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 295bb752a08..3d791839a3c 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -175,6 +175,9 @@ void ED_object_constraint_set_active(struct Object *ob, struct bConstraint *con) void ED_object_constraint_update(struct Object *ob); void ED_object_constraint_dependency_update(struct Main *bmain, struct Object *ob); +void ED_object_constraint_tag_update(struct Object *ob, struct bConstraint *con); +void ED_object_constraint_dependency_tag_update(struct Main *bmain, struct Object *ob, struct bConstraint *con); + /* object_lattice.c */ bool mouse_lattice(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); void undo_push_lattice(struct bContext *C, const char *name); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 11f961605af..84ac3a7c938 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -104,7 +104,7 @@ void ED_view3d_lastview_store(struct RegionView3D *rv3d); /* Depth buffer */ void ED_view3d_depth_update(struct ARegion *ar); -float ED_view3d_depth_read_cached(struct ViewContext *vc, int x, int y); +float ED_view3d_depth_read_cached(const struct ViewContext *vc, int x, int y); void ED_view3d_depth_tag_update(struct RegionView3D *rv3d); /* Projection */ @@ -224,36 +224,45 @@ void ED_view3d_unproject(struct bglMats *mats, float out[3], const float x, cons /* end */ -void ED_view3d_dist_range_get(struct View3D *v3d, - float r_dist_range[2]); -bool ED_view3d_clip_range_get(struct View3D *v3d, struct RegionView3D *rv3d, - float *r_clipsta, float *r_clipend, const bool use_ortho_factor); -bool ED_view3d_viewplane_get(struct View3D *v3d, struct RegionView3D *rv3d, int winxi, int winyi, - struct rctf *r_viewplane, float *r_clipsta, float *r_clipend, float *r_pixsize); +void ED_view3d_dist_range_get( + const struct View3D *v3d, + float r_dist_range[2]); +bool ED_view3d_clip_range_get( + const struct View3D *v3d, const struct RegionView3D *rv3d, + float *r_clipsta, float *r_clipend, const bool use_ortho_factor); +bool ED_view3d_viewplane_get( + const struct View3D *v3d, const struct RegionView3D *rv3d, int winxi, int winyi, + struct rctf *r_viewplane, float *r_clipsta, float *r_clipend, float *r_pixsize); void ED_view3d_polygon_offset(const struct RegionView3D *rv3d, const float dist); -void ED_view3d_calc_camera_border(struct Scene *scene, struct ARegion *ar, - struct View3D *v3d, struct RegionView3D *rv3d, - struct rctf *r_viewborder, const bool no_shift); -void ED_view3d_calc_camera_border_size(struct Scene *scene, struct ARegion *ar, - struct View3D *v3d, struct RegionView3D *rv3d, - float r_size[2]); +void ED_view3d_calc_camera_border( + const struct Scene *scene, const struct ARegion *ar, + const struct View3D *v3d, const struct RegionView3D *rv3d, + struct rctf *r_viewborder, const bool no_shift); +void ED_view3d_calc_camera_border_size( + const struct Scene *scene, const struct ARegion *ar, + const struct View3D *v3d, const struct RegionView3D *rv3d, + float r_size[2]); bool ED_view3d_calc_render_border(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, struct rcti *rect); void ED_view3d_clipping_calc_from_boundbox(float clip[6][4], const struct BoundBox *clipbb, const bool is_flip); void ED_view3d_clipping_calc(struct BoundBox *bb, float planes[4][4], struct bglMats *mats, const struct rcti *rect); void ED_view3d_clipping_local(struct RegionView3D *rv3d, float mat[4][4]); -bool ED_view3d_clipping_test(struct RegionView3D *rv3d, const float co[3], const bool is_local); +bool ED_view3d_clipping_test(const struct RegionView3D *rv3d, const float co[3], const bool is_local); void ED_view3d_clipping_set(struct RegionView3D *rv3d); void ED_view3d_clipping_enable(void); void ED_view3d_clipping_disable(void); -float ED_view3d_pixel_size(struct RegionView3D *rv3d, const float co[3]); +float ED_view3d_pixel_size(const struct RegionView3D *rv3d, const float co[3]); -float ED_view3d_radius_to_persp_dist(const float angle, const float radius); -float ED_view3d_radius_to_ortho_dist(const float lens, const float radius); +float ED_view3d_radius_to_dist_persp(const float angle, const float radius); +float ED_view3d_radius_to_dist_ortho(const float lens, const float radius); +float ED_view3d_radius_to_dist( + const struct View3D *v3d, const struct ARegion *ar, + const char persp, const bool use_aspect, + const float radius); void drawcircball(int mode, const float cent[3], float rad, float tmat[4][4]); @@ -330,15 +339,15 @@ char ED_view3d_lock_view_from_index(int index); char ED_view3d_axis_view_opposite(char view); bool ED_view3d_lock(struct RegionView3D *rv3d); -uint64_t ED_view3d_datamask(struct Scene *scene, struct View3D *v3d); -uint64_t ED_view3d_screen_datamask(struct bScreen *screen); +uint64_t ED_view3d_datamask(const struct Scene *scene, const struct View3D *v3d); +uint64_t ED_view3d_screen_datamask(const struct bScreen *screen); -bool ED_view3d_view_lock_check(struct View3D *v3d, struct RegionView3D *rv3d); +bool ED_view3d_view_lock_check(const struct View3D *v3d, const struct RegionView3D *rv3d); -bool ED_view3d_offset_lock_check(struct View3D *v3d, struct RegionView3D *rv3d); +bool ED_view3d_offset_lock_check(const struct View3D *v3d, const struct RegionView3D *rv3d); /* camera lock functions */ -bool ED_view3d_camera_lock_check(struct View3D *v3d, struct RegionView3D *rv3d); +bool ED_view3d_camera_lock_check(const struct View3D *v3d, const struct RegionView3D *rv3d); /* copy the camera to the view before starting a view transformation */ void ED_view3d_camera_lock_init_ex(struct View3D *v3d, struct RegionView3D *rv3d, const bool calc_dist); void ED_view3d_camera_lock_init(struct View3D *v3d, struct RegionView3D *rv3d); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index a6ff5bb3ad3..71b7b95208b 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -4494,6 +4494,12 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co BKE_palette_color_remove(palette, color); button_activate_state(C, but, BUTTON_STATE_EXIT); + + /* this is risky. it works OK for now, + * but if it gives trouble we should delay execution */ + but->rnapoin = PointerRNA_NULL; + but->rnaprop = NULL; + return WM_UI_HANDLER_BREAK; } } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 9494cf30f44..90485ffbc74 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -2403,9 +2403,6 @@ void uiTemplatePalette(uiLayout *layout, PointerRNA *ptr, const char *propname, palette = cptr.data; - /* first delete any pending colors */ - BKE_palette_cleanup(palette); - color = palette->colors.first; col = uiLayoutColumn(layout, true); diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 97cb45daae3..fe9ee71aa8e 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -685,7 +685,9 @@ static int bake( result = MEM_callocN(sizeof(float) * depth * num_pixels, "bake return pixels"); /* get the mesh as it arrives in the renderer */ - me_low = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0); + me_low = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 0, 0); + BKE_mesh_split_faces(me_low); + BKE_mesh_tessface_ensure(me_low); /* populate the pixel array with the face data */ if ((is_selected_to_active && (ob_cage == NULL) && is_cage) == false) @@ -700,7 +702,9 @@ static int bake( /* prepare cage mesh */ if (ob_cage) { - me_cage = BKE_mesh_new_from_object(bmain, scene, ob_cage, 1, 2, 1, 0); + me_cage = BKE_mesh_new_from_object(bmain, scene, ob_cage, 1, 2, 0, 0); + BKE_mesh_split_faces(me_cage); + BKE_mesh_tessface_ensure(me_cage); if (me_low->totface != me_cage->totface) { BKE_report(reports, RPT_ERROR, "Invalid cage object, the cage mesh must have the same number " @@ -732,7 +736,9 @@ static int bake( ob_low->modifiers = modifiers_tmp; /* get the cage mesh as it arrives in the renderer */ - me_cage = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0); + me_cage = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 0, 0); + BKE_mesh_split_faces(me_cage); + BKE_mesh_tessface_ensure(me_cage); RE_bake_pixels_populate(me_cage, pixel_array_low, num_pixels, &bake_images, uv_layer); } @@ -760,8 +766,10 @@ static int bake( tmd->quad_method = MOD_TRIANGULATE_QUAD_FIXED; tmd->ngon_method = MOD_TRIANGULATE_NGON_EARCLIP; - highpoly[i].me = BKE_mesh_new_from_object(bmain, scene, highpoly[i].ob, 1, 2, 1, 0); + highpoly[i].me = BKE_mesh_new_from_object(bmain, scene, highpoly[i].ob, 1, 2, 0, 0); highpoly[i].ob->restrictflag &= ~OB_RESTRICT_RENDER; + BKE_mesh_split_faces(highpoly[i].me); + BKE_mesh_tessface_ensure(highpoly[i].me); /* lowpoly to highpoly transformation matrix */ copy_m4_m4(highpoly[i].obmat, highpoly[i].ob->obmat); @@ -867,7 +875,9 @@ cage_cleanup: md->mode &= ~eModifierMode_Render; } - me_nores = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0); + me_nores = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 0, 0); + BKE_mesh_split_faces(me_nores); + BKE_mesh_tessface_ensure(me_nores); RE_bake_pixels_populate(me_nores, pixel_array_low, num_pixels, &bake_images, uv_layer); RE_bake_normal_world_to_tangent(pixel_array_low, num_pixels, depth, result, me_nores, normal_swizzle, ob_low->obmat); diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 231d23b481d..f1b349d5a44 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -260,17 +260,221 @@ static void set_constraint_nth_target(bConstraint *con, Object *target, const ch /* ------------- Constraint Sanity Testing ------------------- */ -/* checks validity of object pointers, and NULLs, - * if Bone doesnt exist it sets the CONSTRAINT_DISABLE flag. - */ -static void test_constraints(Object *owner, bPoseChannel *pchan) +static void test_constraint(Object *owner, bPoseChannel *pchan, bConstraint *con, int type) +{ + bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + bool check_targets = true; + + /* clear disabled-flag first */ + con->flag &= ~CONSTRAINT_DISABLE; + + if (con->type == CONSTRAINT_TYPE_KINEMATIC) { + bKinematicConstraint *data = con->data; + + /* bad: we need a separate set of checks here as poletarget is + * optional... otherwise poletarget must exist too or else + * the constraint is deemed invalid + */ + /* default IK check ... */ + if (BKE_object_exists_check(data->tar) == 0) { + data->tar = NULL; + con->flag |= CONSTRAINT_DISABLE; + } + else if (data->tar == owner) { + if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) { + con->flag |= CONSTRAINT_DISABLE; + } + } + + if (data->poletar) { + if (BKE_object_exists_check(data->poletar) == 0) { + data->poletar = NULL; + con->flag |= CONSTRAINT_DISABLE; + } + else if (data->poletar == owner) { + if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->polesubtarget)) { + con->flag |= CONSTRAINT_DISABLE; + } + } + } + /* ... can be overwritten here */ + BIK_test_constraint(owner, con); + /* targets have already been checked for this */ + check_targets = false; + } + else if (con->type == CONSTRAINT_TYPE_PIVOT) { + bPivotConstraint *data = con->data; + + /* target doesn't have to exist, but if it is non-null, it must exist! */ + if (data->tar && BKE_object_exists_check(data->tar) == 0) { + data->tar = NULL; + con->flag |= CONSTRAINT_DISABLE; + } + else if (data->tar == owner) { + if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) { + con->flag |= CONSTRAINT_DISABLE; + } + } + + /* targets have already been checked for this */ + check_targets = false; + } + else if (con->type == CONSTRAINT_TYPE_ACTION) { + bActionConstraint *data = con->data; + + /* validate action */ + if (data->act == NULL) { + /* must have action */ + con->flag |= CONSTRAINT_DISABLE; + } + else if (data->act->idroot != ID_OB) { + /* only object-rooted actions can be used */ + data->act = NULL; + con->flag |= CONSTRAINT_DISABLE; + } + } + else if (con->type == CONSTRAINT_TYPE_FOLLOWPATH) { + bFollowPathConstraint *data = con->data; + + /* don't allow track/up axes to be the same */ + if (data->upflag == data->trackflag) + con->flag |= CONSTRAINT_DISABLE; + if (data->upflag + 3 == data->trackflag) + con->flag |= CONSTRAINT_DISABLE; + } + else if (con->type == CONSTRAINT_TYPE_TRACKTO) { + bTrackToConstraint *data = con->data; + + /* don't allow track/up axes to be the same */ + if (data->reserved2 == data->reserved1) + con->flag |= CONSTRAINT_DISABLE; + if (data->reserved2 + 3 == data->reserved1) + con->flag |= CONSTRAINT_DISABLE; + } + else if (con->type == CONSTRAINT_TYPE_LOCKTRACK) { + bLockTrackConstraint *data = con->data; + + if (data->lockflag == data->trackflag) + con->flag |= CONSTRAINT_DISABLE; + if (data->lockflag + 3 == data->trackflag) + con->flag |= CONSTRAINT_DISABLE; + } + else if (con->type == CONSTRAINT_TYPE_SPLINEIK) { + bSplineIKConstraint *data = con->data; + + /* if the number of points does not match the amount required by the chain length, + * free the points array and request a rebind... + */ + if ((data->points == NULL) || (data->numpoints != data->chainlen + 1)) { + /* free the points array */ + if (data->points) { + MEM_freeN(data->points); + data->points = NULL; + } + + /* clear the bound flag, forcing a rebind next time this is evaluated */ + data->flag &= ~CONSTRAINT_SPLINEIK_BOUND; + } + } + else if (con->type == CONSTRAINT_TYPE_FOLLOWTRACK) { + bFollowTrackConstraint *data = con->data; + + if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0) { + if (data->clip != NULL && data->track[0]) { + MovieTracking *tracking = &data->clip->tracking; + MovieTrackingObject *tracking_object; + + if (data->object[0]) + tracking_object = BKE_tracking_object_get_named(tracking, data->object); + else + tracking_object = BKE_tracking_object_get_camera(tracking); + + if (!tracking_object) { + con->flag |= CONSTRAINT_DISABLE; + } + else { + if (!BKE_tracking_track_get_named(tracking, tracking_object, data->track)) + con->flag |= CONSTRAINT_DISABLE; + } + } + else { + con->flag |= CONSTRAINT_DISABLE; + } + } + } + else if (con->type == CONSTRAINT_TYPE_CAMERASOLVER) { + bCameraSolverConstraint *data = con->data; + + if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL)) + con->flag |= CONSTRAINT_DISABLE; + } + else if (con->type == CONSTRAINT_TYPE_OBJECTSOLVER) { + bObjectSolverConstraint *data = con->data; + + if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL)) + con->flag |= CONSTRAINT_DISABLE; + } + + /* Check targets for constraints */ + if (check_targets && cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); + + /* disable and clear constraints targets that are incorrect */ + for (ct = targets.first; ct; ct = ct->next) { + /* general validity checks (for those constraints that need this) */ + if (BKE_object_exists_check(ct->tar) == 0) { + /* object doesn't exist, but constraint requires target */ + ct->tar = NULL; + con->flag |= CONSTRAINT_DISABLE; + } + else if (ct->tar == owner) { + if (type == CONSTRAINT_OBTYPE_BONE) { + if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), ct->subtarget)) { + /* bone must exist in armature... */ + /* TODO: clear subtarget? */ + con->flag |= CONSTRAINT_DISABLE; + } + else if (STREQ(pchan->name, ct->subtarget)) { + /* cannot target self */ + ct->subtarget[0] = '\0'; + con->flag |= CONSTRAINT_DISABLE; + } + } + else { + /* cannot use self as target */ + ct->tar = NULL; + con->flag |= CONSTRAINT_DISABLE; + } + } + + /* target checks for specific constraints */ + if (ELEM(con->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO, CONSTRAINT_TYPE_SPLINEIK)) { + if (ct->tar) { + if (ct->tar->type != OB_CURVE) { + ct->tar = NULL; + con->flag |= CONSTRAINT_DISABLE; + } + else { + Curve *cu = ct->tar->data; + + /* auto-set 'Path' setting on curve so this works */ + cu->flag |= CU_PATH; + } + } + } + } + + /* free any temporary targets */ + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 0); + } +} + +static int constraint_type_get(Object *owner, bPoseChannel *pchan) { - bConstraint *curcon; - ListBase *conlist = NULL; int type; - - if (owner == NULL) return; - /* Check parents */ if (pchan) { switch (owner->type) { @@ -284,7 +488,22 @@ static void test_constraints(Object *owner, bPoseChannel *pchan) } else type = CONSTRAINT_OBTYPE_OBJECT; + return type; +} + +/* checks validity of object pointers, and NULLs, + * if Bone doesnt exist it sets the CONSTRAINT_DISABLE flag. + */ +static void test_constraints(Object *owner, bPoseChannel *pchan) +{ + bConstraint *curcon; + ListBase *conlist = NULL; + int type; + if (owner == NULL) return; + + type = constraint_type_get(owner, pchan); + /* Get the constraint list for this object */ switch (type) { case CONSTRAINT_OBTYPE_OBJECT: @@ -298,213 +517,7 @@ static void test_constraints(Object *owner, bPoseChannel *pchan) /* Check all constraints - is constraint valid? */ if (conlist) { for (curcon = conlist->first; curcon; curcon = curcon->next) { - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); - ListBase targets = {NULL, NULL}; - bConstraintTarget *ct; - - /* clear disabled-flag first */ - curcon->flag &= ~CONSTRAINT_DISABLE; - - if (curcon->type == CONSTRAINT_TYPE_KINEMATIC) { - bKinematicConstraint *data = curcon->data; - - /* bad: we need a separate set of checks here as poletarget is - * optional... otherwise poletarget must exist too or else - * the constraint is deemed invalid - */ - /* default IK check ... */ - if (BKE_object_exists_check(data->tar) == 0) { - data->tar = NULL; - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (data->tar == owner) { - if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) { - curcon->flag |= CONSTRAINT_DISABLE; - } - } - - if (data->poletar) { - if (BKE_object_exists_check(data->poletar) == 0) { - data->poletar = NULL; - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (data->poletar == owner) { - if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->polesubtarget)) { - curcon->flag |= CONSTRAINT_DISABLE; - } - } - } - /* ... can be overwritten here */ - BIK_test_constraint(owner, curcon); - /* targets have already been checked for this */ - continue; - } - else if (curcon->type == CONSTRAINT_TYPE_PIVOT) { - bPivotConstraint *data = curcon->data; - - /* target doesn't have to exist, but if it is non-null, it must exist! */ - if (data->tar && BKE_object_exists_check(data->tar) == 0) { - data->tar = NULL; - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (data->tar == owner) { - if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) { - curcon->flag |= CONSTRAINT_DISABLE; - } - } - - /* targets have already been checked for this */ - continue; - } - else if (curcon->type == CONSTRAINT_TYPE_ACTION) { - bActionConstraint *data = curcon->data; - - /* validate action */ - if (data->act == NULL) { - /* must have action */ - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (data->act->idroot != ID_OB) { - /* only object-rooted actions can be used */ - data->act = NULL; - curcon->flag |= CONSTRAINT_DISABLE; - } - } - else if (curcon->type == CONSTRAINT_TYPE_FOLLOWPATH) { - bFollowPathConstraint *data = curcon->data; - - /* don't allow track/up axes to be the same */ - if (data->upflag == data->trackflag) - curcon->flag |= CONSTRAINT_DISABLE; - if (data->upflag + 3 == data->trackflag) - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (curcon->type == CONSTRAINT_TYPE_TRACKTO) { - bTrackToConstraint *data = curcon->data; - - /* don't allow track/up axes to be the same */ - if (data->reserved2 == data->reserved1) - curcon->flag |= CONSTRAINT_DISABLE; - if (data->reserved2 + 3 == data->reserved1) - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (curcon->type == CONSTRAINT_TYPE_LOCKTRACK) { - bLockTrackConstraint *data = curcon->data; - - if (data->lockflag == data->trackflag) - curcon->flag |= CONSTRAINT_DISABLE; - if (data->lockflag + 3 == data->trackflag) - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (curcon->type == CONSTRAINT_TYPE_SPLINEIK) { - bSplineIKConstraint *data = curcon->data; - - /* if the number of points does not match the amount required by the chain length, - * free the points array and request a rebind... - */ - if ((data->points == NULL) || (data->numpoints != data->chainlen + 1)) { - /* free the points array */ - if (data->points) { - MEM_freeN(data->points); - data->points = NULL; - } - - /* clear the bound flag, forcing a rebind next time this is evaluated */ - data->flag &= ~CONSTRAINT_SPLINEIK_BOUND; - } - } - else if (curcon->type == CONSTRAINT_TYPE_FOLLOWTRACK) { - bFollowTrackConstraint *data = curcon->data; - - if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0) { - if (data->clip != NULL && data->track[0]) { - MovieTracking *tracking = &data->clip->tracking; - MovieTrackingObject *tracking_object; - - if (data->object[0]) - tracking_object = BKE_tracking_object_get_named(tracking, data->object); - else - tracking_object = BKE_tracking_object_get_camera(tracking); - - if (!tracking_object) { - curcon->flag |= CONSTRAINT_DISABLE; - } - else { - if (!BKE_tracking_track_get_named(tracking, tracking_object, data->track)) - curcon->flag |= CONSTRAINT_DISABLE; - } - } - else { - curcon->flag |= CONSTRAINT_DISABLE; - } - } - } - else if (curcon->type == CONSTRAINT_TYPE_CAMERASOLVER) { - bCameraSolverConstraint *data = curcon->data; - - if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL)) - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (curcon->type == CONSTRAINT_TYPE_OBJECTSOLVER) { - bObjectSolverConstraint *data = curcon->data; - - if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL)) - curcon->flag |= CONSTRAINT_DISABLE; - } - - /* Check targets for constraints */ - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(curcon, &targets); - - /* disable and clear constraints targets that are incorrect */ - for (ct = targets.first; ct; ct = ct->next) { - /* general validity checks (for those constraints that need this) */ - if (BKE_object_exists_check(ct->tar) == 0) { - /* object doesn't exist, but constraint requires target */ - ct->tar = NULL; - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (ct->tar == owner) { - if (type == CONSTRAINT_OBTYPE_BONE) { - if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), ct->subtarget)) { - /* bone must exist in armature... */ - /* TODO: clear subtarget? */ - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (STREQ(pchan->name, ct->subtarget)) { - /* cannot target self */ - ct->subtarget[0] = '\0'; - curcon->flag |= CONSTRAINT_DISABLE; - } - } - else { - /* cannot use self as target */ - ct->tar = NULL; - curcon->flag |= CONSTRAINT_DISABLE; - } - } - - /* target checks for specific constraints */ - if (ELEM(curcon->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO, CONSTRAINT_TYPE_SPLINEIK)) { - if (ct->tar) { - if (ct->tar->type != OB_CURVE) { - ct->tar = NULL; - curcon->flag |= CONSTRAINT_DISABLE; - } - else { - Curve *cu = ct->tar->data; - - /* auto-set 'Path' setting on curve so this works */ - cu->flag |= CU_PATH; - } - } - } - } - - /* free any temporary targets */ - if (cti->flush_constraint_targets) - cti->flush_constraint_targets(curcon, &targets, 0); - } + test_constraint(owner, pchan, curcon, type); } } } @@ -524,6 +537,26 @@ void object_test_constraints(Object *owner) } } +static void object_test_constraint(Object *owner, bConstraint *con) +{ + if (owner->type == OB_ARMATURE && owner->pose) { + if (BLI_findindex(&owner->constraints, con) != -1) { + test_constraint(owner, NULL, con, CONSTRAINT_OBTYPE_OBJECT); + } + else { + bPoseChannel *pchan; + for (pchan = owner->pose->chanbase.first; pchan; pchan = pchan->next) { + if (BLI_findindex(&pchan->constraints, con) != -1) { + test_constraint(owner, pchan, con, CONSTRAINT_OBTYPE_BONE); + break; + } + } + } + } + else { + test_constraint(owner, NULL, con, CONSTRAINT_OBTYPE_OBJECT); + } +} /************************ generic functions for operators using constraint names and data context *********************/ @@ -1161,20 +1194,49 @@ void ED_object_constraint_update(Object *ob) DAG_id_tag_update(&ob->id, OB_RECALC_OB); } +static void object_pose_tag_update(Object *ob) +{ + ob->pose->flag |= POSE_RECALC; /* Checks & sort pose channels. */ + if (ob->proxy && ob->adt) { + /* We need to make use of ugly POSE_ANIMATION_WORKAROUND here too, else anim data are not reloaded + * after calling `BKE_pose_rebuild()`, which causes T43872. + * Note that this is a bit wide here, since we cannot be sure whether there are some locked proxy bones + * or not... + * XXX Temp hack until new depsgraph hopefully solves this. */ + ob->adt->recalc |= ADT_RECALC_ANIM; + } +} + void ED_object_constraint_dependency_update(Main *bmain, Object *ob) { ED_object_constraint_update(ob); if (ob->pose) { - ob->pose->flag |= POSE_RECALC; /* Checks & sort pose channels. */ - if (ob->proxy && ob->adt) { - /* We need to make use of ugly POSE_ANIMATION_WORKAROUND here too, else anim data are not reloaded - * after calling `BKE_pose_rebuild()`, which causes T43872. - * Note that this is a bit wide here, since we cannot be sure whether there are some locked proxy bones - * or not... - * XXX Temp hack until new depsgraph hopefully solves this. */ - ob->adt->recalc |= ADT_RECALC_ANIM; - } + object_pose_tag_update(ob); + } + DAG_relations_tag_update(bmain); +} + +void ED_object_constraint_tag_update(Object *ob, bConstraint *con) +{ + if (ob->pose) { + BKE_pose_tag_update_constraint_flags(ob->pose); + } + + object_test_constraint(ob, con); + + if (ob->type == OB_ARMATURE) + DAG_id_tag_update(&ob->id, OB_RECALC_DATA | OB_RECALC_OB); + else + DAG_id_tag_update(&ob->id, OB_RECALC_OB); +} + +void ED_object_constraint_dependency_tag_update(Main *bmain, Object *ob, bConstraint *con) +{ + ED_object_constraint_tag_update(ob, con); + + if (ob->pose) { + object_pose_tag_update(ob); } DAG_relations_tag_update(bmain); } diff --git a/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c index fb9687da6df..674eb25942f 100644 --- a/source/blender/editors/object/object_shapekey.c +++ b/source/blender/editors/object/object_shapekey.c @@ -127,8 +127,8 @@ static bool ED_object_shape_key_remove(Main *bmain, Object *ob) } else if (rkb->relative >= ob->shapenr) { /* Fix positional shift of the keys when kb is deleted from the list */ - rkb->relative -= 1; - } + rkb->relative -= 1; + } } BLI_remlink(&key->block, kb); @@ -332,6 +332,8 @@ static int shape_key_add_exec(bContext *C, wmOperator *op) ED_object_shape_key_add(C, ob, from_mix); + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index d03f3c157a9..517cf64214c 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -106,9 +106,10 @@ static void draw_render_info(const bContext *C, if (re) { int total_tiles; + bool need_free_tiles; rcti *tiles; - RE_engine_get_current_tiles(re, &total_tiles, &tiles); + tiles = RE_engine_get_current_tiles(re, &total_tiles, &need_free_tiles); if (total_tiles) { int i, x, y; @@ -133,7 +134,9 @@ static void draw_render_info(const bContext *C, glaDrawBorderCorners(tile, zoomx, zoomy); } - MEM_freeN(tiles); + if (need_free_tiles) { + MEM_freeN(tiles); + } glPopMatrix(); } diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 730ee02f448..6050b66fdb7 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -618,7 +618,7 @@ static eOLDrawState tree_element_active_ebone( if (set != OL_SETSEL_NONE) { if (set == OL_SETSEL_NORMAL) { if (!(ebone->flag & BONE_HIDDEN_A)) { - ED_armature_deselect_all(scene->obedit, 0); // deselect + ED_armature_deselect_all(scene->obedit); tree_element_active_ebone__sel(C, scene, arm, ebone, true); status = OL_DRAWSEL_NORMAL; } diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 0b92b41dbee..965467100a2 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -208,7 +208,7 @@ void ED_view3d_clipping_enable(void) } } -static bool view3d_clipping_test(const float co[3], float clip[6][4]) +static bool view3d_clipping_test(const float co[3], const float clip[6][4]) { if (plane_point_side_v3(clip[0], co) > 0.0f) if (plane_point_side_v3(clip[1], co) > 0.0f) @@ -221,7 +221,7 @@ static bool view3d_clipping_test(const float co[3], float clip[6][4]) /* for 'local' ED_view3d_clipping_local must run first * then all comparisons can be done in localspace */ -bool ED_view3d_clipping_test(RegionView3D *rv3d, const float co[3], const bool is_local) +bool ED_view3d_clipping_test(const RegionView3D *rv3d, const float co[3], const bool is_local) { return view3d_clipping_test(co, is_local ? rv3d->clip_local : rv3d->clip); } @@ -950,8 +950,9 @@ static void draw_selected_name(Scene *scene, Object *ob, rcti *rect) BLF_draw_default(offset, 0.5f * U.widget_unit, 0.0f, info, sizeof(info)); } -static void view3d_camera_border(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D *rv3d, - rctf *r_viewborder, const bool no_shift, const bool no_zoom) +static void view3d_camera_border( + const Scene *scene, const ARegion *ar, const View3D *v3d, const RegionView3D *rv3d, + rctf *r_viewborder, const bool no_shift, const bool no_zoom) { CameraParams params; rctf rect_view, rect_camera; @@ -984,7 +985,9 @@ static void view3d_camera_border(Scene *scene, ARegion *ar, View3D *v3d, RegionV r_viewborder->ymax = ((rect_camera.ymax - rect_view.ymin) / BLI_rctf_size_y(&rect_view)) * ar->winy; } -void ED_view3d_calc_camera_border_size(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D *rv3d, float r_size[2]) +void ED_view3d_calc_camera_border_size( + const Scene *scene, const ARegion *ar, const View3D *v3d, const RegionView3D *rv3d, + float r_size[2]) { rctf viewborder; @@ -993,8 +996,9 @@ void ED_view3d_calc_camera_border_size(Scene *scene, ARegion *ar, View3D *v3d, R r_size[1] = BLI_rctf_size_y(&viewborder); } -void ED_view3d_calc_camera_border(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D *rv3d, - rctf *r_viewborder, const bool no_shift) +void ED_view3d_calc_camera_border( + const Scene *scene, const ARegion *ar, const View3D *v3d, const RegionView3D *rv3d, + rctf *r_viewborder, const bool no_shift) { view3d_camera_border(scene, ar, v3d, rv3d, r_viewborder, no_shift, false); } @@ -2578,7 +2582,7 @@ static void gpu_update_lamps_shadows(Scene *scene, View3D *v3d) /* *********************** customdata **************** */ -CustomDataMask ED_view3d_datamask(Scene *scene, View3D *v3d) +CustomDataMask ED_view3d_datamask(const Scene *scene, const View3D *v3d) { CustomDataMask mask = 0; @@ -2604,16 +2608,16 @@ CustomDataMask ED_view3d_datamask(Scene *scene, View3D *v3d) } /* goes over all modes and view3d settings */ -CustomDataMask ED_view3d_screen_datamask(bScreen *screen) +CustomDataMask ED_view3d_screen_datamask(const bScreen *screen) { - Scene *scene = screen->scene; + const Scene *scene = screen->scene; CustomDataMask mask = CD_MASK_BAREMESH; - ScrArea *sa; + const ScrArea *sa; /* check if we need tfaces & mcols due to view mode */ for (sa = screen->areabase.first; sa; sa = sa->next) { if (sa->spacetype == SPACE_VIEW3D) { - mask |= ED_view3d_datamask(scene, (View3D *)sa->spacedata.first); + mask |= ED_view3d_datamask(scene, sa->spacedata.first); } } diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 51915e05a9e..ddb96f9aefc 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -83,7 +83,7 @@ #include "view3d_intern.h" /* own include */ -bool ED_view3d_offset_lock_check(struct View3D *v3d, struct RegionView3D *rv3d) +bool ED_view3d_offset_lock_check(const View3D *v3d, const RegionView3D *rv3d) { return (rv3d->persp != RV3D_CAMOB) && (v3d->ob_centre_cursor || v3d->ob_centre); } @@ -103,7 +103,7 @@ static bool view3d_operator_offset_lock_check(bContext *C, wmOperator *op) /* ********************** view3d_edit: view manipulations ********************* */ -bool ED_view3d_camera_lock_check(View3D *v3d, RegionView3D *rv3d) +bool ED_view3d_camera_lock_check(const View3D *v3d, const RegionView3D *rv3d) { return ((v3d->camera) && (v3d->camera->id.lib == NULL) && @@ -2748,29 +2748,15 @@ static void view3d_from_minmax(bContext *C, View3D *v3d, ARegion *ar, size = max_fff(afm[0], afm[1], afm[2]); if (ok_dist) { - /* fix up zoom distance if needed */ + char persp; if (rv3d->is_persp) { - float lens, sensor_size; - /* offset the view based on the lens */ if (rv3d->persp == RV3D_CAMOB && ED_view3d_camera_lock_check(v3d, rv3d)) { - CameraParams params; - BKE_camera_params_init(¶ms); - params.clipsta = v3d->near; - params.clipend = v3d->far; - BKE_camera_params_from_object(¶ms, v3d->camera); - - lens = params.lens; - sensor_size = BKE_camera_sensor_size(params.sensor_fit, params.sensor_x, params.sensor_y); + persp = RV3D_CAMOB; } else { - lens = v3d->lens; - sensor_size = DEFAULT_SENSOR_WIDTH; + persp = RV3D_PERSP; } - size = ED_view3d_radius_to_persp_dist(focallength_to_fov(lens, sensor_size), size / 2.0f) * VIEW3D_MARGIN; - - /* do not zoom closer than the near clipping plane */ - size = max_ff(size, v3d->near * 1.5f); } else { /* ortho */ if (size < 0.0001f) { @@ -2779,7 +2765,15 @@ static void view3d_from_minmax(bContext *C, View3D *v3d, ARegion *ar, } else { /* adjust zoom so it looks nicer */ - size = ED_view3d_radius_to_ortho_dist(v3d->lens, size / 2.0f) * VIEW3D_MARGIN; + persp = RV3D_ORTHO; + } + } + + if (ok_dist) { + new_dist = ED_view3d_radius_to_dist(v3d, ar, persp, true, (size / 2) * VIEW3D_MARGIN); + if (rv3d->is_persp) { + /* don't zoom closer than the near clipping plane */ + new_dist = max_ff(new_dist, v3d->near * 1.5f); } } } @@ -2787,15 +2781,6 @@ static void view3d_from_minmax(bContext *C, View3D *v3d, ARegion *ar, mid_v3_v3v3(new_ofs, min, max); negate_v3(new_ofs); - new_dist = size; - - /* correction for window aspect ratio */ - if (ar->winy > 2 && ar->winx > 2) { - size = (float)ar->winx / (float)ar->winy; - if (size < 1.0f) size = 1.0f / size; - new_dist *= size; - } - if (rv3d->persp == RV3D_CAMOB && !ED_view3d_camera_lock_check(v3d, rv3d)) { rv3d->persp = RV3D_PERSP; ED_view3d_smooth_view(C, v3d, ar, v3d->camera, NULL, diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c index 11ed9867e2f..f8912345e1c 100644 --- a/source/blender/editors/space_view3d/view3d_header.c +++ b/source/blender/editors/space_view3d/view3d_header.c @@ -105,8 +105,8 @@ static void view3d_layers_editmode_ensure(Scene *scene, View3D *v3d) if (scene->obedit && (scene->obedit->lay & v3d->lay) == 0) { int bit; for (bit = 0; bit < 32; bit++) { - if (scene->obedit->lay & (1 << bit)) { - v3d->lay |= 1 << bit; + if (scene->obedit->lay & (1u << bit)) { + v3d->lay |= (1u << bit); break; } } @@ -161,8 +161,8 @@ static int view3d_layers_exec(bContext *C, wmOperator *op) v3d->layact = 1 << nr; else if ((v3d->lay & v3d->layact) == 0) { for (bit = 0; bit < 32; bit++) { - if (v3d->lay & (1 << bit)) { - v3d->layact = 1 << bit; + if (v3d->lay & (1u << bit)) { + v3d->layact = (1u << bit); break; } } diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index 25dbc8830fe..e07d7b3977c 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -208,7 +208,7 @@ void VIEW3D_OT_localview(struct wmOperatorType *ot); void VIEW3D_OT_game_start(struct wmOperatorType *ot); -bool ED_view3d_boundbox_clip_ex(RegionView3D *rv3d, const struct BoundBox *bb, float obmat[4][4]); +bool ED_view3d_boundbox_clip_ex(const RegionView3D *rv3d, const struct BoundBox *bb, float obmat[4][4]); bool ED_view3d_boundbox_clip(RegionView3D *rv3d, const struct BoundBox *bb); void ED_view3d_smooth_view_ex( @@ -225,8 +225,8 @@ void ED_view3d_smooth_view( const float *ofs, const float *quat, const float *dist, const float *lens, const int smooth_viewtx); -void view3d_winmatrix_set(ARegion *ar, View3D *v3d, const rctf *rect); -void view3d_viewmatrix_set(Scene *scene, View3D *v3d, RegionView3D *rv3d); +void view3d_winmatrix_set(ARegion *ar, const View3D *v3d, const rctf *rect); +void view3d_viewmatrix_set(Scene *scene, const View3D *v3d, RegionView3D *rv3d); void fly_modal_keymap(struct wmKeyConfig *keyconf); void walk_modal_keymap(struct wmKeyConfig *keyconf); diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index 4888c6846d2..4154f8e8845 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -688,7 +688,7 @@ static bool view3d_boundbox_clip_m4(const BoundBox *bb, float persmatob[4][4]) return false; } -bool ED_view3d_boundbox_clip_ex(RegionView3D *rv3d, const BoundBox *bb, float obmat[4][4]) +bool ED_view3d_boundbox_clip_ex(const RegionView3D *rv3d, const BoundBox *bb, float obmat[4][4]) { /* return 1: draw */ @@ -697,7 +697,7 @@ bool ED_view3d_boundbox_clip_ex(RegionView3D *rv3d, const BoundBox *bb, float ob if (bb == NULL) return true; if (bb->flag & BOUNDBOX_DISABLED) return true; - mul_m4_m4m4(persmatob, rv3d->persmat, obmat); + mul_m4_m4m4(persmatob, (float(*)[4])rv3d->persmat, obmat); return view3d_boundbox_clip_m4(bb, persmatob); } @@ -710,7 +710,7 @@ bool ED_view3d_boundbox_clip(RegionView3D *rv3d, const BoundBox *bb) return view3d_boundbox_clip_m4(bb, rv3d->persmatob); } -float ED_view3d_depth_read_cached(ViewContext *vc, int x, int y) +float ED_view3d_depth_read_cached(const ViewContext *vc, int x, int y) { ViewDepths *vd = vc->rv3d->depths; @@ -729,16 +729,19 @@ void ED_view3d_depth_tag_update(RegionView3D *rv3d) rv3d->depths->damaged = true; } -void ED_view3d_dist_range_get(struct View3D *v3d, - float r_dist_range[2]) +void ED_view3d_dist_range_get( + const View3D *v3d, + float r_dist_range[2]) { r_dist_range[0] = v3d->grid * 0.001f; r_dist_range[1] = v3d->far * 10.0f; } /* copies logic of get_view3d_viewplane(), keep in sync */ -bool ED_view3d_clip_range_get(View3D *v3d, RegionView3D *rv3d, float *r_clipsta, float *r_clipend, - const bool use_ortho_factor) +bool ED_view3d_clip_range_get( + const View3D *v3d, const RegionView3D *rv3d, + float *r_clipsta, float *r_clipend, + const bool use_ortho_factor) { CameraParams params; @@ -758,8 +761,9 @@ bool ED_view3d_clip_range_get(View3D *v3d, RegionView3D *rv3d, float *r_clipsta, } /* also exposed in previewrender.c */ -bool ED_view3d_viewplane_get(View3D *v3d, RegionView3D *rv3d, int winx, int winy, - rctf *r_viewplane, float *r_clipsta, float *r_clipend, float *r_pixsize) +bool ED_view3d_viewplane_get( + const View3D *v3d, const RegionView3D *rv3d, int winx, int winy, + rctf *r_viewplane, float *r_clipsta, float *r_clipend, float *r_pixsize) { CameraParams params; @@ -797,7 +801,7 @@ void ED_view3d_polygon_offset(const RegionView3D *rv3d, const float dist) /** * \param rect optional for picking (can be NULL). */ -void view3d_winmatrix_set(ARegion *ar, View3D *v3d, const rctf *rect) +void view3d_winmatrix_set(ARegion *ar, const View3D *v3d, const rctf *rect) { RegionView3D *rv3d = ar->regiondata; rctf viewplane; @@ -917,7 +921,7 @@ bool ED_view3d_lock(RegionView3D *rv3d) } /* don't set windows active in here, is used by renderwin too */ -void view3d_viewmatrix_set(Scene *scene, View3D *v3d, RegionView3D *rv3d) +void view3d_viewmatrix_set(Scene *scene, const View3D *v3d, RegionView3D *rv3d) { if (rv3d->persp == RV3D_CAMOB) { /* obs/camera */ if (v3d->camera) { @@ -956,7 +960,7 @@ void view3d_viewmatrix_set(Scene *scene, View3D *v3d, RegionView3D *rv3d) } else if (v3d->ob_centre_cursor) { float vec[3]; - copy_v3_v3(vec, ED_view3d_cursor3d_get(scene, v3d)); + copy_v3_v3(vec, ED_view3d_cursor3d_get(scene, (View3D *)v3d)); translate_m4(rv3d->viewmat, -vec[0], -vec[1], -vec[2]); use_lock_ofs = true; } @@ -1214,7 +1218,7 @@ static bool view3d_localview_init( View3D *v3d = sa->spacedata.first; Base *base; float min[3], max[3], box[3], mid[3]; - float size = 0.0f, size_persp = 0.0f, size_ortho = 0.0f; + float size = 0.0f; unsigned int locallay; bool ok = false; @@ -1252,13 +1256,6 @@ static bool view3d_localview_init( sub_v3_v3v3(box, max, min); size = max_fff(box[0], box[1], box[2]); - - /* do not zoom closer than the near clipping plane */ - size = max_ff(size, v3d->near * 1.5f); - - /* perspective size (we always switch out of camera view so no need to use its lens size) */ - size_persp = ED_view3d_radius_to_persp_dist(focallength_to_fov(v3d->lens, DEFAULT_SENSOR_WIDTH), size / 2.0f) * VIEW3D_MARGIN; - size_ortho = ED_view3d_radius_to_ortho_dist(v3d->lens, size / 2.0f) * VIEW3D_MARGIN; } if (ok == true) { @@ -1275,6 +1272,7 @@ static bool view3d_localview_init( for (ar = sa->regionbase.first; ar; ar = ar->next) { if (ar->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d = ar->regiondata; + bool ok_dist = true; /* new view values */ Object *camera_old = NULL; @@ -1290,25 +1288,24 @@ static bool view3d_localview_init( camera_old = v3d->camera; } - /* perspective should be a bit farther away to look nice */ - if (rv3d->persp != RV3D_ORTHO) { - dist_new = size_persp; - } - else { - dist_new = size_ortho; + if (rv3d->persp == RV3D_ORTHO) { + if (size < 0.0001) { + ok_dist = false; + } } - /* correction for window aspect ratio */ - if (ar->winy > 2 && ar->winx > 2) { - float asp = (float)ar->winx / (float)ar->winy; - if (asp < 1.0f) asp = 1.0f / asp; - dist_new *= asp; + if (ok_dist) { + dist_new = ED_view3d_radius_to_dist(v3d, ar, rv3d->persp, true, (size / 2) * VIEW3D_MARGIN); + if (rv3d->persp == RV3D_PERSP) { + /* don't zoom closer than the near clipping plane */ + dist_new = max_ff(dist_new, v3d->near * 1.5f); + } } ED_view3d_smooth_view_ex( wm, win, sa, v3d, ar, camera_old, NULL, - ofs_new, NULL, &dist_new, NULL, + ofs_new, NULL, ok_dist ? &dist_new : NULL, NULL, smooth_viewtx); } } @@ -1715,21 +1712,116 @@ void VIEW3D_OT_game_start(wmOperatorType *ot) /* ************************************** */ -float ED_view3d_pixel_size(RegionView3D *rv3d, const float co[3]) +float ED_view3d_pixel_size(const RegionView3D *rv3d, const float co[3]) { - return mul_project_m4_v3_zfac(rv3d->persmat, co) * rv3d->pixsize * U.pixelsize; + return mul_project_m4_v3_zfac((float(*)[4])rv3d->persmat, co) * rv3d->pixsize * U.pixelsize; } -float ED_view3d_radius_to_persp_dist(const float angle, const float radius) +float ED_view3d_radius_to_dist_persp(const float angle, const float radius) { - return (radius / 2.0f) * fabsf(1.0f / cosf((((float)M_PI) - angle) / 2.0f)); + return radius * (1.0f / tanf(angle / 2.0f)); } -float ED_view3d_radius_to_ortho_dist(const float lens, const float radius) +float ED_view3d_radius_to_dist_ortho(const float lens, const float radius) { return radius / (DEFAULT_SENSOR_WIDTH / lens); } +/** + * Return a new RegionView3D.dist value to fit the \a radius. + * + * \note Depth isn't taken into account, this will fit a flat plane exactly, + * but points towards the view (with a perspective projection), + * may be within the radius but outside the view. eg: + * + * <pre> + * + + * pt --> + /^ radius + * / | + * / | + * view + + + * \ | + * \ | + * \| + * + + * </pre> + * + * \param ar Can be NULL if \a use_aspect is false. + * \param persp Allow the caller to tell what kind of perspective to use (ortho/view/camera) + * \param use_aspect Increase the distance to account for non 1:1 view aspect. + * \param radius The radius will be fitted exactly, typically pre-scaled by a margin (#VIEW3D_MARGIN). + */ +float ED_view3d_radius_to_dist( + const View3D *v3d, const ARegion *ar, + const char persp, const bool use_aspect, + const float radius) +{ + float dist; + + BLI_assert(ELEM(persp, RV3D_ORTHO, RV3D_PERSP, RV3D_CAMOB)); + BLI_assert((persp != RV3D_CAMOB) || v3d->camera); + + if (persp == RV3D_ORTHO) { + dist = ED_view3d_radius_to_dist_ortho(v3d->lens, radius); + } + else { + float lens, sensor_size, zoom; + float angle; + + if (persp == RV3D_CAMOB) { + CameraParams params; + BKE_camera_params_init(¶ms); + params.clipsta = v3d->near; + params.clipend = v3d->far; + BKE_camera_params_from_object(¶ms, v3d->camera); + + lens = params.lens; + sensor_size = BKE_camera_sensor_size(params.sensor_fit, params.sensor_x, params.sensor_y); + + /* ignore 'rv3d->camzoom' because we wan't to fit to the cameras frame */ + zoom = CAMERA_PARAM_ZOOM_INIT_CAMOB; + } + else { + lens = v3d->lens; + sensor_size = DEFAULT_SENSOR_WIDTH; + zoom = CAMERA_PARAM_ZOOM_INIT_PERSP; + } + + angle = focallength_to_fov(lens, sensor_size); + + /* zoom influences lens, correct this by scaling the angle as a distance (by the zoom-level) */ + angle = ((atanf(tanf(angle / 2.0f) * zoom) * 2.0f)); + + dist = ED_view3d_radius_to_dist_persp(angle, radius); + } + + if (use_aspect) { + const RegionView3D *rv3d = ar->regiondata; + + float winx, winy; + + if (persp == RV3D_CAMOB) { + /* camera frame x/y in pixels */ + winx = ar->winx / rv3d->viewcamtexcofac[0]; + winy = ar->winy / rv3d->viewcamtexcofac[1]; + } + else { + winx = ar->winx; + winy = ar->winy; + } + + if (winx && winy) { + float aspect = winx / winy; + if (aspect < 1.0f) { + aspect = 1.0f / aspect; + } + dist *= aspect; + } + } + + return dist; +} + /* view matrix properties utilities */ /* unused */ diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 061d00424b8..f4cb8f724bb 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -888,8 +888,12 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf) WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_UP); WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_DOWN); + WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_UP); + WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_DOWN); WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_UP); WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_DOWN); + WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_UP); + WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_DOWN); WM_modalkeymap_add_item(keymap, MOUSEPAN, 0, 0, 0, TFM_MODAL_PROPSIZE); WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_ALT, 0, TFM_MODAL_EDGESLIDE_UP); @@ -1230,7 +1234,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) break; case TFM_MODAL_PROPSIZE_UP: if (t->flag & T_PROP_EDIT) { - t->prop_size *= 1.1f; + t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f; if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far); calculatePropRatio(t); @@ -1240,7 +1244,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) break; case TFM_MODAL_PROPSIZE_DOWN: if (t->flag & T_PROP_EDIT) { - t->prop_size *= 0.90909090f; + t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f; calculatePropRatio(t); t->redraw |= TREDRAW_HARD; handled = true; @@ -1413,7 +1417,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) break; case PADPLUSKEY: if (event->alt && t->flag & T_PROP_EDIT) { - t->prop_size *= 1.1f; + t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f; if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far); calculatePropRatio(t); @@ -1434,7 +1438,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) break; case PADMINUS: if (event->alt && t->flag & T_PROP_EDIT) { - t->prop_size *= 0.90909090f; + t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f; calculatePropRatio(t); t->redraw = TREDRAW_HARD; handled = true; @@ -7307,40 +7311,28 @@ static void headerSeqSlide(TransInfo *t, const float val[2], char str[MAX_INFO_L WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0)); } -static void applySeqSlideValue(TransInfo *t, const float val[2], int frame) +static void applySeqSlideValue(TransInfo *t, const float val[2]) { TransData *td = t->data; int i; - TransSeq *ts = t->customData; for (i = 0; i < t->total; i++, td++) { - float tvec[2]; - if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; - copy_v2_v2(tvec, val); - - mul_v2_fl(tvec, td->factor); - - if (t->modifiers & MOD_SNAP_INVERT) { - td->loc[0] = frame + td->factor * (td->iloc[0] - ts->min); - } - else { - td->loc[0] = td->iloc[0] + tvec[0]; - } - - td->loc[1] = td->iloc[1] + tvec[1]; + madd_v2_v2v2fl(td->loc, td->iloc, val, td->factor); } } static void applySeqSlide(TransInfo *t, const int mval[2]) { char str[MAX_INFO_LEN]; - int snap_frame = 0; + + snapSequenceBounds(t, mval); + if (t->con.mode & CON_APPLY) { float pvec[3] = {0.0f, 0.0f, 0.0f}; float tvec[3]; @@ -7348,7 +7340,6 @@ static void applySeqSlide(TransInfo *t, const int mval[2]) copy_v3_v3(t->values, tvec); } else { - snap_frame = snapSequenceBounds(t, mval); // snapGridIncrement(t, t->values); applyNumInput(&t->num, t->values); } @@ -7357,7 +7348,7 @@ static void applySeqSlide(TransInfo *t, const int mval[2]) t->values[1] = floor(t->values[1] + 0.5f); headerSeqSlide(t, t->values, str); - applySeqSlideValue(t, t->values, snap_frame); + applySeqSlideValue(t, t->values); recalcData(t); diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 8d6c693b14a..97cc6dd9dd1 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -626,7 +626,7 @@ typedef enum { void snapGridIncrement(TransInfo *t, float *val); void snapGridIncrementAction(TransInfo *t, float *val, GearsType action); -int snapSequenceBounds(TransInfo *t, const int mval[2]); +void snapSequenceBounds(TransInfo *t, const int mval[2]); bool activeSnap(TransInfo *t); bool validSnap(TransInfo *t); diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 4a2927ace00..fd189e91141 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -577,6 +577,10 @@ static void initSnappingMode(TransInfo *t) t->tsnap.mode = SCE_SNAP_MODE_INCREMENT; } } + else if (t->spacetype == SPACE_SEQ) { + /* We do our own snapping currently, so nothing here */ + t->tsnap.mode = SCE_SNAP_MODE_GRID; /* Dummy, should we rather add a NOP mode? */ + } else { /* Always grid outside of 3D view */ t->tsnap.mode = SCE_SNAP_MODE_INCREMENT; @@ -2431,7 +2435,7 @@ void snapGridIncrement(TransInfo *t, float *val) snapGridIncrementAction(t, val, action); } -int snapSequenceBounds(TransInfo *t, const int mval[2]) +void snapSequenceBounds(TransInfo *t, const int mval[2]) { float xmouse, ymouse; int frame; @@ -2439,7 +2443,7 @@ int snapSequenceBounds(TransInfo *t, const int mval[2]) TransSeq *ts = t->customData; /* reuse increment, strictly speaking could be another snap mode, but leave as is */ if (!(t->modifiers & MOD_SNAP_INVERT)) - return 0; + return; /* convert to frame range */ UI_view2d_region_to_view(&t->ar->v2d, mval[0], mval[1], &xmouse, &ymouse); @@ -2450,7 +2454,7 @@ int snapSequenceBounds(TransInfo *t, const int mval[2]) if (!ts->snap_left) frame = frame - (ts->max - ts->min); - return frame; + t->values[0] = frame - ts->min; } static void applyGridIncrement(TransInfo *t, float *val, int max_index, const float fac[3], GearsType action) diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index b5d9028a8ae..97b0e7e1e0e 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -61,6 +61,9 @@ set(SRC shaders/gpu_shader_fx_ssao_frag.glsl shaders/gpu_shader_fx_dof_frag.glsl shaders/gpu_shader_fx_dof_vert.glsl + shaders/gpu_shader_fx_dof_hq_frag.glsl + shaders/gpu_shader_fx_dof_hq_vert.glsl + shaders/gpu_shader_fx_dof_hq_geo.glsl shaders/gpu_shader_fx_vert.glsl shaders/gpu_shader_material.glsl shaders/gpu_shader_sep_gaussian_blur_frag.glsl @@ -99,6 +102,9 @@ data_to_c_simple(shaders/gpu_shader_fx_vert.glsl SRC) data_to_c_simple(shaders/gpu_shader_fx_ssao_frag.glsl SRC) data_to_c_simple(shaders/gpu_shader_fx_dof_frag.glsl SRC) data_to_c_simple(shaders/gpu_shader_fx_dof_vert.glsl SRC) +data_to_c_simple(shaders/gpu_shader_fx_dof_hq_frag.glsl SRC) +data_to_c_simple(shaders/gpu_shader_fx_dof_hq_vert.glsl SRC) +data_to_c_simple(shaders/gpu_shader_fx_dof_hq_geo.glsl SRC) data_to_c_simple(shaders/gpu_shader_fx_depth_resolve.glsl SRC) data_to_c_simple(shaders/gpu_shader_fx_lib.glsl SRC) diff --git a/source/blender/gpu/GPU_compositing.h b/source/blender/gpu/GPU_compositing.h index 93f1bc64922..5589084705b 100644 --- a/source/blender/gpu/GPU_compositing.h +++ b/source/blender/gpu/GPU_compositing.h @@ -61,11 +61,16 @@ typedef enum GPUFXShaderEffect { GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FOUR = 5, GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FIVE = 6, - GPU_SHADER_FX_DEPTH_RESOLVE = 7, + /* high quality */ + GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_ONE = 7, + GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_TWO = 8, + GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_THREE = 9, + + GPU_SHADER_FX_DEPTH_RESOLVE = 10, } GPUFXShaderEffect; /* keep in synch with enum above! */ -#define MAX_FX_SHADERS 8 +#define MAX_FX_SHADERS 11 /* generate a new FX compositor */ GPUFX *GPU_fx_compositor_create(void); diff --git a/source/blender/gpu/GPU_extensions.h b/source/blender/gpu/GPU_extensions.h index 985cebc8687..401711e84e5 100644 --- a/source/blender/gpu/GPU_extensions.h +++ b/source/blender/gpu/GPU_extensions.h @@ -61,13 +61,14 @@ bool GPU_non_power_of_two_support(void); bool GPU_vertex_buffer_support(void); bool GPU_display_list_support(void); bool GPU_bicubic_bump_support(void); +bool GPU_geometry_shader_support(void); +bool GPU_instanced_drawing_support(void); int GPU_max_texture_size(void); int GPU_color_depth(void); void GPU_code_generate_glsl_lib(void); - /* GPU Types */ typedef enum GPUDeviceType { @@ -120,7 +121,7 @@ GPUTexture *GPU_texture_create_2D(int w, int h, const float *pixels, GPUHDRType GPUTexture *GPU_texture_create_3D(int w, int h, int depth, int channels, const float *fpixels); GPUTexture *GPU_texture_create_depth(int w, int h, char err_out[256]); GPUTexture *GPU_texture_create_vsm_shadow_map(int size, char err_out[256]); -GPUTexture *GPU_texture_create_2D_procedural(int w, int h, const float *pixels, char err_out[256]); +GPUTexture *GPU_texture_create_2D_procedural(int w, int h, const float *pixels, bool repeat, char err_out[256]); GPUTexture *GPU_texture_create_1D_procedural(int w, const float *pixels, char err_out[256]); GPUTexture *GPU_texture_from_blender(struct Image *ima, struct ImageUser *iuser, bool is_data, double time, int mipmap); @@ -136,7 +137,7 @@ void GPU_texture_ref(GPUTexture *tex); void GPU_texture_bind(GPUTexture *tex, int number); void GPU_texture_unbind(GPUTexture *tex); -void GPU_depth_texture_mode(GPUTexture *tex, bool compare, bool use_filter); +void GPU_texture_filter_mode(GPUTexture *tex, bool compare, bool use_filter); GPUFrameBuffer *GPU_texture_framebuffer(GPUTexture *tex); @@ -183,7 +184,7 @@ int GPU_offscreen_height(const GPUOffScreen *ofs); * - only for fragment shaders now * - must call texture bind before setting a texture as uniform! */ -GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, const char *libcode, const char *defines); +GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, const char *geocode, const char *libcode, const char *defines, int input, int output, int number); void GPU_shader_free(GPUShader *shader); void GPU_shader_bind(GPUShader *shader); @@ -192,8 +193,12 @@ void GPU_shader_unbind(void); int GPU_shader_get_uniform(GPUShader *shader, const char *name); void GPU_shader_uniform_vector(GPUShader *shader, int location, int length, int arraysize, const float *value); +void GPU_shader_uniform_vector_int(GPUShader *shader, int location, int length, + int arraysize, const int *value); + void GPU_shader_uniform_texture(GPUShader *shader, int location, GPUTexture *tex); void GPU_shader_uniform_int(GPUShader *shader, int location, int value); +void GPU_shader_geometry_stage_primitive_io(GPUShader *shader, int input, int output, int number); int GPU_shader_get_attribute(GPUShader *shader, const char *name); diff --git a/source/blender/gpu/SConscript b/source/blender/gpu/SConscript index f52b39dba3b..8d0ef394a06 100644 --- a/source/blender/gpu/SConscript +++ b/source/blender/gpu/SConscript @@ -66,6 +66,9 @@ sources.extend(( os.path.join(env['DATA_SOURCES'], "gpu_shader_simple_frag.glsl.c"), os.path.join(env['DATA_SOURCES'], "gpu_shader_simple_vert.glsl.c"), os.path.join(env['DATA_SOURCES'], "gpu_shader_fx_ssao_frag.glsl.c"), + os.path.join(env['DATA_SOURCES'], "gpu_shader_fx_dof_hq_frag.glsl.c"), + os.path.join(env['DATA_SOURCES'], "gpu_shader_fx_dof_hq_vert.glsl.c"), + os.path.join(env['DATA_SOURCES'], "gpu_shader_fx_dof_hq_geo.glsl.c"), os.path.join(env['DATA_SOURCES'], "gpu_shader_fx_dof_frag.glsl.c"), os.path.join(env['DATA_SOURCES'], "gpu_shader_fx_dof_vert.glsl.c"), os.path.join(env['DATA_SOURCES'], "gpu_shader_fx_depth_resolve.glsl.c"), diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 40c9ec0d862..113ed8e341c 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -1419,7 +1419,7 @@ GPUPass *GPU_generate_pass(ListBase *nodes, GPUNodeLink *outlink, /* generate code and compile with opengl */ fragmentcode = code_generate_fragment(nodes, outlink->output); vertexcode = code_generate_vertex(nodes, type); - shader = GPU_shader_create(vertexcode, fragmentcode, glsl_material_library, NULL); + shader = GPU_shader_create(vertexcode, fragmentcode, NULL, glsl_material_library, NULL, 0, 0, 0); /* failed? */ if (!shader) { diff --git a/source/blender/gpu/intern/gpu_compositing.c b/source/blender/gpu/intern/gpu_compositing.c index 511167b775a..96de0db0acb 100644 --- a/source/blender/gpu/intern/gpu_compositing.c +++ b/source/blender/gpu/intern/gpu_compositing.c @@ -50,7 +50,7 @@ #include "GPU_extensions.h" #include "GPU_compositing.h" -#include "GL/glew.h" +#include "GPU_glew.h" #include "MEM_guardedalloc.h" @@ -62,11 +62,20 @@ struct GPUFX { * depth/color framebuffer. Could be extended later though */ GPUFrameBuffer *gbuffer; + /* dimensions of the gbuffer */ + int gbuffer_dim[2]; + /* texture bound to the first color attachment of the gbuffer */ GPUTexture *color_buffer; /* second texture used for ping-pong compositing */ GPUTexture *color_buffer_sec; + /* texture bound to the depth attachment of the gbuffer */ + GPUTexture *depth_buffer; + GPUTexture *depth_buffer_xray; + + /* texture used for jittering for various effects */ + GPUTexture *jitter_buffer; /* all those buffers below have to coexist. Fortunately they are all quarter sized (1/16th of memory) of original framebuffer */ int dof_downsampled_w; @@ -80,26 +89,20 @@ struct GPUFX { GPUTexture *dof_near_coc_final_buffer; /* half size blur buffer */ - GPUTexture *dof_half_downsampled; - /* high quality dof texture downsamplers. 6 levels means 64 pixels wide */ - GPUTexture *dof_nearfar_coc[6]; + GPUTexture *dof_half_downsampled_near; + GPUTexture *dof_half_downsampled_far; + /* high quality dof texture downsamplers. 6 levels means 64 pixels wide - should be enough */ + GPUTexture *dof_nearfar_coc; GPUTexture *dof_near_blur; GPUTexture *dof_far_blur; - GPUTexture *dof_concentric_samples_tex; - /* texture bound to the depth attachment of the gbuffer */ - GPUTexture *depth_buffer; - GPUTexture *depth_buffer_xray; - - /* texture used for jittering for various effects */ - GPUTexture *jitter_buffer; + /* for high quality we use again a spiral texture with radius adapted */ + bool dof_high_quality; /* texture used for ssao */ - int ssao_sample_count; - GPUTexture *ssao_concentric_samples_tex; + int ssao_sample_count_cache; + GPUTexture *ssao_spiral_samples_tex; - /* dimensions of the gbuffer */ - int gbuffer_dim[2]; GPUFXSettings settings; @@ -111,6 +114,8 @@ struct GPUFX { /* we have a stencil, restore the previous state */ bool restore_stencil; + + unsigned int vbuffer; }; #if 0 @@ -174,6 +179,14 @@ GPUFX *GPU_fx_compositor_create(void) { GPUFX *fx = MEM_callocN(sizeof(GPUFX), "GPUFX compositor"); + if (GLEW_ARB_vertex_buffer_object) { + glGenBuffersARB(1, &fx->vbuffer); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, fx->vbuffer); + glBufferDataARB(GL_ARRAY_BUFFER_ARB, 16 * sizeof(float), NULL, GL_STATIC_DRAW); + glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, 8 * sizeof(float), fullscreencos); + glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 8 * sizeof(float), 8 * sizeof(float), fullscreenuvs); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + } return fx; } @@ -192,16 +205,17 @@ static void cleanup_fx_dof_buffers(GPUFX *fx) fx->dof_near_coc_final_buffer = NULL; } - if (fx->dof_half_downsampled) { - GPU_texture_free(fx->dof_half_downsampled); - fx->dof_half_downsampled = NULL; + if (fx->dof_half_downsampled_near) { + GPU_texture_free(fx->dof_half_downsampled_near); + fx->dof_half_downsampled_near = NULL; } - if (fx->dof_nearfar_coc[0]) { - int i; - for (i = 0; i < 6; i++) { - GPU_texture_free(fx->dof_nearfar_coc[i]); - fx->dof_nearfar_coc[i] = NULL; - } + if (fx->dof_half_downsampled_far) { + GPU_texture_free(fx->dof_half_downsampled_far); + fx->dof_half_downsampled_far = NULL; + } + if (fx->dof_nearfar_coc) { + GPU_texture_free(fx->dof_nearfar_coc); + fx->dof_nearfar_coc = NULL; } if (fx->dof_near_blur) { GPU_texture_free(fx->dof_near_blur); @@ -211,10 +225,6 @@ static void cleanup_fx_dof_buffers(GPUFX *fx) GPU_texture_free(fx->dof_far_blur); fx->dof_far_blur = NULL; } - if (fx->dof_concentric_samples_tex) { - GPU_texture_free(fx->dof_concentric_samples_tex); - fx->dof_concentric_samples_tex = NULL; - } } static void cleanup_fx_gl_data(GPUFX *fx, bool do_fbo) @@ -245,9 +255,9 @@ static void cleanup_fx_gl_data(GPUFX *fx, bool do_fbo) cleanup_fx_dof_buffers(fx); - if (fx->ssao_concentric_samples_tex) { - GPU_texture_free(fx->ssao_concentric_samples_tex); - fx->ssao_concentric_samples_tex = NULL; + if (fx->ssao_spiral_samples_tex) { + GPU_texture_free(fx->ssao_spiral_samples_tex); + fx->ssao_spiral_samples_tex = NULL; } if (fx->jitter_buffer && do_fbo) { @@ -265,6 +275,8 @@ static void cleanup_fx_gl_data(GPUFX *fx, bool do_fbo) void GPU_fx_compositor_destroy(GPUFX *fx) { cleanup_fx_gl_data(fx, true); + if (GLEW_ARB_vertex_buffer_object) + glDeleteBuffersARB(1, &fx->vbuffer); MEM_freeN(fx); } @@ -279,7 +291,7 @@ static GPUTexture * create_jitter_texture(void) normalize_v2(jitter[i]); } - return GPU_texture_create_2D_procedural(64, 64, &jitter[0][0], NULL); + return GPU_texture_create_2D_procedural(64, 64, &jitter[0][0], true, NULL); } @@ -356,54 +368,108 @@ bool GPU_fx_compositor_initialize_passes( } if (fx_flag & GPU_FX_FLAG_SSAO) { - if (fx_settings->ssao->samples != fx->ssao_sample_count || !fx->ssao_concentric_samples_tex) { + if (fx_settings->ssao->samples != fx->ssao_sample_count_cache || !fx->ssao_spiral_samples_tex) { if (fx_settings->ssao->samples < 1) fx_settings->ssao->samples = 1; - fx->ssao_sample_count = fx_settings->ssao->samples; + fx->ssao_sample_count_cache = fx_settings->ssao->samples; - if (fx->ssao_concentric_samples_tex) { - GPU_texture_free(fx->ssao_concentric_samples_tex); + if (fx->ssao_spiral_samples_tex) { + GPU_texture_free(fx->ssao_spiral_samples_tex); } - fx->ssao_concentric_samples_tex = create_spiral_sample_texture(fx_settings->ssao->samples); + fx->ssao_spiral_samples_tex = create_spiral_sample_texture(fx_settings->ssao->samples); } } else { - if (fx->ssao_concentric_samples_tex) { - GPU_texture_free(fx->ssao_concentric_samples_tex); - fx->ssao_concentric_samples_tex = NULL; + if (fx->ssao_spiral_samples_tex) { + GPU_texture_free(fx->ssao_spiral_samples_tex); + fx->ssao_spiral_samples_tex = NULL; } } /* create textures for dof effect */ if (fx_flag & GPU_FX_FLAG_DOF) { - if (!fx->dof_near_coc_buffer || !fx->dof_near_coc_blurred_buffer || !fx->dof_near_coc_final_buffer) { + bool dof_high_quality = (fx_settings->dof->high_quality != 0); + + if (dof_high_quality) { + fx->dof_downsampled_w = w / 2; + fx->dof_downsampled_h = h / 2; + + if (!fx->dof_half_downsampled_near || !fx->dof_nearfar_coc || !fx->dof_near_blur || + !fx->dof_far_blur || !fx->dof_half_downsampled_far) { + + if (!(fx->dof_half_downsampled_near = GPU_texture_create_2D( + fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_NONE, err_out))) + { + printf("%.256s\n", err_out); + cleanup_fx_gl_data(fx, true); + return false; + } + if (!(fx->dof_half_downsampled_far = GPU_texture_create_2D( + fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_NONE, err_out))) + { + printf("%.256s\n", err_out); + cleanup_fx_gl_data(fx, true); + return false; + } + if (!(fx->dof_nearfar_coc = GPU_texture_create_2D_procedural( + fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, false, err_out))) + { + printf("%.256s\n", err_out); + cleanup_fx_gl_data(fx, true); + return false; + } + + + if (!(fx->dof_near_blur = GPU_texture_create_2D( + fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_HALF_FLOAT, err_out))) + { + printf("%.256s\n", err_out); + cleanup_fx_gl_data(fx, true); + return false; + } + + if (!(fx->dof_far_blur = GPU_texture_create_2D( + fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_HALF_FLOAT, err_out))) + { + printf("%.256s\n", err_out); + cleanup_fx_gl_data(fx, true); + return false; + } + } + } + else { fx->dof_downsampled_w = w / 4; fx->dof_downsampled_h = h / 4; - if (!(fx->dof_near_coc_buffer = GPU_texture_create_2D( - fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_NONE, err_out))) - { - printf("%.256s\n", err_out); - cleanup_fx_gl_data(fx, true); - return false; - } - if (!(fx->dof_near_coc_blurred_buffer = GPU_texture_create_2D( - fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_NONE, err_out))) - { - printf("%.256s\n", err_out); - cleanup_fx_gl_data(fx, true); - return false; - } - if (!(fx->dof_near_coc_final_buffer = GPU_texture_create_2D( - fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_NONE, err_out))) - { - printf("%.256s\n", err_out); - cleanup_fx_gl_data(fx, true); - return false; + if (!fx->dof_near_coc_buffer || !fx->dof_near_coc_blurred_buffer || !fx->dof_near_coc_final_buffer) { + + if (!(fx->dof_near_coc_buffer = GPU_texture_create_2D( + fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_NONE, err_out))) + { + printf("%.256s\n", err_out); + cleanup_fx_gl_data(fx, true); + return false; + } + if (!(fx->dof_near_coc_blurred_buffer = GPU_texture_create_2D( + fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_NONE, err_out))) + { + printf("%.256s\n", err_out); + cleanup_fx_gl_data(fx, true); + return false; + } + if (!(fx->dof_near_coc_final_buffer = GPU_texture_create_2D( + fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_NONE, err_out))) + { + printf("%.256s\n", err_out); + cleanup_fx_gl_data(fx, true); + return false; + } } } + + fx->dof_high_quality = dof_high_quality; } else { /* cleanup unnecessary buffers */ @@ -529,8 +595,9 @@ void GPU_fx_compositor_XRay_resolve(GPUFX *fx) glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); /* set up quad buffer */ - glVertexPointer(2, GL_FLOAT, 0, fullscreencos); - glTexCoordPointer(2, GL_FLOAT, 0, fullscreenuvs); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, fx->vbuffer); + glVertexPointer(2, GL_FLOAT, 0, NULL); + glTexCoordPointer(2, GL_FLOAT, 0, ((GLubyte *)NULL + 8 * sizeof(float))); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); @@ -544,14 +611,14 @@ void GPU_fx_compositor_XRay_resolve(GPUFX *fx) GPU_shader_bind(depth_resolve_shader); GPU_texture_bind(fx->depth_buffer_xray, 0); - GPU_depth_texture_mode(fx->depth_buffer_xray, false, true); + GPU_texture_filter_mode(fx->depth_buffer_xray, false, true); GPU_shader_uniform_texture(depth_resolve_shader, depth_uniform, fx->depth_buffer_xray); /* draw */ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); /* disable bindings */ - GPU_depth_texture_mode(fx->depth_buffer_xray, true, false); + GPU_texture_filter_mode(fx->depth_buffer_xray, true, false); GPU_texture_unbind(fx->depth_buffer_xray); GPU_shader_unbind(); @@ -559,6 +626,7 @@ void GPU_fx_compositor_XRay_resolve(GPUFX *fx) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); @@ -595,8 +663,9 @@ bool GPU_fx_do_composite_pass(GPUFX *fx, float projmat[4][4], bool is_persp, str target = fx->color_buffer_sec; /* set up quad buffer */ - glVertexPointer(2, GL_FLOAT, 0, fullscreencos); - glTexCoordPointer(2, GL_FLOAT, 0, fullscreenuvs); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, fx->vbuffer); + glVertexPointer(2, GL_FLOAT, 0, NULL); + glTexCoordPointer(2, GL_FLOAT, 0, ((GLubyte *)NULL + 8 * sizeof(float))); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); @@ -643,7 +712,7 @@ bool GPU_fx_do_composite_pass(GPUFX *fx, float projmat[4][4], bool is_persp, str float ssao_params[4] = {fx_ssao->distance_max, fx_ssao->factor, fx_ssao->attenuation, 0.0f}; float sample_params[4]; - sample_params[0] = fx->ssao_sample_count; + sample_params[0] = fx->ssao_sample_count_cache; /* multiplier so we tile the random texture on screen */ sample_params[2] = fx->gbuffer_dim[0] / 64.0; sample_params[3] = fx->gbuffer_dim[1] / 64.0; @@ -668,14 +737,14 @@ bool GPU_fx_do_composite_pass(GPUFX *fx, float projmat[4][4], bool is_persp, str GPU_shader_uniform_texture(ssao_shader, color_uniform, src); GPU_texture_bind(fx->depth_buffer, numslots++); - GPU_depth_texture_mode(fx->depth_buffer, false, true); + GPU_texture_filter_mode(fx->depth_buffer, false, true); GPU_shader_uniform_texture(ssao_shader, depth_uniform, fx->depth_buffer); GPU_texture_bind(fx->jitter_buffer, numslots++); GPU_shader_uniform_texture(ssao_shader, ssao_jitter_uniform, fx->jitter_buffer); - GPU_texture_bind(fx->ssao_concentric_samples_tex, numslots++); - GPU_shader_uniform_texture(ssao_shader, ssao_concentric_tex, fx->ssao_concentric_samples_tex); + GPU_texture_bind(fx->ssao_spiral_samples_tex, numslots++); + GPU_shader_uniform_texture(ssao_shader, ssao_concentric_tex, fx->ssao_spiral_samples_tex); /* draw */ gpu_fx_bind_render_target(&passes_left, fx, ofs, target); @@ -684,10 +753,10 @@ bool GPU_fx_do_composite_pass(GPUFX *fx, float projmat[4][4], bool is_persp, str /* disable bindings */ GPU_texture_unbind(src); - GPU_depth_texture_mode(fx->depth_buffer, true, false); + GPU_texture_filter_mode(fx->depth_buffer, true, false); GPU_texture_unbind(fx->depth_buffer); GPU_texture_unbind(fx->jitter_buffer); - GPU_texture_unbind(fx->ssao_concentric_samples_tex); + GPU_texture_unbind(fx->ssao_spiral_samples_tex); /* may not be attached, in that case this just returns */ if (target) { @@ -709,7 +778,6 @@ bool GPU_fx_do_composite_pass(GPUFX *fx, float projmat[4][4], bool is_persp, str /* second pass, dof */ if (fx->effects & GPU_FX_FLAG_DOF) { const GPUDOFSettings *fx_dof = fx->settings.dof; - GPUShader *dof_shader_pass1, *dof_shader_pass2, *dof_shader_pass3, *dof_shader_pass4, *dof_shader_pass5; float dof_params[4]; float scale = scene->unit.system ? scene->unit.scale_length : 1.0f; /* this is factor that converts to the scene scale. focal length and sensor are expressed in mm @@ -722,248 +790,465 @@ bool GPU_fx_do_composite_pass(GPUFX *fx, float projmat[4][4], bool is_persp, str dof_params[0] = aperture * fabsf(scale_camera * fx_dof->focal_length / ((fx_dof->focus_distance / scale) - scale_camera * fx_dof->focal_length)); dof_params[1] = fx_dof->focus_distance / scale; dof_params[2] = fx->gbuffer_dim[0] / (scale_camera * fx_dof->sensor); - dof_params[3] = 0.0f; - - /* DOF effect has many passes but most of them are performed on a texture whose dimensions are 4 times less than the original - * (16 times lower than original screen resolution). Technique used is not very exact but should be fast enough and is based - * on "Practical Post-Process Depth of Field" see http://http.developer.nvidia.com/GPUGems3/gpugems3_ch28.html */ - dof_shader_pass1 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_ONE, is_persp); - dof_shader_pass2 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_TWO, is_persp); - dof_shader_pass3 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_THREE, is_persp); - dof_shader_pass4 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FOUR, is_persp); - dof_shader_pass5 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FIVE, is_persp); - - /* error occured, restore framebuffers and return */ - if (!(dof_shader_pass1 && dof_shader_pass2 && dof_shader_pass3 && dof_shader_pass4 && dof_shader_pass5)) { - GPU_framebuffer_texture_unbind(fx->gbuffer, NULL); - GPU_framebuffer_restore(); - return false; - } + dof_params[3] = fx_dof->num_blades; - /* pass first, first level of blur in low res buffer */ - { - int invrendertargetdim_uniform, color_uniform, depth_uniform, dof_uniform; - int viewvecs_uniform; + if (fx->dof_high_quality) { + GPUShader *dof_shader_pass1, *dof_shader_pass2, *dof_shader_pass3; - float invrendertargetdim[2] = {1.0f / fx->gbuffer_dim[0], 1.0f / fx->gbuffer_dim[1]}; + /* custom shaders close to the effect described in CryEngine 3 Graphics Gems */ + dof_shader_pass1 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_ONE, is_persp); + dof_shader_pass2 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_TWO, is_persp); + dof_shader_pass3 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_THREE, is_persp); - dof_uniform = GPU_shader_get_uniform(dof_shader_pass1, "dof_params"); - invrendertargetdim_uniform = GPU_shader_get_uniform(dof_shader_pass1, "invrendertargetdim"); - color_uniform = GPU_shader_get_uniform(dof_shader_pass1, "colorbuffer"); - depth_uniform = GPU_shader_get_uniform(dof_shader_pass1, "depthbuffer"); - viewvecs_uniform = GPU_shader_get_uniform(dof_shader_pass1, "viewvecs"); + /* error occured, restore framebuffers and return */ + if (!(dof_shader_pass1 && dof_shader_pass2 && dof_shader_pass3)) { + GPU_framebuffer_texture_unbind(fx->gbuffer, NULL); + GPU_framebuffer_restore(); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); - GPU_shader_bind(dof_shader_pass1); + GPU_shader_unbind(); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + return false; + } - GPU_shader_uniform_vector(dof_shader_pass1, dof_uniform, 4, 1, dof_params); - GPU_shader_uniform_vector(dof_shader_pass1, invrendertargetdim_uniform, 2, 1, invrendertargetdim); - GPU_shader_uniform_vector(dof_shader_pass1, viewvecs_uniform, 4, 3, viewvecs[0]); + /* pass first, downsample the color buffer to near/far targets and calculate coc texture */ + { + int depth_uniform, dof_uniform; + int viewvecs_uniform; + int invrendertargetdim_uniform, color_uniform; + + float invrendertargetdim[2] = {1.0f / fx->dof_downsampled_w, 1.0f / fx->dof_downsampled_h}; + + invrendertargetdim_uniform = GPU_shader_get_uniform(dof_shader_pass1, "invrendertargetdim"); + color_uniform = GPU_shader_get_uniform(dof_shader_pass1, "colorbuffer"); + dof_uniform = GPU_shader_get_uniform(dof_shader_pass1, "dof_params"); + invrendertargetdim_uniform = GPU_shader_get_uniform(dof_shader_pass1, "invrendertargetdim"); + depth_uniform = GPU_shader_get_uniform(dof_shader_pass1, "depthbuffer"); + viewvecs_uniform = GPU_shader_get_uniform(dof_shader_pass1, "viewvecs"); + + GPU_shader_bind(dof_shader_pass1); + + GPU_shader_uniform_vector(dof_shader_pass1, dof_uniform, 4, 1, dof_params); + GPU_shader_uniform_vector(dof_shader_pass1, invrendertargetdim_uniform, 2, 1, invrendertargetdim); + GPU_shader_uniform_vector(dof_shader_pass1, viewvecs_uniform, 4, 3, viewvecs[0]); + + GPU_shader_uniform_vector(dof_shader_pass1, invrendertargetdim_uniform, 2, 1, invrendertargetdim); + + GPU_texture_bind(fx->depth_buffer, numslots++); + GPU_texture_filter_mode(fx->depth_buffer, false, false); + GPU_shader_uniform_texture(dof_shader_pass1, depth_uniform, fx->depth_buffer); + + GPU_texture_bind(src, numslots++); + /* disable filtering for the texture so custom downsample can do the right thing */ + GPU_texture_filter_mode(src, false, false); + GPU_shader_uniform_texture(dof_shader_pass2, color_uniform, src); + + /* target is the downsampled coc buffer */ + GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_half_downsampled_near, 0, NULL); + GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_half_downsampled_far, 1, NULL); + GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_nearfar_coc, 2, NULL); + /* binding takes care of setting the viewport to the downsampled size */ + GPU_framebuffer_slots_bind(fx->gbuffer, 0); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + /* disable bindings */ + GPU_texture_filter_mode(src, false, true); + GPU_texture_unbind(src); + GPU_texture_filter_mode(fx->depth_buffer, true, false); + GPU_texture_unbind(fx->depth_buffer); + + GPU_framebuffer_texture_detach(fx->dof_half_downsampled_near); + GPU_framebuffer_texture_detach(fx->dof_half_downsampled_far); + GPU_framebuffer_texture_detach(fx->dof_nearfar_coc); + GPU_framebuffer_texture_unbind(fx->gbuffer, fx->dof_half_downsampled_near); + + numslots = 0; + } - GPU_texture_bind(src, numslots++); - GPU_shader_uniform_texture(dof_shader_pass1, color_uniform, src); + /* second pass, shoot quads for every pixel in the downsampled buffers, scaling according + * to circle of confusion */ + { + int rendertargetdim_uniform, coc_uniform, color_uniform, select_uniform, dof_uniform; + int rendertargetdim[2] = {fx->dof_downsampled_w, fx->dof_downsampled_h}; + float selection[2] = {0.0f, 1.0f}; - GPU_texture_bind(fx->depth_buffer, numslots++); - GPU_depth_texture_mode(fx->depth_buffer, false, true); - GPU_shader_uniform_texture(dof_shader_pass1, depth_uniform, fx->depth_buffer); + rendertargetdim_uniform = GPU_shader_get_uniform(dof_shader_pass2, "rendertargetdim"); - /* target is the downsampled coc buffer */ - GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_buffer, 0, NULL); - /* binding takes care of setting the viewport to the downsampled size */ - GPU_texture_bind_as_framebuffer(fx->dof_near_coc_buffer); + color_uniform = GPU_shader_get_uniform(dof_shader_pass2, "colorbuffer"); + coc_uniform = GPU_shader_get_uniform(dof_shader_pass2, "cocbuffer"); + select_uniform = GPU_shader_get_uniform(dof_shader_pass2, "layerselection"); + dof_uniform = GPU_shader_get_uniform(dof_shader_pass2, "dof_params"); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - /* disable bindings */ - GPU_texture_unbind(src); - GPU_depth_texture_mode(fx->depth_buffer, true, false); - GPU_texture_unbind(fx->depth_buffer); + GPU_shader_bind(dof_shader_pass2); - GPU_framebuffer_texture_detach(fx->dof_near_coc_buffer); - numslots = 0; + GPU_shader_uniform_vector(dof_shader_pass2, dof_uniform, 4, 1, dof_params); + GPU_shader_uniform_vector_int(dof_shader_pass2, rendertargetdim_uniform, 2, 1, rendertargetdim); + GPU_shader_uniform_vector(dof_shader_pass2, select_uniform, 2, 1, selection); + + GPU_texture_bind(fx->dof_nearfar_coc, numslots++); + GPU_shader_uniform_texture(dof_shader_pass2, coc_uniform, fx->dof_nearfar_coc); + + GPU_texture_bind(fx->dof_half_downsampled_far, numslots++); + GPU_texture_bind(fx->dof_half_downsampled_near, numslots++); + GPU_shader_uniform_texture(dof_shader_pass2, color_uniform, fx->dof_half_downsampled_far); + GPU_texture_filter_mode(fx->dof_half_downsampled_far, false, false); + + /* target is the downsampled coc buffer */ + GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_far_blur, 0, NULL); + GPU_texture_bind_as_framebuffer(fx->dof_far_blur); + + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + /* have to clear the buffer unfortunately */ + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + /* the draw call we all waited for, draw a point per pixel, scaled per circle of confusion */ + glDrawArraysInstancedARB(GL_POINTS, 0, 1, fx->dof_downsampled_w * fx->dof_downsampled_h); + + GPU_texture_unbind(fx->dof_half_downsampled_far); + GPU_framebuffer_texture_detach(fx->dof_far_blur); + + GPU_shader_uniform_texture(dof_shader_pass2, color_uniform, fx->dof_half_downsampled_near); + GPU_texture_filter_mode(fx->dof_half_downsampled_near, false, false); + + selection[0] = 1.0f; + selection[1] = 0.0f; + + GPU_shader_uniform_vector(dof_shader_pass2, select_uniform, 2, 1, selection); + + GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_blur, 0, NULL); + /* have to clear the buffer unfortunately */ + glClear(GL_COLOR_BUFFER_BIT); + /* the draw call we all waited for, draw a point per pixel, scaled per circle of confusion */ + glDrawArraysInstancedARB(GL_POINTS, 0, 1, fx->dof_downsampled_w * fx->dof_downsampled_h); + + /* disable bindings */ + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_BLEND); + + GPU_framebuffer_texture_detach(fx->dof_near_blur); + + GPU_texture_unbind(fx->dof_half_downsampled_near); + GPU_texture_unbind(fx->dof_nearfar_coc); + + GPU_framebuffer_texture_unbind(fx->gbuffer, fx->dof_far_blur); + } + + /* third pass, accumulate the near/far blur fields */ + { + int invrendertargetdim_uniform, near_uniform, color_uniform; + int dof_uniform, far_uniform, viewvecs_uniform, depth_uniform; + + float invrendertargetdim[2] = {1.0f / fx->dof_downsampled_w, 1.0f / fx->dof_downsampled_h}; + + dof_uniform = GPU_shader_get_uniform(dof_shader_pass3, "dof_params"); + invrendertargetdim_uniform = GPU_shader_get_uniform(dof_shader_pass3, "invrendertargetdim"); + color_uniform = GPU_shader_get_uniform(dof_shader_pass3, "colorbuffer"); + far_uniform = GPU_shader_get_uniform(dof_shader_pass3, "farbuffer"); + near_uniform = GPU_shader_get_uniform(dof_shader_pass3, "nearbuffer"); + viewvecs_uniform = GPU_shader_get_uniform(dof_shader_pass3, "viewvecs"); + depth_uniform = GPU_shader_get_uniform(dof_shader_pass3, "depthbuffer"); + + GPU_shader_bind(dof_shader_pass3); + + GPU_shader_uniform_vector(dof_shader_pass3, dof_uniform, 4, 1, dof_params); + + GPU_shader_uniform_vector(dof_shader_pass3, invrendertargetdim_uniform, 2, 1, invrendertargetdim); + GPU_shader_uniform_vector(dof_shader_pass3, viewvecs_uniform, 4, 3, viewvecs[0]); + + GPU_texture_bind(fx->dof_near_blur, numslots++); + GPU_shader_uniform_texture(dof_shader_pass3, near_uniform, fx->dof_near_blur); + GPU_texture_filter_mode(fx->dof_near_blur, false, false); + + GPU_texture_bind(fx->dof_far_blur, numslots++); + GPU_shader_uniform_texture(dof_shader_pass3, far_uniform, fx->dof_far_blur); + GPU_texture_filter_mode(fx->dof_far_blur, false, false); + + GPU_texture_bind(fx->depth_buffer, numslots++); + GPU_texture_filter_mode(fx->depth_buffer, false, false); + GPU_shader_uniform_texture(dof_shader_pass3, depth_uniform, fx->depth_buffer); + + GPU_texture_bind(src, numslots++); + GPU_shader_uniform_texture(dof_shader_pass3, color_uniform, src); + + /* if this is the last pass, prepare for rendering on the frambuffer */ + gpu_fx_bind_render_target(&passes_left, fx, ofs, target); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + /* disable bindings */ + GPU_texture_unbind(fx->dof_near_blur); + GPU_texture_unbind(fx->dof_far_blur); + GPU_texture_unbind(src); + GPU_texture_filter_mode(fx->depth_buffer, true, false); + GPU_texture_unbind(fx->depth_buffer); + + /* may not be attached, in that case this just returns */ + if (target) { + GPU_framebuffer_texture_detach(target); + if (ofs) { + GPU_offscreen_bind(ofs, false); + } + else { + GPU_framebuffer_restore(); + } + } + + numslots = 0; + } } + else { + GPUShader *dof_shader_pass1, *dof_shader_pass2, *dof_shader_pass3, *dof_shader_pass4, *dof_shader_pass5; + + /* DOF effect has many passes but most of them are performed on a texture whose dimensions are 4 times less than the original + * (16 times lower than original screen resolution). Technique used is not very exact but should be fast enough and is based + * on "Practical Post-Process Depth of Field" see http://http.developer.nvidia.com/GPUGems3/gpugems3_ch28.html */ + dof_shader_pass1 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_ONE, is_persp); + dof_shader_pass2 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_TWO, is_persp); + dof_shader_pass3 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_THREE, is_persp); + dof_shader_pass4 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FOUR, is_persp); + dof_shader_pass5 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FIVE, is_persp); + + /* error occured, restore framebuffers and return */ + if (!(dof_shader_pass1 && dof_shader_pass2 && dof_shader_pass3 && dof_shader_pass4 && dof_shader_pass5)) { + GPU_framebuffer_texture_unbind(fx->gbuffer, NULL); + GPU_framebuffer_restore(); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + GPU_shader_unbind(); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + return false; + } - /* second pass, gaussian blur the downsampled image */ - { - int invrendertargetdim_uniform, color_uniform, depth_uniform, dof_uniform; - int viewvecs_uniform; - float invrendertargetdim[2] = {1.0f / GPU_texture_opengl_width(fx->dof_near_coc_blurred_buffer), - 1.0f / GPU_texture_opengl_height(fx->dof_near_coc_blurred_buffer)}; - float tmp = invrendertargetdim[0]; - invrendertargetdim[0] = 0.0f; + /* pass first, first level of blur in low res buffer */ + { + int invrendertargetdim_uniform, color_uniform, depth_uniform, dof_uniform; + int viewvecs_uniform; - dof_params[2] = GPU_texture_opengl_width(fx->dof_near_coc_blurred_buffer) / (scale_camera * fx_dof->sensor); + float invrendertargetdim[2] = {1.0f / fx->gbuffer_dim[0], 1.0f / fx->gbuffer_dim[1]}; - dof_uniform = GPU_shader_get_uniform(dof_shader_pass2, "dof_params"); - invrendertargetdim_uniform = GPU_shader_get_uniform(dof_shader_pass2, "invrendertargetdim"); - color_uniform = GPU_shader_get_uniform(dof_shader_pass2, "colorbuffer"); - depth_uniform = GPU_shader_get_uniform(dof_shader_pass2, "depthbuffer"); - viewvecs_uniform = GPU_shader_get_uniform(dof_shader_pass2, "viewvecs"); + dof_uniform = GPU_shader_get_uniform(dof_shader_pass1, "dof_params"); + invrendertargetdim_uniform = GPU_shader_get_uniform(dof_shader_pass1, "invrendertargetdim"); + color_uniform = GPU_shader_get_uniform(dof_shader_pass1, "colorbuffer"); + depth_uniform = GPU_shader_get_uniform(dof_shader_pass1, "depthbuffer"); + viewvecs_uniform = GPU_shader_get_uniform(dof_shader_pass1, "viewvecs"); - /* Blurring vertically */ - GPU_shader_bind(dof_shader_pass2); + GPU_shader_bind(dof_shader_pass1); - GPU_shader_uniform_vector(dof_shader_pass2, dof_uniform, 4, 1, dof_params); - GPU_shader_uniform_vector(dof_shader_pass2, invrendertargetdim_uniform, 2, 1, invrendertargetdim); - GPU_shader_uniform_vector(dof_shader_pass2, viewvecs_uniform, 4, 3, viewvecs[0]); + GPU_shader_uniform_vector(dof_shader_pass1, dof_uniform, 4, 1, dof_params); + GPU_shader_uniform_vector(dof_shader_pass1, invrendertargetdim_uniform, 2, 1, invrendertargetdim); + GPU_shader_uniform_vector(dof_shader_pass1, viewvecs_uniform, 4, 3, viewvecs[0]); - GPU_texture_bind(fx->depth_buffer, numslots++); - GPU_depth_texture_mode(fx->depth_buffer, false, true); - GPU_shader_uniform_texture(dof_shader_pass2, depth_uniform, fx->depth_buffer); + GPU_texture_bind(src, numslots++); + GPU_shader_uniform_texture(dof_shader_pass1, color_uniform, src); - GPU_texture_bind(fx->dof_near_coc_buffer, numslots++); - GPU_shader_uniform_texture(dof_shader_pass2, color_uniform, fx->dof_near_coc_buffer); + GPU_texture_bind(fx->depth_buffer, numslots++); + GPU_texture_filter_mode(fx->depth_buffer, false, true); + GPU_shader_uniform_texture(dof_shader_pass1, depth_uniform, fx->depth_buffer); - /* use final buffer as a temp here */ - GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_final_buffer, 0, NULL); + /* target is the downsampled coc buffer */ + GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_buffer, 0, NULL); + /* binding takes care of setting the viewport to the downsampled size */ + GPU_texture_bind_as_framebuffer(fx->dof_near_coc_buffer); - /* Drawing quad */ - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + /* disable bindings */ + GPU_texture_unbind(src); + GPU_texture_filter_mode(fx->depth_buffer, true, false); + GPU_texture_unbind(fx->depth_buffer); - /* *unbind/detach */ - GPU_texture_unbind(fx->dof_near_coc_buffer); - GPU_framebuffer_texture_detach(fx->dof_near_coc_final_buffer); + GPU_framebuffer_texture_detach(fx->dof_near_coc_buffer); + numslots = 0; + } - /* Blurring horizontally */ - invrendertargetdim[0] = tmp; - invrendertargetdim[1] = 0.0f; - GPU_shader_uniform_vector(dof_shader_pass2, invrendertargetdim_uniform, 2, 1, invrendertargetdim); + /* second pass, gaussian blur the downsampled image */ + { + int invrendertargetdim_uniform, color_uniform, depth_uniform, dof_uniform; + int viewvecs_uniform; + float invrendertargetdim[2] = {1.0f / GPU_texture_opengl_width(fx->dof_near_coc_blurred_buffer), + 1.0f / GPU_texture_opengl_height(fx->dof_near_coc_blurred_buffer)}; + float tmp = invrendertargetdim[0]; + invrendertargetdim[0] = 0.0f; - GPU_texture_bind(fx->dof_near_coc_final_buffer, numslots++); - GPU_shader_uniform_texture(dof_shader_pass2, color_uniform, fx->dof_near_coc_final_buffer); + dof_params[2] = GPU_texture_opengl_width(fx->dof_near_coc_blurred_buffer) / (scale_camera * fx_dof->sensor); - GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_blurred_buffer, 0, NULL); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + dof_uniform = GPU_shader_get_uniform(dof_shader_pass2, "dof_params"); + invrendertargetdim_uniform = GPU_shader_get_uniform(dof_shader_pass2, "invrendertargetdim"); + color_uniform = GPU_shader_get_uniform(dof_shader_pass2, "colorbuffer"); + depth_uniform = GPU_shader_get_uniform(dof_shader_pass2, "depthbuffer"); + viewvecs_uniform = GPU_shader_get_uniform(dof_shader_pass2, "viewvecs"); - /* *unbind/detach */ - GPU_depth_texture_mode(fx->depth_buffer, true, false); - GPU_texture_unbind(fx->depth_buffer); + /* Blurring vertically */ + GPU_shader_bind(dof_shader_pass2); - GPU_texture_unbind(fx->dof_near_coc_final_buffer); - GPU_framebuffer_texture_detach(fx->dof_near_coc_blurred_buffer); + GPU_shader_uniform_vector(dof_shader_pass2, dof_uniform, 4, 1, dof_params); + GPU_shader_uniform_vector(dof_shader_pass2, invrendertargetdim_uniform, 2, 1, invrendertargetdim); + GPU_shader_uniform_vector(dof_shader_pass2, viewvecs_uniform, 4, 3, viewvecs[0]); - dof_params[2] = fx->gbuffer_dim[0] / (scale_camera * fx_dof->sensor); + GPU_texture_bind(fx->depth_buffer, numslots++); + GPU_texture_filter_mode(fx->depth_buffer, false, true); + GPU_shader_uniform_texture(dof_shader_pass2, depth_uniform, fx->depth_buffer); - numslots = 0; - } + GPU_texture_bind(fx->dof_near_coc_buffer, numslots++); + GPU_shader_uniform_texture(dof_shader_pass2, color_uniform, fx->dof_near_coc_buffer); - /* third pass, calculate near coc */ - { - int near_coc_downsampled, near_coc_blurred; + /* use final buffer as a temp here */ + GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_final_buffer, 0, NULL); - near_coc_downsampled = GPU_shader_get_uniform(dof_shader_pass3, "colorbuffer"); - near_coc_blurred = GPU_shader_get_uniform(dof_shader_pass3, "blurredcolorbuffer"); + /* Drawing quad */ + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - GPU_shader_bind(dof_shader_pass3); + /* *unbind/detach */ + GPU_texture_unbind(fx->dof_near_coc_buffer); + GPU_framebuffer_texture_detach(fx->dof_near_coc_final_buffer); - GPU_texture_bind(fx->dof_near_coc_buffer, numslots++); - GPU_shader_uniform_texture(dof_shader_pass3, near_coc_downsampled, fx->dof_near_coc_buffer); + /* Blurring horizontally */ + invrendertargetdim[0] = tmp; + invrendertargetdim[1] = 0.0f; + GPU_shader_uniform_vector(dof_shader_pass2, invrendertargetdim_uniform, 2, 1, invrendertargetdim); - GPU_texture_bind(fx->dof_near_coc_blurred_buffer, numslots++); - GPU_shader_uniform_texture(dof_shader_pass3, near_coc_blurred, fx->dof_near_coc_blurred_buffer); + GPU_texture_bind(fx->dof_near_coc_final_buffer, numslots++); + GPU_shader_uniform_texture(dof_shader_pass2, color_uniform, fx->dof_near_coc_final_buffer); - GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_final_buffer, 0, NULL); + GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_blurred_buffer, 0, NULL); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - /* disable bindings */ - GPU_texture_unbind(fx->dof_near_coc_buffer); - GPU_texture_unbind(fx->dof_near_coc_blurred_buffer); + /* *unbind/detach */ + GPU_texture_filter_mode(fx->depth_buffer, true, false); + GPU_texture_unbind(fx->depth_buffer); - /* unbinding here restores the size to the original */ - GPU_framebuffer_texture_detach(fx->dof_near_coc_final_buffer); + GPU_texture_unbind(fx->dof_near_coc_final_buffer); + GPU_framebuffer_texture_detach(fx->dof_near_coc_blurred_buffer); - numslots = 0; - } + dof_params[2] = fx->gbuffer_dim[0] / (scale_camera * fx_dof->sensor); - /* fourth pass blur final coc once to eliminate discontinuities */ - { - int near_coc_downsampled; - int invrendertargetdim_uniform; - float invrendertargetdim[2] = {1.0f / GPU_texture_opengl_width(fx->dof_near_coc_blurred_buffer), - 1.0f / GPU_texture_opengl_height(fx->dof_near_coc_blurred_buffer)}; + numslots = 0; + } - near_coc_downsampled = GPU_shader_get_uniform(dof_shader_pass4, "colorbuffer"); - invrendertargetdim_uniform = GPU_shader_get_uniform(dof_shader_pass4, "invrendertargetdim"); + /* third pass, calculate near coc */ + { + int near_coc_downsampled, near_coc_blurred; - GPU_shader_bind(dof_shader_pass4); + near_coc_downsampled = GPU_shader_get_uniform(dof_shader_pass3, "colorbuffer"); + near_coc_blurred = GPU_shader_get_uniform(dof_shader_pass3, "blurredcolorbuffer"); - GPU_texture_bind(fx->dof_near_coc_final_buffer, numslots++); - GPU_shader_uniform_texture(dof_shader_pass4, near_coc_downsampled, fx->dof_near_coc_final_buffer); - GPU_shader_uniform_vector(dof_shader_pass4, invrendertargetdim_uniform, 2, 1, invrendertargetdim); + GPU_shader_bind(dof_shader_pass3); - GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_buffer, 0, NULL); + GPU_texture_bind(fx->dof_near_coc_buffer, numslots++); + GPU_shader_uniform_texture(dof_shader_pass3, near_coc_downsampled, fx->dof_near_coc_buffer); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - /* disable bindings */ - GPU_texture_unbind(fx->dof_near_coc_final_buffer); + GPU_texture_bind(fx->dof_near_coc_blurred_buffer, numslots++); + GPU_shader_uniform_texture(dof_shader_pass3, near_coc_blurred, fx->dof_near_coc_blurred_buffer); - /* unbinding here restores the size to the original */ - GPU_framebuffer_texture_unbind(fx->gbuffer, fx->dof_near_coc_buffer); - GPU_framebuffer_texture_detach(fx->dof_near_coc_buffer); + GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_final_buffer, 0, NULL); - numslots = 0; - } + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + /* disable bindings */ + GPU_texture_unbind(fx->dof_near_coc_buffer); + GPU_texture_unbind(fx->dof_near_coc_blurred_buffer); - /* final pass, merge blurred layers according to final calculated coc */ - { - int medium_blurred_uniform, high_blurred_uniform, original_uniform, depth_uniform, dof_uniform; - int invrendertargetdim_uniform, viewvecs_uniform; - float invrendertargetdim[2] = {1.0f / fx->gbuffer_dim[0], 1.0f / fx->gbuffer_dim[1]}; + /* unbinding here restores the size to the original */ + GPU_framebuffer_texture_detach(fx->dof_near_coc_final_buffer); - medium_blurred_uniform = GPU_shader_get_uniform(dof_shader_pass5, "mblurredcolorbuffer"); - high_blurred_uniform = GPU_shader_get_uniform(dof_shader_pass5, "blurredcolorbuffer"); - dof_uniform = GPU_shader_get_uniform(dof_shader_pass5, "dof_params"); - invrendertargetdim_uniform = GPU_shader_get_uniform(dof_shader_pass5, "invrendertargetdim"); - original_uniform = GPU_shader_get_uniform(dof_shader_pass5, "colorbuffer"); - depth_uniform = GPU_shader_get_uniform(dof_shader_pass5, "depthbuffer"); - viewvecs_uniform = GPU_shader_get_uniform(dof_shader_pass5, "viewvecs"); + numslots = 0; + } - GPU_shader_bind(dof_shader_pass5); + /* fourth pass blur final coc once to eliminate discontinuities */ + { + int near_coc_downsampled; + int invrendertargetdim_uniform; + float invrendertargetdim[2] = {1.0f / GPU_texture_opengl_width(fx->dof_near_coc_blurred_buffer), + 1.0f / GPU_texture_opengl_height(fx->dof_near_coc_blurred_buffer)}; - GPU_shader_uniform_vector(dof_shader_pass5, dof_uniform, 4, 1, dof_params); - GPU_shader_uniform_vector(dof_shader_pass5, invrendertargetdim_uniform, 2, 1, invrendertargetdim); - GPU_shader_uniform_vector(dof_shader_pass5, viewvecs_uniform, 4, 3, viewvecs[0]); + near_coc_downsampled = GPU_shader_get_uniform(dof_shader_pass4, "colorbuffer"); + invrendertargetdim_uniform = GPU_shader_get_uniform(dof_shader_pass4, "invrendertargetdim"); - GPU_texture_bind(src, numslots++); - GPU_shader_uniform_texture(dof_shader_pass5, original_uniform, src); + GPU_shader_bind(dof_shader_pass4); - GPU_texture_bind(fx->dof_near_coc_blurred_buffer, numslots++); - GPU_shader_uniform_texture(dof_shader_pass5, high_blurred_uniform, fx->dof_near_coc_blurred_buffer); + GPU_texture_bind(fx->dof_near_coc_final_buffer, numslots++); + GPU_shader_uniform_texture(dof_shader_pass4, near_coc_downsampled, fx->dof_near_coc_final_buffer); + GPU_shader_uniform_vector(dof_shader_pass4, invrendertargetdim_uniform, 2, 1, invrendertargetdim); - GPU_texture_bind(fx->dof_near_coc_buffer, numslots++); - GPU_shader_uniform_texture(dof_shader_pass5, medium_blurred_uniform, fx->dof_near_coc_buffer); + GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_buffer, 0, NULL); - GPU_texture_bind(fx->depth_buffer, numslots++); - GPU_depth_texture_mode(fx->depth_buffer, false, true); - GPU_shader_uniform_texture(dof_shader_pass5, depth_uniform, fx->depth_buffer); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + /* disable bindings */ + GPU_texture_unbind(fx->dof_near_coc_final_buffer); - /* if this is the last pass, prepare for rendering on the frambuffer */ - gpu_fx_bind_render_target(&passes_left, fx, ofs, target); + /* unbinding here restores the size to the original */ + GPU_framebuffer_texture_unbind(fx->gbuffer, fx->dof_near_coc_buffer); + GPU_framebuffer_texture_detach(fx->dof_near_coc_buffer); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - /* disable bindings */ - GPU_texture_unbind(fx->dof_near_coc_buffer); - GPU_texture_unbind(fx->dof_near_coc_blurred_buffer); - GPU_texture_unbind(src); - GPU_depth_texture_mode(fx->depth_buffer, true, false); - GPU_texture_unbind(fx->depth_buffer); + numslots = 0; + } - /* may not be attached, in that case this just returns */ - if (target) { - GPU_framebuffer_texture_detach(target); - if (ofs) { - GPU_offscreen_bind(ofs, false); - } - else { - GPU_framebuffer_restore(); + /* final pass, merge blurred layers according to final calculated coc */ + { + int medium_blurred_uniform, high_blurred_uniform, original_uniform, depth_uniform, dof_uniform; + int invrendertargetdim_uniform, viewvecs_uniform; + float invrendertargetdim[2] = {1.0f / fx->gbuffer_dim[0], 1.0f / fx->gbuffer_dim[1]}; + + medium_blurred_uniform = GPU_shader_get_uniform(dof_shader_pass5, "mblurredcolorbuffer"); + high_blurred_uniform = GPU_shader_get_uniform(dof_shader_pass5, "blurredcolorbuffer"); + dof_uniform = GPU_shader_get_uniform(dof_shader_pass5, "dof_params"); + invrendertargetdim_uniform = GPU_shader_get_uniform(dof_shader_pass5, "invrendertargetdim"); + original_uniform = GPU_shader_get_uniform(dof_shader_pass5, "colorbuffer"); + depth_uniform = GPU_shader_get_uniform(dof_shader_pass5, "depthbuffer"); + viewvecs_uniform = GPU_shader_get_uniform(dof_shader_pass5, "viewvecs"); + + GPU_shader_bind(dof_shader_pass5); + + GPU_shader_uniform_vector(dof_shader_pass5, dof_uniform, 4, 1, dof_params); + GPU_shader_uniform_vector(dof_shader_pass5, invrendertargetdim_uniform, 2, 1, invrendertargetdim); + GPU_shader_uniform_vector(dof_shader_pass5, viewvecs_uniform, 4, 3, viewvecs[0]); + + GPU_texture_bind(src, numslots++); + GPU_shader_uniform_texture(dof_shader_pass5, original_uniform, src); + + GPU_texture_bind(fx->dof_near_coc_blurred_buffer, numslots++); + GPU_shader_uniform_texture(dof_shader_pass5, high_blurred_uniform, fx->dof_near_coc_blurred_buffer); + + GPU_texture_bind(fx->dof_near_coc_buffer, numslots++); + GPU_shader_uniform_texture(dof_shader_pass5, medium_blurred_uniform, fx->dof_near_coc_buffer); + + GPU_texture_bind(fx->depth_buffer, numslots++); + GPU_texture_filter_mode(fx->depth_buffer, false, true); + GPU_shader_uniform_texture(dof_shader_pass5, depth_uniform, fx->depth_buffer); + + /* if this is the last pass, prepare for rendering on the frambuffer */ + gpu_fx_bind_render_target(&passes_left, fx, ofs, target); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + /* disable bindings */ + GPU_texture_unbind(fx->dof_near_coc_buffer); + GPU_texture_unbind(fx->dof_near_coc_blurred_buffer); + GPU_texture_unbind(src); + GPU_texture_filter_mode(fx->depth_buffer, true, false); + GPU_texture_unbind(fx->depth_buffer); + + /* may not be attached, in that case this just returns */ + if (target) { + GPU_framebuffer_texture_detach(target); + if (ofs) { + GPU_offscreen_bind(ofs, false); + } + else { + GPU_framebuffer_restore(); + } } - } - SWAP(GPUTexture *, target, src); - numslots = 0; + SWAP(GPUTexture *, target, src); + numslots = 0; + } } } glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); GPU_shader_unbind(); @@ -976,6 +1261,7 @@ void GPU_fx_compositor_init_dof_settings(GPUDOFSettings *fx_dof) fx_dof->focal_length = 1.0f; fx_dof->focus_distance = 1.0f; fx_dof->sensor = 1.0f; + fx_dof->num_blades = 6; } void GPU_fx_compositor_init_ssao_settings(GPUSSAOSettings *fx_ssao) diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.c index f16b1525622..b4ffc978d11 100644 --- a/source/blender/gpu/intern/gpu_extensions.c +++ b/source/blender/gpu/intern/gpu_extensions.c @@ -61,6 +61,7 @@ #endif #define MAX_DEFINE_LENGTH 72 +#define MAX_EXT_DEFINE_LENGTH 280 /* Extensions support */ @@ -83,6 +84,9 @@ extern char datatoc_gpu_shader_fx_vert_glsl[]; extern char datatoc_gpu_shader_fx_ssao_frag_glsl[]; extern char datatoc_gpu_shader_fx_dof_frag_glsl[]; extern char datatoc_gpu_shader_fx_dof_vert_glsl[]; +extern char datatoc_gpu_shader_fx_dof_hq_frag_glsl[]; +extern char datatoc_gpu_shader_fx_dof_hq_vert_glsl[]; +extern char datatoc_gpu_shader_fx_dof_hq_geo_glsl[]; extern char datatoc_gpu_shader_fx_depth_resolve_glsl[]; extern char datatoc_gpu_shader_fx_lib_glsl[]; @@ -278,6 +282,16 @@ bool GPU_bicubic_bump_support(void) return GLEW_ARB_texture_query_lod && GLEW_VERSION_3_0; } +bool GPU_geometry_shader_support(void) +{ + return GLEW_EXT_geometry_shader4 || GLEW_VERSION_3_2; +} + +bool GPU_instanced_drawing_support(void) +{ + return GLEW_ARB_draw_instanced; +} + int GPU_color_depth(void) { return GG.colordepth; @@ -736,14 +750,16 @@ GPUTexture *GPU_texture_create_vsm_shadow_map(int size, char err_out[256]) return tex; } -GPUTexture *GPU_texture_create_2D_procedural(int w, int h, const float *pixels, char err_out[256]) +GPUTexture *GPU_texture_create_2D_procedural(int w, int h, const float *pixels, bool repeat, char err_out[256]) { GPUTexture *tex = GPU_texture_create_nD(w, h, 2, pixels, 0, GPU_HDR_HALF_FLOAT, 2, err_out); if (tex) { /* Now we tweak some of the settings */ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + if (repeat) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -863,7 +879,7 @@ void GPU_texture_unbind(GPUTexture *tex) GPU_ASSERT_NO_GL_ERRORS("Post Texture Unbind"); } -void GPU_depth_texture_mode(GPUTexture *tex, bool compare, bool use_filter) +void GPU_texture_filter_mode(GPUTexture *tex, bool compare, bool use_filter) { GLenum arbnumber; @@ -872,11 +888,6 @@ void GPU_depth_texture_mode(GPUTexture *tex, bool compare, bool use_filter) return; } - if (!tex->depth) { - fprintf(stderr, "Not a depth texture."); - return; - } - if (tex->number == -1) return; @@ -884,10 +895,13 @@ void GPU_depth_texture_mode(GPUTexture *tex, bool compare, bool use_filter) arbnumber = (GLenum)((GLuint)GL_TEXTURE0_ARB + tex->number); if (tex->number != 0) glActiveTextureARB(arbnumber); - if (compare) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); - else - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); + + if (tex->depth) { + if (compare) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); + else + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } if (use_filter) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -1384,6 +1398,7 @@ struct GPUShader { GLhandleARB object; /* handle for full shader */ GLhandleARB vertex; /* handle for vertex shader */ GLhandleARB fragment; /* handle for fragment shader */ + GLhandleARB geometry; /* handle for geometry shader */ GLhandleARB lib; /* handle for libment shader */ int totattrib; /* total number of attributes */ int uniforms; /* required uniforms */ @@ -1430,13 +1445,19 @@ static const char *gpu_shader_version(void) } -static const char *gpu_shader_standard_extensions(void) +static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH]) { /* need this extensions for high quality bump mapping */ if (GPU_bicubic_bump_support()) - return "#extension GL_ARB_texture_query_lod: enable\n"; + strcat(defines, "#extension GL_ARB_texture_query_lod: enable\n"); - return ""; + if (GPU_geometry_shader_support()) + strcat(defines, "#extension GL_EXT_geometry_shader4: enable\n"); + + if (GPU_instanced_drawing_support()) { + strcat(defines, "#extension GL_EXT_gpu_shader4: enable\n"); + strcat(defines, "#extension GL_ARB_draw_instanced: enable\n"); + } } static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH]) @@ -1457,15 +1478,16 @@ static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH]) return; } -GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, const char *libcode, const char *defines) +GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, const char *geocode, const char *libcode, const char *defines, int input, int output, int number) { GLint status; GLcharARB log[5000]; GLsizei length = 0; GPUShader *shader; char standard_defines[MAX_DEFINE_LENGTH] = ""; + char standard_extensions[MAX_EXT_DEFINE_LENGTH] = ""; - if (!GLEW_ARB_vertex_shader || !GLEW_ARB_fragment_shader) + if (!GLEW_ARB_vertex_shader || !GLEW_ARB_fragment_shader || (geocode && !GPU_geometry_shader_support())) return NULL; shader = MEM_callocN(sizeof(GPUShader), "GPUShader"); @@ -1474,11 +1496,15 @@ GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, const shader->vertex = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); if (fragcode) shader->fragment = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); + if (geocode) + shader->geometry = glCreateShaderObjectARB(GL_GEOMETRY_SHADER_EXT); + shader->object = glCreateProgramObjectARB(); if (!shader->object || (vertexcode && !shader->vertex) || - (fragcode && !shader->fragment)) + (fragcode && !shader->fragment) || + (geocode && !shader->geometry)) { fprintf(stderr, "GPUShader, object creation failed.\n"); GPU_shader_free(shader); @@ -1486,6 +1512,7 @@ GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, const } gpu_shader_standard_defines(standard_defines); + gpu_shader_standard_extensions(standard_extensions); if (vertexcode) { const char *source[5]; @@ -1493,11 +1520,11 @@ GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, const int num_source = 0; source[num_source++] = gpu_shader_version(); - source[num_source++] = gpu_shader_standard_extensions(); + source[num_source++] = standard_extensions; source[num_source++] = standard_defines; if (defines) source[num_source++] = defines; - if (vertexcode) source[num_source++] = vertexcode; + source[num_source++] = vertexcode; glAttachObjectARB(shader->object, shader->vertex); glShaderSourceARB(shader->vertex, num_source, source, NULL); @@ -1519,12 +1546,12 @@ GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, const int num_source = 0; source[num_source++] = gpu_shader_version(); - source[num_source++] = gpu_shader_standard_extensions(); + source[num_source++] = standard_extensions; source[num_source++] = standard_defines; if (defines) source[num_source++] = defines; if (libcode) source[num_source++] = libcode; - if (fragcode) source[num_source++] = fragcode; + source[num_source++] = fragcode; glAttachObjectARB(shader->object, shader->fragment); glShaderSourceARB(shader->fragment, num_source, source, NULL); @@ -1541,6 +1568,35 @@ GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, const } } + if (geocode) { + const char *source[6]; + int num_source = 0; + + source[num_source++] = gpu_shader_version(); + source[num_source++] = standard_extensions; + source[num_source++] = standard_defines; + + if (defines) source[num_source++] = defines; + source[num_source++] = geocode; + + glAttachObjectARB(shader->object, shader->geometry); + glShaderSourceARB(shader->geometry, num_source, source, NULL); + + glCompileShaderARB(shader->geometry); + glGetObjectParameterivARB(shader->geometry, GL_OBJECT_COMPILE_STATUS_ARB, &status); + + if (!status) { + glGetInfoLogARB(shader->geometry, sizeof(log), &length, log); + shader_print_errors("compile", log, source, num_source); + + GPU_shader_free(shader); + return NULL; + } + + GPU_shader_geometry_stage_primitive_io(shader, input, output, number); + } + + #if 0 if (lib && lib->lib) glAttachObjectARB(shader->object, lib->lib); @@ -1553,6 +1609,7 @@ GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, const if (fragcode) shader_print_errors("linking", log, &fragcode, 1); else if (vertexcode) shader_print_errors("linking", log, &vertexcode, 1); else if (libcode) shader_print_errors("linking", log, &libcode, 1); + else if (geocode) shader_print_errors("linking", log, &geocode, 1); GPU_shader_free(shader); return NULL; @@ -1648,6 +1705,21 @@ void GPU_shader_uniform_vector(GPUShader *UNUSED(shader), int location, int leng GPU_ASSERT_NO_GL_ERRORS("Post Uniform Vector"); } +void GPU_shader_uniform_vector_int(GPUShader *UNUSED(shader), int location, int length, int arraysize, const int *value) +{ + if (location == -1) + return; + + GPU_ASSERT_NO_GL_ERRORS("Pre Uniform Vector"); + + if (length == 1) glUniform1ivARB(location, arraysize, value); + else if (length == 2) glUniform2ivARB(location, arraysize, value); + else if (length == 3) glUniform3ivARB(location, arraysize, value); + else if (length == 4) glUniform4ivARB(location, arraysize, value); + + GPU_ASSERT_NO_GL_ERRORS("Post Uniform Vector"); +} + void GPU_shader_uniform_int(GPUShader *UNUSED(shader), int location, int value) { if (location == -1) @@ -1656,6 +1728,13 @@ void GPU_shader_uniform_int(GPUShader *UNUSED(shader), int location, int value) GPU_CHECK_ERRORS_AROUND(glUniform1iARB(location, value)); } +void GPU_shader_geometry_stage_primitive_io(GPUShader *shader, int input, int output, int number) +{ + glProgramParameteriEXT(shader->object, GL_GEOMETRY_INPUT_TYPE_EXT, input); + glProgramParameteriEXT(shader->object, GL_GEOMETRY_OUTPUT_TYPE_EXT, output); + glProgramParameteriEXT(shader->object, GL_GEOMETRY_VERTICES_OUT_EXT, number); +} + void GPU_shader_uniform_texture(GPUShader *UNUSED(shader), int location, GPUTexture *tex) { GLenum arbnumber; @@ -1703,12 +1782,12 @@ GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader) switch (shader) { case GPU_SHADER_VSM_STORE: if (!GG.shaders.vsm_store) - GG.shaders.vsm_store = GPU_shader_create(datatoc_gpu_shader_vsm_store_vert_glsl, datatoc_gpu_shader_vsm_store_frag_glsl, NULL, NULL); + GG.shaders.vsm_store = GPU_shader_create(datatoc_gpu_shader_vsm_store_vert_glsl, datatoc_gpu_shader_vsm_store_frag_glsl, NULL, NULL, NULL, 0, 0, 0); retval = GG.shaders.vsm_store; break; case GPU_SHADER_SEP_GAUSSIAN_BLUR: if (!GG.shaders.sep_gaussian_blur) - GG.shaders.sep_gaussian_blur = GPU_shader_create(datatoc_gpu_shader_sep_gaussian_blur_vert_glsl, datatoc_gpu_shader_sep_gaussian_blur_frag_glsl, NULL, NULL); + GG.shaders.sep_gaussian_blur = GPU_shader_create(datatoc_gpu_shader_sep_gaussian_blur_vert_glsl, datatoc_gpu_shader_sep_gaussian_blur_frag_glsl, NULL, NULL, NULL, 0, 0, 0); retval = GG.shaders.sep_gaussian_blur; break; } @@ -1737,38 +1816,57 @@ GPUShader *GPU_shader_get_builtin_fx_shader(int effects, bool persp) } if (!GG.shaders.fx_shaders[offset]) { + GPUShader *shader; + switch(effects) { case GPU_SHADER_FX_SSAO: - GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_vert_glsl, datatoc_gpu_shader_fx_ssao_frag_glsl, datatoc_gpu_shader_fx_lib_glsl, defines); + GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_vert_glsl, datatoc_gpu_shader_fx_ssao_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines, 0, 0, 0); break; case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_ONE: strcat(defines, "#define FIRST_PASS\n"); - GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, datatoc_gpu_shader_fx_lib_glsl, defines); + GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines, 0, 0, 0); break; case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_TWO: strcat(defines, "#define SECOND_PASS\n"); - GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, datatoc_gpu_shader_fx_lib_glsl, defines); + GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines, 0, 0, 0); break; case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_THREE: strcat(defines, "#define THIRD_PASS\n"); - GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, datatoc_gpu_shader_fx_lib_glsl, defines); + GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines, 0, 0, 0); break; case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FOUR: strcat(defines, "#define FOURTH_PASS\n"); - GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, datatoc_gpu_shader_fx_lib_glsl, defines); + GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines, 0, 0, 0); break; case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FIVE: strcat(defines, "#define FIFTH_PASS\n"); - GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, datatoc_gpu_shader_fx_lib_glsl, defines); + GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines, 0, 0, 0); + break; + + case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_ONE: + strcat(defines, "#define FIRST_PASS\n"); + GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_dof_hq_vert_glsl, datatoc_gpu_shader_fx_dof_hq_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines, 0, 0, 0); + break; + + case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_TWO: + strcat(defines, "#define SECOND_PASS\n"); + shader = GPU_shader_create(datatoc_gpu_shader_fx_dof_hq_vert_glsl, datatoc_gpu_shader_fx_dof_hq_frag_glsl, datatoc_gpu_shader_fx_dof_hq_geo_glsl, datatoc_gpu_shader_fx_lib_glsl, + defines, GL_POINTS, GL_TRIANGLE_STRIP, 4); + GG.shaders.fx_shaders[offset] = shader; + break; + + case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_THREE: + strcat(defines, "#define THIRD_PASS\n"); + GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_dof_hq_vert_glsl, datatoc_gpu_shader_fx_dof_hq_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines, 0, 0, 0); break; case GPU_SHADER_FX_DEPTH_RESOLVE: - GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_vert_glsl, datatoc_gpu_shader_fx_depth_resolve_glsl, NULL, defines); + GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_vert_glsl, datatoc_gpu_shader_fx_depth_resolve_glsl, NULL, NULL, defines, 0, 0, 0); } } diff --git a/source/blender/gpu/intern/gpu_simple_shader.c b/source/blender/gpu/intern/gpu_simple_shader.c index 3fa5975ef15..b439a37f3c3 100644 --- a/source/blender/gpu/intern/gpu_simple_shader.c +++ b/source/blender/gpu/intern/gpu_simple_shader.c @@ -151,7 +151,8 @@ static GPUShader *gpu_simple_shader(int options) shader = GPU_shader_create( datatoc_gpu_shader_simple_vert_glsl, datatoc_gpu_shader_simple_frag_glsl, - NULL, defines); + NULL, + NULL, defines, 0, 0, 0); if (shader) { /* set texture map to first texture unit */ diff --git a/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_frag.glsl b/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_frag.glsl new file mode 100644 index 00000000000..8c2aff45679 --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_frag.glsl @@ -0,0 +1,166 @@ +/* amount of offset to move one pixel left-right. + * In second pass some dimensions are zero to control verical/horizontal convolution */ +uniform vec2 invrendertargetdim; + +uniform ivec2 rendertargetdim; + +/* color buffer */ +uniform sampler2D colorbuffer; +uniform sampler2D farbuffer; +uniform sampler2D nearbuffer; + +/* depth buffer */ +uniform sampler2D depthbuffer; + +uniform sampler2D cocbuffer; + +/* this includes focal distance in x and aperture size in y */ +uniform vec4 dof_params; + +/* viewvectors for reconstruction of world space */ +uniform vec4 viewvecs[3]; + +/* initial uv coordinate */ +varying vec2 uvcoord; + +/* coordinate used for calculating radius et al set in geometry shader */ +varying vec2 particlecoord; +varying vec4 color; + +/* downsampling coordinates */ +varying vec2 downsample1; +varying vec2 downsample2; +varying vec2 downsample3; +varying vec2 downsample4; + +#define M_PI 3.1415926535897932384626433832795 + +/* calculate 4 samples at once */ +vec4 calculate_coc(in vec4 zdepth) +{ + vec4 coc = dof_params.x * (vec4(dof_params.y) / zdepth - vec4(1.0)); + + /* multiply by 1.0 / sensor size to get the normalized size */ + return coc * dof_params.z; +} + +#define THRESHOLD 0.0 + +/* downsample the color buffer to half resolution */ +void downsample_pass() +{ + vec4 depth; + vec4 zdepth; + vec4 coc; + float far_coc, near_coc; + + /* custom downsampling. We need to be careful to sample nearest here to avoid leaks */ + vec4 color1 = texture2D(colorbuffer, downsample1); + vec4 color2 = texture2D(colorbuffer, downsample2); + vec4 color3 = texture2D(colorbuffer, downsample3); + vec4 color4 = texture2D(colorbuffer, downsample4); + + depth.r = texture2D(depthbuffer, downsample1).r; + depth.g = texture2D(depthbuffer, downsample2).r; + depth.b = texture2D(depthbuffer, downsample3).r; + depth.a = texture2D(depthbuffer, downsample4).r; + + zdepth = get_view_space_z_from_depth(vec4(viewvecs[0].z), vec4(viewvecs[1].z), depth); + coc = calculate_coc(zdepth); + vec4 coc_far = -coc; + + /* now we need to write the near-far fields premultiplied by the coc */ + vec4 near_weights = vec4((coc.x >= THRESHOLD) ? 1.0 : 0.0, (coc.y >= THRESHOLD) ? 1.0 : 0.0, + (coc.z >= THRESHOLD) ? 1.0 : 0.0, (coc.w >= THRESHOLD) ? 1.0 : 0.0); + vec4 far_weights = vec4((coc_far.x >= THRESHOLD) ? 1.0 : 0.0, (coc_far.y >= THRESHOLD) ? 1.0 : 0.0, + (coc_far.z >= THRESHOLD) ? 1.0 : 0.0, (coc_far.w >= THRESHOLD) ? 1.0 : 0.0); + + near_coc = max(max(max(coc.x, coc.y), max(coc.z, coc.w)), 0.0); + far_coc = max(max(max(coc_far.x, coc_far.y), max(coc_far.z, coc_far.w)), 0.0); + + float norm_near = dot(near_weights, vec4(1.0)); + float norm_far = dot(far_weights, vec4(1.0)); + + /* now write output to weighted buffers. */ + gl_FragData[0] = color1 * near_weights.x + color2 * near_weights.y + color3 * near_weights.z + + color4 * near_weights.w; + gl_FragData[1] = color1 * far_weights.x + color2 * far_weights.y + color3 * far_weights.z + + color4 * far_weights.w; + + if (norm_near > 0.0) + gl_FragData[0] /= norm_near; + if (norm_far > 0.0) + gl_FragData[1] /= norm_far; + gl_FragData[2] = vec4(near_coc, far_coc, 0.0, 1.0); +} + +/* accumulate color in the near/far blur buffers */ +void accumulate_pass(void) { + float theta = atan(particlecoord.y, particlecoord.x); + float r; + + if (dof_params.w == 0.0) + r = 1.0; + else + r = cos(M_PI / dof_params.w) / (cos(theta - (2.0 * M_PI / dof_params.w) * floor((dof_params.w * theta + M_PI) / (2.0 * M_PI)))); + + if (dot(particlecoord, particlecoord) > r * r) + discard; + + gl_FragColor = color; +} +#define MERGE_THRESHOLD 4.0 + +/* combine the passes, */ +void final_pass(void) { + vec4 finalcolor; + float totalweight; + float depth = texture2D(depthbuffer, uvcoord).r; + + vec4 zdepth = get_view_space_z_from_depth(vec4(viewvecs[0].z), vec4(viewvecs[1].z), vec4(depth)); + float coc_near = calculate_coc(zdepth).r; + float coc_far = max(-coc_near, 0.0); + coc_near = max(coc_near, 0.0); + + vec4 farcolor = texture2D(farbuffer, uvcoord); + float farweight = farcolor.a; + if (farweight > 0.0) + farcolor /= farweight; + vec4 nearcolor = texture2D(nearbuffer, uvcoord); + + vec4 srccolor = texture2D(colorbuffer, uvcoord); + + vec4 coc = texture2D(cocbuffer, uvcoord); + + float mixfac = smoothstep(1.0, MERGE_THRESHOLD, coc_far); + finalcolor = mix(srccolor, farcolor, mixfac); + + farweight = mix(1.0, farweight, mixfac); + + float nearweight = nearcolor.a; + if (nearweight > 0.0) { + nearcolor /= nearweight; + } + + if (coc_near > 1.0) { + nearweight = 1.0; + finalcolor = nearcolor; + } + else { + totalweight = nearweight + farweight; + finalcolor = mix(finalcolor, nearcolor, nearweight / totalweight); + } + + gl_FragColor = finalcolor; +} + +void main() +{ +#ifdef FIRST_PASS + downsample_pass(); +#elif defined(SECOND_PASS) + accumulate_pass(); +#elif defined(THIRD_PASS) + final_pass(); +#endif +} diff --git a/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_geo.glsl b/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_geo.glsl new file mode 100644 index 00000000000..9f365a0d671 --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_geo.glsl @@ -0,0 +1,50 @@ +uniform ivec2 rendertargetdim; +uniform sampler2D colorbuffer; + +uniform vec2 layerselection; + +uniform sampler2D cocbuffer; + +/* initial uv coordinate */ +varying in vec2 uvcoord[]; +varying out vec2 particlecoord; +varying out vec4 color; + + +#define M_PI 3.1415926535897932384626433832795 + +void main(void) +{ + vec4 coc = texture2DLod(cocbuffer, uvcoord[0], 0.0); + + float offset_val = dot(coc.rg, layerselection); + if (offset_val < 1.0) + return; + + vec4 colortex = texture2DLod(colorbuffer, uvcoord[0], 0.0); + + /* find the area the pixel will cover and divide the color by it */ + float alpha = 1.0 / (offset_val * offset_val * M_PI); + colortex *= alpha; + colortex.a = alpha; + + vec2 offset_far = vec2(offset_val * 0.5) / vec2(rendertargetdim.x, rendertargetdim.y); + + gl_Position = gl_PositionIn[0] + vec4(-offset_far.x, -offset_far.y, 0.0, 0.0); + color = colortex; + particlecoord = vec2(-1.0, -1.0); + EmitVertex(); + gl_Position = gl_PositionIn[0] + vec4(-offset_far.x, offset_far.y, 0.0, 0.0); + particlecoord = vec2(-1.0, 1.0); + color = colortex; + EmitVertex(); + gl_Position = gl_PositionIn[0] + vec4(offset_far.x, -offset_far.y, 0.0, 0.0); + particlecoord = vec2(1.0, -1.0); + color = colortex; + EmitVertex(); + gl_Position = gl_PositionIn[0] + vec4(offset_far.x, offset_far.y, 0.0, 0.0); + particlecoord = vec2(1.0, 1.0); + color = colortex; + EmitVertex(); + EndPrimitive(); +} diff --git a/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_vert.glsl b/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_vert.glsl new file mode 100644 index 00000000000..09a0c75facc --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_vert.glsl @@ -0,0 +1,58 @@ +uniform vec2 invrendertargetdim; +uniform ivec2 rendertargetdim; +/* initial uv coordinate */ +varying vec2 uvcoord; + +/* coordinate used for calculating radius et al set in geometry shader */ +varying vec2 particlecoord; + +/* downsampling coordinates */ +varying vec2 downsample1; +varying vec2 downsample2; +varying vec2 downsample3; +varying vec2 downsample4; + +void vert_dof_downsample() +{ + /* gather pixels from neighbors. half dimensions means we offset half a pixel to + * get this right though it's possible we may lose a pixel at some point */ + downsample1 = gl_MultiTexCoord0.xy + vec2(-0.5, -0.5) * invrendertargetdim; + downsample2 = gl_MultiTexCoord0.xy + vec2(-0.5, 0.5) * invrendertargetdim; + downsample3 = gl_MultiTexCoord0.xy + vec2(0.5, 0.5) * invrendertargetdim; + downsample4 = gl_MultiTexCoord0.xy + vec2(0.5, -0.5) * invrendertargetdim; + + gl_Position = gl_Vertex; +} + +/* geometry shading pass, calculate a texture coordinate based on the indexed id */ +void vert_dof_coc_scatter_pass() +{ + vec2 pixel = vec2(rendertargetdim.x, rendertargetdim.y); + /* some math to get the target pixel */ + int row = gl_InstanceID / rendertargetdim.x; + int column = gl_InstanceID % rendertargetdim.x; + uvcoord = (vec2(column, row) + vec2(0.5)) / pixel; + + vec2 pos = uvcoord * 2.0 - vec2(1.0); + gl_Position = vec4(pos.x, pos.y, 0.0, 1.0); + +// uvcoord = vec2(0.5, 0.5); +// gl_Position = vec4(0.0, 0.0, 0.0, 1.0); +} + +void vert_dof_final() +{ + uvcoord = gl_MultiTexCoord0.xy; + gl_Position = gl_Vertex; +} + +void main() +{ +#if defined(FIRST_PASS) + vert_dof_downsample(); +#elif defined(SECOND_PASS) + vert_dof_coc_scatter_pass(); +#else + vert_dof_final(); +#endif +}
\ No newline at end of file diff --git a/source/blender/imbuf/intern/bmp.c b/source/blender/imbuf/intern/bmp.c index 19e655a0b3d..298e2da965f 100644 --- a/source/blender/imbuf/intern/bmp.c +++ b/source/blender/imbuf/intern/bmp.c @@ -136,7 +136,7 @@ struct ImBuf *imb_bmp_decode(unsigned char *mem, size_t size, int flags, char co colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); - bmp = mem + LITTLE_LONG(*(int*)(mem + 10)); + bmp = mem + LITTLE_LONG(*(int *)(mem + 10)); if (CHECK_HEADER_FIELD_BMP(mem)) { /* skip fileheader */ diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 7b331181a58..761e76eb249 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -375,7 +375,9 @@ typedef enum ePose_Flags { /* set by BKE_pose_rebuild to give a chance to the IK solver to rebuild IK tree */ POSE_WAS_REBUILT = (1 << 5), /* set by game_copy_pose to indicate that this pose is used in the game engine */ - POSE_GAME_ENGINE = (1 << 6) + POSE_GAME_ENGINE = (1 << 6), + /* pose constraint flags needs to be updated */ + POSE_CONSTRAINTS_NEED_UPDATE_FLAGS = (1 << 7), } ePose_Flags; /* IK Solvers ------------------------------------ */ diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 89c8316002b..dd66acf2781 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -151,7 +151,6 @@ typedef struct Palette /* pointer to individual colours */ ListBase colors; - ListBase deleted; int active_color; int pad; diff --git a/source/blender/makesdna/DNA_gpu_types.h b/source/blender/makesdna/DNA_gpu_types.h index b6009b3f899..967cb7284dc 100644 --- a/source/blender/makesdna/DNA_gpu_types.h +++ b/source/blender/makesdna/DNA_gpu_types.h @@ -38,6 +38,8 @@ typedef struct GPUDOFSettings { float fstop; float focal_length; float sensor; + int num_blades; + int high_quality; } GPUDOFSettings; /* properties for SSAO effect */ diff --git a/source/blender/makesdna/intern/CMakeLists.txt b/source/blender/makesdna/intern/CMakeLists.txt index 317141b14e3..08d2f93866a 100644 --- a/source/blender/makesdna/intern/CMakeLists.txt +++ b/source/blender/makesdna/intern/CMakeLists.txt @@ -97,6 +97,7 @@ set(SRC ../../blenlib/intern/BLI_mempool.c ../../blenlib/intern/listbase.c ../../blenlib/intern/BLI_ghash.c + ../../blenlib/intern/hash_mm2a.c ) blender_add_lib(bf_dna_blenlib "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 88b2946e3a1..8c93438545e 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -936,7 +936,7 @@ static char *rna_def_property_set_func(FILE *f, StructRNA *srna, PropertyRNA *pr if (dp->dnaarraylength == 1) { if (prop->type == PROP_BOOLEAN && dp->booleanbit) { - fprintf(f, " if (%svalues[i]) data->%s |= (%d<<i);\n", + fprintf(f, " if (%svalues[i]) data->%s |= (%du << i);\n", (dp->booleannegative) ? "!" : "", dp->dnaname, dp->booleanbit); fprintf(f, " else data->%s &= ~(%du << i);\n", dp->dnaname, dp->booleanbit); } diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index 421d7b28dda..93e5ceaa229 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -247,8 +247,8 @@ static void rna_bone_layer_set(int *layer, const int *values) return; for (i = 0; i < 32; i++) { - if (values[i]) *layer |= (1 << i); - else *layer &= ~(1 << i); + if (values[i]) *layer |= (1u << i); + else *layer &= ~(1u << i); } } @@ -272,8 +272,8 @@ static void rna_Armature_layer_set(PointerRNA *ptr, const int *values) return; for (i = 0; i < 32; i++) { - if (values[i]) arm->layer |= (1 << i); - else arm->layer &= ~(1 << i); + if (values[i]) arm->layer |= (1u << i); + else arm->layer &= ~(1u << i); } } diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index aaacf07567e..0f4830a8a7e 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -271,12 +271,12 @@ static char *rna_Constraint_path(PointerRNA *ptr) static void rna_Constraint_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { - ED_object_constraint_update(ptr->id.data); + ED_object_constraint_tag_update(ptr->id.data, ptr->data); } static void rna_Constraint_dependency_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { - ED_object_constraint_dependency_update(bmain, ptr->id.data); + ED_object_constraint_dependency_tag_update(bmain, ptr->id.data, ptr->data); } static void rna_Constraint_influence_update(Main *bmain, Scene *scene, PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_controller.c b/source/blender/makesrna/intern/rna_controller.c index ba0214d36ec..605e28653f9 100644 --- a/source/blender/makesrna/intern/rna_controller.c +++ b/source/blender/makesrna/intern/rna_controller.c @@ -120,7 +120,7 @@ static int rna_Controller_state_number_get(struct PointerRNA *ptr) int bit; for (bit = 0; bit < 32; bit++) { - if (cont->state_mask & (1 << bit)) + if (cont->state_mask & (1u << bit)) return bit + 1; } return 0; diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index abf29ef82a3..48a5f09fda0 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -69,47 +69,6 @@ static void rna_Mesh_create_normals_split(Mesh *mesh) } } -static void rna_Mesh_calc_normals_split(Mesh *mesh) -{ - float (*r_loopnors)[3]; - float (*polynors)[3]; - short (*clnors)[2] = NULL; - bool free_polynors = false; - - if (CustomData_has_layer(&mesh->ldata, CD_NORMAL)) { - r_loopnors = CustomData_get_layer(&mesh->ldata, CD_NORMAL); - memset(r_loopnors, 0, sizeof(float[3]) * mesh->totloop); - } - else { - r_loopnors = CustomData_add_layer(&mesh->ldata, CD_NORMAL, CD_CALLOC, NULL, mesh->totloop); - CustomData_set_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY); - } - - /* may be NULL */ - clnors = CustomData_get_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); - - if (CustomData_has_layer(&mesh->pdata, CD_NORMAL)) { - /* This assume that layer is always up to date, not sure this is the case (esp. in Edit mode?)... */ - polynors = CustomData_get_layer(&mesh->pdata, CD_NORMAL); - free_polynors = false; - } - else { - polynors = MEM_mallocN(sizeof(float[3]) * mesh->totpoly, __func__); - BKE_mesh_calc_normals_poly(mesh->mvert, mesh->totvert, mesh->mloop, mesh->mpoly, mesh->totloop, mesh->totpoly, - polynors, false); - free_polynors = true; - } - - BKE_mesh_normals_loop_split( - mesh->mvert, mesh->totvert, mesh->medge, mesh->totedge, - mesh->mloop, r_loopnors, mesh->totloop, mesh->mpoly, (const float (*)[3])polynors, mesh->totpoly, - (mesh->flag & ME_AUTOSMOOTH) != 0, mesh->smoothresh, NULL, clnors, NULL); - - if (free_polynors) { - MEM_freeN(polynors); - } -} - static void rna_Mesh_free_normals_split(Mesh *mesh) { CustomData_free_layers(&mesh->ldata, CD_NORMAL, mesh->totloop); @@ -130,7 +89,7 @@ static void rna_Mesh_calc_tangents(Mesh *mesh, ReportList *reports, const char * /* Compute loop normals if needed. */ if (!CustomData_has_layer(&mesh->ldata, CD_NORMAL)) { - rna_Mesh_calc_normals_split(mesh); + BKE_mesh_calc_normals_split(mesh); } BKE_mesh_loop_tangents(mesh, uvmap, r_looptangents, reports); @@ -256,7 +215,7 @@ void RNA_api_mesh(StructRNA *srna) func = RNA_def_function(srna, "create_normals_split", "rna_Mesh_create_normals_split"); RNA_def_function_ui_description(func, "Empty split vertex normals"); - func = RNA_def_function(srna, "calc_normals_split", "rna_Mesh_calc_normals_split"); + func = RNA_def_function(srna, "calc_normals_split", "BKE_mesh_calc_normals_split"); RNA_def_function_ui_description(func, "Calculate split vertex normals, which preserve sharp edges"); func = RNA_def_function(srna, "free_normals_split", "rna_Mesh_free_normals_split"); diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index b0fde4849a9..aebe673bf5c 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -131,7 +131,7 @@ static void rna_Object_calc_matrix_camera( static void rna_Object_camera_fit_coords( Object *ob, Scene *scene, int num_cos, float *cos, float co_ret[3], float *scale_ret) { - BKE_camera_view_frame_fit_to_coords(scene, (float (*)[3])cos, num_cos / 3, ob, co_ret, scale_ret); + BKE_camera_view_frame_fit_to_coords(scene, (const float (*)[3])cos, num_cos / 3, ob, co_ret, scale_ret); } /* copied from Mesh_getFromObject and adapted to RNA interface */ diff --git a/source/blender/makesrna/intern/rna_palette.c b/source/blender/makesrna/intern/rna_palette.c index 7405df967a0..8cbb57fde2c 100644 --- a/source/blender/makesrna/intern/rna_palette.c +++ b/source/blender/makesrna/intern/rna_palette.c @@ -55,7 +55,7 @@ static void rna_Palette_color_remove(Palette *palette, ReportList *reports, Poin return; } - BKE_palette_color_remove_ex(palette, color, true); + BKE_palette_color_remove(palette, color); RNA_POINTER_INVALIDATE(color_ptr); } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 629fbbf66e9..2e941487e98 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -1745,6 +1745,19 @@ static void rna_GPUFXSettings_fx_update(Main *UNUSED(bmain), Scene *UNUSED(scene BKE_screen_gpu_fx_validate(fx_settings); } +static void rna_GPUDOFSettings_blades_set(PointerRNA *ptr, const int value) +{ + GPUDOFSettings *dofsettings = (GPUDOFSettings *)ptr->data; + + if (value < 3 && dofsettings->num_blades > 2) + dofsettings->num_blades = 0; + else if (value > 0 && dofsettings->num_blades == 0) + dofsettings->num_blades = 3; + else + dofsettings->num_blades = value; +} + + #else static void rna_def_transform_orientation(BlenderRNA *brna) @@ -3908,6 +3921,18 @@ static void rna_def_gpu_dof_fx(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_range(prop, 0.1f, 128.0f, 10, 1); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "blades", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "num_blades"); + RNA_def_property_ui_text(prop, "Viewport Camera Blades", "Blades for dof effect"); + RNA_def_property_range(prop, 0, 16); + RNA_def_property_int_funcs(prop, NULL, "rna_GPUDOFSettings_blades_set", NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "use_high_quality", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "high_quality", 1); + RNA_def_property_ui_text(prop, "High Quality", "Use high quality depth of field"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); } static void rna_def_gpu_ssao_fx(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 9e7d70ab3b2..db4b097b9c3 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -413,8 +413,8 @@ static void rna_SpaceView3D_lock_camera_and_layers_set(PointerRNA *ptr, int valu /* seek for layact */ bit = 0; while (bit < 32) { - if (v3d->lay & (1 << bit)) { - v3d->layact = 1 << bit; + if (v3d->lay & (1u << bit)) { + v3d->layact = (1u << bit); break; } bit++; diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index 8d53cc6219f..608b1149dda 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -59,7 +59,7 @@ #include "BLI_threads.h" #endif -void modifier_init_texture(Scene *scene, Tex *tex) +void modifier_init_texture(const Scene *scene, Tex *tex) { if (!tex) return; diff --git a/source/blender/modifiers/intern/MOD_util.h b/source/blender/modifiers/intern/MOD_util.h index cb851a51c64..adb1cf31bbd 100644 --- a/source/blender/modifiers/intern/MOD_util.h +++ b/source/blender/modifiers/intern/MOD_util.h @@ -40,7 +40,7 @@ struct Scene; struct Tex; struct TexResult; -void modifier_init_texture(struct Scene *scene, struct Tex *texture); +void modifier_init_texture(const struct Scene *scene, struct Tex *texture); void get_texture_coords(struct MappingInfoModifierData *dmd, struct Object *ob, struct DerivedMesh *dm, float (*co)[3], float (*texco)[3], int numVerts); void modifier_vgroup_cache(struct ModifierData *md, float (*vertexCos)[3]); diff --git a/source/blender/render/extern/include/RE_engine.h b/source/blender/render/extern/include/RE_engine.h index 5edf970c129..bc4c4c54f17 100644 --- a/source/blender/render/extern/include/RE_engine.h +++ b/source/blender/render/extern/include/RE_engine.h @@ -159,7 +159,7 @@ void RE_engines_exit(void); RenderEngineType *RE_engines_find(const char *idname); -void RE_engine_get_current_tiles(struct Render *re, int *total_tiles_r, rcti **tiles_r); +rcti* RE_engine_get_current_tiles(struct Render *re, int *total_tiles_r, bool *needs_free_r); struct RenderData *RE_engine_get_render_data(struct Render *re); void RE_bake_engine_set_engine_parameters(struct Render *re, struct Main *bmain, struct Scene *scene); diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h index 8d92fb9eec9..a53ff302475 100644 --- a/source/blender/render/intern/include/render_types.h +++ b/source/blender/render/intern/include/render_types.h @@ -194,6 +194,7 @@ struct Render struct Object *camera_override; unsigned int lay, layer_override; + ThreadRWMutex partsmutex; ListBase parts; /* render engine */ diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c index 4e63a9918e1..653f3f0786a 100644 --- a/source/blender/render/intern/source/external_engine.c +++ b/source/blender/render/intern/source/external_engine.c @@ -368,28 +368,38 @@ void RE_engine_set_error_message(RenderEngine *engine, const char *msg) } } -void RE_engine_get_current_tiles(Render *re, int *total_tiles_r, rcti **tiles_r) +rcti* RE_engine_get_current_tiles(Render *re, int *total_tiles_r, bool *r_needs_free) { + static rcti tiles_static[BLENDER_MAX_THREADS]; + const int allocation_step = BLENDER_MAX_THREADS; RenderPart *pa; int total_tiles = 0; - rcti *tiles = NULL; - int allocation_size = 0, allocation_step = BLENDER_MAX_THREADS; + rcti *tiles = tiles_static; + int allocation_size = BLENDER_MAX_THREADS; + + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_READ); + + *r_needs_free = false; if (re->engine && (re->engine->flag & RE_ENGINE_HIGHLIGHT_TILES) == 0) { *total_tiles_r = 0; - *tiles_r = NULL; - return; + BLI_rw_mutex_unlock(&re->partsmutex); + return NULL; } for (pa = re->parts.first; pa; pa = pa->next) { if (pa->status == PART_STATUS_IN_PROGRESS) { if (total_tiles >= allocation_size) { - if (tiles == NULL) + /* Just in case we're using crazy network rendering with more + * slaves as BLENDER_MAX_THREADS. + */ + if (tiles == tiles_static) tiles = MEM_mallocN(allocation_step * sizeof(rcti), "current engine tiles"); else tiles = MEM_reallocN(tiles, (total_tiles + allocation_step) * sizeof(rcti)); allocation_size += allocation_step; + *r_needs_free = true; } tiles[total_tiles] = pa->disprect; @@ -404,9 +414,9 @@ void RE_engine_get_current_tiles(Render *re, int *total_tiles_r, rcti **tiles_r) total_tiles++; } } - + BLI_rw_mutex_unlock(&re->partsmutex); *total_tiles_r = total_tiles; - *tiles_r = tiles; + return tiles; } RenderData *RE_engine_get_render_data(Render *re) @@ -478,6 +488,8 @@ bool RE_bake_engine( engine->tile_y = 0; engine->flag &= ~RE_ENGINE_RENDERING; + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); + /* re->engine becomes zero if user changed active render engine during render */ if (!persistent_data || !re->engine) { RE_engine_free(engine); @@ -485,6 +497,7 @@ bool RE_bake_engine( } RE_parts_free(re); + BLI_rw_mutex_unlock(&re->partsmutex); if (BKE_reports_contain(re->reports, RPT_ERROR)) G.is_break = true; @@ -663,6 +676,8 @@ int RE_engine_render(Render *re, int do_all) render_result_free_list(&engine->fullresult, engine->fullresult.first); + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); + /* re->engine becomes zero if user changed active render engine during render */ if (!persistent_data || !re->engine) { RE_engine_free(engine); @@ -682,6 +697,7 @@ int RE_engine_render(Render *re, int do_all) } RE_parts_free(re); + BLI_rw_mutex_unlock(&re->partsmutex); if (BKE_reports_contain(re->reports, RPT_ERROR)) G.is_break = true; diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 21ca7924fa0..b397db7c31b 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -385,6 +385,7 @@ Render *RE_NewRender(const char *name) BLI_addtail(&RenderGlobal.renderlist, re); BLI_strncpy(re->name, name, RE_MAXNAME); BLI_rw_mutex_init(&re->resultmutex); + BLI_rw_mutex_init(&re->partsmutex); re->eval_ctx = MEM_callocN(sizeof(EvaluationContext), "re->eval_ctx"); re->eval_ctx->mode = DAG_EVAL_RENDER; } @@ -423,6 +424,7 @@ void RE_FreeRender(Render *re) RE_engine_free(re->engine); BLI_rw_mutex_end(&re->resultmutex); + BLI_rw_mutex_end(&re->partsmutex); BLI_freelistN(&re->r.layers); @@ -1268,8 +1270,9 @@ static void threaded_tile_processor(Render *re) /* unset threadsafety */ g_break = 0; - + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); RE_parts_free(re); + BLI_rw_mutex_unlock(&re->partsmutex); re->viewplane = viewplane; /* restore viewplane, modified by pano render */ } diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 9778f1651de..32a49b6e160 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -199,6 +199,7 @@ void WM_init(bContext *C, int argc, const char **argv) GPU_init(); GPU_set_mipmap(!(U.gameflags & USER_DISABLE_MIPMAP)); + GPU_set_linear_mipmap(true); GPU_set_anisotropic(U.anisotropic_filter); GPU_set_gpu_mipmapping(U.use_gpu_mipmap); diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index 4bb6a14eefe..bbacf8745e3 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -476,7 +476,9 @@ int ED_mesh_uv_texture_add(struct Mesh *me, const char *name, const bool active_ bool ED_mesh_color_remove_named(struct Mesh *me, const char *name) RET_ZERO bool ED_mesh_uv_texture_remove_named(struct Mesh *me, const char *name) RET_ZERO void ED_object_constraint_dependency_update(struct Main *bmain, struct Object *ob) RET_NONE +void ED_object_constraint_dependency_tag_update(struct Main *bmain, struct Object *ob, struct bConstraint *con) RET_NONE void ED_object_constraint_update(struct Object *ob) RET_NONE +void ED_object_constraint_tag_update(struct Object *ob, struct bConstraint *con) RET_NONE void ED_vgroup_vert_add(struct Object *ob, struct bDeformGroup *dg, int vertnum, float weight, int assignmode) RET_NONE void ED_vgroup_vert_remove(struct Object *ob, struct bDeformGroup *dg, int vertnum) RET_NONE float ED_vgroup_vert_weight(struct Object *ob, struct bDeformGroup *dg, int vertnum) RET_ZERO diff --git a/source/creator/creator.c b/source/creator/creator.c index d88beb3c8bb..23c200c9d50 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -1187,19 +1187,24 @@ static int set_skip_frame(int argc, const char **argv, void *data) #define BPY_CTX_SETUP(_cmd) \ { \ wmWindowManager *wm = CTX_wm_manager(C); \ - wmWindow *prevwin = CTX_wm_window(C); \ - Scene *prevscene = CTX_data_scene(C); \ - if (wm->windows.first) { \ + Scene *scene_prev = CTX_data_scene(C); \ + wmWindow *win_prev; \ + const bool has_win = !BLI_listbase_is_empty(&wm->windows); \ + if (has_win) { \ + win_prev = CTX_wm_window(C); \ CTX_wm_window_set(C, wm->windows.first); \ - _cmd; \ - CTX_wm_window_set(C, prevwin); \ } \ else { \ fprintf(stderr, "Python script \"%s\" " \ "running with missing context data.\n", argv[1]); \ + } \ + { \ _cmd; \ } \ - CTX_data_scene_set(C, prevscene); \ + if (has_win) { \ + CTX_wm_window_set(C, win_prev); \ + } \ + CTX_data_scene_set(C, scene_prev); \ } (void)0 \ #endif /* WITH_PYTHON */ diff --git a/source/gameengine/Ketsji/KX_SteeringActuator.cpp b/source/gameengine/Ketsji/KX_SteeringActuator.cpp index f859b1ceae9..83597f9125a 100644 --- a/source/gameengine/Ketsji/KX_SteeringActuator.cpp +++ b/source/gameengine/Ketsji/KX_SteeringActuator.cpp @@ -263,12 +263,12 @@ bool KX_SteeringActuator::Update(double curtime, bool frame) if (apply_steerforce) { - MT_Vector3 newvel; bool isdyna = obj->IsDynamic(); if (isdyna) m_steerVec.z() = 0; if (!m_steerVec.fuzzyZero()) m_steerVec.normalize(); + MT_Vector3 newvel = m_velocity * m_steerVec; //adjust velocity to avoid obstacles if (m_simulation && m_obstacle /*&& !newvel.fuzzyZero()*/) @@ -281,16 +281,13 @@ bool KX_SteeringActuator::Update(double curtime, bool frame) KX_RasterizerDrawDebugLine(mypos, mypos + newvel, MT_Vector3(0.0, 1.0, 0.0)); } - HandleActorFace(m_steerVec); + HandleActorFace(newvel); if (isdyna) { - //TODO: Take into account angular velocity on turns + //temporary solution: set 2D steering velocity directly to obj + //correct way is to apply physical force MT_Vector3 curvel = obj->GetLinearVelocity(); - newvel = (curvel.length() * m_steerVec) + (m_acceleration * delta) * m_steerVec; - if (newvel.length2() >= (m_velocity * m_velocity)) - newvel = m_velocity * m_steerVec; - if (m_lockzvel) newvel.z() = 0.0f; else diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp index c0aaefe9e37..a30ee0fe34f 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp +++ b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp @@ -2644,9 +2644,6 @@ int CcdPhysicsEnvironment::CreateConstraint(class PHY_IPhysicsController* ctrl if (!rb0) return 0; - // If either of the controllers is missing, we can't do anything. - if (!c0 || !c1) return 0; - btVector3 pivotInB = rb1 ? rb1->getCenterOfMassTransform().inverse()(rb0->getCenterOfMassTransform()(pivotInA)) : rb0->getCenterOfMassTransform() * pivotInA; btVector3 axisInA(axisX,axisY,axisZ); @@ -2658,6 +2655,8 @@ int CcdPhysicsEnvironment::CreateConstraint(class PHY_IPhysicsController* ctrl { case PHY_POINT2POINT_CONSTRAINT: { + // If either of the controllers is missing, we can't do anything. + if (!c0 || !c1) return 0; btPoint2PointConstraint* p2p = 0; @@ -2686,6 +2685,9 @@ int CcdPhysicsEnvironment::CreateConstraint(class PHY_IPhysicsController* ctrl case PHY_GENERIC_6DOF_CONSTRAINT: { + // If either of the controllers is missing, we can't do anything. + if (!c0 || !c1) return 0; + btGeneric6DofConstraint* genericConstraint = 0; if (rb1) @@ -2739,7 +2741,7 @@ int CcdPhysicsEnvironment::CreateConstraint(class PHY_IPhysicsController* ctrl *rb0,s_fixedObject2, frameInA,frameInB,useReferenceFrameA); } - + if (genericConstraint) { //m_constraints.push_back(genericConstraint); @@ -2756,6 +2758,9 @@ int CcdPhysicsEnvironment::CreateConstraint(class PHY_IPhysicsController* ctrl } case PHY_CONE_TWIST_CONSTRAINT: { + // If either of the controllers is missing, we can't do anything. + if (!c0 || !c1) return 0; + btConeTwistConstraint* coneTwistContraint = 0; @@ -2807,7 +2812,7 @@ int CcdPhysicsEnvironment::CreateConstraint(class PHY_IPhysicsController* ctrl *rb0,s_fixedObject2, frameInA,frameInB); } - + if (coneTwistContraint) { //m_constraints.push_back(genericConstraint); @@ -2830,6 +2835,9 @@ int CcdPhysicsEnvironment::CreateConstraint(class PHY_IPhysicsController* ctrl case PHY_LINEHINGE_CONSTRAINT: { + // If either of the controllers is missing, we can't do anything. + if (!c0 || !c1) return 0; + btHingeConstraint* hinge = 0; if (rb1) diff --git a/tests/gtests/blenlib/BLI_ghash_performance_test.cc b/tests/gtests/blenlib/BLI_ghash_performance_test.cc new file mode 100644 index 00000000000..ef17349268c --- /dev/null +++ b/tests/gtests/blenlib/BLI_ghash_performance_test.cc @@ -0,0 +1,415 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" +#include "BLI_ressource_strings.h" + +#define GHASH_INTERNAL_API + +extern "C" { +#include "MEM_guardedalloc.h" +#include "BLI_utildefines.h" +#include "BLI_ghash.h" +#include "BLI_rand.h" +#include "BLI_string.h" +#include "PIL_time_utildefines.h" +} + +/* Using http://corpora.uni-leipzig.de/downloads/eng_wikipedia_2010_1M-text.tar.gz + * (1 million of words, about 122MB of text) from http://corpora.informatik.uni-leipzig.de/download.html */ +//#define TEXT_CORPUS_PATH "/path/to/Téléchargements/eng_wikipedia_2010_1M-text/eng_wikipedia_2010_1M-sentences.txt" + +/* Resizing the hash has a huge cost over global filling operation! */ +//#define GHASH_RESERVE + +#define PRINTF_GHASH_STATS(_gh) \ +{ \ + double q, lf, var, pempty, poverloaded; \ + int bigb; \ + q = BLI_ghash_calc_quality_ex((_gh), &lf, &var, &pempty, &poverloaded, &bigb); \ + printf("GHash stats (%d entries):\n\t" \ + "Quality (the lower the better): %f\n\tVariance (the lower the better): %f\n\tLoad: %f\n\t" \ + "Empty buckets: %.2f%%\n\tOverloaded buckets: %.2f%% (biggest bucket: %d)\n", \ + BLI_ghash_size(_gh), q, var, lf, pempty * 100.0, poverloaded * 100.0, bigb); \ +} void (0) + + +/* Str: whole text, lines and words from a 'corpus' text. */ + +static void str_ghash_tests(GHash *ghash, const char *id) +{ + printf("\n========== STARTING %s ==========\n", id); + +#ifdef TEXT_CORPUS_PATH + size_t sz = 0; + char *data; + { + struct stat st; + if (stat(TEXT_CORPUS_PATH, &st) == 0) + sz = st.st_size; + } + if (sz != 0) { + FILE *f = fopen(TEXT_CORPUS_PATH, "r"); + + data = (char *)MEM_mallocN(sz + 1, __func__); + if (fread(data, sizeof(*data), sz, f) != sz) { + printf("ERROR in reading file %s!", TEXT_CORPUS_PATH); + MEM_freeN(data); + data = BLI_strdup(words10k); + } + data[sz] = '\0'; + fclose(f); + } + else { + data = BLI_strdup(words10k); + } +#else + char *data = BLI_strdup(words10k); +#endif + char *data_p = BLI_strdup(data); + char *data_w = BLI_strdup(data); + char *data_bis = BLI_strdup(data); + + { + char *p, *w, *c_p, *c_w; + + TIMEIT_START(string_insert); + +#ifdef GHASH_RESERVE + BLI_ghash_reserve(ghash, strlen(data) / 32); /* rough estimation... */ +#endif + + BLI_ghash_insert(ghash, data, SET_INT_IN_POINTER(data[0])); + + for (p = c_p = data_p, w = c_w = data_w; *c_w; c_w++, c_p++) { + if (*c_p == '.') { + *c_p = *c_w = '\0'; + if (!BLI_ghash_haskey(ghash, p)) { + BLI_ghash_insert(ghash, p, SET_INT_IN_POINTER(p[0])); + } + if (!BLI_ghash_haskey(ghash, w)) { + BLI_ghash_insert(ghash, w, SET_INT_IN_POINTER(w[0])); + } + p = c_p + 1; + w = c_w + 1; + } + else if (*c_w == ' ') { + *c_w = '\0'; + if (!BLI_ghash_haskey(ghash, w)) { + BLI_ghash_insert(ghash, w, SET_INT_IN_POINTER(w[0])); + } + w = c_w + 1; + } + } + + TIMEIT_END(string_insert); + } + + PRINTF_GHASH_STATS(ghash); + + { + char *p, *w, *c; + void *v; + + TIMEIT_START(string_lookup); + + v = BLI_ghash_lookup(ghash, data_bis); + EXPECT_EQ(data_bis[0], GET_INT_FROM_POINTER(v)); + + for (p = w = c = data_bis; *c; c++) { + if (*c == '.') { + *c = '\0'; + v = BLI_ghash_lookup(ghash, w); + EXPECT_EQ(w[0], GET_INT_FROM_POINTER(v)); + v = BLI_ghash_lookup(ghash, p); + EXPECT_EQ(p[0], GET_INT_FROM_POINTER(v)); + p = w = c + 1; + } + else if (*c == ' ') { + *c = '\0'; + v = BLI_ghash_lookup(ghash, w); + EXPECT_EQ(w[0], GET_INT_FROM_POINTER(v)); + w = c + 1; + } + } + + TIMEIT_END(string_lookup); + } + + BLI_ghash_free(ghash, NULL, NULL); + MEM_freeN(data); + MEM_freeN(data_p); + MEM_freeN(data_w); + MEM_freeN(data_bis); + + printf("========== ENDED %s ==========\n\n", id); +} + +TEST(ghash, TextGHash) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__); + + str_ghash_tests(ghash, "StrGHash - GHash"); +} + +TEST(ghash, TextMurmur2a) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_strhash_p_murmur, BLI_ghashutil_strcmp, __func__); + + str_ghash_tests(ghash, "StrGHash - Murmur"); +} + + +/* Int: uniform 100M first integers. */ + +static void int_ghash_tests(GHash *ghash, const char *id, const unsigned int nbr) +{ + printf("\n========== STARTING %s ==========\n", id); + + { + unsigned int i = nbr; + + TIMEIT_START(int_insert); + +#ifdef GHASH_RESERVE + BLI_ghash_reserve(ghash, nbr); +#endif + + while (i--) { + BLI_ghash_insert(ghash, SET_UINT_IN_POINTER(i), SET_UINT_IN_POINTER(i)); + } + + TIMEIT_END(int_insert); + } + + PRINTF_GHASH_STATS(ghash); + + { + unsigned int i = nbr; + + TIMEIT_START(int_lookup); + + while (i--) { + void *v = BLI_ghash_lookup(ghash, SET_UINT_IN_POINTER(i)); + EXPECT_EQ(i, GET_UINT_FROM_POINTER(v)); + } + + TIMEIT_END(int_lookup); + } + + BLI_ghash_free(ghash, NULL, NULL); + + printf("========== ENDED %s ==========\n\n", id); +} + +TEST(ghash, IntGHash12000) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__); + + int_ghash_tests(ghash, "IntGHash - GHash - 12000", 12000); +} + +TEST(ghash, IntGHash100000000) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__); + + int_ghash_tests(ghash, "IntGHash - GHash - 100000000", 100000000); +} + +TEST(ghash, IntMurmur2a12000) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p_murmur, BLI_ghashutil_intcmp, __func__); + + int_ghash_tests(ghash, "IntGHash - Murmur - 12000", 12000); +} + +TEST(ghash, IntMurmur2a100000000) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p_murmur, BLI_ghashutil_intcmp, __func__); + + int_ghash_tests(ghash, "IntGHash - Murmur - 100000000", 100000000); +} + + +/* Int: random 50M integers. */ + +static void randint_ghash_tests(GHash *ghash, const char *id, const unsigned int nbr) +{ + printf("\n========== STARTING %s ==========\n", id); + + unsigned int *data = (unsigned int *)MEM_mallocN(sizeof(*data) * (size_t)nbr, __func__); + unsigned int *dt; + unsigned int i; + + { + RNG *rng = BLI_rng_new(0); + for (i = nbr, dt = data; i--; dt++) { + *dt = BLI_rng_get_uint(rng); + } + BLI_rng_free(rng); + } + + { + TIMEIT_START(int_insert); + +#ifdef GHASH_RESERVE + BLI_ghash_reserve(ghash, nbr); +#endif + + for (i = nbr, dt = data; i--; dt++) { + BLI_ghash_insert(ghash, SET_UINT_IN_POINTER(*dt), SET_UINT_IN_POINTER(*dt)); + } + + TIMEIT_END(int_insert); + } + + PRINTF_GHASH_STATS(ghash); + + { + TIMEIT_START(int_lookup); + + for (i = nbr, dt = data; i--; dt++) { + void *v = BLI_ghash_lookup(ghash, SET_UINT_IN_POINTER(*dt)); + EXPECT_EQ(*dt, GET_UINT_FROM_POINTER(v)); + } + + TIMEIT_END(int_lookup); + } + + BLI_ghash_free(ghash, NULL, NULL); + + printf("========== ENDED %s ==========\n\n", id); +} + +TEST(ghash, IntRandGHash12000) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__); + + randint_ghash_tests(ghash, "RandIntGHash - GHash - 12000", 12000); +} + +TEST(ghash, IntRandGHash50000000) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__); + + randint_ghash_tests(ghash, "RandIntGHash - GHash - 50000000", 50000000); +} + +TEST(ghash, IntRandMurmur2a12000) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p_murmur, BLI_ghashutil_intcmp, __func__); + + randint_ghash_tests(ghash, "RandIntGHash - Murmur - 12000", 12000); +} + +TEST(ghash, IntRandMurmur2a50000000) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p_murmur, BLI_ghashutil_intcmp, __func__); + + randint_ghash_tests(ghash, "RandIntGHash - Murmur - 50000000", 50000000); +} + +static unsigned int ghashutil_tests_nohash_p(const void *p) +{ + return GET_UINT_FROM_POINTER(p); +} + +static bool ghashutil_tests_cmp_p(const void *a, const void *b) +{ + return a != b; +} + +TEST(ghash, Int4NoHash12000) +{ + GHash *ghash = BLI_ghash_new(ghashutil_tests_nohash_p, ghashutil_tests_cmp_p, __func__); + + randint_ghash_tests(ghash, "RandIntGHash - No Hash - 12000", 12000); +} + +TEST(ghash, Int4NoHash50000000) +{ + GHash *ghash = BLI_ghash_new(ghashutil_tests_nohash_p, ghashutil_tests_cmp_p, __func__); + + randint_ghash_tests(ghash, "RandIntGHash - No Hash - 50000000", 50000000); +} + + +/* Int_v4: 20M of randomly-generated integer vectors. */ + +static void int4_ghash_tests(GHash *ghash, const char *id, const unsigned int nbr) +{ + printf("\n========== STARTING %s ==========\n", id); + + unsigned int (*data)[4] = (unsigned int (*)[4])MEM_mallocN(sizeof(*data) * (size_t)nbr, __func__); + unsigned int (*dt)[4]; + unsigned int i, j; + + { + RNG *rng = BLI_rng_new(0); + for (i = nbr, dt = data; i--; dt++) { + for (j = 4; j--; ) { + (*dt)[j] = BLI_rng_get_uint(rng); + } + } + BLI_rng_free(rng); + } + + { + TIMEIT_START(int_v4_insert); + +#ifdef GHASH_RESERVE + BLI_ghash_reserve(ghash, nbr); +#endif + + for (i = nbr, dt = data; i--; dt++) { + BLI_ghash_insert(ghash, *dt, SET_UINT_IN_POINTER(i)); + } + + TIMEIT_END(int_v4_insert); + } + + PRINTF_GHASH_STATS(ghash); + + { + TIMEIT_START(int_v4_lookup); + + for (i = nbr, dt = data; i--; dt++) { + void *v = BLI_ghash_lookup(ghash, (void *)(*dt)); + EXPECT_EQ(i, GET_UINT_FROM_POINTER(v)); + } + + TIMEIT_END(int_v4_lookup); + } + + BLI_ghash_free(ghash, NULL, NULL); + MEM_freeN(data); + + printf("========== ENDED %s ==========\n\n", id); +} + +TEST(ghash, Int4GHash2000) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_uinthash_v4_p, BLI_ghashutil_uinthash_v4_cmp, __func__); + + int4_ghash_tests(ghash, "Int4GHash - GHash - 2000", 2000); +} + +TEST(ghash, Int4GHash20000000) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_uinthash_v4_p, BLI_ghashutil_uinthash_v4_cmp, __func__); + + int4_ghash_tests(ghash, "Int4GHash - GHash - 20000000", 20000000); +} + +TEST(ghash, Int4Murmur2a2000) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_uinthash_v4_p_murmur, BLI_ghashutil_uinthash_v4_cmp, __func__); + + int4_ghash_tests(ghash, "Int4GHash - Murmur - 2000", 2000); +} + +TEST(ghash, Int4Murmur2a20000000) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_uinthash_v4_p_murmur, BLI_ghashutil_uinthash_v4_cmp, __func__); + + int4_ghash_tests(ghash, "Int4GHash - Murmur - 20000000", 20000000); +} diff --git a/tests/gtests/blenlib/BLI_ghash_test.cc b/tests/gtests/blenlib/BLI_ghash_test.cc new file mode 100644 index 00000000000..5fe43d14cbe --- /dev/null +++ b/tests/gtests/blenlib/BLI_ghash_test.cc @@ -0,0 +1,158 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#define GHASH_INTERNAL_API + +extern "C" { +#include "BLI_utildefines.h" +#include "BLI_ghash.h" +#include "BLI_rand.h" +} + +#define TESTCASE_SIZE 10000 + +/* Only keeping this in case here, for now. */ +#define PRINTF_GHASH_STATS(_gh) \ +{ \ + double q, lf, var, pempty, poverloaded; \ + int bigb; \ + q = BLI_ghash_calc_quality_ex((_gh), &lf, &var, &pempty, &poverloaded, &bigb); \ + printf("GHash stats (%d entries):\n\t" \ + "Quality (the lower the better): %f\n\tVariance (the lower the better): %f\n\tLoad: %f\n\t" \ + "Empty buckets: %.2f%%\n\tOverloaded buckets: %.2f%% (biggest bucket: %d)\n", \ + BLI_ghash_size(_gh), q, var, lf, pempty * 100.0, poverloaded * 100.0, bigb); \ +} void (0) + +/* Note: for pure-ghash testing, nature of the keys and data have absolutely no importance! So here we just use mere + * random integers stored in pointers. */ + +static void init_keys(unsigned int keys[TESTCASE_SIZE], const int seed) +{ + RNG *rng = BLI_rng_new(seed); + unsigned int *k; + int i; + + for (i = 0, k = keys; i < TESTCASE_SIZE; ) { + /* Risks of collision are low, but they do exist. + * And we cannot use a GSet, since we test that here! */ + int j, t = BLI_rng_get_uint(rng); + for (j = i; j--; ) { + if (keys[j] == t) { + continue; + } + } + *k = t; + i++; + k++; + } + BLI_rng_free(rng); +} + +/* Here we simply insert and then lookup all keys, ensuring we do get back the expected stored 'data'. */ +TEST(ghash, InsertLookup) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__); + unsigned int keys[TESTCASE_SIZE], *k; + int i; + + init_keys(keys, 0); + + for (i = TESTCASE_SIZE, k = keys; i--; k++) { + BLI_ghash_insert(ghash, SET_UINT_IN_POINTER(*k), SET_UINT_IN_POINTER(*k)); + } + + EXPECT_EQ(TESTCASE_SIZE, BLI_ghash_size(ghash)); + + for (i = TESTCASE_SIZE, k = keys; i--; k++) { + void *v = BLI_ghash_lookup(ghash, SET_UINT_IN_POINTER(*k)); + EXPECT_EQ(*k, GET_UINT_FROM_POINTER(v)); + } + + BLI_ghash_free(ghash, NULL, NULL); +} + +/* Here we simply insert and then remove all keys, ensuring we do get an empty, unshrinked ghash. */ +TEST(ghash, InsertRemove) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__); + unsigned int keys[TESTCASE_SIZE], *k; + int i, bkt_size; + + init_keys(keys, 10); + + for (i = TESTCASE_SIZE, k = keys; i--; k++) { + BLI_ghash_insert(ghash, SET_UINT_IN_POINTER(*k), SET_UINT_IN_POINTER(*k)); + } + + EXPECT_EQ(TESTCASE_SIZE, BLI_ghash_size(ghash)); + bkt_size = BLI_ghash_buckets_size(ghash); + + for (i = TESTCASE_SIZE, k = keys; i--; k++) { + void *v = BLI_ghash_popkey(ghash, SET_UINT_IN_POINTER(*k), NULL); + EXPECT_EQ(*k, GET_UINT_FROM_POINTER(v)); + } + + EXPECT_EQ(0, BLI_ghash_size(ghash)); + EXPECT_EQ(bkt_size, BLI_ghash_buckets_size(ghash)); + + BLI_ghash_free(ghash, NULL, NULL); +} + +/* Same as above, but this time we allow ghash to shrink. */ +TEST(ghash, InsertRemoveShrink) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__); + unsigned int keys[TESTCASE_SIZE], *k; + int i, bkt_size; + + BLI_ghash_flag_set(ghash, GHASH_FLAG_ALLOW_SHRINK); + init_keys(keys, 20); + + for (i = TESTCASE_SIZE, k = keys; i--; k++) { + BLI_ghash_insert(ghash, SET_UINT_IN_POINTER(*k), SET_UINT_IN_POINTER(*k)); + } + + EXPECT_EQ(TESTCASE_SIZE, BLI_ghash_size(ghash)); + bkt_size = BLI_ghash_buckets_size(ghash); + + for (i = TESTCASE_SIZE, k = keys; i--; k++) { + void *v = BLI_ghash_popkey(ghash, SET_UINT_IN_POINTER(*k), NULL); + EXPECT_EQ(*k, GET_UINT_FROM_POINTER(v)); + } + + EXPECT_EQ(0, BLI_ghash_size(ghash)); + EXPECT_LT(BLI_ghash_buckets_size(ghash), bkt_size); + + BLI_ghash_free(ghash, NULL, NULL); +} + +/* Check copy. */ +TEST(ghash, Copy) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__); + GHash *ghash_copy; + unsigned int keys[TESTCASE_SIZE], *k; + int i; + + init_keys(keys, 30); + + for (i = TESTCASE_SIZE, k = keys; i--; k++) { + BLI_ghash_insert(ghash, SET_UINT_IN_POINTER(*k), SET_UINT_IN_POINTER(*k)); + } + + EXPECT_EQ(TESTCASE_SIZE, BLI_ghash_size(ghash)); + + ghash_copy = BLI_ghash_copy(ghash, NULL, NULL); + + EXPECT_EQ(TESTCASE_SIZE, BLI_ghash_size(ghash_copy)); + EXPECT_EQ(BLI_ghash_buckets_size(ghash), BLI_ghash_buckets_size(ghash_copy)); + + for (i = TESTCASE_SIZE, k = keys; i--; k++) { + void *v = BLI_ghash_lookup(ghash_copy, SET_UINT_IN_POINTER(*k)); + EXPECT_EQ(*k, GET_UINT_FROM_POINTER(v)); + } + + BLI_ghash_free(ghash, NULL, NULL); + BLI_ghash_free(ghash_copy, NULL, NULL); +} diff --git a/tests/gtests/blenlib/BLI_ressource_strings.h b/tests/gtests/blenlib/BLI_ressource_strings.h new file mode 100644 index 00000000000..b823f14af53 --- /dev/null +++ b/tests/gtests/blenlib/BLI_ressource_strings.h @@ -0,0 +1,602 @@ +/* Apache License, Version 2.0 */ + +#ifndef __BLENDER_TESTING_BLI_RESSOURCE_STRING_H__ +#define __BLENDER_TESTING_BLI_RESSOURCE_STRING_H__ + +const char *words10k = +"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam auctor ultrices purus tincidunt mollis. Vestibulum " +"tincidunt imperdiet molestie. Vivamus posuere, risus ut mollis rutrum, lacus nulla mollis velit, consectetur auctor " +"erat est in odio. Proin quis lobortis ex. Ut id quam lacus. Morbi ultrices orci quis sem suscipit tincidunt. Nullam " +"ut molestie justo, vulputate placerat diam. Nunc tincidunt auctor venenatis. Phasellus placerat, odio ac dictum " +"pretium, nisi odio tristique sem, sit amet hendrerit odio tortor eu felis. Duis placerat tristique neque, sit amet " +"ornare nulla fermentum vel. Vivamus vitae rhoncus ante. Sed a dolor mauris. Nullam bibendum vehicula semper. Duis ut " +"commodo nibh. Nulla sit amet eros feugiat, accumsan nisl a, ornare quam. In non magna orci. Curabitur finibus tempus " +"semper. Aliquam fringilla arcu consectetur blandit vestibulum. Mauris mollis est arcu. Praesent pellentesque lacus " +"bibendum massa commodo commodo. Aenean facilisis lobortis varius. Ut semper ullamcorper dui, at pellentesque felis. " +"Duis accumsan sapien ut malesuada lacinia. Praesent elementum venenatis arcu in mattis. Nunc sagittis mauris risus, " +"quis rutrum nisi egestas quis. Maecenas pharetra posuere auctor. Suspendisse mollis sollicitudin elit, id cursus " +"massa bibendum eu. Integer tincidunt dolor non porttitor tempus. Donec lacinia sapien eu enim feugiat suscipit non " +"malesuada diam. Suspendisse nec convallis elit. Nulla eu augue ultrices, consequat lorem at, malesuada magna. " +"Aliquam sed tempor ipsum. Sed hendrerit nec lectus et pharetra. In felis sem, cursus at nunc in, tristique convallis " +"purus. Praesent augue turpis, porttitor consequat risus ornare, laoreet commodo dui. Nulla congue ultrices sapien a " +"cursus. Nulla facilisi. Integer lacinia enim sodales sem mattis, sit amet egestas lectus tincidunt. Ut quis nisl ut " +"ex luctus fermentum quis et diam. Maecenas lectus leo, hendrerit eu facilisis et, mattis ut sem. Duis imperdiet nisl " +"vitae urna consequat suscipit. Suspendisse sed viverra massa, dapibus condimentum sem. Morbi suscipit congue odio. " +"Nullam eleifend fringilla nisl et semper. Sed eu neque ante. Sed eget viverra urna. Duis tempor laoreet interdum. " +"Nunc fringilla aliquet urna sit amet commodo. Curabitur non orci nec libero egestas ullamcorper nec nec velit. Nam " +"vitae ligula lobortis, vehicula nulla id, lacinia urna. Morbi id dignissim eros. Etiam eu risus in sem vestibulum " +"dapibus ut mollis sem. Quisque ultricies pulvinar maximus. Proin risus turpis, auctor eget molestie nec, molestie a " +"ipsum. Donec dapibus dui in lorem rhoncus, non rutrum neque convallis. Donec at tincidunt turpis, nec scelerisque " +"lorem. Donec ac sapien mi. Sed commodo efficitur tempus. Maecenas eu lobortis diam. Phasellus enim nulla, ornare ac " +"laoreet egestas, vestibulum ac arcu. Pellentesque ultrices mauris sem, a iaculis diam tristique id. Proin sed " +"facilisis mauris. Aliquam nibh ex, varius in consequat laoreet, sollicitudin id diam. Vivamus semper ultrices sem " +"non tempor. Sed hendrerit maximus malesuada. In ex orci, elementum non magna eget, congue sagittis tellus. Donec " +"malesuada sem leo, quis malesuada risus blandit et. Praesent porta malesuada metus eget pretium. Vestibulum " +"venenatis tempor tellus at varius. Donec mauris arcu, elementum vitae aliquet nec, ullamcorper vitae neque. Nunc eu " +"viverra justo, sit amet viverra elit. Proin urna elit, luctus ut placerat quis, blandit vitae diam. Vestibulum id " +"fringilla enim. Ut eleifend augue ante, ac euismod sapien luctus sit amet. Pellentesque mattis tortor ac rutrum " +"malesuada. Sed et nulla id metus faucibus condimentum. Vestibulum cursus posuere vestibulum. Proin auctor arcu erat, " +"quis porta sem dignissim a. Donec sed finibus ante. Integer porttitor pretium nunc, eu semper elit. Nam sit amet " +"ornare urna. Suspendisse porta augue id massa luctus maximus. Fusce tellus ligula, finibus sed lacus eget, tristique " +"mollis libero. Vivamus velit diam, faucibus vel fringilla vitae, ornare id lacus. Pellentesque vel sem quis nunc " +"semper porta ut sit amet sapien. Integer nec leo at tortor ullamcorper pulvinar at ut ante. Fusce velit nisl, " +"fermentum in tempus ac, gravida ac tellus. In aliquet sollicitudin erat, non vestibulum diam aliquam in. Duis purus " +"justo, aliquet ut libero vel, egestas mollis nibh. Praesent sed tempor mauris, vel tempor augue. Morbi eu eros vel " +"velit condimentum porttitor nec sit amet odio. Nunc suscipit risus at ex aliquam, in pretium mi maximus. Mauris " +"sollicitudin sit amet arcu luctus maximus. Curabitur vehicula condimentum porta. Nunc consequat vitae urna vel " +"gravida. Vivamus vitae mattis augue, sit amet blandit enim. Phasellus odio leo, cursus eget lacus sit amet, " +"facilisis mattis tortor. Duis venenatis ante libero, eu condimentum urna viverra fermentum. Suspendisse libero leo, " +"pretium eu leo at, imperdiet ultricies nunc. Fusce ante neque, feugiat id lacus sed, fringilla suscipit ligula. " +"Phasellus cursus malesuada urna, vel ullamcorper massa suscipit vitae. In eu bibendum augue. Duis auctor posuere " +"turpis nec vestibulum. Vestibulum nec dui in mi consequat auctor sed at nisl. Suspendisse tellus elit, congue ut " +"facilisis vel, ornare id mauris. Integer rutrum fermentum neque, vitae pharetra metus consectetur in. Duis vitae " +"lacus scelerisque, rhoncus nisl id, sagittis elit. Praesent lacinia libero ac ultricies tempus. Etiam ut maximus " +"sapien. Maecenas sit amet ante auctor, feugiat diam non, vulputate diam. Nulla facilisi. Vestibulum id augue velit. " +"Donec at elementum urna. Morbi elementum nunc in neque ornare, sit amet tempor mauris vulputate. Nunc mauris mauris, " +"lobortis non nibh sed, gravida sollicitudin nunc. Nunc vel dolor non augue venenatis semper vitae non turpis. " +"Praesent mattis elit eu interdum porttitor. Etiam quis magna magna. Praesent a ipsum est. Aenean at ligula vel leo " +"faucibus pulvinar sed eget mauris. Nam accumsan blandit nibh, nec tincidunt nisl eleifend sit amet. Etiam ornare, " +"arcu nec dictum volutpat, nulla orci porttitor orci, vel venenatis mi massa at erat. Maecenas eget accumsan nisl, " +"quis ullamcorper turpis. Pellentesque sit amet mi aliquet, feugiat felis in, dictum urna. Cras nulla leo, congue vel " +"consequat gravida, aliquet a nulla. Nulla commodo, nisi eu ultricies feugiat, justo velit tempor ligula, a tincidunt " +"nisi tellus ut sapien. Sed eget ornare magna. Cras ut vehicula sapien. Quisque id malesuada urna, vitae congue ante. " +"Donec nec leo pretium, finibus nibh a, porta lectus. Fusce arcu tellus, tempor semper sem id, aliquam fringilla " +"ipsum. Ut massa ante, placerat quis sapien quis, sollicitudin blandit turpis. Aenean posuere ullamcorper massa. Nam " +"faucibus egestas arcu. Vivamus vehicula auctor diam, eu placerat diam ullamcorper at. Nulla eu consequat elit, vel " +"semper turpis. Curabitur rhoncus nunc vel vestibulum interdum. Nam augue neque, pharetra vel nisi dignissim, " +"vehicula dapibus risus. Cras eget mattis nisi. Sed tempor posuere gravida. Proin sagittis a nisl eget gravida. " +"Curabitur viverra dapibus arcu, sit amet rutrum nibh fringilla euismod. Donec vitae risus non lorem facilisis cursus " +"eu eu quam. Donec quis lacus blandit, consectetur elit ut, sagittis ligula. Etiam dapibus ex sit amet elit commodo " +"finibus. Suspendisse non finibus felis, non cursus libero. Vivamus semper aliquet velit vel elementum. Phasellus " +"dictum, tortor id sagittis ultrices, ex dui porttitor tortor, nec mattis dolor sem nec mi. Ut aliquam consequat eros " +"sit amet mollis. Nullam mollis venenatis porttitor. Donec sit amet velit at velit luctus auctor dictum in neque. Ut " +"vulputate ultricies mollis. Pellentesque elementum augue dolor, non varius ligula tristique ac. Nullam eget mauris " +"urna. Integer elementum eleifend pulvinar. Morbi gravida ante eget ornare faucibus. Mauris pulvinar consequat nunc " +"vel accumsan. Curabitur egestas urna elit, ut accumsan magna dictum in. Nam neque mi, ornare sed leo at, tempor " +"vulputate nunc. Nunc dignissim mauris id dui iaculis fringilla. Praesent malesuada tellus in dapibus feugiat. " +"Vivamus posuere, nisi et consequat euismod, lorem augue iaculis velit, eget iaculis neque quam eu mi. Nullam ac " +"hendrerit felis, non elementum ipsum. Aliquam erat volutpat. Proin vel molestie felis. Nullam luctus vel ante nec " +"facilisis. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Duis et metus " +"justo. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut tristique sit amet elit et congue. Aenean " +"quis elementum enim, vitae pharetra sem. Vestibulum vel finibus nisl, at consequat eros. In vitae mollis lacus, et " +"pharetra elit. Mauris varius sapien quis tincidunt blandit. Proin non semper nibh. Aliquam non elit id felis laoreet " +"interdum eget a risus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. " +"Suspendisse nisl tellus, mollis id erat vel, hendrerit volutpat nunc. Quisque scelerisque cursus tellus, nec " +"placerat quam imperdiet in. Sed porttitor arcu vel ligula finibus, a vestibulum enim ultrices. Fusce imperdiet augue " +"eget est vehicula porttitor. Quisque convallis odio vitae lorem porttitor iaculis. Ut dictum velit ac tortor " +"lobortis ultrices. Vestibulum tincidunt vestibulum mauris, at fermentum elit imperdiet nec. Nunc finibus ornare " +"lorem vel malesuada. Praesent arcu turpis, pulvinar sit amet accumsan quis, tincidunt vel justo. Pellentesque " +"volutpat nec enim sit amet pulvinar. Nam eu libero dignissim, volutpat elit ut, semper tortor. Morbi pellentesque " +"nisl lectus. In vel tellus sed sem luctus lobortis ut nec diam. Phasellus id semper sem. Phasellus in purus " +"consequat, rhoncus mi mollis, finibus ligula. Fusce feugiat dictum consequat. Mauris egestas, est ut euismod " +"consequat, arcu dui dignissim quam, pharetra dignissim orci dolor quis nisl. Nunc dapibus blandit urna non feugiat. " +"Suspendisse non maximus augue. Quisque ut orci aliquet, vulputate massa eget, mattis diam. Etiam efficitur " +"consectetur viverra. Nulla massa augue, elementum at turpis et, cursus ultricies risus. Suspendisse vel nibh " +"placerat, imperdiet elit et, viverra ligula. Donec lorem lorem, hendrerit nec aliquam sit amet, scelerisque sit amet " +"massa. Mauris convallis ullamcorper tortor sed malesuada. Fusce ultricies a turpis eu ornare. Suspendisse potenti. " +"Sed non nulla condimentum, vulputate nisi nec, tincidunt arcu. Morbi erat leo, lobortis id odio ac, hendrerit " +"sodales sem. Ut malesuada, lectus at posuere molestie, orci metus vehicula justo, mattis tincidunt arcu risus quis " +"odio. Fusce non sem sed nisi consectetur finibus vitae quis diam. Vivamus a lacinia nisl. Praesent tempus nunc " +"gravida, lacinia lacus in, lobortis massa. Aliquam gravida consequat nisi at fringilla. Quisque tortor tortor, " +"tincidunt cursus lorem eget, ultrices ultricies lacus. Phasellus mattis iaculis elit, eget mattis nisl bibendum sed. " +"Integer faucibus gravida nisl, ac consequat ex tempor at. Sed tempus elementum vestibulum. Suspendisse vitae enim " +"semper, pulvinar diam eget, suscipit turpis. Maecenas ultricies, diam sed consectetur sagittis, diam sem cursus " +"nisl, nec aliquet tellus augue quis ipsum. Cras vel lorem convallis, mattis risus at, placerat massa. Curabitur vel " +"rutrum ligula. Quisque in nibh libero. Pellentesque diam tellus, consectetur eget quam ac, faucibus imperdiet odio. " +"Sed tortor nulla, scelerisque non turpis nec, fringilla bibendum est. Etiam a urna eget erat tincidunt ultrices. " +"Maecenas lorem odio, volutpat nec ligula id, hendrerit aliquam nulla. Aenean congue lacinia fermentum. Suspendisse " +"sed interdum lacus. Fusce scelerisque posuere sagittis. Ut at semper tellus. Donec condimentum orci nunc, non " +"fermentum purus volutpat eget. Maecenas elementum dapibus ante, eu suscipit quam imperdiet ut. Integer non congue " +"elit. Sed venenatis, turpis varius commodo euismod, libero magna fringilla lacus, quis venenatis velit lectus sed " +"augue. Morbi gravida orci odio, ut ornare massa sollicitudin a. Donec convallis mi et sapien tempor, non dapibus " +"dolor fringilla. Aenean euismod rutrum turpis, et facilisis orci porttitor eu. Suspendisse in neque leo. Nulla " +"facilisi. Etiam mollis orci nisl, quis scelerisque metus efficitur vehicula. Nam porta molestie tortor, sit amet " +"consectetur leo vestibulum vel. Pellentesque a volutpat augue. Maecenas vel elementum ex, eget elementum leo. " +"Curabitur at maximus metus, quis porttitor orci. Praesent auctor commodo elit, a dapibus tortor volutpat et. " +"Praesent dictum posuere dolor sit amet molestie. Sed viverra augue nec eros mattis blandit. In quis sodales dolor. " +"Donec sed purus ex. Fusce erat magna, efficitur ac tempus ac, lacinia quis augue. Aliquam porta efficitur est vel " +"placerat. Phasellus egestas vel nunc eu consequat. Maecenas ligula arcu, molestie ut dui ut, ornare finibus felis. " +"Duis condimentum non augue ut posuere. Aenean mattis eros ut ligula ornare finibus. Aliquam feugiat ut turpis a " +"feugiat. Vestibulum eget sollicitudin orci, nec fermentum justo. Praesent efficitur est a metus bibendum, eget " +"feugiat diam suscipit. Suspendisse sit amet ipsum ut purus feugiat pretium. Morbi nisl risus, ultricies sit amet " +"ullamcorper euismod, commodo eu libero. Aenean fringilla ipsum nec orci rutrum aliquet. Aenean lacus ante, eleifend " +"eu eleifend fringilla, elementum ac justo. Vestibulum tincidunt interdum lectus sit amet fermentum. Etiam rhoncus eu " +"ante lacinia sagittis. Maecenas iaculis ut erat quis feugiat. Maecenas sed est vel tellus bibendum rutrum volutpat " +"nec odio. Vivamus euismod augue nec purus euismod, mattis finibus nisi finibus. Donec quis ultrices massa. Quisque " +"at nisl faucibus, facilisis tellus ut, ultricies dui. Class aptent taciti sociosqu ad litora torquent per conubia " +"nostra, per inceptos himenaeos. Donec et arcu eros. Etiam dapibus bibendum felis eu viverra. Integer a lacus " +"venenatis elit lacinia facilisis non non felis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed " +"ultricies augue at sapien mattis aliquam. Quisque nec semper purus. Cras auctor aliquet lacus, sed facilisis urna " +"sollicitudin non. Vivamus eget erat purus. Sed a risus augue. Donec non sem sed sapien accumsan lacinia. Ut mauris " +"odio, vehicula id accumsan at, tincidunt non odio. Nunc porttitor luctus ante ac cursus. Cras et dapibus ex, id " +"pretium ligula. Proin volutpat rhoncus ex vitae venenatis. Pellentesque imperdiet, magna non tempus auctor, metus " +"dolor scelerisque dui, id tempor purus est in risus. Suspendisse vehicula imperdiet sapien, nec pulvinar dolor " +"ornare ac. Nulla luctus, nisl in aliquam blandit, risus orci placerat nunc, id tempus sem neque vitae leo. Aenean at " +"elit elit. Suspendisse finibus dictum interdum. Nunc consectetur eget quam vitae egestas. Pellentesque tellus augue, " +"aliquet at faucibus ac, imperdiet ut nulla. Maecenas quis lorem velit. Donec porta ligula et suscipit luctus. " +"Aliquam sed pretium nunc. Nunc quis posuere tortor. Fusce in lectus nec turpis rhoncus pellentesque eu at quam. " +"Nulla facilisi. Sed ante nulla, posuere ac ullamcorper vel, rhoncus vitae nisl. Nam non pellentesque arcu. Vivamus " +"nibh leo, pellentesque a mollis non, gravida ut erat. Donec purus urna, pulvinar eu iaculis blandit, rutrum eget " +"nulla. Fusce quis fermentum diam, faucibus volutpat lorem. Maecenas aliquet nisi nisl, eget sollicitudin ipsum " +"facilisis at. Mauris nec sapien nisi. Duis ac laoreet sapien, a condimentum nisi. Nam vitae sapien sed sem convallis " +"ornare. Pellentesque neque diam, ullamcorper et dolor sit amet, faucibus venenatis tortor. Nunc vel erat malesuada, " +"vulputate odio sit amet, aliquam dui. Donec tincidunt arcu ut risus laoreet, id malesuada leo ultrices. Praesent a " +"scelerisque libero, vitae suscipit massa. Quisque faucibus mauris rhoncus turpis vestibulum rhoncus. Donec vel " +"molestie magna. Aenean et lorem dui. Nam iaculis ante sapien, semper tincidunt tortor hendrerit id. Nulla sed orci " +"mi. Aliquam hendrerit libero erat, ac aliquam massa rutrum non. Suspendisse eleifend, elit in aliquet hendrerit, " +"tellus erat sodales neque, quis rhoncus tellus sem vitae est. Interdum et malesuada fames ac ante ipsum primis in " +"faucibus. Etiam quis mauris non ipsum tristique interdum sit amet eget mi. Ut velit risus, gravida ut efficitur sit " +"amet, commodo at diam. Sed consectetur dui porttitor quam feugiat, et auctor mauris maximus. Nullam lobortis ac mi " +"lacinia egestas. Proin ante massa, malesuada ut nulla elementum, venenatis mollis ante. Cum sociis natoque penatibus " +"et magnis dis parturient montes, nascetur ridiculus mus. Mauris eget gravida eros, non varius velit. Integer " +"consectetur lectus nec arcu scelerisque, scelerisque vulputate mauris suscipit. Aliquam orci dui, faucibus et rutrum " +"in, rhoncus quis dolor. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; " +"Maecenas ante nunc, placerat id lectus sit amet, luctus cursus ante. Nulla nec placerat arcu. Fusce ac dictum ex. " +"Vivamus semper nulla vitae neque volutpat, auctor vestibulum arcu tempus. Pellentesque aliquam tincidunt arcu, et " +"pharetra neque. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nunc " +"risus augue, malesuada quis risus a, suscipit semper metus. Suspendisse ac rhoncus felis. Aliquam orci lectus, " +"elementum at nulla at, ullamcorper pellentesque leo. Quisque nisi tellus, pharetra in pellentesque in, facilisis " +"vitae velit. In ex ex, sagittis at dolor vel, congue ultricies velit. Duis quis gravida mi. Aenean tempor efficitur " +"lectus. Fusce sodales, ex eu efficitur iaculis, metus sem eleifend purus, ut commodo arcu tortor eget urna. Etiam " +"nisi nisl, malesuada convallis ex at, malesuada elementum nunc. Vivamus commodo mi id ligula tincidunt posuere. " +"Integer eget arcu cursus, sagittis quam eu, aliquam leo. In auctor eget mauris et elementum. Aenean sagittis euismod " +"tellus sed accumsan. Aliquam erat volutpat. Aliquam erat volutpat. Ut consectetur porta ipsum sit amet porttitor. " +"Nam ut nunc a turpis auctor finibus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac " +"turpis egestas. Donec non nisl condimentum, fermentum augue in, egestas libero. Pellentesque ut odio rhoncus, " +"sollicitudin felis vitae, pellentesque est. Suspendisse tincidunt eros eget ex vestibulum elementum. Vivamus mollis " +"scelerisque diam, quis dignissim dolor venenatis at. Ut gravida sapien vitae risus efficitur, ut auctor justo " +"gravida. Cras arcu elit, interdum vel purus sit amet, venenatis molestie tellus. Integer consectetur tempor velit a " +"varius. Praesent congue, massa non congue blandit, tortor purus imperdiet elit, sit amet pharetra arcu lacus egestas " +"neque. Maecenas in erat arcu. In varius, risus vitae mollis sodales, nisi velit bibendum tortor, vitae sagittis " +"augue tortor quis nunc. Fusce posuere dolor ac tincidunt facilisis. Phasellus in lacus diam. Fusce mattis sapien " +"tellus, scelerisque pharetra leo eleifend nec. Cras libero diam, convallis in luctus a, iaculis a ipsum. Duis arcu " +"leo, volutpat non mauris et, scelerisque suscipit diam. Ut vulputate placerat velit quis placerat. Duis commodo non " +"turpis et convallis. Duis nec pulvinar metus, ac tristique leo. Fusce vehicula augue ac placerat elementum. Nulla " +"dapibus nisi pretium lectus sodales, ac congue sapien ornare. Vestibulum sagittis orci ut purus efficitur, eu mollis " +"libero placerat. Vestibulum ullamcorper odio non quam mollis, eget rhoncus metus eleifend. Mauris scelerisque, massa " +"rutrum sodales malesuada, elit dolor blandit lectus, quis faucibus felis odio feugiat lacus. Nunc bibendum congue " +"efficitur. Nunc a purus neque. In lobortis metus nisi, vel pellentesque mi facilisis sed. Donec in pretium neque, in " +"maximus metus. Integer faucibus diam sed tristique sagittis. Nullam eget maximus leo, eget malesuada leo. Vestibulum " +"ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean porttitor risus eget eros " +"euismod molestie. Integer tristique tincidunt elit, non posuere libero pretium vel. Fusce dapibus, nisi nec egestas " +"dapibus, lectus arcu maximus leo, a finibus diam arcu ut mauris. Vivamus tincidunt lectus ut augue ultrices, et " +"cursus sem cursus. Proin in quam mauris. Maecenas vel magna dapibus, interdum ipsum mattis, posuere tortor. Cras eu " +"massa ex. Donec eget massa vel dui gravida luctus vel a quam. Etiam eu lobortis neque. Etiam ligula dui, dictum ut " +"turpis ac, eleifend pretium turpis. Vestibulum convallis finibus commodo. Morbi fermentum ante nunc, a rhoncus lacus " +"ultricies quis. Suspendisse finibus quam blandit odio elementum, non efficitur diam laoreet. Cras aliquet ligula " +"eget magna scelerisque, ut ornare nisi elementum. Duis nisl massa, suscipit id nibh a, venenatis auctor risus. Nulla " +"luctus eget odio quis ultrices. Etiam consequat sapien ut nisl mollis cursus. Pellentesque a lacinia odio, id varius " +"lorem. Curabitur scelerisque in urna eget pretium. Class aptent taciti sociosqu ad litora torquent per conubia " +"nostra, per inceptos himenaeos. Sed leo metus, fermentum vitae quam ut, suscipit efficitur purus. Sed facilisis " +"dapibus pulvinar. Cras sed eleifend mi. Sed quis nibh in sapien venenatis interdum ac nec orci. Sed non tortor urna. " +"Nam rutrum lacinia diam id vehicula. Quisque vitae lobortis nibh, at tempor purus. Suspendisse dictum interdum nisi, " +"quis maximus ipsum commodo tempus. Nulla semper congue gravida. Aenean at nibh in eros aliquam egestas. Nulla " +"fermentum efficitur laoreet. Donec non lorem nec augue porttitor cursus eu in quam. Aenean laoreet quam neque, at " +"tempus nisi ultrices id. Quisque in diam lacinia nulla scelerisque rhoncus vitae eget nulla. Donec vel est metus. " +"Nullam suscipit odio eu enim lacinia facilisis eget in tellus. Vestibulum vehicula risus nec odio consectetur, a " +"cursus massa imperdiet. Duis facilisis felis quis nunc mattis, nec volutpat libero tempor. Nulla nec leo sed tellus " +"maximus lobortis. Suspendisse at urna nibh. Vestibulum eget turpis nisl. Donec scelerisque neque auctor erat tempor " +"elementum sed id lacus. Sed metus nulla, dictum non luctus vel, suscipit et ex. Quisque laoreet sapien non neque " +"iaculis, at aliquam massa viverra. Nullam nibh diam, imperdiet eu nunc sed, congue cursus leo. Morbi tristique diam " +"metus, at faucibus magna mollis at. Sed eget nibh nunc. Nam nec elementum sem, sit amet tincidunt lorem. In viverra " +"elit et interdum fermentum. Integer imperdiet orci ac justo molestie ullamcorper. Pellentesque fringilla tortor " +"erat, scelerisque maximus nisl sollicitudin a. Integer nisi elit, pharetra eget lacinia non, congue sit amet ex. " +"Phasellus tempus suscipit ultrices. Quisque ac nibh dignissim erat bibendum cursus vel a enim. Curabitur a augue sit " +"amet lorem pharetra feugiat. Donec euismod, massa at venenatis bibendum, elit libero pellentesque velit, eget congue " +"metus risus a enim. Aenean pretium vestibulum enim, sit amet vulputate urna auctor vitae. Praesent porttitor erat eu " +"mi cursus venenatis. Maecenas ut ultrices neque, ac feugiat libero. Nulla finibus sit amet sem in auctor. Nam " +"fermentum maximus ex, et consequat velit lobortis id. Aliquam eu feugiat est. Donec quis leo ex. Suspendisse " +"convallis eget nulla eu aliquet. Quisque aliquet tortor vitae ipsum fermentum tristique. Sed convallis rutrum augue, " +"ac viverra est pharetra quis. Ut porttitor magna massa, placerat maximus lectus scelerisque quis. Sed viverra urna " +"in neque feugiat rhoncus. Donec ut viverra odio, laoreet dignissim dui. Aenean tristique feugiat diam vel luctus. " +"Cras sit amet condimentum neque, ut faucibus ante. Aenean vitae elit id est laoreet efficitur in sit amet magna. " +"Praesent ante felis, blandit id nisl ut, porta fringilla orci. Aenean vel accumsan metus, vel vehicula metus. Nulla " +"placerat nibh et auctor convallis. Maecenas magna metus, pretium ac sodales ac, eleifend quis eros. Praesent " +"volutpat quam a pulvinar pharetra. Sed arcu dolor, aliquet nec magna in, faucibus consequat lorem. In tincidunt, ex " +"a finibus rutrum, metus dui fringilla ex, ac mollis elit leo eget augue. Nunc vehicula facilisis nibh, quis " +"ultricies sem. Praesent nulla est, finibus in lorem in, mattis placerat urna. Proin hendrerit risus nunc, id congue " +"ex posuere id. Aenean ullamcorper tortor quis lorem consectetur, et euismod leo fermentum. Praesent vulputate congue " +"lectus sit amet pulvinar. Vestibulum vel vestibulum quam, in convallis diam. Maecenas sollicitudin magna odio, eget " +"mollis purus posuere eu. Curabitur molestie mattis ligula, a maximus dui fermentum ut. Fusce justo velit, eleifend " +"ut tellus vitae, volutpat maximus risus. Pellentesque suscipit mauris non purus placerat porta. Nunc in malesuada " +"mi, vel bibendum felis. Aenean pretium nunc id efficitur porttitor. Mauris malesuada, tortor sit amet blandit " +"tincidunt, tellus est ullamcorper diam, sit amet aliquet ex velit interdum quam. In hac habitasse platea dictumst. " +"Sed vitae est eu elit posuere mattis nec a mauris. Morbi id ligula sed nunc sagittis finibus vitae eu nisi. Cras " +"dignissim sagittis tellus a suscipit. Nunc semper erat nec libero vestibulum, at mattis purus scelerisque. " +"Pellentesque egestas volutpat eleifend. Nullam venenatis erat id diam venenatis, sed rhoncus felis hendrerit. Nullam " +"luctus facilisis risus. Mauris sed urna nisi. Ut tempus feugiat metus. Integer at purus velit. Praesent neque felis, " +"pellentesque vitae sem nec, tempor commodo urna. Morbi malesuada ante sit amet purus tincidunt pellentesque. Aenean " +"commodo lectus sit amet dignissim hendrerit. Phasellus auctor tellus ligula, eu ultrices ex egestas non. Mauris eget " +"nisl dictum, scelerisque sapien et, dapibus felis. Aenean in dignissim leo. Sed semper, ex at euismod molestie, ex " +"odio ullamcorper nisi, et facilisis lectus eros non magna. In hac habitasse platea dictumst. Pellentesque sed " +"maximus mauris. Cras luctus dapibus nunc, sit amet suscipit dui viverra nec. Donec gravida tortor porttitor orci " +"malesuada porttitor. Nunc condimentum eu libero sit amet varius. Curabitur mollis urna eu porta tincidunt. Nullam " +"ultricies magna libero, et dapibus tellus tempus eu. Nullam pretium lectus nec iaculis pretium. Maecenas at arcu " +"lobortis, ornare ante nec, euismod metus. Pellentesque volutpat tellus nulla. Aenean mattis efficitur velit vitae " +"blandit. Duis vel egestas eros. Pellentesque aliquam placerat elit, eu congue sem ullamcorper sit amet. Ut erat " +"nisl, luctus vitae pellentesque ut, tristique eu odio. Pellentesque nec fermentum ex, rhoncus varius dui. Mauris " +"lobortis nunc nec dui volutpat consequat. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur " +"ridiculus mus. Aliquam dignissim purus sed ligula pretium placerat. In vestibulum ultricies mauris. Curabitur " +"aliquet lorem quis libero auctor, ut rhoncus purus lobortis. Nulla elementum erat nec rhoncus posuere. Integer " +"faucibus quam sed elementum fringilla. In in lobortis sapien, nec commodo tortor. Aenean euismod ipsum nisi, vitae " +"fringilla leo imperdiet ut. Donec a semper odio, et tempor magna. Cras cursus vel augue quis egestas. Nam nec tortor " +"blandit, mattis quam imperdiet, finibus quam. Pellentesque tincidunt eros urna, ut tristique diam faucibus " +"condimentum. Ut dictum risus mi, non sollicitudin turpis facilisis sit amet. Morbi finibus scelerisque mattis. Fusce " +"vel tempor purus, nec pharetra augue. Curabitur dapibus, orci eu consectetur ultrices, diam mauris sodales urna, non " +"euismod diam lacus luctus risus. Mauris commodo accumsan sapien. Proin vel blandit sapien. Donec porta tortor vel " +"nibh faucibus molestie. Pellentesque placerat justo erat, vitae tristique felis fringilla eget. Quisque facilisis " +"justo at orci lobortis, ut commodo diam egestas. Etiam non tristique nisl. Cras varius, massa a sollicitudin ornare, " +"turpis arcu fringilla leo, non euismod ligula arcu id lacus. Suspendisse potenti. Morbi pharetra dolor eget porta " +"tristique. Nullam sem tortor, lobortis eget hendrerit a, efficitur sit amet sapien. Fusce sit amet condimentum odio, " +"aliquet rutrum velit. Morbi vel rhoncus ante. In blandit eros ut lectus varius, quis tempor arcu iaculis. In massa " +"leo, venenatis nec lobortis non, pulvinar non nunc. Nunc vehicula, erat vitae placerat eleifend, eros ipsum " +"consectetur odio, eu ornare velit mauris nec sapien. Integer a consequat libero. Quisque velit augue, blandit eu " +"luctus sit amet, laoreet sit amet odio. Etiam in enim lacus. Interdum et malesuada fames ac ante ipsum primis in " +"faucibus. In rutrum a tortor id pulvinar. Donec pretium lorem sed sem eleifend fringilla. Fusce sollicitudin ac " +"ligula eget pharetra. Sed cursus diam non sem ullamcorper efficitur. Vivamus congue ligula iaculis justo iaculis " +"elementum. Integer tempor nisl arcu, ut tincidunt erat vestibulum et. Suspendisse rutrum aliquet eros non " +"pellentesque. Mauris laoreet, diam id tincidunt faucibus, risus velit venenatis risus, in venenatis justo diam et " +"orci. Etiam pulvinar pulvinar nisi, id efficitur erat vulputate ut. Sed suscipit sodales ante, a blandit orci " +"maximus vel. Vestibulum at aliquet orci. Proin tincidunt nisi quis eros consequat consectetur. Praesent congue " +"lobortis laoreet. Donec imperdiet risus erat, eu volutpat justo posuere id. Fusce placerat sollicitudin eros vitae " +"tincidunt. Sed orci ante, ultricies sed dapibus vel, sagittis ac massa. Pellentesque vel mauris nec est hendrerit " +"posuere. Integer sagittis diam sed felis facilisis ultrices. Aliquam erat volutpat. Nulla pharetra justo in ipsum " +"dapibus, nec viverra nunc euismod. Nulla massa ante, euismod at interdum vel, dapibus ut ex. Etiam consequat mauris " +"a suscipit lobortis. Donec commodo convallis velit, eget commodo urna vulputate ac. Sed molestie vel dui ut feugiat. " +"Donec orci purus, placerat vitae egestas sed, sodales nec ex. Sed egestas turpis non malesuada semper. Donec et mi a " +"nisi volutpat sagittis. Suspendisse potenti. Phasellus mollis sapien ac tellus imperdiet tempus. Praesent nec sapien " +"sit amet ipsum interdum interdum non eget nunc. Aenean fringilla lorem a viverra rutrum. Donec at maximus nibh. " +"Phasellus facilisis justo sit amet metus pharetra sagittis. Quisque mollis metus laoreet ipsum tincidunt " +"sollicitudin. Maecenas sit amet dictum ligula. Fusce molestie iaculis dui, et gravida libero hendrerit in. Praesent " +"euismod libero metus, vitae rhoncus velit ultrices eget. Vestibulum ac massa bibendum, gravida dolor vel, dapibus " +"est. Etiam non elit varius, mollis purus eget, placerat velit. Nullam lectus dui, mattis at pulvinar eu, elementum " +"et lorem. Sed vel auctor orci, nec semper neque. Nullam cursus commodo quam, in ultricies tellus rhoncus vulputate. " +"Mauris dapibus ipsum ipsum, dapibus euismod purus pellentesque at. Nullam euismod lectus non risus consequat " +"vulputate. Quisque finibus a turpis eu convallis. Nam magna turpis, feugiat ut urna in, tempus facilisis elit. Duis " +"dignissim purus sagittis porttitor posuere. Suspendisse varius ligula at egestas scelerisque. Duis placerat sagittis " +"nisi, et molestie tortor posuere condimentum. Morbi hendrerit, ante ornare tempus finibus, ex ipsum laoreet dui, vel " +"ornare felis tortor sit amet metus. Vivamus laoreet placerat massa, non suscipit nisl faucibus eget. Vestibulum ante " +"ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Pellentesque orci lacus, vulputate cursus ex " +"eu, porta aliquam massa. Proin dolor massa, faucibus vel rhoncus et, venenatis a nisi. Vivamus venenatis enim mi. " +"Sed viverra augue vitae lectus lobortis vulputate. Phasellus ac ligula congue, sagittis est non, aliquet tortor. " +"Suspendisse faucibus euismod neque, ac congue felis. Curabitur maximus neque sit amet odio varius gravida. Proin " +"egestas nulla eget mi bibendum luctus. Ut non mollis mi. Quisque finibus, eros non lobortis interdum, diam nisi " +"faucibus diam, non imperdiet leo velit et dolor. Nam est massa, vehicula sed diam sed, laoreet convallis nisi. Donec " +"enim ligula, dignissim non sem ut, pulvinar cursus mi. In at dignissim nulla, ac fringilla neque. In hac habitasse " +"platea dictumst. Quisque luctus mattis orci, consequat egestas nisi. Vivamus et metus et quam porttitor elementum. " +"Suspendisse auctor mauris eu sollicitudin sagittis. Vivamus ac ante non augue iaculis consectetur quis quis dui. In " +"bibendum risus tristique ligula iaculis finibus. Phasellus non ante risus. Maecenas ac leo cursus, molestie purus " +"sed, tristique purus. Etiam sem nulla, aliquam nec laoreet nec, iaculis quis nulla. Maecenas id dui id neque " +"venenatis gravida. Etiam vestibulum felis at diam porta ultrices. Ut finibus tortor et augue ornare, et efficitur " +"purus scelerisque. Phasellus et ultricies arcu, vel lacinia lacus. Aenean tincidunt eleifend nunc, sit amet mattis " +"purus venenatis sit amet. Curabitur eleifend sem nisl, et feugiat diam pharetra sit amet. Mauris ullamcorper mi vel " +"condimentum egestas. Nulla pulvinar purus vel sagittis posuere. Nulla quis enim bibendum, iaculis quam in, tincidunt " +"quam. Vestibulum rhoncus volutpat risus. Nulla ultricies bibendum est non malesuada. Nunc porta erat est, a " +"tincidunt magna gravida vel. Maecenas sit amet aliquet odio. Vestibulum egestas, tortor scelerisque consectetur " +"pharetra, nisi tellus feugiat justo, et bibendum libero mi in diam. Aliquam tempus sapien nec tristique convallis. " +"Nullam congue, lacus quis bibendum dignissim, nisl purus molestie dolor, a tempor dolor nibh pretium tellus. In hac " +"habitasse platea dictumst. Cras at est turpis. Nam nec lacus posuere, mattis mi eu, viverra ex. Nullam eleifend " +"ornare orci, vel tempor tellus. Ut nec eros eget tortor tempus tristique commodo sed lorem. Donec quis scelerisque " +"nibh, non tincidunt velit. Fusce in eleifend sapien. Nunc sodales sem ut nunc pellentesque, eget pharetra justo " +"tempor. Proin pretium velit et vehicula interdum. Maecenas luctus venenatis tincidunt. Donec hendrerit, ligula non " +"volutpat porta, dui ante facilisis massa, at congue orci mi sed quam. Donec lorem ipsum, malesuada quis purus in, " +"commodo malesuada justo. Etiam luctus, lorem vel rutrum tristique, mauris urna volutpat felis, a laoreet urna nunc " +"et neque. Morbi a diam tincidunt augue cursus commodo nec ut ligula. Maecenas ultrices purus fermentum ullamcorper " +"aliquet. Maecenas mi enim, semper nec metus at, posuere tristique ligula. Suspendisse est elit, porta quis massa id, " +"gravida commodo ante. Nullam maximus mauris sit amet dolor tempus posuere. Phasellus purus mi, interdum in ipsum " +"quis, tristique venenatis dolor. Suspendisse potenti. Phasellus odio erat, varius sed aliquet vestibulum, laoreet " +"sed mauris. Vivamus sapien erat, maximus tristique elementum ac, eleifend in enim. Morbi accumsan elementum neque, a " +"facilisis enim laoreet non. Donec auctor condimentum fringilla. Proin id urna nec tellus maximus maximus tincidunt " +"et libero. Integer ultricies venenatis odio, ut volutpat odio laoreet non. Donec in scelerisque justo. Integer " +"mauris libero, fringilla vel sapien sit amet, laoreet tincidunt dolor. Nam efficitur sagittis arcu, vel lobortis dui " +"gravida non. Curabitur lobortis feugiat finibus. Vestibulum dictum tortor nec magna fringilla blandit. Nulla " +"facilisi. Sed cursus laoreet neque vitae pulvinar. Ut iaculis euismod ullamcorper. Nunc in hendrerit lectus, sed " +"venenatis mi. Suspendisse et est dui. Sed elementum augue non ornare cursus. Quisque varius facilisis magna nec " +"laoreet. Suspendisse consequat, risus sed tempus egestas, velit felis faucibus erat, eu pharetra erat nisl sed " +"turpis. Sed ultricies ac quam id mollis. In consequat et erat vitae interdum. Pellentesque malesuada feugiat ligula " +"eu consectetur. Vestibulum tempor mi quis purus luctus dictum. Etiam condimentum ac ligula eget imperdiet. Ut " +"placerat, tortor eu lacinia imperdiet, enim nibh aliquam nibh, quis faucibus enim odio eu arcu. Nullam sagittis, " +"diam a ornare congue, ipsum eros scelerisque est, sit amet sagittis nisl tellus in felis. Nam eget ornare turpis. " +"Sed tempor ac enim a vestibulum. Pellentesque eleifend lacus non libero accumsan, ut consectetur sapien lacinia. " +"Etiam ut arcu non mi feugiat accumsan ut sit amet risus. Donec consequat eros sapien, malesuada imperdiet justo " +"bibendum sit amet. Nulla pretium varius lectus, in eleifend quam fringilla in. Quisque eu pretium velit. Sed eget " +"lectus sit amet tortor blandit tempus vel at sapien. Sed at velit porta, venenatis lorem sed, dapibus arcu. Donec " +"pellentesque tortor id massa interdum pretium. Praesent id diam quis nunc dictum finibus quis quis ipsum. Quisque " +"consectetur risus eu elit viverra, eget laoreet odio efficitur. In congue turpis iaculis ullamcorper bibendum. Duis " +"at elit et velit varius vulputate ut ac turpis. Nunc posuere, urna id lobortis ornare, neque ex ultricies erat, id " +"sollicitudin ante quam sed magna. Nunc ultrices quam erat, eget dictum libero sollicitudin in. Nulla facilisi. " +"Pellentesque eleifend risus non justo imperdiet aliquet. Donec finibus auctor ornare. Duis in arcu lacinia, " +"fermentum tellus vel, efficitur justo. Morbi nec nunc leo. Proin lacinia erat vel elementum dapibus. Proin diam " +"ipsum, mollis eu lobortis a, facilisis consectetur est. Vestibulum rutrum pellentesque urna, a laoreet justo dictum " +"vitae. Nullam dictum, mi elementum dictum interdum, sem nisl fermentum est, nec mattis enim ante aliquam tortor. " +"Phasellus eu quam magna. Vivamus augue enim, dictum in nibh non, condimentum tristique lorem. Suspendisse potenti. " +"Sed nibh lacus, auctor ut arcu sollicitudin, posuere tempor urna. Phasellus at odio euismod ipsum congue auctor. " +"Fusce vestibulum elementum nunc, vel feugiat nibh bibendum at. Quisque felis ligula, fermentum a metus ac, pulvinar " +"hendrerit est. Proin vitae tincidunt purus, vestibulum eleifend ipsum. Ut rhoncus et elit ut varius. Praesent eu " +"pharetra tellus. Suspendisse varius, dui quis efficitur fermentum, est lectus ultricies ex, a fermentum orci nunc eu " +"lorem. Integer aliquet nunc ullamcorper lacinia elementum. In cursus tortor nisi, ut pharetra tortor venenatis eu. " +"Duis tincidunt, libero sed varius dictum, neque velit facilisis enim, eget bibendum mi eros et nisl. Nam turpis " +"neque, lobortis eget ante ac, tristique congue lacus. Aenean dictum vitae tortor sed tristique. Donec sodales in " +"arcu ut tristique. Curabitur in facilisis nisi, non vulputate odio. Phasellus ut fringilla nunc, nec dapibus turpis. " +"Sed ut erat tempor sem vulputate gravida at at dui. Aenean id dolor ante. Morbi auctor interdum nisi, id pretium " +"eros ultrices vel. Nulla eget justo fringilla, finibus quam et, accumsan ex. In nisl neque, pharetra nec volutpat " +"at, mattis nec odio. Nam et sapien sed libero lacinia tempor sit amet vitae turpis. Praesent vel porta lacus, porta " +"dignissim nunc. Aenean vitae vulputate purus. Ut at elit arcu. Integer risus neque, varius ac elit maximus, " +"ultricies sagittis nisi. Pellentesque sapien magna, malesuada tincidunt ornare sed, malesuada tempor odio. Morbi id " +"neque velit. Pellentesque at velit sed elit eleifend auctor. Quisque tincidunt tempus justo, venenatis dapibus sem " +"pellentesque quis. Suspendisse finibus feugiat est id consectetur. Nulla commodo, massa auctor vulputate egestas, " +"arcu massa tincidunt leo, quis ullamcorper sapien augue in nibh. Pellentesque ultrices ligula tincidunt urna " +"fringilla, ac ultricies eros convallis. Ut nec massa diam. Maecenas justo nulla, dapibus id justo sollicitudin, " +"fermentum tempor dui. Vivamus laoreet auctor mi non venenatis. Nulla commodo libero ac ex volutpat tincidunt. Donec " +"vestibulum blandit purus bibendum laoreet. Morbi in porta orci. Nam commodo ex eget diam maximus cursus. Proin " +"bibendum quis felis eget euismod. Praesent neque neque, pulvinar eu sem non, gravida ornare tortor. Ut tortor nisi, " +"suscipit in lectus ac, volutpat pretium nisi. Nam rutrum nec dui quis vulputate. Duis in velit enim. Fusce porttitor " +"vitae nisi a tincidunt. Ut enim purus, venenatis ut purus ut, iaculis dignissim ex. Aliquam erat volutpat. " +"Suspendisse potenti. Maecenas ut malesuada elit. Maecenas tellus neque, pulvinar non metus ut, viverra finibus diam. " +"Sed ac porttitor dui. Fusce sit amet ligula metus. Integer id aliquet libero. Sed tempor nisl in porttitor " +"ultricies. Maecenas molestie orci sed sapien molestie interdum non id felis. Nullam sagittis elementum erat in " +"pretium. Nunc pellentesque, ex sit amet fringilla dignissim, augue quam dictum leo, eget tristique turpis mauris sed " +"metus. Praesent vel mauris risus. Etiam eleifend metus ut risus tempor, ac ultrices dolor dictum. Nulla sagittis non " +"urna vitae feugiat. In venenatis arcu vel finibus volutpat. Nam non bibendum magna, nec eleifend ex. Etiam sit amet " +"nisl euismod, mattis nisi quis, commodo nisl. Nunc eget mauris vulputate, cursus neque in, hendrerit ante. Cras non " +"nisl in nisl laoreet aliquam. Sed vestibulum, nunc vitae molestie varius, lectus justo vehicula est, nec placerat " +"ipsum lectus quis leo. Maecenas efficitur semper eros, sed pretium arcu blandit eu. Aliquam eget purus cursus, " +"sollicitudin augue quis, cursus purus. Maecenas sed finibus ligula. Curabitur at diam quis eros mollis semper. Nulla " +"commodo nisi libero, id feugiat nisl tincidunt bibendum. Mauris convallis tincidunt justo eu sodales. Quisque arcu " +"lacus, finibus sed hendrerit at, convallis ut diam. Nulla enim nulla, efficitur quis tincidunt et, pulvinar sit amet " +"enim. Aenean mattis urna id mauris maximus tincidunt. In hac habitasse platea dictumst. Morbi ornare porta congue. " +"Aliquam hendrerit efficitur mi at aliquet. Vivamus rutrum lectus vel turpis volutpat, consectetur congue sem " +"consectetur. Sed rhoncus elit sed orci tincidunt, ut condimentum diam ornare. Nulla facilisi. Ut placerat et massa " +"nec malesuada. Praesent dapibus condimentum augue, at imperdiet lacus facilisis sed. Praesent at metus nunc. Morbi " +"accumsan eros et turpis viverra, nec sagittis odio iaculis. Aenean rhoncus, nibh a consectetur sodales, massa lorem " +"commodo dui, sit amet consequat ex arcu eget augue. Praesent quis nibh urna. Cras eu congue ligula, in ultricies " +"ante. Etiam interdum, est tincidunt euismod sollicitudin, lectus felis mollis ex, pretium fringilla magna lorem non " +"libero. Fusce aliquam tellus eget sodales commodo. Sed sapien lectus, dapibus quis elit at, ultricies tincidunt " +"eros. Nulla suscipit orci sit amet aliquam pellentesque. Cras sed eleifend ligula, quis vehicula ligula. Integer " +"quis tortor in mauris dictum malesuada sed non turpis. Nulla faucibus quis arcu molestie vulputate. Proin fermentum " +"tellus feugiat, imperdiet mi sit amet, tempor sem. Mauris hendrerit augue a vulputate vulputate. Vivamus sagittis at " +"odio non venenatis. Nunc a molestie dolor. Nunc erat nisi, consequat et tristique in, blandit non tortor. Vivamus " +"euismod bibendum augue, ut aliquam lorem mattis quis. Duis laoreet odio at justo ultricies, nec scelerisque enim " +"euismod. Sed eu turpis a lorem cursus feugiat. Duis ultrices molestie nulla non pharetra. Morbi faucibus est auctor " +"faucibus placerat. Donec blandit quis ex ac pulvinar. Vestibulum a consequat quam. Fusce vitae facilisis ex. Etiam a " +"risus eu orci tincidunt interdum. Proin interdum eros nec nibh venenatis, sed luctus sapien tincidunt. In cursus, " +"ante nec dapibus bibendum, augue tortor venenatis felis, eu aliquam erat est vitae diam. Cras lacinia placerat quam, " +"eu finibus purus. Aenean et augue purus. Praesent efficitur ornare magna in cursus. Nunc quis tempor ante, ac " +"accumsan ligula. Nullam elit diam, tempus in sollicitudin at, fermentum tincidunt mi. Vestibulum accumsan, nisi at " +"rutrum scelerisque, justo mauris cursus nulla, finibus cursus nulla elit quis augue. Aliquam lacus ante, ullamcorper " +"quis varius vitae, ullamcorper eget magna. Phasellus mollis nisl eu nulla eleifend, non tempus tellus faucibus. " +"Curabitur molestie eros id eleifend accumsan. Suspendisse tristique sem ante, non blandit eros accumsan ac. Ut sit " +"amet ante justo. Nam condimentum felis quis urna sagittis hendrerit. Cras condimentum est ac massa aliquet finibus. " +"Donec faucibus malesuada fermentum. Aliquam malesuada augue vitae dolor rutrum pellentesque. Nullam vulputate " +"rhoncus porta. Quisque vulputate dignissim felis sit amet aliquet. Nam elementum odio velit, eget fringilla mi " +"dignissim at. Mauris mollis diam orci, vel porta risus tempor a. Nullam quis dolor volutpat, ornare est at, " +"fermentum urna. Fusce mollis nisl a augue condimentum, eu dictum dolor posuere. Mauris et egestas sem. Sed pretium " +"lectus laoreet velit feugiat luctus. Nullam sodales at augue vel semper. Pellentesque vehicula dictum augue, eget " +"tristique orci interdum a. Aenean non est eleifend, tristique urna sed, elementum nunc. Sed consectetur id lorem " +"quis mollis. Ut et blandit velit, et lobortis dolor. Quisque nec odio sed mi ullamcorper pellentesque. Ut vitae " +"eleifend nisi, vitae dapibus est. Vivamus ornare eleifend volutpat. Sed et tincidunt nisi. Praesent maximus risus a " +"bibendum consequat. Vestibulum quis ex vitae ante ultricies ultricies. Maecenas dictum tellus eget enim tincidunt " +"imperdiet. Quisque vel libero gravida, mollis erat id, placerat dolor. Etiam ante eros, bibendum vitae ultricies in, " +"rhoncus nec turpis. Pellentesque gravida nunc sit amet iaculis condimentum. Phasellus in ultricies libero, et " +"maximus justo. Donec ut ultrices elit. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque " +"rhoncus, nunc at iaculis dictum, magna lectus rhoncus augue, vel aliquam sem mauris in metus. Morbi commodo purus " +"mi, ut faucibus dui luctus et. Suspendisse accumsan placerat tortor. Cras dignissim blandit leo, non tincidunt leo. " +"Nulla euismod turpis ac malesuada aliquam. Ut ultrices bibendum elit sed elementum. Donec auctor aliquam vehicula. " +"Mauris lacinia dignissim leo, ullamcorper egestas nibh rutrum eget. In semper sit amet libero eget ultricies. Proin " +"et imperdiet odio. In hac habitasse platea dictumst. In hac habitasse platea dictumst. Integer sed dolor quis tortor " +"pretium euismod at vel dolor. Donec aliquet et urna at porta. Vestibulum tincidunt eget sapien elementum mattis. " +"Proin lacinia faucibus orci, sed eleifend augue mollis et. Vestibulum ante ipsum primis in faucibus orci luctus et " +"ultrices posuere cubilia Curae; Cras pellentesque, dolor eget bibendum tincidunt, turpis ante pharetra tortor, quis " +"interdum tellus tellus sit amet nisl. Nulla convallis tempus egestas. Curabitur quis condimentum metus, eu placerat " +"metus. Nunc ligula nunc, posuere at iaculis nec, convallis id tellus. Curabitur pretium libero lorem, quis placerat " +"nunc fringilla interdum. Vestibulum et finibus ante. Duis quis nisi neque. Curabitur ornare lorem nec ex fringilla, " +"et porta massa consequat. Nulla malesuada turpis nec eleifend tincidunt. Praesent ultricies dolor vitae mauris " +"lacinia tempor. Sed blandit sapien a odio scelerisque consequat. Mauris non dictum eros. Vestibulum ante ipsum " +"primis in faucibus orci luctus et ultrices posuere cubilia Curae; Pellentesque habitant morbi tristique senectus et " +"netus et malesuada fames ac turpis egestas. In ut sollicitudin tellus. Suspendisse ultrices vitae erat non pharetra. " +"Nulla pellentesque at diam venenatis sollicitudin. Vestibulum sed finibus sapien. Curabitur a metus convallis, " +"euismod est id, iaculis nunc. Vestibulum laoreet ornare turpis. Integer rhoncus, felis nec fermentum suscipit, dui " +"lacus sagittis ligula, vitae vestibulum urna elit aliquam est. Sed sit amet mi tortor. Suspendisse a dapibus velit. " +"Cras eget imperdiet turpis. Maecenas at lorem condimentum, elementum augue mattis, rutrum purus. Duis imperdiet " +"pellentesque nunc, eu tristique lectus malesuada commodo. Vivamus aliquet congue eros ac dapibus. Nunc quis " +"porttitor odio. Nulla quis dui luctus, vestibulum enim malesuada, imperdiet elit. Donec facilisis mollis diam ut " +"posuere. Nulla facilisi. Duis nec magna lacus. Vestibulum consequat ut tortor ut ornare. Curabitur nec felis sit " +"amet dui finibus rutrum. Phasellus sit amet lectus nec nisl egestas posuere. Etiam nec euismod magna, vitae " +"ullamcorper enim. Vestibulum pretium cursus semper. Cras vel lorem ut urna molestie elementum. Mauris luctus vel " +"arcu quis egestas. Suspendisse potenti. Nullam viverra sollicitudin lacus luctus sodales. Maecenas eget diam cursus " +"quam tincidunt ultricies vitae nec lacus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec urna " +"sapien, porta a efficitur vitae, imperdiet vel ligula. Nulla volutpat massa sit amet est aliquet, ut iaculis tellus " +"convallis. Sed justo tortor, sodales non nisi quis, laoreet commodo quam. Cras tempus purus a tempor malesuada. " +"Curabitur enim nibh, viverra in enim eget, viverra euismod nunc. Mauris nunc leo, faucibus blandit condimentum nec, " +"rutrum sit amet leo. Quisque nec tortor sed felis pretium imperdiet. Morbi lobortis, dolor nec lobortis maximus, " +"turpis justo aliquet massa, eget aliquet nunc mauris a lectus. Phasellus dignissim, est nec luctus consequat, ex " +"nisi euismod lacus, a viverra nulla eros et est. Suspendisse in egestas dolor. Etiam non placerat lorem. " +"Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam ut enim " +"tristique, porta nulla quis, placerat eros. Integer eget feugiat mi, ac condimentum felis. Fusce auctor ligula ut " +"est placerat efficitur. Nam hendrerit condimentum ante eget tincidunt. Phasellus vel convallis neque. Vivamus sit " +"amet elit eu enim iaculis scelerisque. Donec imperdiet lacus id magna luctus, vitae dapibus quam condimentum. Donec " +"laoreet vehicula tellus. Nullam nec neque at massa ultricies dignissim. Suspendisse potenti. Cras convallis nunc " +"urna, a tempor metus volutpat ut. Fusce viverra lorem vitae quam ullamcorper cursus. Mauris maximus et mi eget " +"tincidunt. Proin molestie suscipit felis at ultricies. Duis varius rhoncus metus vehicula bibendum. Aliquam " +"consequat non tellus at aliquam. Vivamus nec turpis facilisis, dapibus lacus in, congue tortor. Curabitur at " +"interdum mi, sed rhoncus nibh. Morbi facilisis purus laoreet, tincidunt justo sit amet, elementum lectus. Vestibulum " +"pellentesque sem lacus, in condimentum purus consequat at. Integer pharetra rhoncus aliquam. Duis nec sem ac elit " +"suscipit laoreet. Integer vel est commodo, feugiat sapien eget, cursus quam. Aenean elit leo, interdum a posuere " +"nec, laoreet eu magna. Nam sit amet felis faucibus, porttitor justo eget, commodo mi. Maecenas a eleifend nibh. " +"Donec ut ornare augue. Aliquam pellentesque aliquet eros in hendrerit. Nullam consectetur odio id lectus ullamcorper " +"facilisis. Donec pulvinar, magna non sollicitudin commodo, erat lacus egestas massa, a egestas nibh nulla ac lorem. " +"Maecenas at mi posuere, fringilla lectus sed, fringilla eros. Vivamus mattis at magna ac suscipit. Proin varius mi a " +"quam efficitur ullamcorper. Curabitur venenatis turpis lacus, vitae volutpat velit ultricies sed. Sed faucibus id " +"neque in consequat. Nulla imperdiet fermentum placerat. Donec rutrum libero ac lorem commodo pellentesque in tempor " +"augue. Maecenas sodales cursus ex, ac elementum felis consectetur vel. Cras ante nulla, porttitor nec ex non, " +"venenatis consectetur justo. Nam vitae enim eget augue euismod suscipit et in nulla. Morbi eu sollicitudin libero, " +"ut lobortis purus. Pellentesque sodales tempor diam, a luctus dui vehicula tempus. Cum sociis natoque penatibus et " +"magnis dis parturient montes, nascetur ridiculus mus. Vestibulum dignissim sagittis diam ac aliquam. Integer iaculis " +"ac est eu molestie. Vivamus convallis arcu nec rutrum molestie. Vestibulum mollis ipsum neque. Vestibulum " +"condimentum neque quis tellus elementum, in facilisis neque venenatis. Donec quis ultrices risus. Cras mattis felis " +"eget erat iaculis, id scelerisque mauris pharetra. Vivamus condimentum tempor ipsum, porta commodo erat dictum ut. " +"Fusce et ligula sed arcu tincidunt efficitur nec ut felis. Donec eu justo pellentesque, finibus diam quis, iaculis " +"erat. Fusce a tempus urna, at fermentum est. Sed pretium orci dapibus ante laoreet, a consequat erat scelerisque. " +"Etiam nisi tortor, vulputate quis sapien sit amet, lobortis blandit felis. Morbi urna purus, pellentesque quis orci " +"id, suscipit consequat velit. Donec vehicula ipsum felis. Donec at elit ipsum. Fusce purus sapien, convallis quis " +"faucibus et, tempus at dolor. Vivamus commodo sem ac congue imperdiet. Vivamus convallis eget est eu vulputate. " +"Aliquam vehicula augue ac urna imperdiet interdum. Praesent euismod arcu quis purus vestibulum, et placerat metus " +"hendrerit. Fusce semper lacus sit amet ligula scelerisque scelerisque. Vestibulum neque ex, aliquam non lorem a, " +"aliquam fringilla enim. Aenean consectetur vestibulum tortor. Donec et elit consectetur, tincidunt augue feugiat, " +"condimentum diam. In luctus tellus at massa euismod faucibus. Ut tempus dui hendrerit, vehicula ex ut, facilisis " +"lacus. Pellentesque bibendum enim auctor, vulputate justo vel, ultricies est. Praesent interdum turpis in convallis " +"luctus. Duis vel enim venenatis, mollis elit vitae, mattis velit. In eu posuere nibh. Duis a est est. Nam semper " +"tincidunt nulla id dignissim. Fusce consectetur maximus eros quis posuere. Sed efficitur, enim quis ultrices " +"eleifend, est diam commodo dui, nec euismod augue velit sit amet ante. Integer fringilla vehicula faucibus. " +"Curabitur non placerat turpis. Integer malesuada quam eget sapien tristique aliquet. In hac habitasse platea " +"dictumst. Cras dignissim mauris neque, in facilisis nulla pulvinar ac. Phasellus sagittis ligula non sem aliquet " +"iaculis. Integer interdum elit in dolor vehicula, non condimentum justo pretium. Aliquam eget feugiat tellus. " +"Suspendisse condimentum dui at erat elementum semper. Aliquam vitae cursus lorem. Ut vestibulum porttitor purus ut " +"dapibus. Curabitur posuere nunc quis nisi rhoncus, ac mollis enim eleifend. Aenean tristique at justo ut tempor. " +"Proin posuere condimentum arcu ac lobortis. Proin euismod posuere ipsum, nec dignissim velit eleifend gravida. " +"Quisque quis sem mi. Proin scelerisque consequat lectus nec sodales. Fusce id sapien a erat cursus sodales. Morbi ac " +"magna vitae lorem dictum luctus in et lacus. Morbi imperdiet mi interdum, molestie sem in, accumsan leo. Sed lacinia " +"enim et sem egestas, a pulvinar velit ullamcorper. Aenean laoreet, erat eu viverra dictum, eros odio venenatis mi, " +"tincidunt blandit odio mauris id augue. Donec pretium mauris nibh, ut eleifend velit auctor vitae. Morbi tincidunt " +"lacus id ullamcorper egestas. Proin vel porttitor purus, eu fermentum dui. Aliquam a interdum mi. Aliquam ut rhoncus " +"nibh. Morbi nulla libero, commodo quis eros eu, scelerisque gravida ligula. Aliquam sed arcu nunc. Sed egestas " +"hendrerit orci, nec rhoncus arcu fringilla quis. Pellentesque lobortis nulla arcu. Integer aliquam vel quam sed " +"tempor. Morbi viverra tempus risus vel convallis. Cras eget neque ex. Mauris porta, risus at rhoncus hendrerit, " +"libero metus pharetra sapien, quis viverra tortor nunc tincidunt magna. Aenean a tellus ullamcorper, convallis urna " +"quis, suscipit sem. Vivamus eu eleifend est. Duis venenatis metus eget ex consequat molestie. In ullamcorper a dolor " +"vitae feugiat. Morbi ultrices vestibulum venenatis. Phasellus luctus enim id aliquet pharetra. Aenean mauris felis, " +"finibus eu dolor at, tempor sodales diam. Sed nisl nibh, tincidunt quis fringilla vel, congue eu dui. Duis viverra " +"justo eu sem ultricies dignissim. Morbi et sollicitudin erat. Proin id porttitor odio, et sagittis ex. Aenean " +"laoreet leo sit amet risus vestibulum, mollis ultrices tortor porttitor. Sed vestibulum varius ligula quis accumsan. " +"Duis fermentum, dolor iaculis condimentum tincidunt, purus nunc bibendum nibh, ac sodales tortor odio non ante. Sed " +"leo mauris, consequat molestie quam eu, vulputate volutpat metus. Cras fringilla risus sed arcu consequat luctus. " +"Nam malesuada, turpis at luctus blandit, velit elit fringilla metus, eu mollis odio felis id tortor. Aliquam erat " +"tellus, pulvinar nec iaculis et, consequat sit amet diam. Sed vestibulum, leo ut vehicula suscipit, quam justo " +"maximus lectus, nec lobortis urna tortor nec nisi. Vestibulum eget ornare arcu, sed viverra turpis. Sed posuere " +"tellus iaculis, scelerisque dui id, convallis lectus. Aliquam sodales at mi consectetur dignissim. In fringilla, " +"urna id placerat mattis, diam magna commodo dui, at elementum arcu elit et libero. Duis venenatis vulputate nisl " +"congue pharetra. Fusce sapien velit, cursus a consectetur quis, auctor gravida sem. Maecenas malesuada metus quis " +"elit congue accumsan. Vivamus scelerisque euismod malesuada. Vestibulum purus metus, tempor eget faucibus a, cursus " +"eu arcu. Morbi dictum urna vitae velit pellentesque facilisis. Sed arcu est, tempor ac turpis sit amet, ultricies " +"venenatis augue. Nunc laoreet leo gravida facilisis dapibus. Aliquam convallis ullamcorper felis, sit amet tempor " +"libero euismod sit amet. Quisque leo augue, finibus et euismod non, venenatis sed libero. Cras pharetra rhoncus " +"odio, in pharetra lacus porttitor scelerisque. Maecenas eleifend felis vitae diam blandit viverra. Fusce at " +"ultricies arcu, pharetra finibus enim. Etiam pellentesque semper ligula, sed tincidunt purus. Sed fermentum metus " +"varius, aliquet libero eget, vehicula erat. Sed ac finibus metus. Pellentesque libero leo, semper et eros nec, " +"gravida condimentum urna. Cras nec turpis convallis, efficitur lacus at, ultricies ex. Fusce eu neque elementum leo " +"gravida semper. Duis sed tellus vitae magna fringilla maximus ac ut nisl. Integer id ligula ullamcorper, ultricies " +"quam sit amet, ullamcorper diam. Maecenas rhoncus nulla eu dui vulputate scelerisque. Vestibulum porttitor eget nibh " +"a mattis. Mauris tempus at urna blandit dignissim. Proin turpis leo, mattis ut turpis eget, aliquet tempor ante. " +"Nunc in mollis nunc, et interdum nisi. Cras tristique sollicitudin tortor sit amet ultrices. Proin rhoncus neque " +"urna. Maecenas bibendum, massa sit amet suscipit suscipit, justo tortor maximus dolor, posuere facilisis nisi tellus " +"elementum diam. Quisque id eros vel lectus malesuada tincidunt. Donec at orci ac ligula venenatis dignissim sit amet " +"nec purus. Sed eu neque finibus, tristique ex a, feugiat ante. Pellentesque tincidunt luctus mollis. Nullam blandit " +"faucibus gravida. Ut sit amet malesuada nibh, vel tincidunt ipsum. Donec suscipit lorem in dui luctus, viverra " +"imperdiet magna placerat. Pellentesque venenatis eros quis urna efficitur facilisis. Cras ligula magna, tempus " +"facilisis tincidunt at, varius quis lectus. Sed quam neque, facilisis vel facilisis vel, lobortis ac orci. Nullam " +"pretium interdum erat ac ultrices. Etiam enim mauris, vehicula nec rhoncus quis, volutpat vel erat. Morbi imperdiet " +"rhoncus rutrum. Nullam auctor condimentum diam nec faucibus. Etiam sit amet porta nulla, sit amet lobortis enim. " +"Aenean tincidunt condimentum accumsan. Vestibulum mollis diam risus, vitae ornare enim iaculis non. Nullam vitae " +"risus tristique, imperdiet augue ut, egestas dolor. Sed sit amet leo eu diam commodo vestibulum id in dolor. Vivamus " +"tristique molestie faucibus. Duis tempor porttitor turpis ac consectetur. Curabitur condimentum, ipsum eu dignissim " +"semper, ipsum erat pretium quam, ut maximus erat ligula eu felis. Sed viverra, mauris id tempus tempor, nisi leo " +"consectetur arcu, ac vulputate lorem mauris non sapien. Maecenas rhoncus magna mauris, in luctus nulla dapibus at. " +"Sed magna est, ultrices sit amet erat nec, dapibus lacinia massa. Morbi cursus ex in elit auctor egestas. Quisque id " +"placerat nibh, at mollis tortor. Proin fringilla sodales sapien, ac ullamcorper sem bibendum eget. Donec dui ligula, " +"viverra eget leo ac, tincidunt fringilla mauris. Quisque vel lectus eget metus feugiat laoreet. Morbi eget " +"vestibulum enim, ac ultricies lorem. Nam at mollis magna. Etiam vitae orci eu leo facilisis vestibulum. Ut sed " +"turpis ut nibh iaculis rhoncus. Phasellus sit amet risus pellentesque, gravida eros a, porta nibh. Suspendisse at " +"tincidunt ligula. Vivamus id libero diam. Morbi viverra ipsum turpis, in ullamcorper enim pellentesque nec. Sed " +"ultricies, lectus quis pellentesque sodales, arcu diam commodo massa, a vestibulum purus sapien eget risus. Duis " +"rhoncus in velit in dignissim. Aliquam sit amet metus in quam finibus cursus. Pellentesque eget aliquam justo. Fusce " +"imperdiet, tellus non venenatis facilisis, diam mi lobortis dolor, at consectetur est massa id elit. Vivamus ante " +"ex, faucibus et mollis eget, dignissim vel massa. Duis ultricies diam commodo purus facilisis pharetra. Curabitur " +"pretium massa sed enim vehicula, id vehicula neque vehicula. In quis lectus non mauris pulvinar fermentum. Aliquam " +"condimentum aliquet dui et congue. Maecenas quis augue eget leo gravida aliquet. Praesent sit amet fermentum odio, " +"ut placerat nulla. Curabitur sit amet iaculis erat, eu volutpat odio. Ut iaculis ex quis tempus commodo. " +"Pellentesque cursus eros at velit vulputate, id luctus massa pretium. Morbi ex dui, sodales id finibus id, aliquet a " +"justo. Maecenas semper leo eget dolor rutrum, at imperdiet nibh eleifend. Aliquam eget purus tortor. Cras rutrum " +"tortor massa, vel bibendum nunc aliquam vel. Nullam vestibulum, metus vel fermentum elementum, nulla sapien egestas " +"justo, ac feugiat ex justo nec eros. Donec sit amet nibh mollis, commodo quam sit amet, semper magna. In tortor " +"magna, elementum nec auctor sed, pellentesque at augue. Sed gravida arcu ac aliquet convallis. Nulla facilisi. Duis " +"nunc quam, gravida non interdum id, cursus ac leo. Suspendisse vel ipsum nisl. Aliquam at gravida libero. Maecenas " +"sit amet efficitur orci. Fusce id vehicula sapien. Proin euismod diam non laoreet ultricies. Nunc ullamcorper, nibh " +"id cursus vehicula, ex purus tempor urna, et euismod orci est sed elit. Duis ut blandit mauris. Ut blandit cursus " +"eros, sed laoreet nisl efficitur ac. Phasellus dui elit, fringilla sit amet cursus nec, pharetra quis odio. Ut ut " +"lorem sit amet sem dapibus accumsan. Aenean a laoreet dolor. Donec eu laoreet velit. Etiam id nisi vel nibh dapibus " +"congue a quis odio. Donec velit risus, semper quis porta non, elementum quis lorem. Interdum et malesuada fames ac " +"ante ipsum primis in faucibus. Nullam sit amet dolor magna. Maecenas quis sapien sit amet est pulvinar lobortis " +"efficitur cursus orci. Phasellus tristique mauris lorem, eu ultricies justo ornare condimentum. Integer urna enim, " +"lobortis id malesuada ut, mattis eget libero. Sed commodo tincidunt eleifend. Fusce sed velit ut dui pellentesque " +"pellentesque eget vel diam. Aenean nec turpis at tortor consectetur consectetur. Vestibulum ultrices elit at nisl " +"pellentesque molestie. Maecenas diam dolor, faucibus eget posuere ut, sodales ut eros. Nam vulputate mollis diam nec " +"gravida. Nam et ullamcorper diam. Aenean non nulla non lorem ullamcorper sagittis non quis erat. Pellentesque " +"habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In hac habitasse platea dictumst. " +"Donec quis mauris ac nibh vestibulum eleifend placerat sed lacus. Suspendisse mi elit, viverra non velit ut, " +"tincidunt tempus felis. Fusce ullamcorper, arcu nec aliquet porttitor, odio lacus mollis mi, id malesuada tortor " +"velit aliquet turpis. Sed hendrerit felis nec faucibus ornare. Nulla ut metus eget augue malesuada posuere eget eu " +"tortor. Cras ultrices odio sit amet porttitor vehicula. Sed vulputate leo vitae justo viverra, nec volutpat eros " +"consectetur. Nunc nunc tellus, porta in arcu in, vulputate ultricies tellus. Fusce commodo efficitur lorem, sit amet " +"lacinia sapien sollicitudin at. Etiam aliquet non mi vitae ornare. Cras condimentum imperdiet elit eu dictum. Donec " +"sed enim sed massa tempor porta et sit amet felis. Nam interdum ornare sem, in tincidunt risus consectetur vel. Ut " +"convallis purus mauris, in consequat ligula ullamcorper ut. Quisque elit ipsum, accumsan eget ligula vitae, " +"sollicitudin luctus tellus. Nunc pretium turpis ligula, id dignissim lorem suscipit eu. Nulla facilisi. Sed lectus " +"odio, vehicula vel vulputate id, ultrices non ipsum. Donec arcu quam, consequat eget aliquet sit amet, ullamcorper " +"non nibh. Etiam finibus, mi id lobortis sagittis, leo leo lobortis lectus, sit amet aliquam dui odio sit amet massa. " +"Suspendisse iaculis urna ac lectus gravida, iaculis efficitur tellus hendrerit. Sed tellus enim, condimentum in " +"augue eget, sagittis ullamcorper sem. Suspendisse vitae aliquet libero. Aenean quis purus in sapien dapibus " +"suscipit. Sed commodo nunc in lacus bibendum, vel tincidunt ante ornare. Ut tristique luctus volutpat. Class aptent " +"taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Quisque a ultricies orci, eu porta " +"odio. Vivamus sapien arcu, ultrices vel dui ut, luctus viverra purus. Praesent fringilla sed odio quis pretium. " +"Vestibulum ullamcorper nisi tortor, id sollicitudin lectus tempor a. Ut malesuada sapien eu sapien posuere, non " +"euismod eros porta. Nunc vel tincidunt ligula. Cras dolor ante, tristique tempor metus quis, mollis vulputate orci. " +"Curabitur vitae nisl euismod, elementum purus vel, dictum lorem. Nunc eu mauris at metus porttitor dignissim ut eu " +"neque. In tempor rhoncus neque sit amet commodo. Maecenas sed lacus semper, tempus enim ac, fermentum lorem. Nullam " +"sollicitudin convallis turpis. Curabitur finibus placerat viverra. Pellentesque convallis condimentum tortor id " +"efficitur. Proin semper pretium est, et vehicula ex cursus a. Nam ut felis purus. Phasellus eget felis eget leo " +"dapibus vestibulum. Nulla eleifend malesuada turpis, quis faucibus eros. Nam aliquet euismod viverra. Ut quis semper " +"felis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Pellentesque at nulla " +"arcu. Integer ut tellus ac sapien maximus tincidunt sed vitae risus. Nulla viverra, nibh eget eleifend aliquam, quam " +"quam tempor massa, eu semper ipsum lacus in turpis. Nulla sed purus enim. Nullam sed fermentum ipsum. Sed dui nisi, " +"elementum a auctor at, ultrices et nibh. Phasellus aliquam nulla ut lacinia accumsan. Phasellus sed arcu ligula. " +"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam fermentum magna vitae dui sagittis tempor. Vivamus " +"eu ligula blandit, imperdiet arcu at, rutrum sem. Aliquam erat volutpat. Quisque luctus enim quis volutpat lobortis. " +"Vestibulum eget sodales libero. Aenean at condimentum est. Proin eget massa vel nulla efficitur tempor eget at enim. " +"Integer enim sapien, ornare luctus nisl non, pretium facilisis ex. Donec pretium ligula ligula, a facilisis turpis " +"hendrerit at. Nullam eget malesuada justo, at molestie quam. Sed consequat massa eu faucibus maximus. Curabitur " +"placerat orci sapien, sit amet semper magna sodales non. Ut fermentum accumsan odio in consectetur. Morbi neque mi, " +"vulputate nec mi ut, cursus scelerisque lectus. Nulla sapien enim, finibus id ipsum luctus, consequat ullamcorper " +"lectus. Sed volutpat sed massa in sodales. Morbi lacinia diam eu commodo vulputate. Fusce aliquet pulvinar dolor in " +"egestas. Fusce molestie commodo leo eu ultricies. Nulla mollis rhoncus pharetra. Pellentesque rutrum mauris ac lorem " +"posuere, a eleifend mi rutrum. Nulla porta turpis aliquet felis congue rutrum. Fusce quis arcu in sem placerat " +"condimentum a ut turpis. Quisque quis porttitor nulla. Donec sit amet quam tincidunt, pulvinar erat id, molestie " +"dolor. Praesent luctus vitae nunc vitae pellentesque. Praesent faucibus sed urna ut lacinia. Vivamus id justo quis " +"dolor porta rutrum nec nec odio. Cras euismod tortor quis diam ultrices, eu mattis nisi consectetur. Fusce mattis " +"nisi vel condimentum molestie. Fusce fringilla ut nibh volutpat elementum. Mauris posuere consectetur leo a aliquet. " +"Donec quis sodales sapien. Maecenas ut felis tempus, eleifend mauris et, faucibus mi. Quisque fringilla orci arcu, " +"sit amet porta risus hendrerit non. Aenean id sem nisi. Nullam non nisl vestibulum, pellentesque nisl et, imperdiet " +"ligula. Sed laoreet fringilla felis. Proin ac dolor viverra tellus mollis aliquet eget et neque. Suspendisse mattis " +"nulla vitae nulla sagittis blandit. Sed at tortor rutrum, ornare magna nec, pellentesque nisi. Etiam non aliquet " +"tellus. Aliquam at ex suscipit, posuere sem sit amet, tincidunt."; + +#endif /* __BLENDER_TESTING_BLI_RESSOURCE_STRING_H__ */ diff --git a/tests/gtests/blenlib/CMakeLists.txt b/tests/gtests/blenlib/CMakeLists.txt index 3a86d3f770d..152b65617a4 100644 --- a/tests/gtests/blenlib/CMakeLists.txt +++ b/tests/gtests/blenlib/CMakeLists.txt @@ -43,3 +43,6 @@ BLENDER_TEST(BLI_path_util "bf_blenlib;extern_wcwidth;${ZLIB_LIBRARIES}") BLENDER_TEST(BLI_polyfill2d "bf_blenlib") BLENDER_TEST(BLI_listbase "bf_blenlib") BLENDER_TEST(BLI_hash_mm2a "bf_blenlib") +BLENDER_TEST(BLI_ghash "bf_blenlib") + +BLENDER_TEST(BLI_ghash_performance "bf_blenlib") |