Game Engine: Level of detail support and tools
authorDaniel Stokes <kupomail@gmail.com>
Tue, 17 Dec 2013 22:42:47 +0000 (14:42 -0800)
committerkupoman <kupomail@gmail.com>
Wed, 18 Dec 2013 01:03:27 +0000 (17:03 -0800)
Levels of detail can be added and modified in the object panel. The object
panel also contains new tools for generating levels of detail, setting up
levels of detail based on object names (useful for importing), and
clearing an object's level of detail settings. This is meant as a game
engine feature, though the level of details settings can be previewed in
the viewport.

Reviewed By: moguri, nexyon, brecht

Differential Revision: http://developer.blender.org/D109

25 files changed:
release/scripts/startup/bl_operators/object.py
release/scripts/startup/bl_ui/properties_object.py
source/blender/blenkernel/BKE_object.h
source/blender/blenkernel/intern/material.c
source/blender/blenkernel/intern/object.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/editors/object/CMakeLists.txt
source/blender/editors/object/object_intern.h
source/blender/editors/object/object_lod.c [new file with mode: 0644]
source/blender/editors/object/object_ops.c
source/blender/editors/space_view3d/drawobject.c
source/blender/editors/space_view3d/space_view3d.c
source/blender/editors/space_view3d/view3d_draw.c
source/blender/gpu/intern/gpu_draw.c
source/blender/makesdna/DNA_object_types.h
source/blender/makesrna/intern/rna_object.c
source/blender/windowmanager/WM_types.h
source/gameengine/Converter/BL_BlenderDataConversion.cpp
source/gameengine/Ketsji/KX_Dome.cpp
source/gameengine/Ketsji/KX_GameObject.cpp
source/gameengine/Ketsji/KX_GameObject.h
source/gameengine/Ketsji/KX_KetsjiEngine.cpp
source/gameengine/Ketsji/KX_Scene.cpp
source/gameengine/Ketsji/KX_Scene.h

index e5e53d6a4dc22ebb966407e42ae816b9fb81069f..3218af06cc71b88d405575ddcbc1948bbacc208e 100644 (file)
@@ -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'}
+
index cbebdafbf2e57dbeba049a72f8adc315acffe746..dcd32b17cbd38fbb70fc7e48b3af72de3ed2d062 100644 (file)
@@ -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"
 
index 434175624b715c575a173043e344d013c2e7796b..e6e6d621ef37ac73f0b1aa7b6d7aad11ea8661d6 100644 (file)
@@ -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);
index cf9b838a7c27a7a53a4e744586463f589799c9c2..9df523a5bfe22eea9022b188bfdbe765221f7582 100644 (file)
@@ -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) */
index 0694bde7b910b863acc121d72b173bfde68a107f..70b5fb9d4203bc3ace051e52d2f214b297ff50f6 100644 (file)
@@ -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;
 
index ac34f81907a6e4819444e12f09704c86cd74afa0..f3244a63a8ba29834328e4ba161f2b2965cdd9be 100644 (file)
@@ -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)
index be81f355e2658ce690b61472a3e981ff2d628214..fd8c711fb1ac4bb52ca4ef56242272c0f33d6555 100644 (file)
@@ -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;
        }
index 143a33c3fdd0a968a6c32d495ccd7f246e0fbb2c..ecc967d56620e0bff26c47d989242396e2745e77 100644 (file)
@@ -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
index 536f3f05ab23e4ae6150ff02d35fb2ea1bb3b0fe..8b0ddbbeb9b97c884dd02c8f887ed681088bbf1d 100644 (file)
@@ -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 (file)
index 0000000..952451e
--- /dev/null
@@ -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
index efebbe8ddd71b842b8d29412bbd67551e6d1ba95..a06a6d9781fc2d2445364e687862e95a14192b34 100644 (file)
@@ -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)
index 225c58207e7f0a246b30161313ff542e6f733adf..1e48be2ffabe71dffdd6900a645bd9aef647105e 100644 (file)
@@ -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));
index 03b89972e559d52c39aaad429bd127728f4495b6..9125d439432d28ef9131cb69d3c8534c07f7b397 100644 (file)
@@ -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;
                        }
index 9dd6ab8f841a2bc90814536cdddc07bc08190ea9..701fb26a56240977662606c3de0900bb7f48c8c4 100644 (file)
@@ -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);
 
index ee59cf418bf492cd479a0be322ea7e49de8275e6..1f0e20a92100b00d577fdceb4e34915811a432ec 100644 (file)
@@ -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));
index 9d07e046096141fb104413ed483ef2fa71f6ed7c..bff3ef75a5a19297e98e5e4315756c9483eb83df 100644 (file)
@@ -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 ********************* */
 
index 9c1d0c4cc2bcdcc92b516e563cfa16cf9ff8d7f3..40bea3a60518355104a6b5460540518e2fa36e58 100644 (file)
@@ -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
index dabb90b2ba09035046f5acbf47dcd08ad6508107..83c17170f9eed90f2af3391c72382d261eb86bcc 100644 (file)
@@ -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)
index eeaffd9005a0ef8e7a775e77b1166078ec3237d0..9998f10e41718d91aa0857364ba95ba4b0f4b7b6 100644 (file)
@@ -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
index f87d4799abc58b016dee50df599912d2afcb039a..43d744875425e3608af55a7cd8a8bbea487339a7 100644 (file)
@@ -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();
 }
 
index d3b5a987138d701b1a8f5307cc0909c22db31f40..8b7e6667faeb1ef0d6da14b93bf1ab46d8044071 100644 (file)
@@ -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
index 12aac68365b6c83ef8bc81eee661f6db52d54884..ac0afca91eb6e6b29fb3aaaf2f39a6d44a3f9448 100644 (file)
@@ -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;
@@ -771,6 +772,23 @@ public:
                m_meshes.push_back(mesh);
        }
 
+       /**
+        * 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'.
         */
index 0e9e2cd832871e7f44b003ee92534529042ae277..3aa5a9f4f0ef35ee869b8c6f1078d571c2002e30 100644 (file)
@@ -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);
 
index 70924c65519b47025a48da14e41097713586a28e..4a147b4eb6043585b98345c5855c59d5169bdb0f 100644 (file)
@@ -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) {
index ee2a994d53c32e0738978524132535a64b3bb52e..50fac923fe23aaef2688bbd6c9b74f3719906857 100644 (file)
@@ -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);