mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:44:02 +02:00
Merge pull request #543 from ZappoMan/occlusion_culling
Occlusion culling
This commit is contained in:
commit
726872d955
21 changed files with 1455 additions and 463 deletions
|
@ -979,6 +979,10 @@ void Application::doFalseColorizeInView() {
|
|||
_voxels.falseColorizeInView(&_viewFrustum);
|
||||
}
|
||||
|
||||
void Application::doFalseColorizeOccluded() {
|
||||
_voxels.falseColorizeOccluded();
|
||||
}
|
||||
|
||||
void Application::doTrueVoxelColors() {
|
||||
_voxels.trueColorize();
|
||||
}
|
||||
|
@ -999,6 +1003,10 @@ void Application::setWantsDelta(bool wantsDelta) {
|
|||
_myAvatar.setWantDelta(wantsDelta);
|
||||
}
|
||||
|
||||
void Application::setWantsOcclusionCulling(bool wantsOcclusionCulling) {
|
||||
_myAvatar.setWantOcclusionCulling(wantsOcclusionCulling);
|
||||
}
|
||||
|
||||
void Application::updateVoxelModeActions() {
|
||||
// only the sender can be checked
|
||||
foreach (QAction* action, _voxelModeActions->actions()) {
|
||||
|
@ -1318,9 +1326,6 @@ void Application::initMenu() {
|
|||
_frustumOn->setShortcut(Qt::SHIFT | Qt::Key_F);
|
||||
(_viewFrustumFromOffset = frustumMenu->addAction(
|
||||
"Use Offset Camera", this, SLOT(setFrustumOffset(bool)), Qt::SHIFT | Qt::Key_O))->setCheckable(true);
|
||||
(_cameraFrustum = frustumMenu->addAction("Switch Camera"))->setCheckable(true);
|
||||
_cameraFrustum->setChecked(true);
|
||||
_cameraFrustum->setShortcut(Qt::SHIFT | Qt::Key_C);
|
||||
_frustumRenderModeAction = frustumMenu->addAction(
|
||||
"Render Mode", this, SLOT(cycleFrustumRenderMode()), Qt::SHIFT | Qt::Key_R);
|
||||
updateFrustumRenderModeAction();
|
||||
|
@ -1336,11 +1341,13 @@ void Application::initMenu() {
|
|||
renderDebugMenu->addAction("FALSE Color Voxel Every Other Randomly", this, SLOT(doFalseRandomizeEveryOtherVoxelColors()));
|
||||
renderDebugMenu->addAction("FALSE Color Voxels by Distance", this, SLOT(doFalseColorizeByDistance()));
|
||||
renderDebugMenu->addAction("FALSE Color Voxel Out of View", this, SLOT(doFalseColorizeInView()));
|
||||
renderDebugMenu->addAction("Show TRUE Colors", this, SLOT(doTrueVoxelColors()));
|
||||
renderDebugMenu->addAction("FALSE Color Occluded Voxels", this, SLOT(doFalseColorizeOccluded()), Qt::CTRL | Qt::Key_O);
|
||||
renderDebugMenu->addAction("Show TRUE Colors", this, SLOT(doTrueVoxelColors()), Qt::CTRL | Qt::Key_T);
|
||||
|
||||
debugMenu->addAction("Wants Res-In", this, SLOT(setWantsResIn(bool)))->setCheckable(true);
|
||||
debugMenu->addAction("Wants Monochrome", this, SLOT(setWantsMonochrome(bool)))->setCheckable(true);
|
||||
debugMenu->addAction("Wants View Delta Sending", this, SLOT(setWantsDelta(bool)))->setCheckable(true);
|
||||
debugMenu->addAction("Wants Occlusion Culling", this, SLOT(setWantsOcclusionCulling(bool)))->setCheckable(true);
|
||||
|
||||
QMenu* settingsMenu = menuBar->addMenu("Settings");
|
||||
(_settingsAutosave = settingsMenu->addAction("Autosave"))->setCheckable(true);
|
||||
|
@ -1750,15 +1757,7 @@ void Application::updateAvatar(float deltaTime) {
|
|||
//
|
||||
void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
|
||||
// We will use these below, from either the camera or head vectors calculated above
|
||||
glm::vec3 position;
|
||||
|
||||
// Camera or Head?
|
||||
if (_cameraFrustum->isChecked()) {
|
||||
position = camera.getPosition();
|
||||
} else {
|
||||
position = _myAvatar.getHeadJointPosition();
|
||||
}
|
||||
|
||||
glm::vec3 position(camera.getPosition());
|
||||
float fov = camera.getFieldOfView();
|
||||
float nearClip = camera.getNearClip();
|
||||
float farClip = camera.getFarClip();
|
||||
|
@ -1925,7 +1924,7 @@ void Application::displayOculus(Camera& whichCamera) {
|
|||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
|
||||
void Application::displaySide(Camera& whichCamera) {
|
||||
// transform by eye offset
|
||||
|
||||
|
|
|
@ -107,12 +107,14 @@ private slots:
|
|||
void doFalseRandomizeVoxelColors();
|
||||
void doFalseRandomizeEveryOtherVoxelColors();
|
||||
void doFalseColorizeByDistance();
|
||||
void doFalseColorizeOccluded();
|
||||
void doFalseColorizeInView();
|
||||
void doTrueVoxelColors();
|
||||
void doTreeStats();
|
||||
void setWantsMonochrome(bool wantsMonochrome);
|
||||
void setWantsResIn(bool wantsResIn);
|
||||
void setWantsDelta(bool wantsDelta);
|
||||
void setWantsOcclusionCulling(bool wantsOcclusionCulling);
|
||||
void updateVoxelModeActions();
|
||||
void decreaseVoxelSize();
|
||||
void increaseVoxelSize();
|
||||
|
@ -144,7 +146,6 @@ private:
|
|||
void displaySide(Camera& whichCamera);
|
||||
void displayOverlay();
|
||||
void displayStats();
|
||||
|
||||
void renderViewFrustum(ViewFrustum& viewFrustum);
|
||||
|
||||
void setupPaintingVoxel();
|
||||
|
@ -203,7 +204,6 @@ private:
|
|||
QAction* _destructiveAddVoxel; // when doing voxel editing do we want them to be destructive
|
||||
QAction* _frustumOn; // Whether or not to display the debug view frustum
|
||||
QAction* _viewFrustumFromOffset; // Whether or not to offset the view of the frustum
|
||||
QAction* _cameraFrustum; // which frustum to look at
|
||||
QAction* _fullScreenMode; // whether we are in full screen mode
|
||||
QAction* _frustumRenderModeAction;
|
||||
QAction* _settingsAutosave; // Whether settings are saved automatically
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "Application.h"
|
||||
#include "Log.h"
|
||||
#include "VoxelConstants.h"
|
||||
#include "CoverageMap.h"
|
||||
#include "InterfaceConfig.h"
|
||||
#include "renderer/ProgramObject.h"
|
||||
|
||||
|
@ -1156,3 +1157,139 @@ void VoxelSystem::copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* dest
|
|||
_tree->copyFromTreeIntoSubTree(sourceTree, destinationNode);
|
||||
}
|
||||
|
||||
struct FalseColorizeOccludedArgs {
|
||||
ViewFrustum* viewFrustum;
|
||||
CoverageMap* map;
|
||||
VoxelTree* tree;
|
||||
long totalVoxels;
|
||||
long coloredVoxels;
|
||||
long occludedVoxels;
|
||||
long notOccludedVoxels;
|
||||
long outOfView;
|
||||
long subtreeVoxelsSkipped;
|
||||
long nonLeaves;
|
||||
long nonLeavesOutOfView;
|
||||
long nonLeavesOccluded;
|
||||
long stagedForDeletion;
|
||||
};
|
||||
|
||||
struct FalseColorizeSubTreeOperationArgs {
|
||||
unsigned char color[NUMBER_OF_COLORS];
|
||||
long voxelsTouched;
|
||||
};
|
||||
|
||||
bool VoxelSystem::falseColorizeSubTreeOperation(VoxelNode* node, void* extraData) {
|
||||
FalseColorizeSubTreeOperationArgs* args = (FalseColorizeSubTreeOperationArgs*) extraData;
|
||||
node->setFalseColor(args->color[0], args->color[1], args->color[2]);
|
||||
args->voxelsTouched++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, void* extraData) {
|
||||
|
||||
FalseColorizeOccludedArgs* args = (FalseColorizeOccludedArgs*) extraData;
|
||||
args->totalVoxels++;
|
||||
|
||||
// if this node is staged for deletion, then just return
|
||||
if (node->isStagedForDeletion()) {
|
||||
args->stagedForDeletion++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we are a parent, let's see if we're completely occluded.
|
||||
if (!node->isLeaf()) {
|
||||
args->nonLeaves++;
|
||||
|
||||
AABox voxelBox = node->getAABox();
|
||||
voxelBox.scale(TREE_SCALE);
|
||||
VoxelProjectedPolygon* voxelShadow = new VoxelProjectedPolygon(args->viewFrustum->getProjectedShadow(voxelBox));
|
||||
|
||||
// If we're not all in view, then ignore it, and just return. But keep searching...
|
||||
if (!voxelShadow->getAllInView()) {
|
||||
args->nonLeavesOutOfView++;
|
||||
delete voxelShadow;
|
||||
return true;
|
||||
}
|
||||
|
||||
CoverageMap::StorageResult result = args->map->checkMap(voxelShadow, false);
|
||||
if (result == CoverageMap::OCCLUDED) {
|
||||
args->nonLeavesOccluded++;
|
||||
delete voxelShadow;
|
||||
|
||||
FalseColorizeSubTreeOperationArgs subArgs;
|
||||
subArgs.color[0] = 0;
|
||||
subArgs.color[1] = 255;
|
||||
subArgs.color[2] = 0;
|
||||
subArgs.voxelsTouched = 0;
|
||||
|
||||
args->tree->recurseNodeWithOperation(node, falseColorizeSubTreeOperation, &subArgs );
|
||||
|
||||
args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1);
|
||||
args->totalVoxels += (subArgs.voxelsTouched - 1);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
delete voxelShadow;
|
||||
return true; // keep looking...
|
||||
}
|
||||
|
||||
if (node->isLeaf() && node->isColored() && node->getShouldRender()) {
|
||||
args->coloredVoxels++;
|
||||
|
||||
AABox voxelBox = node->getAABox();
|
||||
voxelBox.scale(TREE_SCALE);
|
||||
VoxelProjectedPolygon* voxelShadow = new VoxelProjectedPolygon(args->viewFrustum->getProjectedShadow(voxelBox));
|
||||
|
||||
// If we're not all in view, then ignore it, and just return. But keep searching...
|
||||
if (!voxelShadow->getAllInView()) {
|
||||
args->outOfView++;
|
||||
delete voxelShadow;
|
||||
return true;
|
||||
}
|
||||
|
||||
CoverageMap::StorageResult result = args->map->checkMap(voxelShadow, true);
|
||||
if (result == CoverageMap::OCCLUDED) {
|
||||
node->setFalseColor(255, 0, 0);
|
||||
args->occludedVoxels++;
|
||||
} else if (result == CoverageMap::STORED) {
|
||||
args->notOccludedVoxels++;
|
||||
//printLog("***** falseColorizeOccludedOperation() NODE is STORED *****\n");
|
||||
} else if (result == CoverageMap::DOESNT_FIT) {
|
||||
//printLog("***** falseColorizeOccludedOperation() NODE DOESNT_FIT???? *****\n");
|
||||
}
|
||||
}
|
||||
return true; // keep going!
|
||||
}
|
||||
void VoxelSystem::falseColorizeOccluded() {
|
||||
PerformanceWarning warn(true, "falseColorizeOccluded()",true);
|
||||
CoverageMap map;
|
||||
FalseColorizeOccludedArgs args;
|
||||
args.viewFrustum = Application::getInstance()->getViewFrustum();
|
||||
args.map = ↦
|
||||
args.totalVoxels = 0;
|
||||
args.coloredVoxels = 0;
|
||||
args.occludedVoxels = 0;
|
||||
args.notOccludedVoxels = 0;
|
||||
args.outOfView = 0;
|
||||
args.subtreeVoxelsSkipped = 0;
|
||||
args.nonLeaves = 0;
|
||||
args.stagedForDeletion = 0;
|
||||
args.nonLeavesOutOfView = 0;
|
||||
args.nonLeavesOccluded = 0;
|
||||
args.tree = _tree;
|
||||
|
||||
glm::vec3 position = args.viewFrustum->getPosition() * (1.0f/TREE_SCALE);
|
||||
|
||||
_tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedOperation, position, (void*)&args);
|
||||
|
||||
printLog("falseColorizeOccluded()\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n",
|
||||
args.totalVoxels, args.coloredVoxels, args.occludedVoxels,
|
||||
args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped,
|
||||
args.stagedForDeletion,
|
||||
args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded);
|
||||
|
||||
|
||||
setupNewVoxelsForDrawing();
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ public:
|
|||
void falseColorizeInView(ViewFrustum* viewFrustum);
|
||||
void falseColorizeDistanceFromView(ViewFrustum* viewFrustum);
|
||||
void falseColorizeRandomEveryOther();
|
||||
void falseColorizeOccluded();
|
||||
|
||||
void killLocalVoxels();
|
||||
void setRenderPipelineWarnings(bool on) { _renderWarningsOn = on; };
|
||||
|
@ -120,6 +121,8 @@ private:
|
|||
static bool removeOutOfViewOperation(VoxelNode* node, void* extraData);
|
||||
static bool falseColorizeRandomEveryOtherOperation(VoxelNode* node, void* extraData);
|
||||
static bool collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData);
|
||||
static bool falseColorizeOccludedOperation(VoxelNode* node, void* extraData);
|
||||
static bool falseColorizeSubTreeOperation(VoxelNode* node, void* extraData);
|
||||
|
||||
int updateNodeInArraysAsFullVBO(VoxelNode* node);
|
||||
int updateNodeInArraysAsPartialVBO(VoxelNode* node);
|
||||
|
|
|
@ -35,6 +35,7 @@ AvatarData::AvatarData(Agent* owningAgent) :
|
|||
_wantResIn(false),
|
||||
_wantColor(true),
|
||||
_wantDelta(false),
|
||||
_wantOcclusionCulling(false),
|
||||
_headData(NULL)
|
||||
{
|
||||
|
||||
|
@ -106,9 +107,10 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
|
|||
|
||||
// bitMask of less than byte wide items
|
||||
unsigned char bitItems = 0;
|
||||
if (_wantResIn) { setAtBit(bitItems,WANT_RESIN_AT_BIT); }
|
||||
if (_wantColor) { setAtBit(bitItems,WANT_COLOR_AT_BIT); }
|
||||
if (_wantDelta) { setAtBit(bitItems,WANT_DELTA_AT_BIT); }
|
||||
if (_wantResIn) { setAtBit(bitItems, WANT_RESIN_AT_BIT); }
|
||||
if (_wantColor) { setAtBit(bitItems, WANT_COLOR_AT_BIT); }
|
||||
if (_wantDelta) { setAtBit(bitItems, WANT_DELTA_AT_BIT); }
|
||||
if (_wantOcclusionCulling) { setAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT); }
|
||||
|
||||
// key state
|
||||
setSemiNibbleAt(bitItems,KEY_STATE_START_BIT,_keyState);
|
||||
|
@ -192,9 +194,10 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|||
// voxel sending features...
|
||||
unsigned char bitItems = 0;
|
||||
bitItems = (unsigned char)*sourceBuffer++;
|
||||
_wantResIn = oneAtBit(bitItems,WANT_RESIN_AT_BIT);
|
||||
_wantColor = oneAtBit(bitItems,WANT_COLOR_AT_BIT);
|
||||
_wantDelta = oneAtBit(bitItems,WANT_DELTA_AT_BIT);
|
||||
_wantResIn = oneAtBit(bitItems, WANT_RESIN_AT_BIT);
|
||||
_wantColor = oneAtBit(bitItems, WANT_COLOR_AT_BIT);
|
||||
_wantDelta = oneAtBit(bitItems, WANT_DELTA_AT_BIT);
|
||||
_wantOcclusionCulling = oneAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT);
|
||||
|
||||
// key state, stored as a semi-nibble in the bitItems
|
||||
_keyState = (KeyState)getSemiNibbleAt(bitItems,KEY_STATE_START_BIT);
|
||||
|
|
|
@ -23,6 +23,7 @@ const int WANT_COLOR_AT_BIT = 1;
|
|||
const int WANT_DELTA_AT_BIT = 2;
|
||||
const int KEY_STATE_START_BIT = 3; // 4th and 5th bits
|
||||
const int HAND_STATE_START_BIT = 5; // 6th and 7th bits
|
||||
const int WANT_OCCLUSION_CULLING_BIT = 7; // 8th bit
|
||||
|
||||
const float MAX_AUDIO_LOUDNESS = 1000.0; // close enough for mouth animation
|
||||
|
||||
|
@ -89,9 +90,11 @@ public:
|
|||
bool getWantResIn() const { return _wantResIn; }
|
||||
bool getWantColor() const { return _wantColor; }
|
||||
bool getWantDelta() const { return _wantDelta; }
|
||||
bool getWantOcclusionCulling() const { return _wantOcclusionCulling; }
|
||||
void setWantResIn(bool wantResIn) { _wantResIn = wantResIn; }
|
||||
void setWantColor(bool wantColor) { _wantColor = wantColor; }
|
||||
void setWantDelta(bool wantDelta) { _wantDelta = wantDelta; }
|
||||
void setWantOcclusionCulling(bool wantOcclusionCulling) { _wantOcclusionCulling = wantOcclusionCulling; }
|
||||
|
||||
void setHeadData(HeadData* headData) { _headData = headData; }
|
||||
|
||||
|
@ -125,6 +128,7 @@ protected:
|
|||
bool _wantResIn;
|
||||
bool _wantColor;
|
||||
bool _wantDelta;
|
||||
bool _wantOcclusionCulling;
|
||||
|
||||
HeadData* _headData;
|
||||
private:
|
||||
|
|
|
@ -409,6 +409,7 @@ void printVoxelCode(unsigned char* voxelCode) {
|
|||
// the second array is a sorted key for the value, the third array is the index for the value in it original
|
||||
// non-sorted array
|
||||
// returns -1 if size exceeded
|
||||
// originalIndexArray is optional
|
||||
int insertIntoSortedArrays(void* value, float key, int originalIndex,
|
||||
void** valueArray, float* keyArray, int* originalIndexArray,
|
||||
int currentCount, int maxCount) {
|
||||
|
@ -424,13 +425,17 @@ int insertIntoSortedArrays(void* value, float key, int originalIndex,
|
|||
if (i < currentCount && i+1 < maxCount) {
|
||||
memcpy(&valueArray[i + 1], &valueArray[i], sizeof(void*) * (currentCount - i));
|
||||
memcpy(&keyArray[i + 1], &keyArray[i], sizeof(float) * (currentCount - i));
|
||||
memcpy(&originalIndexArray[i + 1], &originalIndexArray[i], sizeof(int) * (currentCount - i));
|
||||
if (originalIndexArray) {
|
||||
memcpy(&originalIndexArray[i + 1], &originalIndexArray[i], sizeof(int) * (currentCount - i));
|
||||
}
|
||||
}
|
||||
}
|
||||
// place new element at i
|
||||
valueArray[i] = value;
|
||||
keyArray[i] = key;
|
||||
originalIndexArray[i] = originalIndex;
|
||||
if (originalIndexArray) {
|
||||
originalIndexArray[i] = originalIndex;
|
||||
}
|
||||
return currentCount + 1;
|
||||
}
|
||||
return -1; // error case
|
||||
|
|
|
@ -1,332 +1,353 @@
|
|||
//
|
||||
// AABox.h - Axis Aligned Boxes
|
||||
// hifi
|
||||
//
|
||||
// Added by Brad Hefta-Gaub on 04/11/13.
|
||||
// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards
|
||||
//
|
||||
// Simple axis aligned box class.
|
||||
//
|
||||
|
||||
#include "SharedUtil.h"
|
||||
|
||||
#include "AABox.h"
|
||||
#include "GeometryUtil.h"
|
||||
|
||||
|
||||
void AABox::scale(float scale) {
|
||||
_corner = _corner * scale;
|
||||
_size = _size * scale;
|
||||
_center = _center * scale;
|
||||
}
|
||||
|
||||
|
||||
void AABox::setBox(const glm::vec3& corner, const glm::vec3& size) {
|
||||
_corner = corner;
|
||||
_size = size;
|
||||
|
||||
// In the event that the caller gave us negative sizes, fix things up to be reasonable
|
||||
if (_size.x < 0.0) {
|
||||
_size.x = -size.x;
|
||||
_corner.x -= _size.x;
|
||||
}
|
||||
if (_size.y < 0.0) {
|
||||
_size.y = -size.y;
|
||||
_corner.y -= _size.y;
|
||||
}
|
||||
if (_size.z < 0.0) {
|
||||
_size.z = -size.z;
|
||||
_corner.z -= _size.z;
|
||||
}
|
||||
_center = _corner + (_size * 0.5f);
|
||||
}
|
||||
|
||||
glm::vec3 AABox::getVertexP(const glm::vec3 &normal) const {
|
||||
glm::vec3 res = _corner;
|
||||
if (normal.x > 0)
|
||||
res.x += _size.x;
|
||||
|
||||
if (normal.y > 0)
|
||||
res.y += _size.y;
|
||||
|
||||
if (normal.z > 0)
|
||||
res.z += _size.z;
|
||||
|
||||
return(res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
glm::vec3 AABox::getVertexN(const glm::vec3 &normal) const {
|
||||
glm::vec3 res = _corner;
|
||||
|
||||
if (normal.x < 0)
|
||||
res.x += _size.x;
|
||||
|
||||
if (normal.y < 0)
|
||||
res.y += _size.y;
|
||||
|
||||
if (normal.z < 0)
|
||||
res.z += _size.z;
|
||||
|
||||
return(res);
|
||||
}
|
||||
|
||||
// determines whether a value is within the extents
|
||||
static bool isWithin(float value, float corner, float size) {
|
||||
return value >= corner && value <= corner + size;
|
||||
}
|
||||
|
||||
bool AABox::contains(const glm::vec3& point) const {
|
||||
return isWithin(point.x, _corner.x, _size.x) &&
|
||||
isWithin(point.y, _corner.y, _size.y) &&
|
||||
isWithin(point.z, _corner.z, _size.z);
|
||||
}
|
||||
|
||||
// determines whether a value is within the expanded extents
|
||||
static bool isWithinExpanded(float value, float corner, float size, float expansion) {
|
||||
return value >= corner - expansion && value <= corner + size + expansion;
|
||||
}
|
||||
|
||||
bool AABox::expandedContains(const glm::vec3& point, float expansion) const {
|
||||
return isWithinExpanded(point.x, _corner.x, _size.x, expansion) &&
|
||||
isWithinExpanded(point.y, _corner.y, _size.y, expansion) &&
|
||||
isWithinExpanded(point.z, _corner.z, _size.z, expansion);
|
||||
}
|
||||
|
||||
// finds the intersection between a ray and the facing plane on one axis
|
||||
static bool findIntersection(float origin, float direction, float corner, float size, float& distance) {
|
||||
if (direction > EPSILON) {
|
||||
distance = (corner - origin) / direction;
|
||||
return true;
|
||||
|
||||
} else if (direction < -EPSILON) {
|
||||
distance = (corner + size - origin) / direction;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AABox::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const {
|
||||
// handle the trivial cases where the expanded box contains the start or end
|
||||
if (expandedContains(start, expansion) || expandedContains(end, expansion)) {
|
||||
return true;
|
||||
}
|
||||
// check each axis
|
||||
glm::vec3 expandedCorner = _corner - glm::vec3(expansion, expansion, expansion);
|
||||
glm::vec3 expandedSize = _size + glm::vec3(expansion, expansion, expansion) * 2.0f;
|
||||
glm::vec3 direction = end - start;
|
||||
float axisDistance;
|
||||
return (findIntersection(start.x, direction.x, expandedCorner.x, expandedSize.x, axisDistance) &&
|
||||
axisDistance >= 0.0f && axisDistance <= 1.0f &&
|
||||
isWithin(start.y + axisDistance*direction.y, expandedCorner.y, expandedSize.y) &&
|
||||
isWithin(start.z + axisDistance*direction.z, expandedCorner.z, expandedSize.z)) ||
|
||||
(findIntersection(start.y, direction.y, expandedCorner.y, expandedSize.y, axisDistance) &&
|
||||
axisDistance >= 0.0f && axisDistance <= 1.0f &&
|
||||
isWithin(start.x + axisDistance*direction.x, expandedCorner.x, expandedSize.x) &&
|
||||
isWithin(start.z + axisDistance*direction.z, expandedCorner.z, expandedSize.z)) ||
|
||||
(findIntersection(start.z, direction.z, expandedCorner.z, expandedSize.z, axisDistance) &&
|
||||
axisDistance >= 0.0f && axisDistance <= 1.0f &&
|
||||
isWithin(start.y + axisDistance*direction.y, expandedCorner.y, expandedSize.y) &&
|
||||
isWithin(start.x + axisDistance*direction.x, expandedCorner.x, expandedSize.x));
|
||||
}
|
||||
|
||||
bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const {
|
||||
// handle the trivial case where the box contains the origin
|
||||
if (contains(origin)) {
|
||||
distance = 0;
|
||||
return true;
|
||||
}
|
||||
// check each axis
|
||||
float axisDistance;
|
||||
if ((findIntersection(origin.x, direction.x, _corner.x, _size.x, axisDistance) && axisDistance >= 0 &&
|
||||
isWithin(origin.y + axisDistance*direction.y, _corner.y, _size.y) &&
|
||||
isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z))) {
|
||||
distance = axisDistance;
|
||||
face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE;
|
||||
return true;
|
||||
}
|
||||
if ((findIntersection(origin.y, direction.y, _corner.y, _size.y, axisDistance) && axisDistance >= 0 &&
|
||||
isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x) &&
|
||||
isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z))) {
|
||||
distance = axisDistance;
|
||||
face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE;
|
||||
return true;
|
||||
}
|
||||
if ((findIntersection(origin.z, direction.z, _corner.z, _size.z, axisDistance) && axisDistance >= 0 &&
|
||||
isWithin(origin.y + axisDistance*direction.y, _corner.y, _size.y) &&
|
||||
isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x))) {
|
||||
distance = axisDistance;
|
||||
face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AABox::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const {
|
||||
glm::vec4 center4 = glm::vec4(center, 1.0f);
|
||||
|
||||
float minPenetrationLength = FLT_MAX;
|
||||
for (int i = 0; i < FACE_COUNT; i++) {
|
||||
glm::vec4 facePlane = getPlane((BoxFace)i);
|
||||
glm::vec3 vector = getClosestPointOnFace(center, (BoxFace)i) - center;
|
||||
if (glm::dot(center4, getPlane((BoxFace)i)) >= 0.0f) {
|
||||
// outside this face, so use vector to closest point to determine penetration
|
||||
return ::findSpherePenetration(vector, glm::vec3(-facePlane), radius, penetration);
|
||||
}
|
||||
float vectorLength = glm::length(vector);
|
||||
if (vectorLength < minPenetrationLength) {
|
||||
// remember the smallest penetration vector; if we're inside all faces, we'll use that
|
||||
penetration = (vectorLength < EPSILON) ? glm::vec3(-facePlane) * radius :
|
||||
vector * ((vectorLength + radius) / -vectorLength);
|
||||
minPenetrationLength = vectorLength;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AABox::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) const {
|
||||
glm::vec4 start4 = glm::vec4(start, 1.0f);
|
||||
glm::vec4 end4 = glm::vec4(end, 1.0f);
|
||||
glm::vec4 startToEnd = glm::vec4(end - start, 0.0f);
|
||||
|
||||
float minPenetrationLength = FLT_MAX;
|
||||
for (int i = 0; i < FACE_COUNT; i++) {
|
||||
// find the vector from the segment to the closest point on the face (starting from deeper end)
|
||||
glm::vec4 facePlane = getPlane((BoxFace)i);
|
||||
glm::vec3 closest = (glm::dot(start4, facePlane) <= glm::dot(end4, facePlane)) ?
|
||||
getClosestPointOnFace(start4, startToEnd, (BoxFace)i) : getClosestPointOnFace(end4, -startToEnd, (BoxFace)i);
|
||||
glm::vec3 vector = -computeVectorFromPointToSegment(closest, start, end);
|
||||
if (glm::dot(vector, glm::vec3(facePlane)) < 0.0f) {
|
||||
// outside this face, so use vector to closest point to determine penetration
|
||||
return ::findSpherePenetration(vector, glm::vec3(-facePlane), radius, penetration);
|
||||
}
|
||||
float vectorLength = glm::length(vector);
|
||||
if (vectorLength < minPenetrationLength) {
|
||||
// remember the smallest penetration vector; if we're inside all faces, we'll use that
|
||||
penetration = (vectorLength < EPSILON) ? glm::vec3(-facePlane) * radius :
|
||||
vector * ((vectorLength + radius) / -vectorLength);
|
||||
minPenetrationLength = vectorLength;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
glm::vec3 AABox::getClosestPointOnFace(const glm::vec3& point, BoxFace face) const {
|
||||
switch (face) {
|
||||
case MIN_X_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z),
|
||||
glm::vec3(_corner.x, _corner.y + _size.y, _corner.z + _size.z));
|
||||
|
||||
case MAX_X_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x + _size.x, _corner.y, _corner.z),
|
||||
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z));
|
||||
|
||||
case MIN_Y_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z),
|
||||
glm::vec3(_corner.x + _size.x, _corner.y, _corner.z + _size.z));
|
||||
|
||||
case MAX_Y_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y + _size.y, _corner.z),
|
||||
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z));
|
||||
|
||||
case MIN_Z_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z),
|
||||
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z));
|
||||
|
||||
case MAX_Z_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z + _size.z),
|
||||
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z));
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 AABox::getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const {
|
||||
// check against the four planes that border the face
|
||||
BoxFace oppositeFace = getOppositeFace(face);
|
||||
bool anyOutside = false;
|
||||
for (int i = 0; i < FACE_COUNT; i++) {
|
||||
if (i == face || i == oppositeFace) {
|
||||
continue;
|
||||
}
|
||||
glm::vec4 iPlane = getPlane((BoxFace)i);
|
||||
float originDistance = glm::dot(origin, iPlane);
|
||||
if (originDistance < 0.0f) {
|
||||
continue; // inside the border
|
||||
}
|
||||
anyOutside = true;
|
||||
float divisor = glm::dot(direction, iPlane);
|
||||
if (fabs(divisor) < EPSILON) {
|
||||
continue; // segment is parallel to plane
|
||||
}
|
||||
// find intersection and see if it lies within face bounds
|
||||
float directionalDistance = -originDistance / divisor;
|
||||
glm::vec4 intersection = origin + direction * directionalDistance;
|
||||
BoxFace iOppositeFace = getOppositeFace((BoxFace)i);
|
||||
for (int j = 0; j < FACE_COUNT; j++) {
|
||||
if (j == face || j == oppositeFace || j == i || j == iOppositeFace) {
|
||||
continue;
|
||||
}
|
||||
if (glm::dot(intersection, getPlane((BoxFace)j)) > 0.0f) {
|
||||
goto outerContinue; // intersection is out of bounds
|
||||
}
|
||||
}
|
||||
return getClosestPointOnFace(glm::vec3(intersection), face);
|
||||
|
||||
outerContinue: ;
|
||||
}
|
||||
|
||||
// if we were outside any of the sides, we must check against the diagonals
|
||||
if (anyOutside) {
|
||||
int faceAxis = face / 2;
|
||||
int secondAxis = (faceAxis + 1) % 3;
|
||||
int thirdAxis = (faceAxis + 2) % 3;
|
||||
|
||||
glm::vec4 secondAxisMinPlane = getPlane((BoxFace)(secondAxis * 2));
|
||||
glm::vec4 secondAxisMaxPlane = getPlane((BoxFace)(secondAxis * 2 + 1));
|
||||
glm::vec4 thirdAxisMaxPlane = getPlane((BoxFace)(thirdAxis * 2 + 1));
|
||||
|
||||
glm::vec4 offset = glm::vec4(0.0f, 0.0f, 0.0f,
|
||||
glm::dot(glm::vec3(secondAxisMaxPlane + thirdAxisMaxPlane), _size) * 0.5f);
|
||||
glm::vec4 diagonals[] = { secondAxisMinPlane + thirdAxisMaxPlane + offset,
|
||||
secondAxisMaxPlane + thirdAxisMaxPlane + offset };
|
||||
|
||||
float minDistance = FLT_MAX;
|
||||
for (int i = 0; i < sizeof(diagonals) / sizeof(diagonals[0]); i++) {
|
||||
float divisor = glm::dot(direction, diagonals[i]);
|
||||
if (fabs(divisor) < EPSILON) {
|
||||
continue; // segment is parallel to diagonal plane
|
||||
}
|
||||
minDistance = glm::min(-glm::dot(origin, diagonals[i]) / divisor, minDistance);
|
||||
}
|
||||
if (minDistance != FLT_MAX) {
|
||||
return getClosestPointOnFace(glm::vec3(origin + direction * minDistance), face);
|
||||
}
|
||||
}
|
||||
|
||||
// last resort or all inside: clamp origin to face
|
||||
return getClosestPointOnFace(glm::vec3(origin), face);
|
||||
}
|
||||
|
||||
glm::vec4 AABox::getPlane(BoxFace face) const {
|
||||
switch (face) {
|
||||
case MIN_X_FACE: return glm::vec4(-1.0f, 0.0f, 0.0f, _corner.x);
|
||||
case MAX_X_FACE: return glm::vec4(1.0f, 0.0f, 0.0f, -_corner.x - _size.x);
|
||||
case MIN_Y_FACE: return glm::vec4(0.0f, -1.0f, 0.0f, _corner.y);
|
||||
case MAX_Y_FACE: return glm::vec4(0.0f, 1.0f, 0.0f, -_corner.y - _size.y);
|
||||
case MIN_Z_FACE: return glm::vec4(0.0f, 0.0f, -1.0f, _corner.z);
|
||||
case MAX_Z_FACE: return glm::vec4(0.0f, 0.0f, 1.0f, -_corner.z - _size.z);
|
||||
}
|
||||
}
|
||||
|
||||
BoxFace AABox::getOppositeFace(BoxFace face) {
|
||||
switch (face) {
|
||||
case MIN_X_FACE: return MAX_X_FACE;
|
||||
case MAX_X_FACE: return MIN_X_FACE;
|
||||
case MIN_Y_FACE: return MAX_Y_FACE;
|
||||
case MAX_Y_FACE: return MIN_Y_FACE;
|
||||
case MIN_Z_FACE: return MAX_Z_FACE;
|
||||
case MAX_Z_FACE: return MIN_Z_FACE;
|
||||
}
|
||||
}
|
||||
//
|
||||
// AABox.h - Axis Aligned Boxes
|
||||
// hifi
|
||||
//
|
||||
// Added by Brad Hefta-Gaub on 04/11/13.
|
||||
// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards
|
||||
//
|
||||
// Simple axis aligned box class.
|
||||
//
|
||||
|
||||
#include "SharedUtil.h"
|
||||
|
||||
#include "AABox.h"
|
||||
#include "GeometryUtil.h"
|
||||
|
||||
|
||||
void AABox::scale(float scale) {
|
||||
_corner = _corner * scale;
|
||||
_size = _size * scale;
|
||||
_center = _center * scale;
|
||||
}
|
||||
|
||||
|
||||
glm::vec3 AABox::getVertex(BoxVertex vertex) const {
|
||||
switch (vertex) {
|
||||
case BOTTOM_LEFT_NEAR:
|
||||
return _corner + glm::vec3(_size.x, 0, 0);
|
||||
case BOTTOM_RIGHT_NEAR:
|
||||
return _corner;
|
||||
case TOP_RIGHT_NEAR:
|
||||
return _corner + glm::vec3(0, _size.y, 0);
|
||||
case TOP_LEFT_NEAR:
|
||||
return _corner + glm::vec3(_size.x, _size.y, 0);
|
||||
case BOTTOM_LEFT_FAR:
|
||||
return _corner + glm::vec3(_size.x, 0, _size.z);
|
||||
case BOTTOM_RIGHT_FAR:
|
||||
return _corner + glm::vec3(0, 0, _size.z);
|
||||
case TOP_RIGHT_FAR:
|
||||
return _corner + glm::vec3(0, _size.y, _size.z);
|
||||
case TOP_LEFT_FAR:
|
||||
return _corner + _size;
|
||||
}
|
||||
}
|
||||
|
||||
void AABox::setBox(const glm::vec3& corner, const glm::vec3& size) {
|
||||
_corner = corner;
|
||||
_size = size;
|
||||
|
||||
// In the event that the caller gave us negative sizes, fix things up to be reasonable
|
||||
if (_size.x < 0.0) {
|
||||
_size.x = -size.x;
|
||||
_corner.x -= _size.x;
|
||||
}
|
||||
if (_size.y < 0.0) {
|
||||
_size.y = -size.y;
|
||||
_corner.y -= _size.y;
|
||||
}
|
||||
if (_size.z < 0.0) {
|
||||
_size.z = -size.z;
|
||||
_corner.z -= _size.z;
|
||||
}
|
||||
_center = _corner + (_size * 0.5f);
|
||||
}
|
||||
|
||||
glm::vec3 AABox::getVertexP(const glm::vec3& normal) const {
|
||||
glm::vec3 result = _corner;
|
||||
if (normal.x > 0) {
|
||||
result.x += _size.x;
|
||||
}
|
||||
if (normal.y > 0) {
|
||||
result.y += _size.y;
|
||||
}
|
||||
if (normal.z > 0) {
|
||||
result.z += _size.z;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec3 AABox::getVertexN(const glm::vec3& normal) const {
|
||||
glm::vec3 result = _corner;
|
||||
|
||||
if (normal.x < 0) {
|
||||
result.x += _size.x;
|
||||
}
|
||||
|
||||
if (normal.y < 0) {
|
||||
result.y += _size.y;
|
||||
}
|
||||
|
||||
if (normal.z < 0) {
|
||||
result.z += _size.z;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// determines whether a value is within the extents
|
||||
static bool isWithin(float value, float corner, float size) {
|
||||
return value >= corner && value <= corner + size;
|
||||
}
|
||||
|
||||
bool AABox::contains(const glm::vec3& point) const {
|
||||
return isWithin(point.x, _corner.x, _size.x) &&
|
||||
isWithin(point.y, _corner.y, _size.y) &&
|
||||
isWithin(point.z, _corner.z, _size.z);
|
||||
}
|
||||
|
||||
// determines whether a value is within the expanded extents
|
||||
static bool isWithinExpanded(float value, float corner, float size, float expansion) {
|
||||
return value >= corner - expansion && value <= corner + size + expansion;
|
||||
}
|
||||
|
||||
bool AABox::expandedContains(const glm::vec3& point, float expansion) const {
|
||||
return isWithinExpanded(point.x, _corner.x, _size.x, expansion) &&
|
||||
isWithinExpanded(point.y, _corner.y, _size.y, expansion) &&
|
||||
isWithinExpanded(point.z, _corner.z, _size.z, expansion);
|
||||
}
|
||||
|
||||
// finds the intersection between a ray and the facing plane on one axis
|
||||
static bool findIntersection(float origin, float direction, float corner, float size, float& distance) {
|
||||
if (direction > EPSILON) {
|
||||
distance = (corner - origin) / direction;
|
||||
return true;
|
||||
} else if (direction < -EPSILON) {
|
||||
distance = (corner + size - origin) / direction;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AABox::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const {
|
||||
// handle the trivial cases where the expanded box contains the start or end
|
||||
if (expandedContains(start, expansion) || expandedContains(end, expansion)) {
|
||||
return true;
|
||||
}
|
||||
// check each axis
|
||||
glm::vec3 expandedCorner = _corner - glm::vec3(expansion, expansion, expansion);
|
||||
glm::vec3 expandedSize = _size + glm::vec3(expansion, expansion, expansion) * 2.0f;
|
||||
glm::vec3 direction = end - start;
|
||||
float axisDistance;
|
||||
return (findIntersection(start.x, direction.x, expandedCorner.x, expandedSize.x, axisDistance) &&
|
||||
axisDistance >= 0.0f && axisDistance <= 1.0f &&
|
||||
isWithin(start.y + axisDistance*direction.y, expandedCorner.y, expandedSize.y) &&
|
||||
isWithin(start.z + axisDistance*direction.z, expandedCorner.z, expandedSize.z)) ||
|
||||
(findIntersection(start.y, direction.y, expandedCorner.y, expandedSize.y, axisDistance) &&
|
||||
axisDistance >= 0.0f && axisDistance <= 1.0f &&
|
||||
isWithin(start.x + axisDistance*direction.x, expandedCorner.x, expandedSize.x) &&
|
||||
isWithin(start.z + axisDistance*direction.z, expandedCorner.z, expandedSize.z)) ||
|
||||
(findIntersection(start.z, direction.z, expandedCorner.z, expandedSize.z, axisDistance) &&
|
||||
axisDistance >= 0.0f && axisDistance <= 1.0f &&
|
||||
isWithin(start.y + axisDistance*direction.y, expandedCorner.y, expandedSize.y) &&
|
||||
isWithin(start.x + axisDistance*direction.x, expandedCorner.x, expandedSize.x));
|
||||
}
|
||||
|
||||
bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const {
|
||||
// handle the trivial case where the box contains the origin
|
||||
if (contains(origin)) {
|
||||
distance = 0;
|
||||
return true;
|
||||
}
|
||||
// check each axis
|
||||
float axisDistance;
|
||||
if ((findIntersection(origin.x, direction.x, _corner.x, _size.x, axisDistance) && axisDistance >= 0 &&
|
||||
isWithin(origin.y + axisDistance*direction.y, _corner.y, _size.y) &&
|
||||
isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z))) {
|
||||
distance = axisDistance;
|
||||
face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE;
|
||||
return true;
|
||||
}
|
||||
if ((findIntersection(origin.y, direction.y, _corner.y, _size.y, axisDistance) && axisDistance >= 0 &&
|
||||
isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x) &&
|
||||
isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z))) {
|
||||
distance = axisDistance;
|
||||
face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE;
|
||||
return true;
|
||||
}
|
||||
if ((findIntersection(origin.z, direction.z, _corner.z, _size.z, axisDistance) && axisDistance >= 0 &&
|
||||
isWithin(origin.y + axisDistance*direction.y, _corner.y, _size.y) &&
|
||||
isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x))) {
|
||||
distance = axisDistance;
|
||||
face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AABox::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const {
|
||||
glm::vec4 center4 = glm::vec4(center, 1.0f);
|
||||
|
||||
float minPenetrationLength = FLT_MAX;
|
||||
for (int i = 0; i < FACE_COUNT; i++) {
|
||||
glm::vec4 facePlane = getPlane((BoxFace)i);
|
||||
glm::vec3 vector = getClosestPointOnFace(center, (BoxFace)i) - center;
|
||||
if (glm::dot(center4, getPlane((BoxFace)i)) >= 0.0f) {
|
||||
// outside this face, so use vector to closest point to determine penetration
|
||||
return ::findSpherePenetration(vector, glm::vec3(-facePlane), radius, penetration);
|
||||
}
|
||||
float vectorLength = glm::length(vector);
|
||||
if (vectorLength < minPenetrationLength) {
|
||||
// remember the smallest penetration vector; if we're inside all faces, we'll use that
|
||||
penetration = (vectorLength < EPSILON) ? glm::vec3(-facePlane) * radius :
|
||||
vector * ((vectorLength + radius) / -vectorLength);
|
||||
minPenetrationLength = vectorLength;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AABox::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) const {
|
||||
glm::vec4 start4 = glm::vec4(start, 1.0f);
|
||||
glm::vec4 end4 = glm::vec4(end, 1.0f);
|
||||
glm::vec4 startToEnd = glm::vec4(end - start, 0.0f);
|
||||
|
||||
float minPenetrationLength = FLT_MAX;
|
||||
for (int i = 0; i < FACE_COUNT; i++) {
|
||||
// find the vector from the segment to the closest point on the face (starting from deeper end)
|
||||
glm::vec4 facePlane = getPlane((BoxFace)i);
|
||||
glm::vec3 closest = (glm::dot(start4, facePlane) <= glm::dot(end4, facePlane)) ?
|
||||
getClosestPointOnFace(start4, startToEnd, (BoxFace)i) : getClosestPointOnFace(end4, -startToEnd, (BoxFace)i);
|
||||
glm::vec3 vector = -computeVectorFromPointToSegment(closest, start, end);
|
||||
if (glm::dot(vector, glm::vec3(facePlane)) < 0.0f) {
|
||||
// outside this face, so use vector to closest point to determine penetration
|
||||
return ::findSpherePenetration(vector, glm::vec3(-facePlane), radius, penetration);
|
||||
}
|
||||
float vectorLength = glm::length(vector);
|
||||
if (vectorLength < minPenetrationLength) {
|
||||
// remember the smallest penetration vector; if we're inside all faces, we'll use that
|
||||
penetration = (vectorLength < EPSILON) ? glm::vec3(-facePlane) * radius :
|
||||
vector * ((vectorLength + radius) / -vectorLength);
|
||||
minPenetrationLength = vectorLength;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
glm::vec3 AABox::getClosestPointOnFace(const glm::vec3& point, BoxFace face) const {
|
||||
switch (face) {
|
||||
case MIN_X_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z),
|
||||
glm::vec3(_corner.x, _corner.y + _size.y, _corner.z + _size.z));
|
||||
|
||||
case MAX_X_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x + _size.x, _corner.y, _corner.z),
|
||||
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z));
|
||||
|
||||
case MIN_Y_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z),
|
||||
glm::vec3(_corner.x + _size.x, _corner.y, _corner.z + _size.z));
|
||||
|
||||
case MAX_Y_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y + _size.y, _corner.z),
|
||||
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z));
|
||||
|
||||
case MIN_Z_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z),
|
||||
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z));
|
||||
|
||||
case MAX_Z_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z + _size.z),
|
||||
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z));
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 AABox::getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const {
|
||||
// check against the four planes that border the face
|
||||
BoxFace oppositeFace = getOppositeFace(face);
|
||||
bool anyOutside = false;
|
||||
for (int i = 0; i < FACE_COUNT; i++) {
|
||||
if (i == face || i == oppositeFace) {
|
||||
continue;
|
||||
}
|
||||
glm::vec4 iPlane = getPlane((BoxFace)i);
|
||||
float originDistance = glm::dot(origin, iPlane);
|
||||
if (originDistance < 0.0f) {
|
||||
continue; // inside the border
|
||||
}
|
||||
anyOutside = true;
|
||||
float divisor = glm::dot(direction, iPlane);
|
||||
if (fabs(divisor) < EPSILON) {
|
||||
continue; // segment is parallel to plane
|
||||
}
|
||||
// find intersection and see if it lies within face bounds
|
||||
float directionalDistance = -originDistance / divisor;
|
||||
glm::vec4 intersection = origin + direction * directionalDistance;
|
||||
BoxFace iOppositeFace = getOppositeFace((BoxFace)i);
|
||||
for (int j = 0; j < FACE_COUNT; j++) {
|
||||
if (j == face || j == oppositeFace || j == i || j == iOppositeFace) {
|
||||
continue;
|
||||
}
|
||||
if (glm::dot(intersection, getPlane((BoxFace)j)) > 0.0f) {
|
||||
goto outerContinue; // intersection is out of bounds
|
||||
}
|
||||
}
|
||||
return getClosestPointOnFace(glm::vec3(intersection), face);
|
||||
|
||||
outerContinue: ;
|
||||
}
|
||||
|
||||
// if we were outside any of the sides, we must check against the diagonals
|
||||
if (anyOutside) {
|
||||
int faceAxis = face / 2;
|
||||
int secondAxis = (faceAxis + 1) % 3;
|
||||
int thirdAxis = (faceAxis + 2) % 3;
|
||||
|
||||
glm::vec4 secondAxisMinPlane = getPlane((BoxFace)(secondAxis * 2));
|
||||
glm::vec4 secondAxisMaxPlane = getPlane((BoxFace)(secondAxis * 2 + 1));
|
||||
glm::vec4 thirdAxisMaxPlane = getPlane((BoxFace)(thirdAxis * 2 + 1));
|
||||
|
||||
glm::vec4 offset = glm::vec4(0.0f, 0.0f, 0.0f,
|
||||
glm::dot(glm::vec3(secondAxisMaxPlane + thirdAxisMaxPlane), _size) * 0.5f);
|
||||
glm::vec4 diagonals[] = { secondAxisMinPlane + thirdAxisMaxPlane + offset,
|
||||
secondAxisMaxPlane + thirdAxisMaxPlane + offset };
|
||||
|
||||
float minDistance = FLT_MAX;
|
||||
for (int i = 0; i < sizeof(diagonals) / sizeof(diagonals[0]); i++) {
|
||||
float divisor = glm::dot(direction, diagonals[i]);
|
||||
if (fabs(divisor) < EPSILON) {
|
||||
continue; // segment is parallel to diagonal plane
|
||||
}
|
||||
minDistance = glm::min(-glm::dot(origin, diagonals[i]) / divisor, minDistance);
|
||||
}
|
||||
if (minDistance != FLT_MAX) {
|
||||
return getClosestPointOnFace(glm::vec3(origin + direction * minDistance), face);
|
||||
}
|
||||
}
|
||||
|
||||
// last resort or all inside: clamp origin to face
|
||||
return getClosestPointOnFace(glm::vec3(origin), face);
|
||||
}
|
||||
|
||||
glm::vec4 AABox::getPlane(BoxFace face) const {
|
||||
switch (face) {
|
||||
case MIN_X_FACE: return glm::vec4(-1.0f, 0.0f, 0.0f, _corner.x);
|
||||
case MAX_X_FACE: return glm::vec4(1.0f, 0.0f, 0.0f, -_corner.x - _size.x);
|
||||
case MIN_Y_FACE: return glm::vec4(0.0f, -1.0f, 0.0f, _corner.y);
|
||||
case MAX_Y_FACE: return glm::vec4(0.0f, 1.0f, 0.0f, -_corner.y - _size.y);
|
||||
case MIN_Z_FACE: return glm::vec4(0.0f, 0.0f, -1.0f, _corner.z);
|
||||
case MAX_Z_FACE: return glm::vec4(0.0f, 0.0f, 1.0f, -_corner.z - _size.z);
|
||||
}
|
||||
}
|
||||
|
||||
BoxFace AABox::getOppositeFace(BoxFace face) {
|
||||
switch (face) {
|
||||
case MIN_X_FACE: return MAX_X_FACE;
|
||||
case MAX_X_FACE: return MIN_X_FACE;
|
||||
case MIN_Y_FACE: return MAX_Y_FACE;
|
||||
case MAX_Y_FACE: return MIN_Y_FACE;
|
||||
case MIN_Z_FACE: return MAX_Z_FACE;
|
||||
case MAX_Z_FACE: return MIN_Z_FACE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,72 +1,86 @@
|
|||
//
|
||||
// AABox.h - Axis Aligned Boxes
|
||||
// hifi
|
||||
//
|
||||
// Added by Brad Hefta-Gaub on 04/11/13.
|
||||
// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards
|
||||
//
|
||||
// Simple axis aligned box class.
|
||||
//
|
||||
|
||||
#ifndef _AABOX_
|
||||
#define _AABOX_
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
enum BoxFace {
|
||||
MIN_X_FACE,
|
||||
MAX_X_FACE,
|
||||
MIN_Y_FACE,
|
||||
MAX_Y_FACE,
|
||||
MIN_Z_FACE,
|
||||
MAX_Z_FACE
|
||||
};
|
||||
|
||||
const int FACE_COUNT = 6;
|
||||
|
||||
class AABox
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
AABox(const glm::vec3& corner, float size) : _corner(corner), _size(size, size, size) { };
|
||||
AABox(const glm::vec3& corner, float x, float y, float z) : _corner(corner), _size(x, y, z) { };
|
||||
AABox(const glm::vec3& corner, const glm::vec3& size) : _corner(corner), _size(size) { };
|
||||
AABox() : _corner(0,0,0), _size(0,0,0) { }
|
||||
~AABox() { }
|
||||
|
||||
void setBox(const glm::vec3& corner, float x, float y, float z) { setBox(corner,glm::vec3(x,y,z)); };
|
||||
void setBox(const glm::vec3& corner, const glm::vec3& size);
|
||||
|
||||
// for use in frustum computations
|
||||
glm::vec3 getVertexP(const glm::vec3& normal) const;
|
||||
glm::vec3 getVertexN(const glm::vec3& normal) const;
|
||||
|
||||
void scale(float scale);
|
||||
|
||||
const glm::vec3& getCorner() const { return _corner; };
|
||||
const glm::vec3& getSize() const { return _size; };
|
||||
const glm::vec3& getCenter() const { return _center; };
|
||||
|
||||
bool contains(const glm::vec3& point) const;
|
||||
bool expandedContains(const glm::vec3& point, float expansion) const;
|
||||
bool expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const;
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const;
|
||||
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const;
|
||||
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) const;
|
||||
|
||||
private:
|
||||
|
||||
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
|
||||
glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const;
|
||||
glm::vec4 getPlane(BoxFace face) const;
|
||||
|
||||
static BoxFace getOppositeFace(BoxFace face);
|
||||
|
||||
glm::vec3 _corner;
|
||||
glm::vec3 _center;
|
||||
glm::vec3 _size;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
//
|
||||
// AABox.h - Axis Aligned Boxes
|
||||
// hifi
|
||||
//
|
||||
// Added by Brad Hefta-Gaub on 04/11/13.
|
||||
// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards
|
||||
//
|
||||
// Simple axis aligned box class.
|
||||
//
|
||||
|
||||
#ifndef _AABOX_
|
||||
#define _AABOX_
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
enum BoxFace {
|
||||
MIN_X_FACE,
|
||||
MAX_X_FACE,
|
||||
MIN_Y_FACE,
|
||||
MAX_Y_FACE,
|
||||
MIN_Z_FACE,
|
||||
MAX_Z_FACE
|
||||
};
|
||||
|
||||
|
||||
enum BoxVertex {
|
||||
BOTTOM_LEFT_NEAR = 0,
|
||||
BOTTOM_RIGHT_NEAR = 1,
|
||||
TOP_RIGHT_NEAR = 2,
|
||||
TOP_LEFT_NEAR = 3,
|
||||
BOTTOM_LEFT_FAR = 4,
|
||||
BOTTOM_RIGHT_FAR = 5,
|
||||
TOP_RIGHT_FAR = 6,
|
||||
TOP_LEFT_FAR = 7
|
||||
};
|
||||
|
||||
const int FACE_COUNT = 6;
|
||||
|
||||
class AABox
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
AABox(const glm::vec3& corner, float size) : _corner(corner), _size(size, size, size) { };
|
||||
AABox(const glm::vec3& corner, float x, float y, float z) : _corner(corner), _size(x, y, z) { };
|
||||
AABox(const glm::vec3& corner, const glm::vec3& size) : _corner(corner), _size(size) { };
|
||||
AABox() : _corner(0,0,0), _size(0,0,0) { }
|
||||
~AABox() { }
|
||||
|
||||
void setBox(const glm::vec3& corner, float x, float y, float z) { setBox(corner,glm::vec3(x,y,z)); };
|
||||
void setBox(const glm::vec3& corner, const glm::vec3& size);
|
||||
|
||||
// for use in frustum computations
|
||||
glm::vec3 getVertexP(const glm::vec3& normal) const;
|
||||
glm::vec3 getVertexN(const glm::vec3& normal) const;
|
||||
|
||||
void scale(float scale);
|
||||
|
||||
const glm::vec3& getCorner() const { return _corner; };
|
||||
const glm::vec3& getSize() const { return _size; };
|
||||
const glm::vec3& getCenter() const { return _center; };
|
||||
|
||||
glm::vec3 getVertex(BoxVertex vertex) const;
|
||||
|
||||
bool contains(const glm::vec3& point) const;
|
||||
bool expandedContains(const glm::vec3& point, float expansion) const;
|
||||
bool expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const;
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const;
|
||||
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const;
|
||||
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) const;
|
||||
|
||||
private:
|
||||
|
||||
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
|
||||
glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const;
|
||||
glm::vec4 getPlane(BoxFace face) const;
|
||||
|
||||
static BoxFace getOppositeFace(BoxFace face);
|
||||
|
||||
glm::vec3 _corner;
|
||||
glm::vec3 _center;
|
||||
glm::vec3 _size;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
|
193
libraries/voxels/src/CoverageMap.cpp
Normal file
193
libraries/voxels/src/CoverageMap.cpp
Normal file
|
@ -0,0 +1,193 @@
|
|||
//
|
||||
// CoverageMap.cpp -
|
||||
// hifi
|
||||
//
|
||||
// Added by Brad Hefta-Gaub on 06/11/13.
|
||||
//
|
||||
|
||||
#include "CoverageMap.h"
|
||||
#include <SharedUtil.h>
|
||||
#include <string>
|
||||
#include "Log.h"
|
||||
|
||||
int CoverageMap::_mapCount = 0;
|
||||
const BoundingBox CoverageMap::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-2.f,-2.f), glm::vec2(4.f,4.f));
|
||||
|
||||
CoverageMap::CoverageMap(BoundingBox boundingBox, bool isRoot, bool managePolygons) :
|
||||
_isRoot(isRoot), _myBoundingBox(boundingBox), _managePolygons(managePolygons) {
|
||||
_mapCount++;
|
||||
init();
|
||||
//printLog("CoverageMap created... _mapCount=%d\n",_mapCount);
|
||||
};
|
||||
|
||||
CoverageMap::~CoverageMap() {
|
||||
erase();
|
||||
};
|
||||
|
||||
void CoverageMap::erase() {
|
||||
// If we're in charge of managing the polygons, then clean them up first
|
||||
if (_managePolygons) {
|
||||
for (int i = 0; i < _polygonCount; i++) {
|
||||
delete _polygons[i];
|
||||
_polygons[i] = NULL; // do we need to do this?
|
||||
}
|
||||
}
|
||||
|
||||
// Now, clean up our local storage
|
||||
_polygonCount = 0;
|
||||
_polygonArraySize = 0;
|
||||
if (_polygons) {
|
||||
delete[] _polygons;
|
||||
_polygons = NULL;
|
||||
}
|
||||
if (_polygonDistances) {
|
||||
delete[] _polygonDistances;
|
||||
_polygonDistances = NULL;
|
||||
}
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
if (_childMaps[i]) {
|
||||
delete _childMaps[i];
|
||||
_childMaps[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
if (_isRoot) {
|
||||
printLog("CoverageMap last to be deleted...\n");
|
||||
printLog("_mapCount=%d\n",_mapCount);
|
||||
printLog("_maxPolygonsUsed=%d\n",_maxPolygonsUsed);
|
||||
printLog("_totalPolygons=%d\n",_totalPolygons);
|
||||
|
||||
_maxPolygonsUsed = 0;
|
||||
_totalPolygons = 0;
|
||||
_mapCount = 0;
|
||||
}
|
||||
**/
|
||||
|
||||
}
|
||||
|
||||
void CoverageMap::init() {
|
||||
_polygonCount = 0;
|
||||
_polygonArraySize = 0;
|
||||
_polygons = NULL;
|
||||
_polygonDistances = NULL;
|
||||
memset(_childMaps,0,sizeof(_childMaps));
|
||||
}
|
||||
|
||||
// 0 = bottom, right
|
||||
// 1 = bottom, left
|
||||
// 2 = top, right
|
||||
// 3 = top, left
|
||||
BoundingBox CoverageMap::getChildBoundingBox(int childIndex) {
|
||||
const int LEFT_BIT = 1;
|
||||
const int TOP_BIT = 2;
|
||||
// initialize to our corner, and half our size
|
||||
BoundingBox result(_myBoundingBox.corner,_myBoundingBox.size/2.0f);
|
||||
// if our "left" bit is set, then add size.x to the corner
|
||||
if ((childIndex & LEFT_BIT) == LEFT_BIT) {
|
||||
result.corner.x += result.size.x;
|
||||
}
|
||||
// if our "top" bit is set, then add size.y to the corner
|
||||
if ((childIndex & TOP_BIT) == TOP_BIT) {
|
||||
result.corner.y += result.size.y;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void CoverageMap::growPolygonArray() {
|
||||
VoxelProjectedPolygon** newPolygons = new VoxelProjectedPolygon*[_polygonArraySize + DEFAULT_GROW_SIZE];
|
||||
float* newDistances = new float[_polygonArraySize + DEFAULT_GROW_SIZE];
|
||||
|
||||
|
||||
if (_polygons) {
|
||||
memcpy(newPolygons, _polygons, sizeof(VoxelProjectedPolygon*) * _polygonCount);
|
||||
delete[] _polygons;
|
||||
memcpy(newDistances, _polygonDistances, sizeof(float) * _polygonCount);
|
||||
delete[] _polygonDistances;
|
||||
}
|
||||
_polygons = newPolygons;
|
||||
_polygonDistances = newDistances;
|
||||
_polygonArraySize = _polygonArraySize + DEFAULT_GROW_SIZE;
|
||||
//printLog("CoverageMap::growPolygonArray() _polygonArraySize=%d...\n",_polygonArraySize);
|
||||
}
|
||||
|
||||
int CoverageMap::_maxPolygonsUsed = 0;
|
||||
int CoverageMap::_totalPolygons = 0;
|
||||
|
||||
// just handles storage in the array, doesn't test for occlusion or
|
||||
// determining if this is the correct map to store in!
|
||||
void CoverageMap::storeInArray(VoxelProjectedPolygon* polygon) {
|
||||
|
||||
_totalPolygons++;
|
||||
|
||||
if (_polygonArraySize < _polygonCount + 1) {
|
||||
growPolygonArray();
|
||||
}
|
||||
|
||||
// This old code assumes that polygons will always be added in z-buffer order, but that doesn't seem to
|
||||
// be a good assumption. So instead, we will need to sort this by distance. Use a binary search to find the
|
||||
// insertion point in this array, and shift the array accordingly
|
||||
const int IGNORED = NULL;
|
||||
_polygonCount = insertIntoSortedArrays((void*)polygon, polygon->getDistance(), IGNORED,
|
||||
(void**)_polygons, _polygonDistances, IGNORED,
|
||||
_polygonCount, _polygonArraySize);
|
||||
|
||||
if (_polygonCount > _maxPolygonsUsed) {
|
||||
_maxPolygonsUsed = _polygonCount;
|
||||
//printLog("CoverageMap new _maxPolygonsUsed reached=%d\n",_maxPolygonsUsed);
|
||||
//_myBoundingBox.printDebugDetails("map._myBoundingBox");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// possible results = STORED/NOT_STORED, OCCLUDED, DOESNT_FIT
|
||||
CoverageMap::StorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, bool storeIt) {
|
||||
if (_isRoot || _myBoundingBox.contains(polygon->getBoundingBox())) {
|
||||
// check to make sure this polygon isn't occluded by something at this level
|
||||
for (int i = 0; i < _polygonCount; i++) {
|
||||
VoxelProjectedPolygon* polygonAtThisLevel = _polygons[i];
|
||||
// Check to make sure that the polygon in question is "behind" the polygon in the list
|
||||
// otherwise, we don't need to test it's occlusion (although, it means we've potentially
|
||||
// added an item previously that may be occluded??? Is that possible? Maybe not, because two
|
||||
// voxels can't have the exact same outline. So one occludes the other, they can't both occlude
|
||||
// each other.
|
||||
if (polygonAtThisLevel->occludes(*polygon)) {
|
||||
// if the polygonAtThisLevel is actually behind the one we're inserting, then we don't
|
||||
// want to report our inserted one as occluded, but we do want to add our inserted one.
|
||||
if (polygonAtThisLevel->getDistance() >= polygon->getDistance()) {
|
||||
if (storeIt) {
|
||||
storeInArray(polygon);
|
||||
return STORED;
|
||||
} else {
|
||||
return NOT_STORED;
|
||||
}
|
||||
}
|
||||
// this polygon is occluded by a closer polygon, so don't store it, and let the caller know
|
||||
return OCCLUDED;
|
||||
}
|
||||
}
|
||||
// if we made it here, then it means the polygon being stored is not occluded
|
||||
// at this level of the quad tree, so we can continue to insert it into the map.
|
||||
// First we check to see if it fits in any of our sub maps
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
BoundingBox childMapBoundingBox = getChildBoundingBox(i);
|
||||
if (childMapBoundingBox.contains(polygon->getBoundingBox())) {
|
||||
// if no child map exists yet, then create it
|
||||
if (!_childMaps[i]) {
|
||||
_childMaps[i] = new CoverageMap(childMapBoundingBox, NOT_ROOT, _managePolygons);
|
||||
}
|
||||
return _childMaps[i]->checkMap(polygon, storeIt);
|
||||
}
|
||||
}
|
||||
// if we got this far, then the polygon is in our bounding box, but doesn't fit in
|
||||
// any of our child bounding boxes, so we should add it here.
|
||||
if (storeIt) {
|
||||
storeInArray(polygon);
|
||||
return STORED;
|
||||
} else {
|
||||
return NOT_STORED;
|
||||
}
|
||||
}
|
||||
return DOESNT_FIT;
|
||||
}
|
54
libraries/voxels/src/CoverageMap.h
Normal file
54
libraries/voxels/src/CoverageMap.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// CoverageMap.h - 2D CoverageMap Quad tree for storage of VoxelProjectedPolygons
|
||||
// hifi
|
||||
//
|
||||
// Added by Brad Hefta-Gaub on 06/11/13.
|
||||
//
|
||||
|
||||
#ifndef _COVERAGE_MAP_
|
||||
#define _COVERAGE_MAP_
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include "VoxelProjectedPolygon.h"
|
||||
|
||||
class CoverageMap {
|
||||
|
||||
public:
|
||||
static const int NUMBER_OF_CHILDREN = 4;
|
||||
static const bool NOT_ROOT=false;
|
||||
static const bool IS_ROOT=true;
|
||||
static const BoundingBox ROOT_BOUNDING_BOX;
|
||||
|
||||
CoverageMap(BoundingBox boundingBox = ROOT_BOUNDING_BOX, bool isRoot = IS_ROOT, bool managePolygons = true);
|
||||
~CoverageMap();
|
||||
|
||||
typedef enum {STORED, OCCLUDED, DOESNT_FIT, NOT_STORED} StorageResult;
|
||||
StorageResult checkMap(VoxelProjectedPolygon* polygon, bool storeIt = true);
|
||||
|
||||
BoundingBox getChildBoundingBox(int childIndex);
|
||||
|
||||
void erase(); // erase the coverage map
|
||||
|
||||
private:
|
||||
void init();
|
||||
void growPolygonArray();
|
||||
void storeInArray(VoxelProjectedPolygon* polygon);
|
||||
|
||||
bool _isRoot; // is this map the root, if so, it never returns DOESNT_FIT
|
||||
BoundingBox _myBoundingBox;
|
||||
bool _managePolygons; // will the coverage map delete the polygons on destruct
|
||||
int _polygonCount; // how many polygons at this level
|
||||
int _polygonArraySize; // how much room is there to store polygons at this level
|
||||
VoxelProjectedPolygon** _polygons;
|
||||
float* _polygonDistances;
|
||||
CoverageMap* _childMaps[NUMBER_OF_CHILDREN];
|
||||
|
||||
static const int DEFAULT_GROW_SIZE = 100;
|
||||
static int _mapCount;
|
||||
static int _maxPolygonsUsed;
|
||||
static int _totalPolygons;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // _COVERAGE_MAP_
|
|
@ -115,3 +115,28 @@ glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3&
|
|||
return currentDirection * glm::max(directionalComponent, currentLength) +
|
||||
newPenetration - (currentDirection * directionalComponent);
|
||||
}
|
||||
|
||||
// Do line segments (r1p1.x, r1p1.y)--(r1p2.x, r1p2.y) and (r2p1.x, r2p1.y)--(r2p2.x, r2p2.y) intersect?
|
||||
bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm::vec2 r2p2) {
|
||||
int d1 = computeDirection(r2p1.x, r2p1.y, r2p2.x, r2p2.y, r1p1.x, r1p1.y);
|
||||
int d2 = computeDirection(r2p1.x, r2p1.y, r2p2.x, r2p2.y, r1p2.x, r1p2.y);
|
||||
int d3 = computeDirection(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p1.x, r2p1.y);
|
||||
int d4 = computeDirection(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p2.x, r2p2.y);
|
||||
return (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) &&
|
||||
((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) ||
|
||||
(d1 == 0 && isOnSegment(r2p1.x, r2p1.y, r2p2.x, r2p2.y, r1p1.x, r1p1.y)) ||
|
||||
(d2 == 0 && isOnSegment(r2p1.x, r2p1.y, r2p2.x, r2p2.y, r1p2.x, r1p2.y)) ||
|
||||
(d3 == 0 && isOnSegment(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p1.x, r2p1.y)) ||
|
||||
(d4 == 0 && isOnSegment(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p2.x, r2p2.y));
|
||||
}
|
||||
|
||||
bool isOnSegment(float xi, float yi, float xj, float yj, float xk, float yk) {
|
||||
return (xi <= xk || xj <= xk) && (xk <= xi || xk <= xj) &&
|
||||
(yi <= yk || yj <= yk) && (yk <= yi || yk <= yj);
|
||||
}
|
||||
|
||||
int computeDirection(float xi, float yi, float xj, float yj, float xk, float yk) {
|
||||
float a = (xk - xi) * (yj - yi);
|
||||
float b = (xj - xi) * (yk - yi);
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
}
|
||||
|
|
|
@ -39,4 +39,8 @@ bool findCapsulePlanePenetration(const glm::vec3& penetratorStart, const glm::ve
|
|||
|
||||
glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3& newPenetration);
|
||||
|
||||
bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm::vec2 r2p2);
|
||||
bool isOnSegment(float xi, float yi, float xj, float yj, float xk, float yk);
|
||||
int computeDirection(float xi, float yi, float xj, float yj, float xk, float yk);
|
||||
|
||||
#endif /* defined(__interface__GeometryUtil__) */
|
||||
|
|
|
@ -425,3 +425,116 @@ void ViewFrustum::printDebugDetails() const {
|
|||
_eyeOffsetOrientation.w );
|
||||
}
|
||||
|
||||
|
||||
glm::vec2 ViewFrustum::projectPoint(glm::vec3 point, bool& pointInView) const {
|
||||
|
||||
// Projection matrix : Field of View, ratio, display range : near to far
|
||||
glm::mat4 projection = glm::perspective(_fieldOfView, _aspectRatio, _nearClip, _farClip);
|
||||
glm::vec3 lookAt = _position + _direction;
|
||||
glm::mat4 view = glm::lookAt(_position, lookAt, _up);
|
||||
// Our ModelViewProjection : multiplication of our 3 matrices (note: model is identity, so we can drop it)
|
||||
glm::mat4 VP = projection * view; // Remember, matrix multiplication is the other way around
|
||||
|
||||
glm::vec4 pointVec4 = glm::vec4(point,1);
|
||||
glm::vec4 projectedPointVec4 = VP * pointVec4;
|
||||
pointInView = (projectedPointVec4.w > 0); // math! If the w result is negative then the point is behind the viewer
|
||||
|
||||
// what happens with w is 0???
|
||||
float x = projectedPointVec4.x / projectedPointVec4.w;
|
||||
float y = projectedPointVec4.y / projectedPointVec4.w;
|
||||
glm::vec2 projectedPoint(x,y);
|
||||
|
||||
// if the point is out of view we also need to flip the signs of x and y
|
||||
if (!pointInView) {
|
||||
projectedPoint.x = -x;
|
||||
projectedPoint.y = -y;
|
||||
}
|
||||
|
||||
return projectedPoint;
|
||||
}
|
||||
|
||||
|
||||
const int MAX_POSSIBLE_COMBINATIONS = 43;
|
||||
|
||||
const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_SHADOW_VERTEX_COUNT+1] = {
|
||||
// Number of vertices in shadow polygon for the visible faces, then a list of the index of each vertice from the AABox
|
||||
{0}, // inside
|
||||
{4, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR}, // right
|
||||
{4, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // left
|
||||
{0}, // n/a
|
||||
{4, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR}, // bottom
|
||||
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR},//bottom, right
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR},//bottom, left
|
||||
{0}, // n/a
|
||||
{4, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // top
|
||||
{6, TOP_RIGHT_NEAR, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // top, right
|
||||
{6, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR}, // top, left
|
||||
{0}, // n/a
|
||||
{0}, // n/a
|
||||
{0}, // n/a
|
||||
{0}, // n/a
|
||||
{0}, // n/a
|
||||
{4, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front or near
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front, right
|
||||
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR}, // front, left
|
||||
{0}, // n/a
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR}, // front,bottom
|
||||
{6, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR}, //front,bottom,right
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR}, //front,bottom,left
|
||||
{0}, // n/a
|
||||
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front, top
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front, top, right
|
||||
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR}, // front, top, left
|
||||
{0}, // n/a
|
||||
{0}, // n/a
|
||||
{0}, // n/a
|
||||
{0}, // n/a
|
||||
{0}, // n/a
|
||||
{4, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR}, // back
|
||||
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR}, // back, right
|
||||
{6, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR}, // back, left
|
||||
{0}, // n/a
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR}, // back, bottom
|
||||
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR},//back, bottom, right
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR},//back, bottom, left
|
||||
{0}, // n/a
|
||||
{6, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR}, // back, top
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR}, // back, top, right
|
||||
{6, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR}, // back, top, left
|
||||
};
|
||||
|
||||
VoxelProjectedPolygon ViewFrustum::getProjectedShadow(const AABox& box) const {
|
||||
glm::vec3 bottomNearRight = box.getCorner();
|
||||
glm::vec3 topFarLeft = box.getCorner() + box.getSize();
|
||||
int lookUp = ((_position.x < bottomNearRight.x) ) // 1 = right | compute 6-bit
|
||||
+ ((_position.x > topFarLeft.x ) << 1) // 2 = left | code to
|
||||
+ ((_position.y < bottomNearRight.y) << 2) // 4 = bottom | classify camera
|
||||
+ ((_position.y > topFarLeft.y ) << 3) // 8 = top | with respect to
|
||||
+ ((_position.z < bottomNearRight.z) << 4) // 16 = front/near | the 6 defining
|
||||
+ ((_position.z > topFarLeft.z ) << 5); // 32 = back/far | planes
|
||||
|
||||
int vertexCount = hullVertexLookup[lookUp][0]; //look up number of vertices
|
||||
|
||||
VoxelProjectedPolygon shadow(vertexCount);
|
||||
|
||||
bool pointInView = true;
|
||||
bool allPointsInView = false; // assume the best, but wait till we know we have a vertex
|
||||
bool anyPointsInView = false; // assume the worst!
|
||||
if (vertexCount) {
|
||||
allPointsInView = true; // assume the best!
|
||||
for(int i = 0; i < vertexCount; i++) {
|
||||
int vertexNum = hullVertexLookup[lookUp][i+1];
|
||||
glm::vec3 point = box.getVertex((BoxVertex)vertexNum);
|
||||
glm::vec2 projectedPoint = projectPoint(point, pointInView);
|
||||
allPointsInView = allPointsInView && pointInView;
|
||||
anyPointsInView = anyPointsInView || pointInView;
|
||||
shadow.setVertex(i, projectedPoint);
|
||||
}
|
||||
}
|
||||
// set the distance from our camera position, to the closest vertex
|
||||
float distance = glm::distance(getPosition(), box.getCenter());
|
||||
shadow.setDistance(distance);
|
||||
shadow.setAnyInView(anyPointsInView);
|
||||
shadow.setAllInView(allPointsInView);
|
||||
return shadow;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <glm/gtc/quaternion.hpp>
|
||||
#include "Plane.h"
|
||||
#include "AABox.h"
|
||||
#include "VoxelProjectedPolygon.h"
|
||||
|
||||
const float DEFAULT_KEYHOLE_RADIUS = 2.0f;
|
||||
|
||||
|
@ -87,6 +88,9 @@ public:
|
|||
|
||||
void printDebugDetails() const;
|
||||
|
||||
glm::vec2 projectPoint(glm::vec3 point, bool& pointInView) const;
|
||||
VoxelProjectedPolygon getProjectedShadow(const AABox& box) const;
|
||||
|
||||
private:
|
||||
|
||||
// Used for keyhole calculations
|
||||
|
|
126
libraries/voxels/src/VoxelProjectedPolygon.cpp
Normal file
126
libraries/voxels/src/VoxelProjectedPolygon.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
//
|
||||
// VoxelProjectedPolygon.cpp - The projected shadow (on the 2D view plane) for a voxel
|
||||
// hifi
|
||||
//
|
||||
// Added by Brad Hefta-Gaub on 06/11/13.
|
||||
//
|
||||
|
||||
#include "VoxelProjectedPolygon.h"
|
||||
#include "GeometryUtil.h"
|
||||
#include "Log.h"
|
||||
|
||||
|
||||
bool BoundingBox::contains(const BoundingBox& box) const {
|
||||
return (
|
||||
(box.corner.x >= corner.x) &&
|
||||
(box.corner.y >= corner.y) &&
|
||||
(box.corner.x + box.size.x <= corner.x + size.x) &&
|
||||
(box.corner.y + box.size.y <= corner.y + size.y)
|
||||
);
|
||||
};
|
||||
|
||||
void BoundingBox::printDebugDetails(const char* label) const {
|
||||
if (label) {
|
||||
printLog(label);
|
||||
} else {
|
||||
printLog("BoundingBox");
|
||||
}
|
||||
printLog("\n corner=%f,%f size=%f,%f\n", corner.x, corner.y, size.x, size.y);
|
||||
}
|
||||
|
||||
|
||||
void VoxelProjectedPolygon::setVertex(int vertex, const glm::vec2& point) {
|
||||
_vertices[vertex] = point;
|
||||
|
||||
// keep track of our bounding box
|
||||
if (point.x > _maxX) {
|
||||
_maxX = point.x;
|
||||
}
|
||||
if (point.y > _maxY) {
|
||||
_maxY = point.y;
|
||||
}
|
||||
if (point.x < _minX) {
|
||||
_minX = point.x;
|
||||
}
|
||||
if (point.y < _minY) {
|
||||
_minY = point.y;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
bool VoxelProjectedPolygon::occludes(const VoxelProjectedPolygon& occludee) const {
|
||||
|
||||
// if we are completely out of view, then we definitely don't occlude!
|
||||
// if the occludee is completely out of view, then we also don't occlude it
|
||||
//
|
||||
// this is true, but unfortunately, we're not quite handling projects in the
|
||||
// case when SOME points are in view and others are not. So, we will not consider
|
||||
// occlusion for any shadows that are partially in view.
|
||||
if (!getAllInView() || !occludee.getAllInView() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// first check the bounding boxes, the occludee must be fully within the boounding box of this shadow
|
||||
if ((occludee.getMaxX() > getMaxX()) ||
|
||||
(occludee.getMaxY() > getMaxY()) ||
|
||||
(occludee.getMinX() < getMinX()) ||
|
||||
(occludee.getMinY() < getMinY())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we got this far, then check each vertex of the occludee, if all those points
|
||||
// are inside our polygon, then the tested occludee is fully occluded
|
||||
for(int i = 0; i < occludee.getVertexCount(); i++) {
|
||||
if (!pointInside(occludee.getVertex(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// if we got this far, then indeed the occludee is fully occluded by us
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VoxelProjectedPolygon::pointInside(const glm::vec2& point) const {
|
||||
// first check the bounding boxes, the point must be fully within the boounding box of this shadow
|
||||
if ((point.x > getMaxX()) ||
|
||||
(point.y > getMaxY()) ||
|
||||
(point.x < getMinX()) ||
|
||||
(point.y < getMinY())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float e = (getMaxX() - getMinX()) / 100.0f; // some epsilon
|
||||
|
||||
// We need to have one ray that goes from a known outside position to the point in question. We'll pick a
|
||||
// start point just outside of our min X
|
||||
glm::vec2 r1p1(getMinX() - e, point.y);
|
||||
glm::vec2 r1p2(point);
|
||||
|
||||
glm::vec2 r2p1(getVertex(getVertexCount()-1)); // start with last vertex to first vertex
|
||||
glm::vec2 r2p2;
|
||||
|
||||
// Test the ray against all sides
|
||||
int intersections = 0;
|
||||
for (int i = 0; i < getVertexCount(); i++) {
|
||||
r2p2 = getVertex(i);
|
||||
if (doLineSegmentsIntersect(r1p1, r1p2, r2p1, r2p2)) {
|
||||
intersections++;
|
||||
}
|
||||
r2p1 = r2p2; // set up for next side
|
||||
}
|
||||
|
||||
// If odd number of intersections, we're inside
|
||||
return ((intersections & 1) == 1);
|
||||
}
|
||||
|
||||
void VoxelProjectedPolygon::printDebugDetails() const {
|
||||
printf("VoxelProjectedPolygon...");
|
||||
printf(" minX=%f maxX=%f minY=%f maxY=%f\n", getMinX(), getMaxX(), getMinY(), getMaxY());
|
||||
printf(" vertex count=%d distance=%f\n", getVertexCount(), getDistance());
|
||||
for (int i = 0; i < getVertexCount(); i++) {
|
||||
glm::vec2 point = getVertex(i);
|
||||
printf(" vertex[%d] = %f, %f \n", i, point.x, point.y);
|
||||
}
|
||||
}
|
||||
|
||||
|
78
libraries/voxels/src/VoxelProjectedPolygon.h
Normal file
78
libraries/voxels/src/VoxelProjectedPolygon.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// VoxelProjectedPolygon.h - The projected shadow (on the 2D view plane) for a voxel
|
||||
// hifi
|
||||
//
|
||||
// Added by Brad Hefta-Gaub on 06/11/13.
|
||||
//
|
||||
|
||||
#ifndef _VOXEL_PROJECTED_SHADOW_
|
||||
#define _VOXEL_PROJECTED_SHADOW_
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
const int MAX_SHADOW_VERTEX_COUNT = 6;
|
||||
|
||||
typedef glm::vec2 ShadowVertices[MAX_SHADOW_VERTEX_COUNT];
|
||||
|
||||
class BoundingBox {
|
||||
public:
|
||||
BoundingBox(glm::vec2 corner, glm::vec2 size) : corner(corner), size(size) {};
|
||||
glm::vec2 corner;
|
||||
glm::vec2 size;
|
||||
bool contains(const BoundingBox& box) const;
|
||||
|
||||
void printDebugDetails(const char* label=NULL) const;
|
||||
};
|
||||
|
||||
class VoxelProjectedPolygon {
|
||||
|
||||
public:
|
||||
VoxelProjectedPolygon(int vertexCount = 0) :
|
||||
_vertexCount(vertexCount),
|
||||
_maxX(-FLT_MAX), _maxY(-FLT_MAX), _minX(FLT_MAX), _minY(FLT_MAX),
|
||||
_distance(0)
|
||||
{ };
|
||||
|
||||
~VoxelProjectedPolygon() { };
|
||||
const ShadowVertices& getVerices() const { return _vertices; };
|
||||
const glm::vec2& getVertex(int i) const { return _vertices[i]; };
|
||||
void setVertex(int vertex, const glm::vec2& point);
|
||||
int getVertexCount() const { return _vertexCount; };
|
||||
void setVertexCount(int vertexCount) { _vertexCount = vertexCount; };
|
||||
|
||||
float getDistance() const { return _distance; }
|
||||
void setDistance(float distance) { _distance = distance; }
|
||||
|
||||
bool getAnyInView() const { return _anyInView; };
|
||||
void setAnyInView(bool anyInView) { _anyInView = anyInView; };
|
||||
bool getAllInView() const { return _allInView; };
|
||||
void setAllInView(bool allInView) { _allInView = allInView; };
|
||||
|
||||
bool occludes(const VoxelProjectedPolygon& occludee) const;
|
||||
bool pointInside(const glm::vec2& point) const;
|
||||
|
||||
float getMaxX() const { return _maxX; }
|
||||
float getMaxY() const { return _maxY; }
|
||||
float getMinX() const { return _minX; }
|
||||
float getMinY() const { return _minY; }
|
||||
|
||||
BoundingBox getBoundingBox() const {
|
||||
return BoundingBox(glm::vec2(_minX,_minY), glm::vec2(_maxX - _minX, _maxY - _minY));
|
||||
};
|
||||
|
||||
void printDebugDetails() const;
|
||||
|
||||
private:
|
||||
int _vertexCount;
|
||||
ShadowVertices _vertices;
|
||||
float _maxX;
|
||||
float _maxY;
|
||||
float _minX;
|
||||
float _minY;
|
||||
float _distance;
|
||||
bool _anyInView; // if any points are in view
|
||||
bool _allInView; // if all points are in view
|
||||
};
|
||||
|
||||
|
||||
#endif // _VOXEL_PROJECTED_SHADOW_
|
|
@ -22,6 +22,7 @@
|
|||
#include "ViewFrustum.h"
|
||||
#include <fstream> // to load voxels from file
|
||||
#include "VoxelConstants.h"
|
||||
#include "CoverageMap.h"
|
||||
|
||||
#include <glm/gtc/noise.hpp>
|
||||
|
||||
|
@ -68,7 +69,51 @@ void VoxelTree::recurseNodeWithOperation(VoxelNode* node,RecurseVoxelTreeOperati
|
|||
}
|
||||
}
|
||||
|
||||
VoxelNode * VoxelTree::nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode) const {
|
||||
// Recurses voxel tree calling the RecurseVoxelTreeOperation function for each node.
|
||||
// stops recursion if operation function returns false.
|
||||
void VoxelTree::recurseTreeWithOperationDistanceSorted(RecurseVoxelTreeOperation operation,
|
||||
const glm::vec3& point, void* extraData) {
|
||||
recurseNodeWithOperationDistanceSorted(rootNode, operation, point, extraData);
|
||||
}
|
||||
|
||||
// Recurses voxel node with an operation function
|
||||
void VoxelTree::recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseVoxelTreeOperation operation,
|
||||
const glm::vec3& point, void* extraData) {
|
||||
if (operation(node, extraData)) {
|
||||
// determine the distance sorted order of our children
|
||||
|
||||
VoxelNode* sortedChildren[NUMBER_OF_CHILDREN];
|
||||
float distancesToChildren[NUMBER_OF_CHILDREN];
|
||||
int indexOfChildren[NUMBER_OF_CHILDREN]; // not really needed
|
||||
int currentCount = 0;
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
VoxelNode* childNode = node->getChildAtIndex(i);
|
||||
if (childNode) {
|
||||
// chance to optimize, doesn't need to be actual distance!! Could be distance squared
|
||||
float distanceSquared = childNode->distanceSquareToPoint(point);
|
||||
//printLog("recurseNodeWithOperationDistanceSorted() CHECKING child[%d] point=%f,%f center=%f,%f distance=%f...\n", i, point.x, point.y, center.x, center.y, distance);
|
||||
//childNode->printDebugDetails("");
|
||||
currentCount = insertIntoSortedArrays((void*)childNode, distanceSquared, i,
|
||||
(void**)&sortedChildren, (float*)&distancesToChildren,
|
||||
(int*)&indexOfChildren, currentCount, NUMBER_OF_CHILDREN);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < currentCount; i++) {
|
||||
VoxelNode* childNode = sortedChildren[i];
|
||||
if (childNode) {
|
||||
//printLog("recurseNodeWithOperationDistanceSorted() PROCESSING child[%d] distance=%f...\n", i, distancesToChildren[i]);
|
||||
//childNode->printDebugDetails("");
|
||||
recurseNodeWithOperationDistanceSorted(childNode, operation, point, extraData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VoxelNode* VoxelTree::nodeForOctalCode(VoxelNode* ancestorNode,
|
||||
unsigned char* needleCode, VoxelNode** parentOfFoundNode) const {
|
||||
// find the appropriate branch index based on this ancestorNode
|
||||
if (*needleCode > 0) {
|
||||
int branchForNeedle = branchIndexWithDescendant(ancestorNode->getOctalCode(), needleCode);
|
||||
|
@ -1072,6 +1117,39 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
|||
if (!node->isInView(*params.viewFrustum)) {
|
||||
return bytesAtThisLevel;
|
||||
}
|
||||
|
||||
|
||||
// If the user also asked for occlusion culling, check if this node is occluded, but only if it's not a leaf.
|
||||
// leaf occlusion is handled down below when we check child nodes
|
||||
if (params.wantOcclusionCulling && !node->isLeaf()) {
|
||||
//node->printDebugDetails("upper section, params.wantOcclusionCulling... node=");
|
||||
AABox voxelBox = node->getAABox();
|
||||
voxelBox.scale(TREE_SCALE);
|
||||
VoxelProjectedPolygon* voxelShadow = new VoxelProjectedPolygon(params.viewFrustum->getProjectedShadow(voxelBox));
|
||||
|
||||
// In order to check occlusion culling, the shadow has to be "all in view" otherwise, we will ignore occlusion
|
||||
// culling and proceed as normal
|
||||
if (voxelShadow->getAllInView()) {
|
||||
//node->printDebugDetails("upper section, voxelShadow->getAllInView() node=");
|
||||
|
||||
CoverageMap::StorageResult result = params.map->checkMap(voxelShadow, false);
|
||||
delete voxelShadow; // cleanup
|
||||
if (result == CoverageMap::OCCLUDED) {
|
||||
//node->printDebugDetails("upper section, non-Leaf is occluded!! node=");
|
||||
//args->nonLeavesOccluded++;
|
||||
|
||||
//args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1);
|
||||
//args->totalVoxels += (subArgs.voxelsTouched - 1);
|
||||
|
||||
return bytesAtThisLevel;
|
||||
}
|
||||
} else {
|
||||
//node->printDebugDetails("upper section, shadow Not in view node=");
|
||||
// If this shadow wasn't "all in view" then we ignored it for occlusion culling, but
|
||||
// we do need to clean up memory and proceed as normal...
|
||||
delete voxelShadow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool keepDiggingDeeper = true; // Assuming we're in view we have a great work ethic, we're always ready for more!
|
||||
|
@ -1080,37 +1158,68 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
|||
// is 1 byte for child colors + 3*NUMBER_OF_CHILDREN bytes for the actual colors + 1 byte for child trees. There could be sub trees
|
||||
// below this point, which might take many more bytes, but that's ok, because we can always mark our subtrees as
|
||||
// not existing and stop the packet at this point, then start up with a new packet for the remaining sub trees.
|
||||
const int CHILD_COLOR_MASK_BYTES = 1;
|
||||
unsigned char childrenExistInTreeBits = 0;
|
||||
unsigned char childrenExistInPacketBits = 0;
|
||||
unsigned char childrenColoredBits = 0;
|
||||
|
||||
const int CHILD_COLOR_MASK_BYTES = sizeof(childrenColoredBits);
|
||||
const int BYTES_PER_COLOR = 3;
|
||||
const int CHILD_TREE_EXISTS_BYTES = 1;
|
||||
const int CHILD_TREE_EXISTS_BYTES = sizeof(childrenExistInTreeBits) + sizeof(childrenExistInPacketBits);
|
||||
const int MAX_LEVEL_BYTES = CHILD_COLOR_MASK_BYTES + NUMBER_OF_CHILDREN * BYTES_PER_COLOR + CHILD_TREE_EXISTS_BYTES;
|
||||
|
||||
// Make our local buffer large enough to handle writing at this level in case we need to.
|
||||
unsigned char thisLevelBuffer[MAX_LEVEL_BYTES];
|
||||
unsigned char* writeToThisLevelBuffer = &thisLevelBuffer[0];
|
||||
|
||||
unsigned char childrenExistInTreeBits = 0;
|
||||
unsigned char childrenExistInPacketBits = 0;
|
||||
unsigned char childrenColoredBits = 0;
|
||||
int inViewCount = 0;
|
||||
int inViewNotLeafCount = 0;
|
||||
int inViewWithColorCount = 0;
|
||||
|
||||
// for each child node, check to see if they exist, are colored, and in view, and if so
|
||||
// add them to our distance ordered array of children
|
||||
VoxelNode* sortedChildren[NUMBER_OF_CHILDREN];
|
||||
float distancesToChildren[NUMBER_OF_CHILDREN];
|
||||
int indexOfChildren[NUMBER_OF_CHILDREN]; // not really needed
|
||||
int currentCount = 0;
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
VoxelNode* childNode = node->getChildAtIndex(i);
|
||||
|
||||
|
||||
// if the caller wants to include childExistsBits, then include them even if not in view
|
||||
if (params.includeExistsBits && childNode) {
|
||||
childrenExistInTreeBits += (1 << (7 - i));
|
||||
}
|
||||
|
||||
|
||||
if (params.wantOcclusionCulling) {
|
||||
if (childNode) {
|
||||
// chance to optimize, doesn't need to be actual distance!! Could be distance squared
|
||||
//float distanceSquared = childNode->distanceSquareToPoint(point);
|
||||
//printLog("recurseNodeWithOperationDistanceSorted() CHECKING child[%d] point=%f,%f center=%f,%f distance=%f...\n", i, point.x, point.y, center.x, center.y, distance);
|
||||
//childNode->printDebugDetails("");
|
||||
|
||||
float distance = params.viewFrustum ? childNode->distanceToCamera(*params.viewFrustum) : 0;
|
||||
|
||||
currentCount = insertIntoSortedArrays((void*)childNode, distance, i,
|
||||
(void**)&sortedChildren, (float*)&distancesToChildren,
|
||||
(int*)&indexOfChildren, currentCount, NUMBER_OF_CHILDREN);
|
||||
}
|
||||
} else {
|
||||
sortedChildren[i] = childNode;
|
||||
indexOfChildren[i] = i;
|
||||
distancesToChildren[i] = 0.0f;
|
||||
currentCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// for each child node in Distance sorted order..., check to see if they exist, are colored, and in view, and if so
|
||||
// add them to our distance ordered array of children
|
||||
for (int i = 0; i < currentCount; i++) {
|
||||
VoxelNode* childNode = sortedChildren[i];
|
||||
int originalIndex = indexOfChildren[i];
|
||||
|
||||
bool childIsInView = (childNode && (!params.viewFrustum || childNode->isInView(*params.viewFrustum)));
|
||||
|
||||
if (childIsInView) {
|
||||
// Before we determine consider this further, let's see if it's in our LOD scope...
|
||||
float distance = params.viewFrustum ? childNode->distanceToCamera(*params.viewFrustum) : 0;
|
||||
float distance = distancesToChildren[i]; // params.viewFrustum ? childNode->distanceToCamera(*params.viewFrustum) : 0;
|
||||
float boundaryDistance = params.viewFrustum ? boundaryDistanceForRenderLevel(*childNode->getOctalCode() + 1) : 1;
|
||||
|
||||
if (distance < boundaryDistance) {
|
||||
|
@ -1120,16 +1229,48 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
|||
// we don't care about recursing deeper on them, and we don't consider their
|
||||
// subtree to exist
|
||||
if (!(childNode && childNode->isLeaf())) {
|
||||
childrenExistInPacketBits += (1 << (7 - i));
|
||||
childrenExistInPacketBits += (1 << (7 - originalIndex));
|
||||
inViewNotLeafCount++;
|
||||
}
|
||||
|
||||
bool childIsOccluded = false; // assume it's not occluded
|
||||
|
||||
// If the user also asked for occlusion culling, check if this node is occluded
|
||||
if (params.wantOcclusionCulling && childNode->isLeaf()) {
|
||||
// Don't check occlusion here, just add them to our distance ordered array...
|
||||
|
||||
AABox voxelBox = childNode->getAABox();
|
||||
voxelBox.scale(TREE_SCALE);
|
||||
VoxelProjectedPolygon* voxelShadow = new VoxelProjectedPolygon(params.viewFrustum->getProjectedShadow(voxelBox));
|
||||
|
||||
// In order to check occlusion culling, the shadow has to be "all in view" otherwise, we will ignore occlusion
|
||||
// culling and proceed as normal
|
||||
if (voxelShadow->getAllInView()) {
|
||||
CoverageMap::StorageResult result = params.map->checkMap(voxelShadow, true);
|
||||
|
||||
// In all cases where the shadow wasn't stored, we need to free our own memory.
|
||||
// In the case where it is stored, the CoverageMap will free memory for us later.
|
||||
if (result != CoverageMap::STORED) {
|
||||
delete voxelShadow;
|
||||
}
|
||||
|
||||
// If while attempting to add this voxel's shadow, we determined it was occluded, then
|
||||
// we don't need to process it further and we can exit early.
|
||||
if (result == CoverageMap::OCCLUDED) {
|
||||
childIsOccluded = true;
|
||||
}
|
||||
} else {
|
||||
delete voxelShadow;
|
||||
}
|
||||
} // wants occlusion culling & isLeaf()
|
||||
|
||||
|
||||
bool childWasInView = (childNode && params.deltaViewFrustum &&
|
||||
(params.lastViewFrustum && ViewFrustum::INSIDE == childNode->inFrustum(*params.lastViewFrustum)));
|
||||
|
||||
// track children with actual color, only if the child wasn't previously in view!
|
||||
if (childNode && childNode->isColored() && !childWasInView) {
|
||||
childrenColoredBits += (1 << (7 - i));
|
||||
if (childNode && childNode->isColored() && !childWasInView && !childIsOccluded) {
|
||||
childrenColoredBits += (1 << (7 - originalIndex));
|
||||
inViewWithColorCount++;
|
||||
}
|
||||
}
|
||||
|
@ -1189,15 +1330,35 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
|||
//
|
||||
// we know the last thing we wrote to the outputBuffer was our childrenExistInPacketBits. Let's remember where that was!
|
||||
unsigned char* childExistsPlaceHolder = outputBuffer-sizeof(childrenExistInPacketBits);
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
|
||||
if (oneAtBit(childrenExistInPacketBits, i)) {
|
||||
VoxelNode* childNode = node->getChildAtIndex(i);
|
||||
|
||||
// we are also going to recurse these child trees in "distance" sorted order, but we need to pack them in the
|
||||
// final packet in standard order. So what we're going to do is keep track of how big each subtree was in bytes,
|
||||
// and then later reshuffle these sections of our output buffer back into normal order. This allows us to make
|
||||
// a single recursive pass in distance sorted order, but retain standard order in our encoded packet
|
||||
int recursiveSliceSizes[NUMBER_OF_CHILDREN];
|
||||
unsigned char* recursiveSliceStarts[NUMBER_OF_CHILDREN];
|
||||
unsigned char* firstRecursiveSlice = outputBuffer;
|
||||
int allSlicesSize = 0;
|
||||
|
||||
// for each child node in Distance sorted order..., check to see if they exist, are colored, and in view, and if so
|
||||
// add them to our distance ordered array of children
|
||||
for (int indexByDistance = 0; indexByDistance < currentCount; indexByDistance++) {
|
||||
VoxelNode* childNode = sortedChildren[indexByDistance];
|
||||
int originalIndex = indexOfChildren[indexByDistance];
|
||||
|
||||
if (oneAtBit(childrenExistInPacketBits, originalIndex)) {
|
||||
|
||||
int thisLevel = currentEncodeLevel;
|
||||
|
||||
// remember this for reshuffling
|
||||
recursiveSliceStarts[originalIndex] = outputBuffer;
|
||||
|
||||
int childTreeBytesOut = encodeTreeBitstreamRecursion(childNode, outputBuffer, availableBytes, bag,
|
||||
params, thisLevel);
|
||||
|
||||
// remember this for reshuffling
|
||||
recursiveSliceSizes[originalIndex] = childTreeBytesOut;
|
||||
allSlicesSize += childTreeBytesOut;
|
||||
|
||||
// if the child wrote 0 bytes, it means that nothing below exists or was in view, or we ran out of space,
|
||||
// basically, the children below don't contain any info.
|
||||
|
@ -1227,15 +1388,40 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
|||
// then we want to remove their bit from the childExistsPlaceHolder bitmask
|
||||
if (childTreeBytesOut == 0) {
|
||||
// remove this child's bit...
|
||||
childrenExistInPacketBits -= (1 << (7 - i));
|
||||
childrenExistInPacketBits -= (1 << (7 - originalIndex));
|
||||
// repair the child exists mask
|
||||
*childExistsPlaceHolder = childrenExistInPacketBits;
|
||||
// Note: no need to move the pointer, cause we already stored this
|
||||
} // end if (childTreeBytesOut == 0)
|
||||
} // end if (oneAtBit(childrenExistInPacketBits, i))
|
||||
} // end if (oneAtBit(childrenExistInPacketBits, originalIndex))
|
||||
} // end for
|
||||
} // end keepDiggingDeeper
|
||||
|
||||
// reshuffle here...
|
||||
if (params.wantOcclusionCulling) {
|
||||
unsigned char tempReshuffleBuffer[MAX_VOXEL_PACKET_SIZE];
|
||||
|
||||
unsigned char* tempBufferTo = &tempReshuffleBuffer[0]; // this is our temporary destination
|
||||
|
||||
// iterate through our childrenExistInPacketBits, these will be the sections of the packet that we copied subTree
|
||||
// details into. Unfortunately, they're in distance sorted order, not original index order. we need to put them
|
||||
// back into original distance order
|
||||
for (int originalIndex = 0; originalIndex < NUMBER_OF_CHILDREN; originalIndex++) {
|
||||
if (oneAtBit(childrenExistInPacketBits, originalIndex)) {
|
||||
int thisSliceSize = recursiveSliceSizes[originalIndex];
|
||||
unsigned char* thisSliceStarts = recursiveSliceStarts[originalIndex];
|
||||
|
||||
memcpy(tempBufferTo, thisSliceStarts, thisSliceSize);
|
||||
tempBufferTo += thisSliceSize;
|
||||
}
|
||||
}
|
||||
|
||||
// now that all slices are back in the correct order, copy them to the correct output buffer
|
||||
memcpy(firstRecursiveSlice, &tempReshuffleBuffer[0], allSlicesSize);
|
||||
}
|
||||
|
||||
|
||||
} // end keepDiggingDeeper
|
||||
|
||||
return bytesAtThisLevel;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,20 +13,25 @@
|
|||
#include "ViewFrustum.h"
|
||||
#include "VoxelNode.h"
|
||||
#include "VoxelNodeBag.h"
|
||||
#include "CoverageMap.h"
|
||||
|
||||
// Callback function, for recuseTreeWithOperation
|
||||
typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, void* extraData);
|
||||
typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
|
||||
|
||||
#define NO_EXISTS_BITS false
|
||||
#define WANT_EXISTS_BITS true
|
||||
#define NO_COLOR false
|
||||
#define WANT_COLOR true
|
||||
#define IGNORE_VIEW_FRUSTUM NULL
|
||||
#define JUST_STAGE_DELETION true
|
||||
#define ACTUALLY_DELETE false
|
||||
#define COLLAPSE_EMPTY_TREE true
|
||||
#define DONT_COLLAPSE false
|
||||
#define NO_EXISTS_BITS false
|
||||
#define WANT_EXISTS_BITS true
|
||||
#define NO_COLOR false
|
||||
#define WANT_COLOR true
|
||||
#define IGNORE_VIEW_FRUSTUM NULL
|
||||
#define JUST_STAGE_DELETION true
|
||||
#define ACTUALLY_DELETE false
|
||||
#define COLLAPSE_EMPTY_TREE true
|
||||
#define DONT_COLLAPSE false
|
||||
#define NO_OCCLUSION_CULLING false
|
||||
#define WANT_OCCLUSION_CULLING true
|
||||
#define IGNORE_COVERAGE_MAP NULL
|
||||
#define DONT_CHOP 0
|
||||
|
||||
class EncodeBitstreamParams {
|
||||
public:
|
||||
|
@ -37,6 +42,8 @@ public:
|
|||
int chopLevels;
|
||||
bool deltaViewFrustum;
|
||||
const ViewFrustum* lastViewFrustum;
|
||||
bool wantOcclusionCulling;
|
||||
CoverageMap* map;
|
||||
|
||||
EncodeBitstreamParams(
|
||||
int maxEncodeLevel = INT_MAX,
|
||||
|
@ -45,7 +52,9 @@ public:
|
|||
bool includeExistsBits = WANT_EXISTS_BITS,
|
||||
int chopLevels = 0,
|
||||
bool deltaViewFrustum = false,
|
||||
const ViewFrustum* lastViewFrustum = IGNORE_VIEW_FRUSTUM) :
|
||||
const ViewFrustum* lastViewFrustum = IGNORE_VIEW_FRUSTUM,
|
||||
bool wantOcclusionCulling= NO_OCCLUSION_CULLING,
|
||||
CoverageMap* map = IGNORE_COVERAGE_MAP) :
|
||||
|
||||
maxEncodeLevel (maxEncodeLevel),
|
||||
viewFrustum (viewFrustum),
|
||||
|
@ -53,7 +62,9 @@ public:
|
|||
includeExistsBits (includeExistsBits),
|
||||
chopLevels (chopLevels),
|
||||
deltaViewFrustum (deltaViewFrustum),
|
||||
lastViewFrustum (lastViewFrustum)
|
||||
lastViewFrustum (lastViewFrustum),
|
||||
wantOcclusionCulling(wantOcclusionCulling),
|
||||
map (map)
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -96,6 +107,8 @@ public:
|
|||
creationMode mode, bool destructive = false, bool debug = false);
|
||||
|
||||
void recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData=NULL);
|
||||
void recurseTreeWithOperationDistanceSorted(RecurseVoxelTreeOperation operation,
|
||||
const glm::vec3& point, void* extraData=NULL);
|
||||
|
||||
int encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
|
||||
EncodeBitstreamParams& params) const;
|
||||
|
@ -127,6 +140,10 @@ public:
|
|||
void copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destinationNode);
|
||||
|
||||
bool getShouldReaverage() const { return _shouldReaverage; }
|
||||
|
||||
void recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData);
|
||||
void recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseVoxelTreeOperation operation,
|
||||
const glm::vec3& point, void* extraData);
|
||||
|
||||
private:
|
||||
void deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraData);
|
||||
|
@ -141,7 +158,6 @@ private:
|
|||
|
||||
static bool countVoxelsOperation(VoxelNode* node, void* extraData);
|
||||
|
||||
void recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData);
|
||||
VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode) const;
|
||||
VoxelNode* createMissingNode(VoxelNode* lastParentNode, unsigned char* deepestCodeToCreate);
|
||||
int readNodeData(VoxelNode *destinationNode, unsigned char* nodeData, int bufferSizeBytes,
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <AvatarData.h>
|
||||
#include "VoxelNodeBag.h"
|
||||
#include "VoxelConstants.h"
|
||||
#include "CoverageMap.h"
|
||||
|
||||
class VoxelAgentData : public AvatarData {
|
||||
public:
|
||||
|
@ -36,6 +37,7 @@ public:
|
|||
void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; }
|
||||
|
||||
VoxelNodeBag nodeBag;
|
||||
CoverageMap map;
|
||||
|
||||
ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; };
|
||||
ViewFrustum& getLastKnownViewFrustum() { return _lastKnownViewFrustum; };
|
||||
|
|
|
@ -312,9 +312,13 @@ void deepestLevelVoxelDistributor(AgentList* agentList,
|
|||
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) {
|
||||
if (!agentData->nodeBag.isEmpty()) {
|
||||
VoxelNode* subTree = agentData->nodeBag.extract();
|
||||
|
||||
bool wantOcclusionCulling = agentData->getWantOcclusionCulling();
|
||||
CoverageMap* coverageMap = wantOcclusionCulling ? &agentData->map : IGNORE_COVERAGE_MAP;
|
||||
|
||||
EncodeBitstreamParams params(INT_MAX, &agentData->getCurrentViewFrustum(), agentData->getWantColor(),
|
||||
WANT_EXISTS_BITS, wantDelta, lastViewFrustum);
|
||||
WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum,
|
||||
wantOcclusionCulling, coverageMap);
|
||||
|
||||
bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
|
||||
agentData->nodeBag, params);
|
||||
|
@ -375,6 +379,7 @@ void deepestLevelVoxelDistributor(AgentList* agentList,
|
|||
if (agentData->nodeBag.isEmpty()) {
|
||||
agentData->updateLastKnownViewFrustum();
|
||||
agentData->setViewSent(true);
|
||||
agentData->map.erase();
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue