BGE: New hysteresis offset to improve LOD level transitions
This change introduces a new hysteresis parameter that it will be added or subtracted to/from the LOD distance to avoid popping when a LOD object moves close to the LOD transition continuously. Then, we have the following: - a new LOD Hysteresis setting per scene (default 10%) which is located in Scene context --> Level of Detail panel. This scene parameter also will active/deactive the scene hysteresis. - and a new LOD Hysteresis setting per object (default 10%) which is located in Object context --> Levels of Detail panel. The LOD hysteresis setting per object (if active) will overwrite the hysteresis setting per scene value. For the new blends: the hysteresis setting per scene would be active by default and the per object would be inactive by default. For the old blends: both hysteresis settings (per scene and per object) would be inactive by default. A quick way to take advantage of this feature for old blends would be to activate the hysteresis parameter in the scene context -> Level of Detail panel Reviewers: campbellbarton, kupoman, moguri Reviewed By: kupoman, moguri Subscribers: nonamejuju, lordodin Differential Revision: https://developer.blender.org/D957
This commit is contained in:
@@ -523,7 +523,27 @@ class SCENE_PT_game_navmesh(SceneButtonsPanel, Panel):
|
||||
row.prop(rd, "sample_max_error")
|
||||
|
||||
|
||||
class WorldButtonsPanel:
|
||||
class SCENE_PT_game_hysteresis(SceneButtonsPanel, Panel):
|
||||
bl_label = "Level of Detail"
|
||||
COMPAT_ENGINES = {'BLENDER_GAME'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
scene = context.scene
|
||||
return (scene and scene.render.engine in cls.COMPAT_ENGINES)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
gs = context.scene.game_settings
|
||||
|
||||
row = layout.row()
|
||||
row.prop(gs, "use_scene_hysteresis", text="Hysteresis")
|
||||
row = layout.row()
|
||||
row.active = gs.use_scene_hysteresis
|
||||
row.prop(gs, "scene_hysteresis_percentage", text="")
|
||||
|
||||
|
||||
class WorldButtonsPanel():
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "world"
|
||||
@@ -765,6 +785,7 @@ class OBJECT_PT_levels_of_detail(ObjectButtonsPanel, Panel):
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
ob = context.object
|
||||
gs = context.scene.game_settings
|
||||
|
||||
col = layout.column()
|
||||
|
||||
@@ -782,6 +803,13 @@ class OBJECT_PT_levels_of_detail(ObjectButtonsPanel, Panel):
|
||||
row.prop(level, "use_mesh", text="")
|
||||
row.prop(level, "use_material", text="")
|
||||
|
||||
row = box.row()
|
||||
row.active = gs.use_scene_hysteresis
|
||||
row.prop(level, "use_object_hysteresis", text="Hysteresis Override")
|
||||
row = box.row()
|
||||
row.active = gs.use_scene_hysteresis and level.use_object_hysteresis
|
||||
row.prop(level, "object_hysteresis_percentage", 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')
|
||||
|
||||
@@ -1098,10 +1098,12 @@ void BKE_object_lod_add(Object *ob)
|
||||
BLI_addtail(&ob->lodlevels, base);
|
||||
base->flags = OB_LOD_USE_MESH | OB_LOD_USE_MAT;
|
||||
base->source = ob;
|
||||
base->obhysteresis = 10;
|
||||
last = ob->currentlod = base;
|
||||
}
|
||||
|
||||
lod->distance = last->distance + 25.0f;
|
||||
lod->obhysteresis = 10;
|
||||
lod->flags = OB_LOD_USE_MESH | OB_LOD_USE_MAT;
|
||||
|
||||
BLI_addtail(&ob->lodlevels, lod);
|
||||
|
||||
@@ -677,6 +677,9 @@ Scene *BKE_scene_add(Main *bmain, const char *name)
|
||||
sce->gm.recastData.detailsampledist = 6.0f;
|
||||
sce->gm.recastData.detailsamplemaxerror = 1.0f;
|
||||
|
||||
sce->gm.lodflag = SCE_LOD_USE_HYST;
|
||||
sce->gm.scehysteresis = 10;
|
||||
|
||||
sce->gm.exitkey = 218; // Blender key code for ESC
|
||||
|
||||
sound_create_scene(sce);
|
||||
|
||||
@@ -654,5 +654,23 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* hysteresis setted to 10% but not actived */
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "LodLevel", "int", "obhysteresis")) {
|
||||
Object* ob;
|
||||
for (ob = main->object.first; ob; ob = ob->id.next) {
|
||||
LodLevel *level;
|
||||
for (level = ob->lodlevels.first; level; level = level->next) {
|
||||
level->obhysteresis = 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "GameData", "int", "scehysteresis")) {
|
||||
Scene *scene;
|
||||
for (scene = main->scene.first; scene; scene = scene->id.next) {
|
||||
scene->gm.scehysteresis = 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,9 @@ void BLO_update_defaults_startup_blend(Main *bmain)
|
||||
sculpt->detail_size = 12;
|
||||
}
|
||||
}
|
||||
|
||||
scene->gm.lodflag |= SCE_LOD_USE_HYST;
|
||||
scene->gm.scehysteresis = 10;
|
||||
}
|
||||
|
||||
for (linestyle = bmain->linestyle.first; linestyle; linestyle = linestyle->id.next) {
|
||||
|
||||
@@ -109,7 +109,8 @@ typedef struct LodLevel {
|
||||
struct LodLevel *next, *prev;
|
||||
struct Object *source;
|
||||
int flags;
|
||||
float distance;
|
||||
float distance, pad;
|
||||
int obhysteresis;
|
||||
} LodLevel;
|
||||
|
||||
typedef struct Object {
|
||||
@@ -484,6 +485,7 @@ enum {
|
||||
enum {
|
||||
OB_LOD_USE_MESH = 1 << 0,
|
||||
OB_LOD_USE_MAT = 1 << 1,
|
||||
OB_LOD_USE_HYST = 1 << 2,
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -718,7 +718,12 @@ typedef struct GameData {
|
||||
short obstacleSimulation;
|
||||
short raster_storage;
|
||||
float levelHeight;
|
||||
float deactivationtime, lineardeactthreshold, angulardeactthreshold, pad2;
|
||||
float deactivationtime, lineardeactthreshold, angulardeactthreshold;
|
||||
|
||||
/* Scene LoD */
|
||||
short lodflag, pad2;
|
||||
int scehysteresis, pad5;
|
||||
|
||||
} GameData;
|
||||
|
||||
#define STEREO_NOSTEREO 1
|
||||
@@ -791,6 +796,9 @@ enum {
|
||||
#pragma GCC poison GAME_MAT_TEXFACE
|
||||
#endif
|
||||
|
||||
/* GameData.lodflag */
|
||||
#define SCE_LOD_USE_HYST (1 << 0)
|
||||
|
||||
/* UV Paint */
|
||||
#define UV_SCULPT_LOCK_BORDERS 1
|
||||
#define UV_SCULPT_ALL_ISLANDS 2
|
||||
|
||||
@@ -2089,6 +2089,14 @@ static void rna_def_object_lodlevel(BlenderRNA *brna)
|
||||
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_hysteresis_percentage", PROP_INT, PROP_PERCENTAGE);
|
||||
RNA_def_property_int_sdna(prop, NULL, "obhysteresis");
|
||||
RNA_def_property_range(prop, 0, 100);
|
||||
RNA_def_property_ui_range(prop, 0, 100, 10, 1);
|
||||
RNA_def_property_ui_text(prop, "Hysteresis %", "Minimum distance change required to transition to the previous"
|
||||
" level of detail");
|
||||
RNA_def_property_update(prop, NC_OBJECT | ND_LOD, NULL);
|
||||
|
||||
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");
|
||||
@@ -2107,6 +2115,11 @@ static void rna_def_object_lodlevel(BlenderRNA *brna)
|
||||
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);
|
||||
|
||||
prop = RNA_def_property(srna, "use_object_hysteresis", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flags", OB_LOD_USE_HYST);
|
||||
RNA_def_property_ui_text(prop, "Hysteresis Override", "Override LoD Hysteresis scene setting for this Lod Level");
|
||||
RNA_def_property_update(prop, NC_OBJECT | ND_LOD, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3886,6 +3886,20 @@ static void rna_def_scene_game_data(BlenderRNA *brna)
|
||||
|
||||
/* Nestled Data */
|
||||
rna_def_scene_game_recast_data(brna);
|
||||
|
||||
/* LoD */
|
||||
prop = RNA_def_property(srna, "use_scene_hysteresis", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "lodflag", SCE_LOD_USE_HYST);
|
||||
RNA_def_property_ui_text(prop, "Hysteresis", "Use LoD Hysteresis setting for the scene");
|
||||
RNA_def_property_update(prop, NC_SCENE, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "scene_hysteresis_percentage", PROP_INT, PROP_PERCENTAGE);
|
||||
RNA_def_property_int_sdna(prop, NULL, "scehysteresis");
|
||||
RNA_def_property_range(prop, 0, 100);
|
||||
RNA_def_property_ui_range(prop, 0, 100, 10, 1);
|
||||
RNA_def_property_ui_text(prop, "Hysteresis %", "Minimum distance change required to transition to the previous"
|
||||
" level of detail");
|
||||
RNA_def_property_update(prop, NC_SCENE, NULL);
|
||||
}
|
||||
|
||||
static void rna_def_gpu_dof_fx(BlenderRNA *brna)
|
||||
|
||||
@@ -1554,6 +1554,10 @@ static KX_GameObject *gameobject_from_blenderobject(
|
||||
}
|
||||
gameobj->AddLodMesh(BL_ConvertMesh(lodmesh, lodmatob, kxscene, converter, libloading));
|
||||
}
|
||||
if (blenderscene->gm.lodflag & SCE_LOD_USE_HYST) {
|
||||
kxscene->SetLodHysteresis(true);
|
||||
gameobj->SetLodHysteresisValue(blenderscene->gm.scehysteresis);
|
||||
}
|
||||
}
|
||||
|
||||
// for all objects: check whether they want to
|
||||
|
||||
@@ -92,6 +92,8 @@ KX_GameObject::KX_GameObject(
|
||||
m_bDyna(false),
|
||||
m_layer(0),
|
||||
m_currentLodLevel(0),
|
||||
m_previousLodLevel(0),
|
||||
m_lodHysteresis(0),
|
||||
m_pBlenderObject(NULL),
|
||||
m_pBlenderGroupObject(NULL),
|
||||
m_bSuspendDynamics(false),
|
||||
@@ -784,6 +786,11 @@ void KX_GameObject::AddLodMesh(RAS_MeshObject* mesh)
|
||||
m_lodmeshes.push_back(mesh);
|
||||
}
|
||||
|
||||
void KX_GameObject::SetLodHysteresisValue(int hysteresis)
|
||||
{
|
||||
m_lodHysteresis = hysteresis;
|
||||
}
|
||||
|
||||
void KX_GameObject::UpdateLod(MT_Vector3 &cam_pos)
|
||||
{
|
||||
// Handle dupligroups
|
||||
@@ -804,14 +811,47 @@ void KX_GameObject::UpdateLod(MT_Vector3 &cam_pos)
|
||||
int level = 0;
|
||||
Object *bob = this->GetBlenderObject();
|
||||
LodLevel *lod = (LodLevel*) bob->lodlevels.first;
|
||||
KX_Scene *sce = this->GetScene();
|
||||
|
||||
for (; lod; lod = lod->next, level++) {
|
||||
if (!lod->source || lod->source->type != OB_MESH) level--;
|
||||
if (!lod->next || lod->next->distance * lod->next->distance > distance2) break;
|
||||
if (!lod->next) break;
|
||||
if (level == (this->m_previousLodLevel) || (level == (this->m_previousLodLevel + 1))) {
|
||||
short hysteresis = 0;
|
||||
if (sce->IsActivedLodHysteresis()) {
|
||||
// if exists, LoD level hysteresis will override scene hysteresis
|
||||
if (lod->next->flags & OB_LOD_USE_HYST) {
|
||||
hysteresis = lod->next->obhysteresis;
|
||||
}
|
||||
else if (this->m_lodHysteresis != 0) {
|
||||
hysteresis = m_lodHysteresis;
|
||||
}
|
||||
}
|
||||
float hystvariance = MT_abs(lod->next->distance - lod->distance) * hysteresis / 100;
|
||||
if ((lod->next->distance + hystvariance) * (lod->next->distance + hystvariance) > distance2)
|
||||
break;
|
||||
}
|
||||
else if (level == (this->m_previousLodLevel - 1)) {
|
||||
short hysteresis = 0;
|
||||
if (sce->IsActivedLodHysteresis()) {
|
||||
// if exists, LoD level hysteresis will override scene hysteresis
|
||||
if (lod->next->flags & OB_LOD_USE_HYST) {
|
||||
hysteresis = lod->next->obhysteresis;
|
||||
}
|
||||
else if (this->m_lodHysteresis != 0) {
|
||||
hysteresis = m_lodHysteresis;
|
||||
}
|
||||
}
|
||||
float hystvariance = MT_abs(lod->next->distance - lod->distance) * hysteresis / 100;
|
||||
if ((lod->next->distance - hystvariance) * (lod->next->distance - hystvariance) > distance2)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RAS_MeshObject *mesh = this->m_lodmeshes[level];
|
||||
this->m_currentLodLevel = level;
|
||||
if (mesh != this->m_meshes[0]) {
|
||||
this->m_previousLodLevel = level;
|
||||
this->GetScene()->ReplaceMesh(this, mesh, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,8 @@ protected:
|
||||
std::vector<RAS_MeshObject*> m_meshes;
|
||||
std::vector<RAS_MeshObject*> m_lodmeshes;
|
||||
int m_currentLodLevel;
|
||||
short m_previousLodLevel;
|
||||
int m_lodHysteresis;
|
||||
SG_QList m_meshSlots; // head of mesh slots of this
|
||||
struct Object* m_pBlenderObject;
|
||||
struct Object* m_pBlenderGroupObject;
|
||||
@@ -804,6 +806,14 @@ public:
|
||||
RAS_MeshObject* mesh
|
||||
);
|
||||
|
||||
/**
|
||||
* Set lod hysteresis value
|
||||
*/
|
||||
void
|
||||
SetLodHysteresisValue(
|
||||
int hysteresis
|
||||
);
|
||||
|
||||
/**
|
||||
* Updates the current lod level based on distance from camera.
|
||||
*/
|
||||
|
||||
@@ -156,7 +156,8 @@ KX_Scene::KX_Scene(class SCA_IInputDevice* keyboarddevice,
|
||||
m_networkDeviceInterface(ndi),
|
||||
m_active_camera(NULL),
|
||||
m_ueberExecutionPriority(0),
|
||||
m_blenderScene(scene)
|
||||
m_blenderScene(scene),
|
||||
m_isActivedHysteresis(false)
|
||||
{
|
||||
m_suspendedtime = 0.0;
|
||||
m_suspendeddelta = 0.0;
|
||||
@@ -1763,6 +1764,16 @@ void KX_Scene::UpdateObjectLods(void)
|
||||
}
|
||||
}
|
||||
|
||||
void KX_Scene::SetLodHysteresis(bool active)
|
||||
{
|
||||
m_isActivedHysteresis = active;
|
||||
}
|
||||
|
||||
bool KX_Scene::IsActivedLodHysteresis(void)
|
||||
{
|
||||
return m_isActivedHysteresis;
|
||||
}
|
||||
|
||||
void KX_Scene::UpdateObjectActivity(void)
|
||||
{
|
||||
if (m_activity_culling) {
|
||||
|
||||
@@ -295,6 +295,11 @@ protected:
|
||||
|
||||
KX_ObstacleSimulation* m_obstacleSimulation;
|
||||
|
||||
/**
|
||||
* Does this scene active the LoD Hysteresis?
|
||||
*/
|
||||
bool m_isActivedHysteresis;
|
||||
|
||||
public:
|
||||
KX_Scene(class SCA_IInputDevice* keyboarddevice,
|
||||
class SCA_IInputDevice* mousedevice,
|
||||
@@ -546,6 +551,10 @@ public:
|
||||
|
||||
// Update the mesh for objects based on level of detail settings
|
||||
void UpdateObjectLods(void);
|
||||
|
||||
// Enable/disable LoD Hysteresis
|
||||
void SetLodHysteresis(bool active);
|
||||
bool IsActivedLodHysteresis();
|
||||
|
||||
// Update the activity box settings for objects in this scene, if needed.
|
||||
void UpdateObjectActivity(void);
|
||||
|
||||
Reference in New Issue
Block a user