Compare commits
54 Commits
tmp-new-gp
...
soc-2013-b
Author | SHA1 | Date | |
---|---|---|---|
0aa392d2ff | |||
6167313105 | |||
ee26dba873 | |||
ad94e0b860 | |||
9fc387dee3 | |||
1a100a3124 | |||
b247e869ae | |||
aeaf0cf32f | |||
3757456a08 | |||
6338da9dcd | |||
77d8e4992e | |||
43ab2e5184 | |||
eebd916aca | |||
3b5d72f1fc | |||
aa07acdca6 | |||
a01c6c61c2 | |||
c984e7c992 | |||
7e3de1c868 | |||
ba856c1719 | |||
600ed7cd72 | |||
924b6b16d9 | |||
626523cc1d | |||
74f3fb4a64 | |||
99904ff39b | |||
eea8747b66 | |||
3b2c1d50ed | |||
3ce8456887 | |||
9cf39928ef | |||
d810299b49 | |||
872dc31765 | |||
26bacdebed | |||
495d00f0ae | |||
1c47d1fa02 | |||
3ac2b4e1a6 | |||
10a57d7f84 | |||
6fca4ab09e | |||
0f67698ace | |||
593093f0dd | |||
e08e2bd90f | |||
1362c9f6ce | |||
7e95068429 | |||
ab859a3357 | |||
8243320622 | |||
0f0438f406 | |||
a27959bb09 | |||
3a7e61fe10 | |||
91d24ee4a4 | |||
e7861151a2 | |||
c4d2a9301c | |||
b6cb40040c | |||
863a2cb35e | |||
7546fd071d | |||
eddbe08ca1 | |||
301d2c44ce |
Binary file not shown.
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 251 KiB |
@@ -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"
|
||||
|
||||
|
@@ -84,6 +84,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) */
|
||||
|
@@ -356,6 +356,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);
|
||||
@@ -395,6 +397,7 @@ void BKE_object_unlink(Object *ob)
|
||||
ModifierData *md;
|
||||
ARegion *ar;
|
||||
RegionView3D *rv3d;
|
||||
LodLevel *lod;
|
||||
int a, found;
|
||||
|
||||
unlink_controllers(&ob->controllers);
|
||||
@@ -573,6 +576,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;
|
||||
}
|
||||
|
||||
@@ -876,6 +885,7 @@ static const char *get_obdata_defname(int type)
|
||||
Object *BKE_object_add_only_object(Main *bmain, int type, const char *name)
|
||||
{
|
||||
Object *ob;
|
||||
LodLevel *base;
|
||||
|
||||
if (!name)
|
||||
name = get_obdata_defname(type);
|
||||
@@ -980,6 +990,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;
|
||||
@@ -1191,6 +1333,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) &&
|
||||
@@ -1306,6 +1458,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;
|
||||
|
||||
|
@@ -4520,6 +4520,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5045,6 +5055,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 ***************** */
|
||||
@@ -10705,6 +10718,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)
|
||||
|
@@ -1532,6 +1532,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
|
||||
|
@@ -249,5 +249,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__ */
|
||||
|
||||
|
102
source/blender/editors/object/object_lod.c
Normal file
102
source/blender/editors/object/object_lod.c
Normal file
@@ -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);
|
||||
}
|
@@ -238,6 +238,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_lod_add);
|
||||
WM_operatortype_append(OBJECT_OT_lod_remove);
|
||||
}
|
||||
|
||||
void ED_operatormacros_object(void)
|
||||
|
@@ -3358,7 +3358,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));
|
||||
|
@@ -788,6 +788,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;
|
||||
}
|
||||
|
@@ -2003,6 +2003,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 */
|
||||
@@ -2023,6 +2024,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;
|
||||
@@ -2093,13 +2101,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 */
|
||||
@@ -3225,6 +3233,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)
|
||||
{
|
||||
@@ -3247,6 +3267,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);
|
||||
|
||||
|
@@ -1388,6 +1388,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"
|
||||
@@ -1458,6 +1459,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)
|
||||
@@ -2013,6 +2019,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;
|
||||
@@ -2673,6 +2714,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);
|
||||
}
|
||||
|
||||
@@ -2783,6 +2831,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)
|
||||
|
@@ -96,6 +96,7 @@
|
||||
#include "KX_SoftBodyDeformer.h"
|
||||
//#include "BL_ArmatureController.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BlenderWorldInfo.h"
|
||||
|
||||
#include "KX_KetsjiEngine.h"
|
||||
@@ -1076,8 +1077,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);
|
||||
@@ -1989,6 +1997,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
|
||||
|
@@ -2047,6 +2047,10 @@ void KX_Dome::RenderDomeFrame(KX_Scene* scene, KX_Camera* cam, int i)
|
||||
cam->NodeUpdateGS(0.f);
|
||||
|
||||
scene->CalculateVisibleMeshes(m_rasterizer,cam);
|
||||
|
||||
// update levels of detail
|
||||
scene->UpdateObjectLods();
|
||||
|
||||
scene->RenderBuckets(camtrans, m_rasterizer, m_rendertools);
|
||||
}
|
||||
|
||||
|
@@ -72,6 +72,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"
|
||||
|
||||
@@ -727,6 +729,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
|
||||
|
@@ -88,6 +88,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;
|
||||
@@ -757,6 +758,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'.
|
||||
*/
|
||||
|
@@ -1330,6 +1330,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);
|
||||
|
||||
|
@@ -1694,6 +1694,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) {
|
||||
|
@@ -545,6 +545,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);
|
||||
|
Reference in New Issue
Block a user