mirror of
https://github.com/lubosz/overte.git
synced 2025-04-26 20:55:52 +02:00
marge
This commit is contained in:
commit
b59292bdf1
282 changed files with 37926 additions and 8138 deletions
.gitignoreCMakeLists.txt
SvoViewer
CMakeLists.txtSvoViewer.icnsSvoViewerConfig.h.inSvoViewerVersion.h.in
external
freeglut
glew
src
animation-server/src
assignment-client/src
Agent.cppAgent.hAssignmentClient.cppAssignmentClient.hAssignmentClientMonitor.cpp
audio
avatars
metavoxels
octree
cmake
data-server
domain-server/src
examples
bot.jsclipboardExample.jseditVoxels.jshideAvatarExample.jsmenuExample.jsoverlaysExample.jsrayPickExample.jsribbon.jsseeingVoxelsExample.js
interface
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -39,5 +39,11 @@ interface/external/Leap/util/
|
|||
interface/external/Sixense/include/
|
||||
interface/external/Sixense/lib/
|
||||
|
||||
# Ignore Visage
|
||||
interface/external/visage/dependencies/
|
||||
interface/external/visage/include/
|
||||
interface/external/visage/lib/
|
||||
interface/resources/visage/
|
||||
|
||||
# Ignore interfaceCache for Linux users
|
||||
interface/interfaceCache/
|
||||
|
|
|
@ -31,13 +31,12 @@ ENDIF(APPLE)
|
|||
# targets not supported on windows
|
||||
if (NOT WIN32)
|
||||
add_subdirectory(animation-server)
|
||||
add_subdirectory(data-server)
|
||||
endif (NOT WIN32)
|
||||
|
||||
# targets on all platforms
|
||||
add_subdirectory(assignment-client)
|
||||
add_subdirectory(domain-server)
|
||||
add_subdirectory(interface)
|
||||
add_subdirectory(pairing-server)
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(voxel-edit)
|
||||
add_subdirectory(SvoViewer)
|
||||
|
|
204
SvoViewer/CMakeLists.txt
Normal file
204
SvoViewer/CMakeLists.txt
Normal file
|
@ -0,0 +1,204 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
set(ROOT_DIR ..)
|
||||
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
|
||||
|
||||
set(TARGET_NAME SvoViewer)
|
||||
project(${TARGET_NAME})
|
||||
|
||||
# setup for find modules
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/")
|
||||
|
||||
if (APPLE)
|
||||
set(GL_HEADERS "#include <GLUT/glut.h>\n#include <OpenGL/glext.h>")
|
||||
endif (APPLE)
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
# include the right GL headers for UNIX
|
||||
set(GL_HEADERS "#include <GL/gl.h>\n#include <GL/glut.h>\n#include <GL/glext.h>")
|
||||
endif (UNIX AND NOT APPLE)
|
||||
|
||||
if (WIN32)
|
||||
|
||||
add_definitions( -D_USE_MATH_DEFINES ) # apparently needed to get M_PI and other defines from cmath/math.h
|
||||
add_definitions( -DWINDOWS_LEAN_AND_MEAN ) # needed to make sure windows doesn't go to crazy with its defines
|
||||
|
||||
# windows build needs an external glut, we're using freeglut
|
||||
set(GLUT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/external/freeglut)
|
||||
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${GLUT_ROOT_PATH})
|
||||
|
||||
# windows build needs glew (opengl extention wrangler) this will handle providing access to OpenGL methods after 1.1
|
||||
# which are not accessible on windows without glew or some other dynamic linking mechanism
|
||||
set(GLEW_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/external/glew)
|
||||
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${GLEW_ROOT_PATH})
|
||||
include_directories(SYSTEM ${GLEW_ROOT_PATH}/include ${GLUT_ROOT_PATH}/include)
|
||||
|
||||
#set(GL_HEADERS "#define GLEW_STATIC\n#define FREEGLUT_STATIC\n#define FREEGLUT_LIB_PRAGMAS 0\n#include <GL/glew.h>\n#include <GL/wglew.h>\n#include <GL/freeglut_std.h>\n#include <GL/freeglut_ext.h>")
|
||||
set(GL_HEADERS "#define GLEW_STATIC\n#include <windowshacks.h>\n#include <GL/glew.h>\n#include <GL/glut.h>")
|
||||
|
||||
endif (WIN32)
|
||||
|
||||
# set up the external glm library
|
||||
include(${MACRO_DIR}/IncludeGLM.cmake)
|
||||
include_glm(${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
# create the ${TARGET_NAME}Config.h file based on GL_HEADERS above
|
||||
configure_file(${TARGET_NAME}Config.h.in ${PROJECT_BINARY_DIR}/includes/${TARGET_NAME}Config.h)
|
||||
configure_file(${TARGET_NAME}Version.h.in ${PROJECT_BINARY_DIR}/includes/${TARGET_NAME}Version.h)
|
||||
|
||||
# grab the implementation and header files from src dirs
|
||||
file(GLOB APPLICATION_SRCS src/*.c src/*.cpp src/*.h)
|
||||
foreach(SUBDIR avatar devices renderer ui starfield)
|
||||
file(GLOB_RECURSE SUBDIR_SRCS src/${SUBDIR}/*.cpp src/${SUBDIR}/*.c src/${SUBDIR}/*.h)
|
||||
set(APPLICATION_SRCS ${APPLICATION_SRCS} ${SUBDIR_SRCS})
|
||||
endforeach(SUBDIR)
|
||||
|
||||
foreach(EXTERNAL_SOURCE_SUBDIR ${EXTERNAL_SOURCE_SUBDIRS})
|
||||
file(GLOB_RECURSE SUBDIR_SRCS external/${EXTERNAL_SOURCE_SUBDIR}/src/*.cpp external/${EXTERNAL_SOURCE_SUBDIR}/src/*.c external/${EXTERNAL_SOURCE_SUBDIR}/src/*.h)
|
||||
set(APPLICATION_SRCS ${APPLICATION_SRCS} ${SUBDIR_SRCS})
|
||||
endforeach(EXTERNAL_SOURCE_SUBDIR)
|
||||
|
||||
find_package(Qt5Core REQUIRED)
|
||||
find_package(Qt5Gui REQUIRED)
|
||||
find_package(Qt5Multimedia REQUIRED)
|
||||
find_package(Qt5Network REQUIRED)
|
||||
find_package(Qt5OpenGL REQUIRED)
|
||||
find_package(Qt5Svg REQUIRED)
|
||||
find_package(Qt5WebKit REQUIRED)
|
||||
find_package(Qt5WebKitWidgets REQUIRED)
|
||||
find_package(Qt5Xml REQUIRED)
|
||||
|
||||
# grab the ui files in resources/ui
|
||||
file (GLOB_RECURSE QT_UI_FILES ui/*.ui)
|
||||
# have qt5 wrap them and generate the appropriate header files
|
||||
qt5_wrap_ui(QT_UI_HEADERS ${QT_UI_FILES})
|
||||
|
||||
# add them to the application source files
|
||||
set(APPLICATION_SRCS ${APPLICATION_SRCS} ${QT_UI_HEADERS})
|
||||
|
||||
if (APPLE)
|
||||
|
||||
# configure CMake to use a custom Info.plist
|
||||
SET_TARGET_PROPERTIES( ${this_target} PROPERTIES MACOSX_BUNDLE_INFO_PLIST MacOSXBundleInfo.plist.in )
|
||||
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME SvoViewer)
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER io.highfidelity.${TARGET_NAME})
|
||||
|
||||
# set how the icon shows up in the Info.plist file
|
||||
SET(MACOSX_BUNDLE_ICON_FILE ${TARGET_NAME}.icns)
|
||||
|
||||
# set where in the bundle to put the resources file
|
||||
SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_SOURCE_DIR}/${TARGET_NAME}.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
|
||||
SET(APPLICATION_SRCS ${APPLICATION_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/${TARGET_NAME}.icns)
|
||||
|
||||
# grab the directories in resources and put them in the right spot in Resources
|
||||
file(GLOB RESOURCE_SUBDIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/resources ${CMAKE_CURRENT_SOURCE_DIR}/resources/*)
|
||||
foreach(DIR ${RESOURCE_SUBDIRS})
|
||||
if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/resources/${DIR})
|
||||
FILE(GLOB DIR_CONTENTS resources/${DIR}/*)
|
||||
SET_SOURCE_FILES_PROPERTIES(${DIR_CONTENTS} PROPERTIES MACOSX_PACKAGE_LOCATION Resources/${DIR})
|
||||
|
||||
SET(APPLICATION_SRCS ${APPLICATION_SRCS} ${DIR_CONTENTS})
|
||||
endif()
|
||||
endforeach()
|
||||
endif (APPLE)
|
||||
|
||||
# create the executable, make it a bundle on OS X
|
||||
add_executable(${TARGET_NAME} MACOSX_BUNDLE ${APPLICATION_SRCS})
|
||||
|
||||
# link in the hifi shared library
|
||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
|
||||
# link required hifi libraries
|
||||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
||||
link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
|
||||
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
# find required libraries
|
||||
find_package(GLM REQUIRED)
|
||||
find_package(ZLIB)
|
||||
|
||||
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
|
||||
${GLM_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
${TARGET_NAME}
|
||||
${ZLIB_LIBRARIES}
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
# link in required OS X frameworks and include the right GL headers
|
||||
find_library(AppKit AppKit)
|
||||
find_library(CoreAudio CoreAudio)
|
||||
find_library(CoreServices CoreServices)
|
||||
find_library(Carbon Carbon)
|
||||
find_library(Foundation Foundation)
|
||||
find_library(GLUT GLUT)
|
||||
find_library(OpenGL OpenGL)
|
||||
find_library(IOKit IOKit)
|
||||
find_library(QTKit QTKit)
|
||||
find_library(QuartzCore QuartzCore)
|
||||
|
||||
target_link_libraries(
|
||||
${TARGET_NAME}
|
||||
${AppKit}
|
||||
${CoreAudio}
|
||||
${CoreServices}
|
||||
${Carbon}
|
||||
${Foundation}
|
||||
${GLUT}
|
||||
${OpenGL}
|
||||
${IOKit}
|
||||
${QTKit}
|
||||
${QuartzCore}
|
||||
)
|
||||
else (APPLE)
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(GLUT REQUIRED)
|
||||
include_directories(${GLUT_INCLUDE_DIR} ${OPENGL_INCLUDE_DIR})
|
||||
target_link_libraries(${TARGET_NAME} ${OPENGL_LIBRARY})
|
||||
endif (APPLE)
|
||||
|
||||
# link target to external libraries
|
||||
if (WIN32)
|
||||
target_link_libraries(
|
||||
${TARGET_NAME}
|
||||
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/external/glew/lib/Release/Win32/glew32s.lib
|
||||
${GLUT_ROOT_PATH}/lib/freeglut.lib
|
||||
|
||||
wsock32.lib
|
||||
opengl32.lib
|
||||
)
|
||||
else (WIN32)
|
||||
# link required libraries on UNIX
|
||||
if (UNIX AND NOT APPLE)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
target_link_libraries(
|
||||
${TARGET_NAME}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${GLUT_LIBRARY}
|
||||
)
|
||||
endif (UNIX AND NOT APPLE)
|
||||
endif (WIN32)
|
||||
|
||||
# install command for OS X bundle
|
||||
INSTALL(TARGETS ${TARGET_NAME}
|
||||
BUNDLE DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/install COMPONENT Runtime
|
||||
RUNTIME DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/install COMPONENT Runtime
|
||||
)
|
||||
|
BIN
SvoViewer/SvoViewer.icns
Normal file
BIN
SvoViewer/SvoViewer.icns
Normal file
Binary file not shown.
14
SvoViewer/SvoViewerConfig.h.in
Normal file
14
SvoViewer/SvoViewerConfig.h.in
Normal file
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// SvoViewerConfig.h
|
||||
// SvoViewer
|
||||
//
|
||||
// Copyright (c) 2014 High Fidelity, Inc.. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __SvoViewerConfig__
|
||||
#define __SvoViewerConfig__
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES 1
|
||||
@GL_HEADERS@
|
||||
|
||||
#endif
|
8
SvoViewer/SvoViewerVersion.h.in
Normal file
8
SvoViewer/SvoViewerVersion.h.in
Normal file
|
@ -0,0 +1,8 @@
|
|||
//
|
||||
// SvoViewerVersion.h
|
||||
// Declaration of version and build data
|
||||
//
|
||||
// Copyright (c) 2014 High Fidelity, Inc.. All rights reserved.
|
||||
//
|
||||
|
||||
const QString BUILD_VERSION = "@BUILD_SEQ@";
|
27
SvoViewer/external/freeglut/Copying.txt
vendored
Normal file
27
SvoViewer/external/freeglut/Copying.txt
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
|
||||
Freeglut Copyright
|
||||
------------------
|
||||
|
||||
Freeglut code without an explicit copyright is covered by the following
|
||||
copyright:
|
||||
|
||||
Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of Pawel W. Olszta shall not be
|
||||
used in advertising or otherwise to promote the sale, use or other dealings
|
||||
in this Software without prior written authorization from Pawel W. Olszta.
|
BIN
SvoViewer/external/freeglut/bin/freeglut.dll
vendored
Normal file
BIN
SvoViewer/external/freeglut/bin/freeglut.dll
vendored
Normal file
Binary file not shown.
BIN
SvoViewer/external/freeglut/bin/x64/freeglut.dll
vendored
Normal file
BIN
SvoViewer/external/freeglut/bin/x64/freeglut.dll
vendored
Normal file
Binary file not shown.
22
SvoViewer/external/freeglut/include/GL/freeglut.h
vendored
Normal file
22
SvoViewer/external/freeglut/include/GL/freeglut.h
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef __FREEGLUT_H__
|
||||
#define __FREEGLUT_H__
|
||||
|
||||
/*
|
||||
* freeglut.h
|
||||
*
|
||||
* The freeglut library include file
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "freeglut_std.h"
|
||||
#include "freeglut_ext.h"
|
||||
|
||||
/*** END OF FILE ***/
|
||||
|
||||
#endif /* __FREEGLUT_H__ */
|
239
SvoViewer/external/freeglut/include/GL/freeglut_ext.h
vendored
Normal file
239
SvoViewer/external/freeglut/include/GL/freeglut_ext.h
vendored
Normal file
|
@ -0,0 +1,239 @@
|
|||
#ifndef __FREEGLUT_EXT_H__
|
||||
#define __FREEGLUT_EXT_H__
|
||||
|
||||
/*
|
||||
* freeglut_ext.h
|
||||
*
|
||||
* The non-GLUT-compatible extensions to the freeglut library include file
|
||||
*
|
||||
* Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
|
||||
* Written by Pawel W. Olszta, <olszta@sourceforge.net>
|
||||
* Creation date: Thu Dec 2 1999
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Additional GLUT Key definitions for the Special key function
|
||||
*/
|
||||
#define GLUT_KEY_NUM_LOCK 0x006D
|
||||
#define GLUT_KEY_BEGIN 0x006E
|
||||
#define GLUT_KEY_DELETE 0x006F
|
||||
#define GLUT_KEY_SHIFT_L 0x0070
|
||||
#define GLUT_KEY_SHIFT_R 0x0071
|
||||
#define GLUT_KEY_CTRL_L 0x0072
|
||||
#define GLUT_KEY_CTRL_R 0x0073
|
||||
#define GLUT_KEY_ALT_L 0x0074
|
||||
#define GLUT_KEY_ALT_R 0x0075
|
||||
|
||||
/*
|
||||
* GLUT API Extension macro definitions -- behaviour when the user clicks on an "x" to close a window
|
||||
*/
|
||||
#define GLUT_ACTION_EXIT 0
|
||||
#define GLUT_ACTION_GLUTMAINLOOP_RETURNS 1
|
||||
#define GLUT_ACTION_CONTINUE_EXECUTION 2
|
||||
|
||||
/*
|
||||
* Create a new rendering context when the user opens a new window?
|
||||
*/
|
||||
#define GLUT_CREATE_NEW_CONTEXT 0
|
||||
#define GLUT_USE_CURRENT_CONTEXT 1
|
||||
|
||||
/*
|
||||
* Direct/Indirect rendering context options (has meaning only in Unix/X11)
|
||||
*/
|
||||
#define GLUT_FORCE_INDIRECT_CONTEXT 0
|
||||
#define GLUT_ALLOW_DIRECT_CONTEXT 1
|
||||
#define GLUT_TRY_DIRECT_CONTEXT 2
|
||||
#define GLUT_FORCE_DIRECT_CONTEXT 3
|
||||
|
||||
/*
|
||||
* GLUT API Extension macro definitions -- the glutGet parameters
|
||||
*/
|
||||
#define GLUT_INIT_STATE 0x007C
|
||||
|
||||
#define GLUT_ACTION_ON_WINDOW_CLOSE 0x01F9
|
||||
|
||||
#define GLUT_WINDOW_BORDER_WIDTH 0x01FA
|
||||
#define GLUT_WINDOW_BORDER_HEIGHT 0x01FB
|
||||
#define GLUT_WINDOW_HEADER_HEIGHT 0x01FB /* Docs say it should always have been GLUT_WINDOW_BORDER_HEIGHT, keep this for backward compatibility */
|
||||
|
||||
#define GLUT_VERSION 0x01FC
|
||||
|
||||
#define GLUT_RENDERING_CONTEXT 0x01FD
|
||||
#define GLUT_DIRECT_RENDERING 0x01FE
|
||||
|
||||
#define GLUT_FULL_SCREEN 0x01FF
|
||||
|
||||
#define GLUT_SKIP_STALE_MOTION_EVENTS 0x0204
|
||||
|
||||
/*
|
||||
* New tokens for glutInitDisplayMode.
|
||||
* Only one GLUT_AUXn bit may be used at a time.
|
||||
* Value 0x0400 is defined in OpenGLUT.
|
||||
*/
|
||||
#define GLUT_AUX 0x1000
|
||||
|
||||
#define GLUT_AUX1 0x1000
|
||||
#define GLUT_AUX2 0x2000
|
||||
#define GLUT_AUX3 0x4000
|
||||
#define GLUT_AUX4 0x8000
|
||||
|
||||
/*
|
||||
* Context-related flags, see freeglut_state.c
|
||||
*/
|
||||
#define GLUT_INIT_MAJOR_VERSION 0x0200
|
||||
#define GLUT_INIT_MINOR_VERSION 0x0201
|
||||
#define GLUT_INIT_FLAGS 0x0202
|
||||
#define GLUT_INIT_PROFILE 0x0203
|
||||
|
||||
/*
|
||||
* Flags for glutInitContextFlags, see freeglut_init.c
|
||||
*/
|
||||
#define GLUT_DEBUG 0x0001
|
||||
#define GLUT_FORWARD_COMPATIBLE 0x0002
|
||||
|
||||
|
||||
/*
|
||||
* Flags for glutInitContextProfile, see freeglut_init.c
|
||||
*/
|
||||
#define GLUT_CORE_PROFILE 0x0001
|
||||
#define GLUT_COMPATIBILITY_PROFILE 0x0002
|
||||
|
||||
/*
|
||||
* Process loop function, see freeglut_main.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutMainLoopEvent( void );
|
||||
FGAPI void FGAPIENTRY glutLeaveMainLoop( void );
|
||||
FGAPI void FGAPIENTRY glutExit ( void );
|
||||
|
||||
/*
|
||||
* Window management functions, see freeglut_window.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutFullScreenToggle( void );
|
||||
FGAPI void FGAPIENTRY glutLeaveFullScreen( void );
|
||||
|
||||
/*
|
||||
* Window-specific callback functions, see freeglut_callbacks.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutMouseWheelFunc( void (* callback)( int, int, int, int ) );
|
||||
FGAPI void FGAPIENTRY glutCloseFunc( void (* callback)( void ) );
|
||||
FGAPI void FGAPIENTRY glutWMCloseFunc( void (* callback)( void ) );
|
||||
/* A. Donev: Also a destruction callback for menus */
|
||||
FGAPI void FGAPIENTRY glutMenuDestroyFunc( void (* callback)( void ) );
|
||||
|
||||
/*
|
||||
* State setting and retrieval functions, see freeglut_state.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutSetOption ( GLenum option_flag, int value );
|
||||
FGAPI int * FGAPIENTRY glutGetModeValues(GLenum mode, int * size);
|
||||
/* A.Donev: User-data manipulation */
|
||||
FGAPI void* FGAPIENTRY glutGetWindowData( void );
|
||||
FGAPI void FGAPIENTRY glutSetWindowData(void* data);
|
||||
FGAPI void* FGAPIENTRY glutGetMenuData( void );
|
||||
FGAPI void FGAPIENTRY glutSetMenuData(void* data);
|
||||
|
||||
/*
|
||||
* Font stuff, see freeglut_font.c
|
||||
*/
|
||||
FGAPI int FGAPIENTRY glutBitmapHeight( void* font );
|
||||
FGAPI GLfloat FGAPIENTRY glutStrokeHeight( void* font );
|
||||
FGAPI void FGAPIENTRY glutBitmapString( void* font, const unsigned char *string );
|
||||
FGAPI void FGAPIENTRY glutStrokeString( void* font, const unsigned char *string );
|
||||
|
||||
/*
|
||||
* Geometry functions, see freeglut_geometry.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutWireRhombicDodecahedron( void );
|
||||
FGAPI void FGAPIENTRY glutSolidRhombicDodecahedron( void );
|
||||
FGAPI void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale );
|
||||
FGAPI void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, GLdouble offset[3], GLdouble scale );
|
||||
FGAPI void FGAPIENTRY glutWireCylinder( GLdouble radius, GLdouble height, GLint slices, GLint stacks);
|
||||
FGAPI void FGAPIENTRY glutSolidCylinder( GLdouble radius, GLdouble height, GLint slices, GLint stacks);
|
||||
|
||||
/*
|
||||
* Extension functions, see freeglut_ext.c
|
||||
*/
|
||||
typedef void (*GLUTproc)();
|
||||
FGAPI GLUTproc FGAPIENTRY glutGetProcAddress( const char *procName );
|
||||
|
||||
/*
|
||||
* Multi-touch/multi-pointer extensions
|
||||
*/
|
||||
|
||||
#define GLUT_HAS_MULTI 1
|
||||
|
||||
FGAPI void FGAPIENTRY glutMultiEntryFunc( void (* callback)( int, int ) );
|
||||
FGAPI void FGAPIENTRY glutMultiButtonFunc( void (* callback)( int, int, int, int, int ) );
|
||||
FGAPI void FGAPIENTRY glutMultiMotionFunc( void (* callback)( int, int, int ) );
|
||||
FGAPI void FGAPIENTRY glutMultiPassiveFunc( void (* callback)( int, int, int ) );
|
||||
|
||||
/*
|
||||
* Joystick functions, see freeglut_joystick.c
|
||||
*/
|
||||
/* USE OF THESE FUNCTIONS IS DEPRECATED !!!!! */
|
||||
/* If you have a serious need for these functions in your application, please either
|
||||
* contact the "freeglut" developer community at freeglut-developer@lists.sourceforge.net,
|
||||
* switch to the OpenGLUT library, or else port your joystick functionality over to PLIB's
|
||||
* "js" library.
|
||||
*/
|
||||
int glutJoystickGetNumAxes( int ident );
|
||||
int glutJoystickGetNumButtons( int ident );
|
||||
int glutJoystickNotWorking( int ident );
|
||||
float glutJoystickGetDeadBand( int ident, int axis );
|
||||
void glutJoystickSetDeadBand( int ident, int axis, float db );
|
||||
float glutJoystickGetSaturation( int ident, int axis );
|
||||
void glutJoystickSetSaturation( int ident, int axis, float st );
|
||||
void glutJoystickSetMinRange( int ident, float *axes );
|
||||
void glutJoystickSetMaxRange( int ident, float *axes );
|
||||
void glutJoystickSetCenter( int ident, float *axes );
|
||||
void glutJoystickGetMinRange( int ident, float *axes );
|
||||
void glutJoystickGetMaxRange( int ident, float *axes );
|
||||
void glutJoystickGetCenter( int ident, float *axes );
|
||||
|
||||
/*
|
||||
* Initialization functions, see freeglut_init.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutInitContextVersion( int majorVersion, int minorVersion );
|
||||
FGAPI void FGAPIENTRY glutInitContextFlags( int flags );
|
||||
FGAPI void FGAPIENTRY glutInitContextProfile( int profile );
|
||||
|
||||
/* to get the typedef for va_list */
|
||||
#include <stdarg.h>
|
||||
|
||||
FGAPI void FGAPIENTRY glutInitErrorFunc( void (* vError)( const char *fmt, va_list ap ) );
|
||||
FGAPI void FGAPIENTRY glutInitWarningFunc( void (* vWarning)( const char *fmt, va_list ap ) );
|
||||
|
||||
/*
|
||||
* GLUT API macro definitions -- the display mode definitions
|
||||
*/
|
||||
#define GLUT_CAPTIONLESS 0x0400
|
||||
#define GLUT_BORDERLESS 0x0800
|
||||
#define GLUT_SRGB 0x1000
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*** END OF FILE ***/
|
||||
|
||||
#endif /* __FREEGLUT_EXT_H__ */
|
630
SvoViewer/external/freeglut/include/GL/freeglut_std.h
vendored
Normal file
630
SvoViewer/external/freeglut/include/GL/freeglut_std.h
vendored
Normal file
|
@ -0,0 +1,630 @@
|
|||
#ifndef __FREEGLUT_STD_H__
|
||||
#define __FREEGLUT_STD_H__
|
||||
|
||||
/*
|
||||
* freeglut_std.h
|
||||
*
|
||||
* The GLUT-compatible part of the freeglut library include file
|
||||
*
|
||||
* Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
|
||||
* Written by Pawel W. Olszta, <olszta@sourceforge.net>
|
||||
* Creation date: Thu Dec 2 1999
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Under windows, we have to differentiate between static and dynamic libraries
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
/* #pragma may not be supported by some compilers.
|
||||
* Discussion by FreeGLUT developers suggests that
|
||||
* Visual C++ specific code involving pragmas may
|
||||
* need to move to a separate header. 24th Dec 2003
|
||||
*/
|
||||
|
||||
/* Define FREEGLUT_LIB_PRAGMAS to 1 to include library
|
||||
* pragmas or to 0 to exclude library pragmas.
|
||||
* The default behavior depends on the compiler/platform.
|
||||
*/
|
||||
# ifndef FREEGLUT_LIB_PRAGMAS
|
||||
# if ( defined(_MSC_VER) || defined(__WATCOMC__) ) && !defined(_WIN32_WCE)
|
||||
# define FREEGLUT_LIB_PRAGMAS 1
|
||||
# else
|
||||
# define FREEGLUT_LIB_PRAGMAS 0
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN 1
|
||||
# endif
|
||||
# ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
# endif
|
||||
# include <windows.h>
|
||||
|
||||
/* Windows static library */
|
||||
# ifdef FREEGLUT_STATIC
|
||||
|
||||
#error Static linking is not supported with this build. Please remove the FREEGLUT_STATIC preprocessor directive, or download the source code from http://freeglut.sf.net/ and build against that.
|
||||
|
||||
/* Windows shared library (DLL) */
|
||||
# else
|
||||
|
||||
# define FGAPIENTRY __stdcall
|
||||
# if defined(FREEGLUT_EXPORTS)
|
||||
# define FGAPI __declspec(dllexport)
|
||||
# else
|
||||
# define FGAPI __declspec(dllimport)
|
||||
|
||||
/* Link with Win32 shared freeglut lib */
|
||||
# if FREEGLUT_LIB_PRAGMAS
|
||||
# pragma comment (lib, "freeglut.lib")
|
||||
# endif
|
||||
|
||||
# endif
|
||||
|
||||
# endif
|
||||
|
||||
/* Drag in other Windows libraries as required by FreeGLUT */
|
||||
# if FREEGLUT_LIB_PRAGMAS
|
||||
# pragma comment (lib, "glu32.lib") /* link OpenGL Utility lib */
|
||||
# pragma comment (lib, "opengl32.lib") /* link Microsoft OpenGL lib */
|
||||
# pragma comment (lib, "gdi32.lib") /* link Windows GDI lib */
|
||||
# pragma comment (lib, "winmm.lib") /* link Windows MultiMedia lib */
|
||||
# pragma comment (lib, "user32.lib") /* link Windows user lib */
|
||||
# endif
|
||||
|
||||
#else
|
||||
|
||||
/* Non-Windows definition of FGAPI and FGAPIENTRY */
|
||||
# define FGAPI
|
||||
# define FGAPIENTRY
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The freeglut and GLUT API versions
|
||||
*/
|
||||
#define FREEGLUT 1
|
||||
#define GLUT_API_VERSION 4
|
||||
#define GLUT_XLIB_IMPLEMENTATION 13
|
||||
/* Deprecated:
|
||||
cf. http://sourceforge.net/mailarchive/forum.php?thread_name=CABcAi1hw7cr4xtigckaGXB5X8wddLfMcbA_rZ3NAuwMrX_zmsw%40mail.gmail.com&forum_name=freeglut-developer */
|
||||
#define FREEGLUT_VERSION_2_0 1
|
||||
|
||||
/*
|
||||
* Always include OpenGL and GLU headers
|
||||
*/
|
||||
#if __APPLE__
|
||||
# include <OpenGL/gl.h>
|
||||
# include <OpenGL/glu.h>
|
||||
#else
|
||||
# include <GL/gl.h>
|
||||
# include <GL/glu.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* GLUT API macro definitions -- the special key codes:
|
||||
*/
|
||||
#define GLUT_KEY_F1 0x0001
|
||||
#define GLUT_KEY_F2 0x0002
|
||||
#define GLUT_KEY_F3 0x0003
|
||||
#define GLUT_KEY_F4 0x0004
|
||||
#define GLUT_KEY_F5 0x0005
|
||||
#define GLUT_KEY_F6 0x0006
|
||||
#define GLUT_KEY_F7 0x0007
|
||||
#define GLUT_KEY_F8 0x0008
|
||||
#define GLUT_KEY_F9 0x0009
|
||||
#define GLUT_KEY_F10 0x000A
|
||||
#define GLUT_KEY_F11 0x000B
|
||||
#define GLUT_KEY_F12 0x000C
|
||||
#define GLUT_KEY_LEFT 0x0064
|
||||
#define GLUT_KEY_UP 0x0065
|
||||
#define GLUT_KEY_RIGHT 0x0066
|
||||
#define GLUT_KEY_DOWN 0x0067
|
||||
#define GLUT_KEY_PAGE_UP 0x0068
|
||||
#define GLUT_KEY_PAGE_DOWN 0x0069
|
||||
#define GLUT_KEY_HOME 0x006A
|
||||
#define GLUT_KEY_END 0x006B
|
||||
#define GLUT_KEY_INSERT 0x006C
|
||||
|
||||
/*
|
||||
* GLUT API macro definitions -- mouse state definitions
|
||||
*/
|
||||
#define GLUT_LEFT_BUTTON 0x0000
|
||||
#define GLUT_MIDDLE_BUTTON 0x0001
|
||||
#define GLUT_RIGHT_BUTTON 0x0002
|
||||
#define GLUT_DOWN 0x0000
|
||||
#define GLUT_UP 0x0001
|
||||
#define GLUT_LEFT 0x0000
|
||||
#define GLUT_ENTERED 0x0001
|
||||
|
||||
/*
|
||||
* GLUT API macro definitions -- the display mode definitions
|
||||
*/
|
||||
#define GLUT_RGB 0x0000
|
||||
#define GLUT_RGBA 0x0000
|
||||
#define GLUT_INDEX 0x0001
|
||||
#define GLUT_SINGLE 0x0000
|
||||
#define GLUT_DOUBLE 0x0002
|
||||
#define GLUT_ACCUM 0x0004
|
||||
#define GLUT_ALPHA 0x0008
|
||||
#define GLUT_DEPTH 0x0010
|
||||
#define GLUT_STENCIL 0x0020
|
||||
#define GLUT_MULTISAMPLE 0x0080
|
||||
#define GLUT_STEREO 0x0100
|
||||
#define GLUT_LUMINANCE 0x0200
|
||||
|
||||
/*
|
||||
* GLUT API macro definitions -- windows and menu related definitions
|
||||
*/
|
||||
#define GLUT_MENU_NOT_IN_USE 0x0000
|
||||
#define GLUT_MENU_IN_USE 0x0001
|
||||
#define GLUT_NOT_VISIBLE 0x0000
|
||||
#define GLUT_VISIBLE 0x0001
|
||||
#define GLUT_HIDDEN 0x0000
|
||||
#define GLUT_FULLY_RETAINED 0x0001
|
||||
#define GLUT_PARTIALLY_RETAINED 0x0002
|
||||
#define GLUT_FULLY_COVERED 0x0003
|
||||
|
||||
/*
|
||||
* GLUT API macro definitions -- fonts definitions
|
||||
*
|
||||
* Steve Baker suggested to make it binary compatible with GLUT:
|
||||
*/
|
||||
#if defined(_MSC_VER) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__WATCOMC__)
|
||||
# define GLUT_STROKE_ROMAN ((void *)0x0000)
|
||||
# define GLUT_STROKE_MONO_ROMAN ((void *)0x0001)
|
||||
# define GLUT_BITMAP_9_BY_15 ((void *)0x0002)
|
||||
# define GLUT_BITMAP_8_BY_13 ((void *)0x0003)
|
||||
# define GLUT_BITMAP_TIMES_ROMAN_10 ((void *)0x0004)
|
||||
# define GLUT_BITMAP_TIMES_ROMAN_24 ((void *)0x0005)
|
||||
# define GLUT_BITMAP_HELVETICA_10 ((void *)0x0006)
|
||||
# define GLUT_BITMAP_HELVETICA_12 ((void *)0x0007)
|
||||
# define GLUT_BITMAP_HELVETICA_18 ((void *)0x0008)
|
||||
#else
|
||||
/*
|
||||
* I don't really know if it's a good idea... But here it goes:
|
||||
*/
|
||||
extern void* glutStrokeRoman;
|
||||
extern void* glutStrokeMonoRoman;
|
||||
extern void* glutBitmap9By15;
|
||||
extern void* glutBitmap8By13;
|
||||
extern void* glutBitmapTimesRoman10;
|
||||
extern void* glutBitmapTimesRoman24;
|
||||
extern void* glutBitmapHelvetica10;
|
||||
extern void* glutBitmapHelvetica12;
|
||||
extern void* glutBitmapHelvetica18;
|
||||
|
||||
/*
|
||||
* Those pointers will be used by following definitions:
|
||||
*/
|
||||
# define GLUT_STROKE_ROMAN ((void *) &glutStrokeRoman)
|
||||
# define GLUT_STROKE_MONO_ROMAN ((void *) &glutStrokeMonoRoman)
|
||||
# define GLUT_BITMAP_9_BY_15 ((void *) &glutBitmap9By15)
|
||||
# define GLUT_BITMAP_8_BY_13 ((void *) &glutBitmap8By13)
|
||||
# define GLUT_BITMAP_TIMES_ROMAN_10 ((void *) &glutBitmapTimesRoman10)
|
||||
# define GLUT_BITMAP_TIMES_ROMAN_24 ((void *) &glutBitmapTimesRoman24)
|
||||
# define GLUT_BITMAP_HELVETICA_10 ((void *) &glutBitmapHelvetica10)
|
||||
# define GLUT_BITMAP_HELVETICA_12 ((void *) &glutBitmapHelvetica12)
|
||||
# define GLUT_BITMAP_HELVETICA_18 ((void *) &glutBitmapHelvetica18)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* GLUT API macro definitions -- the glutGet parameters
|
||||
*/
|
||||
#define GLUT_WINDOW_X 0x0064
|
||||
#define GLUT_WINDOW_Y 0x0065
|
||||
#define GLUT_WINDOW_WIDTH 0x0066
|
||||
#define GLUT_WINDOW_HEIGHT 0x0067
|
||||
#define GLUT_WINDOW_BUFFER_SIZE 0x0068
|
||||
#define GLUT_WINDOW_STENCIL_SIZE 0x0069
|
||||
#define GLUT_WINDOW_DEPTH_SIZE 0x006A
|
||||
#define GLUT_WINDOW_RED_SIZE 0x006B
|
||||
#define GLUT_WINDOW_GREEN_SIZE 0x006C
|
||||
#define GLUT_WINDOW_BLUE_SIZE 0x006D
|
||||
#define GLUT_WINDOW_ALPHA_SIZE 0x006E
|
||||
#define GLUT_WINDOW_ACCUM_RED_SIZE 0x006F
|
||||
#define GLUT_WINDOW_ACCUM_GREEN_SIZE 0x0070
|
||||
#define GLUT_WINDOW_ACCUM_BLUE_SIZE 0x0071
|
||||
#define GLUT_WINDOW_ACCUM_ALPHA_SIZE 0x0072
|
||||
#define GLUT_WINDOW_DOUBLEBUFFER 0x0073
|
||||
#define GLUT_WINDOW_RGBA 0x0074
|
||||
#define GLUT_WINDOW_PARENT 0x0075
|
||||
#define GLUT_WINDOW_NUM_CHILDREN 0x0076
|
||||
#define GLUT_WINDOW_COLORMAP_SIZE 0x0077
|
||||
#define GLUT_WINDOW_NUM_SAMPLES 0x0078
|
||||
#define GLUT_WINDOW_STEREO 0x0079
|
||||
#define GLUT_WINDOW_CURSOR 0x007A
|
||||
|
||||
#define GLUT_SCREEN_WIDTH 0x00C8
|
||||
#define GLUT_SCREEN_HEIGHT 0x00C9
|
||||
#define GLUT_SCREEN_WIDTH_MM 0x00CA
|
||||
#define GLUT_SCREEN_HEIGHT_MM 0x00CB
|
||||
#define GLUT_MENU_NUM_ITEMS 0x012C
|
||||
#define GLUT_DISPLAY_MODE_POSSIBLE 0x0190
|
||||
#define GLUT_INIT_WINDOW_X 0x01F4
|
||||
#define GLUT_INIT_WINDOW_Y 0x01F5
|
||||
#define GLUT_INIT_WINDOW_WIDTH 0x01F6
|
||||
#define GLUT_INIT_WINDOW_HEIGHT 0x01F7
|
||||
#define GLUT_INIT_DISPLAY_MODE 0x01F8
|
||||
#define GLUT_ELAPSED_TIME 0x02BC
|
||||
#define GLUT_WINDOW_FORMAT_ID 0x007B
|
||||
|
||||
/*
|
||||
* GLUT API macro definitions -- the glutDeviceGet parameters
|
||||
*/
|
||||
#define GLUT_HAS_KEYBOARD 0x0258
|
||||
#define GLUT_HAS_MOUSE 0x0259
|
||||
#define GLUT_HAS_SPACEBALL 0x025A
|
||||
#define GLUT_HAS_DIAL_AND_BUTTON_BOX 0x025B
|
||||
#define GLUT_HAS_TABLET 0x025C
|
||||
#define GLUT_NUM_MOUSE_BUTTONS 0x025D
|
||||
#define GLUT_NUM_SPACEBALL_BUTTONS 0x025E
|
||||
#define GLUT_NUM_BUTTON_BOX_BUTTONS 0x025F
|
||||
#define GLUT_NUM_DIALS 0x0260
|
||||
#define GLUT_NUM_TABLET_BUTTONS 0x0261
|
||||
#define GLUT_DEVICE_IGNORE_KEY_REPEAT 0x0262
|
||||
#define GLUT_DEVICE_KEY_REPEAT 0x0263
|
||||
#define GLUT_HAS_JOYSTICK 0x0264
|
||||
#define GLUT_OWNS_JOYSTICK 0x0265
|
||||
#define GLUT_JOYSTICK_BUTTONS 0x0266
|
||||
#define GLUT_JOYSTICK_AXES 0x0267
|
||||
#define GLUT_JOYSTICK_POLL_RATE 0x0268
|
||||
|
||||
/*
|
||||
* GLUT API macro definitions -- the glutLayerGet parameters
|
||||
*/
|
||||
#define GLUT_OVERLAY_POSSIBLE 0x0320
|
||||
#define GLUT_LAYER_IN_USE 0x0321
|
||||
#define GLUT_HAS_OVERLAY 0x0322
|
||||
#define GLUT_TRANSPARENT_INDEX 0x0323
|
||||
#define GLUT_NORMAL_DAMAGED 0x0324
|
||||
#define GLUT_OVERLAY_DAMAGED 0x0325
|
||||
|
||||
/*
|
||||
* GLUT API macro definitions -- the glutVideoResizeGet parameters
|
||||
*/
|
||||
#define GLUT_VIDEO_RESIZE_POSSIBLE 0x0384
|
||||
#define GLUT_VIDEO_RESIZE_IN_USE 0x0385
|
||||
#define GLUT_VIDEO_RESIZE_X_DELTA 0x0386
|
||||
#define GLUT_VIDEO_RESIZE_Y_DELTA 0x0387
|
||||
#define GLUT_VIDEO_RESIZE_WIDTH_DELTA 0x0388
|
||||
#define GLUT_VIDEO_RESIZE_HEIGHT_DELTA 0x0389
|
||||
#define GLUT_VIDEO_RESIZE_X 0x038A
|
||||
#define GLUT_VIDEO_RESIZE_Y 0x038B
|
||||
#define GLUT_VIDEO_RESIZE_WIDTH 0x038C
|
||||
#define GLUT_VIDEO_RESIZE_HEIGHT 0x038D
|
||||
|
||||
/*
|
||||
* GLUT API macro definitions -- the glutUseLayer parameters
|
||||
*/
|
||||
#define GLUT_NORMAL 0x0000
|
||||
#define GLUT_OVERLAY 0x0001
|
||||
|
||||
/*
|
||||
* GLUT API macro definitions -- the glutGetModifiers parameters
|
||||
*/
|
||||
#define GLUT_ACTIVE_SHIFT 0x0001
|
||||
#define GLUT_ACTIVE_CTRL 0x0002
|
||||
#define GLUT_ACTIVE_ALT 0x0004
|
||||
|
||||
/*
|
||||
* GLUT API macro definitions -- the glutSetCursor parameters
|
||||
*/
|
||||
#define GLUT_CURSOR_RIGHT_ARROW 0x0000
|
||||
#define GLUT_CURSOR_LEFT_ARROW 0x0001
|
||||
#define GLUT_CURSOR_INFO 0x0002
|
||||
#define GLUT_CURSOR_DESTROY 0x0003
|
||||
#define GLUT_CURSOR_HELP 0x0004
|
||||
#define GLUT_CURSOR_CYCLE 0x0005
|
||||
#define GLUT_CURSOR_SPRAY 0x0006
|
||||
#define GLUT_CURSOR_WAIT 0x0007
|
||||
#define GLUT_CURSOR_TEXT 0x0008
|
||||
#define GLUT_CURSOR_CROSSHAIR 0x0009
|
||||
#define GLUT_CURSOR_UP_DOWN 0x000A
|
||||
#define GLUT_CURSOR_LEFT_RIGHT 0x000B
|
||||
#define GLUT_CURSOR_TOP_SIDE 0x000C
|
||||
#define GLUT_CURSOR_BOTTOM_SIDE 0x000D
|
||||
#define GLUT_CURSOR_LEFT_SIDE 0x000E
|
||||
#define GLUT_CURSOR_RIGHT_SIDE 0x000F
|
||||
#define GLUT_CURSOR_TOP_LEFT_CORNER 0x0010
|
||||
#define GLUT_CURSOR_TOP_RIGHT_CORNER 0x0011
|
||||
#define GLUT_CURSOR_BOTTOM_RIGHT_CORNER 0x0012
|
||||
#define GLUT_CURSOR_BOTTOM_LEFT_CORNER 0x0013
|
||||
#define GLUT_CURSOR_INHERIT 0x0064
|
||||
#define GLUT_CURSOR_NONE 0x0065
|
||||
#define GLUT_CURSOR_FULL_CROSSHAIR 0x0066
|
||||
|
||||
/*
|
||||
* GLUT API macro definitions -- RGB color component specification definitions
|
||||
*/
|
||||
#define GLUT_RED 0x0000
|
||||
#define GLUT_GREEN 0x0001
|
||||
#define GLUT_BLUE 0x0002
|
||||
|
||||
/*
|
||||
* GLUT API macro definitions -- additional keyboard and joystick definitions
|
||||
*/
|
||||
#define GLUT_KEY_REPEAT_OFF 0x0000
|
||||
#define GLUT_KEY_REPEAT_ON 0x0001
|
||||
#define GLUT_KEY_REPEAT_DEFAULT 0x0002
|
||||
|
||||
#define GLUT_JOYSTICK_BUTTON_A 0x0001
|
||||
#define GLUT_JOYSTICK_BUTTON_B 0x0002
|
||||
#define GLUT_JOYSTICK_BUTTON_C 0x0004
|
||||
#define GLUT_JOYSTICK_BUTTON_D 0x0008
|
||||
|
||||
/*
|
||||
* GLUT API macro definitions -- game mode definitions
|
||||
*/
|
||||
#define GLUT_GAME_MODE_ACTIVE 0x0000
|
||||
#define GLUT_GAME_MODE_POSSIBLE 0x0001
|
||||
#define GLUT_GAME_MODE_WIDTH 0x0002
|
||||
#define GLUT_GAME_MODE_HEIGHT 0x0003
|
||||
#define GLUT_GAME_MODE_PIXEL_DEPTH 0x0004
|
||||
#define GLUT_GAME_MODE_REFRESH_RATE 0x0005
|
||||
#define GLUT_GAME_MODE_DISPLAY_CHANGED 0x0006
|
||||
|
||||
/*
|
||||
* Initialization functions, see fglut_init.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutInit( int* pargc, char** argv );
|
||||
FGAPI void FGAPIENTRY glutInitWindowPosition( int x, int y );
|
||||
FGAPI void FGAPIENTRY glutInitWindowSize( int width, int height );
|
||||
FGAPI void FGAPIENTRY glutInitDisplayMode( unsigned int displayMode );
|
||||
FGAPI void FGAPIENTRY glutInitDisplayString( const char* displayMode );
|
||||
|
||||
/*
|
||||
* Process loop function, see freeglut_main.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutMainLoop( void );
|
||||
|
||||
/*
|
||||
* Window management functions, see freeglut_window.c
|
||||
*/
|
||||
FGAPI int FGAPIENTRY glutCreateWindow( const char* title );
|
||||
FGAPI int FGAPIENTRY glutCreateSubWindow( int window, int x, int y, int width, int height );
|
||||
FGAPI void FGAPIENTRY glutDestroyWindow( int window );
|
||||
FGAPI void FGAPIENTRY glutSetWindow( int window );
|
||||
FGAPI int FGAPIENTRY glutGetWindow( void );
|
||||
FGAPI void FGAPIENTRY glutSetWindowTitle( const char* title );
|
||||
FGAPI void FGAPIENTRY glutSetIconTitle( const char* title );
|
||||
FGAPI void FGAPIENTRY glutReshapeWindow( int width, int height );
|
||||
FGAPI void FGAPIENTRY glutPositionWindow( int x, int y );
|
||||
FGAPI void FGAPIENTRY glutShowWindow( void );
|
||||
FGAPI void FGAPIENTRY glutHideWindow( void );
|
||||
FGAPI void FGAPIENTRY glutIconifyWindow( void );
|
||||
FGAPI void FGAPIENTRY glutPushWindow( void );
|
||||
FGAPI void FGAPIENTRY glutPopWindow( void );
|
||||
FGAPI void FGAPIENTRY glutFullScreen( void );
|
||||
|
||||
/*
|
||||
* Display-connected functions, see freeglut_display.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutPostWindowRedisplay( int window );
|
||||
FGAPI void FGAPIENTRY glutPostRedisplay( void );
|
||||
FGAPI void FGAPIENTRY glutSwapBuffers( void );
|
||||
|
||||
/*
|
||||
* Mouse cursor functions, see freeglut_cursor.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutWarpPointer( int x, int y );
|
||||
FGAPI void FGAPIENTRY glutSetCursor( int cursor );
|
||||
|
||||
/*
|
||||
* Overlay stuff, see freeglut_overlay.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutEstablishOverlay( void );
|
||||
FGAPI void FGAPIENTRY glutRemoveOverlay( void );
|
||||
FGAPI void FGAPIENTRY glutUseLayer( GLenum layer );
|
||||
FGAPI void FGAPIENTRY glutPostOverlayRedisplay( void );
|
||||
FGAPI void FGAPIENTRY glutPostWindowOverlayRedisplay( int window );
|
||||
FGAPI void FGAPIENTRY glutShowOverlay( void );
|
||||
FGAPI void FGAPIENTRY glutHideOverlay( void );
|
||||
|
||||
/*
|
||||
* Menu stuff, see freeglut_menu.c
|
||||
*/
|
||||
FGAPI int FGAPIENTRY glutCreateMenu( void (* callback)( int menu ) );
|
||||
FGAPI void FGAPIENTRY glutDestroyMenu( int menu );
|
||||
FGAPI int FGAPIENTRY glutGetMenu( void );
|
||||
FGAPI void FGAPIENTRY glutSetMenu( int menu );
|
||||
FGAPI void FGAPIENTRY glutAddMenuEntry( const char* label, int value );
|
||||
FGAPI void FGAPIENTRY glutAddSubMenu( const char* label, int subMenu );
|
||||
FGAPI void FGAPIENTRY glutChangeToMenuEntry( int item, const char* label, int value );
|
||||
FGAPI void FGAPIENTRY glutChangeToSubMenu( int item, const char* label, int value );
|
||||
FGAPI void FGAPIENTRY glutRemoveMenuItem( int item );
|
||||
FGAPI void FGAPIENTRY glutAttachMenu( int button );
|
||||
FGAPI void FGAPIENTRY glutDetachMenu( int button );
|
||||
|
||||
/*
|
||||
* Global callback functions, see freeglut_callbacks.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutTimerFunc( unsigned int time, void (* callback)( int ), int value );
|
||||
FGAPI void FGAPIENTRY glutIdleFunc( void (* callback)( void ) );
|
||||
|
||||
/*
|
||||
* Window-specific callback functions, see freeglut_callbacks.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutKeyboardFunc( void (* callback)( unsigned char, int, int ) );
|
||||
FGAPI void FGAPIENTRY glutSpecialFunc( void (* callback)( int, int, int ) );
|
||||
FGAPI void FGAPIENTRY glutReshapeFunc( void (* callback)( int, int ) );
|
||||
FGAPI void FGAPIENTRY glutVisibilityFunc( void (* callback)( int ) );
|
||||
FGAPI void FGAPIENTRY glutDisplayFunc( void (* callback)( void ) );
|
||||
FGAPI void FGAPIENTRY glutMouseFunc( void (* callback)( int, int, int, int ) );
|
||||
FGAPI void FGAPIENTRY glutMotionFunc( void (* callback)( int, int ) );
|
||||
FGAPI void FGAPIENTRY glutPassiveMotionFunc( void (* callback)( int, int ) );
|
||||
FGAPI void FGAPIENTRY glutEntryFunc( void (* callback)( int ) );
|
||||
|
||||
FGAPI void FGAPIENTRY glutKeyboardUpFunc( void (* callback)( unsigned char, int, int ) );
|
||||
FGAPI void FGAPIENTRY glutSpecialUpFunc( void (* callback)( int, int, int ) );
|
||||
FGAPI void FGAPIENTRY glutJoystickFunc( void (* callback)( unsigned int, int, int, int ), int pollInterval );
|
||||
FGAPI void FGAPIENTRY glutMenuStateFunc( void (* callback)( int ) );
|
||||
FGAPI void FGAPIENTRY glutMenuStatusFunc( void (* callback)( int, int, int ) );
|
||||
FGAPI void FGAPIENTRY glutOverlayDisplayFunc( void (* callback)( void ) );
|
||||
FGAPI void FGAPIENTRY glutWindowStatusFunc( void (* callback)( int ) );
|
||||
|
||||
FGAPI void FGAPIENTRY glutSpaceballMotionFunc( void (* callback)( int, int, int ) );
|
||||
FGAPI void FGAPIENTRY glutSpaceballRotateFunc( void (* callback)( int, int, int ) );
|
||||
FGAPI void FGAPIENTRY glutSpaceballButtonFunc( void (* callback)( int, int ) );
|
||||
FGAPI void FGAPIENTRY glutButtonBoxFunc( void (* callback)( int, int ) );
|
||||
FGAPI void FGAPIENTRY glutDialsFunc( void (* callback)( int, int ) );
|
||||
FGAPI void FGAPIENTRY glutTabletMotionFunc( void (* callback)( int, int ) );
|
||||
FGAPI void FGAPIENTRY glutTabletButtonFunc( void (* callback)( int, int, int, int ) );
|
||||
|
||||
/*
|
||||
* State setting and retrieval functions, see freeglut_state.c
|
||||
*/
|
||||
FGAPI int FGAPIENTRY glutGet( GLenum query );
|
||||
FGAPI int FGAPIENTRY glutDeviceGet( GLenum query );
|
||||
FGAPI int FGAPIENTRY glutGetModifiers( void );
|
||||
FGAPI int FGAPIENTRY glutLayerGet( GLenum query );
|
||||
|
||||
/*
|
||||
* Font stuff, see freeglut_font.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutBitmapCharacter( void* font, int character );
|
||||
FGAPI int FGAPIENTRY glutBitmapWidth( void* font, int character );
|
||||
FGAPI void FGAPIENTRY glutStrokeCharacter( void* font, int character );
|
||||
FGAPI int FGAPIENTRY glutStrokeWidth( void* font, int character );
|
||||
FGAPI int FGAPIENTRY glutBitmapLength( void* font, const unsigned char* string );
|
||||
FGAPI int FGAPIENTRY glutStrokeLength( void* font, const unsigned char* string );
|
||||
|
||||
/*
|
||||
* Geometry functions, see freeglut_geometry.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutWireCube( GLdouble size );
|
||||
FGAPI void FGAPIENTRY glutSolidCube( GLdouble size );
|
||||
FGAPI void FGAPIENTRY glutWireSphere( GLdouble radius, GLint slices, GLint stacks );
|
||||
FGAPI void FGAPIENTRY glutSolidSphere( GLdouble radius, GLint slices, GLint stacks );
|
||||
FGAPI void FGAPIENTRY glutWireCone( GLdouble base, GLdouble height, GLint slices, GLint stacks );
|
||||
FGAPI void FGAPIENTRY glutSolidCone( GLdouble base, GLdouble height, GLint slices, GLint stacks );
|
||||
|
||||
FGAPI void FGAPIENTRY glutWireTorus( GLdouble innerRadius, GLdouble outerRadius, GLint sides, GLint rings );
|
||||
FGAPI void FGAPIENTRY glutSolidTorus( GLdouble innerRadius, GLdouble outerRadius, GLint sides, GLint rings );
|
||||
FGAPI void FGAPIENTRY glutWireDodecahedron( void );
|
||||
FGAPI void FGAPIENTRY glutSolidDodecahedron( void );
|
||||
FGAPI void FGAPIENTRY glutWireOctahedron( void );
|
||||
FGAPI void FGAPIENTRY glutSolidOctahedron( void );
|
||||
FGAPI void FGAPIENTRY glutWireTetrahedron( void );
|
||||
FGAPI void FGAPIENTRY glutSolidTetrahedron( void );
|
||||
FGAPI void FGAPIENTRY glutWireIcosahedron( void );
|
||||
FGAPI void FGAPIENTRY glutSolidIcosahedron( void );
|
||||
|
||||
/*
|
||||
* Teapot rendering functions, found in freeglut_teapot.c
|
||||
* NB: front facing polygons have clockwise winding, not counter clockwise
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutWireTeapot( GLdouble size );
|
||||
FGAPI void FGAPIENTRY glutSolidTeapot( GLdouble size );
|
||||
|
||||
/*
|
||||
* Game mode functions, see freeglut_gamemode.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutGameModeString( const char* string );
|
||||
FGAPI int FGAPIENTRY glutEnterGameMode( void );
|
||||
FGAPI void FGAPIENTRY glutLeaveGameMode( void );
|
||||
FGAPI int FGAPIENTRY glutGameModeGet( GLenum query );
|
||||
|
||||
/*
|
||||
* Video resize functions, see freeglut_videoresize.c
|
||||
*/
|
||||
FGAPI int FGAPIENTRY glutVideoResizeGet( GLenum query );
|
||||
FGAPI void FGAPIENTRY glutSetupVideoResizing( void );
|
||||
FGAPI void FGAPIENTRY glutStopVideoResizing( void );
|
||||
FGAPI void FGAPIENTRY glutVideoResize( int x, int y, int width, int height );
|
||||
FGAPI void FGAPIENTRY glutVideoPan( int x, int y, int width, int height );
|
||||
|
||||
/*
|
||||
* Colormap functions, see freeglut_misc.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutSetColor( int color, GLfloat red, GLfloat green, GLfloat blue );
|
||||
FGAPI GLfloat FGAPIENTRY glutGetColor( int color, int component );
|
||||
FGAPI void FGAPIENTRY glutCopyColormap( int window );
|
||||
|
||||
/*
|
||||
* Misc keyboard and joystick functions, see freeglut_misc.c
|
||||
*/
|
||||
FGAPI void FGAPIENTRY glutIgnoreKeyRepeat( int ignore );
|
||||
FGAPI void FGAPIENTRY glutSetKeyRepeat( int repeatMode );
|
||||
FGAPI void FGAPIENTRY glutForceJoystickFunc( void );
|
||||
|
||||
/*
|
||||
* Misc functions, see freeglut_misc.c
|
||||
*/
|
||||
FGAPI int FGAPIENTRY glutExtensionSupported( const char* extension );
|
||||
FGAPI void FGAPIENTRY glutReportErrors( void );
|
||||
|
||||
/* Comment from glut.h of classic GLUT:
|
||||
|
||||
Win32 has an annoying issue where there are multiple C run-time
|
||||
libraries (CRTs). If the executable is linked with a different CRT
|
||||
from the GLUT DLL, the GLUT DLL will not share the same CRT static
|
||||
data seen by the executable. In particular, atexit callbacks registered
|
||||
in the executable will not be called if GLUT calls its (different)
|
||||
exit routine). GLUT is typically built with the
|
||||
"/MD" option (the CRT with multithreading DLL support), but the Visual
|
||||
C++ linker default is "/ML" (the single threaded CRT).
|
||||
|
||||
One workaround to this issue is requiring users to always link with
|
||||
the same CRT as GLUT is compiled with. That requires users supply a
|
||||
non-standard option. GLUT 3.7 has its own built-in workaround where
|
||||
the executable's "exit" function pointer is covertly passed to GLUT.
|
||||
GLUT then calls the executable's exit function pointer to ensure that
|
||||
any "atexit" calls registered by the application are called if GLUT
|
||||
needs to exit.
|
||||
|
||||
Note that the __glut*WithExit routines should NEVER be called directly.
|
||||
To avoid the atexit workaround, #define GLUT_DISABLE_ATEXIT_HACK. */
|
||||
|
||||
/* to get the prototype for exit() */
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(_WIN32) && !defined(GLUT_DISABLE_ATEXIT_HACK) && !defined(__WATCOMC__)
|
||||
FGAPI void FGAPIENTRY __glutInitWithExit(int *argcp, char **argv, void (__cdecl *exitfunc)(int));
|
||||
FGAPI int FGAPIENTRY __glutCreateWindowWithExit(const char *title, void (__cdecl *exitfunc)(int));
|
||||
FGAPI int FGAPIENTRY __glutCreateMenuWithExit(void (* func)(int), void (__cdecl *exitfunc)(int));
|
||||
#ifndef FREEGLUT_BUILDING_LIB
|
||||
#if defined(__GNUC__)
|
||||
#define FGUNUSED __attribute__((unused))
|
||||
#else
|
||||
#define FGUNUSED
|
||||
#endif
|
||||
static void FGAPIENTRY FGUNUSED glutInit_ATEXIT_HACK(int *argcp, char **argv) { __glutInitWithExit(argcp, argv, exit); }
|
||||
#define glutInit glutInit_ATEXIT_HACK
|
||||
static int FGAPIENTRY FGUNUSED glutCreateWindow_ATEXIT_HACK(const char *title) { return __glutCreateWindowWithExit(title, exit); }
|
||||
#define glutCreateWindow glutCreateWindow_ATEXIT_HACK
|
||||
static int FGAPIENTRY FGUNUSED glutCreateMenu_ATEXIT_HACK(void (* func)(int)) { return __glutCreateMenuWithExit(func, exit); }
|
||||
#define glutCreateMenu glutCreateMenu_ATEXIT_HACK
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*** END OF FILE ***/
|
||||
|
||||
#endif /* __FREEGLUT_STD_H__ */
|
||||
|
21
SvoViewer/external/freeglut/include/GL/glut.h
vendored
Normal file
21
SvoViewer/external/freeglut/include/GL/glut.h
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef __GLUT_H__
|
||||
#define __GLUT_H__
|
||||
|
||||
/*
|
||||
* glut.h
|
||||
*
|
||||
* The freeglut library include file
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "freeglut_std.h"
|
||||
|
||||
/*** END OF FILE ***/
|
||||
|
||||
#endif /* __GLUT_H__ */
|
BIN
SvoViewer/external/freeglut/lib/freeglut.lib
vendored
Normal file
BIN
SvoViewer/external/freeglut/lib/freeglut.lib
vendored
Normal file
Binary file not shown.
BIN
SvoViewer/external/freeglut/lib/x64/freeglut.lib
vendored
Normal file
BIN
SvoViewer/external/freeglut/lib/x64/freeglut.lib
vendored
Normal file
Binary file not shown.
73
SvoViewer/external/glew/LICENSE.txt
vendored
Normal file
73
SvoViewer/external/glew/LICENSE.txt
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
The OpenGL Extension Wrangler Library
|
||||
Copyright (C) 2002-2007, Milan Ikits <milan ikits[]ieee org>
|
||||
Copyright (C) 2002-2007, Marcelo E. Magallon <mmagallo[]debian org>
|
||||
Copyright (C) 2002, Lev Povalahev
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* The name of the author may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Mesa 3-D graphics library
|
||||
Version: 7.0
|
||||
|
||||
Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
Copyright (c) 2007 The Khronos Group Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and/or associated documentation files (the
|
||||
"Materials"), to deal in the Materials without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
permit persons to whom the Materials are furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Materials.
|
||||
|
||||
THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
BIN
SvoViewer/external/glew/bin/Release MX/Win32/glew32mx.dll
vendored
Normal file
BIN
SvoViewer/external/glew/bin/Release MX/Win32/glew32mx.dll
vendored
Normal file
Binary file not shown.
BIN
SvoViewer/external/glew/bin/Release MX/x64/glew32mx.dll
vendored
Normal file
BIN
SvoViewer/external/glew/bin/Release MX/x64/glew32mx.dll
vendored
Normal file
Binary file not shown.
BIN
SvoViewer/external/glew/bin/Release/Win32/glew32.dll
vendored
Normal file
BIN
SvoViewer/external/glew/bin/Release/Win32/glew32.dll
vendored
Normal file
Binary file not shown.
BIN
SvoViewer/external/glew/bin/Release/Win32/glewinfo.exe
vendored
Normal file
BIN
SvoViewer/external/glew/bin/Release/Win32/glewinfo.exe
vendored
Normal file
Binary file not shown.
BIN
SvoViewer/external/glew/bin/Release/Win32/visualinfo.exe
vendored
Normal file
BIN
SvoViewer/external/glew/bin/Release/Win32/visualinfo.exe
vendored
Normal file
Binary file not shown.
BIN
SvoViewer/external/glew/bin/Release/x64/glew32.dll
vendored
Normal file
BIN
SvoViewer/external/glew/bin/Release/x64/glew32.dll
vendored
Normal file
Binary file not shown.
BIN
SvoViewer/external/glew/bin/Release/x64/glewinfo.exe
vendored
Normal file
BIN
SvoViewer/external/glew/bin/Release/x64/glewinfo.exe
vendored
Normal file
Binary file not shown.
BIN
SvoViewer/external/glew/bin/Release/x64/visualinfo.exe
vendored
Normal file
BIN
SvoViewer/external/glew/bin/Release/x64/visualinfo.exe
vendored
Normal file
Binary file not shown.
18062
SvoViewer/external/glew/include/GL/glew.h
vendored
Normal file
18062
SvoViewer/external/glew/include/GL/glew.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
1669
SvoViewer/external/glew/include/GL/glxew.h
vendored
Normal file
1669
SvoViewer/external/glew/include/GL/glxew.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
1421
SvoViewer/external/glew/include/GL/wglew.h
vendored
Normal file
1421
SvoViewer/external/glew/include/GL/wglew.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
BIN
SvoViewer/external/glew/lib/Release MX/Win32/glew32mx.lib
vendored
Normal file
BIN
SvoViewer/external/glew/lib/Release MX/Win32/glew32mx.lib
vendored
Normal file
Binary file not shown.
BIN
SvoViewer/external/glew/lib/Release MX/Win32/glew32mxs.lib
vendored
Normal file
BIN
SvoViewer/external/glew/lib/Release MX/Win32/glew32mxs.lib
vendored
Normal file
Binary file not shown.
BIN
SvoViewer/external/glew/lib/Release MX/x64/glew32mx.lib
vendored
Normal file
BIN
SvoViewer/external/glew/lib/Release MX/x64/glew32mx.lib
vendored
Normal file
Binary file not shown.
BIN
SvoViewer/external/glew/lib/Release MX/x64/glew32mxs.lib
vendored
Normal file
BIN
SvoViewer/external/glew/lib/Release MX/x64/glew32mxs.lib
vendored
Normal file
Binary file not shown.
BIN
SvoViewer/external/glew/lib/Release/Win32/glew32.lib
vendored
Normal file
BIN
SvoViewer/external/glew/lib/Release/Win32/glew32.lib
vendored
Normal file
Binary file not shown.
BIN
SvoViewer/external/glew/lib/Release/Win32/glew32s.lib
vendored
Normal file
BIN
SvoViewer/external/glew/lib/Release/Win32/glew32s.lib
vendored
Normal file
Binary file not shown.
BIN
SvoViewer/external/glew/lib/Release/x64/glew32.lib
vendored
Normal file
BIN
SvoViewer/external/glew/lib/Release/x64/glew32.lib
vendored
Normal file
Binary file not shown.
BIN
SvoViewer/external/glew/lib/Release/x64/glew32s.lib
vendored
Normal file
BIN
SvoViewer/external/glew/lib/Release/x64/glew32s.lib
vendored
Normal file
Binary file not shown.
189
SvoViewer/src/AABoundingVolume.h
Normal file
189
SvoViewer/src/AABoundingVolume.h
Normal file
|
@ -0,0 +1,189 @@
|
|||
//
|
||||
// AABoundingVolume.h - Axis Aligned Bounding Volumes
|
||||
// hifi
|
||||
//
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef _AABOUNDING_VOLUME_
|
||||
#define _AABOUNDING_VOLUME_
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
|
||||
|
||||
// A simple axis aligned bounding volume.
|
||||
#define AABF_NUM_DIMS 3
|
||||
#define AABF_LOW 0
|
||||
#define AABF_HIGH 1
|
||||
//Todo:: make this a template.
|
||||
class AABoundingVolume
|
||||
{
|
||||
public:
|
||||
AABoundingVolume() : _numPointsInSet(0), _isSingleDirectionSet(false) { 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
|
250
SvoViewer/src/Camera.cpp
Executable file
250
SvoViewer/src/Camera.cpp
Executable file
|
@ -0,0 +1,250 @@
|
|||
//
|
||||
// Camera.cpp
|
||||
// interface
|
||||
//
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
|
||||
#include "SvoViewerConfig.h"
|
||||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
#include <VoxelConstants.h>
|
||||
|
||||
#include "Camera.h"
|
||||
|
||||
const float CAMERA_FIRST_PERSON_MODE_UP_SHIFT = 0.0f;
|
||||
const float CAMERA_FIRST_PERSON_MODE_DISTANCE = 0.0f;
|
||||
const float CAMERA_FIRST_PERSON_MODE_TIGHTNESS = 100.0f;
|
||||
|
||||
const float CAMERA_INDEPENDENT_MODE_UP_SHIFT = 0.0f;
|
||||
const float CAMERA_INDEPENDENT_MODE_DISTANCE = 0.0f;
|
||||
const float CAMERA_INDEPENDENT_MODE_TIGHTNESS = 100.0f;
|
||||
|
||||
const float CAMERA_THIRD_PERSON_MODE_UP_SHIFT = -0.2f;
|
||||
const float CAMERA_THIRD_PERSON_MODE_DISTANCE = 1.5f;
|
||||
const float CAMERA_THIRD_PERSON_MODE_TIGHTNESS = 8.0f;
|
||||
|
||||
const float CAMERA_MIRROR_MODE_UP_SHIFT = 0.0f;
|
||||
const float CAMERA_MIRROR_MODE_DISTANCE = 0.17f;
|
||||
const float CAMERA_MIRROR_MODE_TIGHTNESS = 100.0f;
|
||||
|
||||
|
||||
Camera::Camera() :
|
||||
_needsToInitialize(true),
|
||||
_mode(CAMERA_MODE_THIRD_PERSON),
|
||||
_prevMode(CAMERA_MODE_THIRD_PERSON),
|
||||
_frustumNeedsReshape(true),
|
||||
_position(0.0f, 0.0f, 0.0f),
|
||||
_idealPosition(0.0f, 0.0f, 0.0f),
|
||||
_targetPosition(0.0f, 0.0f, 0.0f),
|
||||
_fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES),
|
||||
_aspectRatio(16.f/9.f),
|
||||
_nearClip(0.08f), // default
|
||||
_farClip(50.0f * TREE_SCALE), // default
|
||||
_upShift(0.0f),
|
||||
_distance(0.0f),
|
||||
_tightness(10.0f), // default
|
||||
_previousUpShift(0.0f),
|
||||
_previousDistance(0.0f),
|
||||
_previousTightness(0.0f),
|
||||
_newUpShift(0.0f),
|
||||
_newDistance(0.0f),
|
||||
_newTightness(0.0f),
|
||||
_modeShift(1.0f),
|
||||
_linearModeShift(0.0f),
|
||||
_modeShiftPeriod(1.0f),
|
||||
_scale(1.0f),
|
||||
_lookingAt(0.0f, 0.0f, 0.0f),
|
||||
_isKeepLookingAt(false)
|
||||
{
|
||||
}
|
||||
|
||||
void Camera::update(float deltaTime) {
|
||||
|
||||
if (_mode != CAMERA_MODE_NULL) {
|
||||
// use iterative forces to push the camera towards the target position and angle
|
||||
updateFollowMode(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
// use iterative forces to keep the camera at the desired position and angle
|
||||
void Camera::updateFollowMode(float deltaTime) {
|
||||
if (_linearModeShift < 1.0f) {
|
||||
_linearModeShift += deltaTime / _modeShiftPeriod;
|
||||
if (_needsToInitialize || _linearModeShift > 1.0f) {
|
||||
_linearModeShift = 1.0f;
|
||||
_modeShift = 1.0f;
|
||||
_upShift = _newUpShift;
|
||||
_distance = _newDistance;
|
||||
_tightness = _newTightness;
|
||||
} else {
|
||||
_modeShift = ONE_HALF - ONE_HALF * cosf(_linearModeShift * PIE );
|
||||
_upShift = _previousUpShift * (1.0f - _modeShift) + _newUpShift * _modeShift;
|
||||
_distance = _previousDistance * (1.0f - _modeShift) + _newDistance * _modeShift;
|
||||
_tightness = _previousTightness * (1.0f - _modeShift) + _newTightness * _modeShift;
|
||||
}
|
||||
}
|
||||
|
||||
// derive t from tightness
|
||||
float t = _tightness * _modeShift * deltaTime;
|
||||
if (t > 1.0) {
|
||||
t = 1.0;
|
||||
}
|
||||
|
||||
// handle keepLookingAt
|
||||
if (_isKeepLookingAt) {
|
||||
lookAt(_lookingAt);
|
||||
}
|
||||
|
||||
// Update position and rotation, setting directly if tightness is 0.0
|
||||
if (_needsToInitialize || (_tightness == 0.0f)) {
|
||||
_rotation = _targetRotation;
|
||||
_idealPosition = _targetPosition + _scale * (_rotation * glm::vec3(0.0f, _upShift, _distance));
|
||||
_position = _idealPosition;
|
||||
_needsToInitialize = false;
|
||||
} else {
|
||||
// pull rotation towards ideal
|
||||
_rotation = safeMix(_rotation, _targetRotation, t);
|
||||
_idealPosition = _targetPosition + _scale * (_rotation * glm::vec3(0.0f, _upShift, _distance));
|
||||
_position += (_idealPosition - _position) * t;
|
||||
}
|
||||
}
|
||||
|
||||
float Camera::getFarClip() const {
|
||||
return (_scale * _farClip < std::numeric_limits<int16_t>::max())
|
||||
? _scale * _farClip
|
||||
: std::numeric_limits<int16_t>::max() - 1;
|
||||
}
|
||||
|
||||
void Camera::setModeShiftPeriod (float period) {
|
||||
const float MIN_PERIOD = 0.001f;
|
||||
const float MAX_PERIOD = 3.0f;
|
||||
_modeShiftPeriod = glm::clamp(period, MIN_PERIOD, MAX_PERIOD);
|
||||
}
|
||||
|
||||
void Camera::setMode(CameraMode m) {
|
||||
|
||||
_prevMode = _mode;
|
||||
_mode = m;
|
||||
_modeShift = 0.0;
|
||||
_linearModeShift = 0.0;
|
||||
|
||||
_previousUpShift = _upShift;
|
||||
_previousDistance = _distance;
|
||||
_previousTightness = _tightness;
|
||||
|
||||
if (_mode == CAMERA_MODE_THIRD_PERSON) {
|
||||
_newUpShift = CAMERA_THIRD_PERSON_MODE_UP_SHIFT;
|
||||
_newDistance = CAMERA_THIRD_PERSON_MODE_DISTANCE;
|
||||
_newTightness = CAMERA_THIRD_PERSON_MODE_TIGHTNESS;
|
||||
} else if (_mode == CAMERA_MODE_FIRST_PERSON) {
|
||||
_newUpShift = CAMERA_FIRST_PERSON_MODE_UP_SHIFT;
|
||||
_newDistance = CAMERA_FIRST_PERSON_MODE_DISTANCE;
|
||||
_newTightness = CAMERA_FIRST_PERSON_MODE_TIGHTNESS;
|
||||
} else if (_mode == CAMERA_MODE_MIRROR) {
|
||||
_newUpShift = CAMERA_MIRROR_MODE_UP_SHIFT;
|
||||
_newDistance = CAMERA_MIRROR_MODE_DISTANCE;
|
||||
_newTightness = CAMERA_MIRROR_MODE_TIGHTNESS;
|
||||
} else if (_mode == CAMERA_MODE_INDEPENDENT) {
|
||||
_newUpShift = CAMERA_INDEPENDENT_MODE_UP_SHIFT;
|
||||
_newDistance = CAMERA_INDEPENDENT_MODE_DISTANCE;
|
||||
_newTightness = CAMERA_INDEPENDENT_MODE_TIGHTNESS;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::setTargetPosition(const glm::vec3& t) {
|
||||
_targetPosition = t;
|
||||
|
||||
// handle keepLookingAt
|
||||
if (_isKeepLookingAt) {
|
||||
lookAt(_lookingAt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Camera::setTargetRotation( const glm::quat& targetRotation ) {
|
||||
_targetRotation = targetRotation;
|
||||
}
|
||||
|
||||
void Camera::setFieldOfView(float f) {
|
||||
_fieldOfView = f;
|
||||
_frustumNeedsReshape = true;
|
||||
}
|
||||
|
||||
void Camera::setAspectRatio(float a) {
|
||||
_aspectRatio = a;
|
||||
_frustumNeedsReshape = true;
|
||||
}
|
||||
|
||||
void Camera::setNearClip(float n) {
|
||||
_nearClip = n;
|
||||
_frustumNeedsReshape = true;
|
||||
}
|
||||
|
||||
void Camera::setFarClip(float f) {
|
||||
_farClip = f;
|
||||
_frustumNeedsReshape = true;
|
||||
}
|
||||
|
||||
void Camera::setEyeOffsetPosition(const glm::vec3& p) {
|
||||
_eyeOffsetPosition = p;
|
||||
_frustumNeedsReshape = true;
|
||||
}
|
||||
|
||||
void Camera::setEyeOffsetOrientation(const glm::quat& o) {
|
||||
_eyeOffsetOrientation = o;
|
||||
_frustumNeedsReshape = true;
|
||||
}
|
||||
|
||||
void Camera::setScale(float s) {
|
||||
_scale = s;
|
||||
_needsToInitialize = true;
|
||||
_frustumNeedsReshape = true;
|
||||
}
|
||||
|
||||
void Camera::initialize() {
|
||||
_needsToInitialize = true;
|
||||
_modeShift = 0.0;
|
||||
}
|
||||
|
||||
// call to find out if the view frustum needs to be reshaped
|
||||
bool Camera::getFrustumNeedsReshape() const {
|
||||
return _frustumNeedsReshape;
|
||||
}
|
||||
|
||||
// call this when deciding whether to render the head or not
|
||||
CameraMode Camera::getInterpolatedMode() const {
|
||||
const float SHIFT_THRESHOLD_INTO_FIRST_PERSON = 0.7f;
|
||||
const float SHIFT_THRESHOLD_OUT_OF_FIRST_PERSON = 0.6f;
|
||||
if ((_mode == CAMERA_MODE_FIRST_PERSON && _linearModeShift < SHIFT_THRESHOLD_INTO_FIRST_PERSON) ||
|
||||
(_prevMode == CAMERA_MODE_FIRST_PERSON && _linearModeShift < SHIFT_THRESHOLD_OUT_OF_FIRST_PERSON)) {
|
||||
return _prevMode;
|
||||
}
|
||||
return _mode;
|
||||
}
|
||||
|
||||
// call this after reshaping the view frustum
|
||||
void Camera::setFrustumWasReshaped() {
|
||||
_frustumNeedsReshape = false;
|
||||
}
|
||||
|
||||
void Camera::lookAt(const glm::vec3& lookAt) {
|
||||
glm::vec3 up = IDENTITY_UP;
|
||||
glm::mat4 lookAtMatrix = glm::lookAt(_targetPosition, lookAt, up);
|
||||
glm::quat rotation = glm::quat_cast(lookAtMatrix);
|
||||
rotation.w = -rotation.w; // Rosedale approved
|
||||
setTargetRotation(rotation);
|
||||
}
|
||||
|
||||
void Camera::keepLookingAt(const glm::vec3& point) {
|
||||
lookAt(point);
|
||||
_isKeepLookingAt = true;
|
||||
_lookingAt = point;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
119
SvoViewer/src/Camera.h
Executable file
119
SvoViewer/src/Camera.h
Executable file
|
@ -0,0 +1,119 @@
|
|||
//
|
||||
// Camera.h
|
||||
// interface
|
||||
//
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__camera__
|
||||
#define __interface__camera__
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
//const float DEFAULT_FIELD_OF_VIEW_DEGREES = 90.0f;
|
||||
|
||||
enum CameraMode
|
||||
{
|
||||
CAMERA_MODE_NULL = -1,
|
||||
CAMERA_MODE_THIRD_PERSON,
|
||||
CAMERA_MODE_FIRST_PERSON,
|
||||
CAMERA_MODE_MIRROR,
|
||||
CAMERA_MODE_INDEPENDENT,
|
||||
NUM_CAMERA_MODES
|
||||
};
|
||||
|
||||
class Camera {
|
||||
|
||||
public:
|
||||
Camera();
|
||||
|
||||
void initialize(); // instantly put the camera at the ideal position and rotation.
|
||||
|
||||
void update( float deltaTime );
|
||||
|
||||
void setUpShift(float u) { _upShift = u; }
|
||||
void setDistance(float d) { _distance = d; }
|
||||
void setPosition(const glm::vec3& p) { _position = p; }
|
||||
void setTargetPosition(const glm::vec3& t);
|
||||
void setTightness(float t) { _tightness = t; }
|
||||
void setTargetRotation(const glm::quat& rotation);
|
||||
|
||||
void setMode(CameraMode m);
|
||||
void setModeShiftPeriod(float r);
|
||||
void setFieldOfView(float f);
|
||||
void setAspectRatio(float a);
|
||||
void setNearClip(float n);
|
||||
void setFarClip(float f);
|
||||
void setEyeOffsetPosition(const glm::vec3& p);
|
||||
void setEyeOffsetOrientation(const glm::quat& o);
|
||||
void setScale(const float s);
|
||||
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
const glm::quat& getRotation() const { return _rotation; }
|
||||
CameraMode getMode() const { return _mode; }
|
||||
const glm::vec3& getTargetPosition() const { return _targetPosition; }
|
||||
const glm::quat& getTargetRotation() const { return _targetRotation; }
|
||||
float getFieldOfView() const { return _fieldOfView; }
|
||||
float getAspectRatio() const { return _aspectRatio; }
|
||||
float getNearClip() const { return _scale * _nearClip; }
|
||||
float getFarClip() const;
|
||||
const glm::vec3& getEyeOffsetPosition() const { return _eyeOffsetPosition; }
|
||||
const glm::quat& getEyeOffsetOrientation() const { return _eyeOffsetOrientation; }
|
||||
float getScale() const { return _scale; }
|
||||
|
||||
CameraMode getInterpolatedMode() const;
|
||||
|
||||
bool getFrustumNeedsReshape() const; // call to find out if the view frustum needs to be reshaped
|
||||
void setFrustumWasReshaped(); // call this after reshaping the view frustum.
|
||||
|
||||
// These only work on independent cameras
|
||||
/// one time change to what the camera is looking at
|
||||
void lookAt(const glm::vec3& value);
|
||||
|
||||
/// fix what the camera is looking at, and keep the camera looking at this even if position changes
|
||||
void keepLookingAt(const glm::vec3& value);
|
||||
|
||||
/// stops the keep looking at feature, doesn't change what's being looked at, but will stop camera from
|
||||
/// continuing to update it's orientation to keep looking at the item
|
||||
void stopLooking() { _isKeepLookingAt = false; }
|
||||
|
||||
private:
|
||||
|
||||
bool _needsToInitialize;
|
||||
CameraMode _mode;
|
||||
CameraMode _prevMode;
|
||||
bool _frustumNeedsReshape;
|
||||
glm::vec3 _position;
|
||||
glm::vec3 _idealPosition;
|
||||
glm::vec3 _targetPosition;
|
||||
float _fieldOfView;
|
||||
float _aspectRatio;
|
||||
float _nearClip;
|
||||
float _farClip;
|
||||
glm::vec3 _eyeOffsetPosition;
|
||||
glm::quat _eyeOffsetOrientation;
|
||||
glm::quat _rotation;
|
||||
glm::quat _targetRotation;
|
||||
float _upShift;
|
||||
float _distance;
|
||||
float _tightness;
|
||||
float _previousUpShift;
|
||||
float _previousDistance;
|
||||
float _previousTightness;
|
||||
float _newUpShift;
|
||||
float _newDistance;
|
||||
float _newTightness;
|
||||
float _modeShift;
|
||||
float _linearModeShift;
|
||||
float _modeShiftPeriod;
|
||||
float _scale;
|
||||
|
||||
glm::vec3 _lookingAt;
|
||||
bool _isKeepLookingAt;
|
||||
|
||||
void updateFollowMode(float deltaTime);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
88
SvoViewer/src/GLCanvas.cpp
Executable file
88
SvoViewer/src/GLCanvas.cpp
Executable file
|
@ -0,0 +1,88 @@
|
|||
//
|
||||
// GLCanvas.cpp
|
||||
// hifi
|
||||
//
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "SvoViewerConfig.h"
|
||||
|
||||
#include "svoviewer.h"
|
||||
|
||||
#include "GLCanvas.h"
|
||||
#include <QMimeData>
|
||||
#include <QUrl>
|
||||
|
||||
GLCanvas::GLCanvas() : QGLWidget(QGLFormat(QGL::NoDepthBuffer, QGL::NoStencilBuffer)) {
|
||||
}
|
||||
|
||||
void GLCanvas::initializeGL() {
|
||||
SvoViewer::getInstance()->initializeGL();
|
||||
setAttribute(Qt::WA_AcceptTouchEvents);
|
||||
setAcceptDrops(true);
|
||||
}
|
||||
|
||||
void GLCanvas::paintGL() {
|
||||
SvoViewer::getInstance()->paintGL();
|
||||
}
|
||||
|
||||
void GLCanvas::resizeGL(int width, int height) {
|
||||
SvoViewer::getInstance()->resizeGL(width, height);
|
||||
}
|
||||
|
||||
void GLCanvas::keyPressEvent(QKeyEvent* event) {
|
||||
SvoViewer::getInstance()->keyPressEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::keyReleaseEvent(QKeyEvent* event) {
|
||||
SvoViewer::getInstance()->keyReleaseEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::mouseMoveEvent(QMouseEvent* event) {
|
||||
SvoViewer::getInstance()->mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::mousePressEvent(QMouseEvent* event) {
|
||||
SvoViewer::getInstance()->mousePressEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::mouseReleaseEvent(QMouseEvent* event) {
|
||||
SvoViewer::getInstance()->mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
int updateTime = 0;
|
||||
bool GLCanvas::event(QEvent* event) {
|
||||
/* switch (event->type()) {
|
||||
case QEvent::TouchBegin:
|
||||
SvoViewer::getInstance()->touchBeginEvent(static_cast<QTouchEvent*>(event));
|
||||
event->accept();
|
||||
return true;
|
||||
case QEvent::TouchEnd:
|
||||
SvoViewer::getInstance()->touchEndEvent(static_cast<QTouchEvent*>(event));
|
||||
return true;
|
||||
case QEvent::TouchUpdate:
|
||||
SvoViewer::getInstance()->touchUpdateEvent(static_cast<QTouchEvent*>(event));
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}*/
|
||||
return QGLWidget::event(event);
|
||||
}
|
||||
|
||||
void GLCanvas::wheelEvent(QWheelEvent* event) {
|
||||
//SvoViewer::getInstance()->wheelEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::dragEnterEvent(QDragEnterEvent* event) {
|
||||
/*const QMimeData *mimeData = event->mimeData();
|
||||
foreach (QUrl url, mimeData->urls()) {
|
||||
if (url.url().toLower().endsWith(SNAPSHOT_EXTENSION)) {
|
||||
event->acceptProposedAction();
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
void GLCanvas::dropEvent(QDropEvent* event) {
|
||||
//SvoViewer::getInstance()->dropEvent(event);
|
||||
}
|
37
SvoViewer/src/GLCanvas.h
Normal file
37
SvoViewer/src/GLCanvas.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// GLCanvas.h
|
||||
//
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__GLCanvas__
|
||||
#define __hifi__GLCanvas__
|
||||
|
||||
#include <QGLWidget>
|
||||
|
||||
/// customized canvas that simply forwards requests/events to the singleton application
|
||||
class GLCanvas : public QGLWidget {
|
||||
public:
|
||||
GLCanvas();
|
||||
protected:
|
||||
|
||||
virtual void initializeGL();
|
||||
virtual void paintGL();
|
||||
virtual void resizeGL(int width, int height);
|
||||
|
||||
virtual void keyPressEvent(QKeyEvent* event);
|
||||
virtual void keyReleaseEvent(QKeyEvent* event);
|
||||
|
||||
virtual void mouseMoveEvent(QMouseEvent* event);
|
||||
virtual void mousePressEvent(QMouseEvent* event);
|
||||
virtual void mouseReleaseEvent(QMouseEvent* event);
|
||||
|
||||
virtual bool event(QEvent* event);
|
||||
|
||||
virtual void wheelEvent(QWheelEvent* event);
|
||||
|
||||
virtual void dragEnterEvent(QDragEnterEvent *event);
|
||||
virtual void dropEvent(QDropEvent* event);
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__GLCanvas__) */
|
754
SvoViewer/src/Render.cpp
Executable file
754
SvoViewer/src/Render.cpp
Executable file
|
@ -0,0 +1,754 @@
|
|||
//
|
||||
// Render.cpp
|
||||
//
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
#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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
qDebug("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;
|
||||
qDebug("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.
|
||||
{
|
||||
//qDebug("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()
|
||||
{
|
||||
qDebug("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;
|
||||
// TODO: this was Matt's original windows code, it doesn't compile on mac, due to type mismatches
|
||||
#ifdef WIN32
|
||||
glCompileShaderARB(_vertexShader);
|
||||
glGetShaderInfoLog(_vertexShader, 1000, &shaderLogLength, shaderLog);
|
||||
if (shaderLog[0] != 0) qDebug("Shaderlog v :\n %s\n", shaderLog);
|
||||
glCompileShaderARB(_geometryShader);
|
||||
glGetShaderInfoLog(_geometryShader, 1000, &shaderLogLength, shaderLog);
|
||||
if (shaderLog[0] != 0) qDebug("Shaderlog g :\n %s\n", shaderLog);
|
||||
glCompileShaderARB(_pixelShader);
|
||||
glGetShaderInfoLog(_pixelShader, 51000, &shaderLogLength, shaderLog);
|
||||
if (shaderLog[0] != 0) qDebug("Shaderlog p :\n %s\n", shaderLog);
|
||||
#endif //def WIN32
|
||||
|
||||
_linkProgram = glCreateProgram();
|
||||
glAttachShader(_linkProgram, _vertexShader);
|
||||
glAttachShader(_linkProgram, _geometryShader);
|
||||
glAttachShader(_linkProgram, _pixelShader);
|
||||
glLinkProgram(_linkProgram);
|
||||
GLint linked;
|
||||
glGetProgramiv(_linkProgram, GL_LINK_STATUS, &linked);
|
||||
if (!linked) qDebug("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++;
|
||||
qDebug("child node %d %d has %d leaves and %d children itself\n", i, j, data.numLeaves, childNode2ndOrder->getChildCount());
|
||||
if (_numSegments >= MAX_NUM_OCTREE_PARTITIONS ) { qDebug("Out of segment space??? What the?\n"); break; }
|
||||
}
|
||||
}
|
||||
if (_numSegments >= MAX_NUM_OCTREE_PARTITIONS ) { qDebug("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);
|
||||
qDebug("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);
|
||||
|
||||
qDebug("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);
|
||||
}
|
||||
|
||||
|
6
SvoViewer/src/Render.h
Executable file
6
SvoViewer/src/Render.h
Executable file
|
@ -0,0 +1,6 @@
|
|||
//
|
||||
// Render.h
|
||||
//
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
375
SvoViewer/src/Render2.cpp
Executable file
375
SvoViewer/src/Render2.cpp
Executable file
|
@ -0,0 +1,375 @@
|
|||
//
|
||||
// Render2.cpp
|
||||
//
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#include "svoviewer.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Helper functions
|
||||
|
||||
// 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++;
|
||||
qDebug("child node %d %d has %d leaves and %d children itself\n", i, j, data.numLeaves, childNode2ndOrder->getChildCount());
|
||||
if (_numSegments >= MAX_NUM_OCTREE_PARTITIONS ) { qDebug("Out of segment space??? What the?\n"); break; }
|
||||
}
|
||||
}
|
||||
if (_numSegments >= MAX_NUM_OCTREE_PARTITIONS ) { qDebug("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);
|
||||
qDebug("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]);
|
||||
}
|
||||
|
||||
qDebug("Partition %d, vertices %d, indices %d, discarded %d\n", i, args.vtxCount, idxCount, args.discardedCount);
|
||||
|
||||
delete [] _readVertexStructs;
|
||||
//delete [] _readIndicesArray;
|
||||
delete [] faceCenters;
|
||||
for (int k = 0; k < NUM_CUBE_FACES; k++) if (_segmentIdxBuffers[i].idxBuff[k] != NULL) delete [] _segmentIdxBuffers[i].idxBuff[k];
|
||||
}
|
||||
|
||||
_voxelOptRenderInitialized = true;
|
||||
|
||||
UpdateOpt2BVFaceVisibility();
|
||||
|
||||
quint64 endInit = usecTimestampNow();
|
||||
quint64 elapsed = endInit - startInit;
|
||||
qDebug() << "init elapsed:" << ((float)elapsed / (float)1000000.0f) << "seconds";
|
||||
|
||||
}
|
||||
|
||||
void SvoViewer::RenderTreeSystemAsOpt2Voxels()
|
||||
{
|
||||
glEnable(GL_LIGHTING);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
// disable specular lighting for ground and voxels
|
||||
glMaterialfv(GL_FRONT, GL_SPECULAR, NO_SPECULAR_COLOR);
|
||||
|
||||
setupWorldLight();
|
||||
_numElemsDrawn = 0;
|
||||
for (int i = 0; i < _numSegments; i++)
|
||||
{
|
||||
if (_displayOnlyPartition == i || _displayOnlyPartition == NO_PARTITION )
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboOVerticesIds[i]);
|
||||
glVertexAttribPointer(ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,position));
|
||||
glEnableVertexAttribArray(ATTRIB_POSITION);
|
||||
|
||||
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,color));
|
||||
glEnableVertexAttribArray(ATTRIB_COLOR);
|
||||
|
||||
//glVertexPointer(3, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex,position));
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), (void*)offsetof(Vertex,color));
|
||||
|
||||
for (int j = 0; j < NUM_CUBE_FACES; j++)
|
||||
{
|
||||
// Add aggressive LOD check here.
|
||||
if (_segmentIdxBuffers[i].visibleFace[j] || _useBoundingVolumes != true)
|
||||
{
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _segmentIdxBuffers[i].idxIds[j]);//_vboOIndicesIds[i]);
|
||||
glDrawElements(GL_TRIANGLES, _segmentIdxBuffers[i].elemCount[j]*3, GL_UNSIGNED_INT, NULL);
|
||||
_numElemsDrawn += _segmentIdxBuffers[i].elemCount[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
glDisableVertexAttribArray(ATTRIB_POSITION);
|
||||
glDisableVertexAttribArray(ATTRIB_COLOR);
|
||||
}
|
||||
|
||||
// special rules for single direction bv sets. Basically, we intersect a lookat ray from the camera with two opposite faces and discard
|
||||
// the entire set of the face that is further away as it must be back facing.
|
||||
void SvoViewer::UpdateOpt2BVFaceVisibility()
|
||||
{
|
||||
if (_currentShaderModel != RENDER_OPT_CULLED_POLYS || _voxelOptRenderInitialized != true ) return;
|
||||
|
||||
float faceParamVals[NUM_CUBE_FACES];
|
||||
glm::vec3 pos = _myCamera.getPosition();
|
||||
|
||||
for (int i = 0; i < _numSegments; i++)
|
||||
{
|
||||
VoxelDimIdxSet* setPtr = &_segmentIdxBuffers[i];
|
||||
// Fast cull check.
|
||||
setPtr->visibleFace[0] = (_segmentIdxBuffers[i].bounds[0].within(pos.y, 1) >= 0) ? true : false;
|
||||
setPtr->visibleFace[1] = (_segmentIdxBuffers[i].bounds[1].within(pos.y, 1) <= 0) ? true : false;
|
||||
setPtr->visibleFace[2] = (_segmentIdxBuffers[i].bounds[2].within(pos.x, 0) >= 0) ? true : false;
|
||||
setPtr->visibleFace[3] = (_segmentIdxBuffers[i].bounds[3].within(pos.x, 0) <= 0) ? true : false;
|
||||
setPtr->visibleFace[4] = (_segmentIdxBuffers[i].bounds[4].within(pos.z, 2) <= 0) ? true : false;
|
||||
setPtr->visibleFace[5] = (_segmentIdxBuffers[i].bounds[5].within(pos.z, 2) >= 0) ? true : false;
|
||||
|
||||
// Make sure its actually on the screen.
|
||||
/*for (int j = 0; j < NUM_CUBE_FACES; j++)
|
||||
{
|
||||
if (setPtr->visibleFace[j])
|
||||
{
|
||||
if (visibleAngleSubtended(&_segmentIdxBuffers[i].bounds[j], &_myCamera, &_viewFrustum) <= 0)
|
||||
setPtr->visibleFace[j] = false;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
/*
|
||||
for (int j = 0; j < NUM_CUBE_FACES; j++)
|
||||
{
|
||||
setPtr->visibleFace[i] = true;
|
||||
AABoundingVolume* volume = &_segmentIdxBuffers[i].bounds[j];
|
||||
glm::vec3 randomPlaneVtx = volume->getCorner((BoxVertex)SvoViewerNames::cubeFaceIndices[j][0]);
|
||||
glm::vec3 raydir = randomPlaneVtx - pos;
|
||||
rayder /= glm::length(raydir);
|
||||
if (glm::dot(target, raydir) < 1) raydir *= -1;
|
||||
if (!parameterizedRayPlaneIntersection(pos, raydir, randomPlaneVtx, SvoViewerNames::faceNormals[j], &faceParamVals[j]))
|
||||
faceParamVals[j] = -1;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void SvoViewer::StopUsingVoxelOpt2RenderSystem()
|
||||
{
|
||||
glDisableVertexAttribArray(ATTRIB_POSITION);
|
||||
glDisableVertexAttribArray(ATTRIB_COLOR);
|
||||
glDisable(GL_LIGHTING);
|
||||
}
|
BIN
SvoViewer/src/SvoViewer.ico
Executable file
BIN
SvoViewer/src/SvoViewer.ico
Executable file
Binary file not shown.
After (image error) Size: 2.7 KiB |
2
SvoViewer/src/SvoViewer.rc
Executable file
2
SvoViewer/src/SvoViewer.rc
Executable file
|
@ -0,0 +1,2 @@
|
|||
IDI_ICON1 ICON DISCARDABLE "SvoViewer.ico"
|
||||
|
190
SvoViewer/src/TextRenderer.cpp
Normal file
190
SvoViewer/src/TextRenderer.cpp
Normal file
|
@ -0,0 +1,190 @@
|
|||
//
|
||||
// TextRenderer.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 4/24/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
|
||||
#include <QFont>
|
||||
#include <QPaintEngine>
|
||||
#include <QtDebug>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include "SvoViewerConfig.h"
|
||||
#include "TextRenderer.h"
|
||||
|
||||
// the width/height of the cached glyph textures
|
||||
const int IMAGE_SIZE = 256;
|
||||
|
||||
Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int width) :
|
||||
_textureID(textureID), _location(location), _bounds(bounds), _width(width) {
|
||||
}
|
||||
|
||||
TextRenderer::TextRenderer(const char* family, int pointSize, int weight,
|
||||
bool italic, EffectType effectType, int effectThickness)
|
||||
: _font(family, pointSize, weight, italic), _metrics(_font), _effectType(effectType),
|
||||
_effectThickness(effectThickness), _x(IMAGE_SIZE), _y(IMAGE_SIZE), _rowHeight(0) {
|
||||
_font.setKerning(false);
|
||||
}
|
||||
|
||||
TextRenderer::~TextRenderer() {
|
||||
glDeleteTextures(_allTextureIDs.size(), _allTextureIDs.constData());
|
||||
}
|
||||
|
||||
int TextRenderer::calculateHeight(const char* str) {
|
||||
int maxHeight = 0;
|
||||
for (const char* ch = str; *ch != 0; ch++) {
|
||||
const Glyph& glyph = getGlyph(*ch);
|
||||
if (glyph.textureID() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (glyph.bounds().height() > maxHeight) {
|
||||
maxHeight = glyph.bounds().height();
|
||||
}
|
||||
}
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
int TextRenderer::draw(int x, int y, const char* str) {
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
int maxHeight = 0;
|
||||
for (const char* ch = str; *ch != 0; ch++) {
|
||||
const Glyph& glyph = getGlyph(*ch);
|
||||
if (glyph.textureID() == 0) {
|
||||
x += glyph.width();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (glyph.bounds().height() > maxHeight) {
|
||||
maxHeight = glyph.bounds().height();
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, glyph.textureID());
|
||||
|
||||
int left = x + glyph.bounds().x();
|
||||
int right = x + glyph.bounds().x() + glyph.bounds().width();
|
||||
int bottom = y + glyph.bounds().y();
|
||||
int top = y + glyph.bounds().y() + glyph.bounds().height();
|
||||
|
||||
float scale = 1.0 / IMAGE_SIZE;
|
||||
float ls = glyph.location().x() * scale;
|
||||
float rs = (glyph.location().x() + glyph.bounds().width()) * scale;
|
||||
float bt = glyph.location().y() * scale;
|
||||
float tt = (glyph.location().y() + glyph.bounds().height()) * scale;
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(ls, bt);
|
||||
glVertex2f(left, bottom);
|
||||
glTexCoord2f(rs, bt);
|
||||
glVertex2f(right, bottom);
|
||||
glTexCoord2f(rs, tt);
|
||||
glVertex2f(right, top);
|
||||
glTexCoord2f(ls, tt);
|
||||
glVertex2f(left, top);
|
||||
glEnd();
|
||||
|
||||
x += glyph.width();
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
int TextRenderer::computeWidth(char ch)
|
||||
{
|
||||
return getGlyph(ch).width();
|
||||
}
|
||||
|
||||
int TextRenderer::computeWidth(const char* str)
|
||||
{
|
||||
int width = 0;
|
||||
for (const char* ch = str; *ch != 0; ch++) {
|
||||
width += computeWidth(*ch);
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
const Glyph& TextRenderer::getGlyph(char c) {
|
||||
Glyph& glyph = _glyphs[c];
|
||||
if (glyph.isValid()) {
|
||||
return glyph;
|
||||
}
|
||||
// we use 'J' as a representative size for the solid block character
|
||||
QChar ch = (c == SOLID_BLOCK_CHAR) ? QChar('J') : QChar(c);
|
||||
QRect bounds = _metrics.boundingRect(ch);
|
||||
if (bounds.isEmpty()) {
|
||||
glyph = Glyph(0, QPoint(), QRect(), _metrics.width(ch));
|
||||
return glyph;
|
||||
}
|
||||
// grow the bounds to account for effect, if any
|
||||
if (_effectType == SHADOW_EFFECT) {
|
||||
bounds.adjust(-_effectThickness, 0, 0, _effectThickness);
|
||||
|
||||
} else if (_effectType == OUTLINE_EFFECT) {
|
||||
bounds.adjust(-_effectThickness, -_effectThickness, _effectThickness, _effectThickness);
|
||||
}
|
||||
|
||||
// grow the bounds to account for antialiasing
|
||||
bounds.adjust(-1, -1, 1, 1);
|
||||
|
||||
if (_x + bounds.width() > IMAGE_SIZE) {
|
||||
// we can't fit it on the current row; move to next
|
||||
_y += _rowHeight;
|
||||
_x = _rowHeight = 0;
|
||||
}
|
||||
if (_y + bounds.height() > IMAGE_SIZE) {
|
||||
// can't fit it on current texture; make a new one
|
||||
glGenTextures(1, &_currentTextureID);
|
||||
_x = _y = _rowHeight = 0;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, _currentTextureID);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
_allTextureIDs.append(_currentTextureID);
|
||||
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, _currentTextureID);
|
||||
}
|
||||
// render the glyph into an image and copy it into the texture
|
||||
QImage image(bounds.width(), bounds.height(), QImage::Format_ARGB32);
|
||||
if (c == SOLID_BLOCK_CHAR) {
|
||||
image.fill(QColor(255, 255, 255));
|
||||
|
||||
} else {
|
||||
image.fill(0);
|
||||
QPainter painter(&image);
|
||||
painter.setFont(_font);
|
||||
if (_effectType == SHADOW_EFFECT) {
|
||||
for (int i = 0; i < _effectThickness; i++) {
|
||||
painter.drawText(-bounds.x() - 1 - i, -bounds.y() + 1 + i, ch);
|
||||
}
|
||||
} else if (_effectType == OUTLINE_EFFECT) {
|
||||
QPainterPath path;
|
||||
QFont font = _font;
|
||||
font.setStyleStrategy(QFont::ForceOutline);
|
||||
path.addText(-bounds.x() - 0.5, -bounds.y() + 0.5, font, ch);
|
||||
QPen pen;
|
||||
pen.setWidth(_effectThickness);
|
||||
pen.setJoinStyle(Qt::RoundJoin);
|
||||
pen.setCapStyle(Qt::RoundCap);
|
||||
painter.setPen(pen);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.drawPath(path);
|
||||
}
|
||||
painter.setPen(QColor(255, 255, 255));
|
||||
painter.drawText(-bounds.x(), -bounds.y(), ch);
|
||||
}
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, bounds.width(), bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());
|
||||
|
||||
glyph = Glyph(_currentTextureID, QPoint(_x, _y), bounds, _metrics.width(ch));
|
||||
_x += bounds.width();
|
||||
_rowHeight = qMax(_rowHeight, bounds.height());
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
return glyph;
|
||||
}
|
114
SvoViewer/src/TextRenderer.h
Normal file
114
SvoViewer/src/TextRenderer.h
Normal file
|
@ -0,0 +1,114 @@
|
|||
//
|
||||
// TextRenderer.h
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 4/26/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__TextRenderer__
|
||||
#define __interface__TextRenderer__
|
||||
|
||||
#include <QFont>
|
||||
#include <QFontMetrics>
|
||||
#include <QHash>
|
||||
#include <QImage>
|
||||
#include <QVector>
|
||||
|
||||
#include "SvoViewerConfig.h"
|
||||
|
||||
// a special "character" that renders as a solid block
|
||||
const char SOLID_BLOCK_CHAR = 127;
|
||||
|
||||
// the standard sans serif font family
|
||||
#define SANS_FONT_FAMILY "Helvetica"
|
||||
|
||||
// the standard mono font family
|
||||
#define MONO_FONT_FAMILY "Courier"
|
||||
|
||||
// the Inconsolata font family
|
||||
#define INCONSOLATA_FONT_FAMILY "Inconsolata"
|
||||
|
||||
|
||||
class Glyph;
|
||||
|
||||
class TextRenderer {
|
||||
public:
|
||||
|
||||
enum EffectType { NO_EFFECT, SHADOW_EFFECT, OUTLINE_EFFECT };
|
||||
|
||||
TextRenderer(const char* family, int pointSize = -1, int weight = -1, bool italic = false,
|
||||
EffectType effect = NO_EFFECT, int effectThickness = 1);
|
||||
~TextRenderer();
|
||||
|
||||
const QFontMetrics& metrics() const { return _metrics; }
|
||||
|
||||
// returns the height of the tallest character
|
||||
int calculateHeight(const char* str);
|
||||
|
||||
// also returns the height of the tallest character
|
||||
int draw(int x, int y, const char* str);
|
||||
|
||||
int computeWidth(char ch);
|
||||
int computeWidth(const char* str);
|
||||
|
||||
private:
|
||||
|
||||
const Glyph& getGlyph (char c);
|
||||
|
||||
// the font to render
|
||||
QFont _font;
|
||||
|
||||
// the font metrics
|
||||
QFontMetrics _metrics;
|
||||
|
||||
// the type of effect to apply
|
||||
EffectType _effectType;
|
||||
|
||||
// the thickness of the effect
|
||||
int _effectThickness;
|
||||
|
||||
// maps characters to cached glyph info
|
||||
QHash<char, Glyph> _glyphs;
|
||||
|
||||
// the id of the glyph texture to which we're currently writing
|
||||
GLuint _currentTextureID;
|
||||
|
||||
// the position within the current glyph texture
|
||||
int _x, _y;
|
||||
|
||||
// the height of the current row of characters
|
||||
int _rowHeight;
|
||||
|
||||
// the list of all texture ids for which we're responsible
|
||||
QVector<GLuint> _allTextureIDs;
|
||||
};
|
||||
|
||||
class Glyph {
|
||||
public:
|
||||
|
||||
Glyph(int textureID = 0, const QPoint& location = QPoint(), const QRect& bounds = QRect(), int width = 0);
|
||||
|
||||
GLuint textureID() const { return _textureID; }
|
||||
const QPoint& location () const { return _location; }
|
||||
const QRect& bounds() const { return _bounds; }
|
||||
int width () const { return _width; }
|
||||
|
||||
bool isValid() { return _width != 0; }
|
||||
|
||||
private:
|
||||
|
||||
// the id of the OpenGL texture containing the glyph
|
||||
GLuint _textureID;
|
||||
|
||||
// the location of the character within the texture
|
||||
QPoint _location;
|
||||
|
||||
// the bounds of the character
|
||||
QRect _bounds;
|
||||
|
||||
// the width of the character (distance to next, as opposed to bounds width)
|
||||
int _width;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__TextRenderer__) */
|
72
SvoViewer/src/globals.cpp
Executable file
72
SvoViewer/src/globals.cpp
Executable file
|
@ -0,0 +1,72 @@
|
|||
//
|
||||
// globals.cpp
|
||||
//
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#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
|
38
SvoViewer/src/globals.h
Executable file
38
SvoViewer/src/globals.h
Executable file
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// globals.h
|
||||
//
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SvoViewerConfig.h"
|
||||
|
||||
#include <QtWidgets/QMainWindow>
|
||||
#include <QDesktopWidget>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
//#include <GLCanvas>
|
||||
|
||||
//#include "ui_svoviewer.h"
|
||||
#include <ViewFrustum.h>
|
||||
#include <VoxelTree.h>
|
||||
#include <SharedUtil.h>
|
||||
//#include <VoxelShader.h>
|
||||
//#include <VoxelSystem.h>
|
||||
//#include <PointShader.h>
|
||||
#include <OctreeRenderer.h>
|
||||
|
||||
#include "Camera.h"
|
||||
|
||||
|
||||
extern const GLfloat WHITE_SPECULAR_COLOR[];
|
||||
extern const GLfloat NO_SPECULAR_COLOR[];
|
||||
|
||||
namespace SvoViewerNames // avoid conflicts with voxelSystem namespace objects
|
||||
{
|
||||
extern GLubyte cubeFaceIndices[][2*3];
|
||||
|
||||
extern GLubyte cubeFaceVtxs[6][4];
|
||||
|
||||
extern glm::vec3 faceNormals[6];
|
||||
}
|
16
SvoViewer/src/main.cpp
Executable file
16
SvoViewer/src/main.cpp
Executable file
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// main.cpp
|
||||
//
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "SvoViewerConfig.h"
|
||||
|
||||
#include "svoviewer.h"
|
||||
#include <QtWidgets/QApplication>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
SvoViewer svoV(argc, argv);
|
||||
return svoV.exec();
|
||||
}
|
689
SvoViewer/src/svoviewer.cpp
Executable file
689
SvoViewer/src/svoviewer.cpp
Executable file
|
@ -0,0 +1,689 @@
|
|||
//
|
||||
// svoviewer.cpp
|
||||
//
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#include "svoviewer.h"
|
||||
#include "GLCanvas.h"
|
||||
#include "TextRenderer.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <QDesktopWidget>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
#include <qtimer.h>
|
||||
#include <QKeyEvent>
|
||||
|
||||
#include <glm/gtx/component_wise.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
|
||||
|
||||
const int IDLE_SIMULATE_MSECS = 1; // How often should call simulate and other stuff
|
||||
// in the idle loop? (60 FPS is default)
|
||||
// Empirically, I found 5 to be about 60fps
|
||||
|
||||
static QTimer* idleTimer = NULL;
|
||||
const ViewFrustumOffset DEFAULT_FRUSTUM_OFFSET = {-135.0f, 0.0f, 0.0f, 25.0f, 0.0f};
|
||||
SvoViewer * _globalSvoViewerObj; // Hack :: var to store global pointer since this wasn't designed to work with the interface nodelist.
|
||||
|
||||
SvoViewer::SvoViewer(int& argc, char** argv, QWidget *parent)
|
||||
: QApplication(argc, argv),
|
||||
_window(new QMainWindow(desktop())),
|
||||
_glWidget(new GLCanvas()),
|
||||
_width(1280),
|
||||
_height(720),
|
||||
_pixelCount(1280*720),
|
||||
_frameCount(0),
|
||||
_leafCount(0),
|
||||
_nodeCount(0),
|
||||
_fps(0.0),
|
||||
_lastTimeFpsUpdated(0),
|
||||
_lastTimeFrameUpdated(0),
|
||||
_ptRenderInitialized(false),
|
||||
_voxelRenderInitialized(false),
|
||||
_voxelOptRenderInitialized(false),
|
||||
_voxelOpt2RenderInitialized(false),
|
||||
_vertexShader(0),
|
||||
_pixelShader(0),
|
||||
_geometryShader(0),
|
||||
_maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM),
|
||||
_voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
|
||||
_boundaryLevelAdjust(0),
|
||||
_viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET),
|
||||
_fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES),
|
||||
_useVoxelTextures(false),
|
||||
_pointVertices(NULL),
|
||||
_pointVerticesCount(0),
|
||||
//_vboShaderData(NULL),
|
||||
_mousePressed(false),
|
||||
_pitch(0),
|
||||
_yaw(0),
|
||||
_roll(0),
|
||||
_numSegments(0),
|
||||
_displayOnlyPartition(NO_PARTITION),
|
||||
_totalPossibleElems(0),
|
||||
_numElemsDrawn(0),
|
||||
_useBoundingVolumes(true)
|
||||
{
|
||||
gettimeofday(&_applicationStartupTime, NULL);
|
||||
_appStartTickCount = usecTimestampNow();
|
||||
|
||||
_globalSvoViewerObj = this;
|
||||
|
||||
//ui.setupUi(this);
|
||||
_window->setWindowTitle("SvoViewer");
|
||||
_window->resize(1280,720);
|
||||
|
||||
_window->setCentralWidget(_glWidget);
|
||||
|
||||
qDebug("Window initialized\n");
|
||||
|
||||
_window->setVisible(true);
|
||||
_glWidget->setFocusPolicy(Qt::StrongFocus);
|
||||
_glWidget->setFocus();
|
||||
_glWidget->setMouseTracking(true);
|
||||
|
||||
QString svoFileToRead;
|
||||
QString shaderMode;
|
||||
|
||||
QStringList argumentList = arguments();
|
||||
int argumentIndex = 0;
|
||||
|
||||
// check if this domain server should use no authentication or a custom hostname for authentication
|
||||
const QString FILE_NAME = "--file";
|
||||
const QString SHADER_MODE = "--mode";
|
||||
if (argumentList.indexOf(FILE_NAME) != -1) {
|
||||
svoFileToRead = argumentList.value(argumentList.indexOf(FILE_NAME) + 1);
|
||||
qDebug() << "file:" << svoFileToRead;
|
||||
}
|
||||
if (argumentList.indexOf(SHADER_MODE) != -1) {
|
||||
shaderMode = argumentList.value(argumentList.indexOf(SHADER_MODE) + 1);
|
||||
qDebug() << "shaderMode:" << shaderMode;
|
||||
}
|
||||
|
||||
if (shaderMode == "RENDER_OPT_CULLED_POLYS") {
|
||||
_currentShaderModel = RENDER_OPT_CULLED_POLYS;
|
||||
} else if (shaderMode == "RENDER_OPT_POLYS") {
|
||||
_currentShaderModel = RENDER_OPT_POLYS;
|
||||
} else if (shaderMode == "RENDER_CLASSIC_POLYS") {
|
||||
_currentShaderModel = RENDER_CLASSIC_POLYS;
|
||||
} else if (shaderMode == "RENDER_POINTS") {
|
||||
_currentShaderModel = RENDER_POINTS;
|
||||
} else {
|
||||
_currentShaderModel = RENDER_OPT_CULLED_POLYS;
|
||||
}
|
||||
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;
|
||||
|
||||
qDebug("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.
|
||||
qDebug() << svoFileToRead;
|
||||
}
|
||||
**/
|
||||
|
||||
//qDebug("Sizeof Octree element is %d\n", sizeof(OctreeElement));
|
||||
|
||||
quint64 readStart = usecTimestampNow();
|
||||
bool readSucceeded = _systemTree.readFromSVOFile(qPrintable(svoFileToRead));
|
||||
qDebug("Done reading SVO file : %f seconds : ", (float)(usecTimestampNow() - readStart) / 1000.0f);
|
||||
readSucceeded ? qDebug("Succeeded\n") : qDebug("Failed\n");
|
||||
|
||||
// this should exist... we just loaded it...
|
||||
if (_systemTree.getVoxelAt(voxelSize, 0, voxelSize, voxelSize)) {
|
||||
qDebug("corner point voxelSize, 0, voxelSize exists...\n");
|
||||
} else {
|
||||
qDebug("corner point voxelSize, 0, voxelSize does not exists...\n");
|
||||
}
|
||||
|
||||
_nodeCount = _systemTree.getOctreeElementsCount();
|
||||
qDebug("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;
|
||||
#ifdef WIN32
|
||||
glutInit(&argc, 0);
|
||||
#endif
|
||||
init();
|
||||
#ifdef WIN32
|
||||
GLenum err = glewInit();
|
||||
if (GLEW_OK != err) {
|
||||
/* Problem: glewInit failed, something is seriously wrong. */
|
||||
qDebug("Error: %s\n", glewGetErrorString(err));
|
||||
}
|
||||
qDebug("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;
|
||||
qDebug("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;
|
||||
const quint64 USECS_PER_SECOND = 1000 * 1000;
|
||||
const int FPS_UPDATE_TIME_INTERVAL = 2;
|
||||
if (interval > (USECS_PER_SECOND * FPS_UPDATE_TIME_INTERVAL)) {
|
||||
int numFrames = _frameCount - _lastTrackedFrameCount;
|
||||
float intervalSeconds = (float)((float)interval/(float)USECS_PER_SECOND);
|
||||
_fps = (float)numFrames / intervalSeconds;
|
||||
_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);
|
||||
}
|
||||
|
||||
void drawtext(int x, int y, float scale, float rotate, float thick, int mono,
|
||||
char const* string, float r, float g, float b) {
|
||||
//
|
||||
// Draws text on screen as stroked so it can be resized
|
||||
//
|
||||
glPushMatrix();
|
||||
glTranslatef(static_cast<float>(x), static_cast<float>(y), 0.0f);
|
||||
glColor3f(r,g,b);
|
||||
glRotated(rotate,0,0,1);
|
||||
// glLineWidth(thick);
|
||||
glScalef(scale / 0.10, scale / 0.10, 1.0);
|
||||
|
||||
TextRenderer textRenderer(SANS_FONT_FAMILY, 11, 50);
|
||||
textRenderer.draw(0, 0, string);
|
||||
|
||||
//textRenderer(mono)->draw(0, 0, string);
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
|
||||
#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];
|
||||
qDebug() << szBuff;
|
||||
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
glMatrixMode( GL_PROJECTION );
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
gluOrtho2D(0, _width, _height, 0);
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
glMatrixMode( GL_MODELVIEW );
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
drawtext(width, height, 0.10f, 0, 1, 2, szBuff, 1,1,1);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
GLubyte SvoViewer::PrintGLErrorCode()
|
||||
{
|
||||
GLubyte err = glGetError();
|
||||
if( err != GL_NO_ERROR ) //qDebug("GL Error! : %x\n", err);
|
||||
qDebug("Error! : %u, %s\n", (unsigned int)err, gluErrorString(err));
|
||||
return err;
|
||||
}
|
327
SvoViewer/src/svoviewer.h
Executable file
327
SvoViewer/src/svoviewer.h
Executable file
|
@ -0,0 +1,327 @@
|
|||
//
|
||||
// svoviewer.h
|
||||
//
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
#ifndef SVOVIEWER_H
|
||||
#define SVOVIEWER_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SvoViewerConfig.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QGLWidget>
|
||||
|
||||
#include "globals.h"
|
||||
#include "AABoundingVolume.h"
|
||||
|
||||
|
||||
|
||||
enum SVOViewerShaderModel
|
||||
{
|
||||
RENDER_NONE,
|
||||
RENDER_POINTS,
|
||||
RENDER_CLASSIC_POLYS,
|
||||
RENDER_OPT_POLYS,
|
||||
RENDER_OPT_CULLED_POLYS,
|
||||
};
|
||||
|
||||
enum CubeOrdering
|
||||
{
|
||||
CUBE_TOP,
|
||||
CUBE_BOTTOM,
|
||||
CUBE_LEFT,
|
||||
CUBE_RIGHT,
|
||||
CUBE_FRONT,
|
||||
CUBE_BACK,
|
||||
NUM_CUBE_FACES
|
||||
};
|
||||
|
||||
struct ViewFrustumOffset {
|
||||
float yaw;
|
||||
float pitch;
|
||||
float roll;
|
||||
float distance;
|
||||
float up;
|
||||
};
|
||||
|
||||
struct RenderFlags
|
||||
{
|
||||
bool ptRenderDirty : 1;
|
||||
bool voxelRenderDirty : 1;
|
||||
bool voxelOptRenderDirty : 1;
|
||||
bool usePtShader : 1;
|
||||
bool useVoxelShader : 1;
|
||||
bool useVoxelOptShader : 1;
|
||||
bool useShadows : 1;
|
||||
};
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
glm::vec3 position;
|
||||
unsigned char color[4];
|
||||
};
|
||||
|
||||
enum VtxAttributes
|
||||
{
|
||||
ATTRIB_POSITION,
|
||||
ATTRIB_COLOR,
|
||||
ATTRIB_TEXTURE
|
||||
};
|
||||
|
||||
struct VoxelDimIdxSet
|
||||
{
|
||||
GLuint idxIds[NUM_CUBE_FACES]; // Id for each voxel face
|
||||
GLuint idxCount[NUM_CUBE_FACES]; // count for each voxel face.
|
||||
GLuint* idxBuff[NUM_CUBE_FACES]; // actual index buffers for each voxel face
|
||||
GLuint elemCount[NUM_CUBE_FACES];
|
||||
AABoundingVolume bounds[NUM_CUBE_FACES]; // Super efficient bounding set here.
|
||||
bool visibleFace[NUM_CUBE_FACES];
|
||||
};
|
||||
|
||||
struct VisibleFacesData
|
||||
{
|
||||
int count;
|
||||
glm::vec3 * ptList;
|
||||
};
|
||||
|
||||
struct FindNumLeavesData
|
||||
{
|
||||
int numLeaves;
|
||||
};
|
||||
|
||||
//#define MAX_VOXELS 4000000
|
||||
#define MAX_VOXELS 8000000
|
||||
#define REASONABLY_LARGE_BUFFER 10000
|
||||
|
||||
#define MAX_NUM_OCTREE_PARTITIONS 20
|
||||
#define MAX_NUM_VBO_ALLOWED MAX_NUM_OCTREE_PARTITIONS * NUM_CUBE_FACES
|
||||
#define NO_PARTITION -1
|
||||
|
||||
class VoxelOptRenderer : public OctreeRenderer {
|
||||
};
|
||||
|
||||
class SvoViewer : public QApplication
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static SvoViewer* getInstance() { return static_cast<SvoViewer*>(QCoreApplication::instance()); }
|
||||
|
||||
SvoViewer(int& argc, char** argv, QWidget *parent = 0);
|
||||
~SvoViewer();
|
||||
|
||||
|
||||
void initializeGL();
|
||||
void paintGL();
|
||||
void resizeGL(int width, int height);
|
||||
void init();
|
||||
void update(float deltaTime);
|
||||
void updateMouseRay();
|
||||
void updateProjectionMatrix(Camera& camera, bool updateViewFrustum);
|
||||
void updateCamera(float deltaTime);
|
||||
void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
|
||||
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
|
||||
void computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal, float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const ;
|
||||
void setupWorldLight();
|
||||
glm::vec2 getViewportDimensions() const{ return glm::vec2(_width,_height); }
|
||||
|
||||
// User Tweakable LOD Items
|
||||
void autoAdjustLOD(float currentFPS);
|
||||
void setVoxelSizeScale(float sizeScale);
|
||||
float getVoxelSizeScale() const { return _voxelSizeScale; }
|
||||
void setBoundaryLevelAdjust(int boundaryLevelAdjust);
|
||||
int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
|
||||
|
||||
bool getIsPointShader(){ return _renderFlags.usePtShader; }
|
||||
void setIsPointShader(const bool b){ _renderFlags.usePtShader = b; }
|
||||
bool getIsVoxelShader(){ return _renderFlags.useVoxelShader; }
|
||||
void setIsVoxelShader(const bool b){ _renderFlags.useVoxelShader = b; }
|
||||
bool getIsVoxelOptShader(){ return _renderFlags.useVoxelOptShader; }
|
||||
void setIsVoxelOptShader(const bool b){ _renderFlags.useVoxelOptShader = b; }
|
||||
void setUseVoxelTextures(const bool b){_useVoxelTextures = b; }
|
||||
bool getUseVoxelTextures(){ return _useVoxelTextures; }
|
||||
void setUseShadows(const bool b){ _renderFlags.useShadows = b; }
|
||||
bool getUseShadows(){ return _renderFlags.useShadows; }
|
||||
|
||||
//VoxelShader* getVoxelShader(){ return &_voxelShader; }
|
||||
//PointShader* getPointShader(){ return &_pointShader; }
|
||||
|
||||
void keyPressEvent(QKeyEvent* event);
|
||||
void keyReleaseEvent(QKeyEvent* event);
|
||||
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseReleaseEvent(QMouseEvent* event);
|
||||
|
||||
void PrintToScreen(const int width, const int height, const char* szFormat, ...);
|
||||
static 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) {
|
||||
glm::vec3 avg = p0 + p1 + p2 + p3;
|
||||
avg /= 4.0f;
|
||||
return avg;
|
||||
}
|
||||
|
||||
bool isVisibleBV(AABoundingVolume * volume, Camera * camera, ViewFrustum * frustum);
|
||||
float visibleAngleSubtended(AABoundingVolume * volume, Camera * camera, ViewFrustum * frustum);
|
||||
static int ptCompFunc(const void * a, const void * b);
|
||||
static int ptCloseEnough(const void * a, const void * b);
|
||||
static int binVecSearch(glm::vec3 searchVal, glm::vec3* list, int count, int * found);
|
||||
bool parameterizedRayPlaneIntersection(const glm::vec3 origin, const glm::vec3 direction, const glm::vec3 planePt, const glm::vec3 planeNormal, float *t);
|
||||
|
||||
protected:
|
||||
void InitializePointRenderSystem();
|
||||
void setupFaceIndices(GLuint& faceVBOID, GLubyte faceIdentityIndices[]);
|
||||
void InitializeVoxelRenderSystem();
|
||||
void InitializeVoxelOptRenderSystem();
|
||||
void InitializeVoxelOpt2RenderSystem();
|
||||
void StopUsingPointRenderSystem();
|
||||
void StopUsingVoxelRenderSystem();
|
||||
void StopUsingVoxelOptRenderSystem();
|
||||
void StopUsingVoxelOpt2RenderSystem();
|
||||
void UpdateOpt2BVFaceVisibility();
|
||||
|
||||
|
||||
void RenderTreeSystemAsPoints();
|
||||
void RenderTreeSystemAsVoxels();
|
||||
void RenderTreeSystemAsOptVoxels();
|
||||
void RenderTreeSystemAsOpt2Voxels();
|
||||
|
||||
// Tree traversal functions.
|
||||
static bool PointRenderAssemblePerVoxel(OctreeElement* node, void* extraData);
|
||||
static bool VoxelRenderAssemblePerVoxel(OctreeElement* node, void* extraData);
|
||||
static bool VoxelOptRenderAssemblePerVoxel(OctreeElement* node, void* extraData);
|
||||
static bool VoxelOpt2RenderAssemblePerVoxel(OctreeElement* node, void* extraData);
|
||||
static bool FindNumLeaves(OctreeElement* node, void* extraData);
|
||||
static bool TrackVisibleFaces(OctreeElement* node, void* extraData);
|
||||
|
||||
|
||||
private slots:
|
||||
void idle();
|
||||
|
||||
private:
|
||||
//Ui::SvoViewerClass ui;
|
||||
|
||||
QMainWindow* _window;
|
||||
int _width;
|
||||
int _height;
|
||||
int _pixelCount;
|
||||
QGLWidget* _glWidget;
|
||||
|
||||
//VoxelSystem _voxels;
|
||||
VoxelTree _systemTree;
|
||||
unsigned long _nodeCount;
|
||||
unsigned int _leafCount;
|
||||
|
||||
ViewFrustum _viewFrustum;
|
||||
Camera _myCamera; // My view onto the world
|
||||
float _pitch, _yaw, _roll;
|
||||
|
||||
int _mouseX;
|
||||
int _mouseY;
|
||||
int _mouseDragStartedX;
|
||||
int _mouseDragStartedY;
|
||||
bool _mousePressed;
|
||||
quint64 _lastMouseMove;
|
||||
bool _mouseHidden;
|
||||
bool _seenMouseMove;
|
||||
|
||||
glm::vec3 _mouseRayOrigin;
|
||||
glm::vec3 _mouseRayDirection;
|
||||
|
||||
glm::mat4 _untranslatedViewMatrix;
|
||||
glm::vec3 _viewMatrixTranslation;
|
||||
GLdouble _projectionMatrix[16];
|
||||
GLdouble _modelviewMatrix[16];
|
||||
GLint _viewport[4];
|
||||
|
||||
SVOViewerShaderModel _currentShaderModel;
|
||||
//VoxelShader _voxelShader;
|
||||
//PointShader _pointShader;
|
||||
|
||||
// Display options
|
||||
int _displayOnlyPartition;
|
||||
|
||||
// Frame Rate Measurement
|
||||
int _frameCount;
|
||||
int _lastTrackedFrameCount;
|
||||
float _fps;
|
||||
timeval _applicationStartupTime;
|
||||
quint64 _appStartTickCount;
|
||||
quint64 _lastTimeFpsUpdated;
|
||||
quint64 _lastTimeFrameUpdated;
|
||||
|
||||
// Render variables.
|
||||
bool _ptRenderInitialized;
|
||||
bool _voxelRenderInitialized;
|
||||
bool _voxelOptRenderInitialized;
|
||||
bool _voxelOpt2RenderInitialized;
|
||||
|
||||
GLuint _vertexShader;
|
||||
GLuint _pixelShader;
|
||||
GLuint _geometryShader;
|
||||
GLuint _linkProgram;
|
||||
|
||||
// Vars for RENDER_POINTS
|
||||
GLuint _pointVtxBuffer;
|
||||
GLuint _pointColorBuffer;
|
||||
glm::vec3* _pointVertices;
|
||||
unsigned char* _pointColors;
|
||||
int _pointVerticesCount;
|
||||
|
||||
// Vars for RENDER_CLASSIC_POLYS
|
||||
GLuint _vboVerticesID;
|
||||
GLuint _vboColorsID;
|
||||
GLuint _vboIndicesIds[NUM_CUBE_FACES];
|
||||
GLuint* _vboIndices[NUM_CUBE_FACES];
|
||||
//VoxelShaderVBOData* _vboShaderData; // may not need.
|
||||
glm::vec3* _readVerticesArray;
|
||||
unsigned char* _readColorsArray;
|
||||
GLuint* _readIndicesArray;
|
||||
|
||||
|
||||
// Vars for RENDER_OPT_POLYS
|
||||
// Allow for primitive first level sorting at the moment.
|
||||
GLuint _vboOVerticesIds[MAX_NUM_VBO_ALLOWED];
|
||||
glm::vec3* _vboOVertices[MAX_NUM_VBO_ALLOWED];
|
||||
GLuint _vboOIndicesIds[MAX_NUM_VBO_ALLOWED];
|
||||
GLuint* _vboOIndices[MAX_NUM_VBO_ALLOWED];
|
||||
GLuint _numChildNodeLeaves[MAX_NUM_VBO_ALLOWED];
|
||||
OctreeElement * _segmentNodeReferences[MAX_NUM_OCTREE_PARTITIONS];
|
||||
AABoundingVolume _segmentBoundingVolumes[MAX_NUM_OCTREE_PARTITIONS];
|
||||
int _segmentElemCount[MAX_NUM_OCTREE_PARTITIONS];
|
||||
unsigned int _numSegments;
|
||||
Vertex * _readVertexStructs;
|
||||
|
||||
// Vars for RENDER_OPT_CULLED_POLYS
|
||||
// Use vtx vars from opt_polys version.
|
||||
VoxelDimIdxSet _segmentIdxBuffers[MAX_NUM_OCTREE_PARTITIONS];
|
||||
bool _useBoundingVolumes;
|
||||
|
||||
|
||||
int _numElemsDrawn;
|
||||
int _totalPossibleElems;
|
||||
|
||||
RenderFlags _renderFlags;
|
||||
|
||||
ViewFrustumOffset _viewFrustumOffset;
|
||||
int _maxVoxels;
|
||||
float _voxelSizeScale;
|
||||
int _boundaryLevelAdjust;
|
||||
float _fieldOfView; /// in Degrees
|
||||
bool _useVoxelTextures;
|
||||
|
||||
};
|
||||
|
||||
//Extern hack since this wasn't built with global linking to old project in mind.
|
||||
extern SvoViewer * _globalSvoViewerObj;
|
||||
|
||||
|
||||
#endif // SVOVIEWER_H
|
4
SvoViewer/src/svoviewer.qrc
Executable file
4
SvoViewer/src/svoviewer.qrc
Executable file
|
@ -0,0 +1,4 @@
|
|||
<RCC>
|
||||
<qresource prefix="SvoViewer">
|
||||
</qresource>
|
||||
</RCC>
|
29
SvoViewer/src/svoviewer.ui
Executable file
29
SvoViewer/src/svoviewer.ui
Executable file
|
@ -0,0 +1,29 @@
|
|||
<UI version="4.0" >
|
||||
<class>SvoViewerClass</class>
|
||||
<widget class="QMainWindow" name="SvoViewerClass" >
|
||||
<property name="objectName" >
|
||||
<string notr="true">SvoViewerClass</string>
|
||||
</property>
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>400</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>SvoViewer</string>
|
||||
</property>
|
||||
<widget class="QMenuBar" name="menuBar" />
|
||||
<widget class="QToolBar" name="mainToolBar" />
|
||||
<widget class="QWidget" name="centralWidget" />
|
||||
<widget class="QStatusBar" name="statusBar" />
|
||||
</widget>
|
||||
<layoutDefault spacing="6" margin="11" />
|
||||
<pixmapfunction></pixmapfunction>
|
||||
<resources>
|
||||
<include location="svoviewer.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</UI>
|
57
SvoViewer/src/windowshacks.h
Normal file
57
SvoViewer/src/windowshacks.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// windowshacks.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 1/12/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// hacks to get windows to compile
|
||||
//
|
||||
|
||||
#ifndef __hifi__windowshacks__
|
||||
#define __hifi__windowshacks__
|
||||
|
||||
#ifdef WIN32
|
||||
#undef NOMINMAX
|
||||
|
||||
#include <cmath>
|
||||
inline double roundf(double value) {
|
||||
return (value > 0.0) ? floor(value + 0.5) : ceil(value - 0.5);
|
||||
}
|
||||
#define round roundf
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#ifndef SNPRINTF_FIX
|
||||
#define SNPRINTF_FIX
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define snprintf c99_snprintf
|
||||
|
||||
inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) {
|
||||
int count = -1;
|
||||
if (size != 0) {
|
||||
count = _vsnprintf_s(str, size, _TRUNCATE, format, ap);
|
||||
}
|
||||
if (count == -1) {
|
||||
count = _vscprintf(format, ap);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
inline int c99_snprintf(char* str, size_t size, const char* format, ...) {
|
||||
int count;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
count = c99_vsnprintf(str, size, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
return count;
|
||||
}
|
||||
#endif // SNPRINTF_FIX
|
||||
#endif // _MSC_VER
|
||||
|
||||
|
||||
#endif // WIN32
|
||||
#endif // __hifi__windowshacks__
|
|
@ -736,12 +736,12 @@ AnimationServer::AnimationServer(int &argc, char **argv) :
|
|||
::wantLocalDomain = cmdOptionExists(argc, (const char**) argv,local);
|
||||
if (::wantLocalDomain) {
|
||||
printf("Local Domain MODE!\n");
|
||||
nodeList->setDomainIPToLocalhost();
|
||||
nodeList->getDomainInfo().setIPToLocalhost();
|
||||
}
|
||||
|
||||
const char* domainHostname = getCmdOption(argc, (const char**) argv, "--domain");
|
||||
if (domainHostname) {
|
||||
NodeList::getInstance()->setDomainHostname(domainHostname);
|
||||
NodeList::getInstance()->getDomainInfo().setHostname(domainHostname);
|
||||
}
|
||||
|
||||
const char* packetsPerSecondCommand = getCmdOption(argc, (const char**) argv, "--pps");
|
||||
|
|
|
@ -27,6 +27,9 @@ Agent::Agent(const QByteArray& packet) :
|
|||
_voxelEditSender(),
|
||||
_particleEditSender()
|
||||
{
|
||||
// be the parent of the script engine so it gets moved when we do
|
||||
_scriptEngine.setParent(this);
|
||||
|
||||
_scriptEngine.getVoxelsScriptingInterface()->setPacketSender(&_voxelEditSender);
|
||||
_scriptEngine.getParticlesScriptingInterface()->setPacketSender(&_particleEditSender);
|
||||
}
|
||||
|
@ -39,6 +42,7 @@ void Agent::readPendingDatagrams() {
|
|||
while (readAvailableDatagram(receivedPacket, senderSockAddr)) {
|
||||
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
|
||||
PacketType datagramPacketType = packetTypeForPacket(receivedPacket);
|
||||
|
||||
if (datagramPacketType == PacketTypeJurisdiction) {
|
||||
int headerBytes = numBytesForPacketHeader(receivedPacket);
|
||||
|
||||
|
@ -48,12 +52,12 @@ void Agent::readPendingDatagrams() {
|
|||
// PacketType_JURISDICTION, first byte is the node type...
|
||||
switch (receivedPacket[headerBytes]) {
|
||||
case NodeType::VoxelServer:
|
||||
_scriptEngine.getVoxelsScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(matchedNode,
|
||||
receivedPacket);
|
||||
_scriptEngine.getVoxelsScriptingInterface()->getJurisdictionListener()->
|
||||
queueReceivedPacket(matchedNode,receivedPacket);
|
||||
break;
|
||||
case NodeType::ParticleServer:
|
||||
_scriptEngine.getParticlesScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(matchedNode,
|
||||
receivedPacket);
|
||||
_scriptEngine.getParticlesScriptingInterface()->getJurisdictionListener()->
|
||||
queueReceivedPacket(matchedNode, receivedPacket);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +67,44 @@ void Agent::readPendingDatagrams() {
|
|||
Particle::handleAddParticleResponse(receivedPacket);
|
||||
|
||||
// also give our local particle tree a chance to remap any internal locally created particles
|
||||
_particleTree.handleAddParticleResponse(receivedPacket);
|
||||
_particleViewer.getTree()->handleAddParticleResponse(receivedPacket);
|
||||
|
||||
} else if (datagramPacketType == PacketTypeParticleData
|
||||
|| datagramPacketType == PacketTypeParticleErase
|
||||
|| datagramPacketType == PacketTypeOctreeStats
|
||||
|| datagramPacketType == PacketTypeVoxelData
|
||||
) {
|
||||
SharedNodePointer sourceNode = nodeList->sendingNodeForPacket(receivedPacket);
|
||||
QByteArray mutablePacket = receivedPacket;
|
||||
ssize_t messageLength = mutablePacket.size();
|
||||
|
||||
if (datagramPacketType == PacketTypeOctreeStats) {
|
||||
|
||||
int statsMessageLength = OctreeHeadlessViewer::parseOctreeStats(mutablePacket, sourceNode);
|
||||
if (messageLength > statsMessageLength) {
|
||||
mutablePacket = mutablePacket.mid(statsMessageLength);
|
||||
|
||||
// TODO: this needs to be fixed, the goal is to test the packet version for the piggyback, but
|
||||
// this is testing the version and hash of the original packet
|
||||
// need to use numBytesArithmeticCodingFromBuffer()...
|
||||
if (!NodeList::getInstance()->packetVersionAndHashMatch(receivedPacket)) {
|
||||
return; // bail since piggyback data doesn't match our versioning
|
||||
}
|
||||
} else {
|
||||
return; // bail since no piggyback data
|
||||
}
|
||||
|
||||
datagramPacketType = packetTypeForPacket(mutablePacket);
|
||||
} // fall through to piggyback message
|
||||
|
||||
if (datagramPacketType == PacketTypeParticleData || datagramPacketType == PacketTypeParticleErase) {
|
||||
_particleViewer.processDatagram(mutablePacket, sourceNode);
|
||||
}
|
||||
|
||||
if (datagramPacketType == PacketTypeVoxelData) {
|
||||
_voxelViewer.processDatagram(mutablePacket, sourceNode);
|
||||
}
|
||||
|
||||
} else {
|
||||
NodeList::getInstance()->processNodeData(senderSockAddr, receivedPacket);
|
||||
}
|
||||
|
@ -79,7 +120,7 @@ void Agent::run() {
|
|||
|
||||
// figure out the URL for the script for this agent assignment
|
||||
QString scriptURLString("http://%1:8080/assignment/%2");
|
||||
scriptURLString = scriptURLString.arg(NodeList::getInstance()->getDomainIP().toString(),
|
||||
scriptURLString = scriptURLString.arg(NodeList::getInstance()->getDomainInfo().getIP().toString(),
|
||||
uuidStringWithoutCurlyBraces(_uuid));
|
||||
|
||||
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);
|
||||
|
@ -92,6 +133,9 @@ void Agent::run() {
|
|||
|
||||
loop.exec();
|
||||
|
||||
// let the AvatarData class use our QNetworkAcessManager
|
||||
AvatarData::setNetworkAccessManager(networkManager);
|
||||
|
||||
QString scriptContents(reply->readAll());
|
||||
|
||||
qDebug() << "Downloaded script:" << scriptContents;
|
||||
|
@ -107,18 +151,34 @@ void Agent::run() {
|
|||
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||
|
||||
// tell our script engine about our local particle tree
|
||||
_scriptEngine.getParticlesScriptingInterface()->setParticleTree(&_particleTree);
|
||||
|
||||
// setup an Avatar for the script to use
|
||||
AvatarData scriptedAvatar;
|
||||
|
||||
// call model URL setters with empty URLs so our avatar, if user, will have the default models
|
||||
scriptedAvatar.setFaceModelURL(QUrl());
|
||||
scriptedAvatar.setSkeletonModelURL(QUrl());
|
||||
|
||||
// give this AvatarData object to the script engine
|
||||
_scriptEngine.setAvatarData(&scriptedAvatar, "Avatar");
|
||||
|
||||
// register ourselves to the script engine
|
||||
_scriptEngine.registerGlobalObject("Agent", this);
|
||||
|
||||
_scriptEngine.init(); // must be done before we set up the viewers
|
||||
|
||||
_scriptEngine.registerGlobalObject("VoxelViewer", &_voxelViewer);
|
||||
// connect the VoxelViewer and the VoxelScriptingInterface to each other
|
||||
JurisdictionListener* voxelJL = _scriptEngine.getVoxelsScriptingInterface()->getJurisdictionListener();
|
||||
_voxelViewer.setJurisdictionListener(voxelJL);
|
||||
_voxelViewer.init();
|
||||
_scriptEngine.getVoxelsScriptingInterface()->setVoxelTree(_voxelViewer.getTree());
|
||||
|
||||
_scriptEngine.registerGlobalObject("ParticleViewer", &_particleViewer);
|
||||
JurisdictionListener* particleJL = _scriptEngine.getParticlesScriptingInterface()->getJurisdictionListener();
|
||||
_particleViewer.setJurisdictionListener(particleJL);
|
||||
_particleViewer.init();
|
||||
_scriptEngine.getParticlesScriptingInterface()->setParticleTree(_particleViewer.getTree());
|
||||
|
||||
_scriptEngine.setScriptContents(scriptContents);
|
||||
_scriptEngine.run();
|
||||
_scriptEngine.run();
|
||||
}
|
||||
|
|
|
@ -17,9 +17,11 @@
|
|||
|
||||
#include <ParticleEditPacketSender.h>
|
||||
#include <ParticleTree.h>
|
||||
#include <ParticleTreeHeadlessViewer.h>
|
||||
#include <ScriptEngine.h>
|
||||
#include <ThreadedAssignment.h>
|
||||
#include <VoxelEditPacketSender.h>
|
||||
#include <VoxelTreeHeadlessViewer.h>
|
||||
|
||||
|
||||
class Agent : public ThreadedAssignment {
|
||||
|
@ -29,7 +31,7 @@ class Agent : public ThreadedAssignment {
|
|||
public:
|
||||
Agent(const QByteArray& packet);
|
||||
|
||||
void setIsAvatar(bool isAvatar) { _scriptEngine.setIsAvatar(isAvatar); }
|
||||
void setIsAvatar(bool isAvatar) { QMetaObject::invokeMethod(&_scriptEngine, "setIsAvatar", Q_ARG(bool, isAvatar)); }
|
||||
bool isAvatar() const { return _scriptEngine.isAvatar(); }
|
||||
|
||||
public slots:
|
||||
|
@ -41,9 +43,11 @@ signals:
|
|||
void willSendVisualDataCallback();
|
||||
private:
|
||||
ScriptEngine _scriptEngine;
|
||||
ParticleTree _particleTree;
|
||||
VoxelEditPacketSender _voxelEditSender;
|
||||
ParticleEditPacketSender _particleEditSender;
|
||||
|
||||
ParticleTreeHeadlessViewer _particleViewer;
|
||||
VoxelTreeHeadlessViewer _voxelViewer;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Agent__) */
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QtCore/QProcess>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <Assignment.h>
|
||||
#include <Logging.h>
|
||||
#include <NodeList.h>
|
||||
|
@ -19,7 +21,7 @@
|
|||
|
||||
#include "AssignmentClient.h"
|
||||
|
||||
const char ASSIGNMENT_CLIENT_TARGET_NAME[] = "assignment-client";
|
||||
const QString ASSIGNMENT_CLIENT_TARGET_NAME = "assignment-client";
|
||||
const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000;
|
||||
|
||||
int hifiSockAddrMeta = qRegisterMetaType<HifiSockAddr>("HifiSockAddr");
|
||||
|
@ -28,6 +30,11 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
|
|||
QCoreApplication(argc, argv),
|
||||
_currentAssignment(NULL)
|
||||
{
|
||||
setOrganizationName("High Fidelity");
|
||||
setOrganizationDomain("highfidelity.io");
|
||||
setApplicationName("assignment-client");
|
||||
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||
|
||||
// register meta type is required for queued invoke method on Assignment subclasses
|
||||
|
||||
// set the logging target to the the CHILD_TARGET_NAME
|
||||
|
@ -91,6 +98,10 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
|
|||
|
||||
// connect our readPendingDatagrams method to the readyRead() signal of the socket
|
||||
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams);
|
||||
|
||||
// connections to AccountManager for authentication
|
||||
connect(&AccountManager::getInstance(), &AccountManager::authRequired,
|
||||
this, &AssignmentClient::handleAuthenticationRequest);
|
||||
}
|
||||
|
||||
void AssignmentClient::sendAssignmentRequest() {
|
||||
|
@ -120,10 +131,10 @@ void AssignmentClient::readPendingDatagrams() {
|
|||
|
||||
// switch our nodelist domain IP and port to whoever sent us the assignment
|
||||
|
||||
nodeList->setDomainSockAddr(senderSockAddr);
|
||||
nodeList->setSessionUUID(_currentAssignment->getUUID());
|
||||
nodeList->getDomainInfo().setSockAddr(senderSockAddr);
|
||||
nodeList->getDomainInfo().setAssignmentUUID(_currentAssignment->getUUID());
|
||||
|
||||
qDebug() << "Destination IP for assignment is" << nodeList->getDomainIP().toString();
|
||||
qDebug() << "Destination IP for assignment is" << nodeList->getDomainInfo().getIP().toString();
|
||||
|
||||
// start the deployed assignment
|
||||
QThread* workerThread = new QThread(this);
|
||||
|
@ -158,6 +169,30 @@ void AssignmentClient::readPendingDatagrams() {
|
|||
}
|
||||
}
|
||||
|
||||
void AssignmentClient::handleAuthenticationRequest() {
|
||||
const QString DATA_SERVER_USERNAME_ENV = "HIFI_AC_USERNAME";
|
||||
const QString DATA_SERVER_PASSWORD_ENV = "HIFI_AC_PASSWORD";
|
||||
|
||||
// this node will be using an authentication server, let's make sure we have a username/password
|
||||
QProcessEnvironment sysEnvironment = QProcessEnvironment::systemEnvironment();
|
||||
|
||||
QString username = sysEnvironment.value(DATA_SERVER_USERNAME_ENV);
|
||||
QString password = sysEnvironment.value(DATA_SERVER_PASSWORD_ENV);
|
||||
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
if (!username.isEmpty() && !password.isEmpty()) {
|
||||
// ask the account manager to log us in from the env variables
|
||||
accountManager.requestAccessToken(username, password);
|
||||
} else {
|
||||
qDebug() << "Authentication was requested against" << qPrintable(accountManager.getAuthURL().toString())
|
||||
<< "but both or one of" << qPrintable(DATA_SERVER_USERNAME_ENV)
|
||||
<< "/" << qPrintable(DATA_SERVER_PASSWORD_ENV) << "are not set. Unable to authenticate.";
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void AssignmentClient::assignmentCompleted() {
|
||||
// reset the logging target to the the CHILD_TARGET_NAME
|
||||
Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
|
||||
|
@ -175,4 +210,5 @@ void AssignmentClient::assignmentCompleted() {
|
|||
// reset our NodeList by switching back to unassigned and clearing the list
|
||||
nodeList->setOwnerType(NodeType::Unassigned);
|
||||
nodeList->reset();
|
||||
nodeList->resetNodeInterestSet();
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ private slots:
|
|||
void sendAssignmentRequest();
|
||||
void readPendingDatagrams();
|
||||
void assignmentCompleted();
|
||||
void handleAuthenticationRequest();
|
||||
private:
|
||||
Assignment _requestAssignment;
|
||||
ThreadedAssignment* _currentAssignment;
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
const char* NUM_FORKS_PARAMETER = "-n";
|
||||
|
||||
const char ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME[] = "assignment-client-monitor";
|
||||
const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor";
|
||||
|
||||
AssignmentClientMonitor::AssignmentClientMonitor(int &argc, char **argv, int numAssignmentClientForks) :
|
||||
QCoreApplication(argc, argv)
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
const short JITTER_BUFFER_MSECS = 12;
|
||||
const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_MSECS * (SAMPLE_RATE / 1000.0);
|
||||
|
||||
const char AUDIO_MIXER_LOGGING_TARGET_NAME[] = "audio-mixer";
|
||||
const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer";
|
||||
|
||||
void attachNewBufferToNode(Node *newNode) {
|
||||
if (!newNode->getLinkedData()) {
|
||||
|
@ -61,9 +61,10 @@ void attachNewBufferToNode(Node *newNode) {
|
|||
}
|
||||
|
||||
AudioMixer::AudioMixer(const QByteArray& packet) :
|
||||
ThreadedAssignment(packet)
|
||||
ThreadedAssignment(packet),
|
||||
_clientMixBuffer(NETWORK_BUFFER_LENGTH_BYTES_STEREO + numBytesForPacketHeaderGivenPacketType(PacketTypeMixedAudio), 0)
|
||||
{
|
||||
|
||||
connect(NodeList::getInstance(), &NodeList::uuidChanged, this, &AudioMixer::receivedSessionUUID);
|
||||
}
|
||||
|
||||
void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuffer* bufferToAdd,
|
||||
|
@ -229,6 +230,10 @@ void AudioMixer::readPendingDatagrams() {
|
|||
}
|
||||
}
|
||||
|
||||
void AudioMixer::receivedSessionUUID(const QUuid& sessionUUID) {
|
||||
populatePacketHeader(_clientMixBuffer, PacketTypeMixedAudio);
|
||||
}
|
||||
|
||||
void AudioMixer::run() {
|
||||
|
||||
commonInit(AUDIO_MIXER_LOGGING_TARGET_NAME, NodeType::AudioMixer);
|
||||
|
@ -245,13 +250,6 @@ void AudioMixer::run() {
|
|||
gettimeofday(&startTime, NULL);
|
||||
|
||||
int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(PacketTypeMixedAudio);
|
||||
// note: Visual Studio 2010 doesn't support variable sized local arrays
|
||||
#ifdef _WIN32
|
||||
unsigned char clientPacket[MAX_PACKET_SIZE];
|
||||
#else
|
||||
unsigned char clientPacket[NETWORK_BUFFER_LENGTH_BYTES_STEREO + numBytesPacketHeader];
|
||||
#endif
|
||||
populatePacketHeader(reinterpret_cast<char*>(clientPacket), PacketTypeMixedAudio);
|
||||
|
||||
while (!_isFinished) {
|
||||
|
||||
|
@ -272,8 +270,8 @@ void AudioMixer::run() {
|
|||
&& ((AudioMixerClientData*) node->getLinkedData())->getAvatarAudioRingBuffer()) {
|
||||
prepareMixForListeningNode(node.data());
|
||||
|
||||
memcpy(clientPacket + numBytesPacketHeader, _clientSamples, sizeof(_clientSamples));
|
||||
nodeList->writeDatagram((char*) clientPacket, sizeof(clientPacket), node);
|
||||
memcpy(_clientMixBuffer.data() + numBytesPacketHeader, _clientSamples, sizeof(_clientSamples));
|
||||
nodeList->writeDatagram(_clientMixBuffer, node);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@ public slots:
|
|||
void run();
|
||||
|
||||
void readPendingDatagrams();
|
||||
private slots:
|
||||
void receivedSessionUUID(const QUuid& sessionUUID);
|
||||
private:
|
||||
/// adds one buffer to the mix for a listening node
|
||||
void addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuffer* bufferToAdd,
|
||||
|
@ -34,6 +36,7 @@ private:
|
|||
/// prepares and sends a mix to one Node
|
||||
void prepareMixForListeningNode(Node* node);
|
||||
|
||||
QByteArray _clientMixBuffer;
|
||||
|
||||
int16_t _clientSamples[NETWORK_BUFFER_LENGTH_SAMPLES_STEREO];
|
||||
};
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
#include "AvatarMixer.h"
|
||||
|
||||
const char AVATAR_MIXER_LOGGING_NAME[] = "avatar-mixer";
|
||||
const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
|
||||
|
||||
const unsigned int AVATAR_DATA_SEND_INTERVAL_USECS = (1 / 60.0) * 1000 * 1000;
|
||||
|
||||
|
@ -61,26 +61,36 @@ void broadcastAvatarData() {
|
|||
// reset packet pointers for this node
|
||||
mixedAvatarByteArray.resize(numPacketHeaderBytes);
|
||||
|
||||
AvatarMixerClientData* myData = reinterpret_cast<AvatarMixerClientData*>(node->getLinkedData());
|
||||
glm::vec3 myPosition = myData->getPosition();
|
||||
|
||||
// this is an AGENT we have received head data from
|
||||
// send back a packet with other active node data to this node
|
||||
foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) {
|
||||
if (otherNode->getLinkedData() && otherNode->getUUID() != node->getUUID()) {
|
||||
|
||||
QByteArray avatarByteArray;
|
||||
avatarByteArray.append(otherNode->getUUID().toRfc4122());
|
||||
|
||||
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData());
|
||||
avatarByteArray.append(nodeData->toByteArray());
|
||||
|
||||
if (avatarByteArray.size() + mixedAvatarByteArray.size() > MAX_PACKET_SIZE) {
|
||||
nodeList->writeDatagram(mixedAvatarByteArray, node);
|
||||
AvatarMixerClientData* otherNodeData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData());
|
||||
glm::vec3 otherPosition = otherNodeData->getPosition();
|
||||
float distanceToAvatar = glm::length(myPosition - otherPosition);
|
||||
// The full rate distance is the distance at which EVERY update will be sent for this avatar
|
||||
// at a distance of twice the full rate distance, there will be a 50% chance of sending this avatar's update
|
||||
const float FULL_RATE_DISTANCE = 2.f;
|
||||
// Decide whether to send this avatar's data based on it's distance from us
|
||||
if ((distanceToAvatar == 0.f) || (randFloat() < FULL_RATE_DISTANCE / distanceToAvatar)) {
|
||||
QByteArray avatarByteArray;
|
||||
avatarByteArray.append(otherNode->getUUID().toRfc4122());
|
||||
avatarByteArray.append(otherNodeData->toByteArray());
|
||||
|
||||
// reset the packet
|
||||
mixedAvatarByteArray.resize(numPacketHeaderBytes);
|
||||
if (avatarByteArray.size() + mixedAvatarByteArray.size() > MAX_PACKET_SIZE) {
|
||||
nodeList->writeDatagram(mixedAvatarByteArray, node);
|
||||
|
||||
// reset the packet
|
||||
mixedAvatarByteArray.resize(numPacketHeaderBytes);
|
||||
}
|
||||
|
||||
// copy the avatar into the mixedAvatarByteArray packet
|
||||
mixedAvatarByteArray.append(avatarByteArray);
|
||||
}
|
||||
|
||||
// copy the avatar into the mixedAvatarByteArray packet
|
||||
mixedAvatarByteArray.append(avatarByteArray);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +103,7 @@ void broadcastIdentityPacket() {
|
|||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
QByteArray avatarIdentityPacket = byteArrayWithPopluatedHeader(PacketTypeAvatarIdentity);
|
||||
QByteArray avatarIdentityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity);
|
||||
int numPacketHeaderBytes = avatarIdentityPacket.size();
|
||||
|
||||
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
||||
|
@ -123,12 +133,36 @@ void broadcastIdentityPacket() {
|
|||
}
|
||||
}
|
||||
|
||||
void broadcastBillboardPacket(const SharedNodePointer& sendingNode) {
|
||||
AvatarMixerClientData* nodeData = static_cast<AvatarMixerClientData*>(sendingNode->getLinkedData());
|
||||
QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard);
|
||||
packet.append(sendingNode->getUUID().toRfc4122());
|
||||
packet.append(nodeData->getBillboard());
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
||||
if (node->getType() == NodeType::Agent && node != sendingNode) {
|
||||
nodeList->writeDatagram(packet, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void broadcastBillboardPackets() {
|
||||
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
||||
if (node->getLinkedData() && node->getType() == NodeType::Agent) {
|
||||
AvatarMixerClientData* nodeData = static_cast<AvatarMixerClientData*>(node->getLinkedData());
|
||||
broadcastBillboardPacket(node);
|
||||
nodeData->setHasSentBillboardBetweenKeyFrames(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
|
||||
if (killedNode->getType() == NodeType::Agent
|
||||
&& killedNode->getLinkedData()) {
|
||||
// this was an avatar we were sending to other people
|
||||
// send a kill packet for it to our other nodes
|
||||
QByteArray killPacket = byteArrayWithPopluatedHeader(PacketTypeKillAvatar);
|
||||
QByteArray killPacket = byteArrayWithPopulatedHeader(PacketTypeKillAvatar);
|
||||
killPacket += killedNode->getUUID().toRfc4122();
|
||||
|
||||
NodeList::getInstance()->broadcastToNodes(killPacket,
|
||||
|
@ -159,7 +193,7 @@ void AvatarMixer::readPendingDatagrams() {
|
|||
if (nodeData->hasIdentityChangedAfterParsing(receivedPacket)
|
||||
&& !nodeData->hasSentIdentityBetweenKeyFrames()) {
|
||||
// this avatar changed their identity in some way and we haven't sent a packet in this keyframe
|
||||
QByteArray identityPacket = byteArrayWithPopluatedHeader(PacketTypeAvatarIdentity);
|
||||
QByteArray identityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity);
|
||||
|
||||
QByteArray individualByteArray = nodeData->identityByteArray();
|
||||
individualByteArray.replace(0, NUM_BYTES_RFC4122_UUID, avatarNode->getUUID().toRfc4122());
|
||||
|
@ -170,6 +204,23 @@ void AvatarMixer::readPendingDatagrams() {
|
|||
nodeList->broadcastToNodes(identityPacket, NodeSet() << NodeType::Agent);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PacketTypeAvatarBillboard: {
|
||||
|
||||
// check if we have a matching node in our list
|
||||
SharedNodePointer avatarNode = nodeList->sendingNodeForPacket(receivedPacket);
|
||||
|
||||
if (avatarNode && avatarNode->getLinkedData()) {
|
||||
AvatarMixerClientData* nodeData = static_cast<AvatarMixerClientData*>(avatarNode->getLinkedData());
|
||||
if (nodeData->hasBillboardChangedAfterParsing(receivedPacket)
|
||||
&& !nodeData->hasSentBillboardBetweenKeyFrames()) {
|
||||
// this avatar changed their billboard and we haven't sent a packet in this keyframe
|
||||
broadcastBillboardPacket(avatarNode);
|
||||
nodeData->setHasSentBillboardBetweenKeyFrames(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PacketTypeKillAvatar: {
|
||||
nodeList->processKillNode(receivedPacket);
|
||||
|
@ -185,6 +236,7 @@ void AvatarMixer::readPendingDatagrams() {
|
|||
}
|
||||
|
||||
const qint64 AVATAR_IDENTITY_KEYFRAME_MSECS = 5000;
|
||||
const qint64 AVATAR_BILLBOARD_KEYFRAME_MSECS = 5000;
|
||||
|
||||
void AvatarMixer::run() {
|
||||
commonInit(AVATAR_MIXER_LOGGING_NAME, NodeType::AvatarMixer);
|
||||
|
@ -202,6 +254,9 @@ void AvatarMixer::run() {
|
|||
QElapsedTimer identityTimer;
|
||||
identityTimer.start();
|
||||
|
||||
QElapsedTimer billboardTimer;
|
||||
billboardTimer.start();
|
||||
|
||||
while (!_isFinished) {
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
@ -219,6 +274,11 @@ void AvatarMixer::run() {
|
|||
// restart the timer so we do it again in AVATAR_IDENTITY_KEYFRAME_MSECS
|
||||
identityTimer.restart();
|
||||
}
|
||||
|
||||
if (billboardTimer.elapsed() >= AVATAR_BILLBOARD_KEYFRAME_MSECS) {
|
||||
broadcastBillboardPackets();
|
||||
billboardTimer.restart();
|
||||
}
|
||||
|
||||
int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * AVATAR_DATA_SEND_INTERVAL_USECS) - usecTimestampNow();
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
#include "AvatarMixerClientData.h"
|
||||
|
||||
AvatarMixerClientData::AvatarMixerClientData() :
|
||||
_hasSentIdentityBetweenKeyFrames(false)
|
||||
_hasSentIdentityBetweenKeyFrames(false),
|
||||
_hasSentBillboardBetweenKeyFrames(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -21,9 +21,15 @@ public:
|
|||
bool hasSentIdentityBetweenKeyFrames() const { return _hasSentIdentityBetweenKeyFrames; }
|
||||
void setHasSentIdentityBetweenKeyFrames(bool hasSentIdentityBetweenKeyFrames)
|
||||
{ _hasSentIdentityBetweenKeyFrames = hasSentIdentityBetweenKeyFrames; }
|
||||
|
||||
bool hasSentBillboardBetweenKeyFrames() const { return _hasSentBillboardBetweenKeyFrames; }
|
||||
void setHasSentBillboardBetweenKeyFrames(bool hasSentBillboardBetweenKeyFrames)
|
||||
{ _hasSentBillboardBetweenKeyFrames = hasSentBillboardBetweenKeyFrames; }
|
||||
|
||||
private:
|
||||
|
||||
bool _hasSentIdentityBetweenKeyFrames;
|
||||
bool _hasSentBillboardBetweenKeyFrames;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__AvatarMixerClientData__) */
|
||||
|
|
|
@ -28,15 +28,16 @@ void MetavoxelServer::applyEdit(const MetavoxelEditMessage& edit) {
|
|||
edit.apply(_data);
|
||||
}
|
||||
|
||||
void MetavoxelServer::removeSession(const QUuid& sessionId) {
|
||||
_sessions.take(sessionId)->deleteLater();
|
||||
}
|
||||
|
||||
const char METAVOXEL_SERVER_LOGGING_NAME[] = "metavoxel-server";
|
||||
const QString METAVOXEL_SERVER_LOGGING_NAME = "metavoxel-server";
|
||||
|
||||
void MetavoxelServer::run() {
|
||||
commonInit(METAVOXEL_SERVER_LOGGING_NAME, NodeType::MetavoxelServer);
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
||||
|
||||
connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), SLOT(maybeAttachSession(const SharedNodePointer&)));
|
||||
|
||||
_lastSend = QDateTime::currentMSecsSinceEpoch();
|
||||
_sendTimer.start(SEND_INTERVAL);
|
||||
}
|
||||
|
@ -50,25 +51,31 @@ void MetavoxelServer::readPendingDatagrams() {
|
|||
while (readAvailableDatagram(receivedPacket, senderSockAddr)) {
|
||||
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
|
||||
switch (packetTypeForPacket(receivedPacket)) {
|
||||
case PacketTypeMetavoxelData: {
|
||||
SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket);
|
||||
if (matchingNode) {
|
||||
processData(receivedPacket, matchingNode);
|
||||
}
|
||||
case PacketTypeMetavoxelData:
|
||||
nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
NodeList::getInstance()->processNodeData(senderSockAddr, receivedPacket);
|
||||
nodeList->processNodeData(senderSockAddr, receivedPacket);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MetavoxelServer::maybeAttachSession(const SharedNodePointer& node) {
|
||||
if (node->getType() == NodeType::Agent) {
|
||||
QMutexLocker locker(&node->getMutex());
|
||||
node->setLinkedData(new MetavoxelSession(this, NodeList::getInstance()->nodeWithUUID(node->getUUID())));
|
||||
}
|
||||
}
|
||||
|
||||
void MetavoxelServer::sendDeltas() {
|
||||
// send deltas for all sessions
|
||||
foreach (MetavoxelSession* session, _sessions) {
|
||||
session->sendDelta();
|
||||
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
||||
if (node->getType() == NodeType::Agent) {
|
||||
static_cast<MetavoxelSession*>(node->getLinkedData())->sendDelta();
|
||||
}
|
||||
}
|
||||
|
||||
// restart the send timer
|
||||
|
@ -79,35 +86,10 @@ void MetavoxelServer::sendDeltas() {
|
|||
_sendTimer.start(qMax(0, 2 * SEND_INTERVAL - elapsed));
|
||||
}
|
||||
|
||||
void MetavoxelServer::processData(const QByteArray& data, const SharedNodePointer& sendingNode) {
|
||||
// read the session id
|
||||
int headerPlusIDSize;
|
||||
QUuid sessionID = readSessionID(data, sendingNode, headerPlusIDSize);
|
||||
if (sessionID.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// forward to session, creating if necessary
|
||||
MetavoxelSession*& session = _sessions[sessionID];
|
||||
if (!session) {
|
||||
session = new MetavoxelSession(this, sessionID, QByteArray::fromRawData(data.constData(), headerPlusIDSize),
|
||||
sendingNode);
|
||||
}
|
||||
session->receivedData(data, sendingNode);
|
||||
}
|
||||
|
||||
MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const QUuid& sessionId,
|
||||
const QByteArray& datagramHeader, const SharedNodePointer& sendingNode) :
|
||||
QObject(server),
|
||||
MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const SharedNodePointer& node) :
|
||||
_server(server),
|
||||
_sessionId(sessionId),
|
||||
_sequencer(datagramHeader),
|
||||
_sendingNode(sendingNode) {
|
||||
|
||||
const int TIMEOUT_INTERVAL = 30 * 1000;
|
||||
_timeoutTimer.setInterval(TIMEOUT_INTERVAL);
|
||||
_timeoutTimer.setSingleShot(true);
|
||||
connect(&_timeoutTimer, SIGNAL(timeout()), SLOT(timedOut()));
|
||||
_sequencer(byteArrayWithPopulatedHeader(PacketTypeMetavoxelData)),
|
||||
_node(node) {
|
||||
|
||||
connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&)));
|
||||
connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&)));
|
||||
|
@ -117,19 +99,15 @@ MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const QUuid& session
|
|||
// insert the baseline send record
|
||||
SendRecord record = { 0 };
|
||||
_sendRecords.append(record);
|
||||
|
||||
qDebug() << "Opened session [sessionId=" << _sessionId << ", sendingNode=" << sendingNode << "]";
|
||||
}
|
||||
|
||||
void MetavoxelSession::receivedData(const QByteArray& data, const SharedNodePointer& sendingNode) {
|
||||
// reset the timeout timer
|
||||
_timeoutTimer.start();
|
||||
MetavoxelSession::~MetavoxelSession() {
|
||||
}
|
||||
|
||||
// save the most recent sender
|
||||
_sendingNode = sendingNode;
|
||||
|
||||
int MetavoxelSession::parseData(const QByteArray& packet) {
|
||||
// process through sequencer
|
||||
_sequencer.receivedDatagram(data);
|
||||
_sequencer.receivedDatagram(packet);
|
||||
return packet.size();
|
||||
}
|
||||
|
||||
void MetavoxelSession::sendDelta() {
|
||||
|
@ -143,13 +121,8 @@ void MetavoxelSession::sendDelta() {
|
|||
_sendRecords.append(record);
|
||||
}
|
||||
|
||||
void MetavoxelSession::timedOut() {
|
||||
qDebug() << "Session timed out [sessionId=" << _sessionId << ", sendingNode=" << _sendingNode << "]";
|
||||
_server->removeSession(_sessionId);
|
||||
}
|
||||
|
||||
void MetavoxelSession::sendData(const QByteArray& data) {
|
||||
NodeList::getInstance()->writeDatagram(data, _sendingNode);
|
||||
NodeList::getInstance()->writeDatagram(data, _node);
|
||||
}
|
||||
|
||||
void MetavoxelSession::readPacket(Bitstream& in) {
|
||||
|
@ -164,11 +137,7 @@ void MetavoxelSession::clearSendRecordsBefore(int index) {
|
|||
|
||||
void MetavoxelSession::handleMessage(const QVariant& message) {
|
||||
int userType = message.userType();
|
||||
if (userType == CloseSessionMessage::Type) {
|
||||
qDebug() << "Session closed [sessionId=" << _sessionId << ", sendingNode=" << _sendingNode << "]";
|
||||
_server->removeSession(_sessionId);
|
||||
|
||||
} else if (userType == ClientStateMessage::Type) {
|
||||
if (userType == ClientStateMessage::Type) {
|
||||
ClientStateMessage state = message.value<ClientStateMessage>();
|
||||
_position = state.position;
|
||||
|
||||
|
|
|
@ -9,12 +9,9 @@
|
|||
#ifndef __hifi__MetavoxelServer__
|
||||
#define __hifi__MetavoxelServer__
|
||||
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QTimer>
|
||||
#include <QUuid>
|
||||
|
||||
#include <HifiSockAddr.h>
|
||||
#include <ThreadedAssignment.h>
|
||||
|
||||
#include <DatagramSequencer.h>
|
||||
|
@ -35,45 +32,38 @@ public:
|
|||
|
||||
const MetavoxelData& getData() const { return _data; }
|
||||
|
||||
void removeSession(const QUuid& sessionId);
|
||||
|
||||
virtual void run();
|
||||
|
||||
virtual void readPendingDatagrams();
|
||||
|
||||
private slots:
|
||||
|
||||
|
||||
void maybeAttachSession(const SharedNodePointer& node);
|
||||
void sendDeltas();
|
||||
|
||||
private:
|
||||
|
||||
void processData(const QByteArray& data, const SharedNodePointer& sendingNode);
|
||||
|
||||
QTimer _sendTimer;
|
||||
qint64 _lastSend;
|
||||
|
||||
QHash<QUuid, MetavoxelSession*> _sessions;
|
||||
|
||||
MetavoxelData _data;
|
||||
};
|
||||
|
||||
/// Contains the state of a single client session.
|
||||
class MetavoxelSession : public QObject {
|
||||
class MetavoxelSession : public NodeData {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
MetavoxelSession(MetavoxelServer* server, const QUuid& sessionId,
|
||||
const QByteArray& datagramHeader, const SharedNodePointer& sendingNode);
|
||||
MetavoxelSession(MetavoxelServer* server, const SharedNodePointer& node);
|
||||
virtual ~MetavoxelSession();
|
||||
|
||||
void receivedData(const QByteArray& data, const SharedNodePointer& sendingNode);
|
||||
virtual int parseData(const QByteArray& packet);
|
||||
|
||||
void sendDelta();
|
||||
|
||||
private slots:
|
||||
|
||||
void timedOut();
|
||||
|
||||
void sendData(const QByteArray& data);
|
||||
|
||||
void readPacket(Bitstream& in);
|
||||
|
@ -91,12 +81,10 @@ private:
|
|||
};
|
||||
|
||||
MetavoxelServer* _server;
|
||||
QUuid _sessionId;
|
||||
|
||||
QTimer _timeoutTimer;
|
||||
DatagramSequencer _sequencer;
|
||||
|
||||
SharedNodePointer _sendingNode;
|
||||
SharedNodePointer _node;
|
||||
|
||||
glm::vec3 _position;
|
||||
|
||||
|
|
|
@ -37,6 +37,18 @@ OctreeQueryNode::OctreeQueryNode() :
|
|||
_sequenceNumber = 0;
|
||||
}
|
||||
|
||||
OctreeQueryNode::~OctreeQueryNode() {
|
||||
if (_octreeSendThread) {
|
||||
_octreeSendThread->terminate();
|
||||
delete _octreeSendThread;
|
||||
}
|
||||
|
||||
delete[] _octreePacket;
|
||||
delete[] _lastOctreePacket;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer, const QUuid& nodeUUID) {
|
||||
// Create octree sending thread...
|
||||
_octreeSendThread = new OctreeSendThread(nodeUUID, octreeServer);
|
||||
|
@ -158,16 +170,6 @@ void OctreeQueryNode::writeToPacket(const unsigned char* buffer, int bytes) {
|
|||
}
|
||||
}
|
||||
|
||||
OctreeQueryNode::~OctreeQueryNode() {
|
||||
if (_octreeSendThread) {
|
||||
_octreeSendThread->terminate();
|
||||
_octreeSendThread->deleteLater();
|
||||
}
|
||||
|
||||
delete[] _octreePacket;
|
||||
delete[] _lastOctreePacket;
|
||||
}
|
||||
|
||||
bool OctreeQueryNode::updateCurrentViewFrustum() {
|
||||
bool currentViewFrustumChanged = false;
|
||||
ViewFrustum newestViewFrustum;
|
||||
|
|
|
@ -20,39 +20,52 @@ quint64 endSceneSleepTime = 0;
|
|||
OctreeSendThread::OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer) :
|
||||
_nodeUUID(nodeUUID),
|
||||
_myServer(myServer),
|
||||
_packetData()
|
||||
_packetData(),
|
||||
_nodeMissingCount(0)
|
||||
{
|
||||
qDebug() << "client connected - starting sending thread";
|
||||
OctreeServer::clientConnected();
|
||||
}
|
||||
|
||||
OctreeSendThread::~OctreeSendThread() {
|
||||
qDebug() << "client disconnected - ending sending thread";
|
||||
OctreeServer::clientDisconnected();
|
||||
}
|
||||
|
||||
|
||||
bool OctreeSendThread::process() {
|
||||
|
||||
const int MAX_NODE_MISSING_CHECKS = 10;
|
||||
if (_nodeMissingCount > MAX_NODE_MISSING_CHECKS) {
|
||||
qDebug() << "our target node:" << _nodeUUID << "has been missing the last" << _nodeMissingCount
|
||||
<< "times we checked, we are going to stop attempting to send.";
|
||||
return false; // stop processing and shutdown, our node no longer exists
|
||||
}
|
||||
|
||||
quint64 start = usecTimestampNow();
|
||||
bool gotLock = false;
|
||||
|
||||
// don't do any send processing until the initial load of the octree is complete...
|
||||
if (_myServer->isInitialLoadComplete()) {
|
||||
SharedNodePointer node = NodeList::getInstance()->nodeWithUUID(_nodeUUID);
|
||||
|
||||
if (node) {
|
||||
// make sure the node list doesn't kill our node while we're using it
|
||||
if (node->getMutex().tryLock()) {
|
||||
gotLock = true;
|
||||
OctreeQueryNode* nodeData = NULL;
|
||||
_nodeMissingCount = 0;
|
||||
OctreeQueryNode* nodeData = NULL;
|
||||
|
||||
nodeData = (OctreeQueryNode*) node->getLinkedData();
|
||||
nodeData = (OctreeQueryNode*) node->getLinkedData();
|
||||
|
||||
int packetsSent = 0;
|
||||
int packetsSent = 0;
|
||||
|
||||
// Sometimes the node data has not yet been linked, in which case we can't really do anything
|
||||
if (nodeData) {
|
||||
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
|
||||
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
|
||||
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
|
||||
}
|
||||
packetsSent = packetDistributor(node, nodeData, viewFrustumChanged);
|
||||
// Sometimes the node data has not yet been linked, in which case we can't really do anything
|
||||
if (nodeData) {
|
||||
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
|
||||
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
|
||||
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
|
||||
}
|
||||
|
||||
node->getMutex().unlock(); // we're done with this node for now.
|
||||
packetsSent = packetDistributor(node, nodeData, viewFrustumChanged);
|
||||
}
|
||||
} else {
|
||||
_nodeMissingCount++;
|
||||
}
|
||||
} else {
|
||||
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
|
||||
|
@ -61,7 +74,7 @@ bool OctreeSendThread::process() {
|
|||
}
|
||||
|
||||
// Only sleep if we're still running and we got the lock last time we tried, otherwise try to get the lock asap
|
||||
if (isStillRunning() && gotLock) {
|
||||
if (isStillRunning()) {
|
||||
// dynamically sleep until we need to fire off the next set of octree elements
|
||||
int elapsed = (usecTimestampNow() - start);
|
||||
int usecToSleep = OCTREE_SEND_INTERVAL_USECS - elapsed;
|
||||
|
@ -70,9 +83,12 @@ bool OctreeSendThread::process() {
|
|||
PerformanceWarning warn(false,"OctreeSendThread... usleep()",false,&_usleepTime,&_usleepCalls);
|
||||
usleep(usecToSleep);
|
||||
} else {
|
||||
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
|
||||
std::cout << "Last send took too much time, not sleeping!\n";
|
||||
if (true || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) {
|
||||
qDebug() << "Last send took too much time (" << (elapsed / USECS_PER_MSEC)
|
||||
<<" msecs), barely sleeping 1 usec!\n";
|
||||
}
|
||||
const int MIN_USEC_TO_SLEEP = 1;
|
||||
usleep(MIN_USEC_TO_SLEEP);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,6 +110,13 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, OctreeQuer
|
|||
int packetsSent = 0;
|
||||
|
||||
// double check that the node has an active socket, otherwise, don't send...
|
||||
|
||||
quint64 lockWaitStart = usecTimestampNow();
|
||||
QMutexLocker locker(&node->getMutex());
|
||||
quint64 lockWaitEnd = usecTimestampNow();
|
||||
float lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart);
|
||||
OctreeServer::trackNodeWaitTime(lockWaitElapsedUsec);
|
||||
|
||||
const HifiSockAddr* nodeAddress = node->getActiveSocket();
|
||||
if (!nodeAddress) {
|
||||
return packetsSent; // without sending...
|
||||
|
@ -420,9 +443,19 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue
|
|||
isFullScene, &nodeData->stats, _myServer->getJurisdiction());
|
||||
|
||||
|
||||
quint64 lockWaitStart = usecTimestampNow();
|
||||
_myServer->getOctree()->lockForRead();
|
||||
quint64 lockWaitEnd = usecTimestampNow();
|
||||
float lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart);
|
||||
OctreeServer::trackTreeWaitTime(lockWaitElapsedUsec);
|
||||
|
||||
nodeData->stats.encodeStarted();
|
||||
|
||||
quint64 encodeStart = usecTimestampNow();
|
||||
bytesWritten = _myServer->getOctree()->encodeTreeBitstream(subTree, &_packetData, nodeData->nodeBag, params);
|
||||
quint64 encodeEnd = usecTimestampNow();
|
||||
int encodeElapsedMsec = (encodeEnd - encodeStart)/USECS_PER_MSEC;
|
||||
OctreeServer::trackEncodeTime(encodeElapsedMsec);
|
||||
|
||||
// If after calling encodeTreeBitstream() there are no nodes left to send, then we know we've
|
||||
// sent the entire scene. We want to know this below so we'll actually write this content into
|
||||
|
@ -535,7 +568,8 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue
|
|||
|
||||
|
||||
quint64 end = usecTimestampNow();
|
||||
int elapsedmsec = (end - start)/1000;
|
||||
int elapsedmsec = (end - start)/USECS_PER_MSEC;
|
||||
OctreeServer::trackLoopTime(elapsedmsec);
|
||||
|
||||
quint64 endCompressCalls = OctreePacketData::getCompressContentCalls();
|
||||
int elapsedCompressCalls = endCompressCalls - startCompressCalls;
|
||||
|
@ -543,7 +577,6 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue
|
|||
quint64 endCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000;
|
||||
int elapsedCompressTimeMsecs = endCompressTimeMsecs - startCompressTimeMsecs;
|
||||
|
||||
|
||||
if (elapsedmsec > 100) {
|
||||
if (elapsedmsec > 1000) {
|
||||
int elapsedsec = (end - start)/1000000;
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
#include "OctreeQueryNode.h"
|
||||
#include "OctreeServer.h"
|
||||
|
||||
|
||||
/// Threaded processor for sending voxel packets to a single client
|
||||
class OctreeSendThread : public GenericThread {
|
||||
public:
|
||||
OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer);
|
||||
virtual ~OctreeSendThread();
|
||||
|
||||
static quint64 _totalBytes;
|
||||
static quint64 _totalWastedBytes;
|
||||
|
@ -40,6 +42,8 @@ private:
|
|||
int packetDistributor(const SharedNodePointer& node, OctreeQueryNode* nodeData, bool viewFrustumChanged);
|
||||
|
||||
OctreePacketData _packetData;
|
||||
|
||||
int _nodeMissingCount;
|
||||
};
|
||||
|
||||
#endif // __octree_server__OctreeSendThread__
|
||||
|
|
|
@ -19,6 +19,11 @@
|
|||
#include "OctreeServerConsts.h"
|
||||
|
||||
OctreeServer* OctreeServer::_instance = NULL;
|
||||
int OctreeServer::_clientCount = 0;
|
||||
SimpleMovingAverage OctreeServer::_averageLoopTime(10000);
|
||||
SimpleMovingAverage OctreeServer::_averageEncodeTime(10000);
|
||||
SimpleMovingAverage OctreeServer::_averageTreeWaitTime(10000);
|
||||
SimpleMovingAverage OctreeServer::_averageNodeWaitTime(10000);
|
||||
|
||||
void OctreeServer::attachQueryNodeToNode(Node* newNode) {
|
||||
if (newNode->getLinkedData() == NULL) {
|
||||
|
@ -35,6 +40,7 @@ OctreeServer::OctreeServer(const QByteArray& packet) :
|
|||
_parsedArgV(NULL),
|
||||
_httpManager(NULL),
|
||||
_packetsPerClientPerInterval(10),
|
||||
_packetsTotalPerInterval(DEFAULT_PACKETS_PER_INTERVAL),
|
||||
_tree(NULL),
|
||||
_wantPersist(true),
|
||||
_debugSending(false),
|
||||
|
@ -48,6 +54,7 @@ OctreeServer::OctreeServer(const QByteArray& packet) :
|
|||
_startedUSecs(usecTimestampNow())
|
||||
{
|
||||
_instance = this;
|
||||
_averageLoopTime.updateAverage(0);
|
||||
}
|
||||
|
||||
OctreeServer::~OctreeServer() {
|
||||
|
@ -75,8 +82,7 @@ OctreeServer::~OctreeServer() {
|
|||
|
||||
delete _jurisdiction;
|
||||
_jurisdiction = NULL;
|
||||
|
||||
qDebug() << "OctreeServer::run()... DONE";
|
||||
qDebug() << "OctreeServer::~OctreeServer()... DONE";
|
||||
}
|
||||
|
||||
void OctreeServer::initHTTPManager(int port) {
|
||||
|
@ -155,12 +161,24 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
statsString += "Uptime: ";
|
||||
|
||||
if (hours > 0) {
|
||||
statsString += QString("%1 hour%2").arg(hours).arg((hours > 1) ? "s" : "");
|
||||
statsString += QString("%1 hour").arg(hours);
|
||||
if (hours > 1) {
|
||||
statsString += QString("s");
|
||||
}
|
||||
}
|
||||
if (minutes > 0) {
|
||||
statsString += QString("%1 minute%s").arg(minutes).arg((minutes > 1) ? "s" : "");
|
||||
if (hours > 0) {
|
||||
statsString += QString(" ");
|
||||
}
|
||||
statsString += QString("%1 minute").arg(minutes);
|
||||
if (minutes > 1) {
|
||||
statsString += QString("s");
|
||||
}
|
||||
}
|
||||
if (seconds > 0) {
|
||||
if (hours > 0 || minutes > 0) {
|
||||
statsString += QString(" ");
|
||||
}
|
||||
statsString += QString().sprintf("%.3f seconds", seconds);
|
||||
}
|
||||
statsString += "\r\n\r\n";
|
||||
|
@ -180,14 +198,26 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
|
||||
int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
|
||||
|
||||
statsString += QString("%1 File Load Took").arg(getMyServerName());
|
||||
statsString += QString("%1 File Load Took ").arg(getMyServerName());
|
||||
if (hours > 0) {
|
||||
statsString += QString("%1 hour%2").arg(hours).arg((hours > 1) ? "s" : "");
|
||||
statsString += QString("%1 hour").arg(hours);
|
||||
if (hours > 1) {
|
||||
statsString += QString("s");
|
||||
}
|
||||
}
|
||||
if (minutes > 0) {
|
||||
statsString += QString("%1 minute%2").arg(minutes).arg((minutes > 1) ? "s" : "");
|
||||
if (hours > 0) {
|
||||
statsString += QString(" ");
|
||||
}
|
||||
statsString += QString("%1 minute").arg(minutes);
|
||||
if (minutes > 1) {
|
||||
statsString += QString("s");
|
||||
}
|
||||
}
|
||||
if (seconds >= 0) {
|
||||
if (hours > 0 || minutes > 0) {
|
||||
statsString += QString(" ");
|
||||
}
|
||||
statsString += QString().sprintf("%.3f seconds", seconds);
|
||||
}
|
||||
statsString += "\r\n";
|
||||
|
@ -234,6 +264,33 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
quint64 totalBytesOfColor = OctreePacketData::getTotalBytesOfColor();
|
||||
|
||||
const int COLUMN_WIDTH = 10;
|
||||
statsString += QString(" Configured Max PPS/Client: %1 pps/client\r\n")
|
||||
.arg(locale.toString((uint)getPacketsPerClientPerSecond()).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Configured Max PPS/Server: %1 pps/server\r\n\r\n")
|
||||
.arg(locale.toString((uint)getPacketsTotalPerSecond()).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Total Clients Connected: %1 clients\r\n\r\n")
|
||||
.arg(locale.toString((uint)getCurrentClientCount()).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
float averageLoopTime = getAverageLoopTime();
|
||||
statsString += QString().sprintf(" Average packetLoop() time: %5.2f msecs\r\n", averageLoopTime);
|
||||
qDebug() << "averageLoopTime=" << averageLoopTime;
|
||||
|
||||
float averageEncodeTime = getAverageEncodeTime();
|
||||
statsString += QString().sprintf(" Average encode time: %5.2f msecs\r\n", averageEncodeTime);
|
||||
qDebug() << "averageEncodeTime=" << averageEncodeTime;
|
||||
|
||||
|
||||
float averageTreeWaitTime = getAverageTreeWaitTime();
|
||||
statsString += QString().sprintf(" Average tree lock wait time: %7.2f usecs\r\n", averageTreeWaitTime);
|
||||
qDebug() << "averageTreeWaitTime=" << averageTreeWaitTime;
|
||||
|
||||
float averageNodeWaitTime = getAverageNodeWaitTime();
|
||||
statsString += QString().sprintf(" Average node lock wait time: %7.2f usecs\r\n", averageNodeWaitTime);
|
||||
qDebug() << "averageNodeWaitTime=" << averageNodeWaitTime;
|
||||
|
||||
|
||||
statsString += QString("\r\n");
|
||||
|
||||
statsString += QString(" Total Outbound Packets: %1 packets\r\n")
|
||||
.arg(locale.toString((uint)totalOutboundPackets).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Total Outbound Bytes: %1 bytes\r\n")
|
||||
|
@ -551,6 +608,9 @@ void OctreeServer::run() {
|
|||
NodeList* nodeList = NodeList::getInstance();
|
||||
nodeList->setOwnerType(getMyNodeType());
|
||||
|
||||
connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer)));
|
||||
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)),SLOT(nodeKilled(SharedNodePointer)));
|
||||
|
||||
// we need to ask the DS about agents so we can ping/reply with them
|
||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
||||
|
||||
|
@ -612,15 +672,28 @@ void OctreeServer::run() {
|
|||
}
|
||||
|
||||
// Check to see if the user passed in a command line option for setting packet send rate
|
||||
const char* PACKETS_PER_SECOND = "--packetsPerSecond";
|
||||
const char* packetsPerSecond = getCmdOption(_argc, _argv, PACKETS_PER_SECOND);
|
||||
if (packetsPerSecond) {
|
||||
_packetsPerClientPerInterval = atoi(packetsPerSecond) / INTERVALS_PER_SECOND;
|
||||
const char* PACKETS_PER_SECOND_PER_CLIENT_MAX = "--packetsPerSecondPerClientMax";
|
||||
const char* packetsPerSecondPerClientMax = getCmdOption(_argc, _argv, PACKETS_PER_SECOND_PER_CLIENT_MAX);
|
||||
if (packetsPerSecondPerClientMax) {
|
||||
_packetsPerClientPerInterval = atoi(packetsPerSecondPerClientMax) / INTERVALS_PER_SECOND;
|
||||
if (_packetsPerClientPerInterval < 1) {
|
||||
_packetsPerClientPerInterval = 1;
|
||||
}
|
||||
qDebug("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d", packetsPerSecond, _packetsPerClientPerInterval);
|
||||
}
|
||||
qDebug("packetsPerSecondPerClientMax=%s _packetsPerClientPerInterval=%d",
|
||||
packetsPerSecondPerClientMax, _packetsPerClientPerInterval);
|
||||
|
||||
// Check to see if the user passed in a command line option for setting packet send rate
|
||||
const char* PACKETS_PER_SECOND_TOTAL_MAX = "--packetsPerSecondTotalMax";
|
||||
const char* packetsPerSecondTotalMax = getCmdOption(_argc, _argv, PACKETS_PER_SECOND_TOTAL_MAX);
|
||||
if (packetsPerSecondTotalMax) {
|
||||
_packetsTotalPerInterval = atoi(packetsPerSecondTotalMax) / INTERVALS_PER_SECOND;
|
||||
if (_packetsTotalPerInterval < 1) {
|
||||
_packetsTotalPerInterval = 1;
|
||||
}
|
||||
}
|
||||
qDebug("packetsPerSecondTotalMax=%s _packetsTotalPerInterval=%d",
|
||||
packetsPerSecondTotalMax, _packetsTotalPerInterval);
|
||||
|
||||
HifiSockAddr senderSockAddr;
|
||||
|
||||
|
@ -656,3 +729,20 @@ void OctreeServer::run() {
|
|||
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||
}
|
||||
|
||||
void OctreeServer::nodeAdded(SharedNodePointer node) {
|
||||
// we might choose to use this notifier to track clients in a pending state
|
||||
}
|
||||
|
||||
void OctreeServer::nodeKilled(SharedNodePointer node) {
|
||||
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(node->getLinkedData());
|
||||
if (nodeData) {
|
||||
// Note: It should be safe to do this without locking the node, because if any other threads
|
||||
// are using the SharedNodePointer, then they have a reference to the SharedNodePointer and the deleteLater()
|
||||
// won't actually delete it until all threads have released their references to the pointer.
|
||||
// But we can and should clear the linked data so that no one else tries to access it.
|
||||
nodeData->deleteLater();
|
||||
node->setLinkedData(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
#include "OctreeServerConsts.h"
|
||||
#include "OctreeInboundPacketProcessor.h"
|
||||
|
||||
const int DEFAULT_PACKETS_PER_INTERVAL = 2000; // some 120,000 packets per second total
|
||||
|
||||
/// Handles assignments of type OctreeServer - sending octrees to various clients.
|
||||
class OctreeServer : public ThreadedAssignment, public HTTPRequestHandler {
|
||||
Q_OBJECT
|
||||
|
@ -41,7 +43,16 @@ public:
|
|||
Octree* getOctree() { return _tree; }
|
||||
JurisdictionMap* getJurisdiction() { return _jurisdiction; }
|
||||
|
||||
int getPacketsPerClientPerInterval() const { return _packetsPerClientPerInterval; }
|
||||
int getPacketsPerClientPerInterval() const { return std::min(_packetsPerClientPerInterval,
|
||||
std::max(1, getPacketsTotalPerInterval() / std::max(1, getCurrentClientCount()))); }
|
||||
|
||||
int getPacketsPerClientPerSecond() const { return getPacketsPerClientPerInterval() * INTERVALS_PER_SECOND; }
|
||||
int getPacketsTotalPerInterval() const { return _packetsTotalPerInterval; }
|
||||
int getPacketsTotalPerSecond() const { return getPacketsTotalPerInterval() * INTERVALS_PER_SECOND; }
|
||||
|
||||
static int getCurrentClientCount() { return _clientCount; }
|
||||
static void clientConnected() { _clientCount++; }
|
||||
static void clientDisconnected() { _clientCount--; }
|
||||
|
||||
bool isInitialLoadComplete() const { return (_persistThread) ? _persistThread->isInitialLoadComplete() : true; }
|
||||
bool isPersistEnabled() const { return (_persistThread) ? true : false; }
|
||||
|
@ -63,11 +74,24 @@ public:
|
|||
|
||||
static void attachQueryNodeToNode(Node* newNode);
|
||||
|
||||
static void trackLoopTime(float time) { _averageLoopTime.updateAverage(time); }
|
||||
static float getAverageLoopTime() { return _averageLoopTime.getAverage(); }
|
||||
|
||||
static void trackEncodeTime(float time) { _averageEncodeTime.updateAverage(time); }
|
||||
static float getAverageEncodeTime() { return _averageEncodeTime.getAverage(); }
|
||||
|
||||
static void trackTreeWaitTime(float time) { _averageTreeWaitTime.updateAverage(time); }
|
||||
static float getAverageTreeWaitTime() { return _averageTreeWaitTime.getAverage(); }
|
||||
static void trackNodeWaitTime(float time) { _averageNodeWaitTime.updateAverage(time); }
|
||||
static float getAverageNodeWaitTime() { return _averageNodeWaitTime.getAverage(); }
|
||||
|
||||
bool handleHTTPRequest(HTTPConnection* connection, const QString& path);
|
||||
public slots:
|
||||
/// runs the voxel server assignment
|
||||
void run();
|
||||
void readPendingDatagrams();
|
||||
void nodeAdded(SharedNodePointer node);
|
||||
void nodeKilled(SharedNodePointer node);
|
||||
|
||||
protected:
|
||||
void parsePayload();
|
||||
|
@ -81,6 +105,7 @@ protected:
|
|||
|
||||
char _persistFilename[MAX_FILENAME_LENGTH];
|
||||
int _packetsPerClientPerInterval;
|
||||
int _packetsTotalPerInterval;
|
||||
Octree* _tree; // this IS a reaveraging tree
|
||||
bool _wantPersist;
|
||||
bool _debugSending;
|
||||
|
@ -95,6 +120,12 @@ protected:
|
|||
|
||||
time_t _started;
|
||||
quint64 _startedUSecs;
|
||||
|
||||
static int _clientCount;
|
||||
static SimpleMovingAverage _averageLoopTime;
|
||||
static SimpleMovingAverage _averageEncodeTime;
|
||||
static SimpleMovingAverage _averageTreeWaitTime;
|
||||
static SimpleMovingAverage _averageNodeWaitTime;
|
||||
};
|
||||
|
||||
#endif // __octree_server__OctreeServer__
|
||||
|
|
|
@ -3,20 +3,11 @@ macro(AUTO_MTC TARGET ROOT_DIR)
|
|||
add_subdirectory(${ROOT_DIR}/tools/mtc ${ROOT_DIR}/tools/mtc)
|
||||
endif (NOT TARGET mtc)
|
||||
|
||||
set(AUTOMTC_SRC ${TARGET}_automtc.cpp)
|
||||
|
||||
file(GLOB INCLUDE_FILES src/*.h)
|
||||
|
||||
add_custom_command(OUTPUT ${TARGET}_automtc.cpp COMMAND mtc -o ${TARGET}_automtc.cpp
|
||||
${INCLUDE_FILES} DEPENDS mtc ${INCLUDE_FILES})
|
||||
|
||||
find_package(Qt5Core REQUIRED)
|
||||
find_package(Qt5Script REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
|
||||
add_library(${TARGET}_automtc STATIC ${TARGET}_automtc.cpp)
|
||||
|
||||
qt5_use_modules(${TARGET}_automtc Core Script Widgets)
|
||||
|
||||
target_link_libraries(${TARGET} ${TARGET}_automtc)
|
||||
add_custom_command(OUTPUT ${AUTOMTC_SRC} COMMAND mtc -o ${AUTOMTC_SRC} ${INCLUDE_FILES} DEPENDS mtc ${INCLUDE_FILES})
|
||||
|
||||
endmacro()
|
||||
|
||||
|
|
76
cmake/modules/FindVisage.cmake
Normal file
76
cmake/modules/FindVisage.cmake
Normal file
|
@ -0,0 +1,76 @@
|
|||
# Try to find the Visage controller library
|
||||
#
|
||||
# You must provide a VISAGE_ROOT_DIR which contains lib and include directories
|
||||
#
|
||||
# Once done this will define
|
||||
#
|
||||
# VISAGE_FOUND - system found Visage
|
||||
# VISAGE_INCLUDE_DIRS - the Visage include directory
|
||||
# VISAGE_LIBRARIES - Link this to use Visage
|
||||
#
|
||||
# Created on 2/11/2014 by Andrzej Kapolka
|
||||
# Copyright (c) 2014 High Fidelity
|
||||
#
|
||||
|
||||
if (VISAGE_LIBRARIES AND VISAGE_INCLUDE_DIRS)
|
||||
# in cache already
|
||||
set(VISAGE_FOUND TRUE)
|
||||
else (VISAGE_LIBRARIES AND VISAGE_INCLUDE_DIRS)
|
||||
find_path(VISAGE_INCLUDE_DIR VisageTracker2.h ${VISAGE_ROOT_DIR}/include)
|
||||
|
||||
if (APPLE)
|
||||
find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h /usr/include/libxml2)
|
||||
find_path(VISAGE_OPENCV_INCLUDE_DIR cv.h ${VISAGE_ROOT_DIR}/dependencies/OpenCV_MacOSX/include)
|
||||
find_path(VISAGE_OPENCV2_INCLUDE_DIR opencv.hpp ${VISAGE_ROOT_DIR}/dependencies/OpenCV_MacOSX/include/opencv2)
|
||||
if (VISAGE_INCLUDE_DIR AND VISAGE_XML_INCLUDE_DIR AND VISAGE_OPENCV_INCLUDE_DIR AND VISAGE_OPENCV2_INCLUDE_DIR)
|
||||
set(VISAGE_INCLUDE_DIRS
|
||||
"${VISAGE_INCLUDE_DIR};${VISAGE_XML_INCLUDE_DIR};${VISAGE_OPENCV_INCLUDE_DIR};${VISAGE_OPENCV2_INCLUDE_DIR}"
|
||||
CACHE INTERNAL "Visage include dirs")
|
||||
endif (VISAGE_INCLUDE_DIR AND VISAGE_XML_INCLUDE_DIR AND VISAGE_OPENCV_INCLUDE_DIR AND VISAGE_OPENCV2_INCLUDE_DIR)
|
||||
|
||||
find_library(VISAGE_CORE_LIBRARY libvscore.a ${VISAGE_ROOT_DIR}/lib)
|
||||
find_library(VISAGE_VISION_LIBRARY libvsvision.a ${VISAGE_ROOT_DIR}/lib)
|
||||
find_library(VISAGE_OPENCV_LIBRARY libOpenCV.a ${VISAGE_ROOT_DIR}/dependencies/OpenCV_MacOSX/lib)
|
||||
if (VISAGE_CORE_LIBRARY AND VISAGE_VISION_LIBRARY AND VISAGE_OPENCV_LIBRARY)
|
||||
set(VISAGE_LIBRARIES "${VISAGE_CORE_LIBRARY};${VISAGE_VISION_LIBRARY};${VISAGE_OPENCV_LIBRARY}"
|
||||
CACHE INTERNAL "Visage libraries")
|
||||
endif (VISAGE_CORE_LIBRARY AND VISAGE_VISION_LIBRARY AND VISAGE_OPENCV_LIBRARY)
|
||||
|
||||
elseif (WIN32)
|
||||
find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h ${VISAGE_ROOT_DIR}/dependencies/libxml2/include)
|
||||
find_path(VISAGE_OPENCV_INCLUDE_DIR opencv/cv.h ${VISAGE_ROOT_DIR}/dependencies/OpenCV/include)
|
||||
find_path(VISAGE_OPENCV2_INCLUDE_DIR cv.h ${VISAGE_ROOT_DIR}/dependencies/OpenCV/include/opencv)
|
||||
if (VISAGE_INCLUDE_DIR AND VISAGE_XML_INCLUDE_DIR AND VISAGE_OPENCV_INCLUDE_DIR AND VISAGE_OPENCV2_INCLUDE_DIR)
|
||||
set(VISAGE_INCLUDE_DIRS
|
||||
"${VISAGE_INCLUDE_DIR};${VISAGE_XML_INCLUDE_DIR};${VISAGE_OPENCV_INCLUDE_DIR};${VISAGE_OPENCV2_INCLUDE_DIR}"
|
||||
CACHE INTERNAL "Visage include dirs")
|
||||
endif (VISAGE_INCLUDE_DIR AND VISAGE_XML_INCLUDE_DIR AND VISAGE_OPENCV_INCLUDE_DIR AND VISAGE_OPENCV2_INCLUDE_DIR)
|
||||
|
||||
find_library(VISAGE_CORE_LIBRARY vscore.lib ${VISAGE_ROOT_DIR}/lib)
|
||||
find_library(VISAGE_VISION_LIBRARY vsvision.lib ${VISAGE_ROOT_DIR}/lib)
|
||||
find_library(VISAGE_OPENCV_LIBRARY opencv_core243.lib ${VISAGE_ROOT_DIR}/dependencies/OpenCV/lib)
|
||||
if (VISAGE_CORE_LIBRARY AND VISAGE_VISION_LIBRARY AND VISAGE_OPENCV_LIBRARY)
|
||||
set(VISAGE_LIBRARIES "${VISAGE_CORE_LIBRARY};${VISAGE_VISION_LIBRARY};${VISAGE_OPENCV_LIBRARY}"
|
||||
CACHE INTERNAL "Visage libraries")
|
||||
endif (VISAGE_CORE_LIBRARY AND VISAGE_VISION_LIBRARY AND VISAGE_OPENCV_LIBRARY)
|
||||
|
||||
endif ()
|
||||
|
||||
if (VISAGE_INCLUDE_DIRS AND VISAGE_LIBRARIES)
|
||||
set(VISAGE_FOUND TRUE)
|
||||
endif (VISAGE_INCLUDE_DIRS AND VISAGE_LIBRARIES)
|
||||
|
||||
if (VISAGE_FOUND)
|
||||
if (NOT VISAGE_FIND_QUIETLY)
|
||||
message(STATUS "Found Visage: ${VISAGE_LIBRARIES}")
|
||||
endif (NOT VISAGE_FIND_QUIETLY)
|
||||
else (VISAGE_FOUND)
|
||||
if (VISAGE_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Could not find Visage")
|
||||
endif (VISAGE_FIND_REQUIRED)
|
||||
endif (VISAGE_FOUND)
|
||||
|
||||
# show the VISAGE_INCLUDE_DIRS and VISAGE_LIBRARIES variables only in the advanced view
|
||||
mark_as_advanced(VISAGE_INCLUDE_DIRS VISAGE_LIBRARIES)
|
||||
|
||||
endif (VISAGE_LIBRARIES AND VISAGE_INCLUDE_DIRS)
|
|
@ -1,23 +0,0 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
set(ROOT_DIR ..)
|
||||
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
|
||||
|
||||
set(TARGET_NAME data-server)
|
||||
|
||||
find_package(Qt5Network REQUIRED)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiProject.cmake)
|
||||
setup_hifi_project(${TARGET_NAME} TRUE)
|
||||
|
||||
qt5_use_modules(${TARGET_NAME} Network)
|
||||
|
||||
# link the shared hifi library
|
||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
# add hiredis as a library
|
||||
FILE(GLOB HIREDIS_SRCS external/hiredis/*.c)
|
||||
add_library(hiredis ${HIREDIS_SRCS})
|
||||
include_directories(external/hiredis/)
|
||||
target_link_libraries(${TARGET_NAME} hiredis)
|
20
data-server/external/hiredis/fmacros.h
vendored
20
data-server/external/hiredis/fmacros.h
vendored
|
@ -1,20 +0,0 @@
|
|||
#ifndef __HIREDIS_FMACRO_H
|
||||
#define __HIREDIS_FMACRO_H
|
||||
|
||||
#if !defined(_BSD_SOURCE)
|
||||
#define _BSD_SOURCE
|
||||
#endif
|
||||
|
||||
#if defined(__sun__)
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
#elif defined(__linux__)
|
||||
#define _XOPEN_SOURCE 600
|
||||
#else
|
||||
#define _XOPEN_SOURCE
|
||||
#endif
|
||||
|
||||
#if __APPLE__ && __MACH__
|
||||
#define _OSX
|
||||
#endif
|
||||
|
||||
#endif
|
1322
data-server/external/hiredis/hiredis.c
vendored
1322
data-server/external/hiredis/hiredis.c
vendored
File diff suppressed because it is too large
Load diff
213
data-server/external/hiredis/hiredis.h
vendored
213
data-server/external/hiredis/hiredis.h
vendored
|
@ -1,213 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __HIREDIS_H
|
||||
#define __HIREDIS_H
|
||||
#include <stdio.h> /* for size_t */
|
||||
#include <stdarg.h> /* for va_list */
|
||||
#include <sys/time.h> /* for struct timeval */
|
||||
|
||||
#define HIREDIS_MAJOR 0
|
||||
#define HIREDIS_MINOR 11
|
||||
#define HIREDIS_PATCH 0
|
||||
|
||||
#define REDIS_ERR -1
|
||||
#define REDIS_OK 0
|
||||
|
||||
/* When an error occurs, the err flag in a context is set to hold the type of
|
||||
* error that occured. REDIS_ERR_IO means there was an I/O error and you
|
||||
* should use the "errno" variable to find out what is wrong.
|
||||
* For other values, the "errstr" field will hold a description. */
|
||||
#define REDIS_ERR_IO 1 /* Error in read or write */
|
||||
#define REDIS_ERR_EOF 3 /* End of file */
|
||||
#define REDIS_ERR_PROTOCOL 4 /* Protocol error */
|
||||
#define REDIS_ERR_OOM 5 /* Out of memory */
|
||||
#define REDIS_ERR_OTHER 2 /* Everything else... */
|
||||
|
||||
/* Connection type can be blocking or non-blocking and is set in the
|
||||
* least significant bit of the flags field in redisContext. */
|
||||
#define REDIS_BLOCK 0x1
|
||||
|
||||
/* Connection may be disconnected before being free'd. The second bit
|
||||
* in the flags field is set when the context is connected. */
|
||||
#define REDIS_CONNECTED 0x2
|
||||
|
||||
/* The async API might try to disconnect cleanly and flush the output
|
||||
* buffer and read all subsequent replies before disconnecting.
|
||||
* This flag means no new commands can come in and the connection
|
||||
* should be terminated once all replies have been read. */
|
||||
#define REDIS_DISCONNECTING 0x4
|
||||
|
||||
/* Flag specific to the async API which means that the context should be clean
|
||||
* up as soon as possible. */
|
||||
#define REDIS_FREEING 0x8
|
||||
|
||||
/* Flag that is set when an async callback is executed. */
|
||||
#define REDIS_IN_CALLBACK 0x10
|
||||
|
||||
/* Flag that is set when the async context has one or more subscriptions. */
|
||||
#define REDIS_SUBSCRIBED 0x20
|
||||
|
||||
/* Flag that is set when monitor mode is active */
|
||||
#define REDIS_MONITORING 0x40
|
||||
|
||||
#define REDIS_REPLY_STRING 1
|
||||
#define REDIS_REPLY_ARRAY 2
|
||||
#define REDIS_REPLY_INTEGER 3
|
||||
#define REDIS_REPLY_NIL 4
|
||||
#define REDIS_REPLY_STATUS 5
|
||||
#define REDIS_REPLY_ERROR 6
|
||||
|
||||
#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */
|
||||
|
||||
#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This is the reply object returned by redisCommand() */
|
||||
typedef struct redisReply {
|
||||
int type; /* REDIS_REPLY_* */
|
||||
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
|
||||
int len; /* Length of string */
|
||||
char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */
|
||||
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
|
||||
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
|
||||
} redisReply;
|
||||
|
||||
typedef struct redisReadTask {
|
||||
int type;
|
||||
int elements; /* number of elements in multibulk container */
|
||||
int idx; /* index in parent (array) object */
|
||||
void *obj; /* holds user-generated value for a read task */
|
||||
struct redisReadTask *parent; /* parent task */
|
||||
void *privdata; /* user-settable arbitrary field */
|
||||
} redisReadTask;
|
||||
|
||||
typedef struct redisReplyObjectFunctions {
|
||||
void *(*createString)(const redisReadTask*, char*, size_t);
|
||||
void *(*createArray)(const redisReadTask*, int);
|
||||
void *(*createInteger)(const redisReadTask*, long long);
|
||||
void *(*createNil)(const redisReadTask*);
|
||||
void (*freeObject)(void*);
|
||||
} redisReplyObjectFunctions;
|
||||
|
||||
/* State for the protocol parser */
|
||||
typedef struct redisReader {
|
||||
int err; /* Error flags, 0 when there is no error */
|
||||
char errstr[128]; /* String representation of error when applicable */
|
||||
|
||||
char *buf; /* Read buffer */
|
||||
size_t pos; /* Buffer cursor */
|
||||
size_t len; /* Buffer length */
|
||||
size_t maxbuf; /* Max length of unused buffer */
|
||||
|
||||
redisReadTask rstack[9];
|
||||
int ridx; /* Index of current read task */
|
||||
void *reply; /* Temporary reply pointer */
|
||||
|
||||
redisReplyObjectFunctions *fn;
|
||||
void *privdata;
|
||||
} redisReader;
|
||||
|
||||
/* Public API for the protocol parser. */
|
||||
redisReader *redisReaderCreate(void);
|
||||
void redisReaderFree(redisReader *r);
|
||||
int redisReaderFeed(redisReader *r, const char *buf, size_t len);
|
||||
int redisReaderGetReply(redisReader *r, void **reply);
|
||||
|
||||
/* Backwards compatibility, can be removed on big version bump. */
|
||||
#define redisReplyReaderCreate redisReaderCreate
|
||||
#define redisReplyReaderFree redisReaderFree
|
||||
#define redisReplyReaderFeed redisReaderFeed
|
||||
#define redisReplyReaderGetReply redisReaderGetReply
|
||||
#define redisReplyReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
|
||||
#define redisReplyReaderGetObject(_r) (((redisReader*)(_r))->reply)
|
||||
#define redisReplyReaderGetError(_r) (((redisReader*)(_r))->errstr)
|
||||
|
||||
/* Function to free the reply objects hiredis returns by default. */
|
||||
void freeReplyObject(void *reply);
|
||||
|
||||
/* Functions to format a command according to the protocol. */
|
||||
int redisvFormatCommand(char **target, const char *format, va_list ap);
|
||||
int redisFormatCommand(char **target, const char *format, ...);
|
||||
int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
|
||||
|
||||
/* Context for a connection to Redis */
|
||||
typedef struct redisContext {
|
||||
int err; /* Error flags, 0 when there is no error */
|
||||
char errstr[128]; /* String representation of error when applicable */
|
||||
int fd;
|
||||
int flags;
|
||||
char *obuf; /* Write buffer */
|
||||
redisReader *reader; /* Protocol reader */
|
||||
} redisContext;
|
||||
|
||||
redisContext *redisConnect(const char *ip, int port);
|
||||
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
|
||||
redisContext *redisConnectNonBlock(const char *ip, int port);
|
||||
redisContext *redisConnectUnix(const char *path);
|
||||
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
|
||||
redisContext *redisConnectUnixNonBlock(const char *path);
|
||||
int redisSetTimeout(redisContext *c, const struct timeval tv);
|
||||
int redisEnableKeepAlive(redisContext *c);
|
||||
void redisFree(redisContext *c);
|
||||
int redisBufferRead(redisContext *c);
|
||||
int redisBufferWrite(redisContext *c, int *done);
|
||||
|
||||
/* In a blocking context, this function first checks if there are unconsumed
|
||||
* replies to return and returns one if so. Otherwise, it flushes the output
|
||||
* buffer to the socket and reads until it has a reply. In a non-blocking
|
||||
* context, it will return unconsumed replies until there are no more. */
|
||||
int redisGetReply(redisContext *c, void **reply);
|
||||
int redisGetReplyFromReader(redisContext *c, void **reply);
|
||||
|
||||
/* Write a command to the output buffer. Use these functions in blocking mode
|
||||
* to get a pipeline of commands. */
|
||||
int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
|
||||
int redisAppendCommand(redisContext *c, const char *format, ...);
|
||||
int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
|
||||
|
||||
/* Issue a command to Redis. In a blocking context, it is identical to calling
|
||||
* redisAppendCommand, followed by redisGetReply. The function will return
|
||||
* NULL if there was an error in performing the request, otherwise it will
|
||||
* return the reply. In a non-blocking context, it is identical to calling
|
||||
* only redisAppendCommand and will always return NULL. */
|
||||
void *redisvCommand(redisContext *c, const char *format, va_list ap);
|
||||
void *redisCommand(redisContext *c, const char *format, ...);
|
||||
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
339
data-server/external/hiredis/net.c
vendored
339
data-server/external/hiredis/net.c
vendored
|
@ -1,339 +0,0 @@
|
|||
/* Extracted from anet.c to work properly with Hiredis error reporting.
|
||||
*
|
||||
* Copyright (c) 2006-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <poll.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "net.h"
|
||||
#include "sds.h"
|
||||
|
||||
/* Defined in hiredis.c */
|
||||
void __redisSetError(redisContext *c, int type, const char *str);
|
||||
|
||||
static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
|
||||
char buf[128] = { 0 };
|
||||
size_t len = 0;
|
||||
|
||||
if (prefix != NULL)
|
||||
len = snprintf(buf,sizeof(buf),"%s: ",prefix);
|
||||
strerror_r(errno,buf+len,sizeof(buf)-len);
|
||||
__redisSetError(c,type,buf);
|
||||
}
|
||||
|
||||
static int redisSetReuseAddr(redisContext *c, int fd) {
|
||||
int on = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static int redisCreateSocket(redisContext *c, int type) {
|
||||
int s;
|
||||
if ((s = socket(type, SOCK_STREAM, 0)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
if (type == AF_INET) {
|
||||
if (redisSetReuseAddr(c,s) == REDIS_ERR) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static int redisSetBlocking(redisContext *c, int fd, int blocking) {
|
||||
int flags;
|
||||
|
||||
/* Set the socket nonblocking.
|
||||
* Note that fcntl(2) for F_GETFL and F_SETFL can't be
|
||||
* interrupted by a signal. */
|
||||
if ((flags = fcntl(fd, F_GETFL)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (blocking)
|
||||
flags &= ~O_NONBLOCK;
|
||||
else
|
||||
flags |= O_NONBLOCK;
|
||||
|
||||
if (fcntl(fd, F_SETFL, flags) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisKeepAlive(redisContext *c, int interval) {
|
||||
int val = 1;
|
||||
int fd = c->fd;
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
#ifdef _OSX
|
||||
val = interval;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
#else
|
||||
val = interval;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
val = interval/3;
|
||||
if (val == 0) val = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
val = 3;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static int redisSetTcpNoDelay(redisContext *c, int fd) {
|
||||
int yes = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
#define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
|
||||
|
||||
static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *timeout) {
|
||||
struct pollfd wfd[1];
|
||||
long msec;
|
||||
|
||||
msec = -1;
|
||||
wfd[0].fd = fd;
|
||||
wfd[0].events = POLLOUT;
|
||||
|
||||
/* Only use timeout when not NULL. */
|
||||
if (timeout != NULL) {
|
||||
if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
|
||||
__redisSetErrorFromErrno(c, REDIS_ERR_IO, NULL);
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000);
|
||||
|
||||
if (msec < 0 || msec > INT_MAX) {
|
||||
msec = INT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
if (errno == EINPROGRESS) {
|
||||
int res;
|
||||
|
||||
if ((res = poll(wfd, 1, msec)) == -1) {
|
||||
__redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
} else if (res == 0) {
|
||||
errno = ETIMEDOUT;
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (redisCheckSocketError(c, fd) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
int redisCheckSocketError(redisContext *c, int fd) {
|
||||
int err = 0;
|
||||
socklen_t errlen = sizeof(err);
|
||||
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
errno = err;
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
|
||||
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout) {
|
||||
int s, rv;
|
||||
char _port[6]; /* strlen("65535"); */
|
||||
struct addrinfo hints, *servinfo, *p;
|
||||
int blocking = (c->flags & REDIS_BLOCK);
|
||||
|
||||
snprintf(_port, 6, "%d", port);
|
||||
memset(&hints,0,sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
/* Try with IPv6 if no IPv4 address was found. We do it in this order since
|
||||
* in a Redis client you can't afford to test if you have IPv6 connectivity
|
||||
* as this would add latency to every connect. Otherwise a more sensible
|
||||
* route could be: Use IPv6 if both addresses are available and there is IPv6
|
||||
* connectivity. */
|
||||
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
|
||||
hints.ai_family = AF_INET6;
|
||||
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
for (p = servinfo; p != NULL; p = p->ai_next) {
|
||||
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
|
||||
continue;
|
||||
|
||||
if (redisSetBlocking(c,s,0) != REDIS_OK)
|
||||
goto error;
|
||||
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
|
||||
if (errno == EHOSTUNREACH) {
|
||||
close(s);
|
||||
continue;
|
||||
} else if (errno == EINPROGRESS && !blocking) {
|
||||
/* This is ok. */
|
||||
} else {
|
||||
if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
|
||||
goto error;
|
||||
if (redisSetTcpNoDelay(c,s) != REDIS_OK)
|
||||
goto error;
|
||||
|
||||
c->fd = s;
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
rv = REDIS_OK;
|
||||
goto end;
|
||||
}
|
||||
if (p == NULL) {
|
||||
char buf[128];
|
||||
snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno));
|
||||
__redisSetError(c,REDIS_ERR_OTHER,buf);
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
rv = REDIS_ERR;
|
||||
end:
|
||||
freeaddrinfo(servinfo);
|
||||
return rv; // Need to return REDIS_OK if alright
|
||||
}
|
||||
|
||||
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
|
||||
int s;
|
||||
int blocking = (c->flags & REDIS_BLOCK);
|
||||
struct sockaddr_un sa;
|
||||
|
||||
if ((s = redisCreateSocket(c,AF_LOCAL)) < 0)
|
||||
return REDIS_ERR;
|
||||
if (redisSetBlocking(c,s,0) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
sa.sun_family = AF_LOCAL;
|
||||
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
|
||||
if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
|
||||
if (errno == EINPROGRESS && !blocking) {
|
||||
/* This is ok. */
|
||||
} else {
|
||||
if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset socket to be blocking after connect(2). */
|
||||
if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
c->fd = s;
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
return REDIS_OK;
|
||||
}
|
48
data-server/external/hiredis/net.h
vendored
48
data-server/external/hiredis/net.h
vendored
|
@ -1,48 +0,0 @@
|
|||
/* Extracted from anet.c to work properly with Hiredis error reporting.
|
||||
*
|
||||
* Copyright (c) 2006-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __NET_H
|
||||
#define __NET_H
|
||||
|
||||
#include "hiredis.h"
|
||||
|
||||
#if defined(__sun)
|
||||
#define AF_LOCAL AF_UNIX
|
||||
#endif
|
||||
|
||||
int redisCheckSocketError(redisContext *c, int fd);
|
||||
int redisContextSetTimeout(redisContext *c, const struct timeval tv);
|
||||
int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);
|
||||
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);
|
||||
int redisKeepAlive(redisContext *c, int interval);
|
||||
|
||||
#endif
|
606
data-server/external/hiredis/sds.c
vendored
606
data-server/external/hiredis/sds.c
vendored
|
@ -1,606 +0,0 @@
|
|||
/* SDSLib, A C dynamic strings library
|
||||
*
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "sds.h"
|
||||
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
static void sdsOomAbort(void) {
|
||||
fprintf(stderr,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n");
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
|
||||
sds sdsnewlen(const void *init, size_t initlen) {
|
||||
struct sdshdr *sh;
|
||||
|
||||
sh = malloc(sizeof(struct sdshdr)+initlen+1);
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
if (sh == NULL) sdsOomAbort();
|
||||
#else
|
||||
if (sh == NULL) return NULL;
|
||||
#endif
|
||||
sh->len = initlen;
|
||||
sh->free = 0;
|
||||
if (initlen) {
|
||||
if (init) memcpy(sh->buf, init, initlen);
|
||||
else memset(sh->buf,0,initlen);
|
||||
}
|
||||
sh->buf[initlen] = '\0';
|
||||
return (char*)sh->buf;
|
||||
}
|
||||
|
||||
sds sdsempty(void) {
|
||||
return sdsnewlen("",0);
|
||||
}
|
||||
|
||||
sds sdsnew(const char *init) {
|
||||
size_t initlen = (init == NULL) ? 0 : strlen(init);
|
||||
return sdsnewlen(init, initlen);
|
||||
}
|
||||
|
||||
sds sdsdup(const sds s) {
|
||||
return sdsnewlen(s, sdslen(s));
|
||||
}
|
||||
|
||||
void sdsfree(sds s) {
|
||||
if (s == NULL) return;
|
||||
free(s-sizeof(struct sdshdr));
|
||||
}
|
||||
|
||||
void sdsupdatelen(sds s) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
int reallen = strlen(s);
|
||||
sh->free += (sh->len-reallen);
|
||||
sh->len = reallen;
|
||||
}
|
||||
|
||||
static sds sdsMakeRoomFor(sds s, size_t addlen) {
|
||||
struct sdshdr *sh, *newsh;
|
||||
size_t free = sdsavail(s);
|
||||
size_t len, newlen;
|
||||
|
||||
if (free >= addlen) return s;
|
||||
len = sdslen(s);
|
||||
sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
newlen = (len+addlen)*2;
|
||||
newsh = realloc(sh, sizeof(struct sdshdr)+newlen+1);
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
if (newsh == NULL) sdsOomAbort();
|
||||
#else
|
||||
if (newsh == NULL) return NULL;
|
||||
#endif
|
||||
|
||||
newsh->free = newlen - len;
|
||||
return newsh->buf;
|
||||
}
|
||||
|
||||
/* Grow the sds to have the specified length. Bytes that were not part of
|
||||
* the original length of the sds will be set to zero. */
|
||||
sds sdsgrowzero(sds s, size_t len) {
|
||||
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
|
||||
size_t totlen, curlen = sh->len;
|
||||
|
||||
if (len <= curlen) return s;
|
||||
s = sdsMakeRoomFor(s,len-curlen);
|
||||
if (s == NULL) return NULL;
|
||||
|
||||
/* Make sure added region doesn't contain garbage */
|
||||
sh = (void*)(s-(sizeof(struct sdshdr)));
|
||||
memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
|
||||
totlen = sh->len+sh->free;
|
||||
sh->len = len;
|
||||
sh->free = totlen-sh->len;
|
||||
return s;
|
||||
}
|
||||
|
||||
sds sdscatlen(sds s, const void *t, size_t len) {
|
||||
struct sdshdr *sh;
|
||||
size_t curlen = sdslen(s);
|
||||
|
||||
s = sdsMakeRoomFor(s,len);
|
||||
if (s == NULL) return NULL;
|
||||
sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
memcpy(s+curlen, t, len);
|
||||
sh->len = curlen+len;
|
||||
sh->free = sh->free-len;
|
||||
s[curlen+len] = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
sds sdscat(sds s, const char *t) {
|
||||
return sdscatlen(s, t, strlen(t));
|
||||
}
|
||||
|
||||
sds sdscpylen(sds s, char *t, size_t len) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
size_t totlen = sh->free+sh->len;
|
||||
|
||||
if (totlen < len) {
|
||||
s = sdsMakeRoomFor(s,len-sh->len);
|
||||
if (s == NULL) return NULL;
|
||||
sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
totlen = sh->free+sh->len;
|
||||
}
|
||||
memcpy(s, t, len);
|
||||
s[len] = '\0';
|
||||
sh->len = len;
|
||||
sh->free = totlen-len;
|
||||
return s;
|
||||
}
|
||||
|
||||
sds sdscpy(sds s, char *t) {
|
||||
return sdscpylen(s, t, strlen(t));
|
||||
}
|
||||
|
||||
sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
|
||||
va_list cpy;
|
||||
char *buf, *t;
|
||||
size_t buflen = 16;
|
||||
|
||||
while(1) {
|
||||
buf = malloc(buflen);
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
if (buf == NULL) sdsOomAbort();
|
||||
#else
|
||||
if (buf == NULL) return NULL;
|
||||
#endif
|
||||
buf[buflen-2] = '\0';
|
||||
va_copy(cpy,ap);
|
||||
vsnprintf(buf, buflen, fmt, cpy);
|
||||
va_end(cpy);
|
||||
if (buf[buflen-2] != '\0') {
|
||||
free(buf);
|
||||
buflen *= 2;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
t = sdscat(s, buf);
|
||||
free(buf);
|
||||
return t;
|
||||
}
|
||||
|
||||
sds sdscatprintf(sds s, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
char *t;
|
||||
va_start(ap, fmt);
|
||||
t = sdscatvprintf(s,fmt,ap);
|
||||
va_end(ap);
|
||||
return t;
|
||||
}
|
||||
|
||||
sds sdstrim(sds s, const char *cset) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
char *start, *end, *sp, *ep;
|
||||
size_t len;
|
||||
|
||||
sp = start = s;
|
||||
ep = end = s+sdslen(s)-1;
|
||||
while(sp <= end && strchr(cset, *sp)) sp++;
|
||||
while(ep > start && strchr(cset, *ep)) ep--;
|
||||
len = (sp > ep) ? 0 : ((ep-sp)+1);
|
||||
if (sh->buf != sp) memmove(sh->buf, sp, len);
|
||||
sh->buf[len] = '\0';
|
||||
sh->free = sh->free+(sh->len-len);
|
||||
sh->len = len;
|
||||
return s;
|
||||
}
|
||||
|
||||
sds sdsrange(sds s, int start, int end) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
size_t newlen, len = sdslen(s);
|
||||
|
||||
if (len == 0) return s;
|
||||
if (start < 0) {
|
||||
start = len+start;
|
||||
if (start < 0) start = 0;
|
||||
}
|
||||
if (end < 0) {
|
||||
end = len+end;
|
||||
if (end < 0) end = 0;
|
||||
}
|
||||
newlen = (start > end) ? 0 : (end-start)+1;
|
||||
if (newlen != 0) {
|
||||
if (start >= (signed)len) {
|
||||
newlen = 0;
|
||||
} else if (end >= (signed)len) {
|
||||
end = len-1;
|
||||
newlen = (start > end) ? 0 : (end-start)+1;
|
||||
}
|
||||
} else {
|
||||
start = 0;
|
||||
}
|
||||
if (start && newlen) memmove(sh->buf, sh->buf+start, newlen);
|
||||
sh->buf[newlen] = 0;
|
||||
sh->free = sh->free+(sh->len-newlen);
|
||||
sh->len = newlen;
|
||||
return s;
|
||||
}
|
||||
|
||||
void sdstolower(sds s) {
|
||||
int len = sdslen(s), j;
|
||||
|
||||
for (j = 0; j < len; j++) s[j] = tolower(s[j]);
|
||||
}
|
||||
|
||||
void sdstoupper(sds s) {
|
||||
int len = sdslen(s), j;
|
||||
|
||||
for (j = 0; j < len; j++) s[j] = toupper(s[j]);
|
||||
}
|
||||
|
||||
int sdscmp(sds s1, sds s2) {
|
||||
size_t l1, l2, minlen;
|
||||
int cmp;
|
||||
|
||||
l1 = sdslen(s1);
|
||||
l2 = sdslen(s2);
|
||||
minlen = (l1 < l2) ? l1 : l2;
|
||||
cmp = memcmp(s1,s2,minlen);
|
||||
if (cmp == 0) return l1-l2;
|
||||
return cmp;
|
||||
}
|
||||
|
||||
/* Split 's' with separator in 'sep'. An array
|
||||
* of sds strings is returned. *count will be set
|
||||
* by reference to the number of tokens returned.
|
||||
*
|
||||
* On out of memory, zero length string, zero length
|
||||
* separator, NULL is returned.
|
||||
*
|
||||
* Note that 'sep' is able to split a string using
|
||||
* a multi-character separator. For example
|
||||
* sdssplit("foo_-_bar","_-_"); will return two
|
||||
* elements "foo" and "bar".
|
||||
*
|
||||
* This version of the function is binary-safe but
|
||||
* requires length arguments. sdssplit() is just the
|
||||
* same function but for zero-terminated strings.
|
||||
*/
|
||||
sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
|
||||
int elements = 0, slots = 5, start = 0, j;
|
||||
|
||||
sds *tokens = malloc(sizeof(sds)*slots);
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
if (tokens == NULL) sdsOomAbort();
|
||||
#endif
|
||||
if (seplen < 1 || len < 0 || tokens == NULL) return NULL;
|
||||
if (len == 0) {
|
||||
*count = 0;
|
||||
return tokens;
|
||||
}
|
||||
for (j = 0; j < (len-(seplen-1)); j++) {
|
||||
/* make sure there is room for the next element and the final one */
|
||||
if (slots < elements+2) {
|
||||
sds *newtokens;
|
||||
|
||||
slots *= 2;
|
||||
newtokens = realloc(tokens,sizeof(sds)*slots);
|
||||
if (newtokens == NULL) {
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
sdsOomAbort();
|
||||
#else
|
||||
goto cleanup;
|
||||
#endif
|
||||
}
|
||||
tokens = newtokens;
|
||||
}
|
||||
/* search the separator */
|
||||
if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
|
||||
tokens[elements] = sdsnewlen(s+start,j-start);
|
||||
if (tokens[elements] == NULL) {
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
sdsOomAbort();
|
||||
#else
|
||||
goto cleanup;
|
||||
#endif
|
||||
}
|
||||
elements++;
|
||||
start = j+seplen;
|
||||
j = j+seplen-1; /* skip the separator */
|
||||
}
|
||||
}
|
||||
/* Add the final element. We are sure there is room in the tokens array. */
|
||||
tokens[elements] = sdsnewlen(s+start,len-start);
|
||||
if (tokens[elements] == NULL) {
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
sdsOomAbort();
|
||||
#else
|
||||
goto cleanup;
|
||||
#endif
|
||||
}
|
||||
elements++;
|
||||
*count = elements;
|
||||
return tokens;
|
||||
|
||||
#ifndef SDS_ABORT_ON_OOM
|
||||
cleanup:
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < elements; i++) sdsfree(tokens[i]);
|
||||
free(tokens);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void sdsfreesplitres(sds *tokens, int count) {
|
||||
if (!tokens) return;
|
||||
while(count--)
|
||||
sdsfree(tokens[count]);
|
||||
free(tokens);
|
||||
}
|
||||
|
||||
sds sdsfromlonglong(long long value) {
|
||||
char buf[32], *p;
|
||||
unsigned long long v;
|
||||
|
||||
v = (value < 0) ? -value : value;
|
||||
p = buf+31; /* point to the last character */
|
||||
do {
|
||||
*p-- = '0'+(v%10);
|
||||
v /= 10;
|
||||
} while(v);
|
||||
if (value < 0) *p-- = '-';
|
||||
p++;
|
||||
return sdsnewlen(p,32-(p-buf));
|
||||
}
|
||||
|
||||
sds sdscatrepr(sds s, char *p, size_t len) {
|
||||
s = sdscatlen(s,"\"",1);
|
||||
if (s == NULL) return NULL;
|
||||
|
||||
while(len--) {
|
||||
switch(*p) {
|
||||
case '\\':
|
||||
case '"':
|
||||
s = sdscatprintf(s,"\\%c",*p);
|
||||
break;
|
||||
case '\n': s = sdscatlen(s,"\\n",2); break;
|
||||
case '\r': s = sdscatlen(s,"\\r",2); break;
|
||||
case '\t': s = sdscatlen(s,"\\t",2); break;
|
||||
case '\a': s = sdscatlen(s,"\\a",2); break;
|
||||
case '\b': s = sdscatlen(s,"\\b",2); break;
|
||||
default:
|
||||
if (isprint(*p))
|
||||
s = sdscatprintf(s,"%c",*p);
|
||||
else
|
||||
s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
if (s == NULL) return NULL;
|
||||
}
|
||||
return sdscatlen(s,"\"",1);
|
||||
}
|
||||
|
||||
/* Split a line into arguments, where every argument can be in the
|
||||
* following programming-language REPL-alike form:
|
||||
*
|
||||
* foo bar "newline are supported\n" and "\xff\x00otherstuff"
|
||||
*
|
||||
* The number of arguments is stored into *argc, and an array
|
||||
* of sds is returned. The caller should sdsfree() all the returned
|
||||
* strings and finally free() the array itself.
|
||||
*
|
||||
* Note that sdscatrepr() is able to convert back a string into
|
||||
* a quoted string in the same format sdssplitargs() is able to parse.
|
||||
*/
|
||||
sds *sdssplitargs(char *line, int *argc) {
|
||||
char *p = line;
|
||||
char *current = NULL;
|
||||
char **vector = NULL, **_vector = NULL;
|
||||
|
||||
*argc = 0;
|
||||
while(1) {
|
||||
/* skip blanks */
|
||||
while(*p && isspace(*p)) p++;
|
||||
if (*p) {
|
||||
/* get a token */
|
||||
int inq=0; /* set to 1 if we are in "quotes" */
|
||||
int done=0;
|
||||
|
||||
if (current == NULL) {
|
||||
current = sdsempty();
|
||||
if (current == NULL) goto err;
|
||||
}
|
||||
|
||||
while(!done) {
|
||||
if (inq) {
|
||||
if (*p == '\\' && *(p+1)) {
|
||||
char c;
|
||||
|
||||
p++;
|
||||
switch(*p) {
|
||||
case 'n': c = '\n'; break;
|
||||
case 'r': c = '\r'; break;
|
||||
case 't': c = '\t'; break;
|
||||
case 'b': c = '\b'; break;
|
||||
case 'a': c = '\a'; break;
|
||||
default: c = *p; break;
|
||||
}
|
||||
current = sdscatlen(current,&c,1);
|
||||
} else if (*p == '"') {
|
||||
/* closing quote must be followed by a space */
|
||||
if (*(p+1) && !isspace(*(p+1))) goto err;
|
||||
done=1;
|
||||
} else if (!*p) {
|
||||
/* unterminated quotes */
|
||||
goto err;
|
||||
} else {
|
||||
current = sdscatlen(current,p,1);
|
||||
}
|
||||
} else {
|
||||
switch(*p) {
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\0':
|
||||
done=1;
|
||||
break;
|
||||
case '"':
|
||||
inq=1;
|
||||
break;
|
||||
default:
|
||||
current = sdscatlen(current,p,1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*p) p++;
|
||||
if (current == NULL) goto err;
|
||||
}
|
||||
/* add the token to the vector */
|
||||
_vector = realloc(vector,((*argc)+1)*sizeof(char*));
|
||||
if (_vector == NULL) goto err;
|
||||
|
||||
vector = _vector;
|
||||
vector[*argc] = current;
|
||||
(*argc)++;
|
||||
current = NULL;
|
||||
} else {
|
||||
return vector;
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
while((*argc)--)
|
||||
sdsfree(vector[*argc]);
|
||||
if (vector != NULL) free(vector);
|
||||
if (current != NULL) sdsfree(current);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef SDS_TEST_MAIN
|
||||
#include <stdio.h>
|
||||
|
||||
int __failed_tests = 0;
|
||||
int __test_num = 0;
|
||||
#define test_cond(descr,_c) do { \
|
||||
__test_num++; printf("%d - %s: ", __test_num, descr); \
|
||||
if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \
|
||||
} while(0);
|
||||
#define test_report() do { \
|
||||
printf("%d tests, %d passed, %d failed\n", __test_num, \
|
||||
__test_num-__failed_tests, __failed_tests); \
|
||||
if (__failed_tests) { \
|
||||
printf("=== WARNING === We have failed tests here...\n"); \
|
||||
} \
|
||||
} while(0);
|
||||
|
||||
int main(void) {
|
||||
{
|
||||
sds x = sdsnew("foo"), y;
|
||||
|
||||
test_cond("Create a string and obtain the length",
|
||||
sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0)
|
||||
|
||||
sdsfree(x);
|
||||
x = sdsnewlen("foo",2);
|
||||
test_cond("Create a string with specified length",
|
||||
sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0)
|
||||
|
||||
x = sdscat(x,"bar");
|
||||
test_cond("Strings concatenation",
|
||||
sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0);
|
||||
|
||||
x = sdscpy(x,"a");
|
||||
test_cond("sdscpy() against an originally longer string",
|
||||
sdslen(x) == 1 && memcmp(x,"a\0",2) == 0)
|
||||
|
||||
x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
|
||||
test_cond("sdscpy() against an originally shorter string",
|
||||
sdslen(x) == 33 &&
|
||||
memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)
|
||||
|
||||
sdsfree(x);
|
||||
x = sdscatprintf(sdsempty(),"%d",123);
|
||||
test_cond("sdscatprintf() seems working in the base case",
|
||||
sdslen(x) == 3 && memcmp(x,"123\0",4) ==0)
|
||||
|
||||
sdsfree(x);
|
||||
x = sdstrim(sdsnew("xxciaoyyy"),"xy");
|
||||
test_cond("sdstrim() correctly trims characters",
|
||||
sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
|
||||
|
||||
y = sdsrange(sdsdup(x),1,1);
|
||||
test_cond("sdsrange(...,1,1)",
|
||||
sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsrange(sdsdup(x),1,-1);
|
||||
test_cond("sdsrange(...,1,-1)",
|
||||
sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsrange(sdsdup(x),-2,-1);
|
||||
test_cond("sdsrange(...,-2,-1)",
|
||||
sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsrange(sdsdup(x),2,1);
|
||||
test_cond("sdsrange(...,2,1)",
|
||||
sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsrange(sdsdup(x),1,100);
|
||||
test_cond("sdsrange(...,1,100)",
|
||||
sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsrange(sdsdup(x),100,100);
|
||||
test_cond("sdsrange(...,100,100)",
|
||||
sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
sdsfree(x);
|
||||
x = sdsnew("foo");
|
||||
y = sdsnew("foa");
|
||||
test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0)
|
||||
|
||||
sdsfree(y);
|
||||
sdsfree(x);
|
||||
x = sdsnew("bar");
|
||||
y = sdsnew("bar");
|
||||
test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
sdsfree(x);
|
||||
x = sdsnew("aar");
|
||||
y = sdsnew("bar");
|
||||
test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
|
||||
}
|
||||
test_report()
|
||||
}
|
||||
#endif
|
88
data-server/external/hiredis/sds.h
vendored
88
data-server/external/hiredis/sds.h
vendored
|
@ -1,88 +0,0 @@
|
|||
/* SDSLib, A C dynamic strings library
|
||||
*
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __SDS_H
|
||||
#define __SDS_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
typedef char *sds;
|
||||
|
||||
struct sdshdr {
|
||||
int len;
|
||||
int free;
|
||||
char buf[];
|
||||
};
|
||||
|
||||
static inline size_t sdslen(const sds s) {
|
||||
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
|
||||
return sh->len;
|
||||
}
|
||||
|
||||
static inline size_t sdsavail(const sds s) {
|
||||
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
|
||||
return sh->free;
|
||||
}
|
||||
|
||||
sds sdsnewlen(const void *init, size_t initlen);
|
||||
sds sdsnew(const char *init);
|
||||
sds sdsempty(void);
|
||||
size_t sdslen(const sds s);
|
||||
sds sdsdup(const sds s);
|
||||
void sdsfree(sds s);
|
||||
size_t sdsavail(sds s);
|
||||
sds sdsgrowzero(sds s, size_t len);
|
||||
sds sdscatlen(sds s, const void *t, size_t len);
|
||||
sds sdscat(sds s, const char *t);
|
||||
sds sdscpylen(sds s, char *t, size_t len);
|
||||
sds sdscpy(sds s, char *t);
|
||||
|
||||
sds sdscatvprintf(sds s, const char *fmt, va_list ap);
|
||||
#ifdef __GNUC__
|
||||
sds sdscatprintf(sds s, const char *fmt, ...)
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
#else
|
||||
sds sdscatprintf(sds s, const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
sds sdstrim(sds s, const char *cset);
|
||||
sds sdsrange(sds s, int start, int end);
|
||||
void sdsupdatelen(sds s);
|
||||
int sdscmp(sds s1, sds s2);
|
||||
sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count);
|
||||
void sdsfreesplitres(sds *tokens, int count);
|
||||
void sdstolower(sds s);
|
||||
void sdstoupper(sds s);
|
||||
sds sdsfromlonglong(long long value);
|
||||
sds sdscatrepr(sds s, char *p, size_t len);
|
||||
sds *sdssplitargs(char *line, int *argc);
|
||||
|
||||
#endif
|
|
@ -1,31 +0,0 @@
|
|||
//
|
||||
// DataServer.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 1/20/2014.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__DataServer__
|
||||
#define __hifi__DataServer__
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QUuid>
|
||||
#include <QtNetwork/QUdpSocket>
|
||||
|
||||
#include <hiredis.h>
|
||||
|
||||
class DataServer : public QCoreApplication {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DataServer(int argc, char* argv[]);
|
||||
~DataServer();
|
||||
private:
|
||||
QUdpSocket _socket;
|
||||
redisContext* _redis;
|
||||
QUuid _uuid;
|
||||
private slots:
|
||||
void readPendingDatagrams();
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__DataServer__) */
|
|
@ -1,28 +0,0 @@
|
|||
//
|
||||
// main.cpp
|
||||
// data-server
|
||||
//
|
||||
// Created by Stephen Birarda on 1/1/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include <Logging.h>
|
||||
|
||||
#include "DataServer.h"
|
||||
|
||||
const char DATA_SERVER_LOGGING_TARGET_NAME[] = "data-server";
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
|
||||
qInstallMessageHandler(Logging::verboseMessageHandler);
|
||||
|
||||
Logging::setTargetName(DATA_SERVER_LOGGING_TARGET_NAME);
|
||||
|
||||
DataServer dataServer(argc, argv);
|
||||
|
||||
return dataServer.exec();
|
||||
}
|
|
@ -12,10 +12,11 @@
|
|||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonArray>
|
||||
#include <QtCore/QProcess>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <HTTPConnection.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
|
@ -25,12 +26,6 @@
|
|||
|
||||
#include "DomainServer.h"
|
||||
|
||||
const int RESTART_HOLD_TIME_MSECS = 5 * 1000;
|
||||
|
||||
const char* VOXEL_SERVER_CONFIG = "voxelServerConfig";
|
||||
const char* PARTICLE_SERVER_CONFIG = "particleServerConfig";
|
||||
const char* METAVOXEL_SERVER_CONFIG = "metavoxelServerConfig";
|
||||
|
||||
const quint16 DOMAIN_SERVER_HTTP_PORT = 8080;
|
||||
|
||||
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||
|
@ -38,39 +33,131 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
_HTTPManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
|
||||
_staticAssignmentHash(),
|
||||
_assignmentQueue(),
|
||||
_hasCompletedRestartHold(false)
|
||||
_nodeAuthenticationURL(),
|
||||
_redeemedTokenResponses()
|
||||
{
|
||||
const char CUSTOM_PORT_OPTION[] = "-p";
|
||||
const char* customPortString = getCmdOption(argc, (const char**) argv, CUSTOM_PORT_OPTION);
|
||||
unsigned short domainServerPort = customPortString ? atoi(customPortString) : DEFAULT_DOMAIN_SERVER_PORT;
|
||||
setOrganizationName("High Fidelity");
|
||||
setOrganizationDomain("highfidelity.io");
|
||||
setApplicationName("domain-server");
|
||||
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||
|
||||
QStringList argumentList = arguments();
|
||||
_argumentList = arguments();
|
||||
int argumentIndex = 0;
|
||||
|
||||
// check if this domain server should use no authentication or a custom hostname for authentication
|
||||
const QString DEFAULT_AUTH_OPTION = "--defaultAuth";
|
||||
const QString CUSTOM_AUTH_OPTION = "--customAuth";
|
||||
if ((argumentIndex = _argumentList.indexOf(DEFAULT_AUTH_OPTION) != -1)) {
|
||||
_nodeAuthenticationURL = QUrl(DEFAULT_NODE_AUTH_URL);
|
||||
} else if ((argumentIndex = _argumentList.indexOf(CUSTOM_AUTH_OPTION)) != -1) {
|
||||
_nodeAuthenticationURL = QUrl(_argumentList.value(argumentIndex + 1));
|
||||
}
|
||||
|
||||
if (!_nodeAuthenticationURL.isEmpty()) {
|
||||
const QString DATA_SERVER_USERNAME_ENV = "HIFI_DS_USERNAME";
|
||||
const QString DATA_SERVER_PASSWORD_ENV = "HIFI_DS_PASSWORD";
|
||||
|
||||
// this node will be using an authentication server, let's make sure we have a username/password
|
||||
QProcessEnvironment sysEnvironment = QProcessEnvironment::systemEnvironment();
|
||||
|
||||
QString username = sysEnvironment.value(DATA_SERVER_USERNAME_ENV);
|
||||
QString password = sysEnvironment.value(DATA_SERVER_PASSWORD_ENV);
|
||||
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
accountManager.setAuthURL(_nodeAuthenticationURL);
|
||||
|
||||
if (!username.isEmpty() && !password.isEmpty()) {
|
||||
|
||||
connect(&accountManager, &AccountManager::loginComplete, this, &DomainServer::requestCreationFromDataServer);
|
||||
|
||||
// ask the account manager to log us in from the env variables
|
||||
accountManager.requestAccessToken(username, password);
|
||||
} else {
|
||||
qDebug() << "Authentication was requested against" << qPrintable(_nodeAuthenticationURL.toString())
|
||||
<< "but both or one of" << qPrintable(DATA_SERVER_USERNAME_ENV)
|
||||
<< "/" << qPrintable(DATA_SERVER_PASSWORD_ENV) << "are not set. Qutting!";
|
||||
|
||||
// bail out
|
||||
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
// auth is not requested for domain-server, setup NodeList and assignments now
|
||||
setupNodeListAndAssignments();
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::requestCreationFromDataServer() {
|
||||
// this slot is fired when we get a valid access token from the data-server
|
||||
// now let's ask it to set us up with a UUID
|
||||
JSONCallbackParameters callbackParams;
|
||||
callbackParams.jsonCallbackReceiver = this;
|
||||
callbackParams.jsonCallbackMethod = "processCreateResponseFromDataServer";
|
||||
|
||||
AccountManager::getInstance().authenticatedRequest("/api/v1/domains/create",
|
||||
QNetworkAccessManager::PostOperation,
|
||||
callbackParams);
|
||||
}
|
||||
|
||||
void DomainServer::processCreateResponseFromDataServer(const QJsonObject& jsonObject) {
|
||||
if (jsonObject["status"].toString() == "success") {
|
||||
// pull out the UUID the data-server is telling us to use, and complete our setup with it
|
||||
QUuid newSessionUUID = QUuid(jsonObject["data"].toObject()["uuid"].toString());
|
||||
setupNodeListAndAssignments(newSessionUUID);
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::processTokenRedeemResponse(const QJsonObject& jsonObject) {
|
||||
// pull out the registration token this is associated with
|
||||
QString registrationToken = jsonObject["data"].toObject()["registration_token"].toString();
|
||||
|
||||
// if we have a registration token add it to our hash of redeemed token responses
|
||||
if (!registrationToken.isEmpty()) {
|
||||
qDebug() << "Redeemed registration token" << registrationToken;
|
||||
_redeemedTokenResponses.insert(registrationToken, jsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
|
||||
|
||||
int argumentIndex = 0;
|
||||
|
||||
const QString CUSTOM_PORT_OPTION = "-p";
|
||||
unsigned short domainServerPort = DEFAULT_DOMAIN_SERVER_PORT;
|
||||
|
||||
if ((argumentIndex = _argumentList.indexOf(CUSTOM_PORT_OPTION)) != -1) {
|
||||
domainServerPort = _argumentList.value(argumentIndex + 1).toUShort();
|
||||
}
|
||||
|
||||
QSet<Assignment::Type> parsedTypes(QSet<Assignment::Type>() << Assignment::AgentType);
|
||||
parseCommandLineTypeConfigs(argumentList, parsedTypes);
|
||||
parseCommandLineTypeConfigs(_argumentList, parsedTypes);
|
||||
|
||||
const QString CONFIG_FILE_OPTION = "--configFile";
|
||||
if ((argumentIndex = argumentList.indexOf(CONFIG_FILE_OPTION)) != -1) {
|
||||
QString configFilePath = argumentList.value(argumentIndex + 1);
|
||||
if ((argumentIndex = _argumentList.indexOf(CONFIG_FILE_OPTION)) != -1) {
|
||||
QString configFilePath = _argumentList.value(argumentIndex + 1);
|
||||
readConfigFile(configFilePath, parsedTypes);
|
||||
}
|
||||
|
||||
populateDefaultStaticAssignmentsExcludingTypes(parsedTypes);
|
||||
|
||||
|
||||
NodeList* nodeList = NodeList::createInstance(NodeType::DomainServer, domainServerPort);
|
||||
|
||||
// create a random UUID for this session for the domain-server
|
||||
nodeList->setSessionUUID(sessionUUID);
|
||||
|
||||
connect(nodeList, &NodeList::nodeAdded, this, &DomainServer::nodeAdded);
|
||||
connect(nodeList, &NodeList::nodeKilled, this, &DomainServer::nodeKilled);
|
||||
|
||||
|
||||
QTimer* silentNodeTimer = new QTimer(this);
|
||||
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||
|
||||
|
||||
connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), SLOT(readAvailableDatagrams()));
|
||||
|
||||
// fire a single shot timer to add static assignments back into the queue after a restart
|
||||
QTimer::singleShot(RESTART_HOLD_TIME_MSECS, this, SLOT(addStaticAssignmentsBackToQueueAfterRestart()));
|
||||
|
||||
// add whatever static assignments that have been parsed to the queue
|
||||
addStaticAssignmentsToQueue();
|
||||
}
|
||||
|
||||
void DomainServer::parseCommandLineTypeConfigs(const QStringList& argumentList, QSet<Assignment::Type>& excludedTypes) {
|
||||
|
@ -212,24 +299,177 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
|
|||
}
|
||||
}
|
||||
|
||||
void DomainServer::requestAuthenticationFromPotentialNode(const HifiSockAddr& senderSockAddr) {
|
||||
// this is a node we do not recognize and we need authentication - ask them to do so
|
||||
// by providing them the hostname they should authenticate with
|
||||
QByteArray authenticationRequestPacket = byteArrayWithPopulatedHeader(PacketTypeDomainServerAuthRequest);
|
||||
|
||||
QDataStream authPacketStream(&authenticationRequestPacket, QIODevice::Append);
|
||||
authPacketStream << _nodeAuthenticationURL;
|
||||
|
||||
qDebug() << "Asking node at" << senderSockAddr << "to authenticate.";
|
||||
|
||||
// send the authentication request back to the node
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram(authenticationRequestPacket,
|
||||
senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||
}
|
||||
|
||||
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
||||
<< NodeType::AvatarMixer << NodeType::VoxelServer << NodeType::ParticleServer
|
||||
<< NodeType::MetavoxelServer;
|
||||
|
||||
|
||||
void DomainServer::addNodeToNodeListAndConfirmConnection(const QByteArray& packet, const HifiSockAddr& senderSockAddr,
|
||||
const QJsonObject& authJsonObject) {
|
||||
|
||||
NodeType_t nodeType;
|
||||
HifiSockAddr publicSockAddr, localSockAddr;
|
||||
|
||||
int numPreInterestBytes = parseNodeDataFromByteArray(nodeType, publicSockAddr, localSockAddr, packet, senderSockAddr);
|
||||
|
||||
QUuid assignmentUUID = uuidFromPacketHeader(packet);
|
||||
SharedAssignmentPointer matchingAssignment;
|
||||
|
||||
if (!assignmentUUID.isNull() && (matchingAssignment = matchingStaticAssignmentForCheckIn(assignmentUUID, nodeType))
|
||||
&& matchingAssignment) {
|
||||
// this is an assigned node, make sure the UUID sent is for an assignment we're actually trying to give out
|
||||
|
||||
// remove the matching assignment from the assignment queue so we don't take the next check in
|
||||
// (if it exists)
|
||||
removeMatchingAssignmentFromQueue(matchingAssignment);
|
||||
} else {
|
||||
assignmentUUID = QUuid();
|
||||
}
|
||||
|
||||
// create a new session UUID for this node
|
||||
QUuid nodeUUID = QUuid::createUuid();
|
||||
|
||||
SharedNodePointer newNode = NodeList::getInstance()->addOrUpdateNode(nodeUUID, nodeType, publicSockAddr, localSockAddr);
|
||||
|
||||
// when the newNode is created the linked data is also created, if this was a static assignment set the UUID
|
||||
reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData())->setStaticAssignmentUUID(assignmentUUID);
|
||||
|
||||
if (!authJsonObject.isEmpty()) {
|
||||
// pull the connection secret from the authJsonObject and set it as the connection secret for this node
|
||||
QUuid connectionSecret(authJsonObject["data"].toObject()["connection_secret"].toString());
|
||||
newNode->setConnectionSecret(connectionSecret);
|
||||
}
|
||||
|
||||
// reply back to the user with a PacketTypeDomainList
|
||||
sendDomainListToNode(newNode, senderSockAddr, nodeInterestListFromPacket(packet, numPreInterestBytes));
|
||||
}
|
||||
|
||||
const int NUM_BYTES_DATA_SERVER_REGISTRATION_TOKEN = 16;
|
||||
|
||||
int DomainServer::parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr,
|
||||
HifiSockAddr& localSockAddr, const QByteArray& packet,
|
||||
const HifiSockAddr& senderSockAddr) {
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
||||
if (packetTypeForPacket(packet) == PacketTypeDomainConnectRequest) {
|
||||
// we need to skip a quint8 that indicates if there is a registration token
|
||||
// and potentially the registration token itself
|
||||
quint8 hasRegistrationToken;
|
||||
packetStream >> hasRegistrationToken;
|
||||
|
||||
if (hasRegistrationToken) {
|
||||
QByteArray registrationToken;
|
||||
packetStream >> registrationToken;
|
||||
}
|
||||
}
|
||||
|
||||
packetStream >> nodeType;
|
||||
packetStream >> publicSockAddr >> localSockAddr;
|
||||
|
||||
if (publicSockAddr.getAddress().isNull()) {
|
||||
// this node wants to use us its STUN server
|
||||
// so set the node public address to whatever we perceive the public address to be
|
||||
|
||||
// if the sender is on our box then leave its public address to 0 so that
|
||||
// other users attempt to reach it on the same address they have for the domain-server
|
||||
if (senderSockAddr.getAddress().isLoopback()) {
|
||||
publicSockAddr.setAddress(QHostAddress());
|
||||
} else {
|
||||
publicSockAddr.setAddress(senderSockAddr.getAddress());
|
||||
}
|
||||
}
|
||||
|
||||
return packetStream.device()->pos();
|
||||
}
|
||||
|
||||
NodeSet DomainServer::nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes) {
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numPreceedingBytes);
|
||||
|
||||
quint8 numInterestTypes = 0;
|
||||
packetStream >> numInterestTypes;
|
||||
|
||||
quint8 nodeType;
|
||||
NodeSet nodeInterestSet;
|
||||
|
||||
for (int i = 0; i < numInterestTypes; i++) {
|
||||
packetStream >> nodeType;
|
||||
nodeInterestSet.insert((NodeType_t) nodeType);
|
||||
}
|
||||
|
||||
return nodeInterestSet;
|
||||
}
|
||||
|
||||
void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr,
|
||||
const NodeSet& nodeInterestList) {
|
||||
|
||||
QByteArray broadcastPacket = byteArrayWithPopulatedHeader(PacketTypeDomainList);
|
||||
|
||||
// always send the node their own UUID back
|
||||
QDataStream broadcastDataStream(&broadcastPacket, QIODevice::Append);
|
||||
broadcastDataStream << node->getUUID();
|
||||
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
if (nodeInterestList.size() > 0) {
|
||||
// if the node has any interest types, send back those nodes as well
|
||||
foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) {
|
||||
if (otherNode->getUUID() != node->getUUID() && nodeInterestList.contains(otherNode->getType())) {
|
||||
|
||||
// don't send avatar nodes to other avatars, that will come from avatar mixer
|
||||
broadcastDataStream << *otherNode.data();
|
||||
|
||||
// pack the secret that these two nodes will use to communicate with each other
|
||||
QUuid secretUUID = nodeData->getSessionSecretHash().value(otherNode->getUUID());
|
||||
if (secretUUID.isNull()) {
|
||||
// generate a new secret UUID these two nodes can use
|
||||
secretUUID = QUuid::createUuid();
|
||||
|
||||
// set that on the current Node's sessionSecretHash
|
||||
nodeData->getSessionSecretHash().insert(otherNode->getUUID(), secretUUID);
|
||||
|
||||
// set it on the other Node's sessionSecretHash
|
||||
reinterpret_cast<DomainServerNodeData*>(otherNode->getLinkedData())
|
||||
->getSessionSecretHash().insert(node->getUUID(), secretUUID);
|
||||
|
||||
}
|
||||
|
||||
broadcastDataStream << secretUUID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nodeList->writeDatagram(broadcastPacket, node, senderSockAddr);
|
||||
}
|
||||
|
||||
void DomainServer::readAvailableDatagrams() {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
HifiSockAddr senderSockAddr, nodePublicAddress, nodeLocalAddress;
|
||||
HifiSockAddr senderSockAddr;
|
||||
|
||||
static QByteArray broadcastPacket = byteArrayWithPopluatedHeader(PacketTypeDomainList);
|
||||
static int numBroadcastPacketHeaderBytes = broadcastPacket.size();
|
||||
|
||||
static QByteArray assignmentPacket = byteArrayWithPopluatedHeader(PacketTypeCreateAssignment);
|
||||
static QByteArray assignmentPacket = byteArrayWithPopulatedHeader(PacketTypeCreateAssignment);
|
||||
static int numAssignmentPacketHeaderBytes = assignmentPacket.size();
|
||||
|
||||
QByteArray receivedPacket;
|
||||
NodeType_t nodeType;
|
||||
|
||||
|
||||
while (nodeList->getNodeSocket().hasPendingDatagrams()) {
|
||||
receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
|
||||
|
@ -238,111 +478,69 @@ void DomainServer::readAvailableDatagrams() {
|
|||
|
||||
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
|
||||
PacketType requestType = packetTypeForPacket(receivedPacket);
|
||||
if (requestType == PacketTypeDomainListRequest) {
|
||||
|
||||
// this is an RFD or domain list request packet, and there is a version match
|
||||
|
||||
if (requestType == PacketTypeDomainConnectRequest) {
|
||||
QDataStream packetStream(receivedPacket);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(receivedPacket));
|
||||
|
||||
QUuid nodeUUID = uuidFromPacketHeader(receivedPacket);
|
||||
quint8 hasRegistrationToken;
|
||||
packetStream >> hasRegistrationToken;
|
||||
|
||||
packetStream >> nodeType;
|
||||
packetStream >> nodePublicAddress >> nodeLocalAddress;
|
||||
|
||||
if (nodePublicAddress.getAddress().isNull()) {
|
||||
// this node wants to use us its STUN server
|
||||
// so set the node public address to whatever we perceive the public address to be
|
||||
if (requiresAuthentication() && !hasRegistrationToken) {
|
||||
// we need authentication and this node did not give us a registration token - tell it to auth
|
||||
requestAuthenticationFromPotentialNode(senderSockAddr);
|
||||
} else if (requiresAuthentication()) {
|
||||
QByteArray registrationToken;
|
||||
packetStream >> registrationToken;
|
||||
|
||||
// if the sender is on our box then leave its public address to 0 so that
|
||||
// other users attempt to reach it on the same address they have for the domain-server
|
||||
if (senderSockAddr.getAddress().isLoopback()) {
|
||||
nodePublicAddress.setAddress(QHostAddress());
|
||||
} else {
|
||||
nodePublicAddress.setAddress(senderSockAddr.getAddress());
|
||||
}
|
||||
}
|
||||
|
||||
SharedAssignmentPointer matchingStaticAssignment;
|
||||
|
||||
// check if this is a non-statically assigned node, a node that is assigned and checking in for the first time
|
||||
// or a node that has already checked in and is continuing to report for duty
|
||||
if (!STATICALLY_ASSIGNED_NODES.contains(nodeType)
|
||||
|| (matchingStaticAssignment = matchingStaticAssignmentForCheckIn(nodeUUID, nodeType))
|
||||
|| nodeList->getInstance()->nodeWithUUID(nodeUUID)) {
|
||||
QString registrationTokenString(registrationToken.toHex());
|
||||
QJsonObject jsonForRedeemedToken = _redeemedTokenResponses.value(registrationTokenString);
|
||||
|
||||
if (nodeUUID.isNull()) {
|
||||
// this is a check in from an unidentified node
|
||||
// we need to generate a session UUID for this node
|
||||
nodeUUID = QUuid::createUuid();
|
||||
}
|
||||
|
||||
SharedNodePointer checkInNode = nodeList->addOrUpdateNode(nodeUUID,
|
||||
nodeType,
|
||||
nodePublicAddress,
|
||||
nodeLocalAddress);
|
||||
|
||||
// resize our broadcast packet in preparation to set it up again
|
||||
broadcastPacket.resize(numBroadcastPacketHeaderBytes);
|
||||
|
||||
if (matchingStaticAssignment) {
|
||||
// this was a newly added node with a matching static assignment
|
||||
// check if we have redeemed this token and are ready to check the node in
|
||||
if (jsonForRedeemedToken.isEmpty()) {
|
||||
// make a request against the data-server to get information required to connect to this node
|
||||
JSONCallbackParameters tokenCallbackParams;
|
||||
tokenCallbackParams.jsonCallbackReceiver = this;
|
||||
tokenCallbackParams.jsonCallbackMethod = "processTokenRedeemResponse";
|
||||
|
||||
// remove the matching assignment from the assignment queue so we don't take the next check in
|
||||
// (if it exists)
|
||||
if (_hasCompletedRestartHold) {
|
||||
removeMatchingAssignmentFromQueue(matchingStaticAssignment);
|
||||
}
|
||||
QString redeemURLString = QString("/api/v1/nodes/redeem/%1.json").arg(registrationTokenString);
|
||||
accountManager.authenticatedRequest(redeemURLString, QNetworkAccessManager::GetOperation,
|
||||
tokenCallbackParams);
|
||||
} else if (jsonForRedeemedToken["status"].toString() != "success") {
|
||||
// we redeemed the token, but it was invalid - get the node to get another
|
||||
requestAuthenticationFromPotentialNode(senderSockAddr);
|
||||
} else {
|
||||
// we've redeemed the token for this node and are ready to start communicating with it
|
||||
// add the node to our NodeList
|
||||
addNodeToNodeListAndConfirmConnection(receivedPacket, senderSockAddr, jsonForRedeemedToken);
|
||||
}
|
||||
|
||||
quint8 numInterestTypes = 0;
|
||||
packetStream >> numInterestTypes;
|
||||
// if it exists, remove this response from the in-memory hash
|
||||
_redeemedTokenResponses.remove(registrationTokenString);
|
||||
|
||||
NodeType_t* nodeTypesOfInterest = reinterpret_cast<NodeType_t*>(receivedPacket.data()
|
||||
+ packetStream.device()->pos());
|
||||
|
||||
// always send the node their own UUID back
|
||||
QDataStream broadcastDataStream(&broadcastPacket, QIODevice::Append);
|
||||
broadcastDataStream << checkInNode->getUUID();
|
||||
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(checkInNode->getLinkedData());
|
||||
|
||||
if (numInterestTypes > 0) {
|
||||
// if the node has any interest types, send back those nodes as well
|
||||
foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) {
|
||||
if (otherNode->getUUID() != nodeUUID &&
|
||||
memchr(nodeTypesOfInterest, otherNode->getType(), numInterestTypes)) {
|
||||
|
||||
// don't send avatar nodes to other avatars, that will come from avatar mixer
|
||||
broadcastDataStream << *otherNode.data();
|
||||
|
||||
// pack the secret that these two nodes will use to communicate with each other
|
||||
QUuid secretUUID = nodeData->getSessionSecretHash().value(otherNode->getUUID());
|
||||
if (secretUUID.isNull()) {
|
||||
// generate a new secret UUID these two nodes can use
|
||||
secretUUID = QUuid::createUuid();
|
||||
|
||||
// set that on the current Node's sessionSecretHash
|
||||
nodeData->getSessionSecretHash().insert(otherNode->getUUID(), secretUUID);
|
||||
|
||||
// set it on the other Node's sessionSecretHash
|
||||
reinterpret_cast<DomainServerNodeData*>(otherNode->getLinkedData())
|
||||
->getSessionSecretHash().insert(nodeUUID, secretUUID);
|
||||
|
||||
}
|
||||
|
||||
broadcastDataStream << secretUUID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update last receive to now
|
||||
quint64 timeNow = usecTimestampNow();
|
||||
checkInNode->setLastHeardMicrostamp(timeNow);
|
||||
|
||||
// send the constructed list back to this node
|
||||
nodeList->getNodeSocket().writeDatagram(broadcastPacket,
|
||||
senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||
} else {
|
||||
// we don't require authentication - add this node to our NodeList
|
||||
// and send back session UUID right away
|
||||
addNodeToNodeListAndConfirmConnection(receivedPacket, senderSockAddr);
|
||||
}
|
||||
|
||||
} else if (requestType == PacketTypeDomainListRequest) {
|
||||
QUuid nodeUUID = uuidFromPacketHeader(receivedPacket);
|
||||
NodeType_t throwawayNodeType;
|
||||
HifiSockAddr nodePublicAddress, nodeLocalAddress;
|
||||
|
||||
int numNodeInfoBytes = parseNodeDataFromByteArray(throwawayNodeType, nodePublicAddress, nodeLocalAddress,
|
||||
receivedPacket, senderSockAddr);
|
||||
|
||||
SharedNodePointer checkInNode = nodeList->updateSocketsForNode(nodeUUID, nodePublicAddress, nodeLocalAddress);
|
||||
|
||||
// update last receive to now
|
||||
quint64 timeNow = usecTimestampNow();
|
||||
checkInNode->setLastHeardMicrostamp(timeNow);
|
||||
|
||||
|
||||
sendDomainListToNode(checkInNode, senderSockAddr, nodeInterestListFromPacket(receivedPacket, numNodeInfoBytes));
|
||||
|
||||
} else if (requestType == PacketTypeRequestAssignment) {
|
||||
|
||||
// construct the requested assignment from the packet data
|
||||
|
@ -490,42 +688,47 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
// this is a script upload - ask the HTTPConnection to parse the form data
|
||||
QList<FormData> formData = connection->parseFormData();
|
||||
|
||||
// create an assignment for this saved script
|
||||
Assignment* scriptAssignment = new Assignment(Assignment::CreateCommand, Assignment::AgentType);
|
||||
|
||||
|
||||
// check how many instances of this assignment the user wants by checking the ASSIGNMENT-INSTANCES header
|
||||
const QString ASSIGNMENT_INSTANCES_HEADER = "ASSIGNMENT-INSTANCES";
|
||||
|
||||
QByteArray assignmentInstancesValue = connection->requestHeaders().value(ASSIGNMENT_INSTANCES_HEADER.toLocal8Bit());
|
||||
|
||||
int numInstances = 1;
|
||||
|
||||
if (!assignmentInstancesValue.isEmpty()) {
|
||||
// the user has requested a specific number of instances
|
||||
// so set that on the created assignment
|
||||
int numInstances = assignmentInstancesValue.toInt();
|
||||
if (numInstances > 0) {
|
||||
qDebug() << numInstances;
|
||||
scriptAssignment->setNumberOfInstances(numInstances);
|
||||
}
|
||||
|
||||
numInstances = assignmentInstancesValue.toInt();
|
||||
}
|
||||
|
||||
|
||||
const char ASSIGNMENT_SCRIPT_HOST_LOCATION[] = "resources/web/assignment";
|
||||
|
||||
QString newPath(ASSIGNMENT_SCRIPT_HOST_LOCATION);
|
||||
newPath += "/";
|
||||
// append the UUID for this script as the new filename, remove the curly braces
|
||||
newPath += uuidStringWithoutCurlyBraces(scriptAssignment->getUUID());
|
||||
|
||||
// create a file with the GUID of the assignment in the script host locaiton
|
||||
QFile scriptFile(newPath);
|
||||
scriptFile.open(QIODevice::WriteOnly);
|
||||
scriptFile.write(formData[0].second);
|
||||
|
||||
qDebug("Saved a script for assignment at %s", qPrintable(newPath));
|
||||
for (int i = 0; i < numInstances; i++) {
|
||||
|
||||
// create an assignment for this saved script
|
||||
Assignment* scriptAssignment = new Assignment(Assignment::CreateCommand, Assignment::AgentType);
|
||||
|
||||
QString newPath(ASSIGNMENT_SCRIPT_HOST_LOCATION);
|
||||
newPath += "/";
|
||||
// append the UUID for this script as the new filename, remove the curly braces
|
||||
newPath += uuidStringWithoutCurlyBraces(scriptAssignment->getUUID());
|
||||
|
||||
// create a file with the GUID of the assignment in the script host locaiton
|
||||
QFile scriptFile(newPath);
|
||||
scriptFile.open(QIODevice::WriteOnly);
|
||||
scriptFile.write(formData[0].second);
|
||||
|
||||
qDebug("Saved a script for assignment at %s", qPrintable(newPath));
|
||||
|
||||
// add the script assigment to the assignment queue
|
||||
_assignmentQueue.enqueue(SharedAssignmentPointer(scriptAssignment));
|
||||
}
|
||||
|
||||
// respond with a 200 code for successful upload
|
||||
connection->respond(HTTPConnection::StatusCode200);
|
||||
|
||||
// add the script assigment to the assignment queue
|
||||
_assignmentQueue.enqueue(SharedAssignmentPointer(scriptAssignment));
|
||||
}
|
||||
} else if (connection->requestOperation() == QNetworkAccessManager::DeleteOperation) {
|
||||
if (path.startsWith(URI_NODE)) {
|
||||
|
@ -583,43 +786,39 @@ void DomainServer::nodeAdded(SharedNodePointer node) {
|
|||
}
|
||||
|
||||
void DomainServer::nodeKilled(SharedNodePointer node) {
|
||||
// if this node's UUID matches a static assignment we need to throw it back in the assignment queue
|
||||
SharedAssignmentPointer matchedAssignment = _staticAssignmentHash.value(node->getUUID());
|
||||
|
||||
if (matchedAssignment) {
|
||||
refreshStaticAssignmentAndAddToQueue(matchedAssignment);
|
||||
}
|
||||
|
||||
// cleanup the connection secrets that we set up for this node (on the other nodes)
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
foreach (const QUuid& otherNodeSessionUUID, nodeData->getSessionSecretHash().keys()) {
|
||||
SharedNodePointer otherNode = NodeList::getInstance()->nodeWithUUID(otherNodeSessionUUID);
|
||||
if (otherNode) {
|
||||
reinterpret_cast<DomainServerNodeData*>(otherNode->getLinkedData())->getSessionSecretHash().remove(node->getUUID());
|
||||
if (nodeData) {
|
||||
// if this node's UUID matches a static assignment we need to throw it back in the assignment queue
|
||||
if (!nodeData->getStaticAssignmentUUID().isNull()) {
|
||||
SharedAssignmentPointer matchedAssignment = _staticAssignmentHash.value(nodeData->getStaticAssignmentUUID());
|
||||
|
||||
if (matchedAssignment) {
|
||||
refreshStaticAssignmentAndAddToQueue(matchedAssignment);
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup the connection secrets that we set up for this node (on the other nodes)
|
||||
foreach (const QUuid& otherNodeSessionUUID, nodeData->getSessionSecretHash().keys()) {
|
||||
SharedNodePointer otherNode = NodeList::getInstance()->nodeWithUUID(otherNodeSessionUUID);
|
||||
if (otherNode) {
|
||||
reinterpret_cast<DomainServerNodeData*>(otherNode->getLinkedData())->getSessionSecretHash().remove(node->getUUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SharedAssignmentPointer DomainServer::matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType) {
|
||||
if (_hasCompletedRestartHold) {
|
||||
// look for a match in the assignment hash
|
||||
|
||||
QQueue<SharedAssignmentPointer>::iterator i = _assignmentQueue.begin();
|
||||
|
||||
while (i != _assignmentQueue.end()) {
|
||||
if (i->data()->getType() == Assignment::typeForNodeType(nodeType) && i->data()->getUUID() == checkInUUID) {
|
||||
return _assignmentQueue.takeAt(i - _assignmentQueue.begin());
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SharedAssignmentPointer matchingStaticAssignment = _staticAssignmentHash.value(checkInUUID);
|
||||
if (matchingStaticAssignment && matchingStaticAssignment->getType() == nodeType) {
|
||||
return matchingStaticAssignment;
|
||||
QQueue<SharedAssignmentPointer>::iterator i = _assignmentQueue.begin();
|
||||
|
||||
while (i != _assignmentQueue.end()) {
|
||||
if (i->data()->getType() == Assignment::typeForNodeType(nodeType) && i->data()->getUUID() == checkInUUID) {
|
||||
return _assignmentQueue.takeAt(i - _assignmentQueue.begin());
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return SharedAssignmentPointer();
|
||||
}
|
||||
|
||||
|
@ -639,14 +838,7 @@ SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assig
|
|||
|
||||
if (assignment->getType() == Assignment::AgentType) {
|
||||
// if there is more than one instance to send out, simply decrease the number of instances
|
||||
|
||||
if (assignment->getNumberOfInstances() == 1) {
|
||||
return _assignmentQueue.takeAt(sharedAssignment - _assignmentQueue.begin());
|
||||
} else {
|
||||
assignment->decrementNumberOfInstances();
|
||||
return *sharedAssignment;
|
||||
}
|
||||
|
||||
return _assignmentQueue.takeAt(sharedAssignment - _assignmentQueue.begin());
|
||||
} else {
|
||||
// remove the assignment from the queue
|
||||
SharedAssignmentPointer deployableAssignment = _assignmentQueue.takeAt(sharedAssignment
|
||||
|
@ -682,8 +874,7 @@ void DomainServer::removeMatchingAssignmentFromQueue(const SharedAssignmentPoint
|
|||
}
|
||||
}
|
||||
|
||||
void DomainServer::addStaticAssignmentsBackToQueueAfterRestart() {
|
||||
_hasCompletedRestartHold = true;
|
||||
void DomainServer::addStaticAssignmentsToQueue() {
|
||||
|
||||
// if the domain-server has just restarted,
|
||||
// check if there are static assignments that we need to throw into the assignment queue
|
||||
|
|
|
@ -11,8 +11,11 @@
|
|||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QQueue>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
#include <Assignment.h>
|
||||
#include <HTTPManager.h>
|
||||
|
@ -25,6 +28,8 @@ class DomainServer : public QCoreApplication, public HTTPRequestHandler {
|
|||
public:
|
||||
DomainServer(int argc, char* argv[]);
|
||||
|
||||
bool requiresAuthentication() const { return !_nodeAuthenticationURL.isEmpty(); }
|
||||
|
||||
bool handleHTTPRequest(HTTPConnection* connection, const QString& path);
|
||||
|
||||
void exit(int retCode = 0);
|
||||
|
@ -36,6 +41,17 @@ public slots:
|
|||
void nodeKilled(SharedNodePointer node);
|
||||
|
||||
private:
|
||||
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
|
||||
|
||||
void requestAuthenticationFromPotentialNode(const HifiSockAddr& senderSockAddr);
|
||||
void addNodeToNodeListAndConfirmConnection(const QByteArray& packet, const HifiSockAddr& senderSockAddr,
|
||||
const QJsonObject& authJsonObject = QJsonObject());
|
||||
int parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr,
|
||||
HifiSockAddr& localSockAddr, const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
||||
NodeSet nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes);
|
||||
void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr,
|
||||
const NodeSet& nodeInterestList);
|
||||
|
||||
void parseCommandLineTypeConfigs(const QStringList& argumentList, QSet<Assignment::Type>& excludedTypes);
|
||||
void readConfigFile(const QString& path, QSet<Assignment::Type>& excludedTypes);
|
||||
QString readServerAssignmentConfig(const QJsonObject& jsonObject, const QString& nodeName);
|
||||
|
@ -47,6 +63,7 @@ private:
|
|||
SharedAssignmentPointer deployableAssignmentForRequest(const Assignment& requestAssignment);
|
||||
void removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment);
|
||||
void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment);
|
||||
void addStaticAssignmentsToQueue();
|
||||
|
||||
QJsonObject jsonForSocket(const HifiSockAddr& socket);
|
||||
QJsonObject jsonObjectForNode(const SharedNodePointer& node);
|
||||
|
@ -56,10 +73,17 @@ private:
|
|||
QHash<QUuid, SharedAssignmentPointer> _staticAssignmentHash;
|
||||
QQueue<SharedAssignmentPointer> _assignmentQueue;
|
||||
|
||||
bool _hasCompletedRestartHold;
|
||||
QUrl _nodeAuthenticationURL;
|
||||
|
||||
QStringList _argumentList;
|
||||
|
||||
QHash<QString, QJsonObject> _redeemedTokenResponses;
|
||||
private slots:
|
||||
void requestCreationFromDataServer();
|
||||
void processCreateResponseFromDataServer(const QJsonObject& jsonObject);
|
||||
void processTokenRedeemResponse(const QJsonObject& jsonObject);
|
||||
|
||||
void readAvailableDatagrams();
|
||||
void addStaticAssignmentsBackToQueueAfterRestart();
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__DomainServer__) */
|
||||
|
|
16
domain-server/src/DomainServerNodeData.cpp
Normal file
16
domain-server/src/DomainServerNodeData.cpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// DomainServerNodeData.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 2/6/2014.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "DomainServerNodeData.h"
|
||||
|
||||
DomainServerNodeData::DomainServerNodeData() :
|
||||
_sessionSecretHash(),
|
||||
_staticAssignmentUUID()
|
||||
{
|
||||
|
||||
}
|
|
@ -10,17 +10,22 @@
|
|||
#define __hifi__DomainServerNodeData__
|
||||
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include <NodeData.h>
|
||||
|
||||
class DomainServerNodeData : public NodeData {
|
||||
public:
|
||||
DomainServerNodeData() : _sessionSecretHash() {};
|
||||
DomainServerNodeData();
|
||||
int parseData(const QByteArray& packet) { return 0; }
|
||||
|
||||
void setStaticAssignmentUUID(const QUuid& staticAssignmentUUID) { _staticAssignmentUUID = staticAssignmentUUID; }
|
||||
const QUuid& getStaticAssignmentUUID() const { return _staticAssignmentUUID; }
|
||||
|
||||
QHash<QUuid, QUuid>& getSessionSecretHash() { return _sessionSecretHash; }
|
||||
private:
|
||||
QHash<QUuid, QUuid> _sessionSecretHash;
|
||||
QUuid _staticAssignmentUUID;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__DomainServerNodeData__) */
|
||||
|
|
187
examples/bot.js
Normal file
187
examples/bot.js
Normal file
|
@ -0,0 +1,187 @@
|
|||
//
|
||||
// bot.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 2/20/14.
|
||||
// Modified by Philip on 2/26/14
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that demonstrates an NPC avatar.
|
||||
//
|
||||
//
|
||||
|
||||
function getRandomFloat(min, max) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
function getRandomInt (min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
var CHANCE_OF_MOVING = 0.005;
|
||||
var CHANCE_OF_SOUND = 0.005;
|
||||
var CHANCE_OF_HEAD_TURNING = 0.05;
|
||||
var CHANCE_OF_BIG_MOVE = 0.1;
|
||||
|
||||
var isMoving = false;
|
||||
var isTurningHead = false;
|
||||
var isPlayingAudio = false;
|
||||
|
||||
var STARTING_RANGE = 18.0;
|
||||
var MAX_RANGE = 25.0;
|
||||
var MOVE_RANGE_SMALL = 0.5;
|
||||
var MOVE_RANGE_BIG = 5.0;
|
||||
var TURN_RANGE = 45.0;
|
||||
var STOP_TOLERANCE = 0.05;
|
||||
var MOVE_RATE = 0.05;
|
||||
var TURN_RATE = 0.15;
|
||||
var PITCH_RATE = 0.20;
|
||||
var PITCH_RANGE = 30.0;
|
||||
|
||||
|
||||
var firstPosition = { x: getRandomFloat(0, STARTING_RANGE), y: 0, z: getRandomFloat(0, STARTING_RANGE) };
|
||||
var targetPosition = { x: 0, y: 0, z: 0 };
|
||||
|
||||
var targetDirection = { x: 0, y: 0, z: 0, w: 0 };
|
||||
var currentDirection = { x: 0, y: 0, z: 0, w: 0 };
|
||||
|
||||
var targetHeadPitch = 0.0;
|
||||
|
||||
var sounds = [];
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/AB1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Anchorman2.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/B1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/B1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Bale1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Bandcamp.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Big1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Big2.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Brian1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Buster1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/CES1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/CES2.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/CES3.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/CES4.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Carrie1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Carrie3.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Charlotte1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/EN1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/EN2.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/EN3.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Eugene1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Francesco1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Italian1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Japanese1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Leigh1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Lucille1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Lucille2.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/MeanGirls.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Murray2.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Nigel1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/PennyLane.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Pitt1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Ricardo.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/SN.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Sake1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Samantha1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Samantha2.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Spicoli1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Supernatural.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Swearengen1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/TheDude.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Tony.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Triumph1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Uma1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Walken1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Walken2.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Z1.raw"));
|
||||
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/Z2.raw"));
|
||||
|
||||
|
||||
// Play a random sound from a list of conversational audio clips
|
||||
function audioDone() {
|
||||
isPlayingAudio = false;
|
||||
}
|
||||
|
||||
var AVERAGE_AUDIO_LENGTH = 8000;
|
||||
function playRandomSound(position) {
|
||||
if (!isPlayingAudio) {
|
||||
var whichSound = Math.floor((Math.random() * sounds.length) % sounds.length);
|
||||
var audioOptions = new AudioInjectionOptions();
|
||||
audioOptions.volume = 0.25 + (Math.random() * 0.75);
|
||||
audioOptions.position = position;
|
||||
Audio.playSound(sounds[whichSound], audioOptions);
|
||||
isPlayingAudio = true;
|
||||
Script.setTimeout(audioDone, AVERAGE_AUDIO_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
// change the avatar's position to the random one
|
||||
Avatar.position = firstPosition;
|
||||
|
||||
// pick an integer between 1 and 20 for the face model for this bot
|
||||
botNumber = getRandomInt(1, 100);
|
||||
|
||||
if (botNumber <= 20) {
|
||||
newFaceFilePrefix = "bot" + botNumber;
|
||||
newBodyFilePrefix = "defaultAvatar_body"
|
||||
} else {
|
||||
if (botNumber <= 40) {
|
||||
newFaceFilePrefix = "superhero";
|
||||
} else if (botNumber <= 60) {
|
||||
newFaceFilePrefix = "amber";
|
||||
} else if (botNumber <= 80) {
|
||||
newFaceFilePrefix = "ron";
|
||||
} else {
|
||||
newFaceFilePrefix = "angie";
|
||||
}
|
||||
|
||||
newBodyFilePrefix = "bot" + botNumber;
|
||||
}
|
||||
|
||||
// set the face model fst using the bot number
|
||||
// there is no need to change the body model - we're using the default
|
||||
Avatar.faceModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newFaceFilePrefix + ".fst";
|
||||
Avatar.skeletonModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newBodyFilePrefix + ".fst";
|
||||
Avatar.billboardURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/billboards/bot" + botNumber + ".png";
|
||||
|
||||
Agent.isAvatar = true;
|
||||
|
||||
function updateBehavior() {
|
||||
if (Math.random() < CHANCE_OF_SOUND) {
|
||||
playRandomSound(Avatar.position);
|
||||
}
|
||||
|
||||
if (!isTurningHead && (Math.random() < CHANCE_OF_HEAD_TURNING)) {
|
||||
targetHeadPitch = getRandomFloat(-PITCH_RANGE, PITCH_RANGE);
|
||||
isTurningHead = true;
|
||||
} else {
|
||||
Avatar.headPitch = Avatar.headPitch + (targetHeadPitch - Avatar.headPitch) * PITCH_RATE;
|
||||
if (Math.abs(Avatar.headPitch - targetHeadPitch) < STOP_TOLERANCE) {
|
||||
isTurningHead = false;
|
||||
}
|
||||
}
|
||||
if (!isMoving && (Math.random() < CHANCE_OF_MOVING)) {
|
||||
// Set new target location
|
||||
targetDirection = Quat.multiply(Avatar.orientation, Quat.angleAxis(getRandomFloat(-TURN_RANGE, TURN_RANGE), { x:0, y:1, z:0 }));
|
||||
var front = Quat.getFront(targetDirection);
|
||||
if (Math.random() < CHANCE_OF_BIG_MOVE) {
|
||||
targetPosition = Vec3.sum(Avatar.position, Vec3.multiply(front, getRandomFloat(0.0, MOVE_RANGE_BIG)));
|
||||
} else {
|
||||
targetPosition = Vec3.sum(Avatar.position, Vec3.multiply(front, getRandomFloat(0.0, MOVE_RANGE_SMALL)));
|
||||
}
|
||||
isMoving = true;
|
||||
} else {
|
||||
Avatar.position = Vec3.sum(Avatar.position, Vec3.multiply(Vec3.subtract(targetPosition, Avatar.position), MOVE_RATE));
|
||||
Avatar.orientation = Quat.mix(Avatar.orientation, targetDirection, TURN_RATE);
|
||||
if (Vec3.length(Vec3.subtract(Avatar.position, targetPosition)) < STOP_TOLERANCE) {
|
||||
isMoving = false;
|
||||
}
|
||||
}
|
||||
if (Vec3.length(Avatar.position) > MAX_RANGE) {
|
||||
// Don't let our happy little person get out of the cage
|
||||
isMoving = false;
|
||||
Avatar.position = { x: 0, y: 0, z: 0 };
|
||||
}
|
||||
}
|
||||
Script.willSendVisualDataCallback.connect(updateBehavior);
|
153
examples/clipboardExample.js
Normal file
153
examples/clipboardExample.js
Normal file
|
@ -0,0 +1,153 @@
|
|||
//
|
||||
// clipboardExample.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 1/28/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that demonstrates use of the Clipboard class
|
||||
//
|
||||
//
|
||||
|
||||
var selectedVoxel = { x: 0, y: 0, z: 0, s: 0 };
|
||||
var selectedSize = 4;
|
||||
|
||||
function setupMenus() {
|
||||
// hook up menus
|
||||
Menu.menuItemEvent.connect(menuItemEvent);
|
||||
|
||||
// delete the standard application menu item
|
||||
Menu.removeMenuItem("Edit", "Cut");
|
||||
Menu.removeMenuItem("Edit", "Copy");
|
||||
Menu.removeMenuItem("Edit", "Paste");
|
||||
Menu.removeMenuItem("Edit", "Delete");
|
||||
Menu.removeMenuItem("Edit", "Nudge");
|
||||
Menu.removeMenuItem("File", "Export Voxels");
|
||||
Menu.removeMenuItem("File", "Import Voxels");
|
||||
|
||||
// delete the standard application menu item
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Cut", shortcutKey: "CTRL+X", afterItem: "Voxels" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Copy", shortcutKey: "CTRL+C", afterItem: "Cut" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste", shortcutKey: "CTRL+V", afterItem: "Copy" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Nudge", shortcutKey: "CTRL+N", afterItem: "Paste" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete", shortcutKeyEvent: { text: "backspace" }, afterItem: "Nudge" });
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Voxels", shortcutKey: "CTRL+E", afterItem: "Voxels" });
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Voxels", shortcutKey: "CTRL+I", afterItem: "Export Voxels" });
|
||||
}
|
||||
|
||||
function menuItemEvent(menuItem) {
|
||||
var debug = true;
|
||||
if (debug) {
|
||||
print("menuItemEvent " + menuItem);
|
||||
}
|
||||
|
||||
// Note: this sample uses Alt+ as the key codes for these clipboard items
|
||||
if (menuItem == "Copy") {
|
||||
print("copying...");
|
||||
Clipboard.copyVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
|
||||
}
|
||||
if (menuItem == "Cut") {
|
||||
print("cutting...");
|
||||
Clipboard.cutVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
|
||||
}
|
||||
if (menuItem == "Paste") {
|
||||
print("pasting...");
|
||||
Clipboard.pasteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
|
||||
}
|
||||
if (menuItem == "Delete") {
|
||||
print("deleting...");
|
||||
Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
|
||||
}
|
||||
|
||||
if (menuItem == "Export Voxels") {
|
||||
print("export");
|
||||
Clipboard.exportVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
|
||||
}
|
||||
if (menuItem == "Import Voxels") {
|
||||
print("import");
|
||||
Clipboard.importVoxels();
|
||||
}
|
||||
if (menuItem == "Nudge") {
|
||||
print("nudge");
|
||||
Clipboard.nudgeVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s, { x: -1, y: 0, z: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
var selectCube = Overlays.addOverlay("cube", {
|
||||
position: { x: 0, y: 0, z: 0},
|
||||
size: selectedSize,
|
||||
color: { red: 255, green: 255, blue: 0},
|
||||
alpha: 1,
|
||||
solid: false,
|
||||
visible: false,
|
||||
lineWidth: 4
|
||||
});
|
||||
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
|
||||
var debug = false;
|
||||
if (debug) {
|
||||
print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y);
|
||||
print("called Camera.computePickRay()");
|
||||
print("computePickRay origin=" + pickRay.origin.x + ", " + pickRay.origin.y + ", " + pickRay.origin.z);
|
||||
print("computePickRay direction=" + pickRay.direction.x + ", " + pickRay.direction.y + ", " + pickRay.direction.z);
|
||||
}
|
||||
|
||||
var intersection = Voxels.findRayIntersection(pickRay);
|
||||
|
||||
if (intersection.intersects) {
|
||||
if (debug) {
|
||||
print("intersection voxel.red/green/blue=" + intersection.voxel.red + ", "
|
||||
+ intersection.voxel.green + ", " + intersection.voxel.blue);
|
||||
print("intersection voxel.x/y/z/s=" + intersection.voxel.x + ", "
|
||||
+ intersection.voxel.y + ", " + intersection.voxel.z+ ": " + intersection.voxel.s);
|
||||
print("intersection face=" + intersection.face);
|
||||
print("intersection distance=" + intersection.distance);
|
||||
print("intersection intersection.x/y/z=" + intersection.intersection.x + ", "
|
||||
+ intersection.intersection.y + ", " + intersection.intersection.z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
var x = Math.floor(intersection.voxel.x / selectedSize) * selectedSize;
|
||||
var y = Math.floor(intersection.voxel.y / selectedSize) * selectedSize;
|
||||
var z = Math.floor(intersection.voxel.z / selectedSize) * selectedSize;
|
||||
selectedVoxel = { x: x, y: y, z: z, s: selectedSize };
|
||||
Overlays.editOverlay(selectCube, { position: selectedVoxel, size: selectedSize, visible: true } );
|
||||
} else {
|
||||
Overlays.editOverlay(selectCube, { visible: false } );
|
||||
selectedVoxel = { x: 0, y: 0, z: 0, s: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
|
||||
function wheelEvent(event) {
|
||||
var debug = false;
|
||||
if (debug) {
|
||||
print("wheelEvent");
|
||||
print(" event.x,y=" + event.x + ", " + event.y);
|
||||
print(" event.delta=" + event.delta);
|
||||
print(" event.orientation=" + event.orientation);
|
||||
print(" event.isLeftButton=" + event.isLeftButton);
|
||||
print(" event.isRightButton=" + event.isRightButton);
|
||||
print(" event.isMiddleButton=" + event.isMiddleButton);
|
||||
print(" event.isShifted=" + event.isShifted);
|
||||
print(" event.isControl=" + event.isControl);
|
||||
print(" event.isMeta=" + event.isMeta);
|
||||
print(" event.isAlt=" + event.isAlt);
|
||||
}
|
||||
}
|
||||
|
||||
Controller.wheelEvent.connect(wheelEvent);
|
||||
|
||||
function scriptEnding() {
|
||||
Overlays.deleteOverlay(selectCube);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
setupMenus();
|
File diff suppressed because it is too large
Load diff
27
examples/hideAvatarExample.js
Normal file
27
examples/hideAvatarExample.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// hideAvatarExample.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 1/28/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that demonstrates how to enable or disable local rendering of your own avatar
|
||||
//
|
||||
//
|
||||
|
||||
function keyReleaseEvent(event) {
|
||||
if (event.text == "F2") {
|
||||
MyAvatar.shouldRenderLocally = !MyAvatar.shouldRenderLocally;
|
||||
}
|
||||
}
|
||||
|
||||
// Map keyPress and mouse move events to our callbacks
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
|
||||
|
||||
function scriptEnding() {
|
||||
// re-enabled the standard behavior
|
||||
MyAvatar.shouldRenderLocally = true;
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(scriptEnding);
|
104
examples/menuExample.js
Normal file
104
examples/menuExample.js
Normal file
|
@ -0,0 +1,104 @@
|
|||
//
|
||||
// menuExample.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2/24/14
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that demonstrates use of the Menu object
|
||||
//
|
||||
|
||||
|
||||
function setupMenus() {
|
||||
Menu.addMenu("Foo");
|
||||
Menu.addMenuItem("Foo","Foo item 1", "SHIFT+CTRL+F" );
|
||||
Menu.addMenuItem("Foo","Foo item 2", "SHIFT+F" );
|
||||
Menu.addMenuItem("Foo","Foo item 3", "META+F" );
|
||||
Menu.addMenuItem({
|
||||
menuName: "Foo",
|
||||
menuItemName: "Foo item 4",
|
||||
isCheckable: true,
|
||||
isChecked: true
|
||||
});
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "Foo",
|
||||
menuItemName: "Foo item 5",
|
||||
shortcutKey: "ALT+F",
|
||||
isCheckable: true
|
||||
});
|
||||
|
||||
|
||||
Menu.addSeparator("Foo","Removable Tools");
|
||||
Menu.addMenuItem("Foo","Remove Foo item 4");
|
||||
Menu.addMenuItem("Foo","Remove Foo");
|
||||
Menu.addMenu("Bar");
|
||||
|
||||
Menu.addMenuItem("Bar","Bar item 1", "b");
|
||||
Menu.addMenuItem({
|
||||
menuName: "Bar",
|
||||
menuItemName: "Bar item 2",
|
||||
shortcutKeyEvent: { text: "B", isControl: true }
|
||||
});
|
||||
|
||||
Menu.addMenu("Bar > Spam");
|
||||
Menu.addMenuItem("Bar > Spam","Spam item 1");
|
||||
Menu.addMenuItem({
|
||||
menuName: "Bar > Spam",
|
||||
menuItemName: "Spam item 2",
|
||||
isCheckable: true,
|
||||
isChecked: false
|
||||
});
|
||||
|
||||
Menu.addSeparator("Bar > Spam","Other Items");
|
||||
Menu.addMenuItem("Bar > Spam","Remove Spam item 2");
|
||||
Menu.addMenuItem("Foo","Remove Spam item 2");
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "Foo",
|
||||
menuItemName: "Remove Spam item 2"
|
||||
});
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "before Cut",
|
||||
beforeItem: "Cut"
|
||||
});
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "after Nudge",
|
||||
afterItem: "Nudge"
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
print("SCRIPT ENDNG!!!\n");
|
||||
|
||||
Menu.removeMenu("Foo");
|
||||
Menu.removeMenu("Bar");
|
||||
}
|
||||
|
||||
function menuItemEvent(menuItem) {
|
||||
print("menuItemEvent() in JS... menuItem=" + menuItem);
|
||||
if (menuItem == "Foo item 4") {
|
||||
print(" checked=" + Menu.isOptionChecked("Foo item 4"));
|
||||
}
|
||||
if (menuItem == "Remove Foo item 4") {
|
||||
Menu.removeMenuItem("Foo", "Foo item 4");
|
||||
}
|
||||
if (menuItem == "Remove Foo") {
|
||||
Menu.removeMenu("Foo");
|
||||
}
|
||||
if (menuItem == "Remove Spam item 2") {
|
||||
Menu.removeMenuItem("Bar > Spam", "Spam item 2");
|
||||
}
|
||||
}
|
||||
|
||||
setupMenus();
|
||||
|
||||
// register our scriptEnding callback
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
Menu.menuItemEvent.connect(menuItemEvent);
|
|
@ -156,6 +156,13 @@ var line3d = Overlays.addOverlay("line3d", {
|
|||
lineWidth: 5
|
||||
});
|
||||
|
||||
// this will display the content of your clipboard at the origin of the domain
|
||||
var clipboardPreview = Overlays.addOverlay("clipboard", {
|
||||
position: { x: 0, y: 0, z: 0},
|
||||
size: 1 / 32,
|
||||
visible: true
|
||||
});
|
||||
|
||||
|
||||
// When our script shuts down, we should clean up all of our overlays
|
||||
function scriptEnding() {
|
||||
|
@ -170,6 +177,7 @@ function scriptEnding() {
|
|||
Overlays.deleteOverlay(solidCube);
|
||||
Overlays.deleteOverlay(sphere);
|
||||
Overlays.deleteOverlay(line3d);
|
||||
Overlays.deleteOverlay(clipboardPreview);
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
|
|
|
@ -26,6 +26,12 @@ function mouseMoveEvent(event) {
|
|||
print("intersection distance=" + intersection.distance);
|
||||
print("intersection intersection.x/y/z=" + intersection.intersection.x + ", "
|
||||
+ intersection.intersection.y + ", " + intersection.intersection.z);
|
||||
|
||||
// also test the getVoxelAt() api which should find and return same voxel
|
||||
|
||||
var voxelAt = Voxels.getVoxelAt(intersection.voxel.x, intersection.voxel.y, intersection.voxel.z, intersection.voxel.s);
|
||||
print("voxelAt.x/y/z/s=" + voxelAt.x + ", " + voxelAt.y + ", " + voxelAt.z + ": " + voxelAt.s);
|
||||
print("voxelAt.red/green/blue=" + voxelAt.red + ", " + voxelAt.green + ", " + voxelAt.blue);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
173
examples/ribbon.js
Normal file
173
examples/ribbon.js
Normal file
|
@ -0,0 +1,173 @@
|
|||
//
|
||||
// ribbon.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Andrzej Kapolka on 2/24/14.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
function vectorMultiply(vector, scalar) {
|
||||
return [ vector[0] * scalar, vector[1] * scalar, vector[2] * scalar ];
|
||||
}
|
||||
|
||||
function vectorAdd(firstVector, secondVector) {
|
||||
return [ firstVector[0] + secondVector[0], firstVector[1] + secondVector[1], firstVector[2] + secondVector[2] ];
|
||||
}
|
||||
|
||||
function vectorSubtract(firstVector, secondVector) {
|
||||
return [ firstVector[0] - secondVector[0], firstVector[1] - secondVector[1], firstVector[2] - secondVector[2] ];
|
||||
}
|
||||
|
||||
function vectorCross(firstVector, secondVector) {
|
||||
return [ firstVector[1] * secondVector[2] - firstVector[2] * secondVector[1],
|
||||
firstVector[2] * secondVector[0] - firstVector[0] * secondVector[2],
|
||||
firstVector[0] * secondVector[1] - firstVector[1] * secondVector[0] ];
|
||||
}
|
||||
|
||||
function vectorLength(vector) {
|
||||
return Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]);
|
||||
}
|
||||
|
||||
function vectorNormalize(vector) {
|
||||
return vectorMultiply(vector, 1.0 / vectorLength(vector));
|
||||
}
|
||||
|
||||
function vectorClone(vector) {
|
||||
return [ vector[0], vector[1], vector[2] ];
|
||||
}
|
||||
|
||||
function mix(first, second, amount) {
|
||||
return first + (second - first) * amount;
|
||||
}
|
||||
|
||||
function vectorMix(firstVector, secondVector, amount) {
|
||||
return vectorAdd(firstVector, vectorMultiply(vectorSubtract(secondVector, firstVector), amount));
|
||||
}
|
||||
|
||||
function randomVector(minVector, maxVector) {
|
||||
return [ mix(minVector[0], maxVector[0], Math.random()), mix(minVector[1], maxVector[1], Math.random()),
|
||||
mix(minVector[2], maxVector[2], Math.random()) ];
|
||||
}
|
||||
|
||||
function applyToLine(start, end, granularity, fn) {
|
||||
// determine the number of steps from the maximum length
|
||||
var steps = Math.max(
|
||||
Math.abs(Math.floor(start[0] / granularity) - Math.floor(end[0] / granularity)),
|
||||
Math.abs(Math.floor(start[1] / granularity) - Math.floor(end[1] / granularity)),
|
||||
Math.abs(Math.floor(start[2] / granularity) - Math.floor(end[2] / granularity)));
|
||||
var position = vectorClone(start);
|
||||
var increment = vectorMultiply(vectorSubtract(end, start), 1.0 / steps);
|
||||
for (var i = 0; i <= steps; i++) {
|
||||
fn(granularity * Math.floor(position[0] / granularity),
|
||||
granularity * Math.floor(position[1] / granularity),
|
||||
granularity * Math.floor(position[2] / granularity),
|
||||
granularity);
|
||||
position = vectorAdd(position, increment);
|
||||
}
|
||||
}
|
||||
|
||||
function drawLine(start, end, color, granularity) {
|
||||
applyToLine(start, end, granularity, function(x, y, z, scale) {
|
||||
Voxels.setVoxel(x, y, z, scale, color[0], color[1], color[2]);
|
||||
});
|
||||
}
|
||||
|
||||
function eraseLine(start, end, granularity) {
|
||||
applyToLine(start, end, granularity, function(x, y, z, scale) {
|
||||
Voxels.eraseVoxel(x, y, z, scale);
|
||||
});
|
||||
}
|
||||
|
||||
function getHueColor(hue) {
|
||||
// see http://en.wikipedia.org/wiki/HSL_and_HSV
|
||||
var hPrime = hue / 60.0;
|
||||
var x = Math.floor(255.0 * (1.0 - Math.abs(hPrime % 2.0 - 1.0)));
|
||||
if (hPrime < 1) {
|
||||
return [255, x, 0];
|
||||
|
||||
} else if (hPrime < 2) {
|
||||
return [x, 255, 0];
|
||||
|
||||
} else if (hPrime < 3) {
|
||||
return [0, 255, x];
|
||||
|
||||
} else if (hPrime < 4) {
|
||||
return [0, x, 255];
|
||||
|
||||
} else if (hPrime < 5) {
|
||||
return [x, 0, 255];
|
||||
|
||||
} else { // hPrime < 6
|
||||
return [255, 0, x];
|
||||
}
|
||||
}
|
||||
|
||||
var UNIT_MIN = [-1.0, -1.0, -1.0];
|
||||
var UNIT_MAX = [1.0, 1.0, 1.0];
|
||||
|
||||
var EPSILON = 0.00001;
|
||||
|
||||
var BOUNDS_MIN = [5.0, 0.0, 5.0];
|
||||
var BOUNDS_MAX = [15.0, 10.0, 15.0];
|
||||
|
||||
var GRANULARITY = 1.0 / 16.0;
|
||||
|
||||
var WIDTH = 0.5;
|
||||
|
||||
var HISTORY_LENGTH = 300;
|
||||
|
||||
var stateHistory = [];
|
||||
var position;
|
||||
var velocity;
|
||||
var hueAngle = 0;
|
||||
var smoothedOffset;
|
||||
|
||||
function step() {
|
||||
if (stateHistory.length === 0) {
|
||||
// start at a random position within the bounds, with a random velocity
|
||||
position = randomVector(BOUNDS_MIN, BOUNDS_MAX);
|
||||
do {
|
||||
velocity = randomVector(UNIT_MIN, UNIT_MAX);
|
||||
} while (vectorLength(velocity) < EPSILON);
|
||||
velocity = vectorMultiply(velocity, GRANULARITY * 0.5 / vectorLength(velocity));
|
||||
smoothedOffset = [0.0, 0.0, 0.0];
|
||||
}
|
||||
|
||||
var right = vectorCross(velocity, [0.0, 1.0, 0.0]);
|
||||
if (vectorLength(right) < EPSILON) {
|
||||
right = [1.0, 0.0, 0.0];
|
||||
} else {
|
||||
right = vectorNormalize(right);
|
||||
}
|
||||
var up = vectorNormalize(vectorCross(right, velocity));
|
||||
var ANGULAR_SPEED = 2.0;
|
||||
var radians = hueAngle * Math.PI * ANGULAR_SPEED / 180.0;
|
||||
var offset = vectorAdd(vectorMultiply(right, WIDTH * Math.cos(radians)), vectorMultiply(up, WIDTH * Math.sin(radians)));
|
||||
var OFFSET_SMOOTHING = 0.9;
|
||||
smoothedOffset = vectorMix(offset, smoothedOffset, OFFSET_SMOOTHING);
|
||||
|
||||
var state = { start: vectorAdd(position, smoothedOffset), end: vectorSubtract(position, smoothedOffset) };
|
||||
drawLine(state.start, state.end, getHueColor(hueAngle), GRANULARITY);
|
||||
stateHistory.push(state);
|
||||
if (stateHistory.length > HISTORY_LENGTH) {
|
||||
var last = stateHistory.shift();
|
||||
eraseLine(last.start, last.end, GRANULARITY);
|
||||
}
|
||||
|
||||
// update position, check against bounds
|
||||
position = vectorAdd(position, velocity);
|
||||
for (var i = 0; i < 3; i++) {
|
||||
if (position[i] < BOUNDS_MIN[i]) {
|
||||
velocity[i] = -velocity[i];
|
||||
position[i] += 2.0 * (BOUNDS_MIN[i] - position[i]);
|
||||
|
||||
} else if (position[i] > BOUNDS_MAX[i]) {
|
||||
velocity[i] = -velocity[i];
|
||||
position[i] += 2.0 * (BOUNDS_MAX[i] - position[i]);
|
||||
}
|
||||
}
|
||||
var MAX_HUE_ANGLE = 360;
|
||||
hueAngle = (hueAngle + 1) % MAX_HUE_ANGLE;
|
||||
}
|
||||
|
||||
Script.willSendVisualDataCallback.connect(step);
|
74
examples/seeingVoxelsExample.js
Normal file
74
examples/seeingVoxelsExample.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// seeingVoxelsExample.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2/26/14
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script
|
||||
//
|
||||
|
||||
var count = 0;
|
||||
var yawDirection = -1;
|
||||
var yaw = 45;
|
||||
var yawMax = 70;
|
||||
var yawMin = 20;
|
||||
|
||||
var isLocal = false;
|
||||
|
||||
// set up our VoxelViewer with a position and orientation
|
||||
var orientation = Quat.fromPitchYawRoll(0, yaw, 0);
|
||||
|
||||
function init() {
|
||||
if (isLocal) {
|
||||
MyAvatar.position = {x: 5000, y: 500, z: 5000};
|
||||
MyAvatar.orientation = orientation;
|
||||
} else {
|
||||
VoxelViewer.setPosition({x: 5000, y: 500, z: 5000});
|
||||
VoxelViewer.setOrientation(orientation);
|
||||
VoxelViewer.queryOctree();
|
||||
Agent.isAvatar = true;
|
||||
}
|
||||
}
|
||||
|
||||
function keepLooking() {
|
||||
//print("count =" + count);
|
||||
|
||||
if (count == 0) {
|
||||
init();
|
||||
}
|
||||
count++;
|
||||
if (count % 10 == 0) {
|
||||
yaw += yawDirection;
|
||||
orientation = Quat.fromPitchYawRoll(0, yaw, 0);
|
||||
if (yaw > yawMax || yaw < yawMin) {
|
||||
yawDirection = yawDirection * -1;
|
||||
}
|
||||
|
||||
print("calling VoxelViewer.queryOctree()... count=" + count + " yaw=" + yaw);
|
||||
|
||||
if (isLocal) {
|
||||
MyAvatar.orientation = orientation;
|
||||
} else {
|
||||
VoxelViewer.setOrientation(orientation);
|
||||
VoxelViewer.queryOctree();
|
||||
print("VoxelViewer.getOctreeElementsCount()=" + VoxelViewer.getOctreeElementsCount());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
print("SCRIPT ENDNG!!!\n");
|
||||
}
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Script.willSendVisualDataCallback.connect(keepLooking);
|
||||
|
||||
// register our scriptEnding callback
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
|
||||
// test for local...
|
||||
Menu.isOptionChecked("Voxels");
|
||||
isLocal = true; // will only get here on local client
|
|
@ -11,6 +11,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake
|
|||
set(FACESHIFT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/faceshift)
|
||||
set(LIBOVR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/LibOVR)
|
||||
set(SIXENSE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/Sixense)
|
||||
set(VISAGE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/visage)
|
||||
|
||||
if (DEFINED ENV{JOB_ID})
|
||||
set(BUILD_SEQ $ENV{JOB_ID})
|
||||
|
@ -40,7 +41,7 @@ if (WIN32)
|
|||
# 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)
|
||||
include_directories(SYSTEM ${GLEW_ROOT_PATH}/include ${GLUT_ROOT_PATH}/include)
|
||||
|
||||
#set(GL_HEADERS "#define GLEW_STATIC\n#define FREEGLUT_STATIC\n#define FREEGLUT_LIB_PRAGMAS 0\n#include <GL/glew.h>\n#include <GL/wglew.h>\n#include <GL/freeglut_std.h>\n#include <GL/freeglut_ext.h>")
|
||||
set(GL_HEADERS "#define GLEW_STATIC\n#include <windowshacks.h>\n#include <GL/glew.h>\n#include <GL/glut.h>")
|
||||
|
@ -138,9 +139,10 @@ find_package(Faceshift)
|
|||
find_package(GLM REQUIRED)
|
||||
find_package(LibOVR)
|
||||
find_package(Sixense)
|
||||
find_package(Visage)
|
||||
find_package(ZLIB)
|
||||
|
||||
# likewise with Sixense library for Razer Hydra
|
||||
# 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})
|
||||
|
@ -150,6 +152,21 @@ if (SIXENSE_FOUND AND NOT DISABLE_SIXENSE)
|
|||
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)
|
||||
|
|
14
interface/external/visage/readme.txt
vendored
Normal file
14
interface/external/visage/readme.txt
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
Instructions for adding the Visage driver to Interface
|
||||
Andrzej Kapolka, February 11, 2014
|
||||
|
||||
1. Copy the Visage sdk folders (lib, include, dependencies) into the interface/external/visage folder.
|
||||
This readme.txt should be there as well.
|
||||
|
||||
2. Copy the Visage configuration data folder (visageSDK-MacOS/Samples/MacOSX/data/) to interface/resources/visage
|
||||
(i.e., so that interface/resources/visage/candide3.wfm is accessible)
|
||||
|
||||
3. Copy the Visage license file to interface/resources/visage/license.vlc.
|
||||
|
||||
4. Delete your build directory, run cmake and build, and you should be all set.
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue