Completed implementation of Model LOD system
--HG-- extra : convert_revision : svn%3A7752d6cf-9f14-4ad2-affc-04f1e67b81a5/trunk%40117
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "KRAABB.h"
|
||||
#include "KRMat4.h"
|
||||
#include "KRVector2.h"
|
||||
#include "assert.h"
|
||||
|
||||
KRAABB::KRAABB(const KRVector3 &minPoint, const KRVector3 &maxPoint)
|
||||
@@ -182,3 +183,31 @@ bool KRAABB::visible(const KRMat4 &matViewProjection) const
|
||||
//is_visible = true;
|
||||
return is_visible;
|
||||
}
|
||||
|
||||
|
||||
float KRAABB::coverage(const KRMat4 &matMVP, const KRVector2 viewportSize) const
|
||||
{
|
||||
if(!visible(matMVP)) {
|
||||
return 0.0f; // Culled out by view frustrum
|
||||
} else {
|
||||
KRVector2 screen_min;
|
||||
KRVector2 screen_max;
|
||||
// Loop through all corners and transform them to screen space
|
||||
for(int i=0; i<8; i++) {
|
||||
KRVector3 screen_pos = KRMat4::Dot(matMVP, KRVector3(i & 1 ? min.x : max.x, i & 2 ? min.y : max.y, i & 4 ? min.z :max.z));
|
||||
if(i==0) {
|
||||
screen_min.x = screen_pos.x;
|
||||
screen_min.y = screen_pos.y;
|
||||
screen_max.x = screen_pos.x;
|
||||
screen_max.y = screen_pos.y;
|
||||
} else {
|
||||
if(screen_pos.x < screen_min.x) screen_min.x = screen_pos.x;
|
||||
if(screen_pos.y < screen_min.y) screen_min.y = screen_pos.y;
|
||||
if(screen_pos.x > screen_max.x) screen_max.x = screen_pos.x;
|
||||
if(screen_pos.y > screen_max.y) screen_max.y = screen_pos.y;
|
||||
}
|
||||
}
|
||||
|
||||
return (screen_max.x - screen_min.x) / viewportSize.x * (screen_max.y - screen_min.y) / viewportSize.y;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "KRVector3.h"
|
||||
|
||||
class KRMat4;
|
||||
class KRVector2;
|
||||
|
||||
class KRAABB {
|
||||
public:
|
||||
@@ -39,6 +40,7 @@ public:
|
||||
KRVector3 max;
|
||||
|
||||
static KRAABB Infinite();
|
||||
float coverage(const KRMat4 &matMVP, const KRVector2 viewportSize) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -35,11 +35,11 @@
|
||||
#import "KRModel.h"
|
||||
#include <assert.h>
|
||||
|
||||
KRInstance::KRInstance(KRScene &scene, std::string instance_name, std::string model_name, std::string light_map) : KRNode(scene, instance_name) {
|
||||
KRInstance::KRInstance(KRScene &scene, std::string instance_name, std::string model_name, std::string light_map, float lod_min_coverage) : KRNode(scene, instance_name) {
|
||||
m_lightMap = light_map;
|
||||
m_pLightMap = NULL;
|
||||
m_pModel = NULL;
|
||||
m_model_name = model_name;
|
||||
m_min_lod_coverage = lod_min_coverage;
|
||||
}
|
||||
|
||||
KRInstance::~KRInstance() {
|
||||
@@ -55,6 +55,7 @@ tinyxml2::XMLElement *KRInstance::saveXML( tinyxml2::XMLNode *parent)
|
||||
tinyxml2::XMLElement *e = KRNode::saveXML(parent);
|
||||
e->SetAttribute("mesh_name", m_model_name.c_str());
|
||||
e->SetAttribute("light_map", m_lightMap.c_str());
|
||||
e->SetAttribute("lod_min_coverage", m_min_lod_coverage);
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -65,9 +66,9 @@ KRMat4 &KRInstance::getModelMatrix() {
|
||||
}
|
||||
|
||||
void KRInstance::loadModel() {
|
||||
if(m_pModel == NULL) {
|
||||
m_pModel = m_pContext->getModelManager()->getModel(m_model_name.c_str());
|
||||
if(m_pModel != NULL) {
|
||||
if(m_models.size() == 0) {
|
||||
m_models = m_pContext->getModelManager()->getModel(m_model_name.c_str()); // The model manager returns the LOD levels in sorted order, with the highest detail first
|
||||
if(m_models.size() > 0) {
|
||||
getScene().notify_sceneGraphModify(this);
|
||||
}
|
||||
// if(m_pModel == NULL) {
|
||||
@@ -89,13 +90,27 @@ void KRInstance::render(KRCamera *pCamera, KRContext *pContext, KRMat4 &viewMatr
|
||||
|
||||
loadModel();
|
||||
|
||||
KRMat4 projectionMatrix;
|
||||
if(renderPass != KRNode::RENDER_PASS_SHADOWMAP) {
|
||||
projectionMatrix = pCamera->getProjectionMatrix();
|
||||
}
|
||||
if(m_models.size() > 0) {
|
||||
KRMat4 projectionMatrix;
|
||||
if(renderPass != KRNode::RENDER_PASS_SHADOWMAP) {
|
||||
projectionMatrix = pCamera->getProjectionMatrix();
|
||||
}
|
||||
KRMat4 matVP = viewMatrix * projectionMatrix;
|
||||
float lod_coverage = getBounds().coverage(matVP, pCamera->getViewportSize()); // This also checks the view frustrum culling
|
||||
if(lod_coverage > m_min_lod_coverage) {
|
||||
|
||||
if(m_pModel != NULL) {
|
||||
if(getBounds().visible(viewMatrix * projectionMatrix)) {
|
||||
// ---===--- Select the best LOD model based on screen coverage ---===---
|
||||
std::vector<KRModel *>::iterator itr=m_models.begin();
|
||||
KRModel *pModel = *itr++;
|
||||
|
||||
while(itr != m_models.end()) {
|
||||
KRModel *pLODModel = *itr++;
|
||||
if((float)pLODModel->getLODCoverage() / 100.0f > lod_coverage) {
|
||||
pModel = pLODModel;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(m_pLightMap == NULL && m_lightMap.size()) {
|
||||
m_pLightMap = pContext->getTextureManager()->getTexture(m_lightMap.c_str());
|
||||
@@ -121,7 +136,7 @@ void KRInstance::render(KRCamera *pCamera, KRContext *pContext, KRMat4 &viewMatr
|
||||
KRVector3 cameraPosObject = KRMat4::Dot(inverseModelMatrix, cameraPosition);
|
||||
KRVector3 lightDirObject = KRMat4::Dot(inverseModelMatrix, lightDirection);
|
||||
|
||||
m_pModel->render(pCamera, pContext, viewMatrix, matModelToView, mvpmatrix, lightDirection, pShadowMatrices, shadowDepthTextures, cShadowBuffers, m_pLightMap, renderPass);
|
||||
pModel->render(pCamera, pContext, viewMatrix, matModelToView, mvpmatrix, lightDirection, pShadowMatrices, shadowDepthTextures, cShadowBuffers, m_pLightMap, renderPass);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,8 +145,8 @@ void KRInstance::render(KRCamera *pCamera, KRContext *pContext, KRMat4 &viewMatr
|
||||
#endif
|
||||
|
||||
bool KRInstance::hasTransparency() {
|
||||
if(m_pModel) {
|
||||
return m_pModel->hasTransparency();
|
||||
if(m_models.size() > 0) {
|
||||
return m_models[0]->hasTransparency();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -142,9 +157,9 @@ KRAABB KRInstance::getBounds() {
|
||||
loadModel();
|
||||
|
||||
KRVector3 meshMin, meshMax;
|
||||
if(m_pModel) {
|
||||
meshMin = m_pModel->getMinPoint();
|
||||
meshMax = m_pModel->getMaxPoint();
|
||||
if(m_models.size() > 0) {
|
||||
meshMin = m_models[0]->getMinPoint();
|
||||
meshMax = m_models[0]->getMaxPoint();
|
||||
} else {
|
||||
meshMin = -KRVector3::Max();
|
||||
meshMax = KRVector3::Max();
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
class KRInstance : public KRNode {
|
||||
|
||||
public:
|
||||
KRInstance(KRScene &scene, std::string instance_name, std::string model_name, std::string light_map);
|
||||
KRInstance(KRScene &scene, std::string instance_name, std::string model_name, std::string light_map, float lod_min_coverage);
|
||||
virtual ~KRInstance();
|
||||
|
||||
virtual std::string getElementName();
|
||||
@@ -70,12 +70,14 @@ public:
|
||||
void calcModelMatrix();
|
||||
|
||||
private:
|
||||
KRModel *m_pModel;
|
||||
std::vector<KRModel *> m_models;
|
||||
KRMat4 m_modelMatrix;
|
||||
KRTexture *m_pLightMap;
|
||||
std::string m_lightMap;
|
||||
std::string m_model_name;
|
||||
|
||||
|
||||
float m_min_lod_coverage;
|
||||
void loadModel();
|
||||
};
|
||||
|
||||
|
||||
@@ -52,6 +52,23 @@ KRModel::KRModel(KRContext &context, std::string name) : KRResource(context, nam
|
||||
m_materials.clear();
|
||||
m_uniqueMaterials.clear();
|
||||
m_pData = new KRDataBlock();
|
||||
m_lodCoverage = 100; // Default to being the full detail mesh; not an LOD level
|
||||
m_lodBaseName = name;
|
||||
|
||||
size_t last_underscore_pos = name.find_last_of('_');
|
||||
if(last_underscore_pos != std::string::npos) {
|
||||
// Found an underscore
|
||||
std::string suffix = name.substr(last_underscore_pos + 1);
|
||||
if(suffix.find_first_of("lod") == 0) {
|
||||
std::string lod_level_string = suffix.substr(3);
|
||||
char *end = NULL;
|
||||
int c = (int)strtol(suffix.c_str(), &end, 10);
|
||||
if(m_lodCoverage < 0 || m_lodCoverage > 100 || *end != '\0') {
|
||||
m_lodCoverage = c;
|
||||
m_lodBaseName = name.substr(0, last_underscore_pos - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KRModel::KRModel(KRContext &context, std::string name, KRDataBlock *data) : KRResource(context, name) {
|
||||
@@ -59,6 +76,7 @@ KRModel::KRModel(KRContext &context, std::string name, KRDataBlock *data) : KRRe
|
||||
m_materials.clear();
|
||||
m_uniqueMaterials.clear();
|
||||
m_pData = new KRDataBlock();
|
||||
m_lodCoverage = 100;
|
||||
loadPack(data);
|
||||
}
|
||||
|
||||
@@ -434,3 +452,17 @@ void KRModel::clearBuffers() {
|
||||
m_submeshes.clear();
|
||||
}
|
||||
|
||||
int KRModel::getLODCoverage() const {
|
||||
return m_lodCoverage;
|
||||
}
|
||||
|
||||
std::string KRModel::getLODBaseName() const {
|
||||
return m_lodBaseName;
|
||||
}
|
||||
|
||||
// Predicate used with std::sort to sort by highest detail model first, decending to lowest detail LOD model
|
||||
bool KRModel::lod_sort_predicate(const KRModel *m1, const KRModel *m2)
|
||||
{
|
||||
return m1->m_lodCoverage > m2->m_lodCoverage;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,8 @@ public:
|
||||
|
||||
#endif
|
||||
|
||||
std::string m_lodBaseName;
|
||||
|
||||
virtual std::string getExtension();
|
||||
virtual bool save(const std::string& path);
|
||||
|
||||
@@ -119,8 +121,14 @@ public:
|
||||
char szName[256];
|
||||
} pack_material;
|
||||
|
||||
int getLODCoverage() const;
|
||||
std::string getLODBaseName() const;
|
||||
|
||||
|
||||
static bool lod_sort_predicate(const KRModel *m1, const KRModel *m2);
|
||||
|
||||
private:
|
||||
int m_lodCoverage; // This LOD level is activated when the bounding box of the model will cover less than this percent of the screen (100 = highest detail model)
|
||||
vector<KRMaterial *> m_materials;
|
||||
set<KRMaterial *> m_uniqueMaterials;
|
||||
|
||||
|
||||
@@ -46,26 +46,45 @@ KRModelManager::KRModelManager(KRContext &context) : KRContextObject(context) {
|
||||
}
|
||||
|
||||
KRModelManager::~KRModelManager() {
|
||||
for(map<std::string, KRModel *>::iterator itr = m_models.begin(); itr != m_models.end(); ++itr){
|
||||
for(std::multimap<std::string, KRModel *>::iterator itr = m_models.begin(); itr != m_models.end(); ++itr){
|
||||
delete (*itr).second;
|
||||
}
|
||||
m_models.empty();
|
||||
}
|
||||
|
||||
KRModel *KRModelManager::loadModel(const char *szName, KRDataBlock *pData) {
|
||||
KRModel *pModel = new KRModel(*m_pContext, szName, pData);
|
||||
m_models[szName] = pModel;
|
||||
|
||||
std::string lowerName = szName;
|
||||
std::transform(lowerName.begin(), lowerName.end(),
|
||||
lowerName.begin(), ::tolower);
|
||||
|
||||
|
||||
KRModel *pModel = new KRModel(*m_pContext, lowerName, pData);
|
||||
m_models.insert(std::pair<std::string, KRModel *>(pModel->getName(), pModel));
|
||||
|
||||
return pModel;
|
||||
}
|
||||
|
||||
KRModel *KRModelManager::getModel(const char *szName) {
|
||||
std::map<std::string, KRModel *>::iterator itr_match = m_models.find(szName);
|
||||
if(itr_match == m_models.end()) {
|
||||
fprintf(stderr, "ERROR: Model not found: %s\n", szName);
|
||||
return NULL;
|
||||
} else {
|
||||
return itr_match->second;
|
||||
std::vector<KRModel *> KRModelManager::getModel(const char *szName) {
|
||||
std::string lowerName = szName;
|
||||
std::transform(lowerName.begin(), lowerName.end(),
|
||||
lowerName.begin(), ::tolower);
|
||||
|
||||
|
||||
std::vector<KRModel *> matching_models;
|
||||
|
||||
std::pair<std::multimap<std::string, KRModel *>::iterator, std::multimap<std::string, KRModel *>::iterator> range = m_models.equal_range(lowerName);
|
||||
for(std::multimap<std::string, KRModel *>::iterator itr_match = range.first; itr_match != range.second; itr_match++) {
|
||||
matching_models.push_back(itr_match->second);
|
||||
}
|
||||
|
||||
std::sort(matching_models.begin(), matching_models.end(), KRModel::lod_sort_predicate);
|
||||
|
||||
if(matching_models.size() == 0) {
|
||||
fprintf(stderr, "ERROR: Model not found: %s\n", lowerName.c_str());
|
||||
}
|
||||
|
||||
return matching_models;
|
||||
}
|
||||
|
||||
KRModel *KRModelManager::getFirstModel() {
|
||||
@@ -73,7 +92,7 @@ KRModel *KRModelManager::getFirstModel() {
|
||||
return (*model_itr).second;
|
||||
}
|
||||
|
||||
std::map<std::string, KRModel *> KRModelManager::getModels() {
|
||||
std::multimap<std::string, KRModel *> KRModelManager::getModels() {
|
||||
return m_models;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,11 +54,11 @@ public:
|
||||
void rotateBuffers(bool new_frame);
|
||||
|
||||
KRModel *loadModel(const char *szName, KRDataBlock *pData);
|
||||
KRModel *getModel(const char *szName);
|
||||
std::vector<KRModel *> getModel(const char *szName);
|
||||
KRModel *getFirstModel();
|
||||
|
||||
std::vector<std::string> getModelNames();
|
||||
std::map<std::string, KRModel *> getModels();
|
||||
std::multimap<std::string, KRModel *> getModels();
|
||||
|
||||
|
||||
void bindVBO(GLvoid *data, GLsizeiptr size, bool enable_vertex, bool enable_normal, bool enable_tangent, bool enable_uva, bool enable_uvb);
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
void configureAttribs(bool enable_vertex, bool enable_normal, bool enable_tangent, bool enable_uva, bool enable_uvb);
|
||||
|
||||
private:
|
||||
std::map<std::string, KRModel *> m_models;
|
||||
std::multimap<std::string, KRModel *> m_models; // Multiple models with the same name/key may be inserted, representing multiple LOD levels of the model
|
||||
|
||||
typedef struct vbo_info {
|
||||
GLuint handle;
|
||||
|
||||
@@ -138,7 +138,11 @@ KRNode *KRNode::LoadXML(KRScene &scene, tinyxml2::XMLElement *e) {
|
||||
} else if(strcmp(szElementName, "spot_light") == 0) {
|
||||
new_node = new KRSpotLight(scene, szName);
|
||||
} else if(strcmp(szElementName, "mesh") == 0) {
|
||||
new_node = new KRInstance(scene, szName, szName, e->Attribute("light_map"));
|
||||
float lod_min_coverage = 0.0f;
|
||||
if(e->QueryFloatAttribute("lod_min_coverage", &lod_min_coverage) != tinyxml2::XML_SUCCESS) {
|
||||
lod_min_coverage = 0.0f; //1.0f / 1024.0f / 768.0f; // FINDME - HACK - Need to dynamically select the absolute minimum based on the render buffer size
|
||||
}
|
||||
new_node = new KRInstance(scene, szName, szName, e->Attribute("light_map"), lod_min_coverage);
|
||||
} else if(strcmp(szElementName, "sky_box") == 0) {
|
||||
new_node = new KRSkyBox(scene, szName);
|
||||
}
|
||||
|
||||
@@ -168,7 +168,14 @@ void KRScene::render(int childOrder[], KROctreeNode *pOctreeNode, std::set<KRAAB
|
||||
if(renderPass != KRNode::RENDER_PASS_SHADOWMAP) {
|
||||
projectionMatrix = pCamera->getProjectionMatrix();
|
||||
}
|
||||
if(pOctreeNode->getBounds().visible(viewMatrix * projectionMatrix)) { // Only recurse deeper if within the view frustrum
|
||||
KRMat4 matVP = viewMatrix * projectionMatrix;
|
||||
|
||||
float min_coverage = 0.0f; // 1.0f / 1024.0f / 768.0f; // FINDME - HACK - Need to dynamically select the absolute minimum based on the render buffer size
|
||||
|
||||
float lod_coverage = pOctreeNode->getBounds().coverage(matVP, pCamera->getViewportSize()); // This also checks the view frustrum culling
|
||||
if(lod_coverage > min_coverage) {
|
||||
|
||||
// if(pOctreeNode->getBounds().visible(viewMatrix * projectionMatrix)) { // Only recurse deeper if within the view frustrum
|
||||
|
||||
// ----====---- Rendering and occlusion test pass ----====----
|
||||
bool bVisible = false;
|
||||
|
||||
Reference in New Issue
Block a user