diff --git a/CMakeLists.txt b/CMakeLists.txt index c68efdf446..7f05548ec4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -179,6 +179,7 @@ option(GET_SOXR "Get Soxr library automatically as external project" 1) option(GET_TBB "Get Threading Building Blocks library automatically as external project" 1) option(GET_LIBOVR "Get LibOVR library automatically as external project" 1) option(GET_VHACD "Get V-HACD library automatically as external project" 1) +option(GET_POLYVOX "Get polyvox library automatically as external project" 1) option(GET_OPENVR "Get OpenVR library automatically as external project" 1) option(GET_BOOSTCONFIG "Get Boost-config library automatically as external project" 1) option(GET_OGLPLUS "Get OGLplus library automatically as external project" 1) diff --git a/cmake/externals/tbb/OSXTBBInstallNameChange.cmake b/cmake/externals/OSXInstallNameChange.cmake similarity index 53% rename from cmake/externals/tbb/OSXTBBInstallNameChange.cmake rename to cmake/externals/OSXInstallNameChange.cmake index 0fa377959b..4922f2f8a5 100644 --- a/cmake/externals/tbb/OSXTBBInstallNameChange.cmake +++ b/cmake/externals/OSXInstallNameChange.cmake @@ -1,6 +1,6 @@ # -# OSXTBBInstallNameChange.cmake -# cmake/externals/tbb +# OSXInstallNameChange.cmake +# cmake/macros # # Copyright 2015 High Fidelity, Inc. # Created by Stephen Birarda on February 20, 2014 @@ -10,36 +10,33 @@ # # first find the so files in the source dir -set(_TBB_LIBRARY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib) -file(GLOB_RECURSE _TBB_LIBRARIES "${_TBB_LIBRARY_DIR}/*.dylib") +message("INSTALL_NAME_LIBRARY_DIR ${INSTALL_NAME_LIBRARY_DIR}") -# raise an error if we found none -if (NOT _TBB_LIBRARIES) - message(FATAL_ERROR "Did not find any TBB libraries") +file(GLOB_RECURSE _LIBRARIES "${INSTALL_NAME_LIBRARY_DIR}/*.dylib") + +if (NOT _LIBRARIES) + message(FATAL_ERROR "OSXInstallNameChange -- no libraries found: ${INSTALL_NAME_LIBRARY_DIR}") endif () # find the install_name_tool command find_program(INSTALL_NAME_TOOL_COMMAND NAMES install_name_tool DOC "Path to the install_name_tool command") -# find the lipo command -find_program(LIPO_COMMAND NAMES lipo DOC "Path to the lipo command") - # enumerate the libraries -foreach(_TBB_LIBRARY ${_TBB_LIBRARIES}) - get_filename_component(_TBB_LIBRARY_FILENAME ${_TBB_LIBRARY} NAME) +foreach(_LIBRARY ${_LIBRARIES}) + get_filename_component(_LIBRARY_FILENAME ${_LIBRARY} NAME) - set(_INSTALL_NAME_ARGS ${INSTALL_NAME_TOOL_COMMAND} -id ${_TBB_LIBRARY} ${_TBB_LIBRARY_FILENAME}) + set(_INSTALL_NAME_ARGS ${INSTALL_NAME_TOOL_COMMAND} -id ${_LIBRARY} ${_LIBRARY_FILENAME}) message(STATUS "${INSTALL_NAME_COMMAND} ${_INSTALL_NAME_ARGS}") execute_process( COMMAND ${INSTALL_NAME_COMMAND} ${_INSTALL_NAME_ARGS} - WORKING_DIRECTORY ${_TBB_LIBRARY_DIR} + WORKING_DIRECTORY ${INSTALL_NAME_LIBRARY_DIR} ERROR_VARIABLE _INSTALL_NAME_ERROR ) if (_INSTALL_NAME_ERROR) - message(FATAL_ERROR "There was an error changing install name for ${_TBB_LIBRARY_FILENAME} - ${_INSTALL_NAME_ERROR}") + message(FATAL_ERROR "There was an error changing install name for ${_LIBRARY_FILENAME} - ${_INSTALL_NAME_ERROR}") endif () endforeach() diff --git a/cmake/externals/polyvox/CMakeLists.txt b/cmake/externals/polyvox/CMakeLists.txt new file mode 100644 index 0000000000..76302f9b4a --- /dev/null +++ b/cmake/externals/polyvox/CMakeLists.txt @@ -0,0 +1,57 @@ +set(EXTERNAL_NAME polyvox) + +include(ExternalProject) +ExternalProject_Add( + ${EXTERNAL_NAME} + URL http://hifi-public.s3.amazonaws.com/dependencies/polyvox-master-2015-5-27.zip + URL_MD5 e3dd09a24df4db29ba370e3bea753388 + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= + BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build + LOG_DOWNLOAD 1 + LOG_CONFIGURE 1 + LOG_BUILD 1 +) + + +ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) + +if (APPLE) + set(INSTALL_NAME_LIBRARY_DIR ${INSTALL_DIR}/lib) + message(STATUS "in polyvox INSTALL_NAME_LIBRARY_DIR ${INSTALL_NAME_LIBRARY_DIR}") + ExternalProject_Add_Step( + ${EXTERNAL_NAME} + change-install-name + COMMENT "Calling install_name_tool on libraries to fix install name for dylib linking" + COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${INSTALL_NAME_LIBRARY_DIR} -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake + DEPENDEES install + WORKING_DIRECTORY + LOG 1 + ) +endif () + + +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) + +if (WIN32) + set(${EXTERNAL_NAME_UPPER}_CORE_INCLUDE_DIRS ${INSTALL_DIR}/PolyVoxCore/include CACHE FILEPATH + "Path to polyvox core include directory") + set(${EXTERNAL_NAME_UPPER}_UTIL_INCLUDE_DIRS ${INSTALL_DIR}/PolyVoxUtil/include CACHE FILEPATH + "Path to polyvox util include directory") +else () + set(${EXTERNAL_NAME_UPPER}_CORE_INCLUDE_DIRS ${INSTALL_DIR}/include/PolyVoxCore CACHE FILEPATH + "Path to polyvox core include directory") + set(${EXTERNAL_NAME_UPPER}_UTIL_INCLUDE_DIRS ${INSTALL_DIR}/include/PolyVoxUtil CACHE FILEPATH + "Path to polyvox util include directory") +endif () + + +if (WIN32) + set(${EXTERNAL_NAME_UPPER}_CORE_LIBRARY ${INSTALL_DIR}/PolyVoxCore/lib/PolyVoxCore.lib CACHE FILEPATH "polyvox core library") +# set(${EXTERNAL_NAME_UPPER}_UTIL_LIBRARY ${INSTALL_DIR}/PolyVoxUtil/lib/PolyVoxUtil.lib CACHE FILEPATH "polyvox util library") +elseif (APPLE) +set(${EXTERNAL_NAME_UPPER}_CORE_LIBRARY ${INSTALL_DIR}/lib/libPolyVoxCore.dylib CACHE FILEPATH "polyvox core library") +# set(${EXTERNAL_NAME_UPPER}_UTIL_LIBRARY ${INSTALL_DIR}/lib/libPolyVoxUtil.dylib CACHE FILEPATH "polyvox util library") +else () + set(${EXTERNAL_NAME_UPPER}_CORE_LIBRARY ${INSTALL_DIR}/lib/libPolyVoxCore.so CACHE FILEPATH "polyvox core library") +# set(${EXTERNAL_NAME_UPPER}_UTIL_LIBRARY ${INSTALL_DIR}/lib/libPolyVoxUtil.so CACHE FILEPATH "polyvox util library") +endif () diff --git a/cmake/externals/tbb/CMakeLists.txt b/cmake/externals/tbb/CMakeLists.txt index b9b2b65010..8f327bd69f 100644 --- a/cmake/externals/tbb/CMakeLists.txt +++ b/cmake/externals/tbb/CMakeLists.txt @@ -66,7 +66,7 @@ if (APPLE) ${EXTERNAL_NAME} change-install-name COMMENT "Calling install_name_tool on TBB libraries to fix install name for dylib linking" - COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/OSXTBBInstallNameChange.cmake + COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_TBB_LIB_DIR} -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake DEPENDEES install WORKING_DIRECTORY LOG 1 @@ -115,4 +115,4 @@ endif () if (DEFINED ${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE) set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE "List of tbb include directories") -endif () \ No newline at end of file +endif () diff --git a/cmake/modules/FindPolyVox.cmake b/cmake/modules/FindPolyVox.cmake new file mode 100644 index 0000000000..60a639e87c --- /dev/null +++ b/cmake/modules/FindPolyVox.cmake @@ -0,0 +1,54 @@ +# +# FindPolyvox.cmake +# +# Try to find the libpolyvox resampling library +# +# You can provide a LIBPOLYVOX_ROOT_DIR which contains lib and include directories +# +# Once done this will define +# +# POLYVOX_FOUND - system found libpolyvox +# POLYVOX_INCLUDE_DIRS - the libpolyvox include directory +# POLYVOX_LIBRARIES - link to this to use libpolyvox +# +# Created on 1/22/2015 by Stephen Birarda +# Copyright 2015 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# + +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("polyvox") + +find_path(POLYVOX_CORE_INCLUDE_DIRS PolyVoxCore/SimpleVolume.h PATH_SUFFIXES include include/PolyVoxCore HINTS ${POLYVOX_SEARCH_DIRS}) +# find_path(POLYVOX_UTIL_INCLUDE_DIRS PolyVoxUtil/Serialization.h PATH_SUFFIXES include include/PolyVoxUtil HINTS ${POLYVOX_SEARCH_DIRS}) + +find_library(POLYVOX_CORE_LIBRARY NAMES PolyVoxCore PATH_SUFFIXES lib HINTS ${POLYVOX_SEARCH_DIRS}) +# find_library(POLYVOX_UTIL_LIBRARY NAMES PolyVoxUtil PATH_SUFFIXES lib HINTS ${POLYVOX_SEARCH_DIRS}) + + +# if (WIN32) +# find_path(POLYVOX_DLL_PATH polyvox.dll PATH_SUFFIXES bin HINTS ${POLYVOX_SEARCH_DIRS}) +# endif() + +# set(POLYVOX_REQUIREMENTS POLYVOX_CORE_INCLUDE_DIRS POLYVOX_UTIL_INCLUDE_DIRS POLYVOX_CORE_LIBRARY POLYVOX_UTIL_LIBRARY) +set(POLYVOX_REQUIREMENTS POLYVOX_CORE_INCLUDE_DIRS POLYVOX_CORE_LIBRARY) +# if (WIN32) +# list(APPEND POLYVOX_REQUIREMENTS POLYVOX_DLL_PATH) +# endif () + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Polyvox DEFAULT_MSG ${POLYVOX_REQUIREMENTS}) + +# if (WIN32) +# add_paths_to_fixup_libs(${POLYVOX_DLL_PATH}) +# endif () + +# set(POLYVOX_INCLUDE_DIRS ${POLYVOX_CORE_INCLUDE_DIRS} ${POLYVOX_UTIL_INCLUDE_DIRS}) +# set(POLYVOX_LIBRARIES ${POLYVOX_CORE_LIBRARY} ${POLYVOX_UTIL_LIBRARY}) + +set(POLYVOX_INCLUDE_DIRS ${POLYVOX_CORE_INCLUDE_DIRS}) +set(POLYVOX_LIBRARIES ${POLYVOX_CORE_LIBRARY}) + +mark_as_advanced(POLYVOX_INCLUDE_DIRS POLYVOX_LIBRARIES POLYVOX_SEARCH_DIRS) diff --git a/examples/blockWorld.js b/examples/blockWorld.js index e69de29bb2..53809612ed 100644 --- a/examples/blockWorld.js +++ b/examples/blockWorld.js @@ -0,0 +1,201 @@ +// blockWorld.js +// examples +// +// Created by Eric Levin on May 26, 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Creates a floor of tiles and then drops planky blocks at random points above the tile floor +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var TILE_SIZE = 7 +var GENERATE_INTERVAL = 50; +var NUM_ROWS = 10; +var angVelRange = 4; + +var floorTiles = []; +var blocks = []; +var blockSpawner; + +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + + +var floorPos = Vec3.sum(MyAvatar.position, { + x: 0, + y: -2, + z: 0 +}); +var x = floorPos.x; + +var currentRowIndex = 0; +var currentColumnIndex = 0; + +var DROP_HEIGHT = floorPos.y + 5; +var BLOCK_GRAVITY = { + x: 0, + y: -9, + z: 0 +}; +var BLOCK_SIZE = { + x: 0.2, + y: 0.1, + z: 0.8 +}; + +var bounds = { + xMin: floorPos.x, + xMax: floorPos.x + (TILE_SIZE * NUM_ROWS) - TILE_SIZE, + zMin: floorPos.z, + zMax: floorPos.z + (TILE_SIZE * NUM_ROWS) - TILE_SIZE +}; + +var screenSize = Controller.getViewportDimensions(); + +var BUTTON_SIZE = 32; +var PADDING = 3; + +var offButton = Overlays.addOverlay("image", { + x: screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING, + y: screenSize.y - (BUTTON_SIZE + PADDING), + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: HIFI_PUBLIC_BUCKET + "images/close.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1 +}); + +var deleteButton = Overlays.addOverlay("image", { + x: screenSize.x / 2 - BUTTON_SIZE, + y: screenSize.y - (BUTTON_SIZE + PADDING), + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: HIFI_PUBLIC_BUCKET + "images/delete.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1 +}); + + +function generateFloor() { + for (var z = floorPos.z; currentColumnIndex < NUM_ROWS; z += TILE_SIZE, currentColumnIndex++) { + floorTiles.push(Entities.addEntity({ + type: 'Box', + position: { + x: x, + y: floorPos.y, + z: z + }, + dimensions: { + x: TILE_SIZE, + y: 2, + z: TILE_SIZE + }, + color: { + red: randFloat(70, 120), + green: randFloat(70, 71), + blue: randFloat(70, 80) + }, + // collisionsWillMove: true + })); + } + + currentRowIndex++; + if (currentRowIndex < NUM_ROWS) { + currentColumnIndex = 0; + x += TILE_SIZE; + Script.setTimeout(generateFloor, GENERATE_INTERVAL); + } else { + //Once we're done generating floor, drop planky blocks at random points on floor + blockSpawner = Script.setInterval(function() { + dropBlock(); + }, GENERATE_INTERVAL) + } +} + +function dropBlock() { + var dropPos = floorPos; + dropPos.y = DROP_HEIGHT; + dropPos.x = randFloat(bounds.xMin, bounds.xMax); + dropPos.z = randFloat(bounds.zMin, bounds.zMax); + blocks.push(Entities.addEntity({ + type: "Model", + modelURL: 'http://s3.amazonaws.com/hifi-public/marketplace/hificontent/Games/blocks/block.fbx', + shapeType: 'box', + position: dropPos, + dimensions: BLOCK_SIZE, + collisionsWillMove: true, + gravity: { + x: 0, + y: -9, + z: 0 + }, + velocity: { + x: 0, + y: .1, + z: 0 + }, + angularVelocity: { + x: randFloat(-angVelRange, angVelRange), + y: randFloat(-angVelRange, angVelRange), + z: randFloat(-angVelRange, angVelRange), + } + })); +} + +function mousePressEvent(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + if (clickedOverlay == offButton) { + Script.clearInterval(blockSpawner); + } + if(clickedOverlay == deleteButton){ + destroyStuff(); + } +} + +generateFloor(); + +function cleanup() { + // for (var i = 0; i < floorTiles.length; i++) { + // Entities.deleteEntity(floorTiles[i]); + // } + // for (var i = 0; i < blocks.length; i++) { + // Entities.deleteEntity(blocks[i]); + // } + Overlays.deleteOverlay(offButton); + Overlays.deleteOverlay(deleteButton) + Script.clearInterval(blockSpawner); +} + +function destroyStuff() { + for (var i = 0; i < floorTiles.length; i++) { + Entities.deleteEntity(floorTiles[i]); + } + for (var i = 0; i < blocks.length; i++) { + Entities.deleteEntity(blocks[i]); + } + Script.clearInterval(blockSpawner); + +} + +function randFloat(low, high) { + return Math.floor(low + Math.random() * (high - low)); +} + +function map(value, min1, max1, min2, max2) { + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); +} + +Script.scriptEnding.connect(cleanup); +Controller.mousePressEvent.connect(mousePressEvent); \ No newline at end of file diff --git a/examples/pointer.js b/examples/pointer.js index eebe4ec5be..32698209a4 100644 --- a/examples/pointer.js +++ b/examples/pointer.js @@ -67,20 +67,6 @@ var pointerButton = Overlays.addOverlay("image", { var center = Vec3.sum(MyAvatar.position, Vec3.multiply(2.0, Quat.getFront(Camera.getOrientation()))); center.y += 0.5; -var whiteBoard = Entities.addEntity({ - type: "Box", - position: center, - dimensions: { - x: 1, - y: 1, - z: .001 - }, - color: { - red: 255, - green: 255, - blue: 255 - } -}); function calculateNearLinePosition(targetPosition) { var handPosition = MyAvatar.getRightPalmPosition(); @@ -243,7 +229,6 @@ function keyReleaseEvent(event) { } function cleanup() { - Entities.deleteEntity(whiteBoard); for (var i = 0; i < strokes.length; i++) { Entities.deleteEntity(strokes[i]); } diff --git a/examples/voxels.js b/examples/voxels.js new file mode 100644 index 0000000000..e274f1c4cc --- /dev/null +++ b/examples/voxels.js @@ -0,0 +1,43 @@ + +var controlHeld = false; + + +function mousePressEvent(event) { + if (!event.isLeftButton) { + return; + } + + var pickRay = Camera.computePickRay(event.x, event.y); + var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking + // var props = Entities.getEntityProperties(intersection.entityID); + if (intersection.intersects) { + var ids = Entities.findEntities(intersection.intersection, 10); + for (var i = 0; i < ids.length; i++) { + var id = ids[i]; + if (controlHeld) { + Entities.setVoxelSphere(id, intersection.intersection, 1.2, 0); + } else { + Entities.setVoxelSphere(id, intersection.intersection, 1.2, 255); + } + } + } +} + + +function keyPressEvent(event) { + if (event.text == "CONTROL") { + controlHeld = true; + } +} + + +function keyReleaseEvent(event) { + if (event.text == "CONTROL") { + controlHeld = false; + } +} + + +Controller.mousePressEvent.connect(mousePressEvent); +Controller.keyPressEvent.connect(keyPressEvent); +Controller.keyReleaseEvent.connect(keyReleaseEvent); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ec80f14bce..0fbea37d9a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1948,7 +1948,9 @@ FaceTracker* Application::getSelectedFaceTracker() { } void Application::setActiveFaceTracker() { +#if defined(HAVE_FACESHIFT) || defined(HAVE_DDE) bool isMuted = Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking); +#endif #ifdef HAVE_FACESHIFT auto faceshiftTracker = DependencyManager::get(); faceshiftTracker->setIsMuted(isMuted); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index c65de2afb0..8a64630266 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -356,7 +356,7 @@ void ApplicationOverlay::displayOverlayTextureHmd(Camera& whichCamera) { }); if (!Application::getInstance()->isMouseHidden()) { - renderPointersOculus(myAvatar->getDefaultEyePosition()); + renderPointersOculus(); } glDepthMask(GL_TRUE); glDisable(GL_TEXTURE_2D); @@ -486,49 +486,40 @@ void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origi direction = glm::normalize(intersectionWithUi - origin); } +glm::vec2 getPolarCoordinates(const PalmData& palm) { + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + auto avatarOrientation = myAvatar->getOrientation(); + auto eyePos = myAvatar->getDefaultEyePosition(); + glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm); + // Direction of the tip relative to the eye + glm::vec3 tipDirection = tip - eyePos; + // orient into avatar space + tipDirection = glm::inverse(avatarOrientation) * tipDirection; + // Normalize for trig functions + tipDirection = glm::normalize(tipDirection); + // Convert to polar coordinates + glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y)); + return polar; +} + //Caculate the click location using one of the sixense controllers. Scale is not applied QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const { - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - - glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm); - glm::vec3 eyePos = myAvatar->getHead()->getEyePosition(); - glm::quat invOrientation = glm::inverse(myAvatar->getOrientation()); - //direction of ray goes towards camera - glm::vec3 dir = invOrientation * glm::normalize(qApp->getCamera()->getPosition() - tip); - glm::vec3 tipPos = invOrientation * (tip - eyePos); - QPoint rv; auto canvasSize = qApp->getCanvasSize(); if (qApp->isHMDMode()) { float t; - - //We back the ray up by dir to ensure that it will not start inside the UI. - glm::vec3 adjustedPos = tipPos - dir; - //Find intersection of crosshair ray. - if (raySphereIntersect(dir, adjustedPos, _oculusUIRadius * myAvatar->getScale(), &t)){ - glm::vec3 collisionPos = adjustedPos + dir * t; - //Normalize it in case its not a radius of 1 - collisionPos = glm::normalize(collisionPos); - //If we hit the back hemisphere, mark it as not a collision - if (collisionPos.z > 0) { - rv.setX(INT_MAX); - rv.setY(INT_MAX); - } else { - - float u = asin(collisionPos.x) / (_textureFov)+0.5f; - float v = 1.0 - (asin(collisionPos.y) / (_textureFov)+0.5f); - - rv.setX(u * canvasSize.x); - rv.setY(v * canvasSize.y); - } - } else { - //if they did not click on the overlay, just set the coords to INT_MAX - rv.setX(INT_MAX); - rv.setY(INT_MAX); - } + glm::vec2 polar = getPolarCoordinates(*palm); + glm::vec2 point = sphericalToScreen(-polar); + rv.rx() = point.x; + rv.ry() = point.y; } else { + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); glm::dmat4 projection; qApp->getProjectionMatrix(&projection); + glm::quat invOrientation = glm::inverse(myAvatar->getOrientation()); + glm::vec3 eyePos = myAvatar->getDefaultEyePosition(); + glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm); + glm::vec3 tipPos = invOrientation * (tip - eyePos); glm::vec4 clipSpacePos = glm::vec4(projection * glm::dvec4(tipPos, 1.0)); glm::vec3 ndcSpacePos; @@ -729,7 +720,7 @@ void ApplicationOverlay::renderControllerPointers() { } } -void ApplicationOverlay::renderPointersOculus(const glm::vec3& eyePos) { +void ApplicationOverlay::renderPointersOculus() { glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); glDisable(GL_DEPTH_TEST); glMatrixMode(GL_MODELVIEW); @@ -737,16 +728,12 @@ void ApplicationOverlay::renderPointersOculus(const glm::vec3& eyePos) { //Controller Pointers MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { - PalmData& palm = myAvatar->getHand()->getPalms()[i]; if (palm.isActive()) { - glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm); - glm::vec3 tipDirection = glm::normalize(glm::inverse(myAvatar->getOrientation()) * (tip - eyePos)); - float pitch = -glm::asin(tipDirection.y); - float yawSign = glm::sign(-tipDirection.x); - float yaw = glm::acos(-tipDirection.z) * - ((yawSign == 0.0f) ? 1.0f : yawSign); - glm::quat orientation = glm::quat(glm::vec3(pitch, yaw, 0.0f)); + glm::vec2 polar = getPolarCoordinates(palm); + // Convert to quaternion + glm::quat orientation = glm::quat(glm::vec3(polar.y, -polar.x, 0.0f)); + // Render reticle at location renderReticle(orientation, _alpha); } } diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 34beb98682..864bd95b4e 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -102,7 +102,7 @@ private: void renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder); void renderControllerPointers(); - void renderPointersOculus(const glm::vec3& eyePos); + void renderPointersOculus(); void renderAudioMeter(); void renderCameraToggle(); diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index 6c7fa04a21..a94bfb5f97 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -12,4 +12,9 @@ find_package(Bullet REQUIRED) target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${BULLET_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES}) +add_dependency_external_projects(polyvox) +find_package(PolyVox REQUIRED) +target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${POLYVOX_INCLUDE_DIRS}) +target_link_libraries(${TARGET_NAME} ${POLYVOX_LIBRARIES}) + link_hifi_libraries(shared gpu script-engine render-utils) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 4a4fd4a0d7..dea184087b 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -39,6 +39,7 @@ #include "RenderableWebEntityItem.h" #include "RenderableZoneEntityItem.h" #include "RenderableLineEntityItem.h" +#include "RenderablePolyVoxEntityItem.h" #include "EntitiesRendererLogging.h" EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, @@ -65,6 +66,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf REGISTER_ENTITY_TYPE_WITH_FACTORY(ParticleEffect, RenderableParticleEffectEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Zone, RenderableZoneEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Line, RenderableLineEntityItem::factory) + REGISTER_ENTITY_TYPE_WITH_FACTORY(PolyVox, RenderablePolyVoxEntityItem::factory) _currentHoverOverEntityID = UNKNOWN_ENTITY_ID; _currentClickingOnEntityID = UNKNOWN_ENTITY_ID; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp new file mode 100644 index 0000000000..977a6511c8 --- /dev/null +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -0,0 +1,336 @@ +// +// RenderablePolyVoxEntityItem.cpp +// libraries/entities-renderer/src/ +// +// Created by Seth Alves on 5/19/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + + +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "model/Geometry.h" +#include "gpu/GLBackend.h" +#include "EntityTreeRenderer.h" +#include "RenderablePolyVoxEntityItem.h" + + +EntityItemPointer RenderablePolyVoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + return EntityItemPointer(new RenderablePolyVoxEntityItem(entityID, properties)); +} + +RenderablePolyVoxEntityItem::~RenderablePolyVoxEntityItem() { + delete _volData; +} + +void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { + + if (_volData && voxelVolumeSize == _voxelVolumeSize) { + return; + } + + qDebug() << "resetting voxel-space size"; + + PolyVoxEntityItem::setVoxelVolumeSize(voxelVolumeSize); + + if (_volData) { + delete _volData; + } + + PolyVox::Vector3DInt32 lowCorner(0, 0, 0); + PolyVox::Vector3DInt32 highCorner(_voxelVolumeSize[0] - 1, // -1 because these corners are inclusive + _voxelVolumeSize[1] - 1, + _voxelVolumeSize[2] - 1); + + _volData = new PolyVox::SimpleVolume(PolyVox::Region(lowCorner, highCorner)); +} + + +void RenderablePolyVoxEntityItem::setVoxelData(QByteArray voxelData) { + if (voxelData == _voxelData) { + return; + } + PolyVoxEntityItem::setVoxelData(voxelData); + decompressVolumeData(); +} + + +glm::mat4 RenderablePolyVoxEntityItem::voxelToWorldMatrix() const { + glm::vec3 scale = _dimensions / _voxelVolumeSize; // meters / voxel-units + glm::mat4 scaled = glm::scale(glm::mat4(), scale); + glm::mat4 centerToCorner = glm::translate(scaled, _voxelVolumeSize / -2.0f); + glm::mat4 rotation = glm::mat4_cast(_rotation); + glm::mat4 translation = glm::translate(getCenter()); + return translation * rotation * centerToCorner; +} + +glm::mat4 RenderablePolyVoxEntityItem::worldToVoxelMatrix() const { + glm::mat4 worldToModelMatrix = glm::inverse(voxelToWorldMatrix()); + return worldToModelMatrix; + +} + +void RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue) { + // This three-level for loop iterates over every voxel in the volume + for (int z = 0; z < _volData->getDepth(); z++) { + for (int y = 0; y < _volData->getHeight(); y++) { + for (int x = 0; x < _volData->getWidth(); x++) { + // Store our current position as a vector... + glm::vec3 pos(x, y, z); + // And compute how far the current position is from the center of the volume + float fDistToCenter = glm::distance(pos, center); + // If the current voxel is less than 'radius' units from the center then we make it solid. + if (fDistToCenter <= radius) { + _volData->setVoxelAt(x, y, z, toValue); + } + } + } + } + compressVolumeData(); + _needsModelReload = true; +} + +void RenderablePolyVoxEntityItem::setSphere(glm::vec3 centerWorldCoords, float radiusWorldCoords, uint8_t toValue) { + // glm::vec3 centerVoxelCoords = worldToVoxelCoordinates(centerWorldCoords); + glm::vec4 centerVoxelCoords = worldToVoxelMatrix() * glm::vec4(centerWorldCoords, 1.0f); + glm::vec3 scale = _dimensions / _voxelVolumeSize; // meters / voxel-units + float scaleY = scale[0]; + float radiusVoxelCoords = radiusWorldCoords / scaleY; + setSphereInVolume(glm::vec3(centerVoxelCoords), radiusVoxelCoords, toValue); +} + +void RenderablePolyVoxEntityItem::getModel() { + if (!_volData) { + // this will cause the allocation of _volData + setVoxelVolumeSize(_voxelVolumeSize); + } + + // A mesh object to hold the result of surface extraction + PolyVox::SurfaceMesh polyVoxMesh; + + switch (_voxelSurfaceStyle) { + case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: { + PolyVox::MarchingCubesSurfaceExtractor> surfaceExtractor + (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); + surfaceExtractor.execute(); + break; + } + case PolyVoxEntityItem::SURFACE_CUBIC: { + PolyVox::CubicSurfaceExtractorWithNormals> surfaceExtractor + (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); + surfaceExtractor.execute(); + break; + } + } + + // convert PolyVox mesh to a Sam mesh + model::Mesh* mesh = new model::Mesh(); + model::MeshPointer meshPtr(mesh); + + const std::vector& vecIndices = polyVoxMesh.getIndices(); + auto indexBuffer = new gpu::Buffer(vecIndices.size() * sizeof(uint32_t), (gpu::Byte*)vecIndices.data()); + auto indexBufferPtr = gpu::BufferPointer(indexBuffer); + mesh->setIndexBuffer(gpu::BufferView(indexBufferPtr, gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW))); + + + const std::vector& vecVertices = polyVoxMesh.getVertices(); + auto vertexBuffer = new gpu::Buffer(vecVertices.size() * sizeof(PolyVox::PositionMaterialNormal), + (gpu::Byte*)vecVertices.data()); + auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer); + mesh->setVertexBuffer(gpu::BufferView(vertexBufferPtr, + 0, + vertexBufferPtr->getSize() - sizeof(float) * 3, + sizeof(PolyVox::PositionMaterialNormal), + gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); + mesh->addAttribute(gpu::Stream::NORMAL, + gpu::BufferView(vertexBufferPtr, + sizeof(float) * 3, + vertexBufferPtr->getSize() - sizeof(float) * 3, + sizeof(PolyVox::PositionMaterialNormal), + gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); + + // auto normalAttrib = mesh->getAttributeBuffer(gpu::Stream::NORMAL); + // for (auto normal = normalAttrib.begin(); normal != normalAttrib.end(); normal++) { + // (*normal) = -(*normal); + // } + + qDebug() << "---- vecIndices.size() =" << vecIndices.size(); + qDebug() << "---- vecVertices.size() =" << vecVertices.size(); + + _modelGeometry.setMesh(meshPtr); + _needsModelReload = false; +} + +void RenderablePolyVoxEntityItem::render(RenderArgs* args) { + PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render"); + assert(getType() == EntityTypes::PolyVox); + + if (_needsModelReload) { + getModel(); + } + + glm::vec3 position = getPosition(); + glm::vec3 dimensions = getDimensions(); + glm::vec3 scale = dimensions / _voxelVolumeSize; + glm::vec3 center = getCenter(); + glm::quat rotation = getRotation(); + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glm::vec3 positionToCenter = center - position; + // make the rendered voxel volume be centered on the entity's position + positionToCenter -= _dimensions * glm::vec3(0.5f,0.5f,0.5f); + glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); + glScalef(scale.x, scale.y, scale.z); + + auto mesh = _modelGeometry.getMesh(); + gpu::Batch batch; + batch.setInputFormat(mesh->getVertexFormat()); + batch.setInputBuffer(gpu::Stream::POSITION, mesh->getVertexBuffer()); + batch.setInputBuffer(gpu::Stream::NORMAL, + mesh->getVertexBuffer()._buffer, + sizeof(float) * 3, + mesh->getVertexBuffer()._stride); + batch.setIndexBuffer(gpu::UINT32, mesh->getIndexBuffer()._buffer, 0); + batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0); + gpu::GLBackend::renderBatch(batch); + glPopMatrix(); + RenderableDebugableEntityItem::render(this, args); +} + +class RaycastFunctor +{ +public: + RaycastFunctor() : _result(glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)) { } + bool operator()(PolyVox::SimpleVolume::Sampler& sampler) + { + if (sampler.getVoxel() == 0) { + return true; // keep raycasting + } + PolyVox::Vector3DInt32 positionIndex = sampler.getPosition(); + _result = glm::vec4(positionIndex.getX(), positionIndex.getY(), positionIndex.getZ(), 1.0f); + return false; + } + glm::vec4 _result; +}; + +bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& origin, + const glm::vec3& direction, + bool& keepSearching, + OctreeElement*& element, + float& distance, BoxFace& face, + void** intersectedObject, + bool precisionPicking) const +{ + if (_needsModelReload || !precisionPicking) { + // just intersect with bounding box + return true; + } + + glm::mat4 wtvMatrix = worldToVoxelMatrix(); + glm::vec3 farPoint = origin + direction; + glm::vec4 originInVoxel = wtvMatrix * glm::vec4(origin, 1.0f); + glm::vec4 farInVoxel = wtvMatrix * glm::vec4(farPoint, 1.0f); + glm::vec4 directionInVoxel = farInVoxel - originInVoxel; + + PolyVox::Vector3DFloat start(originInVoxel[0], originInVoxel[1], originInVoxel[2]); + PolyVox::Vector3DFloat pvDirection(directionInVoxel[0], directionInVoxel[1], directionInVoxel[2]); + pvDirection.normalise(); + + // the PolyVox ray intersection code requires a near and far point. + glm::vec3 scale = _dimensions / _voxelVolumeSize; // meters / voxel-units + float distanceToEntity = glm::distance(origin, _position); + float largestDimension = glm::max(_dimensions[0], _dimensions[1], _dimensions[2]); + // set ray cast length to long enough to cover all of the voxel space + pvDirection *= (distanceToEntity + largestDimension) / glm::min(scale[0], scale[1], scale[2]); + + PolyVox::RaycastResult raycastResult; + RaycastFunctor callback; + raycastResult = PolyVox::raycastWithDirection(_volData, start, pvDirection, callback); + + if (raycastResult == PolyVox::RaycastResults::Completed) { + // the ray completed its path -- nothing was hit. + return false; + } + + glm::vec4 intersectedWorldPosition = voxelToWorldMatrix() * callback._result; + + distance = glm::distance(glm::vec3(intersectedWorldPosition), origin); + + face = BoxFace::MIN_X_FACE; // XXX + + return true; +} + + +// compress the data in _volData and save the results. The compressed form is used during +// saves to disk and for transmission over the wire +void RenderablePolyVoxEntityItem::compressVolumeData() { + int rawSize = _volData->getDepth() * _volData->getHeight() * _volData->getWidth(); + QByteArray uncompressedData = QByteArray(rawSize, '\0'); + + for (int z = 0; z < _volData->getDepth(); z++) { + for (int y = 0; y < _volData->getHeight(); y++) { + for (int x = 0; x < _volData->getWidth(); x++) { + uint8_t uVoxelValue = _volData->getVoxelAt(x, y, z); + int uncompressedIndex = z * _volData->getHeight() * _volData->getWidth() + y * _volData->getWidth() + x; + uncompressedData[uncompressedIndex] = uVoxelValue; + } + } + } + + QByteArray newVoxelData = qCompress(uncompressedData, 9); + // HACK -- until we have a way to allow for properties larger than MTU, don't update. + if (newVoxelData.length() < 1200) { + _voxelData = newVoxelData; + qDebug() << "-------------- voxel compresss --------------"; + qDebug() << "raw-size =" << rawSize << " compressed-size =" << newVoxelData.size(); + } else { + qDebug() << "voxel data too large, reverting change."; + // revert + decompressVolumeData(); + } +} + + +// take compressed data and decompreess it into _volData. +void RenderablePolyVoxEntityItem::decompressVolumeData() { + int rawSize = _volData->getDepth() * _volData->getHeight() * _volData->getWidth(); + QByteArray uncompressedData = QByteArray(rawSize, '\0'); + + uncompressedData = qUncompress(_voxelData); + + for (int z = 0; z < _volData->getDepth(); z++) { + for (int y = 0; y < _volData->getHeight(); y++) { + for (int x = 0; x < _volData->getWidth(); x++) { + int uncompressedIndex = z * _volData->getHeight() * _volData->getWidth() + y * _volData->getWidth() + x; + _volData->setVoxelAt(x, y, z, uncompressedData[uncompressedIndex]); + } + } + } + + _needsModelReload = true; + + qDebug() << "--------------- voxel decompress ---------------"; + qDebug() << "raw-size =" << rawSize << " compressed-size =" << _voxelData.size(); +} diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h new file mode 100644 index 0000000000..b04b32996b --- /dev/null +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -0,0 +1,68 @@ +// +// RenderablePolyVoxEntityItem.h +// libraries/entities-renderer/src/ +// +// Created by Seth Alves on 5/19/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_RenderablePolyVoxEntityItem_h +#define hifi_RenderablePolyVoxEntityItem_h + +#include + +#include "PolyVoxEntityItem.h" +#include "RenderableDebugableEntityItem.h" + +class RenderablePolyVoxEntityItem : public PolyVoxEntityItem { +public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + RenderablePolyVoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : + PolyVoxEntityItem(entityItemID, properties) { } + + virtual ~RenderablePolyVoxEntityItem(); + + virtual void somethingChangedNotification() { + // This gets called from EnityItem::readEntityDataFromBuffer every time a packet describing + // this entity comes from the entity-server. It gets called even if nothing has actually changed + // (see the comment in EntityItem.cpp). If that gets fixed, this could be used to know if we + // need to redo the voxel data. + // _needsModelReload = true; + } + + + void render(RenderArgs* args); + virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, + void** intersectedObject, bool precisionPicking) const; + + void getModel(); + + virtual void setVoxelData(QByteArray voxelData); + + virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize); + glm::mat4 voxelToWorldMatrix() const; + glm::mat4 worldToVoxelMatrix() const; + + // coords are in voxel-volume space + virtual void setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue); + + // coords are in world-space + virtual void setSphere(glm::vec3 center, float radius, uint8_t toValue); + +private: + void compressVolumeData(); + void decompressVolumeData(); + + PolyVox::SimpleVolume* _volData = nullptr; + model::Geometry _modelGeometry; + bool _needsModelReload = true; +}; + + +#endif // hifi_RenderablePolyVoxEntityItem_h diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 104ce168bb..64196d1c35 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -183,9 +183,9 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet QByteArray encodedPropertyFlags; int propertyCount = 0; - successIDFits = packetData->appendValue(encodedID); + successIDFits = packetData->appendRawData(encodedID); if (successIDFits) { - successTypeFits = packetData->appendValue(encodedType); + successTypeFits = packetData->appendRawData(encodedType); } if (successTypeFits) { successCreatedFits = packetData->appendValue(_created); @@ -194,17 +194,17 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet successLastEditedFits = packetData->appendValue(lastEdited); } if (successLastEditedFits) { - successLastUpdatedFits = packetData->appendValue(encodedUpdateDelta); + successLastUpdatedFits = packetData->appendRawData(encodedUpdateDelta); } if (successLastUpdatedFits) { - successLastSimulatedFits = packetData->appendValue(encodedSimulatedDelta); + successLastSimulatedFits = packetData->appendRawData(encodedSimulatedDelta); } if (successLastSimulatedFits) { propertyFlagsOffset = packetData->getUncompressedByteOffset(); encodedPropertyFlags = propertyFlags; oldPropertyFlagsLength = encodedPropertyFlags.length(); - successPropertyFlagsFits = packetData->appendValue(encodedPropertyFlags); + successPropertyFlagsFits = packetData->appendRawData(encodedPropertyFlags); } bool headerFits = successIDFits && successTypeFits && successCreatedFits && successLastEditedFits @@ -652,6 +652,7 @@ void EntityItem::adjustEditPacketForClockSkew(unsigned char* editPacketBuffer, s // lastEdited quint64 lastEditedInLocalTime; memcpy(&lastEditedInLocalTime, dataAt, sizeof(lastEditedInLocalTime)); + assert(lastEditedInLocalTime > 0); quint64 lastEditedInServerTime = lastEditedInLocalTime + clockSkew; memcpy(dataAt, &lastEditedInServerTime, sizeof(lastEditedInServerTime)); #ifdef WANT_DEBUG @@ -956,9 +957,6 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { #endif setLastEdited(now); somethingChangedNotification(); // notify derived classes that something has changed - if (_created == UNKNOWN_CREATED_TIME) { - _created = now; - } if (getDirtyFlags() & (EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES)) { // anything that sets the transform or velocity must update _lastSimulated which is used // for kinematic extrapolation (e.g. we want to extrapolate forward from this moment @@ -967,6 +965,16 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { } } + // timestamps + quint64 timestamp = properties.getCreated(); + if (_created == UNKNOWN_CREATED_TIME && timestamp != UNKNOWN_CREATED_TIME) { + quint64 now = usecTimestampNow(); + if (timestamp > now) { + timestamp = now; + } + _created = timestamp; + } + return somethingChanged; } diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 905fa7d104..856c1a1cb4 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -26,6 +26,7 @@ #include "ParticleEffectEntityItem.h" #include "TextEntityItem.h" #include "ZoneEntityItem.h" +#include "PolyVoxEntityItem.h" AtmospherePropertyGroup EntityItemProperties::_staticAtmosphere; SkyboxPropertyGroup EntityItemProperties::_staticSkybox; @@ -87,10 +88,14 @@ CONSTRUCT_PROPERTY(keyLightColor, ZoneEntityItem::DEFAULT_KEYLIGHT_COLOR), CONSTRUCT_PROPERTY(keyLightIntensity, ZoneEntityItem::DEFAULT_KEYLIGHT_INTENSITY), CONSTRUCT_PROPERTY(keyLightAmbientIntensity, ZoneEntityItem::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY), CONSTRUCT_PROPERTY(keyLightDirection, ZoneEntityItem::DEFAULT_KEYLIGHT_DIRECTION), +CONSTRUCT_PROPERTY(voxelVolumeSize, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE), +CONSTRUCT_PROPERTY(voxelData, PolyVoxEntityItem::DEFAULT_VOXEL_DATA), +CONSTRUCT_PROPERTY(voxelSurfaceStyle, PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE), CONSTRUCT_PROPERTY(name, ENTITY_ITEM_DEFAULT_NAME), CONSTRUCT_PROPERTY(backgroundMode, BACKGROUND_MODE_INHERIT), CONSTRUCT_PROPERTY(sourceUrl, ""), + _id(UNKNOWN_ENTITY_ID), _idSet(false), _lastEdited(0), @@ -336,6 +341,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_DIRECTION, keyLightDirection); CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_MODE, backgroundMode); CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl); + CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); + CHECK_PROPERTY_CHANGE(PROP_VOXEL_DATA, voxelData); + CHECK_PROPERTY_CHANGE(PROP_VOXEL_SURFACE_STYLE, voxelSurfaceStyle); changedProperties += _stage.getChangedProperties(); changedProperties += _atmosphere.getChangedProperties(); @@ -418,6 +426,10 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightDirection); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(backgroundMode, getBackgroundModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(sourceUrl); + + COPY_PROPERTY_TO_QSCRIPTVALUE(voxelVolumeSize); + COPY_PROPERTY_TO_QSCRIPTVALUE(voxelData); + COPY_PROPERTY_TO_QSCRIPTVALUE(voxelSurfaceStyle); // Sitting properties support if (!skipDefaults) { @@ -523,7 +535,11 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { COPY_PROPERTY_FROM_QSCRIPTVALUE(keyLightDirection, glmVec3, setKeyLightDirection); COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(backgroundMode, BackgroundMode); COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl); - + + COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, glmVec3, setVoxelVolumeSize); + COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelData, QByteArray, setVoxelData); + COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelSurfaceStyle, uint16_t, setVoxelSurfaceStyle); + _stage.copyFromScriptValue(object, _defaultSettings); _atmosphere.copyFromScriptValue(object, _defaultSettings); _skybox.copyFromScriptValue(object, _defaultSettings); @@ -619,23 +635,23 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem quint64 lastEdited = properties.getLastEdited(); bool successLastEditedFits = packetData->appendValue(lastEdited); - bool successIDFits = packetData->appendValue(encodedID); + bool successIDFits = packetData->appendRawData(encodedID); if (successIDFits) { - successIDFits = packetData->appendValue(encodedToken); + successIDFits = packetData->appendRawData(encodedToken); } - bool successTypeFits = packetData->appendValue(encodedType); + bool successTypeFits = packetData->appendRawData(encodedType); // NOTE: We intentionally do not send "created" times in edit messages. This is because: // 1) if the edit is to an existing entity, the created time can not be changed // 2) if the edit is to a new entity, the created time is the last edited time // TODO: Should we get rid of this in this in edit packets, since this has to always be 0? - bool successLastUpdatedFits = packetData->appendValue(encodedUpdateDelta); + bool successLastUpdatedFits = packetData->appendRawData(encodedUpdateDelta); int propertyFlagsOffset = packetData->getUncompressedByteOffset(); QByteArray encodedPropertyFlags = propertyFlags; int oldPropertyFlagsLength = encodedPropertyFlags.length(); - bool successPropertyFlagsFits = packetData->appendValue(encodedPropertyFlags); + bool successPropertyFlagsFits = packetData->appendRawData(encodedPropertyFlags); int propertyCount = 0; bool headerFits = successIDFits && successTypeFits && successLastEditedFits @@ -740,6 +756,12 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem _staticSkybox.setProperties(properties); _staticSkybox.appentToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState ); } + + if (properties.getType() == EntityTypes::PolyVox) { + APPEND_ENTITY_PROPERTY(PROP_VOXEL_VOLUME_SIZE, properties.getVoxelVolumeSize()); + APPEND_ENTITY_PROPERTY(PROP_VOXEL_DATA, properties.getVoxelData()); + APPEND_ENTITY_PROPERTY(PROP_VOXEL_SURFACE_STYLE, properties.getVoxelSurfaceStyle()); + } APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, properties.getMarketplaceID()); APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName()); @@ -974,11 +996,17 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int properties.getAtmosphere().decodeFromEditPacket(propertyFlags, dataAt , processedBytes); properties.getSkybox().decodeFromEditPacket(propertyFlags, dataAt , processedBytes); } + + if (properties.getType() == EntityTypes::PolyVox) { + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_VOLUME_SIZE, glm::vec3, setVoxelVolumeSize); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_DATA, QByteArray, setVoxelData); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_SURFACE_STYLE, uint16_t, setVoxelSurfaceStyle); + } READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); - + return valid; } @@ -1079,6 +1107,10 @@ void EntityItemProperties::markAllChanged() { _skybox.markAllChanged(); _sourceUrlChanged = true; + + _voxelVolumeSizeChanged = true; + _voxelDataChanged = true; + _voxelSurfaceStyleChanged = true; } /// The maximum bounding cube for the entity, independent of it's rotation. diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 2c052d0a53..26c26bd474 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -54,6 +54,7 @@ class EntityItemProperties { friend class ZoneEntityItem; // TODO: consider removing this friend relationship and use public methods friend class WebEntityItem; // TODO: consider removing this friend relationship and use public methods friend class LineEntityItem; // TODO: consider removing this friend relationship and use public methods + friend class PolyVoxEntityItem; // TODO: consider removing this friend relationship and use public methods public: EntityItemProperties(); virtual ~EntityItemProperties(); @@ -135,6 +136,9 @@ public: DEFINE_PROPERTY(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float); DEFINE_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, KeyLightAmbientIntensity, keyLightAmbientIntensity, float); DEFINE_PROPERTY_REF(PROP_KEYLIGHT_DIRECTION, KeyLightDirection, keyLightDirection, glm::vec3); + DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3); + DEFINE_PROPERTY_REF(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray); + DEFINE_PROPERTY_REF(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t); DEFINE_PROPERTY_REF(PROP_NAME, Name, name, QString); DEFINE_PROPERTY_REF_ENUM(PROP_BACKGROUND_MODE, BackgroundMode, backgroundMode, BackgroundMode); DEFINE_PROPERTY_GROUP(Stage, stage, StagePropertyGroup); @@ -189,6 +193,8 @@ public: QString getSimulatorIDAsString() const { return _simulatorID.toString().mid(1,36).toUpper(); } + void setVoxelDataDirty() { _voxelDataChanged = true; } + private: QUuid _id; bool _idSet; @@ -219,7 +225,6 @@ void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemP inline void EntityItemProperties::setPosition(const glm::vec3& value) { _position = glm::clamp(value, 0.0f, (float)TREE_SCALE); _positionChanged = true; } - inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { debug << "EntityItemProperties[" << "\n"; @@ -281,6 +286,9 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParticleRadius, particleRadius, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, MarketplaceID, marketplaceID, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, BackgroundMode, backgroundMode, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelVolumeSize, voxelVolumeSize, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelData, voxelData, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, ""); properties.getStage().debugDump(); properties.getAtmosphere().debugDump(); diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 8c4f329cc9..33abc59e4d 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -32,7 +32,7 @@ #define READ_ENTITY_PROPERTY(P,T,S) \ if (propertyFlags.getHasProperty(P)) { \ T fromBuffer; \ - int bytes = OctreePacketData::uppackDataFromBytes(dataAt, fromBuffer); \ + int bytes = OctreePacketData::unpackDataFromBytes(dataAt, fromBuffer); \ dataAt += bytes; \ bytesRead += bytes; \ if (overwriteLocalData) { \ @@ -49,7 +49,7 @@ #define READ_ENTITY_PROPERTY_TO_PROPERTIES(P,T,O) \ if (propertyFlags.getHasProperty(P)) { \ T fromBuffer; \ - int bytes = OctreePacketData::uppackDataFromBytes(dataAt, fromBuffer); \ + int bytes = OctreePacketData::unpackDataFromBytes(dataAt, fromBuffer); \ dataAt += bytes; \ processedBytes += bytes; \ properties.O(fromBuffer); \ @@ -91,10 +91,18 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, float v) { return QScri inline QScriptValue convertScriptValue(QScriptEngine* e, int v) { return QScriptValue(v); } inline QScriptValue convertScriptValue(QScriptEngine* e, quint32 v) { return QScriptValue(v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QString& v) { return QScriptValue(v); } + inline QScriptValue convertScriptValue(QScriptEngine* e, const xColor& v) { return xColorToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::quat& v) { return quatToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QScriptValue& v) { return v; } +inline QScriptValue convertScriptValue(QScriptEngine* e, const QByteArray& v) { + QByteArray b64 = v.toBase64(); + return QScriptValue(QString(b64)); +} + + + #define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(G,g,P,p) \ if (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P()) { \ QScriptValue groupProperties = properties.property(#g); \ @@ -129,6 +137,16 @@ inline int int_convertFromScriptValue(const QScriptValue& v, bool& isValid) { re inline bool bool_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toBool(); } inline QString QString_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toString().trimmed(); } inline QUuid QUuid_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toUuid(); } + + +inline QByteArray QByteArray_convertFromScriptValue(const QScriptValue& v, bool& isValid) { + isValid = true; + QString b64 = v.toVariant().toString().trimmed(); + return QByteArray::fromBase64(b64.toUtf8()); +} + + + inline glmVec3 glmVec3_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = false; /// assume it can't be converted QScriptValue x = v.property("x"); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 8a5d96e8d2..bcb1ec0886 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -110,6 +110,10 @@ enum EntityPropertyList { PROP_RESTITUTION, PROP_FRICTION, + PROP_VOXEL_VOLUME_SIZE, + PROP_VOXEL_DATA, + PROP_VOXEL_SURFACE_STYLE, + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties ABOVE this line PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index e652c77d78..54d2ea705e 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -17,6 +17,7 @@ #include "ModelEntityItem.h" #include "ZoneEntityItem.h" #include "EntitiesLogging.h" +#include "PolyVoxEntityItem.h" EntityScriptingInterface::EntityScriptingInterface() : @@ -382,3 +383,40 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra vec3FromScriptValue(intersection, value.intersection); } } + + +bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value) { + if (!_entityTree) { + return false; + } + + EntityItemPointer entity = static_cast(_entityTree->findEntityByEntityItemID(entityID)); + if (!entity) { + qCDebug(entities) << "EntityScriptingInterface::setVoxelSphere no entity with ID" << entityID; + return false; + } + + EntityTypes::EntityType entityType = entity->getType(); + if (entityType != EntityTypes::PolyVox) { + return false; + } + + auto now = usecTimestampNow(); + + PolyVoxEntityItem* polyVoxEntity = static_cast(entity.get()); + _entityTree->lockForWrite(); + polyVoxEntity->setSphere(center, radius, value); + entity->setLastEdited(now); + entity->setLastBroadcast(now); + _entityTree->unlock(); + + _entityTree->lockForRead(); + EntityItemProperties properties = entity->getProperties(); + _entityTree->unlock(); + + properties.setVoxelDataDirty(); + properties.setLastEdited(now); + + queueEntityMessage(PacketTypeEntityEdit, entityID, properties); + return true; +} diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 655c8982b5..6c2dc06579 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -117,6 +117,8 @@ public slots: Q_INVOKABLE void setSendPhysicsUpdates(bool value); Q_INVOKABLE bool getSendPhysicsUpdates() const; + Q_INVOKABLE bool setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value); + Q_INVOKABLE void dumpTree() const; signals: diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index b5b722cc6c..a41cc22d2e 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -27,6 +27,7 @@ #include "WebEntityItem.h" #include "ZoneEntityItem.h" #include "LineEntityItem.h" +#include "PolyVoxEntityItem.h" QMap EntityTypes::_typeToNameMap; QMap EntityTypes::_nameToTypeMap; @@ -45,6 +46,7 @@ REGISTER_ENTITY_TYPE(Text) REGISTER_ENTITY_TYPE(ParticleEffect) REGISTER_ENTITY_TYPE(Zone) REGISTER_ENTITY_TYPE(Line) +REGISTER_ENTITY_TYPE(PolyVox) const QString& EntityTypes::getEntityTypeName(EntityType entityType) { QMap::iterator matchedTypeName = _typeToNameMap.find(entityType); diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 524e5b6e82..323a4eb92b 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -45,7 +45,8 @@ public: Zone, Web, Line, - LAST = Line + PolyVox, + LAST = PolyVox } EntityType; static const QString& getEntityTypeName(EntityType entityType); diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp new file mode 100644 index 0000000000..a46fdb2682 --- /dev/null +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -0,0 +1,117 @@ +// +// PolyVoxEntityItem.cpp +// libraries/entities/src +// +// Created by Seth Alves on 5/11/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#include +#include + +#include + +#include "PolyVoxEntityItem.h" +#include "EntityTree.h" +#include "EntitiesLogging.h" +#include "EntityTreeElement.h" + + +const glm::vec3 PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE = glm::vec3(32, 32, 32); +const QByteArray PolyVoxEntityItem::DEFAULT_VOXEL_DATA(qCompress(QByteArray(0), 9)); +const PolyVoxEntityItem::PolyVoxSurfaceStyle PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE = + PolyVoxEntityItem::SURFACE_MARCHING_CUBES; + +EntityItemPointer PolyVoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + return EntityItemPointer(new PolyVoxEntityItem(entityID, properties)); +} + +PolyVoxEntityItem::PolyVoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : + EntityItem(entityItemID), + _voxelVolumeSize(PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE), + _voxelData(PolyVoxEntityItem::DEFAULT_VOXEL_DATA), + _voxelSurfaceStyle(PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE) +{ + _type = EntityTypes::PolyVox; + _created = properties.getCreated(); + setProperties(properties); +} + +EntityItemProperties PolyVoxEntityItem::getProperties() const { + EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class + COPY_ENTITY_PROPERTY_TO_PROPERTIES(voxelVolumeSize, getVoxelVolumeSize); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(voxelData, getVoxelData); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(voxelSurfaceStyle, getVoxelSurfaceStyle); + + return properties; +} + +bool PolyVoxEntityItem::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class + SET_ENTITY_PROPERTY_FROM_PROPERTIES(voxelVolumeSize, setVoxelVolumeSize); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(voxelData, setVoxelData); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(voxelSurfaceStyle, setVoxelSurfaceStyle); + + if (somethingChanged) { + bool wantDebug = false; + if (wantDebug) { + uint64_t now = usecTimestampNow(); + int elapsed = now - getLastEdited(); + qCDebug(entities) << "PolyVoxEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << + "now=" << now << " getLastEdited()=" << getLastEdited(); + } + setLastEdited(properties._lastEdited); + } + return somethingChanged; +} + +int PolyVoxEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_VOXEL_VOLUME_SIZE, glm::vec3, setVoxelVolumeSize); + READ_ENTITY_PROPERTY(PROP_VOXEL_DATA, QByteArray, setVoxelData); + READ_ENTITY_PROPERTY(PROP_VOXEL_SURFACE_STYLE, uint16_t, setVoxelSurfaceStyle); + + return bytesRead; +} + + +// TODO: eventually only include properties changed since the params.lastViewFrustumSent time +EntityPropertyFlags PolyVoxEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + requestedProperties += PROP_VOXEL_VOLUME_SIZE; + requestedProperties += PROP_VOXEL_DATA; + requestedProperties += PROP_VOXEL_SURFACE_STYLE; + return requestedProperties; +} + +void PolyVoxEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_VOXEL_VOLUME_SIZE, getVoxelVolumeSize()); + APPEND_ENTITY_PROPERTY(PROP_VOXEL_DATA, getVoxelData()); + APPEND_ENTITY_PROPERTY(PROP_VOXEL_SURFACE_STYLE, (uint16_t) getVoxelSurfaceStyle()); +} + +void PolyVoxEntityItem::debugDump() const { + quint64 now = usecTimestampNow(); + qCDebug(entities) << " POLYVOX EntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " position:" << debugTreeVector(_position); + qCDebug(entities) << " dimensions:" << debugTreeVector(_dimensions); + qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); +} + diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h new file mode 100644 index 0000000000..53675e6efa --- /dev/null +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -0,0 +1,98 @@ +// +// PolyVoxEntityItem.h +// libraries/entities/src +// +// Created by Seth Alves on 5/11/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_PolyVoxEntityItem_h +#define hifi_PolyVoxEntityItem_h + +#include "EntityItem.h" + +class PolyVoxEntityItem : public EntityItem { + public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + PolyVoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); + + ALLOW_INSTANTIATION // This class can be instantiated + + // methods for getting/setting all properties of an entity + virtual EntityItemProperties getProperties() const; + virtual bool setProperties(const EntityItemProperties& properties); + + // TODO: eventually only include properties changed since the params.lastViewFrustumSent time + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData); + + const rgbColor& getColor() const { return _color; } + xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } + + void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); } + void setXColor(const xColor& value) { + _color[RED_INDEX] = value.red; + _color[GREEN_INDEX] = value.green; + _color[BLUE_INDEX] = value.blue; + } + + virtual ShapeType getShapeType() const { return SHAPE_TYPE_POLYVOX; } + + // never have a ray intersection pick a PolyVoxEntityItem. + virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, + void** intersectedObject, bool precisionPicking) const { return false; } + + virtual void debugDump() const; + + virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { _voxelVolumeSize = voxelVolumeSize; } + virtual const glm::vec3& getVoxelVolumeSize() const { return _voxelVolumeSize; } + + virtual void setVoxelData(QByteArray voxelData) { _voxelData = voxelData; } + virtual const QByteArray& getVoxelData() const { return _voxelData; } + + enum PolyVoxSurfaceStyle { + SURFACE_MARCHING_CUBES, + SURFACE_CUBIC + }; + + virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) { _voxelSurfaceStyle = voxelSurfaceStyle; } + virtual void setVoxelSurfaceStyle(uint16_t voxelSurfaceStyle) { + _voxelSurfaceStyle = (PolyVoxSurfaceStyle) voxelSurfaceStyle; + } + virtual PolyVoxSurfaceStyle getVoxelSurfaceStyle() const { return _voxelSurfaceStyle; } + + static const glm::vec3 DEFAULT_VOXEL_VOLUME_SIZE; + static const QByteArray DEFAULT_VOXEL_DATA; + static const PolyVoxSurfaceStyle DEFAULT_VOXEL_SURFACE_STYLE; + + // coords are in voxel-volume space + virtual void setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue) {} + + // coords are in world-space + virtual void setSphere(glm::vec3 center, float radius, uint8_t toValue) {} + + protected: + rgbColor _color; + glm::vec3 _voxelVolumeSize; // this is always 3 bytes + QByteArray _voxelData; + PolyVoxSurfaceStyle _voxelSurfaceStyle; +}; + +#endif // hifi_PolyVoxEntityItem_h diff --git a/libraries/gpu/src/gpu/Resource.h b/libraries/gpu/src/gpu/Resource.h index 7f3f850339..42897e9947 100644 --- a/libraries/gpu/src/gpu/Resource.h +++ b/libraries/gpu/src/gpu/Resource.h @@ -228,13 +228,14 @@ public: { public: - Iterator(T* ptr = NULL) { _ptr = ptr; } + Iterator(T* ptr = NULL, int stride = sizeof(T)): _ptr(ptr), _stride(stride) { } Iterator(const Iterator& iterator) = default; ~Iterator() {} Iterator& operator=(const Iterator& iterator) = default; Iterator& operator=(T* ptr) { _ptr = ptr; + // stride is left unchanged return (*this); } @@ -249,42 +250,48 @@ public: bool operator==(const Iterator& iterator) const { return (_ptr == iterator.getConstPtr()); } bool operator!=(const Iterator& iterator) const { return (_ptr != iterator.getConstPtr()); } + void movePtr(const Index& movement) { + auto byteptr = ((Byte*)_ptr); + byteptr += _stride * movement; + _ptr = (T*)byteptr; + } + Iterator& operator+=(const Index& movement) { - _ptr += movement; + movePtr(movement); return (*this); } Iterator& operator-=(const Index& movement) { - _ptr -= movement; + movePtr(-movement); return (*this); } Iterator& operator++() { - ++_ptr; + movePtr(1); return (*this); } Iterator& operator--() { - --_ptr; + movePtr(-1); return (*this); } Iterator operator++(Index) { auto temp(*this); - ++_ptr; + movePtr(1); return temp; } Iterator operator--(Index) { auto temp(*this); - --_ptr; + movePtr(-1); return temp; } Iterator operator+(const Index& movement) { auto oldPtr = _ptr; - _ptr += movement; + movePtr(movement); auto temp(*this); _ptr = oldPtr; return temp; } Iterator operator-(const Index& movement) { auto oldPtr = _ptr; - _ptr -= movement; + movePtr(-movement); auto temp(*this); _ptr = oldPtr; return temp; @@ -302,16 +309,17 @@ public: protected: T* _ptr; + int _stride; }; - template Iterator begin() { return Iterator(&edit(0)); } - template Iterator end() { return Iterator(&edit(getNum())); } - template Iterator cbegin() const { return Iterator(&get(0)); } - template Iterator cend() const { return Iterator(&get(getNum())); } + template Iterator begin() { return Iterator(&edit(0), _stride); } + template Iterator end() { return Iterator(&edit(getNum()), _stride); } + template Iterator cbegin() const { return Iterator(&get(0), _stride); } + template Iterator cend() const { return Iterator(&get(getNum()), _stride); } // the number of elements of the specified type fitting in the view size template Index getNum() const { - return Index(_size / sizeof(T)); + return Index(_size / _stride); } template const T& get() const { @@ -347,7 +355,7 @@ public: } template const T& get(const Index index) const { - Resource::Size elementOffset = index * sizeof(T) + _offset; + Resource::Size elementOffset = index * _stride + _offset; #if _DEBUG if (!_buffer) { qDebug() << "Accessing null gpu::buffer!"; @@ -363,7 +371,7 @@ public: } template T& edit(const Index index) const { - Resource::Size elementOffset = index * sizeof(T) + _offset; + Resource::Size elementOffset = index * _stride + _offset; #if _DEBUG if (!_buffer) { qDebug() << "Accessing null gpu::buffer!"; diff --git a/libraries/model/src/model/Geometry.cpp b/libraries/model/src/model/Geometry.cpp index ed0201763a..7f0abbd9b9 100755 --- a/libraries/model/src/model/Geometry.cpp +++ b/libraries/model/src/model/Geometry.cpp @@ -42,6 +42,15 @@ void Mesh::addAttribute(Slot slot, const BufferView& buffer) { evalVertexFormat(); } +const BufferView Mesh::getAttributeBuffer(int attrib) const { + auto attribBuffer = _attributeBuffers.find(attrib); + if (attribBuffer != _attributeBuffers.end()) { + return attribBuffer->second; + } else { + return BufferView(); + } +} + void Mesh::evalVertexFormat() { auto vf = new VertexFormat(); int channelNum = 0; diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index 95f1c3bce7..ddefaf4e96 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -52,6 +52,7 @@ public: // Attribute Buffers int getNumAttributes() const { return _attributeBuffers.size(); } void addAttribute(Slot slot, const BufferView& buffer); + const BufferView getAttributeBuffer(int attrib) const; // Stream format const gpu::Stream::FormatPointer getVertexFormat() const { return _vertexFormat; } diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp index a3f4220362..a34a3be3fd 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/model/src/model/Skybox.cpp @@ -107,7 +107,7 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky } else { // skybox has no cubemap, just clear the color buffer auto color = skybox.getColor(); - batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(skybox.getColor(),1.0f), 0.f, 0); + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(color, 1.0f), 0.f, 0); } } diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index 8bc27b872f..19fc278088 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -426,7 +426,12 @@ bool OctreePacketData::appendValue(const QUuid& uuid) { } bool OctreePacketData::appendValue(const QByteArray& bytes) { - bool success = appendRawData((const unsigned char*)bytes.constData(), bytes.size()); + // TODO: make this a ByteCountCoded leading byte + uint16_t length = bytes.size(); + bool success = appendValue(length); + if (success) { + success = appendRawData((const unsigned char*)bytes.constData(), bytes.size()); + } return success; } @@ -451,6 +456,12 @@ bool OctreePacketData::appendRawData(const unsigned char* data, int length) { return success; } + +bool OctreePacketData::appendRawData(QByteArray data) { + return appendRawData((unsigned char *)data.data(), data.size()); +} + + quint64 OctreePacketData::_compressContentTime = 0; quint64 OctreePacketData::_compressContentCalls = 0; @@ -545,7 +556,7 @@ void OctreePacketData::debugContent() { printf("\n"); } -int OctreePacketData::uppackDataFromBytes(const unsigned char* dataBytes, QString& result) { +int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QString& result) { uint16_t length; memcpy(&length, dataBytes, sizeof(length)); dataBytes += sizeof(length); @@ -554,7 +565,7 @@ int OctreePacketData::uppackDataFromBytes(const unsigned char* dataBytes, QStrin return sizeof(length) + length; } -int OctreePacketData::uppackDataFromBytes(const unsigned char* dataBytes, QUuid& result) { +int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QUuid& result) { uint16_t length; memcpy(&length, dataBytes, sizeof(length)); dataBytes += sizeof(length); @@ -567,9 +578,18 @@ int OctreePacketData::uppackDataFromBytes(const unsigned char* dataBytes, QUuid& return sizeof(length) + length; } -int OctreePacketData::uppackDataFromBytes(const unsigned char* dataBytes, xColor& result) { +int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, xColor& result) { result.red = dataBytes[RED_INDEX]; result.green = dataBytes[GREEN_INDEX]; result.blue = dataBytes[BLUE_INDEX]; return sizeof(rgbColor); } + +int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result) { + uint16_t length; + memcpy(&length, dataBytes, sizeof(length)); + dataBytes += sizeof(length); + QByteArray value((const char*)dataBytes, length); + result = value; + return sizeof(length) + length; +} diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 28ea9aa681..5becb26ca2 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -183,6 +183,7 @@ public: /// appends raw bytes, might fail if byte would cause packet to be too large bool appendRawData(const unsigned char* data, int length); + bool appendRawData(QByteArray data); /// returns a byte offset from beginning of the uncompressed stream based on offset from end. /// Positive offsetFromEnd returns that many bytes before the end of uncompressed stream @@ -226,20 +227,21 @@ public: static quint64 getTotalBytesOfBitMasks() { return _totalBytesOfBitMasks; } /// total bytes of bitmasks static quint64 getTotalBytesOfColor() { return _totalBytesOfColor; } /// total bytes of color - static int uppackDataFromBytes(const unsigned char* dataBytes, float& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } - static int uppackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } - static int uppackDataFromBytes(const unsigned char* dataBytes, bool& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } - static int uppackDataFromBytes(const unsigned char* dataBytes, quint64& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } - static int uppackDataFromBytes(const unsigned char* dataBytes, uint32_t& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } - static int uppackDataFromBytes(const unsigned char* dataBytes, uint16_t& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } - static int uppackDataFromBytes(const unsigned char* dataBytes, uint8_t& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } - static int uppackDataFromBytes(const unsigned char* dataBytes, rgbColor& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } - static int uppackDataFromBytes(const unsigned char* dataBytes, glm::quat& result) { int bytes = unpackOrientationQuatFromBytes(dataBytes, result); return bytes; } - static int uppackDataFromBytes(const unsigned char* dataBytes, ShapeType& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } - static int uppackDataFromBytes(const unsigned char* dataBytes, BackgroundMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } - static int uppackDataFromBytes(const unsigned char* dataBytes, QString& result); - static int uppackDataFromBytes(const unsigned char* dataBytes, QUuid& result); - static int uppackDataFromBytes(const unsigned char* dataBytes, xColor& result); + static int unpackDataFromBytes(const unsigned char* dataBytes, float& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } + static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } + static int unpackDataFromBytes(const unsigned char* dataBytes, bool& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } + static int unpackDataFromBytes(const unsigned char* dataBytes, quint64& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } + static int unpackDataFromBytes(const unsigned char* dataBytes, uint32_t& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } + static int unpackDataFromBytes(const unsigned char* dataBytes, uint16_t& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } + static int unpackDataFromBytes(const unsigned char* dataBytes, uint8_t& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } + static int unpackDataFromBytes(const unsigned char* dataBytes, rgbColor& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } + static int unpackDataFromBytes(const unsigned char* dataBytes, glm::quat& result) { int bytes = unpackOrientationQuatFromBytes(dataBytes, result); return bytes; } + static int unpackDataFromBytes(const unsigned char* dataBytes, ShapeType& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } + static int unpackDataFromBytes(const unsigned char* dataBytes, BackgroundMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } + static int unpackDataFromBytes(const unsigned char* dataBytes, QString& result); + static int unpackDataFromBytes(const unsigned char* dataBytes, QUuid& result); + static int unpackDataFromBytes(const unsigned char* dataBytes, xColor& result); + static int unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result); private: /// appends raw bytes, might fail if byte would cause packet to be too large diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index a3fbe55f36..e2a77fbba2 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -35,7 +35,8 @@ enum ShapeType { SHAPE_TYPE_CYLINDER_X, SHAPE_TYPE_CYLINDER_Y, SHAPE_TYPE_CYLINDER_Z, - SHAPE_TYPE_LINE + SHAPE_TYPE_LINE, + SHAPE_TYPE_POLYVOX }; class ShapeInfo { diff --git a/tests/physics/src/MeshMassPropertiesTests.h b/tests/physics/src/MeshMassPropertiesTests.h index ab352bfce2..07cd774d34 100644 --- a/tests/physics/src/MeshMassPropertiesTests.h +++ b/tests/physics/src/MeshMassPropertiesTests.h @@ -15,7 +15,7 @@ namespace MeshMassPropertiesTests{ void testParallelAxisTheorem(); void testTetrahedron(); void testOpenTetrahedonMesh(); - void testClosedTetrahedronMesh(); + void testClosedTetrahedronMesh(); void testBoxAsMesh(); void runAllTests(); }