diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e796e3ca2..d12cf1e45d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,3 +39,4 @@ add_subdirectory(domain-server) add_subdirectory(interface) add_subdirectory(tests) add_subdirectory(voxel-edit) +add_subdirectory(SvoViewer) diff --git a/SvoViewer/CMakeLists.txt b/SvoViewer/CMakeLists.txt new file mode 100644 index 0000000000..4114b2a5bc --- /dev/null +++ b/SvoViewer/CMakeLists.txt @@ -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 \n#include ") +endif (APPLE) + +if (UNIX AND NOT APPLE) + # include the right GL headers for UNIX + set(GL_HEADERS "#include \n#include \n#include ") +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 \n#include \n#include \n#include ") + set(GL_HEADERS "#define GLEW_STATIC\n#include \n#include \n#include ") + +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 +) + diff --git a/SvoViewer/SvoViewer.icns b/SvoViewer/SvoViewer.icns new file mode 100644 index 0000000000..f32590eb21 Binary files /dev/null and b/SvoViewer/SvoViewer.icns differ diff --git a/SvoViewer/SvoViewerConfig.h.in b/SvoViewer/SvoViewerConfig.h.in new file mode 100644 index 0000000000..377f14c984 --- /dev/null +++ b/SvoViewer/SvoViewerConfig.h.in @@ -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 diff --git a/SvoViewer/SvoViewerVersion.h.in b/SvoViewer/SvoViewerVersion.h.in new file mode 100644 index 0000000000..81793a0537 --- /dev/null +++ b/SvoViewer/SvoViewerVersion.h.in @@ -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@"; diff --git a/SvoViewer/src/AABoundingVolume.h b/SvoViewer/src/AABoundingVolume.h new file mode 100644 index 0000000000..0fa98a9034 --- /dev/null +++ b/SvoViewer/src/AABoundingVolume.h @@ -0,0 +1,187 @@ +// +// AABoundingVolume.h - Axis Aligned Bounding Volumes +// hifi +// + +#ifndef _AABOUNDING_VOLUME_ +#define _AABOUNDING_VOLUME_ + +#include + + + +// 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 diff --git a/SvoViewer/src/Camera.cpp b/SvoViewer/src/Camera.cpp new file mode 100755 index 0000000000..a842d2ce18 --- /dev/null +++ b/SvoViewer/src/Camera.cpp @@ -0,0 +1,251 @@ +// +// Camera.cpp +// interface +// +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. + +#include +#include + +#include +#include +#include + +#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::max()) + ? _scale * _farClip + : std::numeric_limits::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; +} + + + + diff --git a/SvoViewer/src/Camera.h b/SvoViewer/src/Camera.h new file mode 100755 index 0000000000..a688a9e3f4 --- /dev/null +++ b/SvoViewer/src/Camera.h @@ -0,0 +1,119 @@ +// +// Camera.h +// interface +// +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__camera__ +#define __interface__camera__ + +#include +#include + +//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 diff --git a/SvoViewer/src/GLCanvas.cpp b/SvoViewer/src/GLCanvas.cpp new file mode 100755 index 0000000000..0e2772a5b8 --- /dev/null +++ b/SvoViewer/src/GLCanvas.cpp @@ -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 +#include + +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(event)); + event->accept(); + return true; + case QEvent::TouchEnd: + SvoViewer::getInstance()->touchEndEvent(static_cast(event)); + return true; + case QEvent::TouchUpdate: + SvoViewer::getInstance()->touchUpdateEvent(static_cast(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); +} diff --git a/SvoViewer/src/GLCanvas.h b/SvoViewer/src/GLCanvas.h new file mode 100644 index 0000000000..0f0cb5c7d0 --- /dev/null +++ b/SvoViewer/src/GLCanvas.h @@ -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 + +/// 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__) */ diff --git a/SvoViewer/src/ProgramObject.cpp b/SvoViewer/src/ProgramObject.cpp new file mode 100755 index 0000000000..5108c15068 --- /dev/null +++ b/SvoViewer/src/ProgramObject.cpp @@ -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); +} + diff --git a/SvoViewer/src/ProgramObject.h b/SvoViewer/src/ProgramObject.h new file mode 100755 index 0000000000..78f07b7a1b --- /dev/null +++ b/SvoViewer/src/ProgramObject.h @@ -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 + +#include + +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__) */ diff --git a/SvoViewer/src/Render.cpp b/SvoViewer/src/Render.cpp new file mode 100755 index 0000000000..39a7940b4c --- /dev/null +++ b/SvoViewer/src/Render.cpp @@ -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); +} + + diff --git a/SvoViewer/src/Render.h b/SvoViewer/src/Render.h new file mode 100755 index 0000000000..e69de29bb2 diff --git a/SvoViewer/src/Render2.cpp b/SvoViewer/src/Render2.cpp new file mode 100755 index 0000000000..d15e9d6b78 --- /dev/null +++ b/SvoViewer/src/Render2.cpp @@ -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); +} diff --git a/SvoViewer/src/SvoViewer.ico b/SvoViewer/src/SvoViewer.ico new file mode 100755 index 0000000000..7500a1d413 Binary files /dev/null and b/SvoViewer/src/SvoViewer.ico differ diff --git a/SvoViewer/src/SvoViewer.rc b/SvoViewer/src/SvoViewer.rc new file mode 100755 index 0000000000..08bf1d355f --- /dev/null +++ b/SvoViewer/src/SvoViewer.rc @@ -0,0 +1,2 @@ +IDI_ICON1 ICON DISCARDABLE "SvoViewer.ico" + diff --git a/SvoViewer/src/globals.cpp b/SvoViewer/src/globals.cpp new file mode 100755 index 0000000000..766cd6e6d3 --- /dev/null +++ b/SvoViewer/src/globals.cpp @@ -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 \ No newline at end of file diff --git a/SvoViewer/src/globals.h b/SvoViewer/src/globals.h new file mode 100755 index 0000000000..4357749d33 --- /dev/null +++ b/SvoViewer/src/globals.h @@ -0,0 +1,33 @@ + +#pragma once + +#include "SvoViewerConfig.h" + +#include +#include +#include +//#include + +//#include "ui_svoviewer.h" +#include +#include +#include +//#include +//#include +//#include +#include + +#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]; +} \ No newline at end of file diff --git a/SvoViewer/src/main.cpp b/SvoViewer/src/main.cpp new file mode 100755 index 0000000000..23cc6c7ba4 --- /dev/null +++ b/SvoViewer/src/main.cpp @@ -0,0 +1,8 @@ +#include "svoviewer.h" +#include + +int main(int argc, char *argv[]) +{ + SvoViewer svoV(argc, argv); + return svoV.exec(); +} diff --git a/SvoViewer/src/svoviewer.cpp b/SvoViewer/src/svoviewer.cpp new file mode 100755 index 0000000000..88e75b5e42 --- /dev/null +++ b/SvoViewer/src/svoviewer.cpp @@ -0,0 +1,644 @@ +#include "svoviewer.h" +#include "GLCanvas.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + + +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; +} \ No newline at end of file diff --git a/SvoViewer/src/svoviewer.h b/SvoViewer/src/svoviewer.h new file mode 100755 index 0000000000..af4732db81 --- /dev/null +++ b/SvoViewer/src/svoviewer.h @@ -0,0 +1,317 @@ +#ifndef SVOVIEWER_H +#define SVOVIEWER_H + +#pragma once + +#include +#include + +/*** +#define GL_GLEXT_PROTOTYPES 1 +#define GLEW_STATIC +#include + ***/ +#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(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 diff --git a/SvoViewer/src/svoviewer.qrc b/SvoViewer/src/svoviewer.qrc new file mode 100755 index 0000000000..1fbe02aea2 --- /dev/null +++ b/SvoViewer/src/svoviewer.qrc @@ -0,0 +1,4 @@ + + + + diff --git a/SvoViewer/src/svoviewer.ui b/SvoViewer/src/svoviewer.ui new file mode 100755 index 0000000000..0bd1cabbae --- /dev/null +++ b/SvoViewer/src/svoviewer.ui @@ -0,0 +1,29 @@ + + SvoViewerClass + + + SvoViewerClass + + + + 0 + 0 + 600 + 400 + + + + SvoViewer + + + + + + + + + + + + +