Game Engine: Level of detail support and tools

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
This commit is contained in:
Daniel Stokes 2013-12-17 14:42:47 -08:00 committed by kupoman
parent 173f7a3d30
commit e9e08a1d12
25 changed files with 690 additions and 6 deletions

@ -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'}

@ -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"

@ -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);

@ -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) */

@ -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;

@ -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)

@ -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;
}

@ -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

@ -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__ */

@ -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);
}

@ -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)

@ -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));

@ -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;
}

@ -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);

@ -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));

@ -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 ********************* */

@ -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

@ -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)

@ -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

@ -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();
}

@ -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

@ -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'.
*/

@ -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);

@ -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) {

@ -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);