Fixed inaccurate view frustum culling
Implemented smarter octree visibility query batching algorithm --HG-- extra : convert_revision : svn%3A7752d6cf-9f14-4ad2-affc-04f1e67b81a5/trunk%40106
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "KRAABB.h"
|
||||
#include "KRMat4.h"
|
||||
#include "assert.h"
|
||||
|
||||
KRAABB::KRAABB(const KRVector3 &minPoint, const KRVector3 &maxPoint)
|
||||
{
|
||||
@@ -87,6 +88,11 @@ bool KRAABB::contains(const KRAABB &b) const
|
||||
return b.min.x >= min.x && b.min.y >= min.y && b.min.z >= min.z && b.max.x <= max.x && b.max.y <= max.y && b.max.z <= max.z;
|
||||
}
|
||||
|
||||
bool KRAABB::contains(const KRVector3 &v) const
|
||||
{
|
||||
return v.x >= min.x && v.x <= max.x && v.y >= min.y && v.y <= max.y && v.z >= min.z && v.z <= max.z;
|
||||
}
|
||||
|
||||
KRAABB KRAABB::Infinite()
|
||||
{
|
||||
return KRAABB(KRVector3::Min(), KRVector3::Max());
|
||||
@@ -102,13 +108,13 @@ bool KRAABB::visible(const KRMat4 &matViewProjection) const
|
||||
int outside_count[6] = {0, 0, 0, 0, 0, 0};
|
||||
|
||||
for(int iCorner=0; iCorner<8; iCorner++) {
|
||||
KRVector3 cornerVertex = KRVector3(
|
||||
KRVector3 sourceCornerVertex = KRVector3(
|
||||
(iCorner & 1) == 0 ? min.x : max.x,
|
||||
(iCorner & 2) == 0 ? min.y : max.y,
|
||||
(iCorner & 4) == 0 ? min.z : max.z);
|
||||
|
||||
cornerVertex = KRMat4::Dot(matViewProjection, cornerVertex);
|
||||
float cornerVertexW = KRMat4::DotW(matViewProjection, cornerVertex);
|
||||
|
||||
KRVector3 cornerVertex = KRMat4::Dot(matViewProjection, sourceCornerVertex);
|
||||
float cornerVertexW = KRMat4::DotW(matViewProjection, sourceCornerVertex);
|
||||
|
||||
if(cornerVertex.x < -cornerVertexW) {
|
||||
outside_count[0]++;
|
||||
@@ -130,6 +136,37 @@ bool KRAABB::visible(const KRMat4 &matViewProjection) const
|
||||
}
|
||||
}
|
||||
|
||||
// for(int iCorner=0; iCorner<8; iCorner++) {
|
||||
// KRVector3 sourceCornerVertex = KRVector3(
|
||||
// (iCorner & 1) == 0 ? min.x : max.x,
|
||||
// (iCorner & 2) == 0 ? min.y : max.y,
|
||||
// (iCorner & 4) == 0 ? min.z : max.z);
|
||||
//
|
||||
// KRVector3 cornerVertex = KRMat4::Dot(matViewProjection, sourceCornerVertex);
|
||||
// float cornerVertexW = KRMat4::DotW(matViewProjection, sourceCornerVertex);
|
||||
// cornerVertex /= cornerVertexW;
|
||||
//
|
||||
//
|
||||
// if(cornerVertex.x < -1.0) {
|
||||
// outside_count[0]++;
|
||||
// }
|
||||
// if(cornerVertex.y < -1.0) {
|
||||
// outside_count[1]++;
|
||||
// }
|
||||
// if(cornerVertex.z < -1.0) {
|
||||
// outside_count[2]++;
|
||||
// }
|
||||
// if(cornerVertex.x > 1.0) {
|
||||
// outside_count[3]++;
|
||||
// }
|
||||
// if(cornerVertex.y > 1.0) {
|
||||
// outside_count[4]++;
|
||||
// }
|
||||
// if(cornerVertex.z > 1.0) {
|
||||
// outside_count[5]++;
|
||||
// }
|
||||
// }
|
||||
|
||||
bool is_visible = true;
|
||||
for(int iFace=0; iFace < 6; iFace++) {
|
||||
if(outside_count[iFace] == 8) {
|
||||
@@ -138,9 +175,10 @@ bool KRAABB::visible(const KRMat4 &matViewProjection) const
|
||||
}
|
||||
|
||||
if(!is_visible) {
|
||||
fprintf(stderr, "AABB culled: %i%i%i%i%i%i out, (%f, %f, %f) - (%f, %f, %f)\n", outside_count[0], outside_count[1], outside_count[2], outside_count[3], outside_count[4], outside_count[5], min.x, min.y, min.z, max.x, max.y, max.z);
|
||||
//fprintf(stderr, "AABB culled: %i%i%i%i%i%i out, (%f, %f, %f) - (%f, %f, %f)\n", outside_count[0], outside_count[1], outside_count[2], outside_count[3], outside_count[4], outside_count[5], min.x, min.y, min.z, max.x, max.y, max.z);
|
||||
} else {
|
||||
fprintf(stderr, "AABB visible: %i%i%i%i%i%i out, (%f, %f, %f) - (%f, %f, %f)\n", outside_count[0], outside_count[1], outside_count[2], outside_count[3], outside_count[4], outside_count[5], min.x, min.y, min.z, max.x, max.y, max.z);
|
||||
//fprintf(stderr, "AABB visible: %i%i%i%i%i%i out, (%f, %f, %f) - (%f, %f, %f)\n", outside_count[0], outside_count[1], outside_count[2], outside_count[3], outside_count[4], outside_count[5], min.x, min.y, min.z, max.x, max.y, max.z);
|
||||
}
|
||||
//is_visible = true;
|
||||
return is_visible;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ public:
|
||||
KRVector3 size() const;
|
||||
bool intersects(const KRAABB& b) const;
|
||||
bool contains(const KRAABB &b) const;
|
||||
bool contains(const KRVector3 &v) const;
|
||||
bool visible(const KRMat4 &matViewProjection) const;
|
||||
|
||||
KRAABB& operator =(const KRAABB& b);
|
||||
|
||||
@@ -192,6 +192,9 @@ void KRCamera::renderFrame(KRScene &scene, KRMat4 &viewMatrix, KRVector3 &lightD
|
||||
setViewportSize(KRVector2(backingWidth, backingHeight));
|
||||
|
||||
KRBoundingVolume frustrumVolume = KRBoundingVolume(viewMatrix, perspective_fov, getViewportSize().x / getViewportSize().y, perspective_nearz, perspective_farz);
|
||||
|
||||
std::set<KRAABB> newVisibleBounds;
|
||||
|
||||
if(bEnableDeferredLighting) {
|
||||
// ----====---- Opaque Geometry, Deferred rendering Pass 1 ----====----
|
||||
|
||||
@@ -216,7 +219,7 @@ void KRCamera::renderFrame(KRScene &scene, KRMat4 &viewMatrix, KRVector3 &lightD
|
||||
GLDEBUG(glDisable(GL_BLEND));
|
||||
|
||||
// Render the geometry
|
||||
scene.render(this, m_visibleBounds, m_pContext, frustrumVolume, viewMatrix, cameraPosition, lightDirection, shadowmvpmatrix, shadowDepthTexture, m_cShadowBuffers, KRNode::RENDER_PASS_DEFERRED_GBUFFER);
|
||||
scene.render(this, m_visibleBounds, m_pContext, frustrumVolume, viewMatrix, cameraPosition, lightDirection, shadowmvpmatrix, shadowDepthTexture, m_cShadowBuffers, KRNode::RENDER_PASS_DEFERRED_GBUFFER, newVisibleBounds);
|
||||
|
||||
// ----====---- Opaque Geometry, Deferred rendering Pass 2 ----====----
|
||||
// Set render target
|
||||
@@ -242,9 +245,7 @@ void KRCamera::renderFrame(KRScene &scene, KRMat4 &viewMatrix, KRVector3 &lightD
|
||||
|
||||
|
||||
// Render the geometry
|
||||
scene.render(this, m_visibleBounds, m_pContext, frustrumVolume, viewMatrix, cameraPosition, lightDirection, shadowmvpmatrix, shadowDepthTexture, 0, KRNode::RENDER_PASS_DEFERRED_LIGHTS);
|
||||
|
||||
scene.getOcclusionQueryResults(m_visibleBounds);
|
||||
scene.render(this, m_visibleBounds, m_pContext, frustrumVolume, viewMatrix, cameraPosition, lightDirection, shadowmvpmatrix, shadowDepthTexture, 0, KRNode::RENDER_PASS_DEFERRED_LIGHTS, newVisibleBounds);
|
||||
|
||||
// ----====---- Opaque Geometry, Deferred rendering Pass 3 ----====----
|
||||
// Set render target
|
||||
@@ -275,7 +276,8 @@ void KRCamera::renderFrame(KRScene &scene, KRMat4 &viewMatrix, KRVector3 &lightD
|
||||
GLDEBUG(glDepthMask(GL_TRUE));
|
||||
|
||||
// Render the geometry
|
||||
scene.render(this, m_visibleBounds, m_pContext, frustrumVolume, viewMatrix, cameraPosition, lightDirection, shadowmvpmatrix, shadowDepthTexture, m_cShadowBuffers, KRNode::RENDER_PASS_DEFERRED_OPAQUE);
|
||||
std::set<KRAABB> emptyBoundsSet; // At this point, we only render octree nodes that produced fragments during the 1st pass into the GBuffer
|
||||
scene.render(this, emptyBoundsSet, m_pContext, frustrumVolume, viewMatrix, cameraPosition, lightDirection, shadowmvpmatrix, shadowDepthTexture, m_cShadowBuffers, KRNode::RENDER_PASS_DEFERRED_OPAQUE, newVisibleBounds);
|
||||
|
||||
// Deactivate source buffer texture units
|
||||
m_pContext->getTextureManager()->selectTexture(6, NULL);
|
||||
@@ -312,9 +314,7 @@ void KRCamera::renderFrame(KRScene &scene, KRMat4 &viewMatrix, KRVector3 &lightD
|
||||
|
||||
|
||||
// Render the geometry
|
||||
scene.render(this, m_visibleBounds, m_pContext, frustrumVolume, viewMatrix, cameraPosition, lightDirection, shadowmvpmatrix, shadowDepthTexture, m_cShadowBuffers, KRNode::RENDER_PASS_FORWARD_OPAQUE);
|
||||
|
||||
scene.getOcclusionQueryResults(m_visibleBounds);
|
||||
scene.render(this, m_visibleBounds, m_pContext, frustrumVolume, viewMatrix, cameraPosition, lightDirection, shadowmvpmatrix, shadowDepthTexture, m_cShadowBuffers, KRNode::RENDER_PASS_FORWARD_OPAQUE, newVisibleBounds);
|
||||
}
|
||||
|
||||
// ----====---- Transparent Geometry, Forward Rendering ----====----
|
||||
@@ -339,7 +339,7 @@ void KRCamera::renderFrame(KRScene &scene, KRMat4 &viewMatrix, KRVector3 &lightD
|
||||
GLDEBUG(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
// Render all transparent geometry
|
||||
scene.render(this, m_visibleBounds, m_pContext, frustrumVolume, viewMatrix, cameraPosition, lightDirection, shadowmvpmatrix, shadowDepthTexture, m_cShadowBuffers, KRNode::RENDER_PASS_FORWARD_TRANSPARENT);
|
||||
scene.render(this, m_visibleBounds, m_pContext, frustrumVolume, viewMatrix, cameraPosition, lightDirection, shadowmvpmatrix, shadowDepthTexture, m_cShadowBuffers, KRNode::RENDER_PASS_FORWARD_TRANSPARENT, newVisibleBounds);
|
||||
|
||||
|
||||
// ----====---- Flares ----====----
|
||||
@@ -363,7 +363,7 @@ void KRCamera::renderFrame(KRScene &scene, KRMat4 &viewMatrix, KRVector3 &lightD
|
||||
GLDEBUG(glBlendFunc(GL_ONE, GL_ONE));
|
||||
|
||||
// Render all flares
|
||||
scene.render(this, m_visibleBounds, m_pContext, frustrumVolume, viewMatrix, cameraPosition, lightDirection, shadowmvpmatrix, shadowDepthTexture, m_cShadowBuffers, KRNode::RENDER_PASS_FLARES);
|
||||
scene.render(this, m_visibleBounds, m_pContext, frustrumVolume, viewMatrix, cameraPosition, lightDirection, shadowmvpmatrix, shadowDepthTexture, m_cShadowBuffers, KRNode::RENDER_PASS_FLARES, newVisibleBounds);
|
||||
|
||||
// ----====---- Debug Overlay ----====----
|
||||
|
||||
@@ -395,15 +395,13 @@ void KRCamera::renderFrame(KRScene &scene, KRMat4 &viewMatrix, KRVector3 &lightD
|
||||
}
|
||||
}
|
||||
|
||||
// scene.getOcclusionQueryResults(m_visibleBounds);
|
||||
|
||||
// fprintf(stderr, "visible bounds: %i\n", (int)m_visibleBounds.size());
|
||||
m_visibleBounds = newVisibleBounds;
|
||||
|
||||
// Re-enable z-buffer write
|
||||
GLDEBUG(glDepthMask(GL_TRUE));
|
||||
|
||||
|
||||
fprintf(stderr, "VBO Mem: %i Kbyte Texture Mem: %i Kbyte Shader Handles: %i\n", (int)m_pContext->getModelManager()->getMemUsed() / 1024, (int)m_pContext->getTextureManager()->getMemUsed() / 1024, (int)m_pContext->getShaderManager()->getShaderHandlesUsed());
|
||||
fprintf(stderr, "VBO Mem: %i Kbyte Texture Mem: %i Kbyte Shader Handles: %i Visible Bounds: %i\n", (int)m_pContext->getModelManager()->getMemUsed() / 1024, (int)m_pContext->getTextureManager()->getMemUsed() / 1024, (int)m_pContext->getShaderManager()->getShaderHandlesUsed(), (int)m_visibleBounds.size());
|
||||
}
|
||||
|
||||
|
||||
@@ -574,8 +572,10 @@ void KRCamera::renderShadowBuffer(KRScene &scene, int iShadow)
|
||||
KRVector3 cameraPosition;
|
||||
KRVector3 lightDirection;
|
||||
KRBoundingVolume shadowVolume = KRBoundingVolume(vertices);
|
||||
scene.render(this, m_shadowVisibleBounds[iShadow], m_pContext, shadowVolume, shadowmvpmatrix[iShadow], cameraPosition, lightDirection, shadowmvpmatrix, NULL, m_cShadowBuffers, KRNode::RENDER_PASS_SHADOWMAP);
|
||||
scene.getOcclusionQueryResults(m_shadowVisibleBounds[iShadow]);
|
||||
|
||||
std::set<KRAABB> newVisibleBounds;
|
||||
scene.render(this, m_shadowVisibleBounds[iShadow], m_pContext, shadowVolume, shadowmvpmatrix[iShadow], cameraPosition, lightDirection, shadowmvpmatrix, NULL, m_cShadowBuffers, KRNode::RENDER_PASS_SHADOWMAP, newVisibleBounds);
|
||||
m_shadowVisibleBounds[iShadow] = newVisibleBounds;
|
||||
GLDEBUG(glViewport(0, 0, backingWidth, backingHeight));
|
||||
}
|
||||
|
||||
|
||||
@@ -91,8 +91,8 @@ void KRInstance::render(KRCamera *pCamera, KRContext *pContext, KRBoundingVolume
|
||||
}
|
||||
|
||||
if(m_pModel != NULL) {
|
||||
if(getExtents(pContext).test_intersect(frustrumVolume) || renderPass == RENDER_PASS_SHADOWMAP) {
|
||||
//if(m_pModel != NULL && (getBounds().visible(viewMatrix * projectionMatrix) || renderPass == RENDER_PASS_SHADOWMAP)) {
|
||||
//if(getExtents(pContext).test_intersect(frustrumVolume) || renderPass == RENDER_PASS_SHADOWMAP) {
|
||||
if(getBounds().visible(viewMatrix * projectionMatrix)) {
|
||||
|
||||
if(m_pLightMap == NULL && m_lightMap.size()) {
|
||||
m_pLightMap = pContext->getTextureManager()->getTexture(m_lightMap.c_str());
|
||||
|
||||
@@ -91,12 +91,3 @@ std::set<KRNode *> &KROctree::getOuterSceneNodes()
|
||||
{
|
||||
return m_outerSceneNodes;
|
||||
}
|
||||
#if TARGET_OS_IPHONE
|
||||
void KROctree::getOcclusionQueryResults(std::set<KRAABB> &renderedBounds)
|
||||
{
|
||||
renderedBounds.clear();
|
||||
if(m_pRootNode) {
|
||||
m_pRootNode->getOcclusionQueryResults(renderedBounds);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -26,9 +26,7 @@ public:
|
||||
|
||||
KROctreeNode *getRootNode();
|
||||
std::set<KRNode *> &getOuterSceneNodes();
|
||||
#if TARGET_OS_IPHONE
|
||||
void getOcclusionQueryResults(std::set<KRAABB> &renderedBounds);
|
||||
#endif
|
||||
|
||||
private:
|
||||
KROctreeNode *m_pRootNode;
|
||||
std::set<KRNode *>m_outerSceneNodes;
|
||||
|
||||
@@ -15,8 +15,6 @@ KROctreeNode::KROctreeNode(const KRAABB &bounds) : m_bounds(bounds)
|
||||
|
||||
m_occlusionQuery = 0;
|
||||
m_occlusionTested = false;
|
||||
m_occlusionQueryTransparent = 0;
|
||||
m_occlusionTestedTransparent = false;
|
||||
m_activeQuery = false;
|
||||
}
|
||||
|
||||
@@ -28,8 +26,6 @@ KROctreeNode::KROctreeNode(const KRAABB &bounds, int iChild, KROctreeNode *pChil
|
||||
|
||||
m_occlusionQuery = 0;
|
||||
m_occlusionTested = false;
|
||||
m_occlusionQueryTransparent = 0;
|
||||
m_occlusionTestedTransparent = false;
|
||||
m_activeQuery = false;
|
||||
}
|
||||
|
||||
@@ -44,21 +40,13 @@ KROctreeNode::~KROctreeNode()
|
||||
if(m_occlusionTested) {
|
||||
GLDEBUG(glDeleteQueriesEXT(1, &m_occlusionQuery));
|
||||
}
|
||||
if(m_occlusionTestedTransparent) {
|
||||
GLDEBUG(glDeleteQueriesEXT(1, &m_occlusionQueryTransparent));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
void KROctreeNode::beginOcclusionQuery(bool bTransparentPass)
|
||||
void KROctreeNode::beginOcclusionQuery()
|
||||
{
|
||||
if(bTransparentPass && !m_occlusionTestedTransparent) {
|
||||
GLDEBUG(glGenQueriesEXT(1, &m_occlusionQueryTransparent));
|
||||
GLDEBUG(glBeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, m_occlusionQueryTransparent));
|
||||
m_occlusionTestedTransparent = true;
|
||||
m_activeQuery = true;
|
||||
} else if(!bTransparentPass && !m_occlusionTested){
|
||||
if(!m_occlusionTested){
|
||||
GLDEBUG(glGenQueriesEXT(1, &m_occlusionQuery));
|
||||
GLDEBUG(glBeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, m_occlusionQuery));
|
||||
m_occlusionTested = true;
|
||||
@@ -74,54 +62,9 @@ void KROctreeNode::endOcclusionQuery()
|
||||
}
|
||||
}
|
||||
|
||||
bool KROctreeNode::getOcclusionQueryResults(std::set<KRAABB> &renderedBounds)
|
||||
{
|
||||
bool bRendered = false;
|
||||
bool bGoDeeper = false;
|
||||
|
||||
if(m_occlusionTested) {
|
||||
GLuint params = 0;
|
||||
GLDEBUG(glGetQueryObjectuivEXT(m_occlusionQuery, GL_QUERY_RESULT_EXT, ¶ms));
|
||||
if(params) bRendered = true; // At least one opaque fragment processed
|
||||
|
||||
GLDEBUG(glDeleteQueriesEXT(1, &m_occlusionQuery));
|
||||
m_occlusionTested = false;
|
||||
bGoDeeper = true;
|
||||
}
|
||||
if(m_occlusionTestedTransparent) {
|
||||
GLuint params = 0;
|
||||
GLDEBUG(glGetQueryObjectuivEXT(m_occlusionQueryTransparent, GL_QUERY_RESULT_EXT, ¶ms));
|
||||
if(params) bRendered = true; // At least one transparent fragment processed
|
||||
|
||||
GLDEBUG(glDeleteQueriesEXT(1, &m_occlusionQueryTransparent));
|
||||
m_occlusionTestedTransparent = false;
|
||||
|
||||
bGoDeeper = true;
|
||||
}
|
||||
|
||||
// FINDME - Test Code:
|
||||
//bGoDeeper = true;
|
||||
//bRendered = true;
|
||||
|
||||
if(bGoDeeper) { // Only recurse deeper if we reached this level in the previous pass
|
||||
for(int i=0; i<8; i++) {
|
||||
if(m_children[i]) {
|
||||
if(m_children[i]->getOcclusionQueryResults(renderedBounds)) {
|
||||
bRendered = true; // We must always include the parent, even if the parent's local scene graph nodes are fully occluded
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(bRendered) {
|
||||
renderedBounds.insert(m_bounds);
|
||||
}
|
||||
|
||||
return bRendered;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
KRAABB KROctreeNode::getBounds()
|
||||
{
|
||||
return m_bounds;
|
||||
@@ -157,55 +100,7 @@ KRAABB KROctreeNode::getChildBounds(int iChild)
|
||||
}
|
||||
|
||||
int KROctreeNode::getChildIndex(KRNode *pNode)
|
||||
{
|
||||
/*
|
||||
|
||||
KRVector3 min = pNode->getMinPoint();
|
||||
KRVector3 max = pNode->getMaxPoint();
|
||||
|
||||
// 0: max.x < center.x && max.y < center.y && max.z < center.z
|
||||
// 1: min.x > center.x && max.y < center.y && max.z < center.z
|
||||
// 2: max.x < center.x && min.y > center.y && max.z < center.z
|
||||
// 3: min.x > center.x && min.y > center.y && max.z < center.z
|
||||
// 4: max.x < center.x && max.y < center.y && min.z > center.z
|
||||
// 5: min.x > center.x && max.y < center.y && min.z > center.z
|
||||
// 6: max.x < center.x && min.y > center.y && min.z > center.z
|
||||
// 7: min.x > center.x && min.y > center.y && min.z > center.z
|
||||
|
||||
KRVector3 center = m_bounds.center();
|
||||
int iChild = -1;
|
||||
if(max.z < center.z) {
|
||||
if(max.y < center.y) {
|
||||
if(max.x < center.x) {
|
||||
iChild = 0;
|
||||
} else if(min.x > center.x) {
|
||||
iChild = 1;
|
||||
}
|
||||
} else if(min.y > center.y) {
|
||||
if(max.x < center.x) {
|
||||
iChild = 2;
|
||||
} else if(min.x > center.x) {
|
||||
iChild = 3;
|
||||
}
|
||||
}
|
||||
} else if(min.z > center.z) {
|
||||
if(max.y < center.y) {
|
||||
if(min.x > center.x) {
|
||||
iChild = 4;
|
||||
} else if(min.x > center.x) {
|
||||
iChild = 5;
|
||||
}
|
||||
} else if(min.y > center.y) {
|
||||
if(max.x < center.x) {
|
||||
iChild = 6;
|
||||
} else if(min.x > center.x) {
|
||||
iChild = 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
return iChild;
|
||||
*/
|
||||
|
||||
{
|
||||
for(int iChild=0; iChild < 8; iChild++) {
|
||||
if(getChildBounds(iChild).contains(pNode->getBounds())) {
|
||||
return iChild;
|
||||
|
||||
@@ -38,19 +38,14 @@ public:
|
||||
bool canShrinkRoot() const;
|
||||
KROctreeNode *stripChild();
|
||||
|
||||
void beginOcclusionQuery(bool bTransparentPass);
|
||||
void beginOcclusionQuery();
|
||||
void endOcclusionQuery();
|
||||
bool getOcclusionQueryResults(std::set<KRAABB> &renderedBounds);
|
||||
|
||||
private:
|
||||
|
||||
GLuint m_occlusionQuery;
|
||||
bool m_occlusionTested;
|
||||
|
||||
GLuint m_occlusionQueryTransparent;
|
||||
bool m_occlusionTestedTransparent;
|
||||
|
||||
bool m_activeQuery;
|
||||
private:
|
||||
|
||||
KRAABB m_bounds;
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ KRScene::~KRScene() {
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
void KRScene::render(KRCamera *pCamera, std::set<KRAABB> &visibleBounds, KRContext *pContext, KRBoundingVolume &frustrumVolume, KRMat4 &viewMatrix, KRVector3 &cameraPosition, KRVector3 &lightDirection, KRMat4 *pShadowMatrices, GLuint *shadowDepthTextures, int cShadowBuffers, KRNode::RenderPass renderPass) {
|
||||
void KRScene::render(KRCamera *pCamera, std::set<KRAABB> &visibleBounds, KRContext *pContext, KRBoundingVolume &frustrumVolume, KRMat4 &viewMatrix, KRVector3 &cameraPosition, KRVector3 &lightDirection, KRMat4 *pShadowMatrices, GLuint *shadowDepthTextures, int cShadowBuffers, KRNode::RenderPass renderPass, std::set<KRAABB> &newVisibleBounds) {
|
||||
|
||||
updateOctree();
|
||||
|
||||
@@ -103,7 +103,33 @@ void KRScene::render(KRCamera *pCamera, std::set<KRAABB> &visibleBounds, KRConte
|
||||
pCamera->dSunB = sun_color.z;
|
||||
}
|
||||
|
||||
render(m_nodeTree.getRootNode(), visibleBounds, pCamera, pContext, frustrumVolume, viewMatrix, cameraPosition, forward_render_light_direction, pShadowMatrices, shadowDepthTextures, cShadowBuffers, renderPass);
|
||||
std::vector<KROctreeNode *> remainingOctrees;
|
||||
std::vector<KROctreeNode *> remainingOctreesTestResults;
|
||||
std::vector<KROctreeNode *> remainingOctreesTestResultsOnly;
|
||||
if(m_nodeTree.getRootNode() != NULL) {
|
||||
remainingOctrees.push_back(m_nodeTree.getRootNode());
|
||||
}
|
||||
|
||||
std::vector<KROctreeNode *> newRemainingOctrees;
|
||||
std::vector<KROctreeNode *> newRemainingOctreesTestResults;
|
||||
while((!remainingOctrees.empty() || !remainingOctreesTestResults.empty())) {
|
||||
newRemainingOctrees.clear();
|
||||
newRemainingOctreesTestResults.clear();
|
||||
for(std::vector<KROctreeNode *>::iterator octree_itr = remainingOctrees.begin(); octree_itr != remainingOctrees.end(); octree_itr++) {
|
||||
render(*octree_itr, visibleBounds, pCamera, pContext, frustrumVolume, viewMatrix, cameraPosition, forward_render_light_direction, pShadowMatrices, shadowDepthTextures, cShadowBuffers, renderPass, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, newVisibleBounds, false, false);
|
||||
}
|
||||
for(std::vector<KROctreeNode *>::iterator octree_itr = remainingOctreesTestResults.begin(); octree_itr != remainingOctreesTestResults.end(); octree_itr++) {
|
||||
render(*octree_itr, visibleBounds, pCamera, pContext, frustrumVolume, viewMatrix, cameraPosition, forward_render_light_direction, pShadowMatrices, shadowDepthTextures, cShadowBuffers, renderPass, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, newVisibleBounds, true, false);
|
||||
}
|
||||
remainingOctrees = newRemainingOctrees;
|
||||
remainingOctreesTestResults = newRemainingOctreesTestResults;
|
||||
}
|
||||
|
||||
newRemainingOctrees.clear();
|
||||
newRemainingOctreesTestResults.clear();
|
||||
for(std::vector<KROctreeNode *>::iterator octree_itr = remainingOctreesTestResultsOnly.begin(); octree_itr != remainingOctreesTestResultsOnly.end(); octree_itr++) {
|
||||
render(*octree_itr, visibleBounds, pCamera, pContext, frustrumVolume, viewMatrix, cameraPosition, forward_render_light_direction, pShadowMatrices, shadowDepthTextures, cShadowBuffers, renderPass, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, newVisibleBounds, true, true);
|
||||
}
|
||||
|
||||
|
||||
for(std::set<KRNode *>::iterator itr=m_nodeTree.getOuterSceneNodes().begin(); itr != m_nodeTree.getOuterSceneNodes().end(); itr++) {
|
||||
@@ -111,61 +137,94 @@ void KRScene::render(KRCamera *pCamera, std::set<KRAABB> &visibleBounds, KRConte
|
||||
}
|
||||
}
|
||||
|
||||
void KRScene::render(KROctreeNode *pOctreeNode, std::set<KRAABB> &visibleBounds, KRCamera *pCamera, KRContext *pContext, KRBoundingVolume &frustrumVolume, KRMat4 &viewMatrix, KRVector3 &cameraPosition, KRVector3 &lightDirection, KRMat4 *pShadowMatrices, GLuint *shadowDepthTextures, int cShadowBuffers, KRNode::RenderPass renderPass)
|
||||
void KRScene::render(KROctreeNode *pOctreeNode, std::set<KRAABB> &visibleBounds, KRCamera *pCamera, KRContext *pContext, KRBoundingVolume &frustrumVolume, KRMat4 &viewMatrix, KRVector3 &cameraPosition, KRVector3 &lightDirection, KRMat4 *pShadowMatrices, GLuint *shadowDepthTextures, int cShadowBuffers, KRNode::RenderPass renderPass, std::vector<KROctreeNode *> &remainingOctrees, std::vector<KROctreeNode *> &remainingOctreesTestResults, std::vector<KROctreeNode *> &remainingOctreesTestResultsOnly, std::set<KRAABB> &newVisibleBounds, bool bOcclusionResultsPass, bool bOcclusionTestResultsOnly)
|
||||
{
|
||||
if(pOctreeNode) {
|
||||
KRMat4 projectionMatrix;
|
||||
if(renderPass != KRNode::RENDER_PASS_SHADOWMAP) {
|
||||
projectionMatrix = pCamera->getProjectionMatrix();
|
||||
}
|
||||
|
||||
KRAABB octreeBounds = pOctreeNode->getBounds();
|
||||
|
||||
KRBoundingVolume frustrumVolumeNoNearClip = KRBoundingVolume(viewMatrix, pCamera->perspective_fov, pCamera->m_viewportSize.x / pCamera->m_viewportSize.y, 0.0, pCamera->perspective_farz);
|
||||
|
||||
//if(true) {
|
||||
//if(pOctreeNode->getBounds().visible(viewMatrix * projectionMatrix)) { // Only recurse deeper if within the view frustrum
|
||||
if(frustrumVolumeNoNearClip.test_intersect(pOctreeNode->getBounds())) { // Only recurse deeper if within the view frustrum
|
||||
if(bOcclusionResultsPass) {
|
||||
// ----====---- Occlusion results pass ----====----
|
||||
if(pOctreeNode->m_occlusionTested) {
|
||||
GLuint params = 0;
|
||||
GLDEBUG(glGetQueryObjectuivEXT(pOctreeNode->m_occlusionQuery, GL_QUERY_RESULT_EXT, ¶ms));
|
||||
if(params) {
|
||||
newVisibleBounds.insert(octreeBounds); // Record the actual tests that succeeded during this frame
|
||||
visibleBounds.insert(octreeBounds); // Update the list of tests that we won't repeat for subsequent passes during this frame
|
||||
if(!bOcclusionTestResultsOnly) {
|
||||
// Schedule a pass to perform the rendering
|
||||
remainingOctrees.push_back(pOctreeNode);
|
||||
}
|
||||
}
|
||||
|
||||
GLDEBUG(glDeleteQueriesEXT(1, &pOctreeNode->m_occlusionQuery));
|
||||
pOctreeNode->m_occlusionTested = false;
|
||||
pOctreeNode->m_occlusionQuery = 0;
|
||||
}
|
||||
} else {
|
||||
|
||||
bool can_occlusion_test = renderPass == KRNode::RENDER_PASS_FORWARD_OPAQUE || renderPass == KRNode::RENDER_PASS_DEFERRED_GBUFFER/* || renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT*/;
|
||||
|
||||
// KRBoundingVolume frustrumVolumeNoNearClip = KRBoundingVolume(viewMatrix, pCamera->perspective_fov, pCamera->m_viewportSize.x / pCamera->m_viewportSize.y, 0.0, pCamera->perspective_farz);
|
||||
// if(frustrumVolumeNoNearClip.test_intersect(pOctreeNode->getBounds())) { // Only recurse deeper if within the view frustrum
|
||||
//
|
||||
|
||||
// can_occlusion_test = false;
|
||||
bool bVisible = true;
|
||||
//bool bVisible = visibleBounds.find(octreeBounds) != visibleBounds.end();
|
||||
|
||||
if(bVisible) {
|
||||
if(can_occlusion_test) {
|
||||
pOctreeNode->beginOcclusionQuery(renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT);
|
||||
}
|
||||
|
||||
// Occlusion test indicates that this bounding box was visible in the last frame
|
||||
for(std::set<KRNode *>::iterator itr=pOctreeNode->getSceneNodes().begin(); itr != pOctreeNode->getSceneNodes().end(); itr++) {
|
||||
//assert(pOctreeNode->getBounds().contains((*itr)->getBounds())); // Sanity check
|
||||
|
||||
(*itr)->render(pCamera, pContext, frustrumVolume, viewMatrix, cameraPosition, lightDirection, pShadowMatrices, shadowDepthTextures, cShadowBuffers, renderPass);
|
||||
}
|
||||
|
||||
if(can_occlusion_test) {
|
||||
pOctreeNode->endOcclusionQuery();
|
||||
}
|
||||
|
||||
|
||||
bool bRenderChildren = renderPass != KRNode::RENDER_PASS_DEFERRED_OPAQUE;
|
||||
if(!bRenderChildren) {
|
||||
bRenderChildren = visibleBounds.find(octreeBounds) != visibleBounds.end();
|
||||
}
|
||||
if(bRenderChildren) {
|
||||
for(int i=0; i<8; i++) {
|
||||
render(pOctreeNode->getChildren()[i], visibleBounds, pCamera, pContext, frustrumVolume, viewMatrix, cameraPosition, lightDirection, pShadowMatrices, shadowDepthTextures, cShadowBuffers, renderPass);
|
||||
|
||||
KRMat4 projectionMatrix;
|
||||
if(renderPass != KRNode::RENDER_PASS_SHADOWMAP) {
|
||||
projectionMatrix = pCamera->getProjectionMatrix();
|
||||
}
|
||||
if(pOctreeNode->getBounds().visible(viewMatrix * projectionMatrix)) { // Only recurse deeper if within the view frustrum
|
||||
|
||||
// ----====---- Rendering and occlusion test pass ----====----
|
||||
bool bVisible = false;
|
||||
bool bNeedOcclusionTest = true;
|
||||
|
||||
//bVisible = true; // FINDME - Test Code
|
||||
|
||||
if(!bVisible) {
|
||||
// Assume bounding boxes are visible without occlusion test queries if the camera is inside the box.
|
||||
// The near clipping plane of the camera is taken into consideration by expanding the match area
|
||||
KRMat4 invView = viewMatrix;
|
||||
invView.invert();
|
||||
KRVector3 cameraPos = KRMat4::Dot(invView, KRVector3::Zero());
|
||||
KRAABB cameraExtents = KRAABB(cameraPos - KRVector3(pCamera->perspective_nearz), cameraPos + KRVector3(pCamera->perspective_nearz));
|
||||
bVisible = octreeBounds.intersects(cameraExtents);
|
||||
if(bVisible) {
|
||||
newVisibleBounds.insert(octreeBounds); // Record the actual tests that succeeded during this frame
|
||||
visibleBounds.insert(octreeBounds); // Update the list of tests that we won't repeat for subsequent passes during this frame
|
||||
bNeedOcclusionTest = false;
|
||||
}
|
||||
}
|
||||
} else if(KRNode::RENDER_PASS_FORWARD_OPAQUE || renderPass == KRNode::RENDER_PASS_DEFERRED_OPAQUE) {
|
||||
if(pOctreeNode->getSceneNodes().empty()) {
|
||||
for(int i=0; i<8; i++) {
|
||||
render(pOctreeNode->getChildren()[i], visibleBounds, pCamera, pContext, frustrumVolume, viewMatrix, cameraPosition, lightDirection, pShadowMatrices, shadowDepthTextures, cShadowBuffers, renderPass);
|
||||
|
||||
if(!bVisible) {
|
||||
// Check if an occlusion query from the prior pass has returned true
|
||||
bVisible = newVisibleBounds.find(octreeBounds) != newVisibleBounds.end();
|
||||
if(bVisible) {
|
||||
bNeedOcclusionTest = false;
|
||||
}
|
||||
} else {
|
||||
pOctreeNode->beginOcclusionQuery(renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT);
|
||||
}
|
||||
|
||||
if(!bVisible) {
|
||||
// Take advantage of temporal consistency of visible elements from frame to frame
|
||||
// If the previous frame rendered this octree, then attempt to render it in this frame without performing a pre-occlusion test
|
||||
bVisible = visibleBounds.find(octreeBounds) != visibleBounds.end();
|
||||
// We don't set bNeedOcclusionTest to false here, as we need to perform an occlusion test to record if this octree node was visible for the next frame
|
||||
}
|
||||
|
||||
if(!bVisible) {
|
||||
// Optimization: If this is an empty octree node with only a single child node, then immediately try to render the child node without an occlusion test for this higher level, as it would be more expensive than the occlusion test for the child
|
||||
if(pOctreeNode->getSceneNodes().empty()) {
|
||||
int child_count = 0;
|
||||
for(int i=0; i<8; i++) {
|
||||
if(pOctreeNode->getChildren()[i] != NULL) child_count++;
|
||||
}
|
||||
if(child_count == 1) bVisible = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(bNeedOcclusionTest) {
|
||||
pOctreeNode->beginOcclusionQuery();
|
||||
|
||||
KRShader *pVisShader = m_pContext->getShaderManager()->getShader("occlusion_test", pCamera, false, false, false, 0, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_TRANSPARENT);
|
||||
|
||||
@@ -180,22 +239,60 @@ void KRScene::render(KROctreeNode *pOctreeNode, std::set<KRAABB> &visibleBounds,
|
||||
// Enable additive blending
|
||||
GLDEBUG(glEnable(GL_BLEND));
|
||||
GLDEBUG(glBlendFunc(GL_ONE, GL_ONE));
|
||||
|
||||
|
||||
|
||||
if(renderPass == KRNode::RENDER_PASS_FORWARD_OPAQUE ||
|
||||
renderPass == KRNode::RENDER_PASS_DEFERRED_GBUFFER ||
|
||||
renderPass == KRNode::RENDER_PASS_DEFERRED_OPAQUE ||
|
||||
renderPass == KRNode::RENDER_PASS_SHADOWMAP) {
|
||||
|
||||
// Disable z-buffer write
|
||||
GLDEBUG(glDepthMask(GL_FALSE));
|
||||
}
|
||||
|
||||
if(pVisShader->bind(pCamera, viewMatrix, mvpmatrix, cameraPosition, lightDirection, pShadowMatrices, shadowDepthTextures, 0, KRNode::RENDER_PASS_FORWARD_TRANSPARENT)) {
|
||||
GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 14));
|
||||
}
|
||||
|
||||
if(renderPass == KRNode::RENDER_PASS_FORWARD_OPAQUE ||
|
||||
renderPass == KRNode::RENDER_PASS_DEFERRED_GBUFFER ||
|
||||
renderPass == KRNode::RENDER_PASS_DEFERRED_OPAQUE ||
|
||||
renderPass == KRNode::RENDER_PASS_SHADOWMAP) {
|
||||
|
||||
// Re-enable z-buffer write
|
||||
GLDEBUG(glDepthMask(GL_TRUE));
|
||||
}
|
||||
|
||||
GLDEBUG(glDisable(GL_BLEND));
|
||||
|
||||
pOctreeNode->endOcclusionQuery();
|
||||
|
||||
|
||||
if(bVisible) {
|
||||
// Schedule a pass to get the result of the occlusion test only for future frames and passes, without rendering the model or recurring further
|
||||
remainingOctreesTestResultsOnly.push_back(pOctreeNode);
|
||||
} else {
|
||||
// Schedule a pass to get the result of the occlusion test and continue recursion and rendering if test is true
|
||||
remainingOctreesTestResults.push_back(pOctreeNode);
|
||||
}
|
||||
}
|
||||
|
||||
if(bVisible) {
|
||||
|
||||
for(std::set<KRNode *>::iterator itr=pOctreeNode->getSceneNodes().begin(); itr != pOctreeNode->getSceneNodes().end(); itr++) {
|
||||
//assert(pOctreeNode->getBounds().contains((*itr)->getBounds())); // Sanity check
|
||||
(*itr)->render(pCamera, pContext, frustrumVolume, viewMatrix, cameraPosition, lightDirection, pShadowMatrices, shadowDepthTextures, cShadowBuffers, renderPass);
|
||||
}
|
||||
|
||||
for(int i=0; i<8; i++) {
|
||||
render(pOctreeNode->getChildren()[i], visibleBounds, pCamera, pContext, frustrumVolume, viewMatrix, cameraPosition, lightDirection, pShadowMatrices, shadowDepthTextures, cShadowBuffers, renderPass, remainingOctrees, remainingOctreesTestResults, remainingOctreesTestResultsOnly, newVisibleBounds, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
//fprintf(stderr, "Octree culled: (%f, %f, %f) - (%f, %f, %f)\n", pOctreeNode->getBounds().min.x, pOctreeNode->getBounds().min.y, pOctreeNode->getBounds().min.z, pOctreeNode->getBounds().max.x, pOctreeNode->getBounds().max.y, pOctreeNode->getBounds().max.z);
|
||||
|
||||
}
|
||||
}
|
||||
// fprintf(stderr, "Octree culled: (%f, %f, %f) - (%f, %f, %f)\n", pOctreeNode->getBounds().min.x, pOctreeNode->getBounds().min.y, pOctreeNode->getBounds().min.z, pOctreeNode->getBounds().max.x, pOctreeNode->getBounds().max.y, pOctreeNode->getBounds().max.z);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -317,9 +414,4 @@ void KRScene::updateOctree()
|
||||
}
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
void KRScene::getOcclusionQueryResults(std::set<KRAABB> &renderedBounds)
|
||||
{
|
||||
m_nodeTree.getOcclusionQueryResults(renderedBounds);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -64,12 +64,9 @@ public:
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
void render(KRCamera *pCamera, std::set<KRAABB> &visibleBounds, KRContext *pContext, KRBoundingVolume &frustrumVolume, KRMat4 &viewMatrix, KRVector3 &cameraPosition, KRVector3 &lightDirection, KRMat4 *pShadowMatrices, GLuint *shadowDepthTextures, int cShadowBuffers, KRNode::RenderPass renderPass);
|
||||
void render(KRCamera *pCamera, std::set<KRAABB> &visibleBounds, KRContext *pContext, KRBoundingVolume &frustrumVolume, KRMat4 &viewMatrix, KRVector3 &cameraPosition, KRVector3 &lightDirection, KRMat4 *pShadowMatrices, GLuint *shadowDepthTextures, int cShadowBuffers, KRNode::RenderPass renderPass, std::set<KRAABB> &newVisibleBounds);
|
||||
|
||||
void render(KROctreeNode *pOctreeNode, std::set<KRAABB> &visibleBounds, KRCamera *pCamera, KRContext *pContext, KRBoundingVolume &frustrumVolume, KRMat4 &viewMatrix, KRVector3 &cameraPosition, KRVector3 &lightDirection, KRMat4 *pShadowMatrices, GLuint *shadowDepthTextures, int cShadowBuffers, KRNode::RenderPass renderPass);
|
||||
|
||||
|
||||
void getOcclusionQueryResults(std::set<KRAABB> &renderedBounds);
|
||||
void render(KROctreeNode *pOctreeNode, std::set<KRAABB> &visibleBounds, KRCamera *pCamera, KRContext *pContext, KRBoundingVolume &frustrumVolume, KRMat4 &viewMatrix, KRVector3 &cameraPosition, KRVector3 &lightDirection, KRMat4 *pShadowMatrices, GLuint *shadowDepthTextures, int cShadowBuffers, KRNode::RenderPass renderPass, std::vector<KROctreeNode *> &remainingOctrees, std::vector<KROctreeNode *> &remainingOctreesTestResults, std::vector<KROctreeNode *> &remainingOctreesTestResultsOnly, std::set<KRAABB> &newVisibleBounds, bool bOcclusionResultsPass, bool bOcclusionTestResultsOnly);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
#define KRENGINE_MAX_TEXTURE_UNITS 8
|
||||
#define KRENGINE_MAX_TEXTURE_HANDLES 10000
|
||||
#define KRENGINE_MAX_TEXTURE_MEM 100000000
|
||||
#define KRENGINE_MAX_TEXTURE_MEM 128000000
|
||||
|
||||
#ifndef KRTEXTUREMANAGER_H
|
||||
#define KRTEXTUREMANAGER_H
|
||||
|
||||
@@ -30,5 +30,6 @@
|
||||
//
|
||||
|
||||
void main() {
|
||||
//gl_FragColor = vec4(0.00, 0.00, 0.00, 0.00);
|
||||
gl_FragColor = vec4(0.00, 0.00, 0.00, 0.00);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0440"
|
||||
LastUpgradeVersion = "0450"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -23,7 +23,7 @@
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
}
|
||||
|
||||
[self.engine setNearZ: 5.0];
|
||||
[self.engine setFarZ: 3000.0];
|
||||
[self.engine setFarZ: 5000.0];
|
||||
//[renderEngine setNearZ: 1.0];
|
||||
//[renderEngine setFarZ: 3000.0];
|
||||
|
||||
|
||||
@@ -252,7 +252,7 @@
|
||||
|
||||
}
|
||||
|
||||
double dScaleFactor = 200.0f * deltaTime;
|
||||
double dScaleFactor = 500.0f * deltaTime;
|
||||
|
||||
camera_position.z += (-cos(camera_pitch) * cos(camera_yaw) * leftStickDeltaX + -cos(camera_pitch) * cos(camera_yaw - 90.0f * d2r) * -leftStickDeltaY) * dScaleFactor;
|
||||
camera_position.x += (cos(camera_pitch) * sin(camera_yaw) * leftStickDeltaX + cos(camera_pitch) * sin(camera_yaw - 90.0f * d2r) * -leftStickDeltaY) * dScaleFactor;
|
||||
|
||||
@@ -271,7 +271,7 @@
|
||||
29B97313FDCFA39411CA2CEA /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0440;
|
||||
LastUpgradeCheck = 0450;
|
||||
};
|
||||
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "KRObjView" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
|
||||
Reference in New Issue
Block a user