Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/bl_operators/object.py151
-rw-r--r--release/scripts/startup/bl_ui/properties_object.py45
-rw-r--r--source/blender/blenkernel/BKE_object.h8
-rw-r--r--source/blender/blenkernel/intern/material.c2
-rw-r--r--source/blender/blenkernel/intern/object.c154
-rw-r--r--source/blender/blenloader/intern/readfile.c19
-rw-r--r--source/blender/blenloader/intern/writefile.c2
-rw-r--r--source/blender/editors/object/CMakeLists.txt1
-rw-r--r--source/blender/editors/object/object_intern.h4
-rw-r--r--source/blender/editors/object/object_lod.c102
-rw-r--r--source/blender/editors/object/object_ops.c3
-rw-r--r--source/blender/editors/space_view3d/drawobject.c2
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c1
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c25
-rw-r--r--source/blender/gpu/intern/gpu_draw.c2
-rw-r--r--source/blender/makesdna/DNA_object_types.h16
-rw-r--r--source/blender/makesrna/intern/rna_object.c49
-rw-r--r--source/blender/windowmanager/WM_types.h1
-rw-r--r--source/gameengine/Converter/BL_BlenderDataConversion.cpp30
-rw-r--r--source/gameengine/Ketsji/KX_Dome.cpp3
-rw-r--r--source/gameengine/Ketsji/KX_GameObject.cpp39
-rw-r--r--source/gameengine/Ketsji/KX_GameObject.h18
-rw-r--r--source/gameengine/Ketsji/KX_KetsjiEngine.cpp3
-rw-r--r--source/gameengine/Ketsji/KX_Scene.cpp13
-rw-r--r--source/gameengine/Ketsji/KX_Scene.h3
25 files changed, 690 insertions, 6 deletions
diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py
index e5e53d6a4dc..3218af06cc7 100644
--- a/release/scripts/startup/bl_operators/object.py
+++ b/release/scripts/startup/bl_operators/object.py
@@ -775,3 +775,154 @@ class DupliOffsetFromCursor(Operator):
ob.users_group[group].dupli_offset = scene.cursor_location
return {'FINISHED'}
+
+
+class LodByName(Operator):
+ """Add levels of detail to this object based on object names"""
+ bl_idname = "object.lod_by_name"
+ bl_label = "Setup Levels of Detail By Name"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ return (context.active_object is not None)
+
+ def execute(self, context):
+ scene = context.scene
+ ob = context.active_object
+
+ prefix = ""
+ suffix = ""
+ name = ""
+ if ob.name.lower().startswith("lod0"):
+ prefix = ob.name[:4]
+ name = ob.name[4:]
+ elif ob.name.lower().endswith("lod0"):
+ name = ob.name[:-4]
+ suffix = ob.name[-4:]
+ else:
+ return {'CANCELLED'}
+
+ level = 0
+ while True:
+ level += 1
+
+ if prefix:
+ prefix = prefix[:3] + str(level)
+ if suffix:
+ suffix = suffix[:3] + str(level)
+
+ lod = None
+ try:
+ lod = bpy.data.objects[prefix + name + suffix]
+ except KeyError:
+ break
+
+ try:
+ ob.lod_levels[level]
+ except IndexError:
+ bpy.ops.object.lod_add()
+
+ ob.lod_levels[level].object = lod
+
+ return {'FINISHED'}
+
+
+class LodClearAll(Operator):
+ """Remove all levels of detail from this object"""
+ bl_idname = "object.lod_clear_all"
+ bl_label = "Clear All Levels of Detail"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ return (context.active_object is not None)
+
+ def execute(self, context):
+ scene = context.scene
+ ob = context.active_object
+
+ if ob.lod_levels:
+ while 'CANCELLED' not in bpy.ops.object.lod_remove():
+ pass
+
+ return {'FINISHED'}
+
+
+class LodGenerate(Operator):
+ """Generates levels of detail using the decimate modifier"""
+ bl_idname = "object.lod_generate"
+ bl_label = "Generate Levels of Detail"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ count = bpy.props.IntProperty(name="Count", default=3)
+ target = bpy.props.FloatProperty(name="Target Size", default=0.1,
+ min=0.0, max=1.0)
+ package = bpy.props.BoolProperty(name="Package into Group", default=False)
+
+ @classmethod
+ def poll(cls, context):
+ return (context.active_object is not None)
+
+ def execute(self, context):
+ scene = bpy.context.scene
+ ob = scene.objects.active
+
+ lod_name = ob.name
+ lod_suffix = "lod"
+ lod_prefix = ""
+ if lod_name.lower().endswith("lod0"):
+ lod_suffix = lod_name[-3:-1]
+ lod_name = lod_name[:-3]
+ elif lod_name.lower().startswith("lod0"):
+ lod_suffix = ""
+ lod_prefix = lod_name[:3]
+ lod_name = lod_name[4:]
+
+ group_name = lod_name.strip(' ._')
+ if self.package:
+ try:
+ bpy.ops.object.group_link(group=group_name)
+ except TypeError:
+ bpy.ops.group.create(name=group_name)
+
+ step = (1.0 - self.target) / (self.count - 1)
+ for i in range(1, self.count):
+ scene.objects.active = ob
+ bpy.ops.object.duplicate()
+ lod = bpy.context.selected_objects[0]
+
+ scene.objects.active = ob
+ bpy.ops.object.lod_add()
+ scene.objects.active = lod
+
+ if lod_prefix:
+ lod.name = lod_prefix + str(i) + lod_name
+ else:
+ lod.name = lod_name + lod_suffix + str(i)
+
+ lod.location.y = ob.location.y + 3.0 * i
+
+ if i == 1:
+ modifier = lod.modifiers.new("lod_decimate", "DECIMATE")
+ else:
+ modifier = lod.modifiers[-1]
+
+ modifier.ratio = 1.0 - step*(i)
+
+ ob.lod_levels[i].object = lod
+
+ if self.package:
+ bpy.ops.object.group_link(group=group_name)
+ lod.parent = ob
+
+ if self.package:
+ for level in ob.lod_levels[1:]:
+ level.object.hide = level.object.hide_render = True
+
+ lod.select = False
+ ob.select = True
+ scene.objects.active = ob
+
+ return {'FINISHED'}
+
diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py
index cbebdafbf2e..dcd32b17cbd 100644
--- a/release/scripts/startup/bl_ui/properties_object.py
+++ b/release/scripts/startup/bl_ui/properties_object.py
@@ -18,7 +18,7 @@
# <pep8 compliant>
import bpy
-from bpy.types import Panel
+from bpy.types import Menu, Panel
from rna_prop_ui import PropertyPanel
@@ -125,6 +125,49 @@ class OBJECT_PT_transform_locks(ObjectButtonsPanel, Panel):
sub.prop(ob, "lock_rotation_w", text="W")
+class OBJECT_MT_lod_tools(Menu):
+ bl_label = "Level Of Detail Tools"
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.operator("object.lod_by_name", text="Set By Name")
+ layout.operator("object.lod_generate", text="Generate")
+ layout.operator("object.lod_clear_all", text="Clear All", icon='PANEL_CLOSE')
+
+
+class OBJECT_PT_levels_of_detail(ObjectButtonsPanel, Panel):
+ bl_label = "Levels of Detail"
+ COMPAT_ENGINES = {'BLENDER_GAME'}
+
+ @classmethod
+ def poll(cls, context):
+ return context.scene.render.engine in cls.COMPAT_ENGINES
+
+ def draw(self, context):
+ layout = self.layout
+ ob = context.object
+
+ col = layout.column()
+
+ for i, level in enumerate(ob.lod_levels):
+ if i == 0: continue
+ box = col.box()
+ row = box.row()
+ row.prop(level, "object", text="")
+ row.operator("object.lod_remove", text="", icon='PANEL_CLOSE').index = i
+
+ row = box.row()
+ row.prop(level, "distance")
+ row = row.row(align=True)
+ row.prop(level, "use_mesh", text="")
+ row.prop(level, "use_material", text="")
+
+ row = col.row(align=True)
+ row.operator("object.lod_add", text="Add", icon='ZOOMIN')
+ row.menu("OBJECT_MT_lod_tools", text="", icon='TRIA_DOWN')
+
+
class OBJECT_PT_relations(ObjectButtonsPanel, Panel):
bl_label = "Relations"
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index 434175624b7..e6e6d621ef3 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -87,6 +87,14 @@ struct Object *BKE_object_add_only_object(struct Main *bmain, int type, const ch
struct Object *BKE_object_add(struct Main *bmain, struct Scene *scene, int type);
void *BKE_object_obdata_add_from_type(struct Main *bmain, int type);
+void BKE_object_lod_add(struct Object *ob);
+void BKE_object_lod_sort(struct Object *ob);
+bool BKE_object_lod_remove(struct Object *ob, int level);
+bool BKE_object_lod_update(struct Object *ob, float camera_position[3]);
+bool BKE_object_lod_is_usable(struct Object *ob, struct Scene *scene);
+struct Object *BKE_object_lod_meshob_get(struct Object *ob, struct Scene *scene);
+struct Object *BKE_object_lod_matob_get(struct Object *ob, struct Scene *scene);
+
struct Object *BKE_object_copy_ex(struct Main *bmain, struct Object *ob, int copy_caches);
struct Object *BKE_object_copy(struct Object *ob);
void BKE_object_make_local(struct Object *ob);
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index cf9b838a7c2..9df523a5bfe 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -675,7 +675,7 @@ Material *give_current_material(Object *ob, short act)
{
Material ***matarar, *ma;
short *totcolp;
-
+
if (ob == NULL) return NULL;
/* if object cannot have material, (totcolp == NULL) */
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 0694bde7b91..70b5fb9d420 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -388,6 +388,8 @@ void BKE_object_free(Object *ob)
if (ob->pc_ids.first) BLI_freelistN(&ob->pc_ids);
+ BLI_freelistN(&ob->lodlevels);
+
/* Free runtime curves data. */
if (ob->curve_cache) {
BLI_freelistN(&ob->curve_cache->bev);
@@ -427,6 +429,7 @@ void BKE_object_unlink(Object *ob)
ModifierData *md;
ARegion *ar;
RegionView3D *rv3d;
+ LodLevel *lod;
int a, found;
unlink_controllers(&ob->controllers);
@@ -608,6 +611,12 @@ void BKE_object_unlink(Object *ob)
DAG_id_tag_update(&obt->id, OB_RECALC_DATA);
}
+ /* levels of detail */
+ for (lod = obt->lodlevels.first; lod; lod = lod->next) {
+ if (lod->source == ob)
+ lod->source = NULL;
+ }
+
obt = obt->id.next;
}
@@ -1015,6 +1024,138 @@ Object *BKE_object_add(Main *bmain, Scene *scene, int type)
return ob;
}
+void BKE_object_lod_add(Object *ob)
+{
+ LodLevel *lod = MEM_callocN(sizeof(LodLevel), "LoD Level");
+ LodLevel *last = ob->lodlevels.last;
+
+ /* If the lod list is empty, initialize it with the base lod level */
+ if (!last) {
+ LodLevel *base = MEM_callocN(sizeof(LodLevel), "Base LoD Level");
+ BLI_addtail(&ob->lodlevels, base);
+ base->flags = OB_LOD_USE_MESH | OB_LOD_USE_MAT;
+ base->source = ob;
+ last = ob->currentlod = base;
+ }
+
+ lod->distance = last->distance + 25.0f;
+ lod->flags = OB_LOD_USE_MESH | OB_LOD_USE_MAT;
+
+ BLI_addtail(&ob->lodlevels, lod);
+}
+
+static int lod_cmp(void *a, void *b)
+{
+ LodLevel *loda = (LodLevel*)a;
+ LodLevel *lodb = (LodLevel*)b;
+
+ if (loda->distance < lodb->distance) return -1;
+ return loda->distance > lodb->distance;
+}
+
+void BKE_object_lod_sort(Object *ob)
+{
+ BLI_sortlist(&ob->lodlevels, lod_cmp);
+}
+
+bool BKE_object_lod_remove(Object *ob, int level)
+{
+ LodLevel *rem;
+
+ if (level < 1 || level > BLI_countlist(&ob->lodlevels) - 1)
+ return false;
+
+ rem = BLI_findlink(&ob->lodlevels, level);
+
+ if (rem == ob->currentlod) {
+ ob->currentlod = rem->prev;
+ }
+
+ BLI_remlink(&ob->lodlevels, rem);
+ MEM_freeN(rem);
+
+ /* If there are no user defined lods, remove the base lod as well */
+ if (BLI_countlist(&ob->lodlevels) == 1) {
+ LodLevel *base = ob->lodlevels.first;
+ BLI_remlink(&ob->lodlevels, base);
+ MEM_freeN(base);
+ ob->currentlod = NULL;
+ }
+
+ return true;
+}
+
+static LodLevel* lod_level_select(Object *ob, float cam_loc[3])
+{
+ LodLevel *current = ob->currentlod;
+ float ob_loc[3], delta[3];
+ float distance2;
+
+ if (!current) return NULL;
+
+ copy_v3_v3(ob_loc, ob->obmat[3]);
+ sub_v3_v3v3(delta, ob_loc, cam_loc);
+ distance2 = len_squared_v3(delta);
+
+ /* check for higher LoD */
+ if (distance2 < current->distance*current->distance) {
+ while (current->prev && distance2 < current->distance*current->distance) {
+ current = current->prev;
+ }
+ }
+ /* check for lower LoD */
+ else {
+ while (current->next && distance2 > current->next->distance*current->next->distance) {
+ current = current->next;
+ }
+ }
+
+ return current;
+}
+
+bool BKE_object_lod_is_usable(Object *ob, Scene *scene)
+{
+ bool active = (scene) ? ob == OBACT : 0;
+ return (ob->mode == OB_MODE_OBJECT || !active);
+}
+
+bool BKE_object_lod_update(Object *ob, float camera_position[3])
+{
+ LodLevel* cur_level = ob->currentlod;
+ LodLevel* new_level = lod_level_select(ob, camera_position);
+
+ if (new_level != cur_level) {
+ ob->currentlod = new_level;
+ return true;
+ }
+
+ return false;
+}
+
+static Object *lod_ob_get(Object *ob, Scene *scene, int flag)
+{
+ LodLevel *current = ob->currentlod;
+
+ if (!current || !BKE_object_lod_is_usable(ob, scene))
+ return ob;
+
+ while( current->prev && (!(current->flags & flag) || !current->source || current->source->type != OB_MESH)) {
+ current = current->prev;
+ }
+
+ return current->source;
+}
+
+struct Object *BKE_object_lod_meshob_get(Object *ob, Scene *scene)
+{
+ return lod_ob_get(ob, scene, OB_LOD_USE_MESH);
+}
+
+struct Object *BKE_object_lod_matob_get(Object *ob, Scene *scene)
+{
+ return lod_ob_get(ob, scene, OB_LOD_USE_MAT);
+}
+
SoftBody *copy_softbody(SoftBody *sb, int copy_caches)
{
SoftBody *sbn;
@@ -1226,6 +1367,16 @@ static void copy_object_pose(Object *obn, Object *ob)
}
}
+static void copy_object_lod(Object *obn, Object *ob)
+{
+ BLI_duplicatelist(&obn->lodlevels, &ob->lodlevels);
+
+ if (obn->lodlevels.first)
+ ((LodLevel*)obn->lodlevels.first)->source = obn;
+
+ obn->currentlod = (LodLevel*) obn->lodlevels.first;
+}
+
bool BKE_object_pose_context_check(Object *ob)
{
if ((ob) &&
@@ -1346,6 +1497,9 @@ Object *BKE_object_copy_ex(Main *bmain, Object *ob, int copy_caches)
obn->mpath = NULL;
+ copy_object_lod(obn, ob);
+
+
/* Copy runtime surve data. */
obn->curve_cache = NULL;
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index ac34f81907a..f3244a63a8b 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -4492,6 +4492,16 @@ static void lib_link_object(FileData *fd, Main *main)
ob->rigidbody_constraint->ob1 = newlibadr(fd, ob->id.lib, ob->rigidbody_constraint->ob1);
ob->rigidbody_constraint->ob2 = newlibadr(fd, ob->id.lib, ob->rigidbody_constraint->ob2);
}
+
+ {
+ LodLevel *level;
+ for (level = ob->lodlevels.first; level; level = level->next) {
+ level->source = newlibadr(fd, ob->id.lib, level->source);
+
+ if (!level->source && level == ob->lodlevels.first)
+ level->source = ob;
+ }
+ }
}
}
@@ -5026,6 +5036,9 @@ static void direct_link_object(FileData *fd, Object *ob)
if (ob->sculpt) {
ob->sculpt = MEM_callocN(sizeof(SculptSession), "reload sculpt session");
}
+
+ link_list(fd, &ob->lodlevels);
+ ob->currentlod = ob->lodlevels.first;
}
/* ************ READ SCENE ***************** */
@@ -8396,6 +8409,12 @@ static void expand_object(FileData *fd, Main *mainvar, Object *ob)
expand_doit(fd, mainvar, ob->rigidbody_constraint->ob2);
}
+ if (ob->currentlod) {
+ LodLevel *level;
+ for (level = ob->lodlevels.first; level; level = level->next) {
+ expand_doit(fd, mainvar, level->source);
+ }
+ }
}
static void expand_scene(FileData *fd, Main *mainvar, Scene *sce)
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index be81f355e26..fd8c711fb1a 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -1537,6 +1537,8 @@ static void write_objects(WriteData *wd, ListBase *idbase)
write_particlesystems(wd, &ob->particlesystem);
write_modifiers(wd, &ob->modifiers);
+
+ writelist(wd, DATA, "LodLevel", &ob->lodlevels);
}
ob= ob->id.next;
}
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index 143a33c3fdd..ecc967d5662 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -47,6 +47,7 @@ set(SRC
object_group.c
object_hook.c
object_lattice.c
+ object_lod.c
object_modifier.c
object_ops.c
object_relations.c
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index 536f3f05ab2..8b0ddbbeb9b 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -255,5 +255,9 @@ void OBJECT_OT_group_remove(struct wmOperatorType *ot);
/* object_bake.c */
void OBJECT_OT_bake_image(wmOperatorType *ot);
+/* object_lod.c */
+void OBJECT_OT_lod_add(struct wmOperatorType *ot);
+void OBJECT_OT_lod_remove(struct wmOperatorType *ot);
+
#endif /* __OBJECT_INTERN_H__ */
diff --git a/source/blender/editors/object/object_lod.c b/source/blender/editors/object/object_lod.c
new file mode 100644
index 00000000000..952451ee21d
--- /dev/null
+++ b/source/blender/editors/object/object_lod.c
@@ -0,0 +1,102 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/object/object_lod.c
+ * \ingroup edobj
+ */
+
+
+#include "DNA_object_types.h"
+
+#include "BKE_context.h"
+#include "BKE_main.h"
+#include "BKE_object.h"
+
+#include "ED_screen.h"
+#include "ED_object.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "object_intern.h"
+
+static int object_lod_add_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = ED_object_context(C);
+ BKE_object_lod_add(ob);
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_lod_add(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Add Level of Detail";
+ ot->description = "Add a level of detail to this object";
+ ot->idname = "OBJECT_OT_lod_add";
+
+ /* api callbacks */
+ ot->exec = object_lod_add_exec;
+ ot->poll = ED_operator_objectmode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int object_lod_remove_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = ED_object_context(C);
+ int index = RNA_int_get(op->ptr, "index");
+ if(!BKE_object_lod_remove(ob, index))
+ return OPERATOR_CANCELLED;
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_LOD, CTX_wm_view3d(C));
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_lod_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Level of Detail";
+ ot->description = "Remove a level of detail from this object";
+ ot->idname = "OBJECT_OT_lod_remove";
+
+ /* api callbacks */
+ ot->exec = object_lod_remove_exec;
+ ot->poll = ED_operator_objectmode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_int(ot->srna, "index", 1, 1, INT_MAX, "Index", "", 1, INT_MAX);
+} \ No newline at end of file
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index efebbe8ddd7..a06a6d9781f 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -243,6 +243,9 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_bake_image);
WM_operatortype_append(OBJECT_OT_drop_named_material);
WM_operatortype_append(OBJECT_OT_laplaciandeform_bind);
+
+ WM_operatortype_append(OBJECT_OT_lod_add);
+ WM_operatortype_append(OBJECT_OT_lod_remove);
}
void ED_operatormacros_object(void)
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index 225c58207e7..1e48be2ffab 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -3359,7 +3359,7 @@ static void draw_mesh_object_outline(View3D *v3d, Object *ob, DerivedMesh *dm)
static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D *rv3d, Base *base,
const char dt, const unsigned char ob_wire_col[4], const short dflag)
{
- Object *ob = base->object;
+ Object *ob = BKE_object_lod_meshob_get(base->object, scene);
Mesh *me = ob->data;
Material *ma = give_current_material(ob, 1);
const short hasHaloMat = (ma && (ma->material_type == MA_TYPE_HALO) && !BKE_scene_use_new_shading_nodes(scene));
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 03b89972e55..9125d439432 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -809,6 +809,7 @@ static void view3d_main_area_listener(bScreen *sc, ScrArea *sa, ARegion *ar, wmN
case ND_CONSTRAINT:
case ND_KEYS:
case ND_PARTICLE:
+ case ND_LOD:
ED_region_tag_redraw(ar);
break;
}
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index 9dd6ab8f841..701fb26a562 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -1956,6 +1956,7 @@ static void draw_dupli_objects_color(Scene *scene, ARegion *ar, View3D *v3d, Bas
{
RegionView3D *rv3d = ar->regiondata;
ListBase *lb;
+ LodLevel *savedlod;
DupliObject *dob_prev = NULL, *dob, *dob_next = NULL;
Base tbase = {NULL};
BoundBox bb, *bb_tmp; /* use a copy because draw_object, calls clear_mesh_caches */
@@ -1976,6 +1977,13 @@ static void draw_dupli_objects_color(Scene *scene, ARegion *ar, View3D *v3d, Bas
for (; dob; dob_prev = dob, dob = dob_next, dob_next = dob_next ? dupli_step(dob_next->next) : NULL) {
tbase.object = dob->ob;
+ /* Make sure lod is updated from dupli's position */
+
+ copy_m4_m4(dob->ob->obmat, dob->mat);
+ savedlod = dob->ob->currentlod;
+ BKE_object_lod_update(dob->ob, rv3d->viewinv[3]);
+
+
/* extra service: draw the duplicator in drawtype of parent, minimum taken
* to allow e.g. boundbox box objects in groups for LOD */
dt = tbase.object->dt;
@@ -2051,13 +2059,13 @@ static void draw_dupli_objects_color(Scene *scene, ARegion *ar, View3D *v3d, Bas
glLoadMatrixf(rv3d->viewmat);
}
else {
- copy_m4_m4(dob->ob->obmat, dob->mat);
draw_object(scene, ar, v3d, &tbase, DRAW_CONSTCOLOR);
}
tbase.object->dt = dt;
tbase.object->dtx = dtx;
tbase.object->transflag = transflag;
+ tbase.object->currentlod = savedlod;
}
/* Transp afterdraw disabled, afterdraw only stores base pointers, and duplis can be same obj */
@@ -3183,6 +3191,18 @@ static void view3d_main_area_clear(Scene *scene, View3D *v3d, ARegion *ar)
}
}
+static void update_lods(Scene *scene, float camera_pos[3])
+{
+ Scene *sce_iter;
+ Base *base;
+ Object *ob;
+
+ for (SETLOOPER(scene, sce_iter, base)) {
+ ob = base->object;
+ BKE_object_lod_update(ob, camera_pos);
+ }
+}
+
/* warning: this function has duplicate drawing in ED_view3d_draw_offscreen() */
static void view3d_main_area_draw_objects(const bContext *C, ARegion *ar, const char **grid_unit)
{
@@ -3205,6 +3225,9 @@ static void view3d_main_area_draw_objects(const bContext *C, ARegion *ar, const
/* setup view matrices */
view3d_main_area_setup_view(scene, v3d, ar, NULL, NULL);
+ /* Make sure LoDs are up to date */
+ update_lods(scene, rv3d->viewinv[3]);
+
/* clear the background */
view3d_main_area_clear(scene, v3d, ar);
diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c
index ee59cf418bf..1f0e20a9210 100644
--- a/source/blender/gpu/intern/gpu_draw.c
+++ b/source/blender/gpu/intern/gpu_draw.c
@@ -1392,6 +1392,8 @@ void GPU_begin_object_materials(View3D *v3d, RegionView3D *rv3d, Scene *scene, O
int gamma = BKE_scene_check_color_management_enabled(scene);
int new_shading_nodes = BKE_scene_use_new_shading_nodes(scene);
int use_matcap = (v3d->flag2 & V3D_SHOW_SOLID_MATCAP); /* assumes v3d->defmaterial->preview is set */
+
+ ob = BKE_object_lod_matob_get(ob, scene);
/* initialize state */
memset(&GMS, 0, sizeof(GMS));
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 9d07e046096..bff3ef75a5a 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -105,6 +105,13 @@ enum {
BOUNDBOX_DIRTY = (1 << 1),
};
+typedef struct LodLevel {
+ struct LodLevel *next, *prev;
+ struct Object *source;
+ int flags;
+ float distance;
+} LodLevel;
+
typedef struct Object {
ID id;
struct AnimData *adt; /* animation data (must be immediately after id for utilities to use it) */
@@ -279,6 +286,9 @@ typedef struct Object {
float ima_ofs[2]; /* offset for image empties */
+ ListBase lodlevels; /* contains data for levels of detail */
+ LodLevel *currentlod;
+
/* Runtime valuated curve-specific data, not stored in the file */
struct CurveCache *curve_cache;
} Object;
@@ -470,6 +480,12 @@ enum {
OB_BOUND_CAPSULE = 7,
};
+/* lod flags */
+enum {
+ OB_LOD_USE_MESH = 1 << 0,
+ OB_LOD_USE_MAT = 1 << 1,
+};
+
/* **************** BASE ********************* */
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index 9c1d0c4cc2b..40bea3a6051 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -40,6 +40,7 @@
#include "DNA_meta_types.h"
#include "BLI_utildefines.h"
+#include "BLI_listbase.h"
#include "BKE_paint.h"
#include "BKE_editmesh.h"
@@ -1471,6 +1472,11 @@ int rna_Object_use_dynamic_topology_sculpting_get(PointerRNA *ptr)
return (ss && ss->bm);
}
+static void rna_Object_lod_distance_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ Object *ob = (Object *)ptr->id.data;
+ BKE_object_lod_sort(ob);
+}
#else
static void rna_def_vertex_group(BlenderRNA *brna)
@@ -2030,6 +2036,41 @@ static void rna_def_object_vertex_groups(BlenderRNA *brna, PropertyRNA *cprop)
}
+static void rna_def_object_lodlevel(BlenderRNA* brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "LodLevel", NULL);
+ RNA_def_struct_sdna(srna, "LodLevel");
+
+ prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_float_sdna(prop, NULL, "distance");
+ RNA_def_property_range(prop, 0.0, FLT_MAX);
+ RNA_def_property_ui_text(prop, "Distance", "Distance to begin using this level of detail");
+ RNA_def_property_update(prop, NC_OBJECT|ND_LOD, "rna_Object_lod_distance_update");
+
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "source");
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Object", "Object to use for this level of detail");
+ RNA_def_property_update(prop, NC_OBJECT|ND_LOD, NULL);
+
+ prop = RNA_def_property(srna, "use_mesh", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", OB_LOD_USE_MESH);
+ RNA_def_property_ui_text(prop, "Use Mesh", "Use the mesh from this object at this level of detail");
+ RNA_def_property_ui_icon(prop, ICON_MESH_DATA, 0);
+ RNA_def_property_update(prop, NC_OBJECT|ND_LOD, NULL);
+
+ prop = RNA_def_property(srna, "use_material", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", OB_LOD_USE_MAT);
+ RNA_def_property_ui_text(prop, "Use Material", "Use the material from this object at this level of detail");
+ RNA_def_property_ui_icon(prop, ICON_MATERIAL, 0);
+ RNA_def_property_update(prop, NC_OBJECT|ND_LOD, NULL);
+}
+
+
static void rna_def_object(BlenderRNA *brna)
{
StructRNA *srna;
@@ -2690,6 +2731,13 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Dynamic Topology Sculpting", NULL);
+ /* Levels of Detail */
+ prop = RNA_def_property(srna, "lod_levels", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "lodlevels", NULL);
+ RNA_def_property_struct_type(prop, "LodLevel");
+ RNA_def_property_ui_text(prop, "Level of Detail Levels", "A collection of detail levels to automatically switch between");
+ RNA_def_property_update(prop, NC_OBJECT|ND_LOD, NULL);
+
RNA_api_object(srna);
}
@@ -2800,6 +2848,7 @@ void RNA_def_object(BlenderRNA *brna)
rna_def_material_slot(brna);
rna_def_dupli_object(brna);
RNA_define_animate_sdna(true);
+ rna_def_object_lodlevel(brna);
}
#endif
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index dabb90b2ba0..83c17170f9e 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -297,6 +297,7 @@ typedef struct wmNotifier {
#define ND_PARTICLE (27<<16)
#define ND_POINTCACHE (28<<16)
#define ND_PARENT (29<<16)
+#define ND_LOD (30<<16)
/* NC_MATERIAL Material */
#define ND_SHADING (30<<16)
diff --git a/source/gameengine/Converter/BL_BlenderDataConversion.cpp b/source/gameengine/Converter/BL_BlenderDataConversion.cpp
index eeaffd9005a..9998f10e417 100644
--- a/source/gameengine/Converter/BL_BlenderDataConversion.cpp
+++ b/source/gameengine/Converter/BL_BlenderDataConversion.cpp
@@ -93,6 +93,7 @@
#include "KX_SoftBodyDeformer.h"
//#include "BL_ArmatureController.h"
#include "BLI_utildefines.h"
+#include "BLI_listbase.h"
#include "BlenderWorldInfo.h"
#include "KX_KetsjiEngine.h"
@@ -938,8 +939,15 @@ RAS_MeshObject* BL_ConvertMesh(Mesh* mesh, Object* blenderobj, KX_Scene* scene,
RAS_MeshObject *meshobj;
int lightlayer = blenderobj ? blenderobj->lay:(1<<20)-1; // all layers if no object.
- if ((meshobj = converter->FindGameMesh(mesh/*, ob->lay*/)) != NULL)
- return meshobj;
+ // Without checking names, we get some reuse we don't want that can cause
+ // problems with material LoDs.
+ if ((meshobj = converter->FindGameMesh(mesh/*, ob->lay*/)) != NULL) {
+ STR_String bge_name = meshobj->GetName();
+ STR_String blender_name = ((Mesh*)blenderobj->data)->id.name+2;
+ if (bge_name == blender_name)
+ return meshobj;
+ }
+
// Get DerivedMesh data
DerivedMesh *dm = CDDM_from_mesh(mesh, blenderobj);
DM_ensure_tessface(dm);
@@ -1852,6 +1860,24 @@ static KX_GameObject *gameobject_from_blenderobject(
// set transformation
gameobj->AddMesh(meshobj);
+
+ // gather levels of detail
+ if (BLI_countlist(&ob->lodlevels) > 1) {
+ LodLevel *lod = ((LodLevel*)ob->lodlevels.first)->next;
+ Mesh* lodmesh = mesh;
+ Object* lodmatob = ob;
+ gameobj->AddLodMesh(meshobj);
+ for (; lod; lod = lod->next) {
+ if (!lod->source || lod->source->type != OB_MESH) continue;
+ if (lod->flags & OB_LOD_USE_MESH) {
+ lodmesh = static_cast<Mesh*>(lod->source->data);
+ }
+ if (lod->flags & OB_LOD_USE_MAT) {
+ lodmatob = lod->source;
+ }
+ gameobj->AddLodMesh(BL_ConvertMesh(lodmesh, lodmatob, kxscene, converter, libloading));
+ }
+ }
// for all objects: check whether they want to
// respond to updates
diff --git a/source/gameengine/Ketsji/KX_Dome.cpp b/source/gameengine/Ketsji/KX_Dome.cpp
index f87d4799abc..43d74487542 100644
--- a/source/gameengine/Ketsji/KX_Dome.cpp
+++ b/source/gameengine/Ketsji/KX_Dome.cpp
@@ -2045,5 +2045,8 @@ void KX_Dome::RenderDomeFrame(KX_Scene* scene, KX_Camera* cam, int i)
scene->CalculateVisibleMeshes(m_rasterizer,cam);
scene->RenderBuckets(camtrans, m_rasterizer);
+
+ // update levels of detail
+ scene->UpdateObjectLods();
}
diff --git a/source/gameengine/Ketsji/KX_GameObject.cpp b/source/gameengine/Ketsji/KX_GameObject.cpp
index d3b5a987138..8b7e6667fae 100644
--- a/source/gameengine/Ketsji/KX_GameObject.cpp
+++ b/source/gameengine/Ketsji/KX_GameObject.cpp
@@ -71,6 +71,8 @@ typedef unsigned long uint_ptr;
#include "NG_NetworkScene.h" //Needed for sendMessage()
#include "KX_ObstacleSimulation.h"
+#include "BKE_object.h"
+
#include "BL_ActionManager.h"
#include "BL_Action.h"
@@ -732,6 +734,43 @@ void KX_GameObject::RemoveMeshes()
m_meshes.clear();
}
+void KX_GameObject::AddLodMesh(RAS_MeshObject* mesh)
+{
+ m_lodmeshes.push_back(mesh);
+}
+
+void KX_GameObject::UpdateLod(MT_Vector3 &cam_pos)
+{
+ // Handle dupligroups
+ if (this->m_pInstanceObjects) {
+ KX_GameObject * instob;
+ int count = this->m_pInstanceObjects->GetCount();
+ for (int i = 0; i < count; i++) {
+ instob = (KX_GameObject*)this->m_pInstanceObjects->GetValue(i);
+ instob->UpdateLod(cam_pos);
+ }
+ }
+
+ if (this->m_lodmeshes.empty()) return;
+
+ MT_Vector3 delta = this->NodeGetWorldPosition() - cam_pos;
+ float distance2 = delta.length2();
+
+ int level = 0;
+ Object *bob = this->GetBlenderObject();
+ LodLevel *lod = (LodLevel*) bob->lodlevels.first;
+ for (; lod; lod = lod->next, level++) {
+ if (!lod->source) level--;
+ if (!lod->next || lod->next->distance * lod->next->distance > distance2) break;
+ }
+
+ RAS_MeshObject *mesh = this->m_lodmeshes[level];
+
+ if (mesh != this->m_meshes[0]) {
+ this->GetScene()->ReplaceMesh(this, mesh, true, false);
+ }
+}
+
void KX_GameObject::UpdateTransform()
{
// HACK: saves function call for dynamic object, they are handled differently
diff --git a/source/gameengine/Ketsji/KX_GameObject.h b/source/gameengine/Ketsji/KX_GameObject.h
index 12aac68365b..ac0afca91eb 100644
--- a/source/gameengine/Ketsji/KX_GameObject.h
+++ b/source/gameengine/Ketsji/KX_GameObject.h
@@ -87,6 +87,7 @@ protected:
STR_String m_text;
int m_layer;
std::vector<RAS_MeshObject*> m_meshes;
+ std::vector<RAS_MeshObject*> m_lodmeshes;
SG_QList m_meshSlots; // head of mesh slots of this
struct Object* m_pBlenderObject;
struct Object* m_pBlenderGroupObject;
@@ -772,6 +773,23 @@ public:
}
/**
+ * Add a level of detail mesh to the object. These should
+ * be added in order.
+ */
+ void
+ AddLodMesh(
+ RAS_MeshObject* mesh
+ );
+
+ /**
+ * Updates the current lod level based on distance from camera.
+ */
+ void
+ UpdateLod(
+ MT_Vector3 &cam_pos
+ );
+
+ /**
* Pick out a mesh associated with the integer 'num'.
*/
RAS_MeshObject*
diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
index 0e9e2cd8328..3aa5a9f4f0e 100644
--- a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
+++ b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
@@ -1315,6 +1315,9 @@ void KX_KetsjiEngine::RenderFrame(KX_Scene* scene, KX_Camera* cam)
scene->CalculateVisibleMeshes(m_rasterizer,cam);
+ // update levels of detail
+ scene->UpdateObjectLods();
+
m_logger->StartLog(tc_rasterizer, m_kxsystem->GetTimeInSeconds(), true);
SG_SetActiveStage(SG_STAGE_RENDER);
diff --git a/source/gameengine/Ketsji/KX_Scene.cpp b/source/gameengine/Ketsji/KX_Scene.cpp
index 70924c65519..4a147b4eb60 100644
--- a/source/gameengine/Ketsji/KX_Scene.cpp
+++ b/source/gameengine/Ketsji/KX_Scene.cpp
@@ -1726,6 +1726,19 @@ void KX_Scene::RenderFonts()
}
}
+void KX_Scene::UpdateObjectLods(void)
+{
+ KX_GameObject* gameobj;
+ MT_Vector3 cam_pos = this->m_active_camera->NodeGetWorldPosition();
+
+ for (int i = 0; i < this->GetObjectList()->GetCount(); i++) {
+ gameobj = (KX_GameObject*) GetObjectList()->GetValue(i);
+ if (!gameobj->GetCulled()){
+ gameobj->UpdateLod(cam_pos);
+ }
+ }
+}
+
void KX_Scene::UpdateObjectActivity(void)
{
if (m_activity_culling) {
diff --git a/source/gameengine/Ketsji/KX_Scene.h b/source/gameengine/Ketsji/KX_Scene.h
index ee2a994d53c..50fac923fe2 100644
--- a/source/gameengine/Ketsji/KX_Scene.h
+++ b/source/gameengine/Ketsji/KX_Scene.h
@@ -544,6 +544,9 @@ public:
// Resume a suspended scene.
void Resume();
+
+ // Update the mesh for objects based on level of detail settings
+ void UpdateObjectLods(void);
// Update the activity box settings for objects in this scene, if needed.
void UpdateObjectActivity(void);