From ad95cb72bc6e76a2b3dc0996293bfb8379084e38 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 19 Mar 2015 20:09:34 +1100 Subject: Transform: Shift for precision PET adjustment Patch T36753 by @hjaarnio --- source/blender/editors/transform/transform.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 061d00424b8..b562eb2e5f1 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; -- cgit v1.2.3 From 52d72a37cd85dc8c23482d4102abb86645a221bb Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 19 Mar 2015 20:34:02 +1100 Subject: Cleanup --- build_files/cmake/Modules/FindPCRE.cmake | 1 - source/blender/editors/object/object_shapekey.c | 4 ++-- source/blender/imbuf/intern/bmp.c | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) 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/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c index fb9687da6df..fb0b2ed231c 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); 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 */ -- cgit v1.2.3 From c69b5e027642a3f82f869062e79ee532bec1b723 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 19 Mar 2015 16:04:49 +0500 Subject: Fix T44040: Blender crashes when nodes are muted It was actually an old issue with wrong conversion happening for muted nodes, which wasn't visible before memory optimization commit. This is to be backported to the final release. --- source/blender/compositor/intern/COM_NodeOperationBuilder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 */ -- cgit v1.2.3 From 15b37a4a4dc8e3684aa53140e7285eb3deaec52b Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 19 Mar 2015 18:28:49 +0500 Subject: Speedup for constraints update from python script General idea is to avoid actual calculation from property update() callback and tag things for update later instead. That said, pose constraint flags are now tagged for update and handled as a part of object update. In the new depsgraph it'll be a nice dedicated operation node. Also avoid updating disabled flags for all the modifiers. This part of the path is not totally optimal since it'll still need to iterate over bones in order to get pchan, but to optimize it further would be nice to find a way to avoid pchan requirement all together. Reviewers: campbellbarton Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D1191 --- source/blender/blenkernel/BKE_action.h | 3 + source/blender/blenkernel/intern/action.c | 6 + source/blender/blenkernel/intern/object.c | 6 +- source/blender/editors/include/ED_object.h | 3 + source/blender/editors/object/object_constraint.c | 512 ++++++++++++---------- source/blender/makesdna/DNA_action_types.h | 4 +- source/blender/makesrna/intern/rna_constraint.c | 4 +- 7 files changed, 309 insertions(+), 229 deletions(-) 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/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/object.c b/source/blender/blenkernel/intern/object.c index d63e2516805..08a74d0c6cd 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -3012,8 +3012,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/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/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/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/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) -- cgit v1.2.3 From 7c65b75d01c6e92458dd2deb937c39641f05792f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 20 Mar 2015 00:41:29 +1100 Subject: Correct recent armature symmetrize Duplicating part of a chain could have invalid 'connected' flag. --- source/blender/editors/armature/armature_add.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index 5cc2101650e..c2158f02ae3 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -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; -- cgit v1.2.3 From 93048873f10bab6ebcee9467a1e462f410f1bf12 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 19 Mar 2015 19:10:09 +0500 Subject: Fix compilation error of blenderplayer after recent constraint commit --- source/blenderplayer/bad_level_call_stubs/stubs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index 0856b50c19b..893eae8485a 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 -- cgit v1.2.3 From 3e9947c4d4714a4752c768bb78fe6d227fc6be97 Mon Sep 17 00:00:00 2001 From: Antony Riakiotakis Date: Tue, 3 Mar 2015 17:47:31 +0100 Subject: Depth of field high quality: A new checkbox "High quality" is provided in camera settings to enable this. This creates a depth of field that is much closer to the rendered result and even supports aperture blades in the effect, but it's more expensive too. There are optimizations to do here since the technique is very fill rate heavy. People, be careful, this -can- lock up your screen if depth of field blurring is too extreme. Technical details: This uses geometry shaders + instancing and is an adaptation of techniques gathered from http://bartwronski.com/2014/04/07/bokeh-depth-of-field-going-insane- http://advances.realtimerendering.com/s2011/SousaSchulzKazyan%20- %20in%20Real-Time%20Rendering%20Course).ppt TODOs: * Support dithering to minimize banding. * Optimize fill rate in geometry shader. --- intern/cycles/blender/addon/ui.py | 5 +- .../startup/bl_ui/properties_data_camera.py | 3 + source/blender/gpu/CMakeLists.txt | 6 + source/blender/gpu/GPU_compositing.h | 9 +- source/blender/gpu/GPU_extensions.h | 13 +- source/blender/gpu/intern/gpu_codegen.c | 2 +- source/blender/gpu/intern/gpu_compositing.c | 763 ++++++++++++++------- source/blender/gpu/intern/gpu_extensions.c | 167 ++++- source/blender/gpu/intern/gpu_simple_shader.c | 1 + .../gpu/shaders/gpu_shader_fx_dof_hq_frag.glsl | 166 +++++ .../gpu/shaders/gpu_shader_fx_dof_hq_geo.glsl | 50 ++ .../gpu/shaders/gpu_shader_fx_dof_hq_vert.glsl | 58 ++ source/blender/makesdna/DNA_gpu_types.h | 2 + source/blender/makesrna/intern/rna_scene.c | 24 + 14 files changed, 981 insertions(+), 288 deletions(-) create mode 100644 source/blender/gpu/shaders/gpu_shader_fx_dof_hq_frag.glsl create mode 100644 source/blender/gpu/shaders/gpu_shader_fx_dof_hq_geo.glsl create mode 100644 source/blender/gpu/shaders/gpu_shader_fx_dof_hq_vert.glsl diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 63518d7fdb6..b0337a69e7d 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, "high_quality") + if dof_options.high_quality: + col.prop(dof_options, "num_blades") + col = split.column() col.label("Aperture:") diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py index 106e31ea89a..2097e82f992 100644 --- a/release/scripts/startup/bl_ui/properties_data_camera.py +++ b/release/scripts/startup/bl_ui/properties_data_camera.py @@ -187,6 +187,9 @@ class DATA_PT_camera_dof(CameraButtonsPanel, Panel): col = split.column() col.prop(dof_options, "fstop") + col.prop(dof_options, "high_quality") + if dof_options.high_quality: + col.prop(dof_options, "num_blades") sub = col.column() sub.active = cam.dof_object is None sub.prop(cam, "dof_distance", text="Distance") 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..aed1a88938a 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); 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/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 40c9ec0d862..fcfb68d4629 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); /* failed? */ if (!shader) { diff --git a/source/blender/gpu/intern/gpu_compositing.c b/source/blender/gpu/intern/gpu_compositing.c index 511167b775a..bfa938d0fed 100644 --- a/source/blender/gpu/intern/gpu_compositing.c +++ b/source/blender/gpu/intern/gpu_compositing.c @@ -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; @@ -192,16 +195,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 +215,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 +245,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) { @@ -279,7 +279,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 +356,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 */ @@ -544,14 +598,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(); @@ -643,7 +697,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 +722,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 +738,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 +763,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,243 +775,456 @@ 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(); + 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_pass1, "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); + + 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 */ + glDrawArraysInstancedEXT(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 */ + glDrawArraysInstancedEXT(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_pass1, "viewvecs"); + depth_uniform = GPU_shader_get_uniform(dof_shader_pass1, "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_pass1, 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(); + 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; + } } } @@ -976,6 +1242,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..24b54a3af37 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_EXT_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_EXT_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) { 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,34 @@ 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; + if (libcode) source[num_source++] = libcode; + 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; + } + } + + #if 0 if (lib && lib->lib) glAttachObjectARB(shader->object, lib->lib); @@ -1553,6 +1608,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 +1704,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 +1727,16 @@ 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); + + /* relink so settings can take effect (sucks but should only be done right after compilation so...) */ + glLinkProgramARB(shader->object); +} + void GPU_shader_uniform_texture(GPUShader *UNUSED(shader), int location, GPUTexture *tex) { GLenum arbnumber; @@ -1703,12 +1784,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); 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); retval = GG.shaders.sep_gaussian_blur; break; } @@ -1737,38 +1818,60 @@ 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); 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); 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); 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); 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); 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); + 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); + 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); + + if (shader) { + GG.shaders.fx_shaders[offset] = shader; + GPU_shader_geometry_stage_primitive_io(shader, GL_POINTS, GL_TRIANGLE_STRIP, 4); + } + 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); 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); } } diff --git a/source/blender/gpu/intern/gpu_simple_shader.c b/source/blender/gpu/intern/gpu_simple_shader.c index 3fa5975ef15..60d4a2f2875 100644 --- a/source/blender/gpu/intern/gpu_simple_shader.c +++ b/source/blender/gpu/intern/gpu_simple_shader.c @@ -151,6 +151,7 @@ static GPUShader *gpu_simple_shader(int options) shader = GPU_shader_create( datatoc_gpu_shader_simple_vert_glsl, datatoc_gpu_shader_simple_frag_glsl, + NULL, NULL, defines); if (shader) { 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..e2d3ab36ec8 --- /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 * 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) + 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) { + 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..e8c505bd15f --- /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(1.0 / float(rendertargetdim.x), 1.0 / float(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) * pixel + 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/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/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 629fbbf66e9..4214c830e4c 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,17 @@ 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, "num_blades", PROP_INT, PROP_NONE); + 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, "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) -- cgit v1.2.3 From 881e05fc54058bc04b217817808315b2f9691c2a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 20 Mar 2015 01:37:29 +1100 Subject: Armature select-hierarchy now uses connected bones Traverse down connected child bones (when available) Also remove redundant bone loops. --- source/blender/editors/armature/armature_select.c | 105 +++++++++++----------- source/blender/editors/armature/pose_select.c | 102 ++++++++++----------- 2 files changed, 103 insertions(+), 104 deletions(-) diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 581dd00e285..5061ba9a812 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -962,75 +962,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_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); -- cgit v1.2.3 From cfdd27381c6730dc0d8d4bb51c132b29337b8af5 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 19 Mar 2015 17:36:59 +0100 Subject: GHash - code reorganization, performance enhancements, add a few missing utils to API. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch is the root of the GHash rework, all other diff will be based on it: Reduce average load from 3.0 to 0.75 ---------------------------------- This is the big performance booster part, e.g. makes tracing a dyntopo stroke between 25% and 30% faster. Not much to say about it, aside that it obviously increase memory footprint (about 25% - 30% too). Add optional shrinking ---------------------------------- I.e. ghashes/gsets can now shrink their buckets array when you remove enough entries. This remains optional and OFF by default. Add code to use masking instead of modulo ---------------------------------- Buckets indices are obtained from hashes by “reducing” the hash value into the valid bucket range. This can be done either by bit-masking, or using modulo operation. The former is quicker, but requires real hashes, while the later is slower (average 10% impact on ghash operations) but can also be used as a 'fake' hashing on raw values, like e.g. indices. In Blender currently not all ghash usages actually hash their keys, so we stick to modulo for now (masking is ifdef’ed out), we may however investigate the benefits of switching to masking with systematic very basic hashing later… Add various missing API helpers ---------------------------------- I.e. a way to deep-copy a ghash/gset, and a way to (re-)reserve entries (i.e. manually grow or shrink the ghash after its creation). Various code refactoring ---------------------------------- * Get rid of the 'hack' regarding ghash size when used as gset (it’s simpler and safer to have two structs defined here, and cast pointers as needed). * Various re-shuffle and factorization in low-level internal code. * Some work on hashing helpers, introducing some murmur2a-based hashing too. Thanks a bunch to Campbell for the extensive review work. :) Reviewers: sergey, campbellbarton Subscribers: psy-fi, lukastoenne Projects: #bf_blender Maniphest Tasks: T43766 Differential Revision: https://developer.blender.org/D1178 --- source/blender/blenlib/BLI_ghash.h | 53 +- source/blender/blenlib/BLI_hash_mm2a.h | 2 + source/blender/blenlib/intern/BLI_ghash.c | 777 +++++++++++++++++++------- source/blender/blenlib/intern/hash_mm2a.c | 44 +- source/blender/makesdna/intern/CMakeLists.txt | 1 + 5 files changed, 667 insertions(+), 210 deletions(-) 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/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c index 5360ea744a1..a2233c38270 100644 --- a/source/blender/blenlib/intern/BLI_ghash.c +++ b/source/blender/blenlib/intern/BLI_ghash.c @@ -36,17 +36,23 @@ #include #include +#include #include #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); } /** - * Check if the number of items in the GHash is large enough to require more buckets. + * Get the full hash for an entry. */ -BLI_INLINE bool ghash_test_expand_buckets(const unsigned int nentries, const unsigned int nbuckets) +BLI_INLINE unsigned int ghash_entryhash(GHash *gh, const Entry *e) { - return (nentries > nbuckets * 3); + return gh->hashfp(e->key); } /** - * Expand buckets to the next size up. + * Get the bucket-hash for an already-computed full hash. */ -BLI_INLINE void ghash_resize_buckets(GHash *gh, const unsigned int nbuckets) +BLI_INLINE unsigned int ghash_bucket_index(GHash *gh, const unsigned int hash) +{ +#ifdef GHASH_USE_MODULO_BUCKETS + return hash % gh->nbuckets; +#else + return full_hash & gh->bucket_mask; +#endif +} + +/** + * Expand buckets to the next size up or down. + */ +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; +} - for (e = gh->buckets[hash]; e; e = e->next) { +/** + * 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; + + /* 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; + gh->buckets = NULL; + gh->flag = flag; - /* if we have reserved the number of elements that this hash will contain */ - if (nentries_reserve) { - ghash_buckets_reserve(gh, nentries_reserve); - } - - 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; } /** @@ -278,20 +548,56 @@ static void ghash_free_cb(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP va unsigned int i; 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); } /** @@ -324,6 +628,23 @@ GHash *BLI_ghash_new(GHashHashFP hashfp, GHashCmpFP cmpfp, const char *info) return BLI_ghash_new_ex(hashfp, cmpfp, info, 0); } +/** + * 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. */ @@ -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); } - sum += count * (count + 1); + *r_variance = sum / (double)(gh->nbuckets - 1); } - return ((double)sum * (double)gh->nbuckets / - ((double)gh->nentries * (gh->nentries + 2 * 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; + } + 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..87ba542e147 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/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}") -- cgit v1.2.3 From 2941b4ad9bd7ee876575b6b4bbe628a9dfaedaa5 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 19 Mar 2015 18:06:15 +0100 Subject: BLI GHash: add some basic gtests. We could likely add much more, but those already covers basic behavior and should be able to catch most errors when editing this code. Also added some performances tests as well (timing ghash insert/lookup under heavy loads, for different kinds of keys). --- tests/gtests/blenlib/BLI_ghash_performance_test.cc | 415 ++++++++++++++ tests/gtests/blenlib/BLI_ghash_test.cc | 158 ++++++ tests/gtests/blenlib/BLI_ressource_strings.h | 602 +++++++++++++++++++++ tests/gtests/blenlib/CMakeLists.txt | 3 + 4 files changed, 1178 insertions(+) create mode 100644 tests/gtests/blenlib/BLI_ghash_performance_test.cc create mode 100644 tests/gtests/blenlib/BLI_ghash_test.cc create mode 100644 tests/gtests/blenlib/BLI_ressource_strings.h 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") -- cgit v1.2.3 From bd1e5eeab7a8459755b59672f494609f84ab8302 Mon Sep 17 00:00:00 2001 From: Antony Riakiotakis Date: Thu, 19 Mar 2015 20:04:55 +0100 Subject: Blender will now use trilinear filtering by default --- source/blender/windowmanager/intern/wm_init_exit.c | 1 + 1 file changed, 1 insertion(+) 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); -- cgit v1.2.3 From eee538f0d262a430cc622bfe17eef7dba9b251c7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 20 Mar 2015 04:00:05 +1100 Subject: UI: minor re-organization of dof options --- release/scripts/startup/bl_ui/properties_data_camera.py | 10 ++++++---- source/blender/makesrna/intern/rna_scene.c | 5 +++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py index 2097e82f992..6a0d680df53 100644 --- a/release/scripts/startup/bl_ui/properties_data_camera.py +++ b/release/scripts/startup/bl_ui/properties_data_camera.py @@ -184,16 +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") - col.prop(dof_options, "high_quality") - if dof_options.high_quality: - col.prop(dof_options, "num_blades") + 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/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 4214c830e4c..2e941487e98 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3922,13 +3922,14 @@ static void rna_def_gpu_dof_fx(BlenderRNA *brna) 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, "num_blades", PROP_INT, PROP_NONE); + 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, "high_quality", PROP_BOOLEAN, PROP_NONE); + 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); -- cgit v1.2.3 From 81472aff2be5a8059e046436b4d1810659032716 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 20 Mar 2015 06:07:22 +1100 Subject: Remove deleted list for palette colors was used because of UI memory access only. --- source/blender/blenkernel/BKE_paint.h | 2 -- source/blender/blenkernel/intern/paint.c | 21 ++------------------- source/blender/blenloader/intern/readfile.c | 1 - .../blender/editors/interface/interface_handlers.c | 6 ++++++ .../blender/editors/interface/interface_templates.c | 3 --- source/blender/makesdna/DNA_brush_types.h | 1 - source/blender/makesrna/intern/rna_palette.c | 2 +- 7 files changed, 9 insertions(+), 27 deletions(-) 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/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/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 6b4d45a85dc..639ee646a67 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1950,7 +1950,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/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 b3c31a1a644..7db734ee919 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -2398,9 +2398,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/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/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); } -- cgit v1.2.3 From ea12b87afdb3c57bba2dbf8ba6bf457f83dc8dd0 Mon Sep 17 00:00:00 2001 From: Antony Riakiotakis Date: Thu, 19 Mar 2015 20:48:43 +0100 Subject: Fix cycles dof settings --- intern/cycles/blender/addon/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index b0337a69e7d..6b7e03320bc 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -459,7 +459,7 @@ class CyclesCamera_PT_dof(CyclesButtonsPanel, Panel): col.prop(dof_options, "fstop") col.prop(dof_options, "high_quality") if dof_options.high_quality: - col.prop(dof_options, "num_blades") + col.prop(dof_options, "blades") col = split.column() -- cgit v1.2.3 From 05e3c261a44b42181aadc8af9d603db2cb721b20 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 19 Mar 2015 20:47:38 +0100 Subject: Fix T43989: Sequencer - Ctrl snapping a sequencer strip does not work if you specify the x axis. New 'strip' snapping was simply not computed in case of constrained transform, hence init '0' value was used as frame offset in this case. This commit reorganizes a bit that snapping, to keep it more 'confined' into `snapSequenceBounds()` dedicated function. It still needs a minor hack (setting snapping mode to something else than defualt `SCE_SNAP_MODE_INCREMENT`, to avoid this snapping to be called by contraint code). Thanks to Antony for review and enhancements. This fix should be backported to 2.74. --- source/blender/editors/transform/transform.c | 25 ++++++----------------- source/blender/editors/transform/transform.h | 2 +- source/blender/editors/transform/transform_snap.c | 11 +++++++--- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index b562eb2e5f1..f4cb8f724bb 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -7311,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]; @@ -7352,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); } @@ -7361,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..1f2a06abfb0 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,15 +2435,16 @@ 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; int mframe; + TransData *td = t->data; 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 +2455,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) -- cgit v1.2.3 From fd559ed94f7ff522e2657cdc7e7153740c480659 Mon Sep 17 00:00:00 2001 From: Antony Riakiotakis Date: Thu, 19 Mar 2015 21:10:05 +0100 Subject: Missed this last commit. --- intern/cycles/blender/addon/ui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 6b7e03320bc..1097b79ffa5 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -457,8 +457,8 @@ 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, "high_quality") - if dof_options.high_quality: + col.prop(dof_options, "use_high_quality") + if dof_options.use_high_quality: col.prop(dof_options, "blades") col = split.column() -- cgit v1.2.3 From f51a3aedc9540e0bcda4f16deec22e9aa722ef24 Mon Sep 17 00:00:00 2001 From: Antony Riakiotakis Date: Thu, 19 Mar 2015 21:11:33 +0100 Subject: Cleanup remove unused variable --- source/blender/editors/transform/transform_snap.c | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 1f2a06abfb0..fd189e91141 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -2440,7 +2440,6 @@ void snapSequenceBounds(TransInfo *t, const int mval[2]) float xmouse, ymouse; int frame; int mframe; - TransData *td = t->data; TransSeq *ts = t->customData; /* reuse increment, strictly speaking could be another snap mode, but leave as is */ if (!(t->modifiers & MOD_SNAP_INVERT)) -- cgit v1.2.3 From cf29010bd4085b2ab76c58a30d1148265a879d69 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 20 Mar 2015 15:26:47 +1100 Subject: Cleanup: warnings bit-shift int overflow --- source/blender/blenkernel/intern/screen.c | 4 ++-- source/blender/editors/armature/pose_edit.c | 5 +++-- source/blender/editors/space_view3d/view3d_header.c | 8 ++++---- source/blender/makesrna/intern/makesrna.c | 2 +- source/blender/makesrna/intern/rna_armature.c | 8 ++++---- source/blender/makesrna/intern/rna_controller.c | 2 +- source/blender/makesrna/intern/rna_space.c | 4 ++-- 7 files changed, 17 insertions(+), 16 deletions(-) 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/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/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/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 894d5ef20a8..5f8639358b7 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<%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_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_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++; -- cgit v1.2.3 From 35ea7efbca4f9b5b19fb0c277fc225f430e1ee91 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 20 Mar 2015 15:50:56 +1100 Subject: Add missing update adding a shape-key Noticeable when pin is enabled. --- source/blender/editors/object/object_shapekey.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c index fb0b2ed231c..674eb25942f 100644 --- a/source/blender/editors/object/object_shapekey.c +++ b/source/blender/editors/object/object_shapekey.c @@ -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; } -- cgit v1.2.3 From 56edaf78673104728e771b835215c111de9528b2 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 20 Mar 2015 12:25:14 +0500 Subject: Fix stupid strict flags in math_color_inline.c It was unleashed with recent ghash commit and issue seems to be the same as fixed in 69065b5b. --- source/blender/blenlib/BLI_utildefines.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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])); \ -- cgit v1.2.3 From 1cd4070e35fa8439bffcf500487e9922e11bd1d4 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 20 Mar 2015 12:41:31 +0500 Subject: Fix T43786: Cycles bake disregards Auto Smooth (vertex per-face normals) Added an utility function which performs vertex split based on the loop normal so now backing API matches to what's happening in Cycles and BI in terms of autosplit. Reviewers: dfelinto, campbellbarton Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D1174 --- source/blender/blenkernel/BKE_mesh.h | 3 + source/blender/blenkernel/intern/mesh.c | 140 ++++++++++++++++++++++++ source/blender/editors/object/object_bake_api.c | 20 +++- source/blender/makesrna/intern/rna_mesh_api.c | 45 +------- 4 files changed, 160 insertions(+), 48 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 224be0f3685..1f3458c06c2 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -130,6 +130,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); diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 809d2137187..8ebc318ff9e 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/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/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"); -- cgit v1.2.3 From 6de12b1b43f846f786abc903b992d9493900f76f Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 20 Mar 2015 13:59:05 +0500 Subject: CMake: Suppress unconfigured branch upstream error in the output --- build_files/cmake/buildinfo.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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. -- cgit v1.2.3 From 6f51df5384f1a78ea0359459f410a9724d7f2e80 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 20 Mar 2015 14:03:54 +0500 Subject: SCons: Attempt to deal with branches without upstream configured --- build_files/scons/tools/Blender.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_files/scons/tools/Blender.py b/build_files/scons/tools/Blender.py index 060904e7cd7..dbe1cafc14f 100644 --- a/build_files/scons/tools/Blender.py +++ b/build_files/scons/tools/Blender.py @@ -441,7 +441,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() -- cgit v1.2.3 From 03d945095af38905f3cbb0136e257cd491e3a48b Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 20 Mar 2015 14:07:56 +0500 Subject: Cmake: Some more tweaks to building from branch without upstream --- build_files/cmake/packaging.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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}) -- cgit v1.2.3 From 6786ef6783fa924d1ad0625d557f0c1fcb5d9ede Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 20 Mar 2015 12:39:25 +0100 Subject: Fix T43986: VSE Mask Modifier don't move with video-Clip. Now mask animation is offset to start of strip, instead of staying at frame 1! Warning: this may break existing files, in case some would be using (hacking around!) current bad behavior... --- source/blender/blenkernel/BKE_sequencer.h | 4 +++- source/blender/blenkernel/intern/seqmodifier.c | 6 +++--- source/blender/blenkernel/intern/sequencer.c | 7 +++++-- 3 files changed, 11 insertions(+), 6 deletions(-) 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/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; -- cgit v1.2.3 From c0be69f7fdb8f905f6133663e6f91a0d849b7795 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 20 Mar 2015 23:38:08 +1100 Subject: Fix/Improve FKey bone creation - new bone is now made active - previous selection cleared - bone direction places the tip on the active bone (if available) --- source/blender/editors/armature/armature_edit.c | 98 ++++++++++++++----------- 1 file changed, 54 insertions(+), 44 deletions(-) diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c index 88c52989c07..6fe4cbf4ade 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, 0); + arm->act_edbone = newbone; + newbone->flag |= BONE_TIPSEL; + } /* updates */ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit); -- cgit v1.2.3 From 3d6642db835a9e35ea15cf9f856ce047447690ca Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 20 Mar 2015 17:48:45 +0500 Subject: Fix race condition and bad memory access highlighting render tiles Is was possible that interface will be refreshed at thesame time as render engine will start freeing render parts. Not sure if we can get away without RW mutex here, seems we need one way of synchronization or another.. --- source/blender/render/intern/include/render_types.h | 1 + source/blender/render/intern/source/external_engine.c | 11 ++++++++++- source/blender/render/intern/source/pipeline.c | 5 ++++- 3 files changed, 15 insertions(+), 2 deletions(-) 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..557fcb01778 100644 --- a/source/blender/render/intern/source/external_engine.c +++ b/source/blender/render/intern/source/external_engine.c @@ -375,9 +375,12 @@ void RE_engine_get_current_tiles(Render *re, int *total_tiles_r, rcti **tiles_r) rcti *tiles = NULL; int allocation_size = 0, allocation_step = BLENDER_MAX_THREADS; + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_READ); + if (re->engine && (re->engine->flag & RE_ENGINE_HIGHLIGHT_TILES) == 0) { *total_tiles_r = 0; *tiles_r = NULL; + BLI_rw_mutex_unlock(&re->partsmutex); return; } @@ -404,7 +407,7 @@ 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; } @@ -478,6 +481,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 +490,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 +669,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 +690,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 */ } -- cgit v1.2.3 From 07b2508305d58d0c772464f9a174fca2b96036c2 Mon Sep 17 00:00:00 2001 From: Antony Riakiotakis Date: Fri, 20 Mar 2015 15:26:13 +0100 Subject: Fix high quality depth of field on the Mac. Quite a few things wrong here: * Mac did not support EXT_draw_instanced, only ARB_draw_instanced * Draw instanced did not work unless data came from vertex buffer, which is second time we see weird things with vertex arrays in mac * There were a few stupid mistakes by me as well, such as binding to uniform locations for the wrong shaders (it's a wonder it ever worked :p) --- source/blender/gpu/GPU_extensions.h | 2 +- source/blender/gpu/intern/gpu_codegen.c | 2 +- source/blender/gpu/intern/gpu_compositing.c | 35 +++++++++++++----- source/blender/gpu/intern/gpu_extensions.c | 43 ++++++++++------------ source/blender/gpu/intern/gpu_simple_shader.c | 2 +- .../gpu/shaders/gpu_shader_fx_dof_hq_frag.glsl | 6 +-- .../gpu/shaders/gpu_shader_fx_dof_hq_vert.glsl | 4 +- 7 files changed, 52 insertions(+), 42 deletions(-) diff --git a/source/blender/gpu/GPU_extensions.h b/source/blender/gpu/GPU_extensions.h index aed1a88938a..401711e84e5 100644 --- a/source/blender/gpu/GPU_extensions.h +++ b/source/blender/gpu/GPU_extensions.h @@ -184,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 *geocode, 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); diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index fcfb68d4629..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, NULL, 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 bfa938d0fed..43dfba1925c 100644 --- a/source/blender/gpu/intern/gpu_compositing.c +++ b/source/blender/gpu/intern/gpu_compositing.c @@ -114,6 +114,8 @@ struct GPUFX { /* we have a stencil, restore the previous state */ bool restore_stencil; + + unsigned int vbuffer; }; #if 0 @@ -177,6 +179,12 @@ GPUFX *GPU_fx_compositor_create(void) { GPUFX *fx = MEM_callocN(sizeof(GPUFX), "GPUFX compositor"); + 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; } @@ -583,8 +591,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); @@ -613,6 +622,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); @@ -649,8 +659,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); @@ -793,6 +804,7 @@ bool GPU_fx_do_composite_pass(GPUFX *fx, float projmat[4][4], bool is_persp, str glDisableClientState(GL_TEXTURE_COORD_ARRAY); GPU_shader_unbind(); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); return false; } @@ -862,7 +874,7 @@ bool GPU_fx_do_composite_pass(GPUFX *fx, float projmat[4][4], bool is_persp, str 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_pass1, "dof_params"); + dof_uniform = GPU_shader_get_uniform(dof_shader_pass2, "dof_params"); GPU_shader_bind(dof_shader_pass2); @@ -882,13 +894,14 @@ bool GPU_fx_do_composite_pass(GPUFX *fx, float projmat[4][4], bool is_persp, str 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 */ - glDrawArraysInstancedEXT(GL_POINTS, 0, 1, fx->dof_downsampled_w * fx->dof_downsampled_h); + 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); @@ -905,7 +918,7 @@ bool GPU_fx_do_composite_pass(GPUFX *fx, float projmat[4][4], bool is_persp, str /* 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 */ - glDrawArraysInstancedEXT(GL_POINTS, 0, 1, fx->dof_downsampled_w * fx->dof_downsampled_h); + glDrawArraysInstancedARB(GL_POINTS, 0, 1, fx->dof_downsampled_w * fx->dof_downsampled_h); /* disable bindings */ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -931,8 +944,8 @@ bool GPU_fx_do_composite_pass(GPUFX *fx, float projmat[4][4], bool is_persp, str 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_pass1, "viewvecs"); - depth_uniform = GPU_shader_get_uniform(dof_shader_pass1, "depthbuffer"); + 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); @@ -951,7 +964,7 @@ bool GPU_fx_do_composite_pass(GPUFX *fx, float projmat[4][4], bool is_persp, str 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_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); @@ -1002,6 +1015,7 @@ bool GPU_fx_do_composite_pass(GPUFX *fx, float projmat[4][4], bool is_persp, str glDisableClientState(GL_TEXTURE_COORD_ARRAY); GPU_shader_unbind(); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); return false; } @@ -1230,6 +1244,7 @@ bool GPU_fx_do_composite_pass(GPUFX *fx, float projmat[4][4], bool is_persp, str glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); GPU_shader_unbind(); diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.c index 24b54a3af37..b4ffc978d11 100644 --- a/source/blender/gpu/intern/gpu_extensions.c +++ b/source/blender/gpu/intern/gpu_extensions.c @@ -289,7 +289,7 @@ bool GPU_geometry_shader_support(void) bool GPU_instanced_drawing_support(void) { - return GLEW_EXT_draw_instanced; + return GLEW_ARB_draw_instanced; } int GPU_color_depth(void) @@ -1456,7 +1456,7 @@ static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH]) if (GPU_instanced_drawing_support()) { strcat(defines, "#extension GL_EXT_gpu_shader4: enable\n"); - strcat(defines, "#extension GL_EXT_draw_instanced: enable\n"); + strcat(defines, "#extension GL_ARB_draw_instanced: enable\n"); } } @@ -1478,7 +1478,7 @@ static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH]) return; } -GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, const char *geocode, 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]; @@ -1577,7 +1577,6 @@ GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, const source[num_source++] = standard_defines; if (defines) source[num_source++] = defines; - if (libcode) source[num_source++] = libcode; source[num_source++] = geocode; glAttachObjectARB(shader->object, shader->geometry); @@ -1593,6 +1592,8 @@ GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, const GPU_shader_free(shader); return NULL; } + + GPU_shader_geometry_stage_primitive_io(shader, input, output, number); } @@ -1732,9 +1733,6 @@ void GPU_shader_geometry_stage_primitive_io(GPUShader *shader, int input, int ou 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); - - /* relink so settings can take effect (sucks but should only be done right after compilation so...) */ - glLinkProgramARB(shader->object); } void GPU_shader_uniform_texture(GPUShader *UNUSED(shader), int location, GPUTexture *tex) @@ -1784,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, 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, 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; } @@ -1822,56 +1820,53 @@ GPUShader *GPU_shader_get_builtin_fx_shader(int effects, bool persp) 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, NULL, 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, NULL, 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, NULL, 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, NULL, 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, NULL, 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, NULL, 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); + 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); - - if (shader) { - GG.shaders.fx_shaders[offset] = shader; - GPU_shader_geometry_stage_primitive_io(shader, GL_POINTS, GL_TRIANGLE_STRIP, 4); - } + 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); + 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, 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 60d4a2f2875..b439a37f3c3 100644 --- a/source/blender/gpu/intern/gpu_simple_shader.c +++ b/source/blender/gpu/intern/gpu_simple_shader.c @@ -152,7 +152,7 @@ static GPUShader *gpu_simple_shader(int options) datatoc_gpu_shader_simple_vert_glsl, datatoc_gpu_shader_simple_frag_glsl, NULL, - NULL, defines); + 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 index e2d3ab36ec8..8c2aff45679 100644 --- a/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_frag.glsl @@ -102,7 +102,7 @@ void accumulate_pass(void) { 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 * M_PI)))); + 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; @@ -124,7 +124,7 @@ void final_pass(void) { vec4 farcolor = texture2D(farbuffer, uvcoord); float farweight = farcolor.a; - if (farweight > 0) + if (farweight > 0.0) farcolor /= farweight; vec4 nearcolor = texture2D(nearbuffer, uvcoord); @@ -138,7 +138,7 @@ void final_pass(void) { farweight = mix(1.0, farweight, mixfac); float nearweight = nearcolor.a; - if (nearweight > 0) { + if (nearweight > 0.0) { nearcolor /= nearweight; } 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 index e8c505bd15f..09a0c75facc 100644 --- a/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_vert.glsl @@ -27,11 +27,11 @@ void vert_dof_downsample() /* geometry shading pass, calculate a texture coordinate based on the indexed id */ void vert_dof_coc_scatter_pass() { - vec2 pixel = vec2(1.0 / float(rendertargetdim.x), 1.0 / float(rendertargetdim.y)); + 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) * pixel + 0.5 * pixel; + 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); -- cgit v1.2.3 From 339e43e1e821276cf69c0f9829c83999bf79d769 Mon Sep 17 00:00:00 2001 From: Antony Riakiotakis Date: Fri, 20 Mar 2015 15:43:34 +0100 Subject: Only create vertex buffers if supported and clean them up properly. --- source/blender/gpu/intern/gpu_compositing.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/source/blender/gpu/intern/gpu_compositing.c b/source/blender/gpu/intern/gpu_compositing.c index 43dfba1925c..53ce5145388 100644 --- a/source/blender/gpu/intern/gpu_compositing.c +++ b/source/blender/gpu/intern/gpu_compositing.c @@ -179,12 +179,14 @@ GPUFX *GPU_fx_compositor_create(void) { GPUFX *fx = MEM_callocN(sizeof(GPUFX), "GPUFX compositor"); - 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); + 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; } @@ -273,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); } -- cgit v1.2.3 From 62b31a6e997ca6ca10cad695252909c657d67401 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 20 Mar 2015 19:56:09 +0500 Subject: Avoid per-draw allocation when drawing currently rendering tiles --- source/blender/editors/space_image/image_draw.c | 7 +++++-- source/blender/render/extern/include/RE_engine.h | 2 +- source/blender/render/intern/source/external_engine.c | 19 ++++++++++++------- 3 files changed, 18 insertions(+), 10 deletions(-) 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/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/source/external_engine.c b/source/blender/render/intern/source/external_engine.c index 557fcb01778..d5f1cab3a66 100644 --- a/source/blender/render/intern/source/external_engine.c +++ b/source/blender/render/intern/source/external_engine.c @@ -368,26 +368,31 @@ 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 *needs_free_r) { + 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); if (re->engine && (re->engine->flag & RE_ENGINE_HIGHLIGHT_TILES) == 0) { *total_tiles_r = 0; - *tiles_r = NULL; BLI_rw_mutex_unlock(&re->partsmutex); - return; + needs_free_r = false; + 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)); @@ -409,7 +414,7 @@ void RE_engine_get_current_tiles(Render *re, int *total_tiles_r, rcti **tiles_r) } BLI_rw_mutex_unlock(&re->partsmutex); *total_tiles_r = total_tiles; - *tiles_r = tiles; + return tiles; } RenderData *RE_engine_get_render_data(Render *re) -- cgit v1.2.3 From d12ab9a9a8c39733c35e4c967cee52eb7d274b4b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 20 Mar 2015 23:55:47 +1100 Subject: Remove unused armature code ED_armature_deselect_all now simply de-selects --- source/blender/editors/armature/armature_add.c | 6 +-- source/blender/editors/armature/armature_edit.c | 2 +- source/blender/editors/armature/armature_select.c | 60 +++------------------- source/blender/editors/include/ED_armature.h | 2 +- .../editors/space_outliner/outliner_select.c | 2 +- 5 files changed, 13 insertions(+), 59 deletions(-) diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index c2158f02ae3..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; @@ -914,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 6fe4cbf4ade..29b7872f304 100644 --- a/source/blender/editors/armature/armature_edit.c +++ b/source/blender/editors/armature/armature_edit.c @@ -714,7 +714,7 @@ static int armature_fill_bones_exec(bContext *C, wmOperator *op) } if (newbone) { - ED_armature_deselect_all(obedit, 0); + ED_armature_deselect_all(obedit); arm->act_edbone = newbone; newbone->flag |= BONE_TIPSEL; } diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 5061ba9a812..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 */ 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/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; } -- cgit v1.2.3 From 63ab5b0883612878818c931b6765237eaeecca4a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Mar 2015 02:16:39 +1100 Subject: Fix for building win32 headless --- intern/ghost/CMakeLists.txt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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}) -- cgit v1.2.3 From 94a3924eab251c7e487c9f2b631e55e9c6ebf2e9 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Mar 2015 02:25:28 +1100 Subject: Correct recent commit --- source/blender/render/intern/source/external_engine.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c index d5f1cab3a66..c66b9c2ce0d 100644 --- a/source/blender/render/intern/source/external_engine.c +++ b/source/blender/render/intern/source/external_engine.c @@ -368,7 +368,7 @@ void RE_engine_set_error_message(RenderEngine *engine, const char *msg) } } -rcti* RE_engine_get_current_tiles(Render *re, int *total_tiles_r, bool *needs_free_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; @@ -382,7 +382,7 @@ rcti* RE_engine_get_current_tiles(Render *re, int *total_tiles_r, bool *needs_fr if (re->engine && (re->engine->flag & RE_ENGINE_HIGHLIGHT_TILES) == 0) { *total_tiles_r = 0; BLI_rw_mutex_unlock(&re->partsmutex); - needs_free_r = false; + *r_needs_free = false; return NULL; } -- cgit v1.2.3 From b9dc4f851bd01a5ca5c7019ea7ef2356d010be5f Mon Sep 17 00:00:00 2001 From: Antony Riakiotakis Date: Fri, 20 Mar 2015 16:35:26 +0100 Subject: Fix link error with GLEW --- source/blender/gpu/intern/gpu_compositing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/gpu/intern/gpu_compositing.c b/source/blender/gpu/intern/gpu_compositing.c index 53ce5145388..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" -- cgit v1.2.3 From f91850d564576189882e872e26f8da48cfbd8829 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Mar 2015 03:02:09 +1100 Subject: Disable IME for headless builds --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e8552f2a1d..21efa668292 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -674,6 +674,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) -- cgit v1.2.3 From 8ff4c53e63dc1eb7f4855915dd45d044b854e301 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 20 Mar 2015 21:09:52 +0500 Subject: Correction to previous commit, initialization of needs_free got lost by accident --- source/blender/render/intern/source/external_engine.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c index c66b9c2ce0d..653f3f0786a 100644 --- a/source/blender/render/intern/source/external_engine.c +++ b/source/blender/render/intern/source/external_engine.c @@ -379,10 +379,11 @@ rcti* RE_engine_get_current_tiles(Render *re, int *total_tiles_r, bool *r_needs_ 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; BLI_rw_mutex_unlock(&re->partsmutex); - *r_needs_free = false; return NULL; } @@ -398,6 +399,7 @@ rcti* RE_engine_get_current_tiles(Render *re, int *total_tiles_r, bool *r_needs_ tiles = MEM_reallocN(tiles, (total_tiles + allocation_step) * sizeof(rcti)); allocation_size += allocation_step; + *r_needs_free = true; } tiles[total_tiles] = pa->disprect; -- cgit v1.2.3 From 919a6654974f40c553ba30a4aba89e9d02251246 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 20 Mar 2015 21:14:50 +0500 Subject: Cycles: Avoid memcpy of intersecting memory Could happen when assignment happens to self during sorting. --- intern/cycles/bvh/bvh_params.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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; } -- cgit v1.2.3 From 0582aa6f130a4b1182fc5db646db3c7ac4a8ca46 Mon Sep 17 00:00:00 2001 From: Jens Verwiebe Date: Fri, 20 Mar 2015 17:57:35 +0100 Subject: Partly fix T44025, pixelFormat retain was left, for 2.74 backport --- intern/ghost/intern/GHOST_ContextCGL.mm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/intern/ghost/intern/GHOST_ContextCGL.mm b/intern/ghost/intern/GHOST_ContextCGL.mm index 0b290c617a5..cf94dc83d18 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 -- cgit v1.2.3 From d5abff589ebdfc2b9c110f4a67dad0db336482b7 Mon Sep 17 00:00:00 2001 From: Antony Riakiotakis Date: Fri, 20 Mar 2015 19:46:48 +0100 Subject: Fix scons build --- SConstruct | 3 +++ 1 file changed, 3 insertions(+) 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") -- cgit v1.2.3 From ed61017391a6d5b9d1bde39e4df95c63ec01cf05 Mon Sep 17 00:00:00 2001 From: Jens Verwiebe Date: Fri, 20 Mar 2015 20:24:30 +0100 Subject: Really fix scons with new glsl shaders --- source/blender/gpu/SConscript | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/gpu/SConscript b/source/blender/gpu/SConscript index f52b39dba3b..9a3ea8f129c 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"), -- cgit v1.2.3 From 997c0c837b5a4d090b124dc5a2e85b139e1ceb56 Mon Sep 17 00:00:00 2001 From: Jens Verwiebe Date: Fri, 20 Mar 2015 20:26:51 +0100 Subject: Fix indentation in last commit, tab slipped in --- source/blender/gpu/SConscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/gpu/SConscript b/source/blender/gpu/SConscript index 9a3ea8f129c..8d0ef394a06 100644 --- a/source/blender/gpu/SConscript +++ b/source/blender/gpu/SConscript @@ -67,7 +67,7 @@ sources.extend(( 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_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"), -- cgit v1.2.3 From e88cfc28b24e1c6c1d67858a6bc930389e0041ea Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Mar 2015 15:11:19 +1100 Subject: View3D: use defines for default zoom-levels --- source/blender/blenkernel/BKE_camera.h | 4 ++++ source/blender/blenkernel/intern/camera.c | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/BKE_camera.h b/source/blender/blenkernel/BKE_camera.h index 77296920ee2..ead58c25d6b 100644 --- a/source/blender/blenkernel/BKE_camera.h +++ b/source/blender/blenkernel/BKE_camera.h @@ -104,6 +104,10 @@ 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); diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 451656a9bca..4588c00b2f6 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -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 */ @@ -285,11 +285,11 @@ void BKE_camera_params_from_view3d(CameraParams *params, View3D *v3d, RegionView params->is_ortho = true; /* make sure any changes to this match ED_view3d_radius_to_ortho_dist() */ 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; } } -- cgit v1.2.3 From e0611ca832d793694cbda0fbae1a4388b81fc589 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Mar 2015 15:11:30 +1100 Subject: View3D: fix view-selected zoom logic - Correct logic converting radius to view distance. - Wasn't taking view-zoom into account converting lens to angle. - Support framing the selection in the camera bounds (for camera locked views). Add ED_view3d_radius_to_dist to handles these details. --- source/blender/blenkernel/intern/camera.c | 2 +- source/blender/editors/include/ED_view3d.h | 8 +- source/blender/editors/space_view3d/view3d_edit.c | 39 ++----- source/blender/editors/space_view3d/view3d_view.c | 134 ++++++++++++++++++---- 4 files changed, 130 insertions(+), 53 deletions(-) diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 4588c00b2f6..ed400bcf565 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -283,7 +283,7 @@ 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 = CAMERA_PARAM_ZOOM_INIT_PERSP; } diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 11f961605af..ec53bce2eb7 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -252,8 +252,12 @@ void ED_view3d_clipping_disable(void); float ED_view3d_pixel_size(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]); diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 51915e05a9e..0bced34e465 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -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_view.c b/source/blender/editors/space_view3d/view3d_view.c index 4888c6846d2..1c10a857179 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -1214,7 +1214,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 +1252,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 +1268,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 +1284,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); } } @@ -1720,16 +1713,111 @@ float ED_view3d_pixel_size(RegionView3D *rv3d, const float co[3]) return mul_project_m4_v3_zfac(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: + * + *
+ *           +
+ * pt --> + /^ radius
+ *         / |
+ *        /  |
+ * view  +   +
+ *        \  |
+ *         \ |
+ *          \|
+ *           +
+ * 
+ * + * \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 */ -- cgit v1.2.3 From 0025d90b98c69dd4ed149918e716b92c81328bbb Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Mar 2015 16:40:17 +1100 Subject: Cleanup: use tabs --- GNUmakefile | 2 +- source/blender/blenkernel/intern/tracking.c | 2 +- source/blender/blenlib/intern/BLI_ghash.c | 66 ++++++++++++++--------------- source/blender/blenlib/intern/hash_mm2a.c | 2 +- 4 files changed, 36 insertions(+), 36 deletions(-) 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/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/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c index a2233c38270..49d3cecafed 100644 --- a/source/blender/blenlib/intern/BLI_ghash.c +++ b/source/blender/blenlib/intern/BLI_ghash.c @@ -547,8 +547,8 @@ static void ghash_free_cb(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP va { unsigned int i; - BLI_assert(keyfreefp || valfreefp); - BLI_assert(!valfreefp|| !(gh->flag & GHASH_FLAG_IS_GSET)); + BLI_assert(keyfreefp || valfreefp); + BLI_assert(!valfreefp || !(gh->flag & GHASH_FLAG_IS_GSET)); for (i = 0; i < gh->nbuckets; i++) { Entry *e; @@ -1370,37 +1370,37 @@ double BLI_ghash_calc_quality_ex( } { - 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; - } - return ((double)sum * (double)gh->nbuckets / - ((double)gh->nentries * (gh->nentries + 2 * 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; + } + 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, diff --git a/source/blender/blenlib/intern/hash_mm2a.c b/source/blender/blenlib/intern/hash_mm2a.c index 87ba542e147..af6ef4f355f 100644 --- a/source/blender/blenlib/intern/hash_mm2a.c +++ b/source/blender/blenlib/intern/hash_mm2a.c @@ -135,7 +135,7 @@ uint32_t BLI_hash_mm2(const unsigned char *data, size_t len, uint32_t seed) 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); -- cgit v1.2.3 From 6e50bdd870b7486cea128e0dcc9777045237ea4a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Mar 2015 22:10:43 +1100 Subject: Cleanup: constify scene/modifiers --- source/blender/blenkernel/BKE_dynamicpaint.h | 4 ++-- source/blender/blenkernel/BKE_editmesh.h | 2 +- source/blender/blenkernel/BKE_scene.h | 18 +++++++++--------- source/blender/blenkernel/BKE_texture.h | 4 +++- source/blender/blenkernel/intern/DerivedMesh.c | 6 +++--- source/blender/blenkernel/intern/dynamicpaint.c | 14 +++++++------- source/blender/blenkernel/intern/editderivedmesh.c | 2 +- source/blender/blenkernel/intern/scene.c | 20 ++++++++++---------- source/blender/blenkernel/intern/texture.c | 4 +++- source/blender/modifiers/intern/MOD_util.c | 2 +- source/blender/modifiers/intern/MOD_util.h | 2 +- 11 files changed, 41 insertions(+), 37 deletions(-) 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_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_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/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/scene.c b/source/blender/blenkernel/intern/scene.c index f94f7cd8f0b..c28d741d7ec 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -1140,13 +1140,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; @@ -1891,7 +1891,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); @@ -1899,7 +1899,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); @@ -1907,7 +1907,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); @@ -1915,7 +1915,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; @@ -1947,18 +1947,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/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/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index c23c22b5fec..eac2f24d064 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]); -- cgit v1.2.3 From d28bfb5022b56a82424c6242e77a7658803ed7ca Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Mar 2015 22:34:20 +1100 Subject: Cleanup: constify view3d/camera --- source/blender/blenkernel/BKE_camera.h | 29 ++++++++------ source/blender/blenkernel/intern/camera.c | 21 ++++++---- source/blender/blenkernel/intern/lattice.c | 5 ++- source/blender/editors/include/ED_view3d.h | 45 ++++++++++++---------- source/blender/editors/space_view3d/view3d_draw.c | 28 ++++++++------ source/blender/editors/space_view3d/view3d_edit.c | 4 +- .../blender/editors/space_view3d/view3d_intern.h | 6 +-- source/blender/editors/space_view3d/view3d_view.c | 32 ++++++++------- source/blender/makesrna/intern/rna_object_api.c | 2 +- 9 files changed, 99 insertions(+), 73 deletions(-) diff --git a/source/blender/blenkernel/BKE_camera.h b/source/blender/blenkernel/BKE_camera.h index ead58c25d6b..4849c66df1b 100644 --- a/source/blender/blenkernel/BKE_camera.h +++ b/source/blender/blenkernel/BKE_camera.h @@ -109,23 +109,30 @@ typedef struct CameraParams { #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/intern/camera.c b/source/blender/blenkernel/intern/camera.c index ed400bcf565..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; @@ -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/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/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index ec53bce2eb7..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,33 +224,38 @@ 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_dist_persp(const float angle, const float radius); float ED_view3d_radius_to_dist_ortho(const float lens, const float radius); @@ -334,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/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 58ef9184dd0..db4499afc26 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -207,7 +207,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) @@ -220,7 +220,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); } @@ -949,8 +949,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; @@ -983,7 +984,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; @@ -992,8 +995,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); } @@ -2554,7 +2558,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; @@ -2580,16 +2584,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 0bced34e465..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) && 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 1c10a857179..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; } @@ -1708,9 +1712,9 @@ 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_dist_persp(const float angle, const float radius) diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 2306682ca01..9fe44511c8c 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 */ -- cgit v1.2.3 From d21018b3340496865626b279852159347ac9c102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Sat, 21 Mar 2015 14:42:57 +0100 Subject: Fix T44065: fixed vehicle constraint Commit ffee7f1a58a18bc08add94176ddffe29809139a6 broke vehicle constraints; this fixes that. --- .../Physics/Bullet/CcdPhysicsEnvironment.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) 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) -- cgit v1.2.3 From 8e9c9fde427c228111cfe2da6dfac9e54abb2a3d Mon Sep 17 00:00:00 2001 From: Jens Verwiebe Date: Sat, 21 Mar 2015 17:15:38 +0100 Subject: OSX/GHOST: need one more release in error case --- intern/ghost/intern/GHOST_ContextCGL.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/intern/ghost/intern/GHOST_ContextCGL.mm b/intern/ghost/intern/GHOST_ContextCGL.mm index cf94dc83d18..ae7e7a69854 100644 --- a/intern/ghost/intern/GHOST_ContextCGL.mm +++ b/intern/ghost/intern/GHOST_ContextCGL.mm @@ -334,6 +334,7 @@ GHOST_TSuccess GHOST_ContextCGL::initializeDrawingContext() error: [m_openGLView setOpenGLContext:prev_openGLContext]; + [pixelFormat release]; [pool drain]; -- cgit v1.2.3 From 9ff3d8eafae92a7071d86028d3c71308ad28125b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 22 Mar 2015 03:24:56 +1100 Subject: Cleanup: minor edits to project generators --- build_files/cmake/cmake_netbeans_project.py | 25 +++++++++-------- build_files/cmake/cmake_qtcreator_project.py | 42 +++++++++++++++------------- 2 files changed, 35 insertions(+), 32 deletions(-) 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 @@ # -""" +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") -- cgit v1.2.3 From e1831990227ad43c9c41fca8f3e3639aaa222c2d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 22 Mar 2015 03:29:59 +1100 Subject: Cleanup: instantiate arg once in context macro --- source/creator/creator.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/source/creator/creator.c b/source/creator/creator.c index 0ca5fbeccac..9815b3c7211 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -1185,19 +1185,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 */ -- cgit v1.2.3 From 2744ce77dea394026bc524e68c687050bc8e0c28 Mon Sep 17 00:00:00 2001 From: Jorge Bernal Date: Sat, 21 Mar 2015 17:53:18 +0100 Subject: Revert part of D1074 related to acceleration taked into account. It has been reverted because it was affecting obstacle avoidance (T44041). This fix should be backported to 2.74 --- source/blender/blenloader/intern/versioning_270.c | 12 ------------ source/gameengine/Ketsji/KX_SteeringActuator.cpp | 11 ++++------- 2 files changed, 4 insertions(+), 19 deletions(-) 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/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 -- cgit v1.2.3