adding Matt's SvoViewer

This commit is contained in:
ZappoMan 2014-02-28 00:06:37 -08:00
parent bde7152246
commit 45b5f54bc5
24 changed files with 3224 additions and 0 deletions

View file

@ -39,3 +39,4 @@ add_subdirectory(domain-server)
add_subdirectory(interface)
add_subdirectory(tests)
add_subdirectory(voxel-edit)
add_subdirectory(SvoViewer)

250
SvoViewer/CMakeLists.txt Normal file
View file

@ -0,0 +1,250 @@
cmake_minimum_required(VERSION 2.8)
set(ROOT_DIR ..)
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
set(TARGET_NAME SvoViewer)
project(${TARGET_NAME})
# setup for find modules
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/")
if (APPLE)
set(GL_HEADERS "#include <GLUT/glut.h>\n#include <OpenGL/glext.h>")
endif (APPLE)
if (UNIX AND NOT APPLE)
# include the right GL headers for UNIX
set(GL_HEADERS "#include <GL/gl.h>\n#include <GL/glut.h>\n#include <GL/glext.h>")
endif (UNIX AND NOT APPLE)
if (WIN32)
add_definitions( -D_USE_MATH_DEFINES ) # apparently needed to get M_PI and other defines from cmath/math.h
add_definitions( -DWINDOWS_LEAN_AND_MEAN ) # needed to make sure windows doesn't go to crazy with its defines
# windows build needs an external glut, we're using freeglut
set(GLUT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/external/freeglut)
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${GLUT_ROOT_PATH})
# windows build needs glew (opengl extention wrangler) this will handle providing access to OpenGL methods after 1.1
# which are not accessible on windows without glew or some other dynamic linking mechanism
set(GLEW_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/external/glew)
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${GLEW_ROOT_PATH})
include_directories(SYSTEM ${GLEW_ROOT_PATH}/include ${GLUT_ROOT_PATH}/include)
#set(GL_HEADERS "#define GLEW_STATIC\n#define FREEGLUT_STATIC\n#define FREEGLUT_LIB_PRAGMAS 0\n#include <GL/glew.h>\n#include <GL/wglew.h>\n#include <GL/freeglut_std.h>\n#include <GL/freeglut_ext.h>")
set(GL_HEADERS "#define GLEW_STATIC\n#include <windowshacks.h>\n#include <GL/glew.h>\n#include <GL/glut.h>")
endif (WIN32)
# set up the external glm library
include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})
# create the ${TARGET_NAME}Config.h file based on GL_HEADERS above
configure_file(${TARGET_NAME}Config.h.in ${PROJECT_BINARY_DIR}/includes/${TARGET_NAME}Config.h)
configure_file(${TARGET_NAME}Version.h.in ${PROJECT_BINARY_DIR}/includes/${TARGET_NAME}Version.h)
# grab the implementation and header files from src dirs
file(GLOB APPLICATION_SRCS src/*.c src/*.cpp src/*.h)
foreach(SUBDIR avatar devices renderer ui starfield)
file(GLOB_RECURSE SUBDIR_SRCS src/${SUBDIR}/*.cpp src/${SUBDIR}/*.c src/${SUBDIR}/*.h)
set(APPLICATION_SRCS ${APPLICATION_SRCS} ${SUBDIR_SRCS})
endforeach(SUBDIR)
foreach(EXTERNAL_SOURCE_SUBDIR ${EXTERNAL_SOURCE_SUBDIRS})
file(GLOB_RECURSE SUBDIR_SRCS external/${EXTERNAL_SOURCE_SUBDIR}/src/*.cpp external/${EXTERNAL_SOURCE_SUBDIR}/src/*.c external/${EXTERNAL_SOURCE_SUBDIR}/src/*.h)
set(APPLICATION_SRCS ${APPLICATION_SRCS} ${SUBDIR_SRCS})
endforeach(EXTERNAL_SOURCE_SUBDIR)
find_package(Qt5Core REQUIRED)
find_package(Qt5Gui REQUIRED)
find_package(Qt5Multimedia REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5OpenGL REQUIRED)
find_package(Qt5Svg REQUIRED)
find_package(Qt5WebKit REQUIRED)
find_package(Qt5WebKitWidgets REQUIRED)
find_package(Qt5Xml REQUIRED)
# grab the ui files in resources/ui
file (GLOB_RECURSE QT_UI_FILES ui/*.ui)
# have qt5 wrap them and generate the appropriate header files
qt5_wrap_ui(QT_UI_HEADERS ${QT_UI_FILES})
# add them to the application source files
set(APPLICATION_SRCS ${APPLICATION_SRCS} ${QT_UI_HEADERS})
if (APPLE)
# configure CMake to use a custom Info.plist
SET_TARGET_PROPERTIES( ${this_target} PROPERTIES MACOSX_BUNDLE_INFO_PLIST MacOSXBundleInfo.plist.in )
set(MACOSX_BUNDLE_BUNDLE_NAME SvoViewer)
set(MACOSX_BUNDLE_GUI_IDENTIFIER io.highfidelity.${TARGET_NAME})
# set how the icon shows up in the Info.plist file
SET(MACOSX_BUNDLE_ICON_FILE ${TARGET_NAME}.icns)
# set where in the bundle to put the resources file
SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_SOURCE_DIR}/${TARGET_NAME}.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
SET(APPLICATION_SRCS ${APPLICATION_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/${TARGET_NAME}.icns)
# grab the directories in resources and put them in the right spot in Resources
file(GLOB RESOURCE_SUBDIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/resources ${CMAKE_CURRENT_SOURCE_DIR}/resources/*)
foreach(DIR ${RESOURCE_SUBDIRS})
if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/resources/${DIR})
FILE(GLOB DIR_CONTENTS resources/${DIR}/*)
SET_SOURCE_FILES_PROPERTIES(${DIR_CONTENTS} PROPERTIES MACOSX_PACKAGE_LOCATION Resources/${DIR})
SET(APPLICATION_SRCS ${APPLICATION_SRCS} ${DIR_CONTENTS})
endif()
endforeach()
endif (APPLE)
# create the executable, make it a bundle on OS X
add_executable(${TARGET_NAME} MACOSX_BUNDLE ${APPLICATION_SRCS})
# link in the hifi shared library
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
# link required hifi libraries
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(metavoxels ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(script-engine ${TARGET_NAME} ${ROOT_DIR})
# find required libraries
find_package(Faceshift)
find_package(GLM REQUIRED)
find_package(LibOVR)
find_package(Sixense)
find_package(Visage)
find_package(ZLIB)
# include the Sixense library for Razer Hydra if available
if (SIXENSE_FOUND AND NOT DISABLE_SIXENSE)
add_definitions(-DHAVE_SIXENSE)
include_directories(SYSTEM ${SIXENSE_INCLUDE_DIRS})
if (APPLE OR UNIX)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${SIXENSE_INCLUDE_DIRS}")
endif (APPLE OR UNIX)
target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
endif (SIXENSE_FOUND AND NOT DISABLE_SIXENSE)
# likewise with Visage library for webcam feature tracking
if (VISAGE_FOUND AND NOT DISABLE_VISAGE)
add_definitions(-DHAVE_VISAGE -DVISAGE_STATIC)
include_directories(SYSTEM ${VISAGE_INCLUDE_DIRS})
if (APPLE)
add_definitions(-DMAC_OS_X)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-comment -isystem ${VISAGE_INCLUDE_DIRS}")
find_library(AVFoundation AVFoundation)
find_library(CoreMedia CoreMedia)
find_library(NEW_STD_LIBRARY libc++.dylib /usr/lib/)
target_link_libraries(${TARGET_NAME} ${AVFoundation} ${CoreMedia} ${NEW_STD_LIBRARY})
endif (APPLE)
target_link_libraries(${TARGET_NAME} ${VISAGE_LIBRARIES})
endif (VISAGE_FOUND AND NOT DISABLE_VISAGE)
# and with LibOVR for Oculus Rift
if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR)
add_definitions(-DHAVE_LIBOVR)
include_directories(SYSTEM ${LIBOVR_INCLUDE_DIRS})
if (APPLE OR UNIX)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${LIBOVR_INCLUDE_DIRS}")
endif (APPLE OR UNIX)
target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES})
endif (LIBOVR_FOUND AND NOT DISABLE_LIBOVR)
qt5_use_modules(${TARGET_NAME} Core Gui Multimedia Network OpenGL Script Svg WebKit WebKitWidgets Xml UiTools)
# include headers for interface
include_directories(
${PROJECT_SOURCE_DIR}/src
${PROJECT_BINARY_DIR}/includes
)
# include external library headers
# use system flag so warnings are supressed
include_directories(
SYSTEM
${FACESHIFT_INCLUDE_DIRS}
${GLM_INCLUDE_DIRS}
)
target_link_libraries(
${TARGET_NAME}
${FACESHIFT_LIBRARIES}
${ZLIB_LIBRARIES}
)
if (APPLE)
# link in required OS X frameworks and include the right GL headers
find_library(AppKit AppKit)
find_library(CoreAudio CoreAudio)
find_library(CoreServices CoreServices)
find_library(Carbon Carbon)
find_library(Foundation Foundation)
find_library(GLUT GLUT)
find_library(OpenGL OpenGL)
find_library(IOKit IOKit)
find_library(QTKit QTKit)
find_library(QuartzCore QuartzCore)
target_link_libraries(
${TARGET_NAME}
${AppKit}
${CoreAudio}
${CoreServices}
${Carbon}
${Foundation}
${GLUT}
${OpenGL}
${IOKit}
${QTKit}
${QuartzCore}
)
else (APPLE)
find_package(OpenGL REQUIRED)
find_package(GLUT REQUIRED)
include_directories(${GLUT_INCLUDE_DIR} ${OPENGL_INCLUDE_DIR})
target_link_libraries(${TARGET_NAME} ${OPENGL_LIBRARY})
endif (APPLE)
# link target to external libraries
if (WIN32)
target_link_libraries(
${TARGET_NAME}
${CMAKE_CURRENT_SOURCE_DIR}/external/glew/lib/Release/Win32/glew32s.lib
${GLUT_ROOT_PATH}/lib/freeglut.lib
wsock32.lib
opengl32.lib
)
else (WIN32)
# link required libraries on UNIX
if (UNIX AND NOT APPLE)
find_package(Threads REQUIRED)
target_link_libraries(
${TARGET_NAME}
${CMAKE_THREAD_LIBS_INIT}
${GLUT_LIBRARY}
)
endif (UNIX AND NOT APPLE)
endif (WIN32)
# install command for OS X bundle
INSTALL(TARGETS ${TARGET_NAME}
BUNDLE DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/install COMPONENT Runtime
RUNTIME DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/install COMPONENT Runtime
)

BIN
SvoViewer/SvoViewer.icns Normal file

Binary file not shown.

View file

@ -0,0 +1,14 @@
//
// SvoViewerConfig.h
// SvoViewer
//
// Copyright (c) 2014 High Fidelity, Inc.. All rights reserved.
//
#ifndef __SvoViewerConfig__
#define __SvoViewerConfig__
#define GL_GLEXT_PROTOTYPES 1
@GL_HEADERS@
#endif

View file

@ -0,0 +1,8 @@
//
// SvoViewerVersion.h
// Declaration of version and build data
//
// Copyright (c) 2014 High Fidelity, Inc.. All rights reserved.
//
const QString BUILD_VERSION = "@BUILD_SEQ@";

View file

@ -0,0 +1,187 @@
//
// AABoundingVolume.h - Axis Aligned Bounding Volumes
// hifi
//
#ifndef _AABOUNDING_VOLUME_
#define _AABOUNDING_VOLUME_
#include <glm/glm.hpp>
// A simple axis aligned bounding volume.
#define AABF_NUM_DIMS 3
#define AABF_LOW 0
#define AABF_HIGH 1
//Todo:: make this a template.
class AABoundingVolume
{
public:
AABoundingVolume() : _isSingleDirectionSet(false), _numPointsInSet(0) { memset(_bounds,0,sizeof(_bounds)); }
~AABoundingVolume(){}
void AddToSet(const glm::vec3 newPt)
{
if (_numPointsInSet == 0)
{
for (int i = 0; i < AABF_NUM_DIMS; i++)
{
_bounds[i][AABF_LOW] = _bounds[i][AABF_HIGH] = newPt[i];
}
}
else
{
for (int i = 0; i < AABF_NUM_DIMS; i++)
{
if (newPt[i] < _bounds[i][AABF_LOW]) _bounds[i][AABF_LOW] = newPt[i];
if (newPt[i] > _bounds[i][AABF_HIGH]) _bounds[i][AABF_HIGH] = newPt[i];
}
}
_numPointsInSet++;
}
float getBound(const int dim, const int lohi) { return _bounds[dim][lohi]; }
glm::vec3 getCorner(const BoxVertex i)
{
switch (i)
{
case BOTTOM_LEFT_NEAR:
return glm::vec3(_bounds[0][AABF_LOW], _bounds[1][AABF_LOW], _bounds[2][AABF_HIGH]);
break;
case BOTTOM_RIGHT_NEAR:
return glm::vec3(_bounds[0][AABF_HIGH], _bounds[1][AABF_LOW], _bounds[2][AABF_HIGH]);
break;
case TOP_RIGHT_NEAR:
return glm::vec3(_bounds[0][AABF_HIGH], _bounds[1][AABF_HIGH], _bounds[2][AABF_HIGH]);
break;
case TOP_LEFT_NEAR:
return glm::vec3(_bounds[0][AABF_LOW], _bounds[1][AABF_HIGH], _bounds[2][AABF_HIGH]);
break;
case BOTTOM_LEFT_FAR:
return glm::vec3(_bounds[0][AABF_LOW], _bounds[1][AABF_LOW], _bounds[2][AABF_LOW]);
break;
case BOTTOM_RIGHT_FAR:
return glm::vec3(_bounds[0][AABF_HIGH], _bounds[1][AABF_LOW], _bounds[2][AABF_LOW]);
break;
case TOP_RIGHT_FAR:
return glm::vec3(_bounds[0][AABF_HIGH], _bounds[1][AABF_HIGH], _bounds[2][AABF_LOW]);
break;
case TOP_LEFT_FAR:
return glm::vec3(_bounds[0][AABF_LOW], _bounds[1][AABF_HIGH], _bounds[2][AABF_LOW]);
break;
default :
assert(0 && "Requested invalid bounding volume vertex!");
}
return glm::vec3(-1.0, -1.0, -1.0);
}
void setIsSingleDirection(const bool enable, const glm::vec3 normal)
{
if (enable) _singleDirectionNormal = normal;
_isSingleDirectionSet = true;
}
int within(const float loc, int dim)
{
if (loc < _bounds[dim][AABF_LOW]) return -1;
if (loc > _bounds[dim][AABF_HIGH]) return 1;
return 0;
}
bool isSingleDirectionSet(){ return _isSingleDirectionSet; }
glm::vec3 getSingleDirectionNormal(){ return _singleDirectionNormal; }
AABoundingVolume& operator= (const AABoundingVolume& val) {
if (this !=&val)
{
memcpy(_bounds, &val._bounds, sizeof(AABoundingVolume));
}
return *this;
}
protected:
float _bounds[AABF_NUM_DIMS][2]; // Bounds for each dimension. NOTE:: Not a vector so don't store it that way!
int _numPointsInSet;
bool _isSingleDirectionSet;
glm::vec3 _singleDirectionNormal;
};
#define AABF2D_NUM_DIMS 2
#define UNDEFINED_RANGE_VAL 99999.0
class AA2DBoundingVolume
{
public:
AA2DBoundingVolume() : _numPointsInSet(0) {}
~AA2DBoundingVolume(){}
void AddToSet(const float newPt[2])
{
if (_numPointsInSet == 0)
{
for (int i = 0; i < AABF2D_NUM_DIMS; i++)
{
_bounds[i][AABF_LOW] = _bounds[i][AABF_HIGH] = newPt[i];
}
}
else
{
for (int i = 0; i < AABF2D_NUM_DIMS; i++)
{
if (newPt[i] < _bounds[i][AABF_LOW]) _bounds[i][AABF_LOW] = newPt[i];
if (newPt[i] > _bounds[i][AABF_HIGH]) _bounds[i][AABF_HIGH] = newPt[i];
}
}
_numPointsInSet++;
}
// return true if its in range.
bool clipToRegion(const float lowx, const float lowy, const float highx, const float highy)
{
assert(highx > lowx && highy > lowy);
bool inRange = true;
if (_bounds[0][AABF_LOW] > highx || _bounds[0][AABF_HIGH] < lowx)
{
_bounds[0][AABF_LOW] = _bounds[0][AABF_HIGH] = UNDEFINED_RANGE_VAL;
inRange=false;
}
else
{
if (_bounds[0][AABF_LOW] < lowx) _bounds[0][AABF_LOW] = lowx;
if (_bounds[0][AABF_HIGH] > highx) _bounds[0][AABF_HIGH] = highx;
}
if (_bounds[1][AABF_LOW] > highy || _bounds[1][AABF_HIGH] < lowy)
{
_bounds[1][AABF_LOW] = _bounds[1][AABF_HIGH] = UNDEFINED_RANGE_VAL;
inRange=false;
}
else
{
if (_bounds[1][AABF_LOW] < lowy) _bounds[1][AABF_LOW] = lowy;
if (_bounds[1][AABF_HIGH] > highy) _bounds[1][AABF_HIGH] = highy;
}
return inRange;
}
float getHeight()
{
if (_bounds[1][AABF_HIGH] == UNDEFINED_RANGE_VAL || _bounds[1][AABF_LOW] == UNDEFINED_RANGE_VAL) return 0;
return _bounds[1][AABF_HIGH] - _bounds[1][AABF_LOW];
}
float getWidth()
{
if (_bounds[0][AABF_HIGH] == UNDEFINED_RANGE_VAL || _bounds[0][AABF_LOW] == UNDEFINED_RANGE_VAL) return 0;
return _bounds[0][AABF_HIGH] - _bounds[0][AABF_LOW];
}
protected:
float _bounds[AABF2D_NUM_DIMS][2];
int _numPointsInSet;
};
#endif

251
SvoViewer/src/Camera.cpp Executable file
View file

@ -0,0 +1,251 @@
//
// Camera.cpp
// interface
//
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
#include <glm/gtx/quaternion.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <SharedUtil.h>
#include <VoxelConstants.h>
#include <EventTypes.h>
#include "Camera.h"
#include "Menu.h"
#include "Util.h"
const float CAMERA_FIRST_PERSON_MODE_UP_SHIFT = 0.0f;
const float CAMERA_FIRST_PERSON_MODE_DISTANCE = 0.0f;
const float CAMERA_FIRST_PERSON_MODE_TIGHTNESS = 100.0f;
const float CAMERA_INDEPENDENT_MODE_UP_SHIFT = 0.0f;
const float CAMERA_INDEPENDENT_MODE_DISTANCE = 0.0f;
const float CAMERA_INDEPENDENT_MODE_TIGHTNESS = 100.0f;
const float CAMERA_THIRD_PERSON_MODE_UP_SHIFT = -0.2f;
const float CAMERA_THIRD_PERSON_MODE_DISTANCE = 1.5f;
const float CAMERA_THIRD_PERSON_MODE_TIGHTNESS = 8.0f;
const float CAMERA_MIRROR_MODE_UP_SHIFT = 0.0f;
const float CAMERA_MIRROR_MODE_DISTANCE = 0.17f;
const float CAMERA_MIRROR_MODE_TIGHTNESS = 100.0f;
Camera::Camera() :
_needsToInitialize(true),
_mode(CAMERA_MODE_THIRD_PERSON),
_prevMode(CAMERA_MODE_THIRD_PERSON),
_frustumNeedsReshape(true),
_position(0.0f, 0.0f, 0.0f),
_idealPosition(0.0f, 0.0f, 0.0f),
_targetPosition(0.0f, 0.0f, 0.0f),
_fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES),
_aspectRatio(16.f/9.f),
_nearClip(0.08f), // default
_farClip(50.0f * TREE_SCALE), // default
_upShift(0.0f),
_distance(0.0f),
_tightness(10.0f), // default
_previousUpShift(0.0f),
_previousDistance(0.0f),
_previousTightness(0.0f),
_newUpShift(0.0f),
_newDistance(0.0f),
_newTightness(0.0f),
_modeShift(1.0f),
_linearModeShift(0.0f),
_modeShiftPeriod(1.0f),
_scale(1.0f),
_lookingAt(0.0f, 0.0f, 0.0f),
_isKeepLookingAt(false)
{
}
void Camera::update(float deltaTime) {
if (_mode != CAMERA_MODE_NULL) {
// use iterative forces to push the camera towards the target position and angle
updateFollowMode(deltaTime);
}
}
// use iterative forces to keep the camera at the desired position and angle
void Camera::updateFollowMode(float deltaTime) {
if (_linearModeShift < 1.0f) {
_linearModeShift += deltaTime / _modeShiftPeriod;
if (_needsToInitialize || _linearModeShift > 1.0f) {
_linearModeShift = 1.0f;
_modeShift = 1.0f;
_upShift = _newUpShift;
_distance = _newDistance;
_tightness = _newTightness;
} else {
_modeShift = ONE_HALF - ONE_HALF * cosf(_linearModeShift * PIE );
_upShift = _previousUpShift * (1.0f - _modeShift) + _newUpShift * _modeShift;
_distance = _previousDistance * (1.0f - _modeShift) + _newDistance * _modeShift;
_tightness = _previousTightness * (1.0f - _modeShift) + _newTightness * _modeShift;
}
}
// derive t from tightness
float t = _tightness * _modeShift * deltaTime;
if (t > 1.0) {
t = 1.0;
}
// handle keepLookingAt
if (_isKeepLookingAt) {
lookAt(_lookingAt);
}
// Update position and rotation, setting directly if tightness is 0.0
if (_needsToInitialize || (_tightness == 0.0f)) {
_rotation = _targetRotation;
_idealPosition = _targetPosition + _scale * (_rotation * glm::vec3(0.0f, _upShift, _distance));
_position = _idealPosition;
_needsToInitialize = false;
} else {
// pull rotation towards ideal
_rotation = safeMix(_rotation, _targetRotation, t);
_idealPosition = _targetPosition + _scale * (_rotation * glm::vec3(0.0f, _upShift, _distance));
_position += (_idealPosition - _position) * t;
}
}
float Camera::getFarClip() const {
return (_scale * _farClip < std::numeric_limits<int16_t>::max())
? _scale * _farClip
: std::numeric_limits<int16_t>::max() - 1;
}
void Camera::setModeShiftPeriod (float period) {
const float MIN_PERIOD = 0.001f;
const float MAX_PERIOD = 3.0f;
_modeShiftPeriod = glm::clamp(period, MIN_PERIOD, MAX_PERIOD);
}
void Camera::setMode(CameraMode m) {
_prevMode = _mode;
_mode = m;
_modeShift = 0.0;
_linearModeShift = 0.0;
_previousUpShift = _upShift;
_previousDistance = _distance;
_previousTightness = _tightness;
if (_mode == CAMERA_MODE_THIRD_PERSON) {
_newUpShift = CAMERA_THIRD_PERSON_MODE_UP_SHIFT;
_newDistance = CAMERA_THIRD_PERSON_MODE_DISTANCE;
_newTightness = CAMERA_THIRD_PERSON_MODE_TIGHTNESS;
} else if (_mode == CAMERA_MODE_FIRST_PERSON) {
_newUpShift = CAMERA_FIRST_PERSON_MODE_UP_SHIFT;
_newDistance = CAMERA_FIRST_PERSON_MODE_DISTANCE;
_newTightness = CAMERA_FIRST_PERSON_MODE_TIGHTNESS;
} else if (_mode == CAMERA_MODE_MIRROR) {
_newUpShift = CAMERA_MIRROR_MODE_UP_SHIFT;
_newDistance = CAMERA_MIRROR_MODE_DISTANCE;
_newTightness = CAMERA_MIRROR_MODE_TIGHTNESS;
} else if (_mode == CAMERA_MODE_INDEPENDENT) {
_newUpShift = CAMERA_INDEPENDENT_MODE_UP_SHIFT;
_newDistance = CAMERA_INDEPENDENT_MODE_DISTANCE;
_newTightness = CAMERA_INDEPENDENT_MODE_TIGHTNESS;
}
}
void Camera::setTargetPosition(const glm::vec3& t) {
_targetPosition = t;
// handle keepLookingAt
if (_isKeepLookingAt) {
lookAt(_lookingAt);
}
}
void Camera::setTargetRotation( const glm::quat& targetRotation ) {
_targetRotation = targetRotation;
}
void Camera::setFieldOfView(float f) {
_fieldOfView = f;
_frustumNeedsReshape = true;
}
void Camera::setAspectRatio(float a) {
_aspectRatio = a;
_frustumNeedsReshape = true;
}
void Camera::setNearClip(float n) {
_nearClip = n;
_frustumNeedsReshape = true;
}
void Camera::setFarClip(float f) {
_farClip = f;
_frustumNeedsReshape = true;
}
void Camera::setEyeOffsetPosition(const glm::vec3& p) {
_eyeOffsetPosition = p;
_frustumNeedsReshape = true;
}
void Camera::setEyeOffsetOrientation(const glm::quat& o) {
_eyeOffsetOrientation = o;
_frustumNeedsReshape = true;
}
void Camera::setScale(float s) {
_scale = s;
_needsToInitialize = true;
_frustumNeedsReshape = true;
}
void Camera::initialize() {
_needsToInitialize = true;
_modeShift = 0.0;
}
// call to find out if the view frustum needs to be reshaped
bool Camera::getFrustumNeedsReshape() const {
return _frustumNeedsReshape;
}
// call this when deciding whether to render the head or not
CameraMode Camera::getInterpolatedMode() const {
const float SHIFT_THRESHOLD_INTO_FIRST_PERSON = 0.7f;
const float SHIFT_THRESHOLD_OUT_OF_FIRST_PERSON = 0.6f;
if ((_mode == CAMERA_MODE_FIRST_PERSON && _linearModeShift < SHIFT_THRESHOLD_INTO_FIRST_PERSON) ||
(_prevMode == CAMERA_MODE_FIRST_PERSON && _linearModeShift < SHIFT_THRESHOLD_OUT_OF_FIRST_PERSON)) {
return _prevMode;
}
return _mode;
}
// call this after reshaping the view frustum
void Camera::setFrustumWasReshaped() {
_frustumNeedsReshape = false;
}
void Camera::lookAt(const glm::vec3& lookAt) {
glm::vec3 up = IDENTITY_UP;
glm::mat4 lookAtMatrix = glm::lookAt(_targetPosition, lookAt, up);
glm::quat rotation = glm::quat_cast(lookAtMatrix);
rotation.w = -rotation.w; // Rosedale approved
setTargetRotation(rotation);
}
void Camera::keepLookingAt(const glm::vec3& point) {
lookAt(point);
_isKeepLookingAt = true;
_lookingAt = point;
}

119
SvoViewer/src/Camera.h Executable file
View file

@ -0,0 +1,119 @@
//
// Camera.h
// interface
//
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__camera__
#define __interface__camera__
#include <glm/glm.hpp>
#include <ViewFrustum.h>
//const float DEFAULT_FIELD_OF_VIEW_DEGREES = 90.0f;
enum CameraMode
{
CAMERA_MODE_NULL = -1,
CAMERA_MODE_THIRD_PERSON,
CAMERA_MODE_FIRST_PERSON,
CAMERA_MODE_MIRROR,
CAMERA_MODE_INDEPENDENT,
NUM_CAMERA_MODES
};
class Camera {
public:
Camera();
void initialize(); // instantly put the camera at the ideal position and rotation.
void update( float deltaTime );
void setUpShift(float u) { _upShift = u; }
void setDistance(float d) { _distance = d; }
void setPosition(const glm::vec3& p) { _position = p; }
void setTargetPosition(const glm::vec3& t);
void setTightness(float t) { _tightness = t; }
void setTargetRotation(const glm::quat& rotation);
void setMode(CameraMode m);
void setModeShiftPeriod(float r);
void setFieldOfView(float f);
void setAspectRatio(float a);
void setNearClip(float n);
void setFarClip(float f);
void setEyeOffsetPosition(const glm::vec3& p);
void setEyeOffsetOrientation(const glm::quat& o);
void setScale(const float s);
const glm::vec3& getPosition() const { return _position; }
const glm::quat& getRotation() const { return _rotation; }
CameraMode getMode() const { return _mode; }
const glm::vec3& getTargetPosition() const { return _targetPosition; }
const glm::quat& getTargetRotation() const { return _targetRotation; }
float getFieldOfView() const { return _fieldOfView; }
float getAspectRatio() const { return _aspectRatio; }
float getNearClip() const { return _scale * _nearClip; }
float getFarClip() const;
const glm::vec3& getEyeOffsetPosition() const { return _eyeOffsetPosition; }
const glm::quat& getEyeOffsetOrientation() const { return _eyeOffsetOrientation; }
float getScale() const { return _scale; }
CameraMode getInterpolatedMode() const;
bool getFrustumNeedsReshape() const; // call to find out if the view frustum needs to be reshaped
void setFrustumWasReshaped(); // call this after reshaping the view frustum.
// These only work on independent cameras
/// one time change to what the camera is looking at
void lookAt(const glm::vec3& value);
/// fix what the camera is looking at, and keep the camera looking at this even if position changes
void keepLookingAt(const glm::vec3& value);
/// stops the keep looking at feature, doesn't change what's being looked at, but will stop camera from
/// continuing to update it's orientation to keep looking at the item
void stopLooking() { _isKeepLookingAt = false; }
private:
bool _needsToInitialize;
CameraMode _mode;
CameraMode _prevMode;
bool _frustumNeedsReshape;
glm::vec3 _position;
glm::vec3 _idealPosition;
glm::vec3 _targetPosition;
float _fieldOfView;
float _aspectRatio;
float _nearClip;
float _farClip;
glm::vec3 _eyeOffsetPosition;
glm::quat _eyeOffsetOrientation;
glm::quat _rotation;
glm::quat _targetRotation;
float _upShift;
float _distance;
float _tightness;
float _previousUpShift;
float _previousDistance;
float _previousTightness;
float _newUpShift;
float _newDistance;
float _newTightness;
float _modeShift;
float _linearModeShift;
float _modeShiftPeriod;
float _scale;
glm::vec3 _lookingAt;
bool _isKeepLookingAt;
void updateFollowMode(float deltaTime);
};
#endif

87
SvoViewer/src/GLCanvas.cpp Executable file
View file

@ -0,0 +1,87 @@
//
// GLCanvas.cpp
// hifi
//
// Created by Stephen Birarda on 8/14/13.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include "SvoViewer.h"
#include "GLCanvas.h"
#include <QMimeData>
#include <QUrl>
GLCanvas::GLCanvas() : QGLWidget(QGLFormat(QGL::NoDepthBuffer, QGL::NoStencilBuffer)) {
}
void GLCanvas::initializeGL() {
SvoViewer::getInstance()->initializeGL();
setAttribute(Qt::WA_AcceptTouchEvents);
setAcceptDrops(true);
}
void GLCanvas::paintGL() {
SvoViewer::getInstance()->paintGL();
}
void GLCanvas::resizeGL(int width, int height) {
SvoViewer::getInstance()->resizeGL(width, height);
}
void GLCanvas::keyPressEvent(QKeyEvent* event) {
SvoViewer::getInstance()->keyPressEvent(event);
}
void GLCanvas::keyReleaseEvent(QKeyEvent* event) {
SvoViewer::getInstance()->keyReleaseEvent(event);
}
void GLCanvas::mouseMoveEvent(QMouseEvent* event) {
SvoViewer::getInstance()->mouseMoveEvent(event);
}
void GLCanvas::mousePressEvent(QMouseEvent* event) {
SvoViewer::getInstance()->mousePressEvent(event);
}
void GLCanvas::mouseReleaseEvent(QMouseEvent* event) {
SvoViewer::getInstance()->mouseReleaseEvent(event);
}
int updateTime = 0;
bool GLCanvas::event(QEvent* event) {
/* switch (event->type()) {
case QEvent::TouchBegin:
SvoViewer::getInstance()->touchBeginEvent(static_cast<QTouchEvent*>(event));
event->accept();
return true;
case QEvent::TouchEnd:
SvoViewer::getInstance()->touchEndEvent(static_cast<QTouchEvent*>(event));
return true;
case QEvent::TouchUpdate:
SvoViewer::getInstance()->touchUpdateEvent(static_cast<QTouchEvent*>(event));
return true;
default:
break;
}*/
return QGLWidget::event(event);
}
void GLCanvas::wheelEvent(QWheelEvent* event) {
//SvoViewer::getInstance()->wheelEvent(event);
}
void GLCanvas::dragEnterEvent(QDragEnterEvent* event) {
/*const QMimeData *mimeData = event->mimeData();
foreach (QUrl url, mimeData->urls()) {
if (url.url().toLower().endsWith(SNAPSHOT_EXTENSION)) {
event->acceptProposedAction();
break;
}
}*/
}
void GLCanvas::dropEvent(QDropEvent* event) {
//SvoViewer::getInstance()->dropEvent(event);
}

39
SvoViewer/src/GLCanvas.h Normal file
View file

@ -0,0 +1,39 @@
//
// GLCanvas.h
// hifi
//
// Created by Stephen Birarda on 8/14/13.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#ifndef __hifi__GLCanvas__
#define __hifi__GLCanvas__
#include <QGLWidget>
/// customized canvas that simply forwards requests/events to the singleton application
class GLCanvas : public QGLWidget {
public:
GLCanvas();
protected:
virtual void initializeGL();
virtual void paintGL();
virtual void resizeGL(int width, int height);
virtual void keyPressEvent(QKeyEvent* event);
virtual void keyReleaseEvent(QKeyEvent* event);
virtual void mouseMoveEvent(QMouseEvent* event);
virtual void mousePressEvent(QMouseEvent* event);
virtual void mouseReleaseEvent(QMouseEvent* event);
virtual bool event(QEvent* event);
virtual void wheelEvent(QWheelEvent* event);
virtual void dragEnterEvent(QDragEnterEvent *event);
virtual void dropEvent(QDropEvent* event);
};
#endif /* defined(__hifi__GLCanvas__) */

20
SvoViewer/src/ProgramObject.cpp Executable file
View file

@ -0,0 +1,20 @@
//
// ProgramObject.cpp
// interface
//
// Created by Andrzej Kapolka on 5/7/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
#include "ProgramObject.h"
ProgramObject::ProgramObject(QObject* parent) : QGLShaderProgram(parent) {
}
void ProgramObject::setUniform(int location, const glm::vec3& value) {
setUniformValue(location, value.x, value.y, value.z);
}
void ProgramObject::setUniform(const char* name, const glm::vec3& value) {
setUniformValue(name, value.x, value.y, value.z);
}

25
SvoViewer/src/ProgramObject.h Executable file
View file

@ -0,0 +1,25 @@
//
// ProgramObject.h
// interface
//
// Created by Andrzej Kapolka on 5/7/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__ProgramObject__
#define __interface__ProgramObject__
#include <QGLShaderProgram>
#include <glm/glm.hpp>
class ProgramObject : public QGLShaderProgram {
public:
ProgramObject(QObject* parent = 0);
void setUniform(int location, const glm::vec3& value);
void setUniform(const char* name, const glm::vec3& value);
};
#endif /* defined(__interface__ProgramObject__) */

744
SvoViewer/src/Render.cpp Executable file
View file

@ -0,0 +1,744 @@
#include "svoviewer.h"
////////////////////////////////////////////////////
// Some GL helper functions
GLubyte SvoViewer::SetupGlVBO(GLuint * id, int sizeInBytes, GLenum target, GLenum usage, void * dataUp )
{
glGenBuffers(1, id);
glBindBuffer(target, *id);
glBufferData(target, sizeInBytes, dataUp, usage);
return PrintGLErrorCode();
}
////////////////////////////////////////////////////
// Include all the render methods here.
////////////////////////////////////////////////////
// Points system
bool SvoViewer::FindNumLeaves(OctreeElement* node, void* extraData)
{
VoxelTreeElement* voxel = (VoxelTreeElement*)node;
FindNumLeavesData* args = (FindNumLeavesData*)extraData;
if (node->isLeaf()) args->numLeaves++;
return true;
}
struct PointRenderAssembleData
{
glm::vec3 * buffer;
unsigned char* colorBuffer;
unsigned int count;
};
bool SvoViewer::PointRenderAssemblePerVoxel(OctreeElement* node, void* extraData)
{
VoxelTreeElement* voxel = (VoxelTreeElement*)node;
PointRenderAssembleData* args = (PointRenderAssembleData*)extraData;
if (!node->isLeaf()) return true;
AABox box = voxel->getAABox();
glm::vec3 center = box.calcCenter();
center += glm::vec3(0, -.05, 0);
center *= 100.0;
args->buffer[args->count] = center;
int cCount = args->count * 3;
args->colorBuffer[cCount] = voxel->getTrueColor()[0];
args->colorBuffer[cCount+1] = voxel->getTrueColor()[1];
args->colorBuffer[cCount+2] = voxel->getTrueColor()[2];
args->count++;
return true; // keep going!
}
void SvoViewer::InitializePointRenderSystem()
{
DebugPrint("Initializing point render system!\n");
quint64 fstart = usecTimestampNow();
_renderFlags.voxelRenderDirty = true;
_renderFlags.voxelOptRenderDirty = true;
glGenBuffers(1, &_pointVtxBuffer);
glBindBuffer( GL_ARRAY_BUFFER, _pointVtxBuffer);
// Assemble the buffer data.
PointRenderAssembleData args;
args.buffer = _pointVertices = new glm::vec3[_nodeCount];
args.colorBuffer = _pointColors = new unsigned char[_nodeCount*3];
assert(args.buffer != NULL);
args.count = 0;
_systemTree.recurseTreeWithOperation(&PointRenderAssemblePerVoxel, &args);
assert(args.count < _nodeCount);
_pointVerticesCount = args.count;
// create the data store.
int size = _nodeCount * sizeof(glm::vec3);
glBindBuffer( GL_ARRAY_BUFFER, _pointVtxBuffer);
glBufferData(GL_ARRAY_BUFFER, _nodeCount * 3, args.buffer, GL_STATIC_DRAW);
//glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
//glBindBuffer(GL_ARRAY_BUFFER, _pointColorBuffer);
//glBufferData(GL_ARRAY_BUFFER, size, args.colorBuffer, GL_STATIC_DRAW);
//glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
_renderFlags.ptRenderDirty = false;
_ptRenderInitialized = true;
float elapsed = (float)(usecTimestampNow() - fstart) / 1000.f;
DebugPrint("Point render intialization took %f time for %d nodes\n", elapsed, _nodeCount);
}
void SvoViewer::RenderTreeSystemAsPoints()
{
glDisable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
//glDisable(GL_DEPTH_TEST);
glColor3f(.5, 0, 0);
glPointSize(1.0f);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, _pointVtxBuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glVertexPointer(3, GL_FLOAT, 0, _pointVertices);
// Removed for testing.
/*glEnableClientState(GL_COLOR_ARRAY);
//glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, _pointColorBuffer);*/
//glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
//glColorPointer(3, GL_UNSIGNED_BYTE, 0, _pointColors);
glDrawArrays(GL_POINTS, 0, _pointVerticesCount);
}
void SvoViewer::StopUsingPointRenderSystem()
{
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
}
////////////////////////////////////////////////////
const GLchar * simpleVtxShaderSrc =
"uniform mat4 viewMatrix;\n\
uniform mat4 projectionMatrix;\n\
uniform mat4 modelMatrix;\n\
void main()\n\
{\n\
vec4 world_pos = modelMatrix * gl_Vertex;\n\
vec4 view_pos = viewMatrix * world_pos;\n\
gl_Position = projectionMatrix * view_pos;\n\
gl_FrontColor = gl_Color;\n\
}\n";
int simpleVtxShaderLen = strlen(simpleVtxShaderSrc);
const GLchar * simpleGeomShaderSrc =
"#version 330 compatibility\n\
layout(triangles) in;\n\
layout(triangle_strip, max_vertices=3) out;\n\
void main()\n\
{\n\
for(int i=0; i<3; i++)\n\
{\n\
gl_Position = gl_in[i].gl_Position;\n\
EmitVertex();\n\
}\n\
EndPrimitive();\n\
}";
int simpleGeomShaderLen = strlen(simpleGeomShaderSrc);
const GLchar * simpleFragShaderSrc =
"void main()\n\
{\n\
gl_FragColor = gl_Color;\n\
}\n";
int simpleFragShaderLen = strlen(simpleFragShaderSrc);
struct VoxelRenderAssembleData
{
glm::vec3 * buffer;
unsigned char* colorBuffer;
GLuint** idxBuffs;
unsigned int leafCount;
unsigned int lastBufferSegmentStart;
GLuint vtxID;
GLuint colorID;
GLuint* idxIds;
};
bool SvoViewer::VoxelRenderAssemblePerVoxel(OctreeElement* node, void* extraData)
{
VoxelTreeElement* voxel = (VoxelTreeElement*)node;
VoxelRenderAssembleData* args = (VoxelRenderAssembleData*)extraData;
if (!node->isLeaf()) return true;
AABox box = voxel->getAABox();
int totalNodesProcessedSinceLastFlush = args->leafCount - args->lastBufferSegmentStart;
// ack, one of these components is flags, not alpha
int cCount = totalNodesProcessedSinceLastFlush * 4; // Place it relative to the current segment.
unsigned char col[4] = {voxel->getTrueColor()[0], voxel->getTrueColor()[1], voxel->getTrueColor()[2], 1};
for(int i = 0; i < GLOBAL_NORMALS_VERTICES_PER_VOXEL; i++)
memcpy(&args->colorBuffer[cCount+i*4], col, sizeof(col));
int vCount = totalNodesProcessedSinceLastFlush * GLOBAL_NORMALS_VERTICES_PER_VOXEL; // num vertices we've added so far.
{
int idxCount = totalNodesProcessedSinceLastFlush * INDICES_PER_FACE; // same for every cube face.
// Indices follow a static pattern.
for (int i = 0; i < NUM_CUBE_FACES; i++)
{
GLuint* idxBuff = args->idxBuffs[i];
for (int j = 0; j < INDICES_PER_FACE; j++)
{
idxBuff[idxCount+j] = SvoViewerNames::cubeFaceIndices[i][j] + vCount;
}
}
}
// Grab vtx positions according to setup from indices layout above.
//center += glm::vec3(0, -.05, 0);
float scale = 100.0;
for ( int i = 0; i < GLOBAL_NORMALS_VERTICES_PER_VOXEL; i++)
{
args->buffer[vCount] = box.getVertex((BoxVertex)i); // i corresponds to BOTTOM_LEFT_NEAR,BOTTOM_RIGHT_NEAR,...
args->buffer[vCount++] *= scale;
}
args->leafCount++;
totalNodesProcessedSinceLastFlush++;
if (totalNodesProcessedSinceLastFlush >= REASONABLY_LARGE_BUFFER) // Flush data to GL once we have assembled enough of it.
{
//DebugPrint("committing!\n");
PrintGLErrorCode();
glBindBuffer(GL_ARRAY_BUFFER, args->vtxID);
glBufferSubData(GL_ARRAY_BUFFER, args->lastBufferSegmentStart * sizeof(glm::vec3) * GLOBAL_NORMALS_VERTICES_PER_VOXEL,
totalNodesProcessedSinceLastFlush * sizeof(glm::vec3) * GLOBAL_NORMALS_VERTICES_PER_VOXEL, args->buffer);
PrintGLErrorCode();
glBindBuffer(GL_ARRAY_BUFFER, args->colorID);
glBufferSubData(GL_ARRAY_BUFFER, args->lastBufferSegmentStart * sizeof(GLubyte) * 4 * GLOBAL_NORMALS_VERTICES_PER_VOXEL,
totalNodesProcessedSinceLastFlush * sizeof(GLubyte) * 4 * GLOBAL_NORMALS_VERTICES_PER_VOXEL, args->colorBuffer);
PrintGLErrorCode();
for (int i = 0; i < NUM_CUBE_FACES; i++)
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, args->idxIds[i]);
GLuint* idxBuff = args->idxBuffs[i];
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, args->lastBufferSegmentStart * sizeof(GLuint) * INDICES_PER_FACE,
totalNodesProcessedSinceLastFlush * sizeof(GLuint) * INDICES_PER_FACE, idxBuff); // Rework.
PrintGLErrorCode();
}
glFlush();
PrintGLErrorCode();
args->lastBufferSegmentStart = args->leafCount;
}
return true;
}
void SvoViewer::InitializeVoxelRenderSystem()
{
DebugPrint("Initializing voxel render system.\n");
FindNumLeavesData data;
data.numLeaves = 0;
_systemTree.recurseTreeWithOperation(&FindNumLeaves, &data);
_leafCount = data.numLeaves;
glGenBuffers(NUM_CUBE_FACES, _vboIndicesIds);
for (int i = 0; i < NUM_CUBE_FACES; i++)
{
_vboIndices[i] = new GLuint[REASONABLY_LARGE_BUFFER * INDICES_PER_FACE];
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesIds[i]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, INDICES_PER_FACE * sizeof(GLuint) * _nodeCount,
NULL, GL_STATIC_DRAW);
}
glGenBuffers(1, &_vboVerticesID);
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
glBufferData(GL_ARRAY_BUFFER, GLOBAL_NORMALS_VERTICES_PER_VOXEL * sizeof(glm::vec3) * _nodeCount, NULL, GL_STATIC_DRAW);
PrintGLErrorCode();
glGenBuffers(1, &_vboColorsID);
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
glBufferData(GL_ARRAY_BUFFER, GLOBAL_NORMALS_VERTICES_PER_VOXEL * sizeof(GLubyte) * 4 * _nodeCount, NULL, GL_STATIC_DRAW);
PrintGLErrorCode();
//int numVertices = GLOBAL_NORMALS_VERTICES_PER_VOXEL * _nodeCount;
// Working set test = dead after about 1.5gb! I.e... don't try to allocate this here.
// This will tell you about what you have to play with. On my system, it died consistently after 1.3-1.5gb.
//glm::vec3* memPtrs[20];
//int i;
//for (i = 0; i < 20; i++)
//{
// memPtrs[i] = new glm::vec3[numVertices / 20];
//}
//
//for (i = 0; i < 20; i++)
// delete [] memPtrs[i];
#define PAD 32
_readVerticesArray = new glm::vec3[GLOBAL_NORMALS_VERTICES_PER_VOXEL * REASONABLY_LARGE_BUFFER + PAD];
_readColorsArray = new GLubyte[(GLOBAL_NORMALS_VERTICES_PER_VOXEL * REASONABLY_LARGE_BUFFER * 4) + PAD];
// Assemble the buffer data.
VoxelRenderAssembleData args;
args.buffer = _readVerticesArray;
args.colorBuffer = _readColorsArray;
assert(args.buffer != NULL && args.colorBuffer != NULL);
args.leafCount = 0;
args.lastBufferSegmentStart = 0;
args.idxIds = _vboIndicesIds;
args.idxBuffs = _vboIndices;
args.vtxID = _vboVerticesID;
args.colorID = _vboColorsID;
_systemTree.recurseTreeWithOperation(&VoxelRenderAssemblePerVoxel, &args);
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), 0);
glEnableVertexAttribArray(0);
//glVertexPointer(3, GL_FLOAT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(unsigned char) * 4, 0);
glEnableVertexAttribArray(1);
// Set up the shaders.
_vertexShader = glCreateShader(GL_VERTEX_SHADER);
_geometryShader = glCreateShader(GL_GEOMETRY_SHADER);
_pixelShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(_vertexShader, 1, &simpleVtxShaderSrc, &simpleVtxShaderLen);
glShaderSource(_geometryShader, 1, &simpleGeomShaderSrc, &simpleGeomShaderLen);
glShaderSource(_pixelShader, 1, &simpleFragShaderSrc, &simpleFragShaderLen);
GLchar shaderLog[1000];
GLsizei shaderLogLength;
GLint compiled;
glCompileShaderARB(&_vertexShader);
glGetShaderInfoLog(_vertexShader, 1000, &shaderLogLength, shaderLog);
if (shaderLog[0] != 0) DebugPrint("Shaderlog v :\n %s\n", shaderLog);
glCompileShaderARB(&_geometryShader);
glGetShaderInfoLog(_geometryShader, 1000, &shaderLogLength, shaderLog);
if (shaderLog[0] != 0) DebugPrint("Shaderlog g :\n %s\n", shaderLog);
glCompileShaderARB(&_pixelShader);
glGetShaderInfoLog(_pixelShader, 51000, &shaderLogLength, shaderLog);
if (shaderLog[0] != 0) DebugPrint("Shaderlog p :\n %s\n", shaderLog);
_linkProgram = glCreateProgram();
glAttachShader(_linkProgram, _vertexShader);
glAttachShader(_linkProgram, _geometryShader);
glAttachShader(_linkProgram, _pixelShader);
glLinkProgram(_linkProgram);
GLint linked;
glGetProgramiv(_linkProgram, GL_LINK_STATUS, &linked);
if (!linked) DebugPrint("Linking failed! %d\n", linked);
_voxelOptRenderInitialized = true;
}
void SvoViewer::RenderTreeSystemAsVoxels()
{
if (!_voxelOptRenderInitialized) return; // What the??
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
// for performance, enable backface culling
glEnable(GL_CULL_FACE);
// disable specular lighting for ground and voxels
glMaterialfv(GL_FRONT, GL_SPECULAR, NO_SPECULAR_COLOR);
setupWorldLight();
glNormal3f(0,1.0f,0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesIds[CUBE_TOP]);
glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _leafCount - 1,
INDICES_PER_FACE * _leafCount, GL_UNSIGNED_INT, 0);
glNormal3f(0,-1.0f,0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesIds[CUBE_BOTTOM]);
glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _leafCount - 1,
INDICES_PER_FACE * _leafCount, GL_UNSIGNED_INT, 0);
glNormal3f(-1.0f,0,0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesIds[CUBE_LEFT]);
glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _leafCount - 1,
INDICES_PER_FACE * _leafCount, GL_UNSIGNED_INT, 0);
glNormal3f(1.0f,0,0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesIds[CUBE_RIGHT]);
glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _leafCount - 1,
INDICES_PER_FACE * _leafCount, GL_UNSIGNED_INT, 0);
glNormal3f(0,0,-1.0f);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesIds[CUBE_FRONT]);
glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _leafCount - 1,
INDICES_PER_FACE * _leafCount, GL_UNSIGNED_INT, 0);
glNormal3f(0,0,1.0f);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesIds[CUBE_BACK]);
glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _leafCount - 1,
INDICES_PER_FACE * _leafCount, GL_UNSIGNED_INT, 0);
//glDisable(GL_CULL_FACE);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void SvoViewer::StopUsingVoxelRenderSystem()
{
if (!_voxelOptRenderInitialized) return;
//for (int i = 0; i < NUM_CUBE_FACES; i++) delete [] _vboIndices[i];
//delete [] _readVerticesArray;
//delete [] _readColorsArray;
glDeleteBuffers(1, &_vboVerticesID);
glDeleteBuffers(1, &_vboColorsID);
glDeleteBuffers(NUM_CUBE_FACES, _vboIndicesIds);
_voxelOptRenderInitialized = false;
}
////////////////////////////////////////////////////
// NOTE - the voxel tree data structure doesn't currently have neighbor information encoded in any convenient searchable
// way. So here I'm using a HUGE hack to compute repeated faces in the data structure. I 1) compute the barycentric coordinates
// of every triangle for every leaf face. 2) Compute the center point for the triangle. (edit: I removed the barycentric part after some testing),
// 3) That center point becomes a ID for that tri.
// 4) If that ID is added more than once, discard this triangle, as it must be an internal. External triangles will only be added once.
// SUBNOTE - I have NO idea how the tree traversal is actually happening as I haven't looked into its usage pattern. If traversal order was intelligent,
// static and the pattern was predetermined, traversal could actually generate a mostly sorted list automatically. Because that isn't guaranteed
// here, its uglier.
// The
bool SvoViewer::TrackVisibleFaces(OctreeElement* node, void* extraData)
{
VoxelTreeElement* voxel = (VoxelTreeElement*)node;
VisibleFacesData* args = (VisibleFacesData*)extraData;
if (!node->isLeaf()) return true;
AABox box = voxel->getAABox();
glm::vec3 p0, p1, p2, p3, hackCenterVal;
glm::vec3 cubeVerts[GLOBAL_NORMALS_VERTICES_PER_VOXEL];
for (int i = 0; i < GLOBAL_NORMALS_VERTICES_PER_VOXEL; i++) // Cache as aabox reconstructs with every call.
cubeVerts[i] = box.getVertex((BoxVertex)i);
for (int i = 0; i < NUM_CUBE_FACES; i++)
{
p0 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][0]];
p1 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][1]];
p2 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][2]];
p3 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][3]];
hackCenterVal = computeQuickAndDirtyQuadCenter(p0, p1, p2, p3);
args->ptList[args->count] += hackCenterVal;
args->count++;
}
return true;
}
// Do a binary search on the vector list.
int SvoViewer::binVecSearch(glm::vec3 searchVal, glm::vec3* list, int count, int * found)
{
int key = count / 2;
int lowerbound = 0;
int upperbound = count-1;
int lastkey = -1;
while (lastkey != key)
{
lastkey = key;
int ret = ptCloseEnough(&searchVal, &list[key]);
if (ret == 0) { *found = 1; return key; }
if (lowerbound == key || upperbound == key) { *found = 0; return key;} // closest we can get!
if (ret < 0)
{
upperbound = key;
if (key == lowerbound+1) key = lowerbound;
else key = ((upperbound - lowerbound) / 2) + lowerbound;
}
else if (ret > 0)
{
lowerbound = key;
if (key == upperbound-1) key = upperbound;
else key = ((upperbound - lowerbound) /2) + lowerbound;
}
}
*found = 0;
return key;
}
struct VoxelOptRenderAssembleData
{
Vertex* vtxBuffer;
GLuint* idxBuffer;
int vtxCount;
int idxCount;
AABoundingVolume bounds;
int faceCenterCount;
glm::vec3 * faceCenterList;
int discardedCount;
int elemCount;
};
bool SvoViewer::VoxelOptRenderAssemblePerVoxel(OctreeElement* node, void* extraData)
{
VoxelTreeElement* voxel = (VoxelTreeElement*)node;
VoxelOptRenderAssembleData* args = (VoxelOptRenderAssembleData*)extraData;
if (!node->isLeaf()) return true;
AABox box = voxel->getAABox();
glm::vec3 p0, p1, p2, p3, hackCenterVal;
glm::vec3 cubeVerts[GLOBAL_NORMALS_VERTICES_PER_VOXEL];
for (int i = 0; i < GLOBAL_NORMALS_VERTICES_PER_VOXEL; i++) // Cache, as aabox reconstructs with every call.
cubeVerts[i] = box.getVertex((BoxVertex)i);
bool doAddFace[NUM_CUBE_FACES] = {true, false, true, true, true, true}; // Cull bottom faces by default.
// Accumulate all the faces that need to be added.
for (int i = 0; i < NUM_CUBE_FACES; i++)
{
p0 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][0]];
p1 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][1]];
p2 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][2]];
p3 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][3]];
hackCenterVal = computeQuickAndDirtyQuadCenter(p0, p1, p2, p3);
// Search for this in the face center list
//glm::vec3 * foundVal = (glm::vec3*)bsearch(&hackCenterVal, args->faceCenterList, args->faceCenterCount, sizeof(glm::vec3), ptCompFunc);
int foundBVS = 0;
int idxIntoList = binVecSearch(hackCenterVal, args->faceCenterList, args->faceCenterCount, &foundBVS);
if (foundBVS == 0) { assert(0); continue; } // What the?
//if (foundVal == NULL) { assert(0); continue; } // What the?
//unsigned int idxIntoList = (foundVal - args->faceCenterList) / sizeof(glm::vec3);
assert(idxIntoList <= args->faceCenterCount-1);
// Now check face center list values that are immmediately adjacent to this value. If they're equal, don't add this face as
// another leaf voxel must contain this triangle too.
bool foundMatch = false;
if (idxIntoList != 0)
{
if (ptCloseEnough(&hackCenterVal, &args->faceCenterList[idxIntoList-1]) == 0) foundMatch = true;
}
if (idxIntoList != args->faceCenterCount-1 && foundMatch == false)
{
if (ptCloseEnough(&hackCenterVal, &args->faceCenterList[idxIntoList+1]) == 0) foundMatch = true;
}
if (foundMatch)
{
doAddFace[i] = false; // Remove.
args->discardedCount++;
}
}
#define VTX_NOT_USED 255
unsigned char vtxToAddMap[GLOBAL_NORMALS_VERTICES_PER_VOXEL]; // Map from vertex to actual position in the new vtx list.
memset(vtxToAddMap, VTX_NOT_USED, sizeof(vtxToAddMap));
// Figure out what vertices to add. NOTE - QUICK and dirty. easy opt - precalulate bit pattern for every face and just & it.
bool useVtx[GLOBAL_NORMALS_VERTICES_PER_VOXEL];
memset(useVtx, 0, sizeof(useVtx));
for ( int face = 0; face < NUM_CUBE_FACES; face++) // Vertex add order.
{
if (doAddFace[face])
{
for (int vOrder = 0; vOrder < 4; vOrder++)
{
useVtx[SvoViewerNames::cubeFaceVtxs[face][vOrder]] = true;
}
}
}
unsigned char vtxAddedCount = 0;
int baseVtxCount = args->vtxCount;
for (int i = 0; i < GLOBAL_NORMALS_VERTICES_PER_VOXEL; i++)
{
if (useVtx[i])
{
vtxToAddMap[i] = vtxAddedCount;
vtxAddedCount++;
args->vtxBuffer[args->vtxCount].position = cubeVerts[i];
args->vtxBuffer[args->vtxCount].position *= 100;
args->vtxBuffer[args->vtxCount].position.x -= 25;
args->vtxBuffer[args->vtxCount].position.y -= 4;
args->vtxBuffer[args->vtxCount].color[0] = voxel->getTrueColor()[0];
args->vtxBuffer[args->vtxCount].color[1] = voxel->getTrueColor()[1];
args->vtxBuffer[args->vtxCount].color[2] = voxel->getTrueColor()[2];
args->vtxBuffer[args->vtxCount].color[3] = 1;
args->bounds.AddToSet(args->vtxBuffer[args->vtxCount].position);
args->vtxCount++;
}
}
// Assemble the index lists.
for ( int face = 0; face < NUM_CUBE_FACES; face++)
{
if (doAddFace[face])
{
for (int i = 0; i < 6; i++)
{
// index is : current vtx + offset in map table
args->idxBuffer[args->idxCount] = baseVtxCount + vtxToAddMap[ SvoViewerNames::cubeFaceIndices[face][i] ];
args->idxCount++;
args->elemCount += 2; // Add 2 elements per face
}
}
}
return true;
}
void SvoViewer::InitializeVoxelOptRenderSystem()
{
if (_voxelOptRenderInitialized) return;
_numSegments = 0;
_totalPossibleElems = 0;
memset(_numChildNodeLeaves, 0, sizeof(_numChildNodeLeaves));
memset(_segmentNodeReferences, 0, sizeof(_segmentNodeReferences));
memset(_segmentBoundingVolumes, 0, sizeof(_segmentBoundingVolumes));
// Set up the segments. Find the number of leaves at each subtree.
OctreeElement * rootNode = _systemTree.getRoot();
OctreeElement* node0fromRoot = rootNode->getChildAtIndex(0); // ALL the interesting data for our test SVO is in this node! HACK!!
int rootNumChildren = rootNode->getChildCount();
for (int i = 0; i < NUMBER_OF_CHILDREN; i++)
{
OctreeElement* childNode1stOrder = node0fromRoot->getChildAtIndex(i);
if (childNode1stOrder == NULL) continue;
// Grab 2nd order nodes for better separation. At some point, this would need to be done intelligently.
for (int j = 0; j < NUMBER_OF_CHILDREN; j++)
{
OctreeElement* childNode2ndOrder = childNode1stOrder->getChildAtIndex(j);
if (childNode2ndOrder == NULL) continue;
int num2ndOrderChildren = childNode2ndOrder->getChildCount();
// Figure out how populated this child is.
FindNumLeavesData data;
data.numLeaves = 0;
_systemTree.recurseNodeWithOperation(childNode2ndOrder, &FindNumLeaves, &data, 0);
// Some of these nodes have a single leaf. Ignore for the moment. We really only want the big segments in this test. Add this back in at some point.
if (data.numLeaves > 1)
{
_numChildNodeLeaves[_numSegments] = data.numLeaves;
_segmentNodeReferences[_numSegments] = childNode2ndOrder;
_totalPossibleElems += data.numLeaves * NUM_CUBE_FACES * 2;
_numSegments++;
DebugPrint("child node %d %d has %d leaves and %d children itself\n", i, j, data.numLeaves, childNode2ndOrder->getChildCount());
if (_numSegments >= MAX_NUM_OCTREE_PARTITIONS ) { DebugPrint("Out of segment space??? What the?\n"); break; }
}
}
if (_numSegments >= MAX_NUM_OCTREE_PARTITIONS ) { DebugPrint("Out of segment space??? What the?\n"); break; }
}
// Set up the VBO's. Once for every partition we stored.
for (int i = 0; i < _numSegments; i++)
{
// compute the visible set of this segment first.
glm::vec3* faceCenters = new glm::vec3[NUM_CUBE_FACES *_numChildNodeLeaves[i]];
VisibleFacesData visFaceData;
visFaceData.ptList = faceCenters;
visFaceData.count = 0;
_systemTree.recurseNodeWithOperation(_segmentNodeReferences[i], &TrackVisibleFaces, &visFaceData, 0);
// Now there's a list of all the face centers. Sort it.
qsort(faceCenters, visFaceData.count, sizeof(glm::vec3), ptCompFunc);
DebugPrint("Creating VBO's. Sorted neighbor list %d\n", i);
_readVertexStructs = new Vertex[GLOBAL_NORMALS_VERTICES_PER_VOXEL * _numChildNodeLeaves[i]];
_readIndicesArray = new GLuint[NUM_CUBE_FACES * 2 * 3 * _numChildNodeLeaves[i]];
VoxelOptRenderAssembleData args;
args.vtxBuffer = _readVertexStructs;
args.idxBuffer = _readIndicesArray;
args.vtxCount = 0;
args.idxCount = 0;
args.faceCenterCount = visFaceData.count;
args.faceCenterList = visFaceData.ptList;
args.discardedCount = 0;
args.elemCount = 0;
_systemTree.recurseNodeWithOperation(_segmentNodeReferences[i], &VoxelOptRenderAssemblePerVoxel, &args, 0);
_segmentBoundingVolumes[i] = args.bounds;
_segmentElemCount[i] = args.elemCount;
SetupGlVBO(&_vboOVerticesIds[i], args.vtxCount * sizeof(Vertex), GL_ARRAY_BUFFER, GL_STATIC_DRAW, _readVertexStructs);
SetupGlVBO(&_vboOIndicesIds[i], args.idxCount * sizeof(GLuint), GL_ARRAY_BUFFER, GL_STATIC_DRAW, _readIndicesArray);
DebugPrint("Partition %d, vertices %d, indices %d, discarded %d\n", i, args.vtxCount, args.idxCount, args.discardedCount);
delete [] _readVertexStructs;
delete [] _readIndicesArray;
delete [] faceCenters;
}
_voxelOptRenderInitialized = true;
}
void SvoViewer::RenderTreeSystemAsOptVoxels()
{
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
// disable specular lighting for ground and voxels
glMaterialfv(GL_FRONT, GL_SPECULAR, NO_SPECULAR_COLOR);
setupWorldLight();
_numElemsDrawn = 0;
for (int i = 0; i < _numSegments; i++)
{
if (_displayOnlyPartition == i || _displayOnlyPartition == NO_PARTITION )
{
if (isVisibleBV(&_segmentBoundingVolumes[i], &_myCamera, &_viewFrustum)) // Add aggressive LOD check here.
{
glBindBuffer(GL_ARRAY_BUFFER, _vboOVerticesIds[i]);
glVertexAttribPointer(ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,position));
glEnableVertexAttribArray(ATTRIB_POSITION);
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,color));
glEnableVertexAttribArray(ATTRIB_COLOR);
//glVertexPointer(3, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex,position));
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), (void*)offsetof(Vertex,color));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboOIndicesIds[i]);
glDrawElements(GL_TRIANGLES, _segmentElemCount[i], GL_UNSIGNED_INT, NULL);
_numElemsDrawn += _segmentElemCount[i];
/*glColor3f(.8f, 0.0f, 0.0f);
glBegin(GL_POINTS);
for (int j = 0; j < GLOBAL_NORMALS_VERTICES_PER_VOXEL; j++)
{
glm::vec3 pt = _segmentBoundingVolumes[i].getCorner((BoxVertex)j);
glVertex3f(pt.x, pt.y, pt.z);
}
glEnd();*/
}
}
}
glDisableVertexAttribArray(ATTRIB_POSITION);
glDisableVertexAttribArray(ATTRIB_COLOR);
}
void SvoViewer::StopUsingVoxelOptRenderSystem()
{
glDisableVertexAttribArray(ATTRIB_POSITION);
glDisableVertexAttribArray(ATTRIB_COLOR);
glDisable(GL_LIGHTING);
}

0
SvoViewer/src/Render.h Executable file
View file

375
SvoViewer/src/Render2.cpp Executable file
View file

@ -0,0 +1,375 @@
#include "svoviewer.h"
/////////////////////////////////////////////////////////////////////////////
// Helper functions
inline glm::vec3 SvoViewer::computeQuickAndDirtyQuadCenter(glm::vec3 p0, glm::vec3 p1, glm::vec3 p2, glm::vec3 p3)
{
glm::vec3 avg = p0 + p1 + p2 + p3;
avg /= 4.0f;
return avg;
}
// Precision dependent hack. After debugging - change this to a magnitude function.
// simple version for clarity/debugging.
int SvoViewer::ptCompFunc(const void * a, const void * b)
{
if ((*(glm::vec3*)a).x < (*(glm::vec3*)b).x) return -1;
else if ((*(glm::vec3*)a).x > (*(glm::vec3*)b).x) return 1;
if ((*(glm::vec3*)a).y < (*(glm::vec3*)b).y) return -1;
else if ((*(glm::vec3*)a).y > (*(glm::vec3*)b).y) return 1;
if ((*(glm::vec3*)a).z < (*(glm::vec3*)b).z) return -1;
else if ((*(glm::vec3*)a).z > (*(glm::vec3*)b).z) return 1;
return 0;
}
//#define PRECISION_ERR .00000001
#define PRECISION_ERR .00001
// aggressive mode
//(0.00097656250 /2) // Space of smallest voxel should define our error bounds here. Test this if time allows.
int SvoViewer::ptCloseEnough(const void * a, const void * b)
{
glm::vec3 diffVec = (*(glm::vec3*)a) - (*(glm::vec3*)b);
if (fabs(diffVec.x) < PRECISION_ERR && fabs(diffVec.y) < PRECISION_ERR && fabs(diffVec.z) < PRECISION_ERR) return 0;
//float len = diffVec.length();
//if (len < PRECISION_ERR) return 0;
if ((*(glm::vec3*)a).x < (*(glm::vec3*)b).x) return -1;
else if ((*(glm::vec3*)a).x > (*(glm::vec3*)b).x) return 1;
if ((*(glm::vec3*)a).y < (*(glm::vec3*)b).y) return -1;
else if ((*(glm::vec3*)a).y > (*(glm::vec3*)b).y) return 1;
if ((*(glm::vec3*)a).z < (*(glm::vec3*)b).z) return -1;
else if ((*(glm::vec3*)a).z > (*(glm::vec3*)b).z) return 1;
return 0;
}
// return parameterized intersection in t.
bool SvoViewer::parameterizedRayPlaneIntersection(const glm::vec3 origin, const glm::vec3 direction, const glm::vec3 planePt, const glm::vec3 planeNormal, float *t)
{
float denom = glm::dot(direction, planeNormal);
if (denom < PRECISION_ERR) return false;
glm::vec3 p010 = planePt - origin;
*t = glm::dot(p010, planeNormal) / denom;
return true;
}
/////////////////////////////////////////////////////////////////////////////
// 2nd stab at optimizing this. Cull back faces more aggressively.
struct VoxelOpt2RenderAssembleData
{
Vertex* vtxBuffer;
VoxelDimIdxSet* idxSet;
int vtxCount;
int faceCenterCount;
glm::vec3 * faceCenterList;
int discardedCount;
};
bool SvoViewer::VoxelOpt2RenderAssemblePerVoxel(OctreeElement* node, void* extraData)
{
VoxelTreeElement* voxel = (VoxelTreeElement*)node;
VoxelOpt2RenderAssembleData* args = (VoxelOpt2RenderAssembleData*)extraData;
if (!node->isLeaf()) return true;
AABox box = voxel->getAABox();
glm::vec3 p0, p1, p2, p3, hackCenterVal;
glm::vec3 cubeVerts[GLOBAL_NORMALS_VERTICES_PER_VOXEL];
for (int i = 0; i < GLOBAL_NORMALS_VERTICES_PER_VOXEL; i++) // Cache, as aabox reconstructs with every call.
cubeVerts[i] = box.getVertex((BoxVertex)i);
bool doAddFace[NUM_CUBE_FACES] = {true, false, true, true, true, true}; // Cull bottom faces by default.
// Accumulate all the faces that need to be added.
for (int i = 0; i < NUM_CUBE_FACES; i++)
{
p0 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][0]];
p1 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][1]];
p2 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][2]];
p3 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][3]];
hackCenterVal = computeQuickAndDirtyQuadCenter(p0, p1, p2, p3);
// Search for this in the face center list
//glm::vec3 * foundVal = (glm::vec3*)bsearch(&hackCenterVal, args->faceCenterList, args->faceCenterCount, sizeof(glm::vec3), ptCompFunc);
// BSEARCH FAILING! What the? wrote my own index approximate version.
int foundBVS = 0;
int idxIntoList = binVecSearch(hackCenterVal, args->faceCenterList, args->faceCenterCount, &foundBVS);
if (foundBVS == 0) { assert(0); continue; } // What the?
assert(idxIntoList <= args->faceCenterCount-1);
// Now check face center list values that are immmediately adjacent to this value. If they're equal, don't add this face as
// another leaf voxel must contain this triangle too.
bool foundMatch = false;
if (idxIntoList != 0)
{
if (ptCloseEnough(&hackCenterVal, &args->faceCenterList[idxIntoList-1]) == 0) foundMatch = true;
}
if (idxIntoList != args->faceCenterCount-1 && foundMatch == false)
{
if (ptCloseEnough(&hackCenterVal, &args->faceCenterList[idxIntoList+1]) == 0) foundMatch = true;
}
if (foundMatch)
{
doAddFace[i] = false; // Remove.
args->discardedCount++;
}
}
#define VTX_NOT_USED 255
unsigned char vtxToAddMap[GLOBAL_NORMALS_VERTICES_PER_VOXEL]; // Map from vertex to actual position in the new vtx list.
memset(vtxToAddMap, VTX_NOT_USED, sizeof(vtxToAddMap));
// Figure out what vertices to add. NOTE - QUICK and dirty. easy opt - precalulate bit pattern for every face and just & it.
bool useVtx[GLOBAL_NORMALS_VERTICES_PER_VOXEL];
memset(useVtx, 0, sizeof(useVtx));
for ( int face = 0; face < NUM_CUBE_FACES; face++) // Vertex add order.
{
if (doAddFace[face])
{
for (int vOrder = 0; vOrder < 4; vOrder++)
{
useVtx[SvoViewerNames::cubeFaceVtxs[face][vOrder]] = true;
}
}
}
unsigned char vtxAddedCount = 0;
int baseVtxCount = args->vtxCount;
for (int i = 0; i < GLOBAL_NORMALS_VERTICES_PER_VOXEL; i++)
{
if (useVtx[i])
{
vtxToAddMap[i] = vtxAddedCount;
vtxAddedCount++;
args->vtxBuffer[args->vtxCount].position = cubeVerts[i];
args->vtxBuffer[args->vtxCount].position *= 100;
args->vtxBuffer[args->vtxCount].position.x -= 25;
args->vtxBuffer[args->vtxCount].position.y -= 4;
args->vtxBuffer[args->vtxCount].color[0] = voxel->getTrueColor()[0];
args->vtxBuffer[args->vtxCount].color[1] = voxel->getTrueColor()[1];
args->vtxBuffer[args->vtxCount].color[2] = voxel->getTrueColor()[2];
args->vtxBuffer[args->vtxCount].color[3] = 1;
cubeVerts[i] = args->vtxBuffer[args->vtxCount].position;
args->vtxCount++;
}
}
// Assemble the index lists.
for ( int face = 0; face < NUM_CUBE_FACES; face++)
{
if (doAddFace[face])
{
for (int i = 0; i < 6; i++) // 2 * 3 triangles.
{
args->idxSet->idxBuff[face][args->idxSet->idxCount[face]] = baseVtxCount + vtxToAddMap[ SvoViewerNames::cubeFaceIndices[face][i] ];
args->idxSet->idxCount[face]++;
}
for (int i = 0; i < 4; i++)
{
args->idxSet->bounds[face].AddToSet( cubeVerts[SvoViewerNames::cubeFaceVtxs[face][i]] );
}
args->idxSet->elemCount[face] += 2;
}
}
return true;
}
void SvoViewer::InitializeVoxelOpt2RenderSystem()
{
quint64 startInit = usecTimestampNow();
if (_voxelOptRenderInitialized) return;
_numSegments = 0;
_totalPossibleElems = 0;
memset(_numChildNodeLeaves, 0, sizeof(_numChildNodeLeaves));
memset(_segmentNodeReferences, 0, sizeof(_segmentNodeReferences));
// Set up the segments. Find the number of leaves at each subtree.
OctreeElement * rootNode = _systemTree.getRoot();
OctreeElement* node0fromRoot = rootNode->getChildAtIndex(0); // ALL the interesting data for our test SVO is in this node! HACK!!
int rootNumChildren = rootNode->getChildCount();
for (int i = 0; i < NUMBER_OF_CHILDREN; i++)
{
OctreeElement* childNode1stOrder = node0fromRoot->getChildAtIndex(i);
if (childNode1stOrder == NULL) continue;
// Grab 2nd order nodes for better separation. At some point, this would need to be done intelligently.
for (int j = 0; j < NUMBER_OF_CHILDREN; j++)
{
OctreeElement* childNode2ndOrder = childNode1stOrder->getChildAtIndex(j);
if (childNode2ndOrder == NULL) continue;
int num2ndOrderChildren = childNode2ndOrder->getChildCount();
// Figure out how populated this child is.
FindNumLeavesData data;
data.numLeaves = 0;
_systemTree.recurseNodeWithOperation(childNode2ndOrder, &FindNumLeaves, &data, 0);
// Some of these nodes have a single leaf. Ignore for the moment. We really only want the big segments in this test. Add this back in at some point.
if (data.numLeaves > 1)
{
_numChildNodeLeaves[_numSegments] = data.numLeaves;
_segmentNodeReferences[_numSegments] = childNode2ndOrder;
_totalPossibleElems += data.numLeaves * NUM_CUBE_FACES * 2;
_numSegments++;
DebugPrint("child node %d %d has %d leaves and %d children itself\n", i, j, data.numLeaves, childNode2ndOrder->getChildCount());
if (_numSegments >= MAX_NUM_OCTREE_PARTITIONS ) { DebugPrint("Out of segment space??? What the?\n"); break; }
}
}
if (_numSegments >= MAX_NUM_OCTREE_PARTITIONS ) { DebugPrint("Out of segment space??? What the?\n"); break; }
}
// Set up the VBO's. Once for every partition we stored.
for (int i = 0; i < _numSegments; i++)
{
// compute the visible set of this segment first.
glm::vec3* faceCenters = new glm::vec3[NUM_CUBE_FACES *_numChildNodeLeaves[i]];
VisibleFacesData visFaceData;
visFaceData.ptList = faceCenters;
visFaceData.count = 0;
_systemTree.recurseNodeWithOperation(_segmentNodeReferences[i], &TrackVisibleFaces, &visFaceData, 0);
// Now there's a list of all the face centers. Sort it.
qsort(faceCenters, visFaceData.count, sizeof(glm::vec3), ptCompFunc);
DebugPrint("Creating VBO's. Sorted neighbor list %d\n", i);
_readVertexStructs = new Vertex[GLOBAL_NORMALS_VERTICES_PER_VOXEL * _numChildNodeLeaves[i]];
memset(&_segmentIdxBuffers[i], 0, sizeof(VoxelDimIdxSet)); // Don't do it this way if we ever use a vtable for AABoundingVolumes!
for (int k = 0; k < NUM_CUBE_FACES; k++)
{
_segmentIdxBuffers[i].idxBuff[k] = new GLuint[2 * 3 * _numChildNodeLeaves[i]];
assert(_segmentIdxBuffers[i].idxBuff[k] != NULL);
}
VoxelOpt2RenderAssembleData args;
args.vtxBuffer = _readVertexStructs;
args.vtxCount = 0;
args.faceCenterCount = visFaceData.count;
args.faceCenterList = visFaceData.ptList;
args.discardedCount = 0;
args.idxSet = &_segmentIdxBuffers[i];
_systemTree.recurseNodeWithOperation(_segmentNodeReferences[i], &VoxelOpt2RenderAssemblePerVoxel, &args, 0);
SetupGlVBO(&_vboOVerticesIds[i], args.vtxCount * sizeof(Vertex), GL_ARRAY_BUFFER, GL_STATIC_DRAW, _readVertexStructs);
unsigned int idxCount = 0;
for (int k = 0; k < NUM_CUBE_FACES; k++)
{
SetupGlVBO(&_segmentIdxBuffers[i].idxIds[k], _segmentIdxBuffers[i].idxCount[k] * sizeof(GLuint),
GL_ARRAY_BUFFER, GL_STATIC_DRAW, _segmentIdxBuffers[i].idxBuff[k]);
idxCount += _segmentIdxBuffers[i].idxCount[k];
_segmentIdxBuffers[i].bounds[k].setIsSingleDirection(true, SvoViewerNames::faceNormals[k]);
}
DebugPrint("Partition %d, vertices %d, indices %d, discarded %d\n", i, args.vtxCount, idxCount, args.discardedCount);
delete [] _readVertexStructs;
//delete [] _readIndicesArray;
delete [] faceCenters;
for (int k = 0; k < NUM_CUBE_FACES; k++) if (_segmentIdxBuffers[i].idxBuff[k] != NULL) delete [] _segmentIdxBuffers[i].idxBuff[k];
}
_voxelOptRenderInitialized = true;
UpdateOpt2BVFaceVisibility();
quint64 endInit = usecTimestampNow();
quint64 elapsed = endInit - startInit;
qDebug() << "init elapsed:" << ((float)elapsed / (float)1000000.0f) << "seconds";
}
void SvoViewer::RenderTreeSystemAsOpt2Voxels()
{
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
// disable specular lighting for ground and voxels
glMaterialfv(GL_FRONT, GL_SPECULAR, NO_SPECULAR_COLOR);
setupWorldLight();
_numElemsDrawn = 0;
for (int i = 0; i < _numSegments; i++)
{
if (_displayOnlyPartition == i || _displayOnlyPartition == NO_PARTITION )
{
glBindBuffer(GL_ARRAY_BUFFER, _vboOVerticesIds[i]);
glVertexAttribPointer(ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,position));
glEnableVertexAttribArray(ATTRIB_POSITION);
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,color));
glEnableVertexAttribArray(ATTRIB_COLOR);
//glVertexPointer(3, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex,position));
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), (void*)offsetof(Vertex,color));
for (int j = 0; j < NUM_CUBE_FACES; j++)
{
// Add aggressive LOD check here.
if (_segmentIdxBuffers[i].visibleFace[j] || _useBoundingVolumes != true)
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _segmentIdxBuffers[i].idxIds[j]);//_vboOIndicesIds[i]);
glDrawElements(GL_TRIANGLES, _segmentIdxBuffers[i].elemCount[j]*3, GL_UNSIGNED_INT, NULL);
_numElemsDrawn += _segmentIdxBuffers[i].elemCount[j];
}
}
}
}
glDisableVertexAttribArray(ATTRIB_POSITION);
glDisableVertexAttribArray(ATTRIB_COLOR);
}
// special rules for single direction bv sets. Basically, we intersect a lookat ray from the camera with two opposite faces and discard
// the entire set of the face that is further away as it must be back facing.
void SvoViewer::UpdateOpt2BVFaceVisibility()
{
if (_currentShaderModel != RENDER_OPT_CULLED_POLYS || _voxelOptRenderInitialized != true ) return;
float faceParamVals[NUM_CUBE_FACES];
glm::vec3 pos = _myCamera.getPosition();
for (int i = 0; i < _numSegments; i++)
{
VoxelDimIdxSet* setPtr = &_segmentIdxBuffers[i];
// Fast cull check.
setPtr->visibleFace[0] = (_segmentIdxBuffers[i].bounds[0].within(pos.y, 1) >= 0) ? true : false;
setPtr->visibleFace[1] = (_segmentIdxBuffers[i].bounds[1].within(pos.y, 1) <= 0) ? true : false;
setPtr->visibleFace[2] = (_segmentIdxBuffers[i].bounds[2].within(pos.x, 0) >= 0) ? true : false;
setPtr->visibleFace[3] = (_segmentIdxBuffers[i].bounds[3].within(pos.x, 0) <= 0) ? true : false;
setPtr->visibleFace[4] = (_segmentIdxBuffers[i].bounds[4].within(pos.z, 2) <= 0) ? true : false;
setPtr->visibleFace[5] = (_segmentIdxBuffers[i].bounds[5].within(pos.z, 2) >= 0) ? true : false;
// Make sure its actually on the screen.
/*for (int j = 0; j < NUM_CUBE_FACES; j++)
{
if (setPtr->visibleFace[j])
{
if (visibleAngleSubtended(&_segmentIdxBuffers[i].bounds[j], &_myCamera, &_viewFrustum) <= 0)
setPtr->visibleFace[j] = false;
}
}*/
}
/*
for (int j = 0; j < NUM_CUBE_FACES; j++)
{
setPtr->visibleFace[i] = true;
AABoundingVolume* volume = &_segmentIdxBuffers[i].bounds[j];
glm::vec3 randomPlaneVtx = volume->getCorner((BoxVertex)SvoViewerNames::cubeFaceIndices[j][0]);
glm::vec3 raydir = randomPlaneVtx - pos;
rayder /= glm::length(raydir);
if (glm::dot(target, raydir) < 1) raydir *= -1;
if (!parameterizedRayPlaneIntersection(pos, raydir, randomPlaneVtx, SvoViewerNames::faceNormals[j], &faceParamVals[j]))
faceParamVals[j] = -1;
}
*/
}
void SvoViewer::StopUsingVoxelOpt2RenderSystem()
{
glDisableVertexAttribArray(ATTRIB_POSITION);
glDisableVertexAttribArray(ATTRIB_COLOR);
glDisable(GL_LIGHTING);
}

BIN
SvoViewer/src/SvoViewer.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

2
SvoViewer/src/SvoViewer.rc Executable file
View file

@ -0,0 +1,2 @@
IDI_ICON1 ICON DISCARDABLE "SvoViewer.ico"

67
SvoViewer/src/globals.cpp Executable file
View file

@ -0,0 +1,67 @@
#include "globals.h"
const GLfloat WHITE_SPECULAR_COLOR[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat NO_SPECULAR_COLOR[] = { 0.0f, 0.0f, 0.0f, 1.0f };
namespace SvoViewerNames // avoid conflicts with voxelSystem namespace objects
{
float identityVerticesGlobalNormals[] = { 0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1 };
float identityVertices[] = { 0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1, //0-7
0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1, //8-15
0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1 }; // 16-23
GLfloat identityNormals[] = { 0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1,
0,0,+1, 0,0,+1, 0,0,+1, 0,0,+1,
0,-1,0, 0,-1,0, 0,+1,0, 0,+1,0,
0,-1,0, 0,-1,0, 0,+1,0, 0,+1,0,
-1,0,0, +1,0,0, +1,0,0, -1,0,0,
-1,0,0, +1,0,0, +1,0,0, -1,0,0 };
GLubyte identityIndices[] = { 0,2,1, 0,3,2, // Z-
8,9,13, 8,13,12, // Y-
16,23,19, 16,20,23, // X-
17,18,22, 17,22,21, // X+
10,11,15, 10,15,14, // Y+
4,5,6, 4,6,7 }; // Z+
GLubyte identityIndicesTop[] = { 2, 3, 7, 2, 7, 6 };
GLubyte identityIndicesBottom[] = { 0, 1, 5, 0, 5, 4 };
GLubyte identityIndicesLeft[] = { 0, 7, 3, 0, 4, 7 };
GLubyte identityIndicesRight[] = { 1, 2, 6, 1, 6, 5 };
GLubyte identityIndicesFront[] = { 0, 2, 1, 0, 3, 2 };
GLubyte identityIndicesBack[] = { 4, 5, 6, 4, 6, 7 };
GLubyte cubeFaceIndices[][2*3] =
{
{ 2, 3, 7, 2, 7, 6 },
{ 0, 1, 5, 0, 5, 4 },
{ 0, 7, 3, 0, 4, 7 },
{ 1, 2, 6, 1, 6, 5 },
{ 0, 2, 1, 0, 3, 2 },
{ 4, 5, 6, 4, 6, 7 }
};
GLubyte cubeFaceVtxs[6][4] =
{
{ 2, 3, 6, 7 },
{ 0, 1, 4, 5 },
{ 0, 3, 4, 7 },
{ 1, 2, 5, 6 },
{ 0, 1, 2, 3 },
{ 4, 5, 6, 7 }
};
glm::vec3 faceNormals[6] =
{
glm::vec3(0.0, 1.0, 0.0),
glm::vec3(0.0, -1.0, 0.0),
glm::vec3(-1.0, 0.0, 0.0),
glm::vec3(1.0, 0.0, 0.0),
glm::vec3(0.0, 0.0, 1.0),
glm::vec3(0.0, 0.0, -1.0)
};
} // SvoViewerNames

33
SvoViewer/src/globals.h Executable file
View file

@ -0,0 +1,33 @@
#pragma once
#include "SvoViewerConfig.h"
#include <QtWidgets/QMainWindow>
#include <QDesktopWidget>
#include <QOpenGLFramebufferObject>
//#include <GLCanvas>
//#include "ui_svoviewer.h"
#include <ViewFrustum.h>
#include <VoxelTree.h>
#include <SharedUtil.h>
//#include <VoxelShader.h>
//#include <VoxelSystem.h>
//#include <PointShader.h>
#include <OctreeRenderer.h>
#include "Camera.h"
extern const GLfloat WHITE_SPECULAR_COLOR[];
extern const GLfloat NO_SPECULAR_COLOR[];
namespace SvoViewerNames // avoid conflicts with voxelSystem namespace objects
{
extern GLubyte cubeFaceIndices[][2*3];
extern GLubyte cubeFaceVtxs[6][4];
extern glm::vec3 faceNormals[6];
}

8
SvoViewer/src/main.cpp Executable file
View file

@ -0,0 +1,8 @@
#include "svoviewer.h"
#include <QtWidgets/QApplication>
int main(int argc, char *argv[])
{
SvoViewer svoV(argc, argv);
return svoV.exec();
}

644
SvoViewer/src/svoviewer.cpp Executable file
View file

@ -0,0 +1,644 @@
#include "svoviewer.h"
#include "GLCanvas.h"
#include <cstdio>
#include <QDesktopWidget>
#include <QOpenGLFramebufferObject>
#include <qtimer.h>
#include <QKeyEvent>
#include <glm/gtx/component_wise.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/vector_angle.hpp>
const int IDLE_SIMULATE_MSECS = 1; // How often should call simulate and other stuff
// in the idle loop? (60 FPS is default)
// Empirically, I found 5 to be about 60fps
static QTimer* idleTimer = NULL;
const ViewFrustumOffset DEFAULT_FRUSTUM_OFFSET = {-135.0f, 0.0f, 0.0f, 25.0f, 0.0f};
SvoViewer * _globalSvoViewerObj; // Hack :: var to store global pointer since this wasn't designed to work with the interface nodelist.
SvoViewer::SvoViewer(int& argc, char** argv, QWidget *parent)
: QApplication(argc, argv),
_window(new QMainWindow(desktop())),
_glWidget(new GLCanvas()),
_width(1280),
_height(720),
_pixelCount(1280*720),
_frameCount(0),
_leafCount(0),
_nodeCount(0),
_fps(0.0),
_lastTimeFpsUpdated(0),
_lastTimeFrameUpdated(0),
_ptRenderInitialized(false),
_voxelRenderInitialized(false),
_voxelOptRenderInitialized(false),
_voxelOpt2RenderInitialized(false),
_vertexShader(0),
_pixelShader(0),
_geometryShader(0),
_maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM),
_voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
_boundaryLevelAdjust(0),
_viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET),
_fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES),
_useVoxelTextures(false),
_pointVertices(NULL),
_pointVerticesCount(0),
//_vboShaderData(NULL),
_mousePressed(false),
_pitch(0),
_yaw(0),
_roll(0),
_numSegments(0),
_displayOnlyPartition(NO_PARTITION),
_totalPossibleElems(0),
_numElemsDrawn(0),
_useBoundingVolumes(true)
{
gettimeofday(&_applicationStartupTime, NULL);
_appStartTickCount = usecTimestampNow();
_globalSvoViewerObj = this;
//ui.setupUi(this);
_window->setWindowTitle("SvoViewer");
_window->resize(1280,720);
_window->setCentralWidget(_glWidget);
DebugPrint("Window initialized\n");
_window->setVisible(true);
_glWidget->setFocusPolicy(Qt::StrongFocus);
_glWidget->setFocus();
_glWidget->setMouseTracking(true);
_currentShaderModel = RENDER_OPT_CULLED_POLYS; //RENDER_OPT_POLYS;// RENDER_CLASSIC_POLYS; //RENDER_POINTS;//
memset(&_renderFlags, 0, sizeof(_renderFlags));
_renderFlags.useShadows = false;
// Load the scene.
// We want our corner voxels to be about 1/2 meter high, and our TREE_SCALE is in meters, so...
float voxelSize = 0.5f / TREE_SCALE;
DebugPrint("Reading SVO file\n");
//H:\highfidelity\hifi-19509\build\interface\resources\voxels1A.svo
const int MAX_PATH = 1024;
char svoFileToRead[MAX_PATH] = "./voxels10.svo"; //"H:\\highfidelity\\hifi-19509\\build\\interface\\resources\\voxels10.svo"
if (argc > 1) strcpy(svoFileToRead, argv[1]); // Command line is arg 0 by default.
//DebugPrint("Sizeof Octree element is %d\n", sizeof(OctreeElement));
quint64 readStart = usecTimestampNow();
bool readSucceeded = _systemTree.readFromSVOFile(svoFileToRead);
DebugPrint("Done reading SVO file : %f seconds : ", (float)(usecTimestampNow() - readStart) / 1000.0f);
readSucceeded ? DebugPrint("Succeeded\n") : DebugPrint("Failed\n");
// this should exist... we just loaded it...
if (_systemTree.getVoxelAt(voxelSize, 0, voxelSize, voxelSize)) {
DebugPrint("corner point voxelSize, 0, voxelSize exists...\n");
} else {
DebugPrint("corner point voxelSize, 0, voxelSize does not exists...\n");
}
_nodeCount = _systemTree.getOctreeElementsCount();
DebugPrint("Nodes after loading file: %ld nodes\n", _nodeCount);
// Initialize the display model we're using.
switch(_currentShaderModel)
{
case RENDER_NONE:
break;
case RENDER_POINTS:
_renderFlags.useVoxelShader = false;
_renderFlags.usePtShader = true;
if (!_ptRenderInitialized) InitializePointRenderSystem();
break;
case RENDER_CLASSIC_POLYS:
_renderFlags.useVoxelShader = true;
_renderFlags.usePtShader = false;
if (!_voxelRenderInitialized) InitializeVoxelRenderSystem();
break;
case RENDER_OPT_POLYS:
_renderFlags.useVoxelShader = true;
_renderFlags.usePtShader = false;
if (!_voxelOptRenderInitialized) InitializeVoxelOptRenderSystem();
break;
case RENDER_OPT_CULLED_POLYS:
_renderFlags.useVoxelShader = true;
_renderFlags.usePtShader = false;
if (!_voxelOpt2RenderInitialized) InitializeVoxelOpt2RenderSystem();
break;
}
}
SvoViewer::~SvoViewer()
{
//glDeleteBuffers(_pointVtxBuffer); // TODO :: add comprehensive cleanup!!
//glDeleteBuffers(1, _pointVtxBuffer);
//glDeleteBuffers(1, _pointColorBuffer);
if (_pointVertices) delete [] _pointVertices;
if (_pointColors) delete [] _pointColors;
//if (_vboShaderData) delete [] _vboShaderData;
StopUsingVoxelRenderSystem();
}
void SvoViewer::init() {
//_voxelShader.init();
//_pointShader.init();
_mouseX = _glWidget->width() / 2;
_mouseY = _glWidget->height() / 2;
QCursor::setPos(_mouseX, _mouseY);
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
_myCamera.setPosition(glm::vec3(0.0f, 0.0f, -5.0f));
_myCamera.setNearClip(0.01f);
//_myCamera.setFarClip(500.0f * TREE_SCALE);
_myCamera.setFarClip(TREE_SCALE);
_myCamera.setFieldOfView(_fieldOfView);
_myCamera.lookAt(glm::vec3(0.0f,0.0f,0.0f));
_myCamera.setAspectRatio((float)_width / (float) _height);
loadViewFrustum(_myCamera, _viewFrustum);
}
void SvoViewer::initializeGL()
{
int argc = 0;
glutInit(&argc, 0);
init();
#ifdef WIN32
GLenum err = glewInit();
if (GLEW_OK != err) {
/* Problem: glewInit failed, something is seriously wrong. */
DebugPrint("Error: %s\n", glewGetErrorString(err));
}
DebugPrint("Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
#endif
glViewport(0, 0, _width, _height);
glGetIntegerv(GL_VIEWPORT, _viewport);
//glEnable(GL_BLEND);
//glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glShadeModel(GL_SMOOTH);
//glEnable(GL_LIGHTING);
//glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
//// call our idle function whenever we can
idleTimer = new QTimer(this);
connect(idleTimer, SIGNAL(timeout()), SLOT(idle()));
idleTimer->start(0);
//_idleLoopStdev.reset();
_lastTimeFpsUpdated = _lastTimeFpsUpdated = usecTimestampNow();
float startupTime = (float)(_lastTimeFpsUpdated - _appStartTickCount) / 1000.0;
DebugPrint("Startup time: %4.2f seconds.", startupTime);
//// update before the first render
updateProjectionMatrix(_myCamera, true);
update(0.0f);
UpdateOpt2BVFaceVisibility();
}
void SvoViewer::idle() {
quint64 tc = usecTimestampNow();
// Only run simulation code if more than IDLE_SIMULATE_MSECS have passed since last time we ran
quint64 elapsed = tc - _lastTimeFrameUpdated;
//if (elapsed >= IDLE_SIMULATE_MSECS)
{
const float BIGGEST_DELTA_TIME_SECS = 0.25f;
update(glm::clamp((float)elapsed / 1000.f, 0.f, BIGGEST_DELTA_TIME_SECS));
_glWidget->updateGL();
// After finishing all of the above work, restart the idle timer, allowing 2ms to process events.
idleTimer->start(2);
_lastTimeFrameUpdated = tc;
}
}
void SvoViewer::paintGL()
{
glClearColor( 0.f, 0.f, .3f, 0.f );
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_LINE_SMOOTH);
glViewport(0, 0, _width, _height);
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glm::vec3 pos = _myCamera.getPosition();
glm::vec3 target = _myCamera.getTargetPosition();
updateProjectionMatrix(_myCamera, true);
// save matrix
glGetDoublev(GL_PROJECTION_MATRIX, _projectionMatrix);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
gluLookAt(pos.x, pos.y, pos.z, target.x, target.y, target.z, 0, 1, 0);
glGetDoublev(GL_MODELVIEW_MATRIX, _modelviewMatrix);
//glFrontFace(GL_CW);
glPointSize(5.0);
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_POINTS);
glVertex3f(0,0,0);
glVertex3f(1,0,0);
glVertex3f(-1,0,0);
glVertex3f(0,1,0);
glVertex3f(0,-1,0);
glVertex3f(0,0,1);
glVertex3f(0,0,-1);
glEnd();
switch(_currentShaderModel)
{
case RENDER_NONE:
// Nothing to do!
break;
case RENDER_POINTS:
RenderTreeSystemAsPoints();
break;
case RENDER_CLASSIC_POLYS:
RenderTreeSystemAsVoxels();
break;
case RENDER_OPT_POLYS:
RenderTreeSystemAsOptVoxels();
break;
case RENDER_OPT_CULLED_POLYS:
RenderTreeSystemAsOpt2Voxels();
break;
}
_frameCount++;
// Print out some statistics.
// Update every x seconds for more stability
quint64 tc = usecTimestampNow();
quint64 interval = tc - _lastTimeFpsUpdated;
#define FPS_UPDATE_TIME_INTERVAL 2
if (interval > 1000 * FPS_UPDATE_TIME_INTERVAL)
{
int numFrames = _frameCount - _lastTrackedFrameCount;
_fps = (float)numFrames / (float)(FPS_UPDATE_TIME_INTERVAL);
_lastTrackedFrameCount = _frameCount;
_lastTimeFpsUpdated = tc;
}
PrintToScreen((_width / 3) * 2, 10, "FPS is : %f", _fps );
PrintToScreen(10, 10, "Camera Pos : %f %f %f", pos.x, pos.y, pos.z);
PrintToScreen(10, 30, "Drawing %d of %d (%% %f) total elements", _numElemsDrawn, _totalPossibleElems, ((float)_numElemsDrawn / (float)_totalPossibleElems) * 100.0);
}
#define TEMP_STRING_BUFFER_MAX 1024
#define SHADOW_OFFSET 2
void SvoViewer::PrintToScreen(const int width, const int height, const char* szFormat, ...)
{
char szBuff[TEMP_STRING_BUFFER_MAX];
assert(strlen(szFormat) < TEMP_STRING_BUFFER_MAX); // > max_path. Use this only for small messages.
va_list arg;
va_start(arg, szFormat);
vsnprintf(szBuff, sizeof(szBuff), szFormat, arg);
va_end(arg);
// Passed in via char for convenience - convert to unsigned for glutBitmapString
unsigned char szUBuff[TEMP_STRING_BUFFER_MAX];
memset(szUBuff, 0, sizeof(szUBuff));
int len = strlen(szBuff);
for (int i = 0; i < len; i++) szUBuff[i] = (unsigned char)szBuff[i];
glEnable(GL_DEPTH_TEST);
glMatrixMode( GL_PROJECTION );
glPushMatrix();
glLoadIdentity();
glOrtho(0, _width, 0, _height, 0, 1);
glDisable(GL_LIGHTING);
glMatrixMode( GL_MODELVIEW );
glPushMatrix();
glLoadIdentity();
glColor3f(.8f, .8f, .8f); // Matt:: reverse ordering once depth enabled.
glRasterPos2i(width, height);
//glutBitmapString(GLUT_BITMAP_HELVETICA_18, szUBuff);
glColor3f(0.0f, 0.0f, 0.0f);
glRasterPos2i(width - SHADOW_OFFSET, height - SHADOW_OFFSET );
//glutBitmapString(GLUT_BITMAP_HELVETICA_18, szUBuff);
//glEnable(GL_LIGHTING);
glPopMatrix();
glMatrixMode( GL_PROJECTION );
glPopMatrix();
glMatrixMode( GL_MODELVIEW );
glEnable(GL_DEPTH_TEST);
}
void SvoViewer::update(float deltaTime){
}
void SvoViewer::resizeGL(int width, int height)
{
glViewport(0, 0, width, height); // shouldn't this account for the menu???
glGetIntegerv(GL_VIEWPORT, _viewport);
_pixelCount = width * height;
_width = width;
_height = height;
updateProjectionMatrix(_myCamera, true);
glLoadIdentity();
}
void SvoViewer::updateProjectionMatrix(Camera& camera, bool updateViewFrustum)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float left, right, bottom, top, nearVal, farVal;
glm::vec4 nearClipPlane, farClipPlane;
// Tell our viewFrustum about this change, using the application camera
if (updateViewFrustum) {
loadViewFrustum(camera, _viewFrustum);
computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
} else {
ViewFrustum tempViewFrustum;
loadViewFrustum(camera, tempViewFrustum);
tempViewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
}
glFrustum(left, right, bottom, top, nearVal, farVal);
// save matrix
//glGetDoublev(GL_PROJECTION_MATRIX, _projectionMatrix);
glMatrixMode(GL_MODELVIEW);
}
/////////////////////////////////////////////////////////////////////////////////////
// loadViewFrustum()
//
// Description: this will load the view frustum bounds for EITHER the head
// or the "myCamera".
//
void SvoViewer::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum)
{
// We will use these below, from either the camera or head vectors calculated above
glm::vec3 position(camera.getPosition());
float fov = camera.getFieldOfView();
float nearClip = camera.getNearClip();
float farClip = camera.getFarClip();
float aspectRatio = camera.getAspectRatio();
glm::quat rotation = camera.getRotation();
// Set the viewFrustum up with the correct position and orientation of the camera
viewFrustum.setPosition(position);
viewFrustum.setOrientation(rotation);
// Also make sure it's got the correct lens details from the camera
viewFrustum.setAspectRatio(aspectRatio);
viewFrustum.setFieldOfView(fov);
viewFrustum.setNearClip(nearClip);
viewFrustum.setFarClip(farClip);
viewFrustum.setEyeOffsetPosition(camera.getEyeOffsetPosition());
viewFrustum.setEyeOffsetOrientation(camera.getEyeOffsetOrientation());
// Ask the ViewFrustum class to calculate our corners
viewFrustum.calculate();
}
void SvoViewer::computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const
{
_viewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
}
void SvoViewer::updateCamera(float deltaTime)
{
/*if (Menu::getInstance()->isOptionChecked(MenuOption::OffAxisProjection)) {
float xSign = _myCamera.getMode() == CAMERA_MODE_MIRROR ? 1.0f : -1.0f;
if (_faceshift.isActive()) {
const float EYE_OFFSET_SCALE = 0.025f;
glm::vec3 position = _faceshift.getHeadTranslation() * EYE_OFFSET_SCALE;
_myCamera.setEyeOffsetPosition(glm::vec3(position.x * xSign, position.y, -position.z));
updateProjectionMatrix();
}
}*/
}
void SvoViewer::setupWorldLight()
{
// Setup 3D lights (after the camera transform, so that they are positioned in world space)
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
//glm::vec3 sunDirection = getSunDirection();
GLfloat light_position0[] = {10.0, 10.0, -20.0, 0.0};//{ sunDirection.x, sunDirection.y, sunDirection.z, 0.0 };
glLightfv(GL_LIGHT0, GL_POSITION, light_position0);
GLfloat ambient_color[] = { 0.7f, 0.7f, 0.8f };
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color);
GLfloat diffuse_color[] = { 0.8f, 0.7f, 0.7f };
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_color);
glLightfv(GL_LIGHT0, GL_SPECULAR, WHITE_SPECULAR_COLOR);
glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE_SPECULAR_COLOR);
glMateriali(GL_FRONT, GL_SHININESS, 96);
}
inline glm::vec3 MAD(glm::vec3 v1, float mult, glm::vec3 v2)
{
return glm::vec3(v1.x * mult + v2.x, v1.y * mult + v2.y, v1.z * mult + v2.z);
}
void SvoViewer::keyPressEvent(QKeyEvent* event)
{
int keyval = event->key();
glm::vec3 lookAt = glm::normalize(_myCamera.getTargetPosition() - _myCamera.getPosition());
glm::vec3 up = glm::vec3(0.0,1.0f,0.0f);
glm::vec3 right = glm::cross(lookAt, up);
glm::vec3 rotY;
switch (keyval)
{
case Qt::Key_W:
_myCamera.setPosition( MAD(lookAt, .2, _myCamera.getPosition()) );
_myCamera.setTargetPosition( MAD(lookAt, .2, _myCamera.getTargetPosition()) );
break;
case Qt::Key_S:
_myCamera.setPosition( MAD(lookAt, -.2, _myCamera.getPosition()) );
_myCamera.setTargetPosition( MAD(lookAt, -.2, _myCamera.getTargetPosition()) );
break;
case Qt::Key_A:
_myCamera.setPosition( MAD(right, -.2, _myCamera.getPosition()) );
_myCamera.setTargetPosition( MAD(right, -.2, _myCamera.getTargetPosition()) );
break;
case Qt::Key_D:
_myCamera.setPosition( MAD(right, .2, _myCamera.getPosition()) );
_myCamera.setTargetPosition( MAD(right, .2, _myCamera.getTargetPosition()) );
break;
case Qt::Key_R:
_myCamera.setPosition( MAD(up, .2, _myCamera.getPosition()) );
_myCamera.setTargetPosition( MAD(up, .2, _myCamera.getTargetPosition()) );
break;
case Qt::Key_F:
_myCamera.setPosition( MAD(up, -.2, _myCamera.getPosition()) );
_myCamera.setTargetPosition( MAD(up, -.2, _myCamera.getTargetPosition()) );
break;
case Qt::Key_Q: // rotate left
_yaw += 10;
_myCamera.setTargetRotation(glm::quat(glm::radians(glm::vec3(_pitch, _yaw, _roll))));
break;
case Qt::Key_E: // rotate right
_yaw -= 10;
_myCamera.setTargetRotation(glm::quat(glm::radians(glm::vec3(_pitch, _yaw, _roll))));
break;
case Qt::Key_B: // rotate right
_useBoundingVolumes ^= 1;
break;
default:
if (keyval >= Qt::Key_0 && keyval <= Qt::Key_9)
{
int newPartitionToDisplay = keyval - Qt::Key_0;
if (_displayOnlyPartition == newPartitionToDisplay) _displayOnlyPartition = NO_PARTITION;
else _displayOnlyPartition = newPartitionToDisplay;
}
}
loadViewFrustum(_myCamera, _viewFrustum);
UpdateOpt2BVFaceVisibility();
}
void SvoViewer::keyReleaseEvent(QKeyEvent* event) {}
void SvoViewer::mouseMoveEvent(QMouseEvent* event)
{
int deltaX = event->x() - _mouseX;
int deltaY = event->y() - _mouseY;
_mouseX = event->x();
_mouseY = event->y();
loadViewFrustum(_myCamera, _viewFrustum);
}
void SvoViewer::mousePressEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
{
_mouseX = event->x();
_mouseY = event->y();
_mouseDragStartedX = _mouseX;
_mouseDragStartedY = _mouseY;
_mousePressed = true;
}
}
void SvoViewer::mouseReleaseEvent(QMouseEvent* event) {
}
void SvoViewer::updateMouseRay()
{
// if the mouse pointer isn't visible, act like it's at the center of the screen
float x = 0.5f, y = 0.5f;
if (!_mouseHidden) {
x = _mouseX / (float)_glWidget->width();
y = _mouseY / (float)_glWidget->height();
}
_viewFrustum.computePickRay(x, y, _mouseRayOrigin, _mouseRayDirection);
// adjust for mirroring
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
glm::vec3 mouseRayOffset = _mouseRayOrigin - _viewFrustum.getPosition();
_mouseRayOrigin -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), mouseRayOffset) +
_viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), mouseRayOffset));
_mouseRayDirection -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), _mouseRayDirection) +
_viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), _mouseRayDirection));
}
}
// Stub for the full function.
bool SvoViewer::isVisibleBV(AABoundingVolume * volume, Camera * camera, ViewFrustum * frustum)
{
//if (pos.z >= volume->getBound(2,AABF_HIGH)) return false;
// Project all the points into screen space.
AA2DBoundingVolume twoDBounds;
float xvals[2] = {9999.0, -1.0};
float yvals[2] = {9999.0, -1.0};
//project all bv points into screen space.
GLdouble scr[3];
for (int i = 0; i < 8; i++)
{
glm::vec3 pt = volume->getCorner((BoxVertex)i);
gluProject((GLdouble)pt.x, (GLdouble)pt.y, (GLdouble)pt.z, _modelviewMatrix, _projectionMatrix, _viewport, &scr[0], &scr[1], &scr[2]);
if (scr[2] > 0 && scr[2] < 1)
{
float tPt[2] = {(float)scr[0], (float)scr[1]};
twoDBounds.AddToSet(tPt);
}
}
bool inVisibleSpace = twoDBounds.clipToRegion(0, 0, _width, _height);
return inVisibleSpace;
}
float SvoViewer::visibleAngleSubtended(AABoundingVolume * volume, Camera * camera, ViewFrustum * frustum)
{
AA2DBoundingVolume twoDBounds;
float xvals[2] = {9999.0, -1.0};
float yvals[2] = {9999.0, -1.0};
//project all bv points into screen space.
GLdouble scr[3];
for (int i = 0; i < 8; i++)
{
glm::vec3 pt = volume->getCorner((BoxVertex)i);
gluProject((GLdouble)pt.x, (GLdouble)pt.y, (GLdouble)pt.z, _modelviewMatrix, _projectionMatrix, _viewport, &scr[0], &scr[1], &scr[2]);
if (scr[2] > 0 && scr[2] < 1)
{
float tPt[2] = {(float)scr[0], (float)scr[1]};
twoDBounds.AddToSet(tPt);
}
}
twoDBounds.clipToRegion(0, 0, _width, _height);
float area = twoDBounds.getWidth() * twoDBounds.getHeight();
if (area < 1) return 0.0;
return area / (float)_pixelCount;
}
void SvoViewer::DebugPrint(const char* szFormat, ...)
{
/**
char szBuff[TEMP_STRING_BUFFER_MAX];
assert(strlen(szFormat) < TEMP_STRING_BUFFER_MAX); // > max_path. Use this only for small messages.
va_list arg;
va_start(arg, szFormat);
vsnprintf(szBuff, sizeof(szBuff), TEMP_STRING_BUFFER_MAX-100, szFormat, arg);
va_end(arg);
**/
qDebug(szFormat);
}
GLubyte SvoViewer::PrintGLErrorCode()
{
GLubyte err = glGetError();
if( err != GL_NO_ERROR ) //DebugPrint("GL Error! : %x\n", err);
DebugPrint("Error! : %u, %s\n", (unsigned int)err, gluErrorString(err));
return err;
}

317
SvoViewer/src/svoviewer.h Executable file
View file

@ -0,0 +1,317 @@
#ifndef SVOVIEWER_H
#define SVOVIEWER_H
#pragma once
#include <QApplication>
#include <QGLWidget>
/***
#define GL_GLEXT_PROTOTYPES 1
#define GLEW_STATIC
#include <windowshacks.h>
***/
#include "globals.h"
#include "AABoundingVolume.h"
enum SVOViewerShaderModel
{
RENDER_NONE,
RENDER_POINTS,
RENDER_CLASSIC_POLYS,
RENDER_OPT_POLYS,
RENDER_OPT_CULLED_POLYS,
};
enum CubeOrdering
{
CUBE_TOP,
CUBE_BOTTOM,
CUBE_LEFT,
CUBE_RIGHT,
CUBE_FRONT,
CUBE_BACK,
NUM_CUBE_FACES
};
struct ViewFrustumOffset {
float yaw;
float pitch;
float roll;
float distance;
float up;
};
struct RenderFlags
{
bool ptRenderDirty : 1;
bool voxelRenderDirty : 1;
bool voxelOptRenderDirty : 1;
bool usePtShader : 1;
bool useVoxelShader : 1;
bool useVoxelOptShader : 1;
bool useShadows : 1;
};
struct Vertex
{
glm::vec3 position;
unsigned char color[4];
};
enum VtxAttributes
{
ATTRIB_POSITION,
ATTRIB_COLOR,
ATTRIB_TEXTURE
};
struct VoxelDimIdxSet
{
GLuint idxIds[NUM_CUBE_FACES]; // Id for each voxel face
GLuint idxCount[NUM_CUBE_FACES]; // count for each voxel face.
GLuint* idxBuff[NUM_CUBE_FACES]; // actual index buffers for each voxel face
GLuint elemCount[NUM_CUBE_FACES];
AABoundingVolume bounds[NUM_CUBE_FACES]; // Super efficient bounding set here.
bool visibleFace[NUM_CUBE_FACES];
};
struct VisibleFacesData
{
int count;
glm::vec3 * ptList;
};
struct FindNumLeavesData
{
int numLeaves;
};
//#define MAX_VOXELS 4000000
#define MAX_VOXELS 8000000
#define REASONABLY_LARGE_BUFFER 10000
#define MAX_NUM_OCTREE_PARTITIONS 20
#define MAX_NUM_VBO_ALLOWED MAX_NUM_OCTREE_PARTITIONS * NUM_CUBE_FACES
#define NO_PARTITION -1
class VoxelOptRenderer : public OctreeRenderer {
};
class SvoViewer : public QApplication
{
Q_OBJECT
public:
static SvoViewer* getInstance() { return static_cast<SvoViewer*>(QCoreApplication::instance()); }
SvoViewer(int& argc, char** argv, QWidget *parent = 0);
~SvoViewer();
void initializeGL();
void paintGL();
void resizeGL(int width, int height);
void init();
void update(float deltaTime);
void updateMouseRay();
void updateProjectionMatrix(Camera& camera, bool updateViewFrustum);
void updateCamera(float deltaTime);
void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
void computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal, float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const ;
void setupWorldLight();
glm::vec2 getViewportDimensions() const{ return glm::vec2(_width,_height); }
// User Tweakable LOD Items
void autoAdjustLOD(float currentFPS);
void setVoxelSizeScale(float sizeScale);
float getVoxelSizeScale() const { return _voxelSizeScale; }
void setBoundaryLevelAdjust(int boundaryLevelAdjust);
int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
bool getIsPointShader(){ return _renderFlags.usePtShader; }
void setIsPointShader(const bool b){ _renderFlags.usePtShader = b; }
bool getIsVoxelShader(){ return _renderFlags.useVoxelShader; }
void setIsVoxelShader(const bool b){ _renderFlags.useVoxelShader = b; }
bool getIsVoxelOptShader(){ return _renderFlags.useVoxelOptShader; }
void setIsVoxelOptShader(const bool b){ _renderFlags.useVoxelOptShader = b; }
void setUseVoxelTextures(const bool b){_useVoxelTextures = b; }
bool getUseVoxelTextures(){ return _useVoxelTextures; }
void setUseShadows(const bool b){ _renderFlags.useShadows = b; }
bool getUseShadows(){ return _renderFlags.useShadows; }
//VoxelShader* getVoxelShader(){ return &_voxelShader; }
//PointShader* getPointShader(){ return &_pointShader; }
void keyPressEvent(QKeyEvent* event);
void keyReleaseEvent(QKeyEvent* event);
void mouseMoveEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void PrintToScreen(const int width, const int height, const char* szFormat, ...);
static void DebugPrint(const char* szFormat, ...); // utility function.
static GLubyte PrintGLErrorCode();
// Some helper functions.
GLubyte SetupGlVBO(GLuint * id, int sizeInBytes, GLenum target, GLenum usage, void * dataUp );
static glm::vec3 computeQuickAndDirtyQuadCenter(glm::vec3 p0, glm::vec3 p1, glm::vec3 p2, glm::vec3 p3);
bool isVisibleBV(AABoundingVolume * volume, Camera * camera, ViewFrustum * frustum);
float visibleAngleSubtended(AABoundingVolume * volume, Camera * camera, ViewFrustum * frustum);
static int ptCompFunc(const void * a, const void * b);
static int ptCloseEnough(const void * a, const void * b);
static int binVecSearch(glm::vec3 searchVal, glm::vec3* list, int count, int * found);
bool parameterizedRayPlaneIntersection(const glm::vec3 origin, const glm::vec3 direction, const glm::vec3 planePt, const glm::vec3 planeNormal, float *t);
protected:
void InitializePointRenderSystem();
void setupFaceIndices(GLuint& faceVBOID, GLubyte faceIdentityIndices[]);
void InitializeVoxelRenderSystem();
void InitializeVoxelOptRenderSystem();
void InitializeVoxelOpt2RenderSystem();
void StopUsingPointRenderSystem();
void StopUsingVoxelRenderSystem();
void StopUsingVoxelOptRenderSystem();
void StopUsingVoxelOpt2RenderSystem();
void UpdateOpt2BVFaceVisibility();
void RenderTreeSystemAsPoints();
void RenderTreeSystemAsVoxels();
void RenderTreeSystemAsOptVoxels();
void RenderTreeSystemAsOpt2Voxels();
// Tree traversal functions.
static bool PointRenderAssemblePerVoxel(OctreeElement* node, void* extraData);
static bool VoxelRenderAssemblePerVoxel(OctreeElement* node, void* extraData);
static bool VoxelOptRenderAssemblePerVoxel(OctreeElement* node, void* extraData);
static bool VoxelOpt2RenderAssemblePerVoxel(OctreeElement* node, void* extraData);
static bool FindNumLeaves(OctreeElement* node, void* extraData);
static bool TrackVisibleFaces(OctreeElement* node, void* extraData);
private slots:
void idle();
private:
//Ui::SvoViewerClass ui;
QMainWindow* _window;
int _width;
int _height;
int _pixelCount;
QGLWidget* _glWidget;
//VoxelSystem _voxels;
VoxelTree _systemTree;
unsigned long _nodeCount;
unsigned int _leafCount;
ViewFrustum _viewFrustum;
Camera _myCamera; // My view onto the world
float _pitch, _yaw, _roll;
int _mouseX;
int _mouseY;
int _mouseDragStartedX;
int _mouseDragStartedY;
bool _mousePressed;
quint64 _lastMouseMove;
bool _mouseHidden;
bool _seenMouseMove;
glm::vec3 _mouseRayOrigin;
glm::vec3 _mouseRayDirection;
glm::mat4 _untranslatedViewMatrix;
glm::vec3 _viewMatrixTranslation;
GLdouble _projectionMatrix[16];
GLdouble _modelviewMatrix[16];
GLint _viewport[4];
SVOViewerShaderModel _currentShaderModel;
//VoxelShader _voxelShader;
//PointShader _pointShader;
// Display options
int _displayOnlyPartition;
// Frame Rate Measurement
int _frameCount;
int _lastTrackedFrameCount;
float _fps;
timeval _applicationStartupTime;
quint64 _appStartTickCount;
quint64 _lastTimeFpsUpdated;
quint64 _lastTimeFrameUpdated;
// Render variables.
bool _ptRenderInitialized;
bool _voxelRenderInitialized;
bool _voxelOptRenderInitialized;
bool _voxelOpt2RenderInitialized;
GLuint _vertexShader;
GLuint _pixelShader;
GLuint _geometryShader;
GLuint _linkProgram;
// Vars for RENDER_POINTS
GLuint _pointVtxBuffer;
GLuint _pointColorBuffer;
glm::vec3* _pointVertices;
unsigned char* _pointColors;
int _pointVerticesCount;
// Vars for RENDER_CLASSIC_POLYS
GLuint _vboVerticesID;
GLuint _vboColorsID;
GLuint _vboIndicesIds[NUM_CUBE_FACES];
GLuint* _vboIndices[NUM_CUBE_FACES];
//VoxelShaderVBOData* _vboShaderData; // may not need.
glm::vec3* _readVerticesArray;
unsigned char* _readColorsArray;
GLuint* _readIndicesArray;
// Vars for RENDER_OPT_POLYS
// Allow for primitive first level sorting at the moment.
GLuint _vboOVerticesIds[MAX_NUM_VBO_ALLOWED];
glm::vec3* _vboOVertices[MAX_NUM_VBO_ALLOWED];
GLuint _vboOIndicesIds[MAX_NUM_VBO_ALLOWED];
GLuint* _vboOIndices[MAX_NUM_VBO_ALLOWED];
GLuint _numChildNodeLeaves[MAX_NUM_VBO_ALLOWED];
OctreeElement * _segmentNodeReferences[MAX_NUM_OCTREE_PARTITIONS];
AABoundingVolume _segmentBoundingVolumes[MAX_NUM_OCTREE_PARTITIONS];
int _segmentElemCount[MAX_NUM_OCTREE_PARTITIONS];
unsigned int _numSegments;
Vertex * _readVertexStructs;
// Vars for RENDER_OPT_CULLED_POLYS
// Use vtx vars from opt_polys version.
VoxelDimIdxSet _segmentIdxBuffers[MAX_NUM_OCTREE_PARTITIONS];
bool _useBoundingVolumes;
int _numElemsDrawn;
int _totalPossibleElems;
RenderFlags _renderFlags;
ViewFrustumOffset _viewFrustumOffset;
int _maxVoxels;
float _voxelSizeScale;
int _boundaryLevelAdjust;
float _fieldOfView; /// in Degrees
bool _useVoxelTextures;
};
//Extern hack since this wasn't built with global linking to old project in mind.
extern SvoViewer * _globalSvoViewerObj;
#endif // SVOVIEWER_H

4
SvoViewer/src/svoviewer.qrc Executable file
View file

@ -0,0 +1,4 @@
<RCC>
<qresource prefix="SvoViewer">
</qresource>
</RCC>

29
SvoViewer/src/svoviewer.ui Executable file
View file

@ -0,0 +1,29 @@
<UI version="4.0" >
<class>SvoViewerClass</class>
<widget class="QMainWindow" name="SvoViewerClass" >
<property name="objectName" >
<string notr="true">SvoViewerClass</string>
</property>
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle" >
<string>SvoViewer</string>
</property>
<widget class="QMenuBar" name="menuBar" />
<widget class="QToolBar" name="mainToolBar" />
<widget class="QWidget" name="centralWidget" />
<widget class="QStatusBar" name="statusBar" />
</widget>
<layoutDefault spacing="6" margin="11" />
<pixmapfunction></pixmapfunction>
<resources>
<include location="svoviewer.qrc"/>
</resources>
<connections/>
</UI>