resolve conflicts on merge with upstream master

This commit is contained in:
Stephen Birarda 2013-07-11 10:58:45 -07:00
commit fcb8865d2e
79 changed files with 10605 additions and 975 deletions

View file

@ -10,14 +10,16 @@
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <OctalCode.h>
#include <iostream>
#include <EnvironmentData.h>
#include <NodeList.h>
#include <NodeTypes.h>
#include <EnvironmentData.h>
#include <VoxelTree.h>
#include <SharedUtil.h>
#include <OctalCode.h>
#include <PacketHeaders.h>
#include <SceneUtils.h>
#include <SharedUtil.h>
#include <VoxelTree.h>
#ifdef _WIN32
#include "Syssocket.h"
@ -643,14 +645,14 @@ void* animateVoxels(void* args) {
sendDanceFloor();
}
long long end = usecTimestampNow();
long long elapsedSeconds = (end - ::start) / 1000000;
uint64_t end = usecTimestampNow();
uint64_t elapsedSeconds = (end - ::start) / 1000000;
if (::shouldShowPacketsPerSecond) {
printf("packetsSent=%ld, bytesSent=%ld pps=%f bps=%f\n",packetsSent,bytesSent,
(float)(packetsSent/elapsedSeconds),(float)(bytesSent/elapsedSeconds));
}
// dynamically sleep until we need to fire off the next set of voxels
long long usecToSleep = ANIMATE_VOXELS_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime));
uint64_t usecToSleep = ANIMATE_VOXELS_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime));
if (usecToSleep > 0) {
usleep(usecToSleep);

View file

@ -47,7 +47,7 @@ const unsigned short MIXER_LISTEN_PORT = 55443;
const short JITTER_BUFFER_MSECS = 12;
const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_MSECS * (SAMPLE_RATE / 1000.0);
const long long BUFFER_SEND_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000);
const unsigned int BUFFER_SEND_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000);
const long MAX_SAMPLE_VALUE = std::numeric_limits<int16_t>::max();
const long MIN_SAMPLE_VALUE = std::numeric_limits<int16_t>::min();
@ -414,7 +414,7 @@ int main(int argc, const char* argv[]) {
numStatCollections++;
}
long long usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();
int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();
if (usecToSleep > 0) {
usleep(usecToSleep);

View file

@ -0,0 +1,44 @@
# Try to find the MotionDriver library to access the InvenSense gyros
#
# You must provide a MOTIONDRIVER_ROOT_DIR which contains lib and include directories
#
# Once done this will define
#
# MOTIONDRIVER_FOUND - system found MotionDriver
# MOTIONDRIVER_INCLUDE_DIRS - the MotionDriver include directory
# MOTIONDRIVER_LIBRARIES - Link this to use MotionDriver
#
# Created on 7/9/2013 by Andrzej Kapolka
# Copyright (c) 2013 High Fidelity
#
if (MOTIONDRIVER_LIBRARIES AND MOTIONDRIVER_INCLUDE_DIRS)
# in cache already
set(MOTIONDRIVER_FOUND TRUE)
else (MOTIONDRIVER_LIBRARIES AND MOTIONDRIVER_INCLUDE_DIRS)
find_path(MOTIONDRIVER_INCLUDE_DIRS inv_mpu.h ${MOTIONDRIVER_ROOT_DIR}/include)
if (APPLE)
find_library(MOTIONDRIVER_LIBRARIES libMotionDriver.a ${MOTIONDRIVER_ROOT_DIR}/lib/MacOS/)
elseif (UNIX)
find_library(MOTIONDRIVER_LIBRARIES libMotionDriver.a ${MOTIONDRIVER_ROOT_DIR}/lib/UNIX/)
endif ()
if (MOTIONDRIVER_INCLUDE_DIRS AND MOTIONDRIVER_LIBRARIES)
set(MOTIONDRIVER_FOUND TRUE)
endif (MOTIONDRIVER_INCLUDE_DIRS AND MOTIONDRIVER_LIBRARIES)
if (MOTIONDRIVER_FOUND)
if (NOT MOTIONDRIVER_FIND_QUIETLY)
message(STATUS "Found MotionDriver: ${MOTIONDRIVER_LIBRARIES}")
endif (NOT MOTIONDRIVER_FIND_QUIETLY)
else (MOTIONDRIVER_FOUND)
if (MOTIONDRIVER_FIND_REQUIRED)
message(FATAL_ERROR "Could not find MotionDriver")
endif (MOTIONDRIVER_FIND_REQUIRED)
endif (MOTIONDRIVER_FOUND)
# show the MOTIONDRIVER_INCLUDE_DIRS and MOTIONDRIVER_LIBRARIES variables only in the advanced view
mark_as_advanced(MOTIONDRIVER_INCLUDE_DIRS MOTIONDRIVER_LIBRARIES)
endif (MOTIONDRIVER_LIBRARIES AND MOTIONDRIVER_INCLUDE_DIRS)

View file

@ -0,0 +1,44 @@
# Find the OpenNI library
#
# You must provide an OPENNI_ROOT_DIR which contains lib and include directories
#
# Once done this will define
#
# OPENNI_FOUND - system found OpenNI
# OPENNI_INCLUDE_DIRS - the OpenNI include directory
# OPENNI_LIBRARIES - Link this to use OpenNI
#
# Created on 6/28/2013 by Andrzej Kapolka
# Copyright (c) 2013 High Fidelity
#
if (OPENNI_LIBRARIES AND OPENNI_INCLUDE_DIRS)
# in cache already
set(OPENNI_FOUND TRUE)
else (OPENNI_LIBRARIES AND OPENNI_INCLUDE_DIRS)
find_path(OPENNI_INCLUDE_DIRS XnOpenNI.h /usr/include/ni)
if (APPLE)
find_library(OPENNI_LIBRARIES libOpenNI.dylib /usr/lib)
elseif (UNIX)
find_library(OPENNI_LIBRARIES libOpenNI.so /usr/lib)
endif ()
if (OPENNI_INCLUDE_DIRS AND OPENNI_LIBRARIES)
set(OPENNI_FOUND TRUE)
endif (OPENNI_INCLUDE_DIRS AND OPENNI_LIBRARIES)
if (OPENNI_FOUND)
if (NOT OPENNI_FIND_QUIETLY)
message(STATUS "Found OpenNI: ${OPENNI_LIBRARIES}")
endif (NOT OPENNI_FIND_QUIETLY)
else (OPENNI_FOUND)
if (OPENNI_FIND_REQUIRED)
message(FATAL_ERROR "Could not find OpenNI")
endif (OPENNI_FIND_REQUIRED)
endif (OPENNI_FOUND)
# show the OPENNI_INCLUDE_DIRS and OPENNI_LIBRARIES variables only in the advanced view
mark_as_advanced(OPENNI_INCLUDE_DIRS OPENNI_LIBRARIES)
endif (OPENNI_LIBRARIES AND OPENNI_INCLUDE_DIRS)

View file

@ -163,7 +163,7 @@ int main(int argc, const char * argv[])
}
// update last receive to now
long long timeNow = usecTimestampNow();
uint64_t timeNow = usecTimestampNow();
newNode->setLastHeardMicrostamp(timeNow);
if (packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY

View file

@ -126,7 +126,7 @@ int main(int argc, const char* argv[]) {
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_HEAD_DATA);
timeval thisSend;
long long numMicrosecondsSleep = 0;
int numMicrosecondsSleep = 0;
int handStateTimer = 0;

View file

@ -10,6 +10,7 @@ project(${TARGET_NAME})
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/")
set(LIBOVR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/LibOVR)
set(LEAP_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/Leap)
set(MOTIONDRIVER_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/MotionDriver)
set(PORTAUDIO_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/PortAudio)
set(OPENCV_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/OpenCV)
set(UVCCAMERACONTROL_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/UVCCameraControl)
@ -65,7 +66,7 @@ if (APPLE)
endif (APPLE)
find_package(Qt4 REQUIRED QtCore QtGui QtNetwork QtOpenGL QtWebKit)
find_package(Qt4 REQUIRED QtCore QtGui QtNetwork QtOpenGL QtWebKit QtSvg)
include(${QT_USE_FILE})
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${QT_QTGUI_INCLUDE_DIR}")
@ -92,9 +93,19 @@ link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR})
find_package(GLM REQUIRED)
find_package(LibOVR)
find_package(Leap)
find_package(MotionDriver)
find_package(OpenCV)
find_package(ZLIB)
find_package(OpenNI)
find_package(UVCCameraControl)
find_package(ZLIB)
# let the source know that we have OpenNI/NITE for Kinect
if (OPENNI_FOUND)
add_definitions(-DHAVE_OPENNI)
include_directories(SYSTEM ${OPENNI_INCLUDE_DIRS})
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${OPENNI_INCLUDE_DIRS}")
target_link_libraries(${TARGET_NAME} ${OPENNI_LIBRARIES})
endif (OPENNI_FOUND)
# include headers for interface and InterfaceConfig.
include_directories(
@ -109,6 +120,7 @@ include_directories(
${GLM_INCLUDE_DIRS}
${LIBOVR_INCLUDE_DIRS}
${LEAP_INCLUDE_DIRS}
${MOTIONDRIVER_INCLUDE_DIRS}
${OPENCV_INCLUDE_DIRS}
)
@ -116,6 +128,7 @@ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${OPENCV_INCLUDE_DIRS}")
target_link_libraries(
${TARGET_NAME}
${QT_LIBRARIES}
${MOTIONDRIVER_LIBRARIES}
${OPENCV_LIBRARIES}
${ZLIB_LIBRARIES}
fervor

11
interface/external/Leap/readme.txt vendored Normal file
View file

@ -0,0 +1,11 @@
Instructions for adding the Leap driver to Interface
Eric Johnston, July 10, 2013
NOTE: Without doing step 2, you will crash at program start time.
1. Copy the Leap sdk folders (lib, include, etc.) into the interface/external/Leap folder. There should be a folder already there called "stub", and this read me.txt should be there as well.
2. IMPORTANT: Copy the file interface/external/Leap/lib/libc++/libLeap.dylib to /usr/lib
3. Delete your build directory, run cmake and build, and you should be all set.

View file

@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 2.8)
set(TARGET_NAME MotionDriver)
project(${TARGET_NAME})
# let the library know which device we're using
add_definitions(-DMPU9150)
# grab the implemenation and header files
file(GLOB MOTION_DRIVER_SRCS include/*.h src/*.c)
include_directories(include)
add_library(${TARGET_NAME} ${MOTION_DRIVER_SRCS})

View file

@ -0,0 +1,218 @@
SOFTWARE LICENSE AGREEMENT
Unless you and InvenSense Corporation ("InvenSense") execute a separate written
software license agreement governing use of the accompanying software, this
software is licensed to you under the terms of this Software License
Agreement ("Agreement").
ANY USE, REPRODUCTION OR DISTRIBUTION OF THE SOFTWARE CONSTITUTES YOUR
ACCEPTANCE OF THIS AGREEMENT.
1. DEFINITIONS.
1.1. "InvenSense Product" means any of the proprietary integrated circuit
product(s) sold by InvenSense with which the Software was designed to be used,
or their successors.
1.2. "Licensee" means you or if you are accepting on behalf of an entity
then the entity and its affiliates exercising rights under, and complying
with all of the terms of this Agreement.
1.3. "Software" shall mean that software made available by InvenSense to
Licensee in binary code form with this Agreement.
2. LICENSE GRANT; OWNERSHIP
2.1. License Grants. Subject to the terms and conditions of this Agreement,
InvenSense hereby grants to Licensee a non-exclusive, non-transferable,
royalty-free license (i) to use and integrate the Software in conjunction
with any other software; and (ii) to reproduce and distribute the Software
complete, unmodified and only for use with a InvenSense Product.
2.2. Restriction on Modification. If and to the extent that the Software is
designed to be compliant with any published communications standard
(including, without limitation, DOCSIS, HomePNA, IEEE, and ITU standards),
Licensee may not make any modifications to the Software that would cause the
Software or the accompanying InvenSense Products to be incompatible with such
standard.
2.3. Restriction on Distribution. Licensee shall only distribute the
Software (a) under the terms of this Agreement and a copy of this Agreement
accompanies such distribution, and (b) agrees to defend and indemnify
InvenSense and its licensors from and against any damages, costs, liabilities,
settlement amounts and/or expenses (including attorneys' fees) incurred in
connection with any claim, lawsuit or action by any third party that arises
or results from the use or distribution of any and all Software by the
Licensee except as contemplated herein.
2.4. Proprietary Notices. Licensee shall not remove, efface or obscure any
copyright or trademark notices from the Software. Licensee shall include
reproductions of the InvenSense copyright notice with each copy of the
Software, except where such Software is embedded in a manner not readily
accessible to the end user. Licensee acknowledges that any symbols,
trademarks, tradenames, and service marks adopted by InvenSense to identify the
Software belong to InvenSense and that Licensee shall have no rights therein.
2.5. Ownership. InvenSense shall retain all right, title and interest,
including all intellectual property rights, in and to the Software. Licensee
hereby covenants that it will not assert any claim that the Software created
by or for InvenSense infringe any intellectual property right owned or
controlled by Licensee.
2.6. No Other Rights Granted; Restrictions. Apart from the license rights
expressly set forth in this Agreement, InvenSense does not grant and Licensee
does not receive any ownership right, title or interest nor any security
interest or other interest in any intellectual property rights relating to
the Software, nor in any copy of any part of the foregoing. No license is
granted to Licensee in any human readable code of the Software (source code).
Licensee shall not (i) use, license, sell or otherwise distribute the
Software except as provided in this Agreement, (ii) attempt to reverse
engineer, decompile or disassemble any portion of the Software; or (iii) use
the Software or other material in violation of any applicable law or
regulation, including but not limited to any regulatory agency, such as FCC,
rules.
3. NO WARRANTY OR SUPPORT
3.1. No Warranty. THE SOFTWARE IS OFFERED "AS IS," AND INVENSENSE GRANTS AND
LICENSEE RECEIVES NO WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, BY STATUTE,
COMMUNICATION OR CONDUCT WITH LICENSEE, OR OTHERWISE. INVENSENSE SPECIFICALLY
DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A SPECIFIC
PURPOSE OR NONINFRINGEMENT CONCERNING THE SOFTWARE OR ANY UPGRADES TO OR
DOCUMENTATION FOR THE SOFTWARE. WITHOUT LIMITATION OF THE ABOVE, INVENSENSE
GRANTS NO WARRANTY THAT THE SOFTWARE IS ERROR-FREE OR WILL OPERATE WITHOUT
INTERRUPTION, AND GRANTS NO WARRANTY REGARDING ITS USE OR THE RESULTS
THEREFROM INCLUDING, WITHOUT LIMITATION, ITS CORRECTNESS, ACCURACY OR
RELIABILITY.
3.2. No Support. Nothing in this agreement shall obligate InvenSense to
provide any support for the Software. InvenSense may, but shall be under no
obligation to, correct any defects in the Software and/or provide updates to
licensees of the Software. Licensee shall make reasonable efforts to
promptly report to InvenSense any defects it finds in the Software, as an aid
to creating improved revisions of the Software.
3.3. Dangerous Applications. The Software is not designed, intended, or
certified for use in components of systems intended for the operation of
weapons, weapons systems, nuclear installations, means of mass
transportation, aviation, life-support computers or equipment (including
resuscitation equipment and surgical implants), pollution control, hazardous
substances management, or for any other dangerous application in which the
failure of the Software could create a situation where personal injury or
death may occur. Licensee understands that use of the Software in such
applications is fully at the risk of Licensee.
4. TERM AND TERMINATION
4.1. Termination. This Agreement will automatically terminate if Licensee
fails to comply with any of the terms and conditions hereof. In such event,
Licensee must destroy all copies of the Software and all of its component
parts.
4.2. Effect Of Termination. Upon any termination of this Agreement, the
rights and licenses granted to Licensee under this Agreement shall
immediately terminate.
4.3. Survival. The rights and obligations under this Agreement which by
their nature should survive termination will remain in effect after
expiration or termination of this Agreement.
5. CONFIDENTIALITY
5.1. Obligations. Licensee acknowledges and agrees that any documentation
relating to the Software, and any other information (if such other
information is identified as confidential or should be recognized as
confidential under the circumstances) provided to Licensee by InvenSense
hereunder (collectively, "Confidential Information") constitute the
confidential and proprietary information of InvenSense, and that Licensee's
protection thereof is an essential condition to Licensee's use and possession
of the Software. Licensee shall retain all Confidential Information in
strict confidence and not disclose it to any third party or use it in any way
except under a written agreement with terms and conditions at least as
protective as the terms of this Section. Licensee will exercise at least the
same amount of diligence in preserving the secrecy of the Confidential
Information as it uses in preserving the secrecy of its own most valuable
confidential information, but in no event less than reasonable diligence.
Information shall not be considered Confidential Information if and to the
extent that it: (i) was in the public domain at the time it was disclosed or
has entered the public domain through no fault of Licensee; (ii) was known to
Licensee, without restriction, at the time of disclosure as proven by the
files of Licensee in existence at the time of disclosure; or (iii) becomes
known to Licensee, without restriction, from a source other than InvenSense
without breach of this Agreement by Licensee and otherwise not in violation
of InvenSense's rights.
5.2. Return of Confidential Information. Notwithstanding the foregoing, all
documents and other tangible objects containing or representing InvenSense
Confidential Information and all copies thereof which are in the possession
of Licensee shall be and remain the property of InvenSense, and shall be
promptly returned to InvenSense upon written request by InvenSense or upon
termination of this Agreement.
6. LIMITATION OF LIABILITY
TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL INVENSENSE OR ANY OF
INVENSENSE'S LICENSORS HAVE ANY LIABILITY FOR ANY INDIRECT, INCIDENTAL,
SPECIAL, OR CONSEQUENTIAL DAMAGES, HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER FOR BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE) OR
OTHERWISE, ARISING OUT OF THIS AGREEMENT, INCLUDING BUT NOT LIMITED TO LOSS
OF PROFITS, EVEN IF SUCH PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES. IN NO EVENT WILL INVENSENSE'S LIABILITY WHETHER IN CONTRACT, TORT
(INCLUDING NEGLIGENCE), OR OTHERWISE, EXCEED THE AMOUNT PAID BY LICENSEE FOR
SOFTWARE UNDER THIS AGREEMENT. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING
ANY FAILURE OF ESSENTIAL PURPOSE OF ANY LIMITED REMEDY.
7. MISCELLANEOUS
7.1. Export Regulations. YOU UNDERSTAND AND AGREE THAT THE SOFTWARE IS
SUBJECT TO UNITED STATES AND OTHER APPLICABLE EXPORT-RELATED LAWS AND
REGULATIONS AND THAT YOU MAY NOT EXPORT, RE-EXPORT OR TRANSFER THE SOFTWARE
OR ANY DIRECT PRODUCT OF THE SOFTWARE EXCEPT AS PERMITTED UNDER THOSE LAWS.
WITHOUT LIMITING THE FOREGOING, EXPORT, RE-EXPORT OR TRANSFER OF THE SOFTWARE
TO CUBA, IRAN, NORTH KOREA, SUDAN AND SYRIA IS PROHIBITED.
7.2 Assignment. This Agreement shall be binding upon and inure to the
benefit of the parties and their respective successors and assigns, provided,
however that Licensee may not assign this Agreement or any rights or
obligation hereunder, directly or indirectly, by operation of law or
otherwise, without the prior written consent of InvenSense, and any such
attempted assignment shall be void. Notwithstanding the foregoing, Licensee
may assign this Agreement to a successor to all or substantially all of its
business or assets to which this Agreement relates that is not a competitor
of InvenSense.
7.3. Governing Law; Venue. This Agreement shall be governed by the laws of
California without regard to any conflict-of-laws rules, and the United
Nations Convention on Contracts for the International Sale of Goods is hereby
excluded. The sole jurisdiction and venue for actions related to the subject
matter hereof shall be the state and federal courts located in the County of
Orange, California, and both parties hereby consent to such jurisdiction and
venue.
7.4. Severability. All terms and provisions of this Agreement shall, if
possible, be construed in a manner which makes them valid, but in the event
any term or provision of this Agreement is found by a court of competent
jurisdiction to be illegal or unenforceable, the validity or enforceability
of the remainder of this Agreement shall not be affected if the illegal or
unenforceable provision does not materially affect the intent of this
Agreement. If the illegal or unenforceable provision materially affects the
intent of the parties to this Agreement, this Agreement shall become
terminated.
7.5. Equitable Relief. Licensee hereby acknowledges that its breach of this
Agreement would cause irreparable harm and significant injury to InvenSense
that may be difficult to ascertain and that a remedy at law would be
inadequate. Accordingly, Licensee agrees that InvenSense shall have the right
to seek and obtain immediate injunctive relief to enforce obligations under
the Agreement in addition to any other rights and remedies it may have.
7.6. Waiver. The waiver of, or failure to enforce, any breach or default
hereunder shall not constitute the waiver of any other or subsequent breach
or default.
7.7. Entire Agreement. This Agreement sets forth the entire Agreement
between the parties and supersedes any and all prior proposals, agreements
and representations between them, whether written or oral concerning the
Software. This Agreement may be changed only by mutual agreement of the
parties in writing.

View file

@ -0,0 +1,494 @@
/*
$License:
Copyright (C) 2011 InvenSense Corporation, All Rights Reserved.
$
*/
#ifndef DMPKEY_H__
#define DMPKEY_H__
#define KEY_CFG_25 (0)
#define KEY_CFG_24 (KEY_CFG_25 + 1)
#define KEY_CFG_26 (KEY_CFG_24 + 1)
#define KEY_CFG_27 (KEY_CFG_26 + 1)
#define KEY_CFG_21 (KEY_CFG_27 + 1)
#define KEY_CFG_20 (KEY_CFG_21 + 1)
#define KEY_CFG_TAP4 (KEY_CFG_20 + 1)
#define KEY_CFG_TAP5 (KEY_CFG_TAP4 + 1)
#define KEY_CFG_TAP6 (KEY_CFG_TAP5 + 1)
#define KEY_CFG_TAP7 (KEY_CFG_TAP6 + 1)
#define KEY_CFG_TAP0 (KEY_CFG_TAP7 + 1)
#define KEY_CFG_TAP1 (KEY_CFG_TAP0 + 1)
#define KEY_CFG_TAP2 (KEY_CFG_TAP1 + 1)
#define KEY_CFG_TAP3 (KEY_CFG_TAP2 + 1)
#define KEY_CFG_TAP_QUANTIZE (KEY_CFG_TAP3 + 1)
#define KEY_CFG_TAP_JERK (KEY_CFG_TAP_QUANTIZE + 1)
#define KEY_CFG_DR_INT (KEY_CFG_TAP_JERK + 1)
#define KEY_CFG_AUTH (KEY_CFG_DR_INT + 1)
#define KEY_CFG_TAP_SAVE_ACCB (KEY_CFG_AUTH + 1)
#define KEY_CFG_TAP_CLEAR_STICKY (KEY_CFG_TAP_SAVE_ACCB + 1)
#define KEY_CFG_FIFO_ON_EVENT (KEY_CFG_TAP_CLEAR_STICKY + 1)
#define KEY_FCFG_ACCEL_INPUT (KEY_CFG_FIFO_ON_EVENT + 1)
#define KEY_FCFG_ACCEL_INIT (KEY_FCFG_ACCEL_INPUT + 1)
#define KEY_CFG_23 (KEY_FCFG_ACCEL_INIT + 1)
#define KEY_FCFG_1 (KEY_CFG_23 + 1)
#define KEY_FCFG_3 (KEY_FCFG_1 + 1)
#define KEY_FCFG_2 (KEY_FCFG_3 + 1)
#define KEY_CFG_3D (KEY_FCFG_2 + 1)
#define KEY_CFG_3B (KEY_CFG_3D + 1)
#define KEY_CFG_3C (KEY_CFG_3B + 1)
#define KEY_FCFG_5 (KEY_CFG_3C + 1)
#define KEY_FCFG_4 (KEY_FCFG_5 + 1)
#define KEY_FCFG_7 (KEY_FCFG_4 + 1)
#define KEY_FCFG_FSCALE (KEY_FCFG_7 + 1)
#define KEY_FCFG_AZ (KEY_FCFG_FSCALE + 1)
#define KEY_FCFG_6 (KEY_FCFG_AZ + 1)
#define KEY_FCFG_LSB4 (KEY_FCFG_6 + 1)
#define KEY_CFG_12 (KEY_FCFG_LSB4 + 1)
#define KEY_CFG_14 (KEY_CFG_12 + 1)
#define KEY_CFG_15 (KEY_CFG_14 + 1)
#define KEY_CFG_16 (KEY_CFG_15 + 1)
#define KEY_CFG_18 (KEY_CFG_16 + 1)
#define KEY_CFG_6 (KEY_CFG_18 + 1)
#define KEY_CFG_7 (KEY_CFG_6 + 1)
#define KEY_CFG_4 (KEY_CFG_7 + 1)
#define KEY_CFG_5 (KEY_CFG_4 + 1)
#define KEY_CFG_2 (KEY_CFG_5 + 1)
#define KEY_CFG_3 (KEY_CFG_2 + 1)
#define KEY_CFG_1 (KEY_CFG_3 + 1)
#define KEY_CFG_EXTERNAL (KEY_CFG_1 + 1)
#define KEY_CFG_8 (KEY_CFG_EXTERNAL + 1)
#define KEY_CFG_9 (KEY_CFG_8 + 1)
#define KEY_CFG_ORIENT_3 (KEY_CFG_9 + 1)
#define KEY_CFG_ORIENT_2 (KEY_CFG_ORIENT_3 + 1)
#define KEY_CFG_ORIENT_1 (KEY_CFG_ORIENT_2 + 1)
#define KEY_CFG_GYRO_SOURCE (KEY_CFG_ORIENT_1 + 1)
#define KEY_CFG_ORIENT_IRQ_1 (KEY_CFG_GYRO_SOURCE + 1)
#define KEY_CFG_ORIENT_IRQ_2 (KEY_CFG_ORIENT_IRQ_1 + 1)
#define KEY_CFG_ORIENT_IRQ_3 (KEY_CFG_ORIENT_IRQ_2 + 1)
#define KEY_FCFG_MAG_VAL (KEY_CFG_ORIENT_IRQ_3 + 1)
#define KEY_FCFG_MAG_MOV (KEY_FCFG_MAG_VAL + 1)
#define KEY_CFG_LP_QUAT (KEY_FCFG_MAG_MOV + 1)
/* MPU6050 keys */
#define KEY_CFG_ACCEL_FILTER (KEY_CFG_LP_QUAT + 1)
#define KEY_CFG_MOTION_BIAS (KEY_CFG_ACCEL_FILTER + 1)
#define KEY_TEMPLABEL (KEY_CFG_MOTION_BIAS + 1)
#define KEY_D_0_22 (KEY_TEMPLABEL + 1)
#define KEY_D_0_24 (KEY_D_0_22 + 1)
#define KEY_D_0_36 (KEY_D_0_24 + 1)
#define KEY_D_0_52 (KEY_D_0_36 + 1)
#define KEY_D_0_96 (KEY_D_0_52 + 1)
#define KEY_D_0_104 (KEY_D_0_96 + 1)
#define KEY_D_0_108 (KEY_D_0_104 + 1)
#define KEY_D_0_163 (KEY_D_0_108 + 1)
#define KEY_D_0_188 (KEY_D_0_163 + 1)
#define KEY_D_0_192 (KEY_D_0_188 + 1)
#define KEY_D_0_224 (KEY_D_0_192 + 1)
#define KEY_D_0_228 (KEY_D_0_224 + 1)
#define KEY_D_0_232 (KEY_D_0_228 + 1)
#define KEY_D_0_236 (KEY_D_0_232 + 1)
#define KEY_DMP_PREVPTAT (KEY_D_0_236 + 1)
#define KEY_D_1_2 (KEY_DMP_PREVPTAT + 1)
#define KEY_D_1_4 (KEY_D_1_2 + 1)
#define KEY_D_1_8 (KEY_D_1_4 + 1)
#define KEY_D_1_10 (KEY_D_1_8 + 1)
#define KEY_D_1_24 (KEY_D_1_10 + 1)
#define KEY_D_1_28 (KEY_D_1_24 + 1)
#define KEY_D_1_36 (KEY_D_1_28 + 1)
#define KEY_D_1_40 (KEY_D_1_36 + 1)
#define KEY_D_1_44 (KEY_D_1_40 + 1)
#define KEY_D_1_72 (KEY_D_1_44 + 1)
#define KEY_D_1_74 (KEY_D_1_72 + 1)
#define KEY_D_1_79 (KEY_D_1_74 + 1)
#define KEY_D_1_88 (KEY_D_1_79 + 1)
#define KEY_D_1_90 (KEY_D_1_88 + 1)
#define KEY_D_1_92 (KEY_D_1_90 + 1)
#define KEY_D_1_96 (KEY_D_1_92 + 1)
#define KEY_D_1_98 (KEY_D_1_96 + 1)
#define KEY_D_1_100 (KEY_D_1_98 + 1)
#define KEY_D_1_106 (KEY_D_1_100 + 1)
#define KEY_D_1_108 (KEY_D_1_106 + 1)
#define KEY_D_1_112 (KEY_D_1_108 + 1)
#define KEY_D_1_128 (KEY_D_1_112 + 1)
#define KEY_D_1_152 (KEY_D_1_128 + 1)
#define KEY_D_1_160 (KEY_D_1_152 + 1)
#define KEY_D_1_168 (KEY_D_1_160 + 1)
#define KEY_D_1_175 (KEY_D_1_168 + 1)
#define KEY_D_1_176 (KEY_D_1_175 + 1)
#define KEY_D_1_178 (KEY_D_1_176 + 1)
#define KEY_D_1_179 (KEY_D_1_178 + 1)
#define KEY_D_1_218 (KEY_D_1_179 + 1)
#define KEY_D_1_232 (KEY_D_1_218 + 1)
#define KEY_D_1_236 (KEY_D_1_232 + 1)
#define KEY_D_1_240 (KEY_D_1_236 + 1)
#define KEY_D_1_244 (KEY_D_1_240 + 1)
#define KEY_D_1_250 (KEY_D_1_244 + 1)
#define KEY_D_1_252 (KEY_D_1_250 + 1)
#define KEY_D_2_12 (KEY_D_1_252 + 1)
#define KEY_D_2_96 (KEY_D_2_12 + 1)
#define KEY_D_2_108 (KEY_D_2_96 + 1)
#define KEY_D_2_208 (KEY_D_2_108 + 1)
#define KEY_FLICK_MSG (KEY_D_2_208 + 1)
#define KEY_FLICK_COUNTER (KEY_FLICK_MSG + 1)
#define KEY_FLICK_LOWER (KEY_FLICK_COUNTER + 1)
#define KEY_CFG_FLICK_IN (KEY_FLICK_LOWER + 1)
#define KEY_FLICK_UPPER (KEY_CFG_FLICK_IN + 1)
#define KEY_CGNOTICE_INTR (KEY_FLICK_UPPER + 1)
#define KEY_D_2_224 (KEY_CGNOTICE_INTR + 1)
#define KEY_D_2_244 (KEY_D_2_224 + 1)
#define KEY_D_2_248 (KEY_D_2_244 + 1)
#define KEY_D_2_252 (KEY_D_2_248 + 1)
#define KEY_D_GYRO_BIAS_X (KEY_D_2_252 + 1)
#define KEY_D_GYRO_BIAS_Y (KEY_D_GYRO_BIAS_X + 1)
#define KEY_D_GYRO_BIAS_Z (KEY_D_GYRO_BIAS_Y + 1)
#define KEY_D_ACC_BIAS_X (KEY_D_GYRO_BIAS_Z + 1)
#define KEY_D_ACC_BIAS_Y (KEY_D_ACC_BIAS_X + 1)
#define KEY_D_ACC_BIAS_Z (KEY_D_ACC_BIAS_Y + 1)
#define KEY_D_GYRO_ENABLE (KEY_D_ACC_BIAS_Z + 1)
#define KEY_D_ACCEL_ENABLE (KEY_D_GYRO_ENABLE + 1)
#define KEY_D_QUAT_ENABLE (KEY_D_ACCEL_ENABLE +1)
#define KEY_D_OUTPUT_ENABLE (KEY_D_QUAT_ENABLE + 1)
#define KEY_D_CR_TIME_G (KEY_D_OUTPUT_ENABLE + 1)
#define KEY_D_CR_TIME_A (KEY_D_CR_TIME_G + 1)
#define KEY_D_CR_TIME_Q (KEY_D_CR_TIME_A + 1)
#define KEY_D_CS_TAX (KEY_D_CR_TIME_Q + 1)
#define KEY_D_CS_TAY (KEY_D_CS_TAX + 1)
#define KEY_D_CS_TAZ (KEY_D_CS_TAY + 1)
#define KEY_D_CS_TGX (KEY_D_CS_TAZ + 1)
#define KEY_D_CS_TGY (KEY_D_CS_TGX + 1)
#define KEY_D_CS_TGZ (KEY_D_CS_TGY + 1)
#define KEY_D_CS_TQ0 (KEY_D_CS_TGZ + 1)
#define KEY_D_CS_TQ1 (KEY_D_CS_TQ0 + 1)
#define KEY_D_CS_TQ2 (KEY_D_CS_TQ1 + 1)
#define KEY_D_CS_TQ3 (KEY_D_CS_TQ2 + 1)
/* Compass keys */
#define KEY_CPASS_BIAS_X (KEY_D_CS_TQ3 + 1)
#define KEY_CPASS_BIAS_Y (KEY_CPASS_BIAS_X + 1)
#define KEY_CPASS_BIAS_Z (KEY_CPASS_BIAS_Y + 1)
#define KEY_CPASS_MTX_00 (KEY_CPASS_BIAS_Z + 1)
#define KEY_CPASS_MTX_01 (KEY_CPASS_MTX_00 + 1)
#define KEY_CPASS_MTX_02 (KEY_CPASS_MTX_01 + 1)
#define KEY_CPASS_MTX_10 (KEY_CPASS_MTX_02 + 1)
#define KEY_CPASS_MTX_11 (KEY_CPASS_MTX_10 + 1)
#define KEY_CPASS_MTX_12 (KEY_CPASS_MTX_11 + 1)
#define KEY_CPASS_MTX_20 (KEY_CPASS_MTX_12 + 1)
#define KEY_CPASS_MTX_21 (KEY_CPASS_MTX_20 + 1)
#define KEY_CPASS_MTX_22 (KEY_CPASS_MTX_21 + 1)
/* Gesture Keys */
#define KEY_DMP_TAPW_MIN (KEY_CPASS_MTX_22 + 1)
#define KEY_DMP_TAP_THR_X (KEY_DMP_TAPW_MIN + 1)
#define KEY_DMP_TAP_THR_Y (KEY_DMP_TAP_THR_X + 1)
#define KEY_DMP_TAP_THR_Z (KEY_DMP_TAP_THR_Y + 1)
#define KEY_DMP_SH_TH_Y (KEY_DMP_TAP_THR_Z + 1)
#define KEY_DMP_SH_TH_X (KEY_DMP_SH_TH_Y + 1)
#define KEY_DMP_SH_TH_Z (KEY_DMP_SH_TH_X + 1)
#define KEY_DMP_ORIENT (KEY_DMP_SH_TH_Z + 1)
#define KEY_D_ACT0 (KEY_DMP_ORIENT + 1)
#define KEY_D_ACSX (KEY_D_ACT0 + 1)
#define KEY_D_ACSY (KEY_D_ACSX + 1)
#define KEY_D_ACSZ (KEY_D_ACSY + 1)
#define KEY_X_GRT_Y_TMP (KEY_D_ACSZ + 1)
#define KEY_SKIP_X_GRT_Y_TMP (KEY_X_GRT_Y_TMP + 1)
#define KEY_SKIP_END_COMPARE (KEY_SKIP_X_GRT_Y_TMP + 1)
#define KEY_END_COMPARE_Y_X_TMP2 (KEY_SKIP_END_COMPARE + 1)
#define KEY_CFG_ANDROID_ORIENT_INT (KEY_END_COMPARE_Y_X_TMP2 + 1)
#define KEY_NO_ORIENT_INTERRUPT (KEY_CFG_ANDROID_ORIENT_INT + 1)
#define KEY_END_COMPARE_Y_X_TMP (KEY_NO_ORIENT_INTERRUPT + 1)
#define KEY_END_ORIENT_1 (KEY_END_COMPARE_Y_X_TMP + 1)
#define KEY_END_COMPARE_Y_X (KEY_END_ORIENT_1 + 1)
#define KEY_END_ORIENT (KEY_END_COMPARE_Y_X + 1)
#define KEY_X_GRT_Y (KEY_END_ORIENT + 1)
#define KEY_NOT_TIME_MINUS_1 (KEY_X_GRT_Y + 1)
#define KEY_END_COMPARE_Y_X_TMP3 (KEY_NOT_TIME_MINUS_1 + 1)
#define KEY_X_GRT_Y_TMP2 (KEY_END_COMPARE_Y_X_TMP3 + 1)
/* Authenticate Keys */
#define KEY_D_AUTH_OUT (KEY_X_GRT_Y_TMP2 + 1)
#define KEY_D_AUTH_IN (KEY_D_AUTH_OUT + 1)
#define KEY_D_AUTH_A (KEY_D_AUTH_IN + 1)
#define KEY_D_AUTH_B (KEY_D_AUTH_A + 1)
/* Pedometer standalone only keys */
#define KEY_D_PEDSTD_BP_B (KEY_D_AUTH_B + 1)
#define KEY_D_PEDSTD_HP_A (KEY_D_PEDSTD_BP_B + 1)
#define KEY_D_PEDSTD_HP_B (KEY_D_PEDSTD_HP_A + 1)
#define KEY_D_PEDSTD_BP_A4 (KEY_D_PEDSTD_HP_B + 1)
#define KEY_D_PEDSTD_BP_A3 (KEY_D_PEDSTD_BP_A4 + 1)
#define KEY_D_PEDSTD_BP_A2 (KEY_D_PEDSTD_BP_A3 + 1)
#define KEY_D_PEDSTD_BP_A1 (KEY_D_PEDSTD_BP_A2 + 1)
#define KEY_D_PEDSTD_INT_THRSH (KEY_D_PEDSTD_BP_A1 + 1)
#define KEY_D_PEDSTD_CLIP (KEY_D_PEDSTD_INT_THRSH + 1)
#define KEY_D_PEDSTD_SB (KEY_D_PEDSTD_CLIP + 1)
#define KEY_D_PEDSTD_SB_TIME (KEY_D_PEDSTD_SB + 1)
#define KEY_D_PEDSTD_PEAKTHRSH (KEY_D_PEDSTD_SB_TIME + 1)
#define KEY_D_PEDSTD_TIML (KEY_D_PEDSTD_PEAKTHRSH + 1)
#define KEY_D_PEDSTD_TIMH (KEY_D_PEDSTD_TIML + 1)
#define KEY_D_PEDSTD_PEAK (KEY_D_PEDSTD_TIMH + 1)
#define KEY_D_PEDSTD_TIMECTR (KEY_D_PEDSTD_PEAK + 1)
#define KEY_D_PEDSTD_STEPCTR (KEY_D_PEDSTD_TIMECTR + 1)
#define KEY_D_PEDSTD_WALKTIME (KEY_D_PEDSTD_STEPCTR + 1)
#define KEY_D_PEDSTD_DECI (KEY_D_PEDSTD_WALKTIME + 1)
/*Host Based No Motion*/
#define KEY_D_HOST_NO_MOT (KEY_D_PEDSTD_DECI + 1)
/* EIS keys */
#define KEY_P_EIS_FIFO_FOOTER (KEY_D_HOST_NO_MOT + 1)
#define KEY_P_EIS_FIFO_YSHIFT (KEY_P_EIS_FIFO_FOOTER + 1)
#define KEY_P_EIS_DATA_RATE (KEY_P_EIS_FIFO_YSHIFT + 1)
#define KEY_P_EIS_FIFO_XSHIFT (KEY_P_EIS_DATA_RATE + 1)
#define KEY_P_EIS_FIFO_SYNC (KEY_P_EIS_FIFO_XSHIFT + 1)
#define KEY_P_EIS_FIFO_ZSHIFT (KEY_P_EIS_FIFO_SYNC + 1)
#define KEY_P_EIS_FIFO_READY (KEY_P_EIS_FIFO_ZSHIFT + 1)
#define KEY_DMP_FOOTER (KEY_P_EIS_FIFO_READY + 1)
#define KEY_DMP_INTX_HC (KEY_DMP_FOOTER + 1)
#define KEY_DMP_INTX_PH (KEY_DMP_INTX_HC + 1)
#define KEY_DMP_INTX_SH (KEY_DMP_INTX_PH + 1)
#define KEY_DMP_AINV_SH (KEY_DMP_INTX_SH + 1)
#define KEY_DMP_A_INV_XH (KEY_DMP_AINV_SH + 1)
#define KEY_DMP_AINV_PH (KEY_DMP_A_INV_XH + 1)
#define KEY_DMP_CTHX_H (KEY_DMP_AINV_PH + 1)
#define KEY_DMP_CTHY_H (KEY_DMP_CTHX_H + 1)
#define KEY_DMP_CTHZ_H (KEY_DMP_CTHY_H + 1)
#define KEY_DMP_NCTHX_H (KEY_DMP_CTHZ_H + 1)
#define KEY_DMP_NCTHY_H (KEY_DMP_NCTHX_H + 1)
#define KEY_DMP_NCTHZ_H (KEY_DMP_NCTHY_H + 1)
#define KEY_DMP_CTSQ_XH (KEY_DMP_NCTHZ_H + 1)
#define KEY_DMP_CTSQ_YH (KEY_DMP_CTSQ_XH + 1)
#define KEY_DMP_CTSQ_ZH (KEY_DMP_CTSQ_YH + 1)
#define KEY_DMP_INTX_H (KEY_DMP_CTSQ_ZH + 1)
#define KEY_DMP_INTY_H (KEY_DMP_INTX_H + 1)
#define KEY_DMP_INTZ_H (KEY_DMP_INTY_H + 1)
//#define KEY_DMP_HPX_H (KEY_DMP_INTZ_H + 1)
//#define KEY_DMP_HPY_H (KEY_DMP_HPX_H + 1)
//#define KEY_DMP_HPZ_H (KEY_DMP_HPY_H + 1)
/* Stream keys */
#define KEY_STREAM_P_GYRO_Z (KEY_DMP_INTZ_H + 1)
#define KEY_STREAM_P_GYRO_Y (KEY_STREAM_P_GYRO_Z + 1)
#define KEY_STREAM_P_GYRO_X (KEY_STREAM_P_GYRO_Y + 1)
#define KEY_STREAM_P_TEMP (KEY_STREAM_P_GYRO_X + 1)
#define KEY_STREAM_P_AUX_Y (KEY_STREAM_P_TEMP + 1)
#define KEY_STREAM_P_AUX_X (KEY_STREAM_P_AUX_Y + 1)
#define KEY_STREAM_P_AUX_Z (KEY_STREAM_P_AUX_X + 1)
#define KEY_STREAM_P_ACCEL_Y (KEY_STREAM_P_AUX_Z + 1)
#define KEY_STREAM_P_ACCEL_X (KEY_STREAM_P_ACCEL_Y + 1)
#define KEY_STREAM_P_FOOTER (KEY_STREAM_P_ACCEL_X + 1)
#define KEY_STREAM_P_ACCEL_Z (KEY_STREAM_P_FOOTER + 1)
#define NUM_KEYS (KEY_STREAM_P_ACCEL_Z + 1)
typedef struct {
unsigned short key;
unsigned short addr;
} tKeyLabel;
#define DINA0A 0x0a
#define DINA22 0x22
#define DINA42 0x42
#define DINA5A 0x5a
#define DINA06 0x06
#define DINA0E 0x0e
#define DINA16 0x16
#define DINA1E 0x1e
#define DINA26 0x26
#define DINA2E 0x2e
#define DINA36 0x36
#define DINA3E 0x3e
#define DINA46 0x46
#define DINA4E 0x4e
#define DINA56 0x56
#define DINA5E 0x5e
#define DINA66 0x66
#define DINA6E 0x6e
#define DINA76 0x76
#define DINA7E 0x7e
#define DINA00 0x00
#define DINA08 0x08
#define DINA10 0x10
#define DINA18 0x18
#define DINA20 0x20
#define DINA28 0x28
#define DINA30 0x30
#define DINA38 0x38
#define DINA40 0x40
#define DINA48 0x48
#define DINA50 0x50
#define DINA58 0x58
#define DINA60 0x60
#define DINA68 0x68
#define DINA70 0x70
#define DINA78 0x78
#define DINA04 0x04
#define DINA0C 0x0c
#define DINA14 0x14
#define DINA1C 0x1C
#define DINA24 0x24
#define DINA2C 0x2c
#define DINA34 0x34
#define DINA3C 0x3c
#define DINA44 0x44
#define DINA4C 0x4c
#define DINA54 0x54
#define DINA5C 0x5c
#define DINA64 0x64
#define DINA6C 0x6c
#define DINA74 0x74
#define DINA7C 0x7c
#define DINA01 0x01
#define DINA09 0x09
#define DINA11 0x11
#define DINA19 0x19
#define DINA21 0x21
#define DINA29 0x29
#define DINA31 0x31
#define DINA39 0x39
#define DINA41 0x41
#define DINA49 0x49
#define DINA51 0x51
#define DINA59 0x59
#define DINA61 0x61
#define DINA69 0x69
#define DINA71 0x71
#define DINA79 0x79
#define DINA25 0x25
#define DINA2D 0x2d
#define DINA35 0x35
#define DINA3D 0x3d
#define DINA4D 0x4d
#define DINA55 0x55
#define DINA5D 0x5D
#define DINA6D 0x6d
#define DINA75 0x75
#define DINA7D 0x7d
#define DINADC 0xdc
#define DINAF2 0xf2
#define DINAAB 0xab
#define DINAAA 0xaa
#define DINAF1 0xf1
#define DINADF 0xdf
#define DINADA 0xda
#define DINAB1 0xb1
#define DINAB9 0xb9
#define DINAF3 0xf3
#define DINA8B 0x8b
#define DINAA3 0xa3
#define DINA91 0x91
#define DINAB6 0xb6
#define DINAB4 0xb4
#define DINC00 0x00
#define DINC01 0x01
#define DINC02 0x02
#define DINC03 0x03
#define DINC08 0x08
#define DINC09 0x09
#define DINC0A 0x0a
#define DINC0B 0x0b
#define DINC10 0x10
#define DINC11 0x11
#define DINC12 0x12
#define DINC13 0x13
#define DINC18 0x18
#define DINC19 0x19
#define DINC1A 0x1a
#define DINC1B 0x1b
#define DINC20 0x20
#define DINC21 0x21
#define DINC22 0x22
#define DINC23 0x23
#define DINC28 0x28
#define DINC29 0x29
#define DINC2A 0x2a
#define DINC2B 0x2b
#define DINC30 0x30
#define DINC31 0x31
#define DINC32 0x32
#define DINC33 0x33
#define DINC38 0x38
#define DINC39 0x39
#define DINC3A 0x3a
#define DINC3B 0x3b
#define DINC40 0x40
#define DINC41 0x41
#define DINC42 0x42
#define DINC43 0x43
#define DINC48 0x48
#define DINC49 0x49
#define DINC4A 0x4a
#define DINC4B 0x4b
#define DINC50 0x50
#define DINC51 0x51
#define DINC52 0x52
#define DINC53 0x53
#define DINC58 0x58
#define DINC59 0x59
#define DINC5A 0x5a
#define DINC5B 0x5b
#define DINC60 0x60
#define DINC61 0x61
#define DINC62 0x62
#define DINC63 0x63
#define DINC68 0x68
#define DINC69 0x69
#define DINC6A 0x6a
#define DINC6B 0x6b
#define DINC70 0x70
#define DINC71 0x71
#define DINC72 0x72
#define DINC73 0x73
#define DINC78 0x78
#define DINC79 0x79
#define DINC7A 0x7a
#define DINC7B 0x7b
#define DIND40 0x40
#define DINA80 0x80
#define DINA90 0x90
#define DINAA0 0xa0
#define DINAC9 0xc9
#define DINACB 0xcb
#define DINACD 0xcd
#define DINACF 0xcf
#define DINAC8 0xc8
#define DINACA 0xca
#define DINACC 0xcc
#define DINACE 0xce
#define DINAD8 0xd8
#define DINADD 0xdd
#define DINAF8 0xf0
#define DINAFE 0xfe
#define DINBF8 0xf8
#define DINAC0 0xb0
#define DINAC1 0xb1
#define DINAC2 0xb4
#define DINAC3 0xb5
#define DINAC4 0xb8
#define DINAC5 0xb9
#define DINBC0 0xc0
#define DINBC2 0xc2
#define DINBC4 0xc4
#define DINBC6 0xc6
#endif // DMPKEY_H__

View file

@ -0,0 +1,264 @@
/*
$License:
Copyright (C) 2011 InvenSense Corporation, All Rights Reserved.
$
*/
#ifndef DMPMAP_H
#define DMPMAP_H
#ifdef __cplusplus
extern "C"
{
#endif
#define DMP_PTAT 0
#define DMP_XGYR 2
#define DMP_YGYR 4
#define DMP_ZGYR 6
#define DMP_XACC 8
#define DMP_YACC 10
#define DMP_ZACC 12
#define DMP_ADC1 14
#define DMP_ADC2 16
#define DMP_ADC3 18
#define DMP_BIASUNC 20
#define DMP_FIFORT 22
#define DMP_INVGSFH 24
#define DMP_INVGSFL 26
#define DMP_1H 28
#define DMP_1L 30
#define DMP_BLPFSTCH 32
#define DMP_BLPFSTCL 34
#define DMP_BLPFSXH 36
#define DMP_BLPFSXL 38
#define DMP_BLPFSYH 40
#define DMP_BLPFSYL 42
#define DMP_BLPFSZH 44
#define DMP_BLPFSZL 46
#define DMP_BLPFMTC 48
#define DMP_SMC 50
#define DMP_BLPFMXH 52
#define DMP_BLPFMXL 54
#define DMP_BLPFMYH 56
#define DMP_BLPFMYL 58
#define DMP_BLPFMZH 60
#define DMP_BLPFMZL 62
#define DMP_BLPFC 64
#define DMP_SMCTH 66
#define DMP_0H2 68
#define DMP_0L2 70
#define DMP_BERR2H 72
#define DMP_BERR2L 74
#define DMP_BERR2NH 76
#define DMP_SMCINC 78
#define DMP_ANGVBXH 80
#define DMP_ANGVBXL 82
#define DMP_ANGVBYH 84
#define DMP_ANGVBYL 86
#define DMP_ANGVBZH 88
#define DMP_ANGVBZL 90
#define DMP_BERR1H 92
#define DMP_BERR1L 94
#define DMP_ATCH 96
#define DMP_BIASUNCSF 98
#define DMP_ACT2H 100
#define DMP_ACT2L 102
#define DMP_GSFH 104
#define DMP_GSFL 106
#define DMP_GH 108
#define DMP_GL 110
#define DMP_0_5H 112
#define DMP_0_5L 114
#define DMP_0_0H 116
#define DMP_0_0L 118
#define DMP_1_0H 120
#define DMP_1_0L 122
#define DMP_1_5H 124
#define DMP_1_5L 126
#define DMP_TMP1AH 128
#define DMP_TMP1AL 130
#define DMP_TMP2AH 132
#define DMP_TMP2AL 134
#define DMP_TMP3AH 136
#define DMP_TMP3AL 138
#define DMP_TMP4AH 140
#define DMP_TMP4AL 142
#define DMP_XACCW 144
#define DMP_TMP5 146
#define DMP_XACCB 148
#define DMP_TMP8 150
#define DMP_YACCB 152
#define DMP_TMP9 154
#define DMP_ZACCB 156
#define DMP_TMP10 158
#define DMP_DZH 160
#define DMP_DZL 162
#define DMP_XGCH 164
#define DMP_XGCL 166
#define DMP_YGCH 168
#define DMP_YGCL 170
#define DMP_ZGCH 172
#define DMP_ZGCL 174
#define DMP_YACCW 176
#define DMP_TMP7 178
#define DMP_AFB1H 180
#define DMP_AFB1L 182
#define DMP_AFB2H 184
#define DMP_AFB2L 186
#define DMP_MAGFBH 188
#define DMP_MAGFBL 190
#define DMP_QT1H 192
#define DMP_QT1L 194
#define DMP_QT2H 196
#define DMP_QT2L 198
#define DMP_QT3H 200
#define DMP_QT3L 202
#define DMP_QT4H 204
#define DMP_QT4L 206
#define DMP_CTRL1H 208
#define DMP_CTRL1L 210
#define DMP_CTRL2H 212
#define DMP_CTRL2L 214
#define DMP_CTRL3H 216
#define DMP_CTRL3L 218
#define DMP_CTRL4H 220
#define DMP_CTRL4L 222
#define DMP_CTRLS1 224
#define DMP_CTRLSF1 226
#define DMP_CTRLS2 228
#define DMP_CTRLSF2 230
#define DMP_CTRLS3 232
#define DMP_CTRLSFNLL 234
#define DMP_CTRLS4 236
#define DMP_CTRLSFNL2 238
#define DMP_CTRLSFNL 240
#define DMP_TMP30 242
#define DMP_CTRLSFJT 244
#define DMP_TMP31 246
#define DMP_TMP11 248
#define DMP_CTRLSF2_2 250
#define DMP_TMP12 252
#define DMP_CTRLSF1_2 254
#define DMP_PREVPTAT 256
#define DMP_ACCZB 258
#define DMP_ACCXB 264
#define DMP_ACCYB 266
#define DMP_1HB 272
#define DMP_1LB 274
#define DMP_0H 276
#define DMP_0L 278
#define DMP_ASR22H 280
#define DMP_ASR22L 282
#define DMP_ASR6H 284
#define DMP_ASR6L 286
#define DMP_TMP13 288
#define DMP_TMP14 290
#define DMP_FINTXH 292
#define DMP_FINTXL 294
#define DMP_FINTYH 296
#define DMP_FINTYL 298
#define DMP_FINTZH 300
#define DMP_FINTZL 302
#define DMP_TMP1BH 304
#define DMP_TMP1BL 306
#define DMP_TMP2BH 308
#define DMP_TMP2BL 310
#define DMP_TMP3BH 312
#define DMP_TMP3BL 314
#define DMP_TMP4BH 316
#define DMP_TMP4BL 318
#define DMP_STXG 320
#define DMP_ZCTXG 322
#define DMP_STYG 324
#define DMP_ZCTYG 326
#define DMP_STZG 328
#define DMP_ZCTZG 330
#define DMP_CTRLSFJT2 332
#define DMP_CTRLSFJTCNT 334
#define DMP_PVXG 336
#define DMP_TMP15 338
#define DMP_PVYG 340
#define DMP_TMP16 342
#define DMP_PVZG 344
#define DMP_TMP17 346
#define DMP_MNMFLAGH 352
#define DMP_MNMFLAGL 354
#define DMP_MNMTMH 356
#define DMP_MNMTML 358
#define DMP_MNMTMTHRH 360
#define DMP_MNMTMTHRL 362
#define DMP_MNMTHRH 364
#define DMP_MNMTHRL 366
#define DMP_ACCQD4H 368
#define DMP_ACCQD4L 370
#define DMP_ACCQD5H 372
#define DMP_ACCQD5L 374
#define DMP_ACCQD6H 376
#define DMP_ACCQD6L 378
#define DMP_ACCQD7H 380
#define DMP_ACCQD7L 382
#define DMP_ACCQD0H 384
#define DMP_ACCQD0L 386
#define DMP_ACCQD1H 388
#define DMP_ACCQD1L 390
#define DMP_ACCQD2H 392
#define DMP_ACCQD2L 394
#define DMP_ACCQD3H 396
#define DMP_ACCQD3L 398
#define DMP_XN2H 400
#define DMP_XN2L 402
#define DMP_XN1H 404
#define DMP_XN1L 406
#define DMP_YN2H 408
#define DMP_YN2L 410
#define DMP_YN1H 412
#define DMP_YN1L 414
#define DMP_YH 416
#define DMP_YL 418
#define DMP_B0H 420
#define DMP_B0L 422
#define DMP_A1H 424
#define DMP_A1L 426
#define DMP_A2H 428
#define DMP_A2L 430
#define DMP_SEM1 432
#define DMP_FIFOCNT 434
#define DMP_SH_TH_X 436
#define DMP_PACKET 438
#define DMP_SH_TH_Y 440
#define DMP_FOOTER 442
#define DMP_SH_TH_Z 444
#define DMP_TEMP29 448
#define DMP_TEMP30 450
#define DMP_XACCB_PRE 452
#define DMP_XACCB_PREL 454
#define DMP_YACCB_PRE 456
#define DMP_YACCB_PREL 458
#define DMP_ZACCB_PRE 460
#define DMP_ZACCB_PREL 462
#define DMP_TMP22 464
#define DMP_TAP_TIMER 466
#define DMP_TAP_THX 468
#define DMP_TAP_THY 472
#define DMP_TAP_THZ 476
#define DMP_TAPW_MIN 478
#define DMP_TMP25 480
#define DMP_TMP26 482
#define DMP_TMP27 484
#define DMP_TMP28 486
#define DMP_ORIENT 488
#define DMP_THRSH 490
#define DMP_ENDIANH 492
#define DMP_ENDIANL 494
#define DMP_BLPFNMTCH 496
#define DMP_BLPFNMTCL 498
#define DMP_BLPFNMXH 500
#define DMP_BLPFNMXL 502
#define DMP_BLPFNMYH 504
#define DMP_BLPFNMYL 506
#define DMP_BLPFNMZH 508
#define DMP_BLPFNMZL 510
#ifdef __cplusplus
}
#endif
#endif // DMPMAP_H

View file

@ -0,0 +1,127 @@
/*
$License:
Copyright (C) 2011-2012 InvenSense Corporation, All Rights Reserved.
See included License.txt for License information.
$
*/
/**
* @addtogroup DRIVERS Sensor Driver Layer
* @brief Hardware drivers to communicate with sensors via I2C.
*
* @{
* @file inv_mpu.h
* @brief An I2C-based driver for Invensense gyroscopes.
* @details This driver currently works for the following devices:
* MPU6050
* MPU6500
* MPU9150 (or MPU6050 w/ AK8975 on the auxiliary bus)
* MPU9250 (or MPU6500 w/ AK8963 on the auxiliary bus)
*/
#ifndef _INV_MPU_H_
#define _INV_MPU_H_
#define INV_X_GYRO (0x40)
#define INV_Y_GYRO (0x20)
#define INV_Z_GYRO (0x10)
#define INV_XYZ_GYRO (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO)
#define INV_XYZ_ACCEL (0x08)
#define INV_XYZ_COMPASS (0x01)
struct int_param_s {
#if defined EMPL_TARGET_MSP430 || defined MOTION_DRIVER_TARGET_MSP430
void (*cb)(void);
unsigned short pin;
unsigned char lp_exit;
unsigned char active_low;
#elif defined EMPL_TARGET_UC3L0
unsigned long pin;
void (*cb)(volatile void*);
void *arg;
#endif
};
#define MPU_INT_STATUS_DATA_READY (0x0001)
#define MPU_INT_STATUS_DMP (0x0002)
#define MPU_INT_STATUS_PLL_READY (0x0004)
#define MPU_INT_STATUS_I2C_MST (0x0008)
#define MPU_INT_STATUS_FIFO_OVERFLOW (0x0010)
#define MPU_INT_STATUS_ZMOT (0x0020)
#define MPU_INT_STATUS_MOT (0x0040)
#define MPU_INT_STATUS_FREE_FALL (0x0080)
#define MPU_INT_STATUS_DMP_0 (0x0100)
#define MPU_INT_STATUS_DMP_1 (0x0200)
#define MPU_INT_STATUS_DMP_2 (0x0400)
#define MPU_INT_STATUS_DMP_3 (0x0800)
#define MPU_INT_STATUS_DMP_4 (0x1000)
#define MPU_INT_STATUS_DMP_5 (0x2000)
/* Set up APIs */
int mpu_init(struct int_param_s *int_param);
int mpu_init_slave(void);
int mpu_set_bypass(unsigned char bypass_on);
/* Configuration APIs */
int mpu_lp_accel_mode(unsigned char rate);
int mpu_lp_motion_interrupt(unsigned short thresh, unsigned char time,
unsigned char lpa_freq);
int mpu_set_int_level(unsigned char active_low);
int mpu_set_int_latched(unsigned char enable);
int mpu_set_dmp_state(unsigned char enable);
int mpu_get_dmp_state(unsigned char *enabled);
int mpu_get_lpf(unsigned short *lpf);
int mpu_set_lpf(unsigned short lpf);
int mpu_get_gyro_fsr(unsigned short *fsr);
int mpu_set_gyro_fsr(unsigned short fsr);
int mpu_get_accel_fsr(unsigned char *fsr);
int mpu_set_accel_fsr(unsigned char fsr);
int mpu_get_compass_fsr(unsigned short *fsr);
int mpu_get_gyro_sens(float *sens);
int mpu_get_accel_sens(unsigned short *sens);
int mpu_get_sample_rate(unsigned short *rate);
int mpu_set_sample_rate(unsigned short rate);
int mpu_get_compass_sample_rate(unsigned short *rate);
int mpu_set_compass_sample_rate(unsigned short rate);
int mpu_get_fifo_config(unsigned char *sensors);
int mpu_configure_fifo(unsigned char sensors);
int mpu_get_power_state(unsigned char *power_on);
int mpu_set_sensors(unsigned char sensors);
int mpu_set_accel_bias(const long *accel_bias);
/* Data getter/setter APIs */
int mpu_get_gyro_reg(short *data, unsigned long *timestamp);
int mpu_get_accel_reg(short *data, unsigned long *timestamp);
int mpu_get_compass_reg(short *data, unsigned long *timestamp);
int mpu_get_temperature(long *data, unsigned long *timestamp);
int mpu_get_int_status(short *status);
int mpu_read_fifo(short *gyro, short *accel, unsigned long *timestamp,
unsigned char *sensors, unsigned char *more);
int mpu_read_fifo_stream(unsigned short length, unsigned char *data,
unsigned char *more);
int mpu_reset_fifo(void);
int mpu_write_mem(unsigned short mem_addr, unsigned short length,
unsigned char *data);
int mpu_read_mem(unsigned short mem_addr, unsigned short length,
unsigned char *data);
int mpu_load_firmware(unsigned short length, const unsigned char *firmware,
unsigned short start_addr, unsigned short sample_rate);
int mpu_reg_dump(void);
int mpu_read_reg(unsigned char reg, unsigned char *data);
int mpu_run_self_test(long *gyro, long *accel);
int mpu_register_tap_cb(void (*func)(unsigned char, unsigned char));
#endif /* #ifndef _INV_MPU_H_ */

View file

@ -0,0 +1,97 @@
/*
$License:
Copyright (C) 2011-2012 InvenSense Corporation, All Rights Reserved.
See included License.txt for License information.
$
*/
/**
* @addtogroup DRIVERS Sensor Driver Layer
* @brief Hardware drivers to communicate with sensors via I2C.
*
* @{
* @file inv_mpu_dmp_motion_driver.h
* @brief DMP image and interface functions.
* @details All functions are preceded by the dmp_ prefix to
* differentiate among MPL and general driver function calls.
*/
#ifndef _INV_MPU_DMP_MOTION_DRIVER_H_
#define _INV_MPU_DMP_MOTION_DRIVER_H_
#define TAP_X (0x01)
#define TAP_Y (0x02)
#define TAP_Z (0x04)
#define TAP_XYZ (0x07)
#define TAP_X_UP (0x01)
#define TAP_X_DOWN (0x02)
#define TAP_Y_UP (0x03)
#define TAP_Y_DOWN (0x04)
#define TAP_Z_UP (0x05)
#define TAP_Z_DOWN (0x06)
#define ANDROID_ORIENT_PORTRAIT (0x00)
#define ANDROID_ORIENT_LANDSCAPE (0x01)
#define ANDROID_ORIENT_REVERSE_PORTRAIT (0x02)
#define ANDROID_ORIENT_REVERSE_LANDSCAPE (0x03)
#define DMP_INT_GESTURE (0x01)
#define DMP_INT_CONTINUOUS (0x02)
#define DMP_FEATURE_TAP (0x001)
#define DMP_FEATURE_ANDROID_ORIENT (0x002)
#define DMP_FEATURE_LP_QUAT (0x004)
#define DMP_FEATURE_PEDOMETER (0x008)
#define DMP_FEATURE_6X_LP_QUAT (0x010)
#define DMP_FEATURE_GYRO_CAL (0x020)
#define DMP_FEATURE_SEND_RAW_ACCEL (0x040)
#define DMP_FEATURE_SEND_RAW_GYRO (0x080)
#define DMP_FEATURE_SEND_CAL_GYRO (0x100)
#define INV_WXYZ_QUAT (0x100)
/* Set up functions. */
int dmp_load_motion_driver_firmware(void);
int dmp_set_fifo_rate(unsigned short rate);
int dmp_get_fifo_rate(unsigned short *rate);
int dmp_enable_feature(unsigned short mask);
int dmp_get_enabled_features(unsigned short *mask);
int dmp_set_interrupt_mode(unsigned char mode);
int dmp_set_orientation(unsigned short orient);
int dmp_set_gyro_bias(long *bias);
int dmp_set_accel_bias(long *bias);
/* Tap functions. */
int dmp_register_tap_cb(void (*func)(unsigned char, unsigned char));
int dmp_set_tap_thresh(unsigned char axis, unsigned short thresh);
int dmp_set_tap_axes(unsigned char axis);
int dmp_set_tap_count(unsigned char min_taps);
int dmp_set_tap_time(unsigned short time);
int dmp_set_tap_time_multi(unsigned short time);
int dmp_set_shake_reject_thresh(long sf, unsigned short thresh);
int dmp_set_shake_reject_time(unsigned short time);
int dmp_set_shake_reject_timeout(unsigned short time);
/* Android orientation functions. */
int dmp_register_android_orient_cb(void (*func)(unsigned char));
/* LP quaternion functions. */
int dmp_enable_lp_quat(unsigned char enable);
int dmp_enable_6x_lp_quat(unsigned char enable);
/* Pedometer functions. */
int dmp_get_pedometer_step_count(unsigned long *count);
int dmp_set_pedometer_step_count(unsigned long count);
int dmp_get_pedometer_walk_time(unsigned long *time);
int dmp_set_pedometer_walk_time(unsigned long time);
/* DMP gyro calibration functions. */
int dmp_enable_gyro_cal(unsigned char enable);
/* Read function. This function should be called whenever the MPU interrupt is
* detected.
*/
int dmp_read_fifo(short *gyro, short *accel, long *quat,
unsigned long *timestamp, short *sensors, unsigned char *more);
#endif /* #ifndef _INV_MPU_DMP_MOTION_DRIVER_H_ */

View file

@ -0,0 +1,22 @@
//
// inv_tty.h
// interface
//
// Created by Andrzej Kapolka on 7/9/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__inv_tty__
#define __interface__inv_tty__
void tty_set_file_descriptor(int file_descriptor);
int tty_i2c_write(unsigned char slave_addr, unsigned char reg_addr, unsigned char length, unsigned char const *data);
int tty_i2c_read(unsigned char slave_addr, unsigned char reg_addr, unsigned char length, unsigned char *data);
void tty_delay_ms(unsigned long num_ms);
void tty_get_ms(unsigned long *count);
#endif /* defined(__interface__inv_tty__) */

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,90 @@
//
// inv_tty.c
// interface
//
// Created by Andrzej Kapolka on 7/9/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
#include <time.h>
#include <unistd.h>
#include <sys/time.h>
#include "inv_tty.h"
// the file descriptor of the tty
static int ttyFileDescriptor;
void tty_set_file_descriptor(int file_descriptor) {
ttyFileDescriptor = file_descriptor;
}
static char to_hex_digit(unsigned char value) {
return (value < 10) ? '0' + value : 'A' + (value - 10);
}
static unsigned char from_hex_digit(char digit) {
return (digit < 'A') ? digit - '0' : (digit - 'A') + 10;
}
static void write_byte(unsigned char value) {
char chars[] = { to_hex_digit(value / 16), to_hex_digit(value % 16) };
write(ttyFileDescriptor, chars, 2);
}
static unsigned char read_byte() {
char chars[2];
read(ttyFileDescriptor, chars, 2);
return from_hex_digit(chars[0]) * 16 + from_hex_digit(chars[1]);
}
int tty_i2c_write(unsigned char slave_addr, unsigned char reg_addr, unsigned char length, unsigned char const *data) {
write(ttyFileDescriptor, "WR", 2);
write_byte(slave_addr);
write_byte(reg_addr);
int i;
for (i = 0; i < length; i++) {
write_byte(data[i]);
}
write(ttyFileDescriptor, "\n", 1);
char response[8];
read(ttyFileDescriptor, response, 8);
return 0;
}
int tty_i2c_read(unsigned char slave_addr, unsigned char reg_addr, unsigned char length, unsigned char *data) {
write(ttyFileDescriptor, "RD", 2);
write_byte(slave_addr);
write_byte(reg_addr);
write_byte(length);
write(ttyFileDescriptor, "\n", 1);
char prefix[6];
read(ttyFileDescriptor, prefix, 6);
int i;
for (i = 0; i < length; i++) {
data[i] = read_byte();
}
char suffix[2];
read(ttyFileDescriptor, suffix, 2);
return 0;
}
void tty_delay_ms(unsigned long num_ms) {
struct timespec required, remaining;
required.tv_sec = 0;
const long NANOSECONDS_PER_MILLISECOND = 1000000;
required.tv_nsec = num_ms * NANOSECONDS_PER_MILLISECOND;
nanosleep(&required, &remaining);
}
void tty_get_ms(unsigned long *count) {
struct timeval time;
gettimeofday(&time, 0);
const long MILLISECONDS_PER_SECOND = 1000;
const long MICROSECONDS_PER_MILLISECOND = 1000;
*count = time.tv_sec * MILLISECONDS_PER_SECOND + time.tv_usec / MICROSECONDS_PER_MILLISECOND;
}

View file

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="124px" height="400px" viewBox="-0.5 0.5 124 400" enable-background="new -0.5 0.5 124 400" xml:space="preserve">
<g>
<g>
<rect x="-0.5" y="120.382" width="62" height="40"/>
</g>
<g>
<g>
<path fill="#999999" d="M13.77,149.864c0.296,0.296,0.665,0.102,0.665,0.102s0.527-0.615,1.101-0.328
c0.811,0.405,1.315-0.113,1.315-0.113l10.611-10.611l-1.419-1.419l-1.161-1.161l-10.61,10.61c0,0-0.575,0.449-0.169,1.261
c0.287,0.573-0.384,1.045-0.384,1.045S13.474,149.569,13.77,149.864"/>
</g>
<g>
<path fill="#CCCCCC" d="M25.913,135.042l2.173-2.173c0,0,1.898-1.905,3.256-0.547c0.68,0.68,1.083,1.627-0.546,3.257
c-1.549,1.548-2.173,2.172-2.173,2.172L25.913,135.042z"/>
</g>
<g>
<rect x="22.984" y="135.651" transform="matrix(-0.7071 -0.7071 0.7071 -0.7071 -52.0828 253.0143)" fill="#CCCCCC" width="6.751" height="3.285"/>
</g>
</g>
<g>
<path fill="#FFFFFF" d="M46.482,138.915h3.697v4.617c-1.004,0.344-2.107,0.516-3.311,0.516c-1.32,0-2.341-0.383-3.062-1.148
s-1.081-1.857-1.081-3.275c0-1.383,0.395-2.459,1.184-3.229s1.895-1.154,3.316-1.154c0.539,0,1.048,0.051,1.526,0.152
s0.896,0.23,1.251,0.387l-0.732,1.816c-0.617-0.305-1.295-0.457-2.033-0.457c-0.676,0-1.198,0.22-1.567,0.659
s-0.554,1.067-0.554,1.884c0,0.801,0.167,1.411,0.501,1.831s0.815,0.63,1.444,0.63c0.344,0,0.66-0.033,0.949-0.1v-1.342h-1.529
L46.482,138.915L46.482,138.915z"/>
</g>
</g>
<g>
<g>
<rect x="-0.5" y="80.5" width="62" height="40"/>
</g>
<g>
<path fill="#FFFFFF" d="M43.84,96.434h2.988c1.164,0,2.028,0.173,2.593,0.519s0.847,0.884,0.847,1.614
c0,0.48-0.123,0.891-0.369,1.23s-0.57,0.559-0.973,0.656v0.059c0.531,0.141,0.916,0.375,1.154,0.703s0.357,0.754,0.357,1.277
c0,0.777-0.292,1.39-0.876,1.837S48.182,105,47.174,105H43.84V96.434z M46.154,99.721h0.697c0.332,0,0.589-0.068,0.771-0.205
s0.272-0.34,0.272-0.609c0-0.48-0.363-0.721-1.09-0.721h-0.65C46.154,98.186,46.154,99.721,46.154,99.721z M46.154,101.414v1.799
h0.814c0.723,0,1.084-0.305,1.084-0.914c0-0.285-0.097-0.504-0.29-0.656s-0.474-0.229-0.841-0.229
C46.921,101.414,46.154,101.414,46.154,101.414z"/>
</g>
<g>
<path fill="#FFFFFF" d="M18,106.06c-3,1-3,3-7,3c2,1,8,4,9,0C20.47,107.181,18,106.06,18,106.06z"/>
<path fill="#CCCCCC" d="M19,105.06l2,2c0,0,3.952-4.712,7-9c3.048-4.287,7.32-10.785,3-7C28.399,93.338,22.048,100.772,19,105.06z
"/>
</g>
</g>
<g>
<g>
<rect x="-0.5" y="40.5" width="62" height="40"/>
</g>
<g>
<g>
<g>
<polygon fill="#A5A5A5" points="26,58.63 39,53.893 39,67.213 26,72.394 "/>
</g>
<g>
<g>
<path fill="#333333" d="M38,54.5v12.59l-12,5.032V58.757l12.204-4.468 M39,53.63l-13,4.873v14.162l13-5.33V53.63L39,53.63z"/>
</g>
</g>
</g>
<g>
<g>
<polygon fill="#BFBFBF" points="13,67.213 13,53.893 25,58.63 25,72.394 "/>
</g>
<g>
<path fill="#BFBFBF" d="M12.887,54.289L25,58.757v13.365L13,67.09V54.5 M13,53.63v13.705l13,5.33V58.503L13,53.63L13,53.63z"/>
</g>
</g>
<g>
<g>
<polygon fill="#FFFFFF" points="13.173,53.63 25.746,48.952 38.318,53.63 25.746,58.309 "/>
</g>
<g>
<path fill="#333333" d="M25.746,49.146l12.049,4.483l-12.049,4.483L13.697,53.63L25.746,49.146 M25.746,48.758L12.65,53.63
l13.096,4.873l13.096-4.873L25.746,48.758L25.746,48.758z"/>
</g>
</g>
</g>
<g>
<path fill="#FFFFFF" d="M46.119,62.383V65.5h-2.314v-8.566h2.807c2.328,0,3.492,0.844,3.492,2.531c0,0.992-0.484,1.76-1.453,2.303
l2.495,3.732h-2.625l-1.816-3.117H46.119z M46.119,60.643h0.434c0.809,0,1.213-0.357,1.213-1.072c0-0.59-0.396-0.885-1.189-0.885
H46.12L46.119,60.643L46.119,60.643z"/>
</g>
<g>
<polygon fill="#333333" points="7,59.5 9,59.5 10,59.5 15,59.5 16,59.5 18,59.5 18,64.5 16,64.5 15,64.5 10,64.5 9,64.5 7,64.5
"/>
<g>
<g>
<rect x="8" y="60.5" fill="#FFFFFF" width="9" height="3"/>
</g>
</g>
</g>
</g>
<g>
<rect x="-0.5" y="0.5" width="62" height="40"/>
</g>
<g>
<g>
<g>
<polygon fill="#A5A5A5" points="26,18.63 39,13.893 39,27.213 26,32.394 "/>
</g>
<g>
<g>
<path fill="#333333" d="M38,14.5v12.59l-12,5.032V18.757l12.204-4.468 M39,13.63l-13,4.873v14.162l13-5.33V13.63L39,13.63z"/>
</g>
</g>
</g>
<g>
<g>
<polygon fill="#BFBFBF" points="13,27.213 13,13.893 25,18.63 25,32.394 "/>
</g>
<g>
<path fill="#BFBFBF" d="M12.887,14.289L25,18.757v13.365L13,27.09V14.5 M13,13.63v13.705l13,5.33V18.503L13,13.63L13,13.63z"/>
</g>
</g>
<g>
<g>
<polygon fill="#FFFFFF" points="13.173,13.63 25.746,8.952 38.318,13.63 25.746,18.309 "/>
</g>
<g>
<path fill="#333333" d="M25.746,9.146l12.049,4.483l-12.049,4.483L13.697,13.63L25.746,9.146 M25.746,8.758L12.65,13.63
l13.096,4.873l13.096-4.873L25.746,8.758L25.746,8.758z"/>
</g>
</g>
</g>
<g>
<path fill="#FFFFFF" d="M48.41,16.934H51L48.193,25.5h-2.725l-2.795-8.566h2.602l1.166,4.342c0.242,0.965,0.375,1.637,0.398,2.016
c0.027-0.273,0.082-0.615,0.164-1.025s0.154-0.732,0.217-0.967L48.41,16.934z"/>
</g>
<g>
<polygon fill="#333333" points="7,19.5 10,19.5 10,16.5 15,16.5 15,19.5 18,19.5 18,24.5 15,24.5 15,27.5 10,27.5 10,24.5 7,24.5
"/>
<g>
<g>
<rect x="11" y="17.5" fill="#FFFFFF" width="3" height="9"/>
</g>
<g>
<rect x="8" y="20.5" fill="#FFFFFF" width="9" height="3"/>
</g>
</g>
</g>
<g>
<g>
<rect x="-0.5" y="160.5" width="62" height="40"/>
</g>
<g>
<path fill="#FFFFFF" d="M50.379,179.205c0,1.441-0.354,2.537-1.061,3.287s-1.742,1.125-3.105,1.125
c-1.344,0-2.374-0.377-3.091-1.131s-1.075-1.852-1.075-3.293c0-1.426,0.356-2.515,1.069-3.267s1.749-1.128,3.108-1.128
c1.363,0,2.396,0.373,3.1,1.119S50.379,177.76,50.379,179.205z M44.484,179.205c0,1.656,0.576,2.484,1.729,2.484
c0.586,0,1.021-0.201,1.304-0.604s0.425-1.029,0.425-1.881c0-0.855-0.144-1.487-0.431-1.896s-0.716-0.612-1.286-0.612
C45.064,176.697,44.484,177.533,44.484,179.205z"/>
</g>
<g>
<g>
<polyline fill="none" stroke="#CCCCCC" stroke-miterlimit="10" points="19.944,187.389 19.944,188.389 19.031,187.981 "/>
<line fill="none" stroke="#CCCCCC" stroke-miterlimit="10" stroke-dasharray="1.9973,1.9973" x1="17.208" y1="187.166" x2="10.825" y2="184.315"/>
<polyline fill="none" stroke="#CCCCCC" stroke-miterlimit="10" points="9.913,183.908 9,183.5 9,182.5 "/>
<line fill="none" stroke="#CCCCCC" stroke-miterlimit="10" stroke-dasharray="2,2" x1="8.5" y1="180.5" x2="8.5" y2="173.5"/>
<polyline fill="none" stroke="#CCCCCC" stroke-miterlimit="10" points="9,172.5 9,171.5 9.927,171.876 "/>
<line fill="none" stroke="#CCCCCC" stroke-miterlimit="10" stroke-dasharray="1.9625,1.9625" x1="11.745" y1="172.615" x2="18.109" y2="175.199"/>
<polyline fill="none" stroke="#CCCCCC" stroke-miterlimit="10" points="19.018,175.568 19.944,175.944 19.944,176.944 "/>
<line fill="none" stroke="#CCCCCC" stroke-miterlimit="10" stroke-dasharray="2,2" x1="19.5" y1="179.5" x2="19.5" y2="186.5"/>
</g>
</g>
<polyline fill="none" stroke="#CCCCCC" stroke-miterlimit="10" stroke-dasharray="2,2" points="19.944,175.944 30,171.5 20,168.5
9,171.5 "/>
<polyline fill="none" stroke="#CCCCCC" stroke-miterlimit="10" stroke-dasharray="2,2" points="20.333,188.611 29.5,183.5
29.5,171.5 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.5 KiB

View file

@ -8,6 +8,7 @@
#include <sstream>
#include <stdlib.h>
#include <cmath>
#ifdef _WIN32
#include "Syssocket.h"
@ -63,6 +64,7 @@
#include "Util.h"
#include "renderer/ProgramObject.h"
#include "ui/TextRenderer.h"
#include "Swatch.h"
#include "fvupdater.h"
using namespace std;
@ -198,7 +200,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_packetCount(0),
_packetsPerSecond(0),
_bytesPerSecond(0),
_bytesCount(0)
_bytesCount(0),
_swatch(NULL)
{
_applicationStartupTime = startup_time;
_window->setWindowTitle("Interface");
@ -273,7 +276,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
FvUpdater::sharedUpdater()->SetFeedURL("https://s3-us-west-1.amazonaws.com/highfidelity/appcast.xml");
FvUpdater::sharedUpdater()->CheckForUpdatesSilent();
#endif
initMenu();
QRect available = desktop()->availableGeometry();
@ -337,6 +340,7 @@ void Application::initializeGL() {
QTimer* idleTimer = new QTimer(this);
connect(idleTimer, SIGNAL(timeout()), SLOT(idle()));
idleTimer->start(0);
_idleLoopStdev.reset();
if (_justStarted) {
float startupTime = (usecTimestampNow() - usecTimestamp(&_applicationStartupTime)) / 1000000.0;
@ -726,7 +730,16 @@ void Application::keyPressEvent(QKeyEvent* event) {
deleteVoxelUnderCursor();
}
break;
case Qt::Key_1:
case Qt::Key_2:
case Qt::Key_3:
case Qt::Key_4:
case Qt::Key_5:
case Qt::Key_6:
case Qt::Key_7:
case Qt::Key_8:
_swatch.handleEvent(event->key(), _eyedropperMode->isChecked());
break;
default:
event->ignore();
break;
@ -865,6 +878,8 @@ void Application::touchUpdateEvent(QTouchEvent* event) {
void Application::touchBeginEvent(QTouchEvent* event) {
touchUpdateEvent(event);
_lastTouchAvgX = _touchAvgX;
_lastTouchAvgY = _touchAvgY;
}
void Application::touchEndEvent(QTouchEvent* event) {
@ -890,8 +905,9 @@ void Application::wheelEvent(QWheelEvent* event) {
void Application::sendPingPackets() {
char nodeTypesOfInterest[] = {NODE_TYPE_VOXEL_SERVER, NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER};
long long currentTime = usecTimestampNow();
unsigned char pingPacket[MAX_PACKET_HEADER_BYTES + sizeof(currentTime)];
uint64_t currentTime = usecTimestampNow();
unsigned char pingPacket[numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_PING) + sizeof(currentTime)];
int numHeaderBytes = populateTypeAndVersion(pingPacket, PACKET_TYPE_PING);
memcpy(&pingPacket[1], &currentTime, sizeof(currentTime));
@ -948,25 +964,36 @@ static glm::vec3 getFaceVector(BoxFace face) {
}
void Application::idle() {
timeval check;
gettimeofday(&check, NULL);
// Only run simulation code if more than IDLE_SIMULATE_MSECS have passed since last time we ran
if (diffclock(&_lastTimeIdle, &check) > IDLE_SIMULATE_MSECS) {
// We call processEvents() here because the idle timer takes priority over
// event handling in Qt, so when the framerate gets low events will pile up
// unless we handle them here.
// NOTE - this is commented out for now - causing event processing issues reported by Philip and Ryan
// birarda - July 3rd
// processEvents();
update(1.0f / _fps);
double timeSinceLastUpdate = diffclock(&_lastTimeUpdated, &check);
if (timeSinceLastUpdate > IDLE_SIMULATE_MSECS) {
// If we're using multi-touch look, immediately process any
// touch events, and no other events.
// This is necessary because id the idle() call takes longer than the
// interval between idle() calls, the event loop never gets to run,
// and touch events get delayed.
sendPostedEvents(NULL, QEvent::TouchBegin);
sendPostedEvents(NULL, QEvent::TouchUpdate);
sendPostedEvents(NULL, QEvent::TouchEnd);
const float BIGGEST_DELTA_TIME_SECS = 0.25f;
update(glm::clamp((float)timeSinceLastUpdate / 1000.f, 0.f, BIGGEST_DELTA_TIME_SECS));
_glWidget->updateGL();
_lastTimeIdle = check;
_lastTimeUpdated = check;
_idleLoopStdev.addValue(timeSinceLastUpdate);
// Record standard deviation and reset counter if needed
const int STDEV_SAMPLES = 500;
if (_idleLoopStdev.getSamples() > STDEV_SAMPLES) {
_idleLoopMeasuredJitter = _idleLoopStdev.getStDev();
_idleLoopStdev.reset();
}
}
}
void Application::terminate() {
@ -1197,6 +1224,10 @@ void Application::doFalseColorizeOccluded() {
_voxels.falseColorizeOccluded();
}
void Application::doFalseColorizeOccludedV2() {
_voxels.falseColorizeOccludedV2();
}
void Application::doTrueVoxelColors() {
_voxels.trueColorize();
}
@ -1209,16 +1240,12 @@ void Application::setWantsMonochrome(bool wantsMonochrome) {
_myAvatar.setWantColor(!wantsMonochrome);
}
void Application::setWantsResIn(bool wantsResIn) {
_myAvatar.setWantResIn(wantsResIn);
}
void Application::setWantsDelta(bool wantsDelta) {
_myAvatar.setWantDelta(wantsDelta);
}
void Application::setWantsOcclusionCulling(bool wantsOcclusionCulling) {
_myAvatar.setWantOcclusionCulling(wantsOcclusionCulling);
void Application::disableOcclusionCulling(bool disableOcclusionCulling) {
_myAvatar.setWantOcclusionCulling(!disableOcclusionCulling);
}
void Application::updateVoxelModeActions() {
@ -1253,7 +1280,11 @@ void Application::decreaseVoxelSize() {
void Application::increaseVoxelSize() {
_mouseVoxelScale *= 2;
}
void Application::resetSwatchColors() {
_swatch.reset();
}
static QIcon createSwatchIcon(const QColor& color) {
QPixmap map(16, 16);
map.fill(color);
@ -1349,8 +1380,10 @@ void Application::exportVoxels() {
void Application::importVoxels() {
QString desktopLocation = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation);
QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Import Voxels"), desktopLocation,
tr("Sparse Voxel Octree Files, Square PNG (*.svo *.png)"));
QString fileNameString = QFileDialog::getOpenFileName(
_glWidget, tr("Import Voxels"), desktopLocation,
tr("Sparse Voxel Octree Files, Square PNG, Schematic Files (*.svo *.png *.schematic)"));
QByteArray fileNameAscii = fileNameString.toAscii();
const char* fileName = fileNameAscii.data();
@ -1371,8 +1404,10 @@ void Application::importVoxels() {
}
importVoxels.readFromSquareARGB32Pixels(pixels, pngImage.height());
} else {
} else if (fileNameString.endsWith(".svo", Qt::CaseInsensitive)) {
importVoxels.readFromSVOFile(fileName);
} else {
importVoxels.readFromSchematicFile(fileName);
}
VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
@ -1482,10 +1517,6 @@ void Application::initMenu() {
optionsMenu->addAction("Noise", this, SLOT(setNoise(bool)), Qt::Key_N)->setCheckable(true);
(_gyroLook = optionsMenu->addAction("Smooth Gyro Look"))->setCheckable(true);
_gyroLook->setChecked(true);
(_mouseLook = optionsMenu->addAction("Mouse Look"))->setCheckable(true);
_mouseLook->setChecked(true);
(_touchLook = optionsMenu->addAction("Touch Look"))->setCheckable(true);
_touchLook->setChecked(false);
(_showHeadMouse = optionsMenu->addAction("Head Mouse"))->setCheckable(true);
_showHeadMouse->setChecked(false);
(_transmitterDrives = optionsMenu->addAction("Transmitter Drive"))->setCheckable(true);
@ -1496,12 +1527,13 @@ void Application::initMenu() {
(_testPing = optionsMenu->addAction("Test Ping"))->setCheckable(true);
_testPing->setChecked(true);
(_fullScreenMode = optionsMenu->addAction("Fullscreen", this, SLOT(setFullscreen(bool)), Qt::Key_F))->setCheckable(true);
optionsMenu->addAction("Webcam", &_webcam, SLOT(setEnabled(bool)))->setCheckable(true);
optionsMenu->addAction("Webcam", &_webcam, SLOT(setEnabled(bool)))->setCheckable(true);
optionsMenu->addAction("Go Home", this, SLOT(goHome()));
QMenu* renderMenu = menuBar->addMenu("Render");
(_renderVoxels = renderMenu->addAction("Voxels"))->setCheckable(true);
_renderVoxels->setChecked(true);
_renderVoxels->setShortcut(Qt::Key_V);
_renderVoxels->setShortcut(Qt::SHIFT | Qt::Key_V);
(_renderVoxelTextures = renderMenu->addAction("Voxel Textures"))->setCheckable(true);
(_renderStarsOn = renderMenu->addAction("Stars"))->setCheckable(true);
_renderStarsOn->setChecked(true);
@ -1543,36 +1575,39 @@ void Application::initMenu() {
_voxelModeActions->setExclusive(false); // exclusivity implies one is always checked
(_addVoxelMode = voxelMenu->addAction(
"Add Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_A))->setCheckable(true);
"Add Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_V))->setCheckable(true);
_voxelModeActions->addAction(_addVoxelMode);
(_deleteVoxelMode = voxelMenu->addAction(
"Delete Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_D))->setCheckable(true);
"Delete Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_R))->setCheckable(true);
_voxelModeActions->addAction(_deleteVoxelMode);
(_colorVoxelMode = voxelMenu->addAction(
"Color Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_B))->setCheckable(true);
"Color Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_B))->setCheckable(true);
_voxelModeActions->addAction(_colorVoxelMode);
(_selectVoxelMode = voxelMenu->addAction(
"Select Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_S))->setCheckable(true);
"Select Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_O))->setCheckable(true);
_voxelModeActions->addAction(_selectVoxelMode);
(_eyedropperMode = voxelMenu->addAction(
"Get Color Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_G))->setCheckable(true);
"Get Color Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_G))->setCheckable(true);
_voxelModeActions->addAction(_eyedropperMode);
voxelMenu->addAction("Decrease Voxel Size", this, SLOT(decreaseVoxelSize()), QKeySequence::ZoomOut);
voxelMenu->addAction("Increase Voxel Size", this, SLOT(increaseVoxelSize()), QKeySequence::ZoomIn);
voxelMenu->addAction("Reset Swatch Colors", this, SLOT(resetSwatchColors()));
_voxelPaintColor = voxelMenu->addAction("Voxel Paint Color", this,
SLOT(chooseVoxelPaintColor()), Qt::META | Qt::Key_C);
_swatch.setAction(_voxelPaintColor);
QColor paintColor(128, 128, 128);
_voxelPaintColor->setData(paintColor);
_voxelPaintColor->setIcon(createSwatchIcon(paintColor));
(_destructiveAddVoxel = voxelMenu->addAction("Create Voxel is Destructive"))->setCheckable(true);
voxelMenu->addAction("Export Voxels", this, SLOT(exportVoxels()), Qt::CTRL | Qt::Key_E);
voxelMenu->addAction("Import Voxels", this, SLOT(importVoxels()), Qt::CTRL | Qt::Key_I);
voxelMenu->addAction("Cut Voxels", this, SLOT(cutVoxels()), Qt::CTRL | Qt::Key_X);
voxelMenu->addAction("Copy Voxels", this, SLOT(copyVoxels()), Qt::CTRL | Qt::Key_C);
voxelMenu->addAction("Paste Voxels", this, SLOT(pasteVoxels()), Qt::CTRL | Qt::Key_V);
voxelMenu->addAction("Export Voxels", this, SLOT(exportVoxels()), Qt::CTRL | Qt::Key_E);
voxelMenu->addAction("Import Voxels", this, SLOT(importVoxels()), Qt::CTRL | Qt::Key_I);
voxelMenu->addAction("Cut Voxels", this, SLOT(cutVoxels()), Qt::CTRL | Qt::Key_X);
voxelMenu->addAction("Copy Voxels", this, SLOT(copyVoxels()), Qt::CTRL | Qt::Key_C);
voxelMenu->addAction("Paste Voxels", this, SLOT(pasteVoxels()), Qt::CTRL | Qt::Key_V);
QMenu* debugMenu = menuBar->addMenu("Debug");
@ -1597,13 +1632,20 @@ void Application::initMenu() {
renderDebugMenu->addAction("FALSE Color Voxels by Distance", this, SLOT(doFalseColorizeByDistance()));
renderDebugMenu->addAction("FALSE Color Voxel Out of View", this, SLOT(doFalseColorizeInView()));
renderDebugMenu->addAction("FALSE Color Occluded Voxels", this, SLOT(doFalseColorizeOccluded()), Qt::CTRL | Qt::Key_O);
renderDebugMenu->addAction("FALSE Color Occluded V2 Voxels", this, SLOT(doFalseColorizeOccludedV2()), Qt::CTRL | Qt::Key_P);
renderDebugMenu->addAction("Show TRUE Colors", this, SLOT(doTrueVoxelColors()), Qt::CTRL | Qt::Key_T);
debugMenu->addAction("Wants Res-In", this, SLOT(setWantsResIn(bool)))->setCheckable(true);
debugMenu->addAction("Wants Monochrome", this, SLOT(setWantsMonochrome(bool)))->setCheckable(true);
debugMenu->addAction("Wants View Delta Sending", this, SLOT(setWantsDelta(bool)))->setCheckable(true);
(_shouldLowPassFilter = debugMenu->addAction("Test: LowPass filter"))->setCheckable(true);
debugMenu->addAction("Wants Occlusion Culling", this, SLOT(setWantsOcclusionCulling(bool)))->setCheckable(true);
debugMenu->addAction("Disable Occlusion Culling", this, SLOT(disableOcclusionCulling(bool)),
Qt::SHIFT | Qt::Key_C)->setCheckable(true);
(_renderCoverageMap = debugMenu->addAction("Render Coverage Map"))->setCheckable(true);
_renderCoverageMap->setShortcut(Qt::SHIFT | Qt::CTRL | Qt::Key_O);
(_renderCoverageMapV2 = debugMenu->addAction("Render Coverage Map V2"))->setCheckable(true);
_renderCoverageMapV2->setShortcut(Qt::SHIFT | Qt::CTRL | Qt::Key_P);
QMenu* settingsMenu = menuBar->addMenu("Settings");
(_settingsAutosave = settingsMenu->addAction("Autosave"))->setCheckable(true);
@ -1664,8 +1706,6 @@ void Application::init() {
_headMouseX = _mouseX = _glWidget->width() / 2;
_headMouseY = _mouseY = _glWidget->height() / 2;
_stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0);
_myAvatar.init();
_myAvatar.setPosition(START_LOCATION);
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
@ -1682,7 +1722,7 @@ void Application::init() {
LeapManager::initialize();
gettimeofday(&_timerStart, NULL);
gettimeofday(&_lastTimeIdle, NULL);
gettimeofday(&_lastTimeUpdated, NULL);
loadSettings();
if (!shouldDynamicallySetJitterBuffer()) {
@ -1691,12 +1731,35 @@ void Application::init() {
printLog("Loaded settings.\n");
sendAvatarVoxelURLMessage(_myAvatar.getVoxels()->getVoxelURL());
_palette.init(_glWidget->width(), _glWidget->height());
_palette.addAction(_addVoxelMode, 0, 0);
_palette.addAction(_deleteVoxelMode, 0, 1);
_palette.addTool(&_swatch);
_palette.addAction(_colorVoxelMode, 0, 2);
_palette.addAction(_eyedropperMode, 0, 3);
_palette.addAction(_selectVoxelMode, 0, 4);
}
const float MAX_AVATAR_EDIT_VELOCITY = 1.0f;
const float MAX_VOXEL_EDIT_DISTANCE = 20.0f;
const float HEAD_SPHERE_RADIUS = 0.07;
bool Application::isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, glm::vec3& eyePosition) {
NodeList* nodeList = NodeList::getInstance();
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) {
Avatar* avatar = (Avatar *) node->getLinkedData();
glm::vec3 headPosition = avatar->getHead().getPosition();
if (rayIntersectsSphere(mouseRayOrigin, mouseRayDirection, headPosition, HEAD_SPHERE_RADIUS)) {
eyePosition = avatar->getHead().getEyeLevelPosition();
return true;
}
}
}
return false;
}
void Application::update(float deltaTime) {
// Use Transmitter Hand to move hand if connected, else use mouse
@ -1724,8 +1787,15 @@ void Application::update(float deltaTime) {
_myAvatar.setMouseRay(mouseRayOrigin, mouseRayDirection);
// Set where I am looking based on my mouse ray (so that other people can see)
glm::vec3 myLookAtFromMouse(mouseRayOrigin + mouseRayDirection);
_myAvatar.getHead().setLookAtPosition(myLookAtFromMouse);
glm::vec3 eyePosition;
if (isLookingAtOtherAvatar(mouseRayOrigin, mouseRayDirection, eyePosition)) {
// If the mouse is over another avatar's head...
glm::vec3 myLookAtFromMouse(eyePosition);
_myAvatar.getHead().setLookAtPosition(myLookAtFromMouse);
} else {
glm::vec3 myLookAtFromMouse(mouseRayOrigin + mouseRayDirection);
_myAvatar.getHead().setLookAtPosition(myLookAtFromMouse);
}
// If we are dragging on a voxel, add thrust according to the amount the mouse is dragging
const float VOXEL_GRAB_THRUST = 0.0f;
@ -1821,20 +1891,19 @@ void Application::update(float deltaTime) {
if (_myAvatar.getMode() == AVATAR_MODE_WALKING) {
_handControl.stop();
}
// Update from Mouse
if (_mouseLook->isChecked()) {
QPoint mouse = QCursor::pos();
_myAvatar.updateFromMouse(_glWidget->mapFromGlobal(mouse).x(),
_glWidget->mapFromGlobal(mouse).y(),
_glWidget->width(),
_glWidget->height());
}
// Update from Touch
if (_isTouchPressed && _touchLook->isChecked()) {
_myAvatar.updateFromTouch(_touchAvgX - _touchDragStartedAvgX,
_touchAvgY - _touchDragStartedAvgY);
if (_isTouchPressed) {
float TOUCH_YAW_SCALE = -50.0f;
float TOUCH_PITCH_SCALE = -50.0f;
_myAvatar.getHead().addYaw((_touchAvgX - _lastTouchAvgX)
* TOUCH_YAW_SCALE
* deltaTime);
_myAvatar.getHead().addPitch((_touchAvgY - _lastTouchAvgY)
* TOUCH_PITCH_SCALE
* deltaTime);
_lastTouchAvgX = _touchAvgX;
_lastTouchAvgY = _touchAvgY;
}
// Leap finger-sensing device
@ -1925,7 +1994,7 @@ void Application::update(float deltaTime) {
if (_bandwidthDialog) {
_bandwidthDialog->update();
}
// Update audio stats for procedural sounds
#ifndef _WIN32
_audio.setLastAcceleration(_myAvatar.getThrust());
@ -1936,11 +2005,11 @@ void Application::update(float deltaTime) {
void Application::updateAvatar(float deltaTime) {
// Update my avatar's head position from gyros and/or webcam
_myAvatar.updateHeadFromGyrosAndOrWebcam(_gyroLook->isChecked(),
glm::vec3(_headCameraPitchYawScale,
_headCameraPitchYawScale,
_headCameraPitchYawScale));
// Update my avatar's state from gyros and/or webcam
_myAvatar.updateFromGyrosAndOrWebcam(_gyroLook->isChecked(),
glm::vec3(_headCameraPitchYawScale,
_headCameraPitchYawScale,
_headCameraPitchYawScale));
if (_serialHeadSensor.isActive()) {
@ -1962,6 +2031,17 @@ void Application::updateAvatar(float deltaTime) {
_headMouseY = max(_headMouseY, 0);
_headMouseY = min(_headMouseY, _glWidget->height());
// Set lookAtPosition if an avatar is at the center of the screen
glm::vec3 screenCenterRayOrigin, screenCenterRayDirection;
_viewFrustum.computePickRay(0.5, 0.5, screenCenterRayOrigin, screenCenterRayDirection);
glm::vec3 eyePosition;
if (isLookingAtOtherAvatar(screenCenterRayOrigin, screenCenterRayDirection, eyePosition)) {
glm::vec3 myLookAtFromMouse(eyePosition);
_myAvatar.getHead().setLookAtPosition(myLookAtFromMouse);
}
}
if (OculusManager::isConnected()) {
@ -2215,6 +2295,15 @@ void Application::displayOculus(Camera& whichCamera) {
void Application::displaySide(Camera& whichCamera) {
// transform by eye offset
// flip x if in mirror mode (also requires reversing winding order for backface culling)
if (_lookingInMirror->isChecked()) {
glScalef(-1.0f, 1.0f, 1.0f);
glFrontFace(GL_CW);
} else {
glFrontFace(GL_CCW);
}
glm::vec3 eyeOffsetPos = whichCamera.getEyeOffsetPosition();
glm::quat eyeOffsetOrient = whichCamera.getEyeOffsetOrientation();
glm::vec3 eyeOffsetAxis = glm::axis(eyeOffsetOrient);
@ -2250,6 +2339,9 @@ void Application::displaySide(Camera& whichCamera) {
glMateriali(GL_FRONT, GL_SHININESS, 96);
if (_renderStarsOn->isChecked()) {
if (!_stars.getFileLoaded()) {
_stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0);
}
// should be the first rendering pass - w/o depth buffer / lighting
// compute starfield alpha based on distance from atmosphere
@ -2348,6 +2440,7 @@ void Application::displaySide(Camera& whichCamera) {
// brad's frustum for debugging
if (_frustumOn->isChecked()) renderViewFrustum(_viewFrustum);
}
void Application::displayOverlay() {
@ -2396,6 +2489,9 @@ void Application::displayOverlay() {
if (_renderStatsOn->isChecked()) { displayStats(); }
// testing rendering coverage map
if (_renderCoverageMapV2->isChecked()) { renderCoverageMapV2(); }
if (_renderCoverageMap->isChecked()) { renderCoverageMap(); }
if (_bandwidthDisplayOn->isChecked()) { _bandwidthMeter.render(_glWidget->width(), _glWidget->height()); }
if (_logOn->isChecked()) { LogDisplay::instance.render(_glWidget->width(), _glWidget->height()); }
@ -2408,7 +2504,7 @@ void Application::displayOverlay() {
// Show on-screen msec timer
if (_renderFrameTimerOn->isChecked()) {
char frameTimer[10];
long long mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5);
uint64_t mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5);
sprintf(frameTimer, "%d\n", (int)(mSecsNow % 1000));
drawtext(_glWidget->width() - 100, _glWidget->height() - 20, 0.30, 0, 1.0, 0, frameTimer, 0, 0, 0);
drawtext(_glWidget->width() - 102, _glWidget->height() - 22, 0.30, 0, 1.0, 0, frameTimer, 1, 1, 1);
@ -2440,7 +2536,52 @@ void Application::displayOverlay() {
// render the webcam input frame
_webcam.renderPreview(_glWidget->width(), _glWidget->height());
_palette.render(_glWidget->width(), _glWidget->height());
if (_eyedropperMode->isChecked() && _voxelPaintColor->data().value<QColor>() != _swatch.getColor()) {
QColor color = _voxelPaintColor->data().value<QColor>();
TextRenderer textRenderer(SANS_FONT_FAMILY, 11, 50);
const char line1[] = "Assign this color to a swatch";
const char line2[] = "by choosing a key from 1 to 8.";
int left = (_glWidget->width() - POPUP_WIDTH - 2 * POPUP_MARGIN) / 2;
int top = _glWidget->height() / 40;
glBegin(GL_POLYGON);
glColor3f(0.0f, 0.0f, 0.0f);
for (double a = M_PI; a < 1.5f * M_PI; a += POPUP_STEP) {
glVertex2f(left + POPUP_MARGIN * cos(a) , top + POPUP_MARGIN * sin(a));
}
for (double a = 1.5f * M_PI; a < 2.0f * M_PI; a += POPUP_STEP) {
glVertex2f(left + POPUP_WIDTH + POPUP_MARGIN * cos(a), top + POPUP_MARGIN * sin(a));
}
for (double a = 0.0f; a < 0.5f * M_PI; a += POPUP_STEP) {
glVertex2f(left + POPUP_WIDTH + POPUP_MARGIN * cos(a), top + POPUP_HEIGHT + POPUP_MARGIN * sin(a));
}
for (double a = 0.5f * M_PI; a < 1.0f * M_PI; a += POPUP_STEP) {
glVertex2f(left + POPUP_MARGIN * cos(a) , top + POPUP_HEIGHT + POPUP_MARGIN * sin(a));
}
glEnd();
glBegin(GL_QUADS);
glColor3f(color.redF(),
color.greenF(),
color.blueF());
glVertex2f(left , top);
glVertex2f(left + SWATCH_WIDTH, top);
glVertex2f(left + SWATCH_WIDTH, top + SWATCH_HEIGHT);
glVertex2f(left , top + SWATCH_HEIGHT);
glEnd();
glColor3f(1.0f, 1.0f, 1.0f);
textRenderer.draw(left + SWATCH_WIDTH + POPUP_MARGIN, top + FIRST_LINE_OFFSET , line1);
textRenderer.draw(left + SWATCH_WIDTH + POPUP_MARGIN, top + SECOND_LINE_OFFSET, line2);
}
else {
_swatch.checkColor();
}
glPopMatrix();
}
@ -2536,8 +2677,8 @@ void Application::renderThrustAtVoxel(const glm::vec3& thrust) {
glVertex3f(voxelTouched.x + thrust.x, voxelTouched.y + thrust.y, voxelTouched.z + thrust.z);
glEnd();
}
}
void Application::renderLineToTouchedVoxel() {
// Draw a teal line to the voxel I am currently dragging on
if (_mousePressed) {
@ -2552,6 +2693,149 @@ void Application::renderLineToTouchedVoxel() {
}
}
glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) {
float horizontalScale = _glWidget->width() / 2.0f;
float verticalScale = _glWidget->height() / 2.0f;
// -1,-1 is 0,windowHeight
// 1,1 is windowWidth,0
// -1,1 1,1
// +-----------------------+
// | | |
// | | |
// | -1,0 | |
// |-----------+-----------|
// | 0,0 |
// | | |
// | | |
// | | |
// +-----------------------+
// -1,-1 1,-1
glm::vec2 screenPoint((projectedPoint.x + 1.0) * horizontalScale,
((projectedPoint.y + 1.0) * -verticalScale) + _glWidget->height());
return screenPoint;
}
// render the coverage map on screen
void Application::renderCoverageMapV2() {
//printLog("renderCoverageMap()\n");
glDisable(GL_LIGHTING);
glLineWidth(2.0);
glBegin(GL_LINES);
glColor3f(0,1,1);
renderCoverageMapsV2Recursively(&_voxels.myCoverageMapV2);
glEnd();
glEnable(GL_LIGHTING);
}
void Application::renderCoverageMapsV2Recursively(CoverageMapV2* map) {
// render ourselves...
if (map->isCovered()) {
BoundingBox box = map->getBoundingBox();
glm::vec2 firstPoint = getScaledScreenPoint(box.getVertex(0));
glm::vec2 lastPoint(firstPoint);
for (int i = 1; i < box.getVertexCount(); i++) {
glm::vec2 thisPoint = getScaledScreenPoint(box.getVertex(i));
glVertex2f(lastPoint.x, lastPoint.y);
glVertex2f(thisPoint.x, thisPoint.y);
lastPoint = thisPoint;
}
glVertex2f(lastPoint.x, lastPoint.y);
glVertex2f(firstPoint.x, firstPoint.y);
} else {
// iterate our children and call render on them.
for (int i = 0; i < CoverageMapV2::NUMBER_OF_CHILDREN; i++) {
CoverageMapV2* childMap = map->getChild(i);
if (childMap) {
renderCoverageMapsV2Recursively(childMap);
}
}
}
}
// render the coverage map on screen
void Application::renderCoverageMap() {
//printLog("renderCoverageMap()\n");
glDisable(GL_LIGHTING);
glLineWidth(2.0);
glBegin(GL_LINES);
glColor3f(0,0,1);
renderCoverageMapsRecursively(&_voxels.myCoverageMap);
glEnd();
glEnable(GL_LIGHTING);
}
void Application::renderCoverageMapsRecursively(CoverageMap* map) {
for (int i = 0; i < map->getPolygonCount(); i++) {
VoxelProjectedPolygon* polygon = map->getPolygon(i);
if (polygon->getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_BOTTOM)) {
glColor3f(.5,0,0); // dark red
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_RIGHT)) {
glColor3f(.5,.5,0); // dark yellow
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT)) {
glColor3f(.5,.5,.5); // gray
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT | PROJECTION_BOTTOM)) {
glColor3f(.5,0,.5); // dark magenta
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_BOTTOM)) {
glColor3f(.75,0,0); // red
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_TOP)) {
glColor3f(1,0,1); // magenta
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT | PROJECTION_TOP)) {
glColor3f(0,0,1); // Blue
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_RIGHT | PROJECTION_TOP)) {
glColor3f(0,1,0); // green
} else if (polygon->getProjectionType() == (PROJECTION_NEAR)) {
glColor3f(1,1,0); // yellow
} else if (polygon->getProjectionType() == (PROJECTION_FAR | PROJECTION_RIGHT | PROJECTION_BOTTOM)) {
glColor3f(0,.5,.5); // dark cyan
} else {
glColor3f(1,0,0);
}
glm::vec2 firstPoint = getScaledScreenPoint(polygon->getVertex(0));
glm::vec2 lastPoint(firstPoint);
for (int i = 1; i < polygon->getVertexCount(); i++) {
glm::vec2 thisPoint = getScaledScreenPoint(polygon->getVertex(i));
glVertex2f(lastPoint.x, lastPoint.y);
glVertex2f(thisPoint.x, thisPoint.y);
lastPoint = thisPoint;
}
glVertex2f(lastPoint.x, lastPoint.y);
glVertex2f(firstPoint.x, firstPoint.y);
}
// iterate our children and call render on them.
for (int i = 0; i < CoverageMapV2::NUMBER_OF_CHILDREN; i++) {
CoverageMap* childMap = map->getChild(i);
if (childMap) {
renderCoverageMapsRecursively(childMap);
}
}
}
/////////////////////////////////////////////////////////////////////////////////////
// renderViewFrustum()
//
@ -2815,6 +3099,7 @@ void Application::eyedropperVoxelUnderCursor() {
}
void Application::goHome() {
printLog("Going Home!\n");
_myAvatar.setPosition(START_LOCATION);
}
@ -2992,7 +3277,8 @@ void Application::loadSettings(QSettings* settings) {
settings->endGroup();
scanMenuBar(&Application::loadAction, settings);
getAvatar()->loadData(settings);
getAvatar()->loadData(settings);
_swatch.loadData(settings);
}
@ -3014,6 +3300,7 @@ void Application::saveSettings(QSettings* settings) {
scanMenuBar(&Application::saveAction, settings);
getAvatar()->saveData(settings);
_swatch.saveData(settings);
}
void Application::importSettings() {

View file

@ -39,6 +39,8 @@
#include "Webcam.h"
#include "renderer/GeometryCache.h"
#include "ui/ChatEntry.h"
#include "ToolsPalette.h"
#include "Swatch.h"
class QAction;
class QActionGroup;
@ -129,16 +131,17 @@ private slots:
void doFalseRandomizeEveryOtherVoxelColors();
void doFalseColorizeByDistance();
void doFalseColorizeOccluded();
void doFalseColorizeOccludedV2();
void doFalseColorizeInView();
void doTrueVoxelColors();
void doTreeStats();
void setWantsMonochrome(bool wantsMonochrome);
void setWantsResIn(bool wantsResIn);
void setWantsDelta(bool wantsDelta);
void setWantsOcclusionCulling(bool wantsOcclusionCulling);
void disableOcclusionCulling(bool disableOcclusionCulling);
void updateVoxelModeActions();
void decreaseVoxelSize();
void increaseVoxelSize();
void resetSwatchColors();
void chooseVoxelPaintColor();
void loadSettings(QSettings* set = NULL);
void saveSettings(QSettings* set = NULL);
@ -150,6 +153,16 @@ private slots:
void copyVoxels();
void pasteVoxels();
void runTests();
void renderCoverageMap();
void renderCoverageMapsRecursively(CoverageMap* map);
void renderCoverageMapV2();
void renderCoverageMapsV2Recursively(CoverageMapV2* map);
glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint);
void goHome();
private:
static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes,
@ -168,6 +181,7 @@ private:
void init();
void update(float deltaTime);
bool isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, glm::vec3& eyePosition);
void updateAvatar(float deltaTime);
void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
@ -184,7 +198,6 @@ private:
void maybeEditVoxelUnderCursor();
void deleteVoxelUnderCursor();
void eyedropperVoxelUnderCursor();
void goHome();
void resetSensors();
void setMenuShortcutsEnabled(bool enabled);
@ -211,8 +224,6 @@ private:
QAction* _shouldLowPassFilter; // Use test lowpass filter
QAction* _gyroLook; // Whether to allow the gyro data from head to move your view
QAction* _renderAvatarBalls; // Switch between voxels and joints/balls for avatar render
QAction* _mouseLook; // Whether the have the mouse near edge of screen move your view
QAction* _touchLook; // Whether a 2-finger touch may be used to control look direction
QAction* _showHeadMouse; // Whether the have the mouse near edge of screen move your view
QAction* _transmitterDrives; // Whether to have Transmitter data move/steer the Avatar
QAction* _gravityUse; // Whether gravity is on or not
@ -244,6 +255,9 @@ private:
QAction* _fullScreenMode; // whether we are in full screen mode
QAction* _frustumRenderModeAction;
QAction* _settingsAutosave; // Whether settings are saved automatically
QAction* _renderCoverageMapV2;
QAction* _renderCoverageMap;
BandwidthMeter _bandwidthMeter;
BandwidthDialog* _bandwidthDialog;
@ -260,7 +274,7 @@ private:
float _fps;
timeval _applicationStartupTime;
timeval _timerStart, _timerEnd;
timeval _lastTimeIdle;
timeval _lastTimeUpdated;
bool _justStarted;
Stars _stars;
@ -312,6 +326,8 @@ private:
float _touchAvgX;
float _touchAvgY;
float _lastTouchAvgX;
float _lastTouchAvgY;
float _touchDragStartedAvgX;
float _touchDragStartedAvgY;
bool _isTouchPressed; // true if multitouch has been pressed (clear when finished)
@ -360,7 +376,12 @@ private:
int _packetsPerSecond;
int _bytesPerSecond;
int _bytesCount;
StDev _idleLoopStdev;
float _idleLoopMeasuredJitter;
ToolsPalette _palette;
Swatch _swatch;
};
#endif /* defined(__interface__Application__) */

View file

@ -148,121 +148,121 @@ void Avatar::initializeBodyBalls() {
_bodyBall[ BODY_BALL_HEAD_BASE ].radius = 0.07;
_bodyBall[ BODY_BALL_LEFT_COLLAR ].radius = 0.04;
_bodyBall[ BODY_BALL_LEFT_SHOULDER ].radius = 0.03;
_bodyBall[ BODY_BALL_LEFT_ELBOW ].radius = 0.02;
_bodyBall[ BODY_BALL_LEFT_ELBOW ].radius = 0.02;
_bodyBall[ BODY_BALL_LEFT_WRIST ].radius = 0.02;
_bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].radius = 0.01;
_bodyBall[ BODY_BALL_RIGHT_COLLAR ].radius = 0.04;
_bodyBall[ BODY_BALL_RIGHT_SHOULDER ].radius = 0.03;
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].radius = 0.02;
_bodyBall[ BODY_BALL_RIGHT_SHOULDER ].radius = 0.03;
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].radius = 0.02;
_bodyBall[ BODY_BALL_RIGHT_WRIST ].radius = 0.02;
_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].radius = 0.01;
_bodyBall[ BODY_BALL_LEFT_HIP ].radius = 0.04;
_bodyBall[ BODY_BALL_LEFT_HIP ].radius = 0.04;
//_bodyBall[ BODY_BALL_LEFT_MID_THIGH ].radius = 0.03;
_bodyBall[ BODY_BALL_LEFT_KNEE ].radius = 0.025;
_bodyBall[ BODY_BALL_LEFT_HEEL ].radius = 0.025;
_bodyBall[ BODY_BALL_LEFT_TOES ].radius = 0.025;
_bodyBall[ BODY_BALL_RIGHT_HIP ].radius = 0.04;
_bodyBall[ BODY_BALL_RIGHT_KNEE ].radius = 0.025;
_bodyBall[ BODY_BALL_RIGHT_HEEL ].radius = 0.025;
_bodyBall[ BODY_BALL_RIGHT_TOES ].radius = 0.025;
_bodyBall[ BODY_BALL_LEFT_KNEE ].radius = 0.025;
_bodyBall[ BODY_BALL_LEFT_HEEL ].radius = 0.025;
_bodyBall[ BODY_BALL_LEFT_TOES ].radius = 0.025;
_bodyBall[ BODY_BALL_RIGHT_HIP ].radius = 0.04;
_bodyBall[ BODY_BALL_RIGHT_KNEE ].radius = 0.025;
_bodyBall[ BODY_BALL_RIGHT_HEEL ].radius = 0.025;
_bodyBall[ BODY_BALL_RIGHT_TOES ].radius = 0.025;
// specify the parent joint for each ball
_bodyBall[ BODY_BALL_PELVIS ].parentJoint = AVATAR_JOINT_PELVIS;
_bodyBall[ BODY_BALL_PELVIS ].parentJoint = AVATAR_JOINT_PELVIS;
_bodyBall[ BODY_BALL_TORSO ].parentJoint = AVATAR_JOINT_TORSO;
_bodyBall[ BODY_BALL_CHEST ].parentJoint = AVATAR_JOINT_CHEST;
_bodyBall[ BODY_BALL_NECK_BASE ].parentJoint = AVATAR_JOINT_NECK_BASE;
_bodyBall[ BODY_BALL_CHEST ].parentJoint = AVATAR_JOINT_CHEST;
_bodyBall[ BODY_BALL_NECK_BASE ].parentJoint = AVATAR_JOINT_NECK_BASE;
_bodyBall[ BODY_BALL_HEAD_BASE ].parentJoint = AVATAR_JOINT_HEAD_BASE;
_bodyBall[ BODY_BALL_HEAD_TOP ].parentJoint = AVATAR_JOINT_HEAD_TOP;
_bodyBall[ BODY_BALL_LEFT_COLLAR ].parentJoint = AVATAR_JOINT_LEFT_COLLAR;
_bodyBall[ BODY_BALL_LEFT_SHOULDER ].parentJoint = AVATAR_JOINT_LEFT_SHOULDER;
_bodyBall[ BODY_BALL_LEFT_ELBOW ].parentJoint = AVATAR_JOINT_LEFT_ELBOW;
_bodyBall[ BODY_BALL_LEFT_WRIST ].parentJoint = AVATAR_JOINT_LEFT_WRIST;
_bodyBall[ BODY_BALL_LEFT_ELBOW ].parentJoint = AVATAR_JOINT_LEFT_ELBOW;
_bodyBall[ BODY_BALL_LEFT_WRIST ].parentJoint = AVATAR_JOINT_LEFT_WRIST;
_bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].parentJoint = AVATAR_JOINT_LEFT_FINGERTIPS;
_bodyBall[ BODY_BALL_RIGHT_COLLAR ].parentJoint = AVATAR_JOINT_RIGHT_COLLAR;
_bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentJoint = AVATAR_JOINT_RIGHT_SHOULDER;
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentJoint = AVATAR_JOINT_RIGHT_ELBOW;
_bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentJoint = AVATAR_JOINT_RIGHT_SHOULDER;
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentJoint = AVATAR_JOINT_RIGHT_ELBOW;
_bodyBall[ BODY_BALL_RIGHT_WRIST ].parentJoint = AVATAR_JOINT_RIGHT_WRIST;
_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].parentJoint = AVATAR_JOINT_RIGHT_FINGERTIPS;
_bodyBall[ BODY_BALL_LEFT_HIP ].parentJoint = AVATAR_JOINT_LEFT_HIP;
_bodyBall[ BODY_BALL_LEFT_KNEE ].parentJoint = AVATAR_JOINT_LEFT_KNEE;
_bodyBall[ BODY_BALL_LEFT_HEEL ].parentJoint = AVATAR_JOINT_LEFT_HEEL;
_bodyBall[ BODY_BALL_LEFT_TOES ].parentJoint = AVATAR_JOINT_LEFT_TOES;
_bodyBall[ BODY_BALL_RIGHT_HIP ].parentJoint = AVATAR_JOINT_RIGHT_HIP;
_bodyBall[ BODY_BALL_RIGHT_KNEE ].parentJoint = AVATAR_JOINT_RIGHT_KNEE;
_bodyBall[ BODY_BALL_RIGHT_HEEL ].parentJoint = AVATAR_JOINT_RIGHT_HEEL;
_bodyBall[ BODY_BALL_RIGHT_TOES ].parentJoint = AVATAR_JOINT_RIGHT_TOES;
_bodyBall[ BODY_BALL_LEFT_HIP ].parentJoint = AVATAR_JOINT_LEFT_HIP;
_bodyBall[ BODY_BALL_LEFT_KNEE ].parentJoint = AVATAR_JOINT_LEFT_KNEE;
_bodyBall[ BODY_BALL_LEFT_HEEL ].parentJoint = AVATAR_JOINT_LEFT_HEEL;
_bodyBall[ BODY_BALL_LEFT_TOES ].parentJoint = AVATAR_JOINT_LEFT_TOES;
_bodyBall[ BODY_BALL_RIGHT_HIP ].parentJoint = AVATAR_JOINT_RIGHT_HIP;
_bodyBall[ BODY_BALL_RIGHT_KNEE ].parentJoint = AVATAR_JOINT_RIGHT_KNEE;
_bodyBall[ BODY_BALL_RIGHT_HEEL ].parentJoint = AVATAR_JOINT_RIGHT_HEEL;
_bodyBall[ BODY_BALL_RIGHT_TOES ].parentJoint = AVATAR_JOINT_RIGHT_TOES;
//_bodyBall[ BODY_BALL_LEFT_MID_THIGH].parentJoint = AVATAR_JOINT_LEFT_HIP;
//_bodyBall[ BODY_BALL_LEFT_MID_THIGH ].parentJoint = AVATAR_JOINT_LEFT_HIP;
// specify the parent offset for each ball
_bodyBall[ BODY_BALL_PELVIS ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_PELVIS ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_TORSO ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_CHEST ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_NECK_BASE ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_CHEST ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_NECK_BASE ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_HEAD_BASE ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_HEAD_TOP ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_COLLAR ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_SHOULDER ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_ELBOW ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_WRIST ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_ELBOW ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_WRIST ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_COLLAR ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_WRIST ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_HIP ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_KNEE ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_HEEL ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_TOES ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_HIP ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_KNEE ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_HEEL ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_TOES ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_HIP ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_KNEE ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_HEEL ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_TOES ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_HIP ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_KNEE ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_HEEL ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_TOES ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
//_bodyBall[ BODY_BALL_LEFT_MID_THIGH].parentOffset = glm::vec3(-0.1, -0.1, 0.0);
//_bodyBall[ BODY_BALL_LEFT_MID_THIGH ].parentOffset = glm::vec3(-0.1, -0.1, 0.0);
// specify the parent BALL for each ball
_bodyBall[ BODY_BALL_PELVIS ].parentBall = BODY_BALL_NULL;
_bodyBall[ BODY_BALL_PELVIS ].parentBall = BODY_BALL_NULL;
_bodyBall[ BODY_BALL_TORSO ].parentBall = BODY_BALL_PELVIS;
_bodyBall[ BODY_BALL_CHEST ].parentBall = BODY_BALL_TORSO;
_bodyBall[ BODY_BALL_NECK_BASE ].parentBall = BODY_BALL_CHEST;
_bodyBall[ BODY_BALL_CHEST ].parentBall = BODY_BALL_TORSO;
_bodyBall[ BODY_BALL_NECK_BASE ].parentBall = BODY_BALL_CHEST;
_bodyBall[ BODY_BALL_HEAD_BASE ].parentBall = BODY_BALL_NECK_BASE;
_bodyBall[ BODY_BALL_HEAD_TOP ].parentBall = BODY_BALL_HEAD_BASE;
_bodyBall[ BODY_BALL_LEFT_COLLAR ].parentBall = BODY_BALL_CHEST;
_bodyBall[ BODY_BALL_LEFT_SHOULDER ].parentBall = BODY_BALL_LEFT_COLLAR;
_bodyBall[ BODY_BALL_LEFT_ELBOW ].parentBall = BODY_BALL_LEFT_SHOULDER;
_bodyBall[ BODY_BALL_LEFT_WRIST ].parentBall = BODY_BALL_LEFT_ELBOW;
_bodyBall[ BODY_BALL_LEFT_ELBOW ].parentBall = BODY_BALL_LEFT_SHOULDER;
_bodyBall[ BODY_BALL_LEFT_WRIST ].parentBall = BODY_BALL_LEFT_ELBOW;
_bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].parentBall = BODY_BALL_LEFT_WRIST;
_bodyBall[ BODY_BALL_RIGHT_COLLAR ].parentBall = BODY_BALL_CHEST;
_bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentBall = BODY_BALL_RIGHT_COLLAR;
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentBall = BODY_BALL_RIGHT_SHOULDER;
_bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentBall = BODY_BALL_RIGHT_COLLAR;
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentBall = BODY_BALL_RIGHT_SHOULDER;
_bodyBall[ BODY_BALL_RIGHT_WRIST ].parentBall = BODY_BALL_RIGHT_ELBOW;
_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].parentBall = BODY_BALL_RIGHT_WRIST;
_bodyBall[ BODY_BALL_LEFT_HIP ].parentBall = BODY_BALL_PELVIS;
_bodyBall[ BODY_BALL_LEFT_HIP ].parentBall = BODY_BALL_PELVIS;
//_bodyBall[ BODY_BALL_LEFT_MID_THIGH ].parentBall = BODY_BALL_LEFT_HIP;
//_bodyBall[ BODY_BALL_LEFT_MID_THIGH ].parentBall = BODY_BALL_LEFT_HIP;
// _bodyBall[ BODY_BALL_LEFT_KNEE ].parentBall = BODY_BALL_LEFT_MID_THIGH;
_bodyBall[ BODY_BALL_LEFT_KNEE ].parentBall = BODY_BALL_LEFT_HIP;
//_bodyBall[ BODY_BALL_LEFT_KNEE ].parentBall = BODY_BALL_LEFT_MID_THIGH;
_bodyBall[ BODY_BALL_LEFT_KNEE ].parentBall = BODY_BALL_LEFT_HIP;
_bodyBall[ BODY_BALL_LEFT_HEEL ].parentBall = BODY_BALL_LEFT_KNEE;
_bodyBall[ BODY_BALL_LEFT_TOES ].parentBall = BODY_BALL_LEFT_HEEL;
_bodyBall[ BODY_BALL_RIGHT_HIP ].parentBall = BODY_BALL_PELVIS;
_bodyBall[ BODY_BALL_RIGHT_KNEE ].parentBall = BODY_BALL_RIGHT_HIP;
_bodyBall[ BODY_BALL_RIGHT_HEEL ].parentBall = BODY_BALL_RIGHT_KNEE;
_bodyBall[ BODY_BALL_RIGHT_TOES ].parentBall = BODY_BALL_RIGHT_HEEL;
_bodyBall[ BODY_BALL_LEFT_HEEL ].parentBall = BODY_BALL_LEFT_KNEE;
_bodyBall[ BODY_BALL_LEFT_TOES ].parentBall = BODY_BALL_LEFT_HEEL;
_bodyBall[ BODY_BALL_RIGHT_HIP ].parentBall = BODY_BALL_PELVIS;
_bodyBall[ BODY_BALL_RIGHT_KNEE ].parentBall = BODY_BALL_RIGHT_HIP;
_bodyBall[ BODY_BALL_RIGHT_HEEL ].parentBall = BODY_BALL_RIGHT_KNEE;
_bodyBall[ BODY_BALL_RIGHT_TOES ].parentBall = BODY_BALL_RIGHT_HEEL;
/*
// to aid in hand-shaking and hand-holding, the right hand is not collidable
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].isCollidable = false;
_bodyBall[ BODY_BALL_RIGHT_WRIST ].isCollidable = false;
_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS].isCollidable = false;
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].isCollidable = false;
_bodyBall[ BODY_BALL_RIGHT_WRIST ].isCollidable = false;
_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].isCollidable = false;
*/
}
@ -285,24 +285,37 @@ void Avatar::reset() {
}
// Update avatar head rotation with sensor data
void Avatar::updateHeadFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyAngle) {
void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyAngle) {
SerialInterface* gyros = Application::getInstance()->getSerialHeadSensor();
Webcam* webcam = Application::getInstance()->getWebcam();
glm::vec3 estimatedPosition, estimatedRotation;
if (gyros->isActive()) {
if (webcam->isActive()) {
estimatedPosition = webcam->getEstimatedPosition();
}
estimatedRotation = gyros->getEstimatedRotation();
} else if (webcam->isActive()) {
estimatedPosition = webcam->getEstimatedPosition();
estimatedRotation = webcam->getEstimatedRotation();
} else {
return;
}
if (webcam->isActive()) {
estimatedPosition = webcam->getEstimatedPosition();
// compute and store the joint rotations
const JointVector& joints = webcam->getEstimatedJoints();
_joints.clear();
for (int i = 0; i < NUM_AVATAR_JOINTS; i++) {
if (joints.size() > i && joints[i].isValid) {
JointData data = { i, joints[i].rotation };
_joints.push_back(data);
if (i == AVATAR_JOINT_CHEST) {
// if we have a chest rotation, don't apply lean based on head
estimatedPosition = glm::vec3();
}
}
}
}
_head.setPitch(estimatedRotation.x * amplifyAngle.x);
_head.setYaw(estimatedRotation.y * amplifyAngle.y);
_head.setRoll(estimatedRotation.z * amplifyAngle.z);
@ -311,7 +324,7 @@ void Avatar::updateHeadFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& ampl
// Update torso lean distance based on accelerometer data
const float TORSO_LENGTH = 0.5f;
const float MAX_LEAN = 45.0f;
_head.setLeanSideways(glm::clamp(glm::degrees(atanf(-estimatedPosition.x * _leanScale / TORSO_LENGTH)),
_head.setLeanSideways(glm::clamp(glm::degrees(atanf(estimatedPosition.x * _leanScale / TORSO_LENGTH)),
-MAX_LEAN, MAX_LEAN));
_head.setLeanForward(glm::clamp(glm::degrees(atanf(estimatedPosition.z * _leanScale / TORSO_LENGTH)),
-MAX_LEAN, MAX_LEAN));
@ -337,34 +350,7 @@ glm::vec3 Avatar::getUprightHeadPosition() const {
return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, _pelvisToHeadLength, 0.0f);
}
void Avatar::updateFromMouse(int mouseX, int mouseY, int screenWidth, int screenHeight) {
// Update head yaw and pitch based on mouse input
const float MOUSE_ROTATE_SPEED = 0.01f;
const float MOUSE_PITCH_SPEED = 0.02f;
const int TITLE_BAR_HEIGHT = 46;
if ((mouseX > 1) && (mouseX < screenWidth) && (mouseY > TITLE_BAR_HEIGHT) && (mouseY < screenHeight)) {
//
// Mouse must be inside screen (not at edge) and not on title bar for movement to happen
//
int pixelMoveThreshold = screenWidth / 6;
glm::vec2 mouseVector(mouseX - (screenWidth / 2), mouseY - (screenHeight / 2));
if (glm::length(mouseVector) > pixelMoveThreshold) {
mouseVector -= glm::normalize(mouseVector) * (float) pixelMoveThreshold;
_head.addYaw(-mouseVector.x * MOUSE_ROTATE_SPEED);
_head.addPitch(-mouseVector.y * MOUSE_PITCH_SPEED);
}
}
}
void Avatar::updateFromTouch(float touchAvgDistX, float touchAvgDistY) {
const float TOUCH_ROTATE_SPEED = 0.01f;
const float TOUCH_PITCH_SPEED = 0.02f;
_head.addYaw(-touchAvgDistX * TOUCH_ROTATE_SPEED);
_head.addPitch(-touchAvgDistY * TOUCH_PITCH_SPEED);
}
void Avatar::updateThrust(float deltaTime, Transmitter * transmitter) {
//
@ -485,9 +471,18 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
_skeleton.joint[AVATAR_JOINT_TORSO].rotation = glm::quat(glm::radians(glm::vec3(
_head.getLeanForward(), 0.0f, _head.getLeanSideways())));
// update avatar skeleton
_skeleton.update(deltaTime, getOrientation(), _position);
// apply joint data (if any) to skeleton
bool enableHandMovement = true;
for (vector<JointData>::iterator it = _joints.begin(); it != _joints.end(); it++) {
_skeleton.joint[it->jointID].rotation = it->rotation;
// disable hand movement if we have joint info for the right wrist
enableHandMovement &= (it->jointID != AVATAR_JOINT_RIGHT_WRIST);
}
// update avatar skeleton
_skeleton.update(deltaTime, getOrientation(), _position);
//determine the lengths of the body springs now that we have updated the skeleton at least once
if (!_ballSpringsInitialized) {
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
@ -518,7 +513,7 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
}
//update the movement of the hand and process handshaking with other avatars...
updateHandMovementAndTouching(deltaTime);
updateHandMovementAndTouching(deltaTime, enableHandMovement);
_avatarTouch.simulate(deltaTime);
// apply gravity and collision with the ground/floor
@ -698,7 +693,7 @@ void Avatar::setOrientation(const glm::quat& orientation) {
_bodyRoll = eulerAngles.z;
}
void Avatar::updateHandMovementAndTouching(float deltaTime) {
void Avatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMovement) {
glm::quat orientation = getOrientation();
@ -707,12 +702,14 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) {
glm::vec3 up = orientation * IDENTITY_UP;
glm::vec3 front = orientation * IDENTITY_FRONT;
glm::vec3 transformedHandMovement
= right * _movedHandOffset.x * 2.0f
+ up * -_movedHandOffset.y * 2.0f
+ front * -_movedHandOffset.y * 2.0f;
if (enableHandMovement) {
glm::vec3 transformedHandMovement =
right * _movedHandOffset.x * 2.0f +
up * -_movedHandOffset.y * 2.0f +
front * -_movedHandOffset.y * 2.0f;
_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position += transformedHandMovement;
_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position += transformedHandMovement;
}
if (isMyAvatar()) {
_avatarTouch.setMyBodyPosition(_position);
@ -803,7 +800,9 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) {
//constrain right arm length and re-adjust elbow position as it bends
// NOTE - the following must be called on all avatars - not just _isMine
updateArmIKAndConstraints(deltaTime);
if (enableHandMovement) {
updateArmIKAndConstraints(deltaTime);
}
//Set right hand position and state to be transmitted, and also tell AvatarTouch about it
if (isMyAvatar()) {
@ -1223,7 +1222,7 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
// Always render other people, and render myself when beyond threshold distance
if (b == BODY_BALL_HEAD_BASE) { // the head is rendered as a special
if (alpha > 0.0f) {
_head.render(lookingInMirror, alpha);
_head.render(alpha);
}
} else if (alpha > 0.0f) {
// Render the body ball sphere

View file

@ -87,9 +87,7 @@ public:
void reset();
void simulate(float deltaTime, Transmitter* transmitter);
void updateThrust(float deltaTime, Transmitter * transmitter);
void updateHeadFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyAngles);
void updateFromMouse(int mouseX, int mouseY, int screenWidth, int screenHeight);
void updateFromTouch(float touchAvgDistX, float touchAvgDistY);
void updateFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyAngles);
void addBodyYaw(float y) {_bodyYaw += y;};
void render(bool lookingInMirror, bool renderAvatarBalls);
@ -151,10 +149,6 @@ public:
// Get the position/rotation of a single body ball
void getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const;
//read/write avatar data
void writeAvatarDataToFile();
void readAvatarDataFromFile();
static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2);
@ -229,7 +223,7 @@ private:
void updateBodyBalls( float deltaTime );
void calculateBoneLengths();
void readSensors();
void updateHandMovementAndTouching(float deltaTime);
void updateHandMovementAndTouching(float deltaTime, bool enableHandMovement);
void updateAvatarCollisions(float deltaTime);
void updateArmIKAndConstraints( float deltaTime );
void updateCollisionWithSphere( glm::vec3 position, float radius, float deltaTime );

View file

@ -54,6 +54,7 @@ Head::Head(Avatar* owningAvatar) :
_rotation(0.0f, 0.0f, 0.0f),
_leftEyePosition(0.0f, 0.0f, 0.0f),
_rightEyePosition(0.0f, 0.0f, 0.0f),
_eyeLevelPosition(0.0f, 0.0f, 0.0f),
_leftEyeBrowPosition(0.0f, 0.0f, 0.0f),
_rightEyeBrowPosition(0.0f, 0.0f, 0.0f),
_leftEarPosition(0.0f, 0.0f, 0.0f),
@ -67,7 +68,6 @@ Head::Head(Avatar* owningAvatar) :
_audioAttack(0.0f),
_returnSpringScale(1.0f),
_bodyRotation(0.0f, 0.0f, 0.0f),
_lookingInMirror(false),
_renderLookatVectors(false),
_mohawkTriangleFan(NULL),
_mohawkColors(NULL),
@ -223,11 +223,11 @@ void Head::simulate(float deltaTime, bool isMine) {
if (isMine && _cameraFollowsHead) {
// If we are using gyros and using gyroLook, have the camera follow head but with a null region
// to create stable rendering view with small head movements.
const float CAMERA_FOLLOW_HEAD_RATE_START = 0.05f;
const float CAMERA_FOLLOW_HEAD_RATE_MAX = 0.25f;
const float CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE = 1.5f;
const float CAMERA_STOP_TOLERANCE_DEGREES = 0.25f;
const float CAMERA_START_TOLERANCE_DEGREES = 15.0f;
const float CAMERA_FOLLOW_HEAD_RATE_START = 0.01f;
const float CAMERA_FOLLOW_HEAD_RATE_MAX = 0.5f;
const float CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE = 1.05f;
const float CAMERA_STOP_TOLERANCE_DEGREES = 0.1f;
const float CAMERA_START_TOLERANCE_DEGREES = 2.0f;
float cameraHeadAngleDifference = glm::length(glm::vec2(_pitch - _cameraPitch, _yaw - _cameraYaw));
if (_isCameraMoving) {
_cameraFollowHeadRate = glm::clamp(_cameraFollowHeadRate * CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE,
@ -269,6 +269,8 @@ void Head::calculateGeometry() {
+ up * _scale * EYE_UP_OFFSET
+ front * _scale * EYE_FRONT_OFFSET;
_eyeLevelPosition = _position + up * _scale * EYE_UP_OFFSET;
//calculate the eyebrow positions
_leftEyeBrowPosition = _leftEyePosition;
_rightEyeBrowPosition = _rightEyePosition;
@ -283,11 +285,10 @@ void Head::calculateGeometry() {
}
void Head::render(bool lookingInMirror, float alpha) {
void Head::render(float alpha) {
_renderAlpha = alpha;
_lookingInMirror = lookingInMirror;
calculateGeometry();
glEnable(GL_DEPTH_TEST);
@ -375,8 +376,8 @@ void Head::renderMohawk() {
} else {
glPushMatrix();
glTranslatef(_position.x, _position.y, _position.z);
glRotatef((_lookingInMirror ? (_bodyRotation.y - _yaw) : (_bodyRotation.y + _yaw)), 0, 1, 0);
glRotatef(_lookingInMirror ? _roll: -_roll, 0, 0, 1);
glRotatef(_bodyRotation.y + _yaw, 0, 1, 0);
glRotatef(-_roll, 0, 0, 1);
glRotatef(-_pitch - _bodyRotation.x, 1, 0, 0);
glBegin(GL_TRIANGLE_FAN);
@ -391,8 +392,7 @@ void Head::renderMohawk() {
}
glm::quat Head::getOrientation() const {
return glm::quat(glm::radians(_bodyRotation)) * glm::quat(glm::radians(_lookingInMirror ?
glm::vec3(_pitch, -_yaw, -_roll) : glm::vec3(_pitch, _yaw, _roll)));
return glm::quat(glm::radians(_bodyRotation)) * glm::quat(glm::radians(glm::vec3(_pitch, _yaw, _roll)));
}
glm::quat Head::getCameraOrientation () const {
@ -627,7 +627,7 @@ void Head::renderEyeBalls() {
glm::vec3 rotationAxis = glm::axis(orientation);
glRotatef(glm::angle(orientation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
glScalef(EYELID_RADIUS, EYELID_RADIUS, EYELID_RADIUS);
glRotatef(-90 * _leftEyeBlink, 1, 0, 0);
glRotatef(-40 - 50 * _leftEyeBlink, 1, 0, 0);
Application::getInstance()->getGeometryCache()->renderHemisphere(15, 10);
glRotatef(180 * _leftEyeBlink, 1, 0, 0);
Application::getInstance()->getGeometryCache()->renderHemisphere(15, 10);
@ -640,7 +640,7 @@ void Head::renderEyeBalls() {
glm::vec3 rotationAxis = glm::axis(orientation);
glRotatef(glm::angle(orientation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
glScalef(EYELID_RADIUS, EYELID_RADIUS, EYELID_RADIUS);
glRotatef(-90 * _rightEyeBlink, 1, 0, 0);
glRotatef(-40 - 50 * _rightEyeBlink, 1, 0, 0);
Application::getInstance()->getGeometryCache()->renderHemisphere(15, 10);
glRotatef(180 * _rightEyeBlink, 1, 0, 0);
Application::getInstance()->getGeometryCache()->renderHemisphere(15, 10);

View file

@ -35,7 +35,7 @@ public:
void init();
void reset();
void simulate(float deltaTime, bool isMine);
void render(bool lookingInMirror, float alpha);
void render(float alpha);
void renderMohawk();
void setScale (float scale ) { _scale = scale; }
@ -53,7 +53,8 @@ public:
glm::quat getOrientation() const;
glm::quat getCameraOrientation () const;
glm::vec3 getPosition() const { return _position; }
glm::vec3 getPosition() const { return _position; }
const glm::vec3& getEyeLevelPosition() const { return _eyeLevelPosition; }
glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
glm::vec3 getUpDirection () const { return getOrientation() * IDENTITY_UP; }
glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
@ -88,7 +89,8 @@ private:
glm::vec3 _position;
glm::vec3 _rotation;
glm::vec3 _leftEyePosition;
glm::vec3 _rightEyePosition;
glm::vec3 _rightEyePosition;
glm::vec3 _eyeLevelPosition;
glm::vec3 _leftEyeBrowPosition;
glm::vec3 _rightEyeBrowPosition;
glm::vec3 _leftEarPosition;
@ -102,7 +104,6 @@ private:
float _audioAttack;
float _returnSpringScale; //strength of return springs
glm::vec3 _bodyRotation;
bool _lookingInMirror;
bool _renderLookatVectors;
HairTuft _hairTuft[NUM_HAIR_TUFTS];
glm::vec3* _mohawkTriangleFan;

View file

@ -5,7 +5,7 @@
// Read interface data from the gyros/accelerometer Invensense board using the SerialUSB
//
#ifdef __APPLE__
#ifndef _WIN32
#include <regex.h>
#include <sys/time.h>
#include <string>
@ -15,6 +15,11 @@
#include <glm/gtx/vector_angle.hpp>
extern "C" {
#include <inv_tty.h>
#include <inv_mpu.h>
}
#include <SharedUtil.h>
#include "Application.h"
@ -24,24 +29,31 @@
const short NO_READ_MAXIMUM_MSECS = 3000;
const int GRAVITY_SAMPLES = 60; // Use the first few samples to baseline values
const int SENSOR_FUSION_SAMPLES = 20;
const int NORTH_SAMPLES = 30;
const int ACCELERATION_SENSOR_FUSION_SAMPLES = 20;
const int COMPASS_SENSOR_FUSION_SAMPLES = 200;
const int LONG_TERM_RATE_SAMPLES = 1000;
const bool USING_INVENSENSE_MPU9150 = 1;
void SerialInterface::pair() {
#ifdef __APPLE__
#ifndef _WIN32
// look for a matching gyro setup
DIR *devDir;
struct dirent *entry;
int matchStatus;
regex_t regex;
// for now this only works on OS X, where the usb serial shows up as /dev/tty.usb*
// for now this only works on OS X, where the usb serial shows up as /dev/tty.usb*,
// and (possibly just Ubuntu) Linux, where it shows up as /dev/ttyACM*
if((devDir = opendir("/dev"))) {
while((entry = readdir(devDir))) {
#ifdef __APPLE__
regcomp(&regex, "tty\\.usb", REG_EXTENDED|REG_NOSUB);
#else
regcomp(&regex, "ttyACM", REG_EXTENDED|REG_NOSUB);
#endif
matchStatus = regexec(&regex, entry->d_name, (size_t) 0, NULL, 0);
if (matchStatus == 0) {
char *serialPortname = new char[100];
@ -60,7 +72,7 @@ void SerialInterface::pair() {
// connect to the serial port
void SerialInterface::initializePort(char* portname) {
#ifdef __APPLE__
#ifndef _WIN32
_serialDescriptor = open(portname, O_RDWR | O_NOCTTY | O_NDELAY);
printLog("Opening SerialUSB %s: ", portname);
@ -87,22 +99,17 @@ void SerialInterface::initializePort(char* portname) {
int currentFlags = fcntl(_serialDescriptor, F_GETFL);
fcntl(_serialDescriptor, F_SETFL, currentFlags & ~O_NONBLOCK);
// there are extra commands to send to the invensense when it fires up
// this takes it out of SLEEP
write(_serialDescriptor, "WR686B01\n", 9);
// delay after the wakeup
usleep(10000);
// make sure there's nothing queued up to be read
tcflush(_serialDescriptor, TCIOFLUSH);
// this disables streaming so there's no garbage data on reads
write(_serialDescriptor, "SD\n", 3);
char result[4];
read(_serialDescriptor, result, 4);
// delay after disabling streaming
usleep(10000);
// flush whatever was produced by the last two commands
tcflush(_serialDescriptor, TCIOFLUSH);
tty_set_file_descriptor(_serialDescriptor);
mpu_init(0);
mpu_set_sensors(INV_XYZ_GYRO | INV_XYZ_ACCEL | INV_XYZ_COMPASS);
}
printLog("Connected.\n");
@ -187,56 +194,39 @@ void SerialInterface::renderLevels(int width, int height) {
}
}
void convertHexToInt(unsigned char* sourceBuffer, int& destinationInt) {
unsigned int byte[2];
for(int i = 0; i < 2; i++) {
sscanf((char*) sourceBuffer + 2 * i, "%2x", &byte[i]);
}
int16_t result = (byte[0] << 8);
result += byte[1];
destinationInt = result;
}
void SerialInterface::readData(float deltaTime) {
#ifdef __APPLE__
#ifndef _WIN32
int initialSamples = totalSamples;
if (USING_INVENSENSE_MPU9150) {
unsigned char sensorBuffer[36];
// ask the invensense for raw gyro data
write(_serialDescriptor, "RD683B0E\n", 9);
read(_serialDescriptor, sensorBuffer, 36);
int accelXRate, accelYRate, accelZRate;
convertHexToInt(sensorBuffer + 6, accelZRate);
convertHexToInt(sensorBuffer + 10, accelYRate);
convertHexToInt(sensorBuffer + 14, accelXRate);
short accelData[3];
mpu_get_accel_reg(accelData, 0);
const float LSB_TO_METERS_PER_SECOND2 = 1.f / 16384.f * GRAVITY_EARTH;
// From MPU-9150 register map, with setting on
// highest resolution = +/- 2G
_lastAcceleration = glm::vec3(-accelXRate, -accelYRate, -accelZRate) * LSB_TO_METERS_PER_SECOND2;
int rollRate, yawRate, pitchRate;
convertHexToInt(sensorBuffer + 22, rollRate);
convertHexToInt(sensorBuffer + 26, yawRate);
convertHexToInt(sensorBuffer + 30, pitchRate);
_lastAcceleration = glm::vec3(-accelData[2], -accelData[1], -accelData[0]) * LSB_TO_METERS_PER_SECOND2;
short gyroData[3];
mpu_get_gyro_reg(gyroData, 0);
// Convert the integer rates to floats
const float LSB_TO_DEGREES_PER_SECOND = 1.f / 16.4f; // From MPU-9150 register map, 2000 deg/sec.
glm::vec3 rotationRates;
rotationRates[0] = ((float) -pitchRate) * LSB_TO_DEGREES_PER_SECOND;
rotationRates[1] = ((float) -yawRate) * LSB_TO_DEGREES_PER_SECOND;
rotationRates[2] = ((float) -rollRate) * LSB_TO_DEGREES_PER_SECOND;
rotationRates[0] = ((float) -gyroData[2]) * LSB_TO_DEGREES_PER_SECOND;
rotationRates[1] = ((float) -gyroData[1]) * LSB_TO_DEGREES_PER_SECOND;
rotationRates[2] = ((float) -gyroData[0]) * LSB_TO_DEGREES_PER_SECOND;
short compassData[3];
mpu_get_compass_reg(compassData, 0);
// Convert integer values to floats, update extents
_lastCompass = glm::vec3(compassData[2], -compassData[0], -compassData[1]);
// update and subtract the long term average
_averageRotationRates = (1.f - 1.f/(float)LONG_TERM_RATE_SAMPLES) * _averageRotationRates +
1.f/(float)LONG_TERM_RATE_SAMPLES * rotationRates;
@ -323,19 +313,31 @@ void SerialInterface::readData(float deltaTime) {
}
else {
if (totalSamples < GRAVITY_SAMPLES) {
_gravity = (1.f - 1.f/(float)GRAVITY_SAMPLES) * _gravity +
1.f/(float)GRAVITY_SAMPLES * _lastAcceleration;
_gravity = glm::mix(_gravity, _lastAcceleration, 1.0f / GRAVITY_SAMPLES);
// North samples start later, because the initial compass readings are screwy
int northSample = totalSamples - (GRAVITY_SAMPLES - NORTH_SAMPLES);
if (northSample == 0) {
_north = _lastCompass;
} else if (northSample > 0) {
_north = glm::mix(_north, _lastCompass, 1.0f / NORTH_SAMPLES);
}
} else {
// Use gravity reading to do sensor fusion on the pitch and roll estimation
estimatedRotation = safeMix(estimatedRotation,
rotationBetween(estimatedRotation * _lastAcceleration, _gravity) * estimatedRotation,
1.0f / SENSOR_FUSION_SAMPLES);
1.0f / ACCELERATION_SENSOR_FUSION_SAMPLES);
// Without a compass heading, always decay estimated Yaw slightly
const float YAW_DECAY = 0.999f;
glm::vec3 forward = estimatedRotation * glm::vec3(0.0f, 0.0f, -1.0f);
estimatedRotation = safeMix(glm::angleAxis(glm::degrees(atan2f(forward.x, -forward.z)),
glm::vec3(0.0f, 1.0f, 0.0f)) * estimatedRotation, estimatedRotation, YAW_DECAY);
// Update the compass extents
_compassMinima = glm::min(_compassMinima, _lastCompass);
_compassMaxima = glm::max(_compassMaxima, _lastCompass);
// Same deal with the compass heading
estimatedRotation = safeMix(estimatedRotation,
rotationBetween(estimatedRotation * recenterCompass(_lastCompass),
recenterCompass(_north)) * estimatedRotation,
1.0f / COMPASS_SENSOR_FUSION_SAMPLES);
}
}
@ -371,13 +373,17 @@ void SerialInterface::resetAverages() {
}
void SerialInterface::resetSerial() {
#ifdef __APPLE__
#ifndef _WIN32
resetAverages();
_active = false;
gettimeofday(&lastGoodRead, NULL);
#endif
}
glm::vec3 SerialInterface::recenterCompass(const glm::vec3& compass) {
// compensate for "hard iron" distortion by subtracting the midpoint on each axis; see
// http://www.sensorsmag.com/sensors/motion-velocity-displacement/compensating-tilt-hard-iron-and-soft-iron-effects-6475
return compass - (_compassMinima + _compassMaxima) * 0.5f;
}

View file

@ -13,7 +13,7 @@
#include "Log.h"
// These includes are for serial port reading/writing
#ifdef __APPLE__
#ifndef _WIN32
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
@ -33,11 +33,13 @@ public:
_estimatedVelocity(0, 0, 0),
_lastAcceleration(0, 0, 0),
_lastRotationRates(0, 0, 0),
_angularVelocityToLinearAccel( // experimentally derived initial values
_compassMinima(-235, -132, -184), // experimentally derived initial values follow
_compassMaxima(83, 155, 120),
_angularVelocityToLinearAccel(
0.003f, -0.001f, -0.006f,
-0.005f, -0.001f, -0.006f,
0.010f, 0.004f, 0.007f),
_angularAccelToLinearAccel( // experimentally derived initial values
_angularAccelToLinearAccel(
0.0f, 0.0f, 0.002f,
0.0f, 0.0f, 0.001f,
-0.002f, -0.002f, 0.0f)
@ -64,11 +66,14 @@ private:
void initializePort(char* portname);
void resetSerial();
glm::vec3 recenterCompass(const glm::vec3& compass);
bool _active;
int _serialDescriptor;
int totalSamples;
timeval lastGoodRead;
glm::vec3 _gravity;
glm::vec3 _north;
glm::vec3 _averageRotationRates;
glm::vec3 _averageAcceleration;
glm::vec3 _estimatedRotation;
@ -77,6 +82,9 @@ private:
glm::vec3 _estimatedAcceleration;
glm::vec3 _lastAcceleration;
glm::vec3 _lastRotationRates;
glm::vec3 _lastCompass;
glm::vec3 _compassMinima;
glm::vec3 _compassMaxima;
glm::mat3 _angularVelocityToLinearAccel;
glm::mat3 _angularAccelToLinearAccel;

View file

@ -6,6 +6,7 @@
#include "Skeleton.h"
#include "Util.h"
#include "world.h"
const float BODY_SPRING_DEFAULT_TIGHTNESS = 1000.0f;
const float FLOATING_HEIGHT = 0.13f;
@ -18,12 +19,21 @@ void Skeleton::initialize() {
for (int b = 0; b < NUM_AVATAR_JOINTS; b++) {
joint[b].parent = AVATAR_JOINT_NULL;
joint[b].position = glm::vec3(0.0, 0.0, 0.0);
joint[b].defaultPosePosition = glm::vec3(0.0, 0.0, 0.0);
joint[b].rotation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f);
joint[b].length = 0.0;
joint[b].bindRadius = 1.0f / 8;
}
// put the arms at the side
joint[AVATAR_JOINT_LEFT_ELBOW].rotation = glm::quat(glm::vec3(0.0f, 0.0f, PIf * 0.5f));
joint[AVATAR_JOINT_RIGHT_ELBOW].rotation = glm::quat(glm::vec3(0.0f, 0.0f, -PIf * 0.5f));
// bend the knees
joint[AVATAR_JOINT_LEFT_KNEE].rotation = joint[AVATAR_JOINT_RIGHT_KNEE].rotation =
glm::quat(glm::vec3(PIf / 8.0f, 0.0f, 0.0f));
joint[AVATAR_JOINT_LEFT_HEEL].rotation = joint[AVATAR_JOINT_RIGHT_HEEL].rotation =
glm::quat(glm::vec3(-PIf / 4.0f, 0.0f, 0.0f));
// specify the parental hierarchy
joint[ AVATAR_JOINT_PELVIS ].parent = AVATAR_JOINT_NULL;
joint[ AVATAR_JOINT_TORSO ].parent = AVATAR_JOINT_PELVIS;
@ -80,39 +90,9 @@ void Skeleton::initialize() {
joint[ AVATAR_JOINT_RIGHT_HEEL ].bindPosePosition = glm::vec3( 0.00, -0.23, 0.00 );
joint[ AVATAR_JOINT_RIGHT_TOES ].bindPosePosition = glm::vec3( 0.00, 0.00, -0.06 );
// specify the default pose position
joint[ AVATAR_JOINT_PELVIS ].defaultPosePosition = glm::vec3( 0.0, 0.0, 0.0 );
joint[ AVATAR_JOINT_TORSO ].defaultPosePosition = glm::vec3( 0.0, 0.09, -0.01 );
joint[ AVATAR_JOINT_CHEST ].defaultPosePosition = glm::vec3( 0.0, 0.09, -0.01 );
joint[ AVATAR_JOINT_NECK_BASE ].defaultPosePosition = glm::vec3( 0.0, 0.14, 0.01 );
joint[ AVATAR_JOINT_HEAD_BASE ].defaultPosePosition = glm::vec3( 0.0, 0.04, 0.00 );
joint[ AVATAR_JOINT_HEAD_TOP ].defaultPosePosition = glm::vec3( 0.0, 0.04, 0.00 );
joint[ AVATAR_JOINT_LEFT_COLLAR ].defaultPosePosition = glm::vec3( -0.06, 0.04, 0.01 );
joint[ AVATAR_JOINT_LEFT_SHOULDER ].defaultPosePosition = glm::vec3( -0.05, 0.0, 0.01 );
joint[ AVATAR_JOINT_LEFT_ELBOW ].defaultPosePosition = glm::vec3( 0.0, -0.16, 0.0 );
joint[ AVATAR_JOINT_LEFT_WRIST ].defaultPosePosition = glm::vec3( 0.0, -0.117, 0.0 );
joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].defaultPosePosition = glm::vec3( 0.0, -0.1, 0.0 );
joint[ AVATAR_JOINT_RIGHT_COLLAR ].defaultPosePosition = glm::vec3( 0.06, 0.04, 0.01 );
joint[ AVATAR_JOINT_RIGHT_SHOULDER ].defaultPosePosition = glm::vec3( 0.05, 0.0, 0.01 );
joint[ AVATAR_JOINT_RIGHT_ELBOW ].defaultPosePosition = glm::vec3( 0.0, -0.16, 0.0 );
joint[ AVATAR_JOINT_RIGHT_WRIST ].defaultPosePosition = glm::vec3( 0.0, -0.117, 0.0 );
joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].defaultPosePosition = glm::vec3( 0.0, -0.1, 0.0 );
joint[ AVATAR_JOINT_LEFT_HIP ].defaultPosePosition = glm::vec3( -0.05, 0.0, 0.02 );
joint[ AVATAR_JOINT_LEFT_KNEE ].defaultPosePosition = glm::vec3( 0.01, -0.25, -0.03 );
joint[ AVATAR_JOINT_LEFT_HEEL ].defaultPosePosition = glm::vec3( 0.01, -0.22, 0.08 );
joint[ AVATAR_JOINT_LEFT_TOES ].defaultPosePosition = glm::vec3( 0.00, -0.03, -0.05 );
joint[ AVATAR_JOINT_RIGHT_HIP ].defaultPosePosition = glm::vec3( 0.05, 0.0, 0.02 );
joint[ AVATAR_JOINT_RIGHT_KNEE ].defaultPosePosition = glm::vec3( -0.01, -0.25, -0.03 );
joint[ AVATAR_JOINT_RIGHT_HEEL ].defaultPosePosition = glm::vec3( -0.01, -0.22, 0.08 );
joint[ AVATAR_JOINT_RIGHT_TOES ].defaultPosePosition = glm::vec3( 0.00, -0.03, -0.05 );
// calculate bone length, absolute bind positions/rotations
for (int b = 0; b < NUM_AVATAR_JOINTS; b++) {
joint[b].length = glm::length(joint[b].defaultPosePosition);
joint[b].length = glm::length(joint[b].bindPosePosition);
if (joint[b].parent == AVATAR_JOINT_NULL) {
joint[b].absoluteBindPosePosition = joint[b].bindPosePosition;
@ -122,7 +102,7 @@ void Skeleton::initialize() {
joint[b].bindPosePosition;
glm::vec3 parentDirection = joint[ joint[b].parent ].absoluteBindPoseRotation * JOINT_DIRECTION;
joint[b].absoluteBindPoseRotation = rotationBetween(parentDirection, joint[b].bindPosePosition) *
joint[ joint[b].parent ].absoluteBindPoseRotation;
joint[ joint[b].parent ].absoluteBindPoseRotation;
}
}
}
@ -140,7 +120,7 @@ void Skeleton::update(float deltaTime, const glm::quat& orientation, glm::vec3 p
joint[b].position = joint[ joint[b].parent ].position;
}
glm::vec3 rotatedJointVector = joint[b].absoluteRotation * joint[b].defaultPosePosition;
glm::vec3 rotatedJointVector = joint[b].absoluteRotation * joint[b].bindPosePosition;
joint[b].position += rotatedJointVector;
}
}

View file

@ -63,7 +63,6 @@ public:
{
AvatarJointID parent; // which joint is this joint connected to?
glm::vec3 position; // the position at the "end" of the joint - in global space
glm::vec3 defaultPosePosition; // the parent relative position when the avatar is in the default pose
glm::vec3 bindPosePosition; // the parent relative position when the avatar is in the "T-pose"
glm::vec3 absoluteBindPosePosition; // the absolute position when the avatar is in the "T-pose"
glm::quat absoluteBindPoseRotation; // the absolute rotation when the avatar is in the "T-pose"

View file

@ -14,7 +14,7 @@
#undef __interface__Starfield_impl__
Stars::Stars() :
_controller(0l) {
_controller(0l), _fileLoaded(false) {
_controller = new starfield::Controller;
}
@ -23,7 +23,8 @@ Stars::~Stars() {
}
bool Stars::readInput(const char* url, const char* cacheFile, unsigned limit) {
return _controller->readInput(url, cacheFile, limit);
_fileLoaded = _controller->readInput(url, cacheFile, limit);
return _fileLoaded;
}
bool Stars::setResolution(unsigned k) {

View file

@ -65,6 +65,7 @@ class Stars {
float changeLOD(float factor,
float overalloc = 0.25, float realloc = 0.15);
bool getFileLoaded() const { return _fileLoaded; };
private:
// don't copy/assign
Stars(Stars const&); // = delete;
@ -73,6 +74,8 @@ class Stars {
// variables
starfield::Controller* _controller;
bool _fileLoaded;
};

169
interface/src/Swatch.cpp Normal file
View file

@ -0,0 +1,169 @@
#include "Swatch.h"
#include <iostream>
Swatch::Swatch(QAction* action) :
Tool(action, 0, -1, -1),
_textRenderer(MONO_FONT_FAMILY, 10, 100),
_selected(1) {
}
void Swatch::reset() {
for (int i = 0; i < 8; ++i) {
_colors[i].setRgb(colorBase[i][0],
colorBase[i][1],
colorBase[i][2]);
}
}
QColor Swatch::getColor() {
return _colors[_selected - 1];
}
void Swatch::checkColor() {
if (_action->data().value<QColor>() == _colors[_selected - 1]) {
return;
}
QPixmap map(16, 16);
map.fill(_colors[_selected - 1]);
_action->setData(_colors[_selected - 1]) ;
_action->setIcon(map);
}
void Swatch::saveData(QSettings* settings) {
settings->beginGroup("Swatch");
for (int i(0); i < SWATCH_SIZE; ++i) {
QString rx("R1"), gx("G1"), bx("B1");
rx[1] = '1' + i;
gx[1] = rx[1];
bx[1] = rx[1];
settings->setValue(rx, _colors[i].red());
settings->setValue(gx, _colors[i].green());
settings->setValue(bx, _colors[i].blue());
}
settings->endGroup();
}
void Swatch::loadData(QSettings* settings) {
settings->beginGroup("Swatch");
for (int i = 0; i < SWATCH_SIZE; ++i) {
QString rx("R1"), gx("G1"), bx("B1");
rx[1] = '1' + i;
gx[1] = rx[1];
bx[1] = rx[1];
_colors[i].setRgb(settings->value(rx, colorBase[i][0]).toInt(),
settings->value(gx, colorBase[i][1]).toInt(),
settings->value(bx, colorBase[i][2]).toInt());
}
settings->endGroup();
checkColor();
}
void Swatch::handleEvent(int key, bool getColor) {
int next(0);
switch (key) {
case Qt::Key_1:
next = 1;
break;
case Qt::Key_2:
next = 2;
break;
case Qt::Key_3:
next = 3;
break;
case Qt::Key_4:
next = 4;
break;
case Qt::Key_5:
next = 5;
break;
case Qt::Key_6:
next = 6;
break;
case Qt::Key_7:
next = 7;
break;
case Qt::Key_8:
next = 8;
break;
default:
break;
}
if (getColor) {
if (_action->data().value<QColor>() != _colors[_selected - 1]) {
_selected = next;
_colors[_selected - 1] = _action->data().value<QColor>();
}
} else {
_selected = next;
QPixmap map(16, 16);
map.fill(_colors[_selected - 1]);
_action->setData(_colors[_selected - 1]) ;
_action->setIcon(map);
}
}
void Swatch::render(int width, int height) {
char str[2];
int margin = 0.10f * height;
height = 0.75f * height;
glBegin(GL_QUADS);
glColor3f(0.0f, 0.0f, 0.0f);
glVertex2f(0, 8 * (height - margin) + margin);
glVertex2f(width, 8 * (height - margin) + margin);
glVertex2f(width, 0);
glVertex2f(0, 0);
glEnd();
for (unsigned int i = 0; i < SWATCH_SIZE; ++i) {
glBegin(GL_QUADS);
glColor3f(_colors[i].redF(),
_colors[i].greenF(),
_colors[i].blueF());
glVertex2f(margin , (i + 1) * (height - margin));
glVertex2f(width - margin, (i + 1) * (height - margin));
glVertex2f(width - margin, i * (height - margin) + margin);
glVertex2f(margin , i * (height - margin) + margin);
glEnd();
if (_colors[i].lightness() < 50) {
glBegin(GL_LINES);
glColor3f(1.0f, 1.0f, 1.0f);
glVertex2f(margin , (i + 1) * (height - margin));
glVertex2f(width - margin, (i + 1) * (height - margin));
glVertex2f(width - margin, (i + 1) * (height - margin));
glVertex2f(width - margin, i * (height - margin) + margin);
glVertex2f(width - margin, i * (height - margin) + margin);
glVertex2f(margin , i * (height - margin) + margin);
glVertex2f(margin , i * (height - margin) + margin);
glVertex2f(margin , (i + 1) * (height - margin));
glEnd();
} else {
glColor3f(0.0f, 0.0f, 0.0f);
}
if (_selected == i + 1) {
glBegin(GL_TRIANGLES);
glVertex2f(margin , (i + 1) * (height - margin) - margin);
glVertex2f(width/4 - margin, i * (height - margin) + height / 2.0f);
glVertex2f(margin , i * (height - margin) + margin + margin);
glEnd();
}
sprintf(str, "%d", i + 1);
_textRenderer.draw(3 * width/4, (i + 1) * (height - margin) - 0.2f * height, str);
}
glTranslated(0, 8 * (height - margin) + margin + 0.075f * height, 0);
}

43
interface/src/Swatch.h Normal file
View file

@ -0,0 +1,43 @@
//
// Swatch.h
// interface
//
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__Swatch__
#define __interface__Swatch__
#include "Tool.h"
#include "ui/TextRenderer.h"
static const int SWATCH_SIZE = 8;
static const int colorBase[8][3] = {{237, 175, 0},
{61, 211, 72},
{51, 204, 204},
{63, 169, 245},
{193, 99, 122},
{255, 54, 69},
{124, 36, 36},
{63, 35, 19}};
class Swatch : public Tool {
public:
Swatch(QAction* action);
QColor getColor();
void checkColor();
void saveData(QSettings* settings);
void loadData(QSettings* settings);
void reset();
void render(int width, int height);
void handleEvent(int key, bool getColor);
private:
TextRenderer _textRenderer;
QColor _colors[SWATCH_SIZE];
int _selected;
};
#endif /* defined(__interface__Swatch__) */

51
interface/src/Tool.cpp Normal file
View file

@ -0,0 +1,51 @@
#include "Tool.h"
#include <QSvgRenderer>
#include <QPainter>
#include <QGLWidget>
Tool::Tool(QAction *action, GLuint texture, int x, int y) :
_action(action),
_texture(texture),
_x(x),
_y(y) {
}
void Tool::setAction(QAction* action) {
_action = action;
}
bool Tool::isActive() {
return _action->isChecked();
}
void Tool::render(int width, int height) {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, _texture);
if (_action == 0 || _action->isChecked()) {
glColor3f(1.0f, 1.0f, 1.0f); // reset gl color
} else {
glColor3f(0.3f, 0.3f, 0.3f);
}
glBegin(GL_QUADS);
glTexCoord2f( _x / NUM_TOOLS_COLS, 1.0f - (_y + 1) / NUM_TOOLS_ROWS);
glVertex2f(0 , height);
glTexCoord2f((_x + 1) / NUM_TOOLS_COLS, 1.0f - (_y + 1) / NUM_TOOLS_ROWS);
glVertex2f(width, height);
glTexCoord2f((_x + 1) / NUM_TOOLS_COLS, 1.0f - _y / NUM_TOOLS_ROWS);
glVertex2f(width, 0);
glTexCoord2f( _x / NUM_TOOLS_COLS, 1.0f - _y / NUM_TOOLS_ROWS);
glVertex2f(0 , 0);
glEnd();
glDisable(GL_TEXTURE_2D);
glTranslated(0, 1.10f * height, 0);
}

55
interface/src/Tool.h Normal file
View file

@ -0,0 +1,55 @@
//
// Tool.h
// interface
//
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__Tool__
#define __interface__Tool__
#include <QAction>
#include "InterfaceConfig.h"
#include "Util.h"
class QAction;
// Number of rows and columns in the SVG file for the tool palette
static const int NUM_TOOLS_ROWS = 10;
static const int NUM_TOOLS_COLS = 2;
static const int SWATCHS_TOOLS_COUNT = 13; // 8 swatch + 5 tools
static const int WIDTH_MIN = 47; // Minimal tools width
static const float TOOLS_RATIO = 40.0f / 60.0f; // ratio height/width of tools icons
static const float PAL_SCREEN_RATIO = 3.0f / 100.0f; // Percentage of the screeen width the palette is going to occupy
// Swatch popup consts
static const float POPUP_STEP = 0.05f;
static const float POPUP_MARGIN = 10.0f;
static const int POPUP_WIDTH = 280;
static const int POPUP_HEIGHT = 30;
static const int SWATCH_WIDTH = 64;
static const int SWATCH_HEIGHT = 30;
static const int FIRST_LINE_OFFSET = 12;
static const int SECOND_LINE_OFFSET = 28;
class Tool {
public:
Tool(QAction* action, GLuint texture, int x, int y);
void setAction(QAction* action);
bool isActive();
virtual void render(int width, int height);
protected:
QAction* _action;
GLuint _texture;
// position in the SVG grid
double _x;
double _y;
};
#endif /* defined(__interface__Tool__) */

View file

@ -0,0 +1,79 @@
#include "ToolsPalette.h"
#include <QSvgRenderer>
#include <QPainter>
#include <QGLWidget>
#include <SharedUtil.h>
void ToolsPalette::init(int screenWidth, int screenHeight) {
_width = (PAL_SCREEN_RATIO * screenWidth < WIDTH_MIN) ? WIDTH_MIN : PAL_SCREEN_RATIO * screenWidth;
_height = TOOLS_RATIO * _width;
_left = screenWidth / 150;
_top = (screenHeight - SWATCHS_TOOLS_COUNT * _height) / 2;
// Load SVG
switchToResourcesParentIfRequired();
QSvgRenderer renderer(QString("./resources/images/hifi-interface-tools.svg"));
// Prepare a QImage with desired characteritisc
QImage image(NUM_TOOLS_COLS * _width, NUM_TOOLS_ROWS * _height, QImage::Format_ARGB32);
// Get QPainter that paints to the image
QPainter painter(&image);
renderer.render(&painter);
//get the OpenGL-friendly image
_textureImage = QGLWidget::convertToGLFormat(image);
glGenTextures(1, &_textureID);
glBindTexture(GL_TEXTURE_2D, _textureID);
//generate the texture
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
_textureImage.width(),
_textureImage.height(),
0, GL_RGBA, GL_UNSIGNED_BYTE,
_textureImage.bits());
//texture parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
void ToolsPalette::addAction(QAction* action, int x, int y) {
Tool* tmp = new Tool(action, _textureID, x, y);
_tools.push_back(tmp);
}
void ToolsPalette::addTool(Tool* tool) {
_tools.push_back(tool);
}
void ToolsPalette::render(int screenWidth, int screenHeight) {
_width = (PAL_SCREEN_RATIO * screenWidth < WIDTH_MIN) ? WIDTH_MIN : PAL_SCREEN_RATIO * screenWidth;
_height = TOOLS_RATIO * _width;
_left = screenWidth / 150;
_top = (screenHeight - SWATCHS_TOOLS_COUNT * _height) / 2;
glPushMatrix();
glTranslated(_left, _top, 0);
bool show = false;
for (unsigned int i = 0; i < _tools.size(); ++i) {
if (_tools[i]->isActive()) {
show = true;
break;
}
}
if (show) {
for (unsigned int i = 0; i < _tools.size(); ++i) {
_tools[i]->render(_width, _height);
}
}
glPopMatrix();
}

View file

@ -0,0 +1,35 @@
//
// ToolsPalette.h
// interface
//
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__ToolsPalette__
#define __interface__ToolsPalette__
#include "Tool.h"
#include "Swatch.h"
#include <vector>
class ToolsPalette {
public:
void init(int screenWidth, int screenHeight);
void addAction(QAction* action, int x, int y);
void addTool(Tool* tool);
void render(int screenWidth, int screenHeight);
private:
QImage _textureImage;
GLuint _textureID;
std::vector<Tool*> _tools;
int _top;
int _left;
int _width;
int _height;
};
#endif /* defined(__interface__ToolsPalette__) */

View file

@ -499,6 +499,34 @@ void runTimingTests() {
gettimeofday(&endTime, NULL);
elapsedMsecs = diffclock(&startTime, &endTime);
printLog("powf(f, 0.5) usecs: %f\n", 1000.0f * elapsedMsecs / (float) numTests);
// Vector Math
float distance;
glm::vec3 pointA(randVector()), pointB(randVector());
gettimeofday(&startTime, NULL);
for (int i = 1; i < numTests; i++) {
//glm::vec3 temp = pointA - pointB;
//float distanceSquared = glm::dot(temp, temp);
distance = glm::distance(pointA, pointB);
}
gettimeofday(&endTime, NULL);
elapsedMsecs = diffclock(&startTime, &endTime);
printLog("vector math usecs: %f [%f msecs total for %d tests]\n",
1000.0f * elapsedMsecs / (float) numTests, elapsedMsecs, numTests);
// Vec3 test
glm::vec3 vecA(randVector()), vecB(randVector());
float result;
gettimeofday(&startTime, NULL);
for (int i = 1; i < numTests; i++) {
glm::vec3 temp = vecA-vecB;
result = glm::dot(temp,temp);
}
gettimeofday(&endTime, NULL);
elapsedMsecs = diffclock(&startTime, &endTime);
printLog("vec3 assign and dot() usecs: %f\n", 1000.0f * elapsedMsecs / (float) numTests);
}
@ -509,3 +537,13 @@ float loadSetting(QSettings* settings, const char* name, float defaultValue) {
}
return value;
}
bool rayIntersectsSphere(glm::vec3& rayStarting, glm::vec3& rayNormalizedDirection, glm::vec3& sphereCenter, double sphereRadius) {
glm::vec3 vecFromRayToSphereCenter = sphereCenter - rayStarting;
double projection = glm::dot(vecFromRayToSphereCenter, rayNormalizedDirection);
double shortestDistance = sqrt(glm::dot(vecFromRayToSphereCenter, vecFromRayToSphereCenter) - projection * projection);
if (shortestDistance <= sphereRadius) {
return true;
}
return false;
}

View file

@ -25,7 +25,6 @@
// the standard mono font family
#define MONO_FONT_FAMILY "Courier"
void eulerToOrthonormals(glm::vec3 * angles, glm::vec3 * fwd, glm::vec3 * left, glm::vec3 * up);
float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos);
@ -72,4 +71,6 @@ void runTimingTests();
float loadSetting(QSettings* settings, const char* name, float defaultValue);
bool rayIntersectsSphere(glm::vec3& rayStarting, glm::vec3& rayNormalizedDirection, glm::vec3& sphereCenter, double sphereRadius);
#endif

View file

@ -23,6 +23,7 @@
#include "Log.h"
#include "VoxelConstants.h"
#include "CoverageMap.h"
#include "CoverageMapV2.h"
#include "InterfaceConfig.h"
#include "renderer/ProgramObject.h"
@ -167,17 +168,17 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
void VoxelSystem::setupNewVoxelsForDrawing() {
PerformanceWarning warn(_renderWarningsOn, "setupNewVoxelsForDrawing()"); // would like to include _voxelsInArrays, _voxelsUpdated
long long start = usecTimestampNow();
long long sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000;
uint64_t start = usecTimestampNow();
uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000;
bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging
if (!iAmDebugging && sinceLastTime <= std::max(_setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) {
if (!iAmDebugging && sinceLastTime <= std::max((float) _setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) {
return; // bail early, it hasn't been long enough since the last time we ran
}
long long sinceLastViewCulling = (start - _lastViewCulling) / 1000;
uint64_t sinceLastViewCulling = (start - _lastViewCulling) / 1000;
// If the view frustum is no longer changing, but has changed, since last time, then remove nodes that are out of view
if ((sinceLastViewCulling >= std::max(_lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS))
if ((sinceLastViewCulling >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS))
&& !isViewChanging() && hasViewChanged()) {
_lastViewCulling = start;
@ -191,7 +192,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
// VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary.
cleanupRemovedVoxels();
long long endViewCulling = usecTimestampNow();
uint64_t endViewCulling = usecTimestampNow();
_lastViewCullingElapsed = (endViewCulling - start) / 1000;
}
@ -228,8 +229,8 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
pthread_mutex_unlock(&_bufferWriteLock);
long long end = usecTimestampNow();
long long elapsedmsec = (end - start) / 1000;
uint64_t end = usecTimestampNow();
int elapsedmsec = (end - start) / 1000;
_setupNewVoxelsForDrawingLastFinished = end;
_setupNewVoxelsForDrawingLastElapsed = elapsedmsec;
}
@ -1088,13 +1089,13 @@ void VoxelSystem::collectStatsForTreesAndVBOs() {
args.expectedMax = _voxelsInWriteArrays;
_tree->recurseTreeWithOperation(collectStatsForTreesAndVBOsOperation,&args);
printLog("_voxelsDirty=%s _voxelsInWriteArrays=%ld minDirty=%ld maxDirty=%ld \n", debug::valueOf(_voxelsDirty),
_voxelsInWriteArrays, minDirty, maxDirty);
printLog("stats: total %ld, leaves %ld, dirty %ld, colored %ld, shouldRender %ld, inVBO %ld\n",
printLog("Local Voxel Tree Statistics:\n total nodes %ld \n leaves %ld \n dirty %ld \n colored %ld \n shouldRender %ld \n",
args.totalNodes, args.leafNodes, args.dirtyNodes, args.coloredNodes, args.shouldRenderNodes);
printLog("inVBO %ld, nodesInVBOOverExpectedMax %ld, duplicateVBOIndex %ld, nodesInVBONotShouldRender %ld\n",
printLog(" _voxelsDirty=%s \n _voxelsInWriteArrays=%ld \n minDirty=%ld \n maxDirty=%ld \n", debug::valueOf(_voxelsDirty),
_voxelsInWriteArrays, minDirty, maxDirty);
printLog(" inVBO %ld \n nodesInVBOOverExpectedMax %ld \n duplicateVBOIndex %ld \n nodesInVBONotShouldRender %ld \n",
args.nodesInVBO, args.nodesInVBOOverExpectedMax, args.duplicateVBOIndex, args.nodesInVBONotShouldRender);
glBufferIndex minInVBO = GLBUFFER_INDEX_UNKNOWN;
@ -1107,7 +1108,7 @@ void VoxelSystem::collectStatsForTreesAndVBOs() {
}
}
printLog("minInVBO=%ld maxInVBO=%ld _voxelsInWriteArrays=%ld _voxelsInReadArrays=%ld\n",
printLog(" minInVBO=%ld \n maxInVBO=%ld \n _voxelsInWriteArrays=%ld \n _voxelsInReadArrays=%ld \n",
minInVBO, maxInVBO, _voxelsInWriteArrays, _voxelsInReadArrays);
}
@ -1161,6 +1162,7 @@ void VoxelSystem::copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* dest
struct FalseColorizeOccludedArgs {
ViewFrustum* viewFrustum;
CoverageMap* map;
CoverageMapV2* mapV2;
VoxelTree* tree;
long totalVoxels;
long coloredVoxels;
@ -1180,9 +1182,11 @@ struct FalseColorizeSubTreeOperationArgs {
};
bool VoxelSystem::falseColorizeSubTreeOperation(VoxelNode* node, void* extraData) {
FalseColorizeSubTreeOperationArgs* args = (FalseColorizeSubTreeOperationArgs*) extraData;
node->setFalseColor(args->color[0], args->color[1], args->color[2]);
args->voxelsTouched++;
if (node->getShouldRender()) {
FalseColorizeSubTreeOperationArgs* args = (FalseColorizeSubTreeOperationArgs*) extraData;
node->setFalseColor(args->color[0], args->color[1], args->color[2]);
args->voxelsTouched++;
}
return true;
}
@ -1262,12 +1266,143 @@ bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, void* extraDat
}
return true; // keep going!
}
void VoxelSystem::falseColorizeOccluded() {
PerformanceWarning warn(true, "falseColorizeOccluded()",true);
CoverageMap map;
myCoverageMap.erase();
FalseColorizeOccludedArgs args;
args.viewFrustum = Application::getInstance()->getViewFrustum();
args.map = &map;
args.map = &myCoverageMap;
args.totalVoxels = 0;
args.coloredVoxels = 0;
args.occludedVoxels = 0;
args.notOccludedVoxels = 0;
args.outOfView = 0;
args.subtreeVoxelsSkipped = 0;
args.nonLeaves = 0;
args.stagedForDeletion = 0;
args.nonLeavesOutOfView = 0;
args.nonLeavesOccluded = 0;
args.tree = _tree;
VoxelProjectedPolygon::pointInside_calls = 0;
VoxelProjectedPolygon::occludes_calls = 0;
VoxelProjectedPolygon::intersects_calls = 0;
glm::vec3 position = args.viewFrustum->getPosition() * (1.0f/TREE_SCALE);
_tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedOperation, position, (void*)&args);
printLog("falseColorizeOccluded()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n",
position.x, position.y,
args.totalVoxels, args.coloredVoxels, args.occludedVoxels,
args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped,
args.stagedForDeletion,
args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded,
VoxelProjectedPolygon::pointInside_calls,
VoxelProjectedPolygon::occludes_calls,
VoxelProjectedPolygon::intersects_calls
);
//myCoverageMap.erase();
setupNewVoxelsForDrawing();
}
bool VoxelSystem::falseColorizeOccludedV2Operation(VoxelNode* node, void* extraData) {
FalseColorizeOccludedArgs* args = (FalseColorizeOccludedArgs*) extraData;
args->totalVoxels++;
// if this node is staged for deletion, then just return
if (node->isStagedForDeletion()) {
args->stagedForDeletion++;
return true;
}
// If we are a parent, let's see if we're completely occluded.
if (!node->isLeaf()) {
args->nonLeaves++;
AABox voxelBox = node->getAABox();
voxelBox.scale(TREE_SCALE);
VoxelProjectedPolygon* voxelPolygon = new VoxelProjectedPolygon(args->viewFrustum->getProjectedPolygon(voxelBox));
// If we're not all in view, then ignore it, and just return. But keep searching...
if (!voxelPolygon->getAllInView()) {
args->nonLeavesOutOfView++;
delete voxelPolygon;
return true;
}
CoverageMapV2StorageResult result = args->mapV2->checkMap(voxelPolygon, false);
if (result == V2_OCCLUDED) {
args->nonLeavesOccluded++;
delete voxelPolygon;
FalseColorizeSubTreeOperationArgs subArgs;
subArgs.color[0] = 0;
subArgs.color[1] = 255;
subArgs.color[2] = 0;
subArgs.voxelsTouched = 0;
args->tree->recurseNodeWithOperation(node, falseColorizeSubTreeOperation, &subArgs );
args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1);
args->totalVoxels += (subArgs.voxelsTouched - 1);
return false;
}
delete voxelPolygon;
return true; // keep looking...
}
if (node->isLeaf() && node->isColored() && node->getShouldRender()) {
args->coloredVoxels++;
AABox voxelBox = node->getAABox();
voxelBox.scale(TREE_SCALE);
VoxelProjectedPolygon* voxelPolygon = new VoxelProjectedPolygon(args->viewFrustum->getProjectedPolygon(voxelBox));
// If we're not all in view, then ignore it, and just return. But keep searching...
if (!voxelPolygon->getAllInView()) {
args->outOfView++;
delete voxelPolygon;
return true;
}
CoverageMapV2StorageResult result = args->mapV2->checkMap(voxelPolygon, true);
if (result == V2_OCCLUDED) {
node->setFalseColor(255, 0, 0);
args->occludedVoxels++;
} else if (result == V2_STORED) {
args->notOccludedVoxels++;
//printLog("***** falseColorizeOccludedOperation() NODE is STORED *****\n");
} else if (result == V2_DOESNT_FIT) {
//printLog("***** falseColorizeOccludedOperation() NODE DOESNT_FIT???? *****\n");
}
delete voxelPolygon; // V2 maps don't store polygons, so we're always in charge of freeing
}
return true; // keep going!
}
void VoxelSystem::falseColorizeOccludedV2() {
PerformanceWarning warn(true, "falseColorizeOccludedV2()",true);
myCoverageMapV2.erase();
CoverageMapV2::wantDebugging = true;
VoxelProjectedPolygon::pointInside_calls = 0;
VoxelProjectedPolygon::occludes_calls = 0;
VoxelProjectedPolygon::intersects_calls = 0;
FalseColorizeOccludedArgs args;
args.viewFrustum = Application::getInstance()->getViewFrustum();
args.mapV2 = &myCoverageMapV2;
args.totalVoxels = 0;
args.coloredVoxels = 0;
args.occludedVoxels = 0;
@ -1282,15 +1417,22 @@ void VoxelSystem::falseColorizeOccluded() {
glm::vec3 position = args.viewFrustum->getPosition() * (1.0f/TREE_SCALE);
_tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedOperation, position, (void*)&args);
_tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedV2Operation, position, (void*)&args);
printLog("falseColorizeOccluded()\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n",
printLog("falseColorizeOccludedV2()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n",
position.x, position.y,
args.totalVoxels, args.coloredVoxels, args.occludedVoxels,
args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped,
args.stagedForDeletion,
args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded);
args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded,
VoxelProjectedPolygon::pointInside_calls,
VoxelProjectedPolygon::occludes_calls,
VoxelProjectedPolygon::intersects_calls
);
//myCoverageMapV2.erase();
setupNewVoxelsForDrawing();
}

View file

@ -11,11 +11,15 @@
#include "InterfaceConfig.h"
#include <glm/glm.hpp>
#include <SharedUtil.h>
#include <UDPSocket.h>
#include <CoverageMapV2.h>
#include <NodeData.h>
#include <VoxelTree.h>
#include <ViewFrustum.h>
#include <VoxelTree.h>
#include "Camera.h"
#include "Util.h"
#include "world.h"
@ -57,6 +61,7 @@ public:
void falseColorizeDistanceFromView(ViewFrustum* viewFrustum);
void falseColorizeRandomEveryOther();
void falseColorizeOccluded();
void falseColorizeOccludedV2();
void killLocalVoxels();
void setRenderPipelineWarnings(bool on) { _renderWarningsOn = on; };
@ -84,6 +89,9 @@ public:
void copySubTreeIntoNewTree(VoxelNode* startNode, VoxelTree* destinationTree, bool rebaseToRoot);
void copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destinationNode);
CoverageMapV2 myCoverageMapV2;
CoverageMap myCoverageMap;
protected:
float _treeScale;
@ -123,6 +131,8 @@ private:
static bool collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData);
static bool falseColorizeOccludedOperation(VoxelNode* node, void* extraData);
static bool falseColorizeSubTreeOperation(VoxelNode* node, void* extraData);
static bool falseColorizeOccludedV2Operation(VoxelNode* node, void* extraData);
int updateNodeInArraysAsFullVBO(VoxelNode* node);
int updateNodeInArraysAsPartialVBO(VoxelNode* node);
@ -150,10 +160,10 @@ private:
bool _writeRenderFullVBO;
bool _readRenderFullVBO;
double _setupNewVoxelsForDrawingLastElapsed;
double _setupNewVoxelsForDrawingLastFinished;
double _lastViewCulling;
double _lastViewCullingElapsed;
int _setupNewVoxelsForDrawingLastElapsed;
uint64_t _setupNewVoxelsForDrawingLastFinished;
uint64_t _lastViewCulling;
int _lastViewCullingElapsed;
GLuint _vboVerticesID;
GLuint _vboNormalsID;

View file

@ -21,11 +21,16 @@
using namespace cv;
using namespace std;
// register OpenCV matrix type with Qt metatype system
#ifdef HAVE_OPENNI
using namespace xn;
#endif
// register types with Qt metatype system
int jointVectorMetaType = qRegisterMetaType<JointVector>("JointVector");
int matMetaType = qRegisterMetaType<Mat>("cv::Mat");
int rotatedRectMetaType = qRegisterMetaType<RotatedRect>("cv::RotatedRect");
Webcam::Webcam() : _enabled(false), _active(false), _frameTextureID(0) {
Webcam::Webcam() : _enabled(false), _active(false), _frameTextureID(0), _depthTextureID(0) {
// the grabber simply runs as fast as possible
_grabber = new FrameGrabber();
_grabber->moveToThread(&_grabberThread);
@ -79,9 +84,45 @@ void Webcam::renderPreview(int screenWidth, int screenHeight) {
glTexCoord2f(0, 1);
glVertex2f(left, top + PREVIEW_HEIGHT);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
if (_depthTextureID != 0) {
glBindTexture(GL_TEXTURE_2D, _depthTextureID);
glBegin(GL_QUADS);
int depthPreviewWidth = _depthWidth * PREVIEW_HEIGHT / _depthHeight;
int depthLeft = screenWidth - depthPreviewWidth - 10;
glTexCoord2f(0, 0);
glVertex2f(depthLeft, top - PREVIEW_HEIGHT);
glTexCoord2f(1, 0);
glVertex2f(depthLeft + depthPreviewWidth, top - PREVIEW_HEIGHT);
glTexCoord2f(1, 1);
glVertex2f(depthLeft + depthPreviewWidth, top);
glTexCoord2f(0, 1);
glVertex2f(depthLeft, top);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
if (!_joints.isEmpty()) {
glColor3f(1.0f, 0.0f, 0.0f);
glPointSize(4.0f);
glBegin(GL_POINTS);
float projectedScale = PREVIEW_HEIGHT / (float)_depthHeight;
foreach (const Joint& joint, _joints) {
if (joint.isValid) {
glVertex2f(depthLeft + joint.projected.x * projectedScale,
top - PREVIEW_HEIGHT + joint.projected.y * projectedScale);
}
}
glEnd();
glPointSize(1.0f);
}
} else {
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_LINE_LOOP);
Point2f facePoints[4];
_faceRect.points(facePoints);
@ -107,32 +148,51 @@ Webcam::~Webcam() {
delete _grabber;
}
void Webcam::setFrame(const Mat& frame, const RotatedRect& faceRect) {
void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const RotatedRect& faceRect, const JointVector& joints) {
IplImage image = frame;
glPixelStorei(GL_UNPACK_ROW_LENGTH, image.widthStep / 3);
if (_frameTextureID == 0) {
glGenTextures(1, &_frameTextureID);
glBindTexture(GL_TEXTURE_2D, _frameTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _frameWidth = image.width, _frameHeight = image.height, 0, GL_BGR,
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _frameWidth = image.width, _frameHeight = image.height, 0, format,
GL_UNSIGNED_BYTE, image.imageData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
printLog("Capturing webcam at %dx%d.\n", _frameWidth, _frameHeight);
printLog("Capturing video at %dx%d.\n", _frameWidth, _frameHeight);
} else {
glBindTexture(GL_TEXTURE_2D, _frameTextureID);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _frameWidth, _frameHeight, GL_BGR, GL_UNSIGNED_BYTE, image.imageData);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _frameWidth, _frameHeight, format, GL_UNSIGNED_BYTE, image.imageData);
}
if (!depth.empty()) {
IplImage depthImage = depth;
glPixelStorei(GL_UNPACK_ROW_LENGTH, depthImage.widthStep);
if (_depthTextureID == 0) {
glGenTextures(1, &_depthTextureID);
glBindTexture(GL_TEXTURE_2D, _depthTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, _depthWidth = depthImage.width, _depthHeight = depthImage.height, 0,
GL_LUMINANCE, GL_UNSIGNED_BYTE, depthImage.imageData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
printLog("Capturing depth at %dx%d.\n", _depthWidth, _depthHeight);
} else {
glBindTexture(GL_TEXTURE_2D, _depthTextureID);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _depthWidth, _depthHeight, GL_LUMINANCE,
GL_UNSIGNED_BYTE, depthImage.imageData);
}
}
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glBindTexture(GL_TEXTURE_2D, 0);
// store our face rect, update our frame count for fps computation
// store our face rect and joints, update our frame count for fps computation
_faceRect = faceRect;
_joints = joints;
_frameCount++;
const int MAX_FPS = 60;
const int MIN_FRAME_DELAY = 1000000 / MAX_FPS;
long long now = usecTimestampNow();
long long remaining = MIN_FRAME_DELAY;
uint64_t now = usecTimestampNow();
int remaining = MIN_FRAME_DELAY;
if (_startTimestamp == 0) {
_startTimestamp = now;
} else {
@ -140,33 +200,60 @@ void Webcam::setFrame(const Mat& frame, const RotatedRect& faceRect) {
}
_lastFrameTimestamp = now;
// roll is just the angle of the face rect (correcting for 180 degree rotations)
float roll = faceRect.angle;
if (roll < -90.0f) {
roll += 180.0f;
// see if we have joint data
if (!_joints.isEmpty()) {
_estimatedJoints.resize(NUM_AVATAR_JOINTS);
glm::vec3 origin;
if (_joints[AVATAR_JOINT_LEFT_HIP].isValid && _joints[AVATAR_JOINT_RIGHT_HIP].isValid) {
origin = glm::mix(_joints[AVATAR_JOINT_LEFT_HIP].position, _joints[AVATAR_JOINT_RIGHT_HIP].position, 0.5f);
} else if (_joints[AVATAR_JOINT_TORSO].isValid) {
const glm::vec3 TORSO_TO_PELVIS = glm::vec3(0.0f, -0.09f, -0.01f);
origin = _joints[AVATAR_JOINT_TORSO].position + TORSO_TO_PELVIS;
}
for (int i = 0; i < NUM_AVATAR_JOINTS; i++) {
if (!_joints[i].isValid) {
continue;
}
const float JOINT_SMOOTHING = 0.9f;
_estimatedJoints[i].isValid = true;
_estimatedJoints[i].position = glm::mix(_joints[i].position - origin,
_estimatedJoints[i].position, JOINT_SMOOTHING);
_estimatedJoints[i].rotation = safeMix(_joints[i].rotation,
_estimatedJoints[i].rotation, JOINT_SMOOTHING);
}
_estimatedRotation = safeEulerAngles(_estimatedJoints[AVATAR_JOINT_HEAD_BASE].rotation);
_estimatedPosition = _estimatedJoints[AVATAR_JOINT_HEAD_BASE].position;
} else if (roll > 90.0f) {
roll -= 180.0f;
}
const float ROTATION_SMOOTHING = 0.95f;
_estimatedRotation.z = glm::mix(roll, _estimatedRotation.z, ROTATION_SMOOTHING);
// determine position based on translation and scaling of the face rect
if (_initialFaceRect.size.area() == 0) {
_initialFaceRect = faceRect;
_estimatedPosition = glm::vec3();
} else {
float proportion = sqrtf(_initialFaceRect.size.area() / (float)faceRect.size.area());
const float DISTANCE_TO_CAMERA = 0.333f;
const float POSITION_SCALE = 0.5f;
float z = DISTANCE_TO_CAMERA * proportion - DISTANCE_TO_CAMERA;
glm::vec3 position = glm::vec3(
(faceRect.center.x - _initialFaceRect.center.x) * proportion * POSITION_SCALE / _frameWidth,
(faceRect.center.y - _initialFaceRect.center.y) * proportion * POSITION_SCALE / _frameWidth,
z);
const float POSITION_SMOOTHING = 0.95f;
_estimatedPosition = glm::mix(position, _estimatedPosition, POSITION_SMOOTHING);
// roll is just the angle of the face rect (correcting for 180 degree rotations)
float roll = faceRect.angle;
if (roll < -90.0f) {
roll += 180.0f;
} else if (roll > 90.0f) {
roll -= 180.0f;
}
const float ROTATION_SMOOTHING = 0.95f;
_estimatedRotation.z = glm::mix(roll, _estimatedRotation.z, ROTATION_SMOOTHING);
// determine position based on translation and scaling of the face rect
if (_initialFaceRect.size.area() == 0) {
_initialFaceRect = faceRect;
_estimatedPosition = glm::vec3();
} else {
float proportion = sqrtf(_initialFaceRect.size.area() / (float)faceRect.size.area());
const float DISTANCE_TO_CAMERA = 0.333f;
const float POSITION_SCALE = 0.5f;
float z = DISTANCE_TO_CAMERA * proportion - DISTANCE_TO_CAMERA;
glm::vec3 position = glm::vec3(
(faceRect.center.x - _initialFaceRect.center.x) * proportion * POSITION_SCALE / _frameWidth,
(faceRect.center.y - _initialFaceRect.center.y) * proportion * POSITION_SCALE / _frameWidth,
z);
const float POSITION_SMOOTHING = 0.95f;
_estimatedPosition = glm::mix(position, _estimatedPosition, POSITION_SMOOTHING);
}
}
// note that we have data
@ -176,7 +263,7 @@ void Webcam::setFrame(const Mat& frame, const RotatedRect& faceRect) {
QTimer::singleShot(qMax((int)remaining / 1000, 0), _grabber, SLOT(grabFrame()));
}
FrameGrabber::FrameGrabber() : _capture(0), _searchWindow(0, 0, 0, 0) {
FrameGrabber::FrameGrabber() : _initialized(false), _capture(0), _searchWindow(0, 0, 0, 0) {
}
FrameGrabber::~FrameGrabber() {
@ -185,52 +272,170 @@ FrameGrabber::~FrameGrabber() {
}
}
#ifdef HAVE_OPENNI
static AvatarJointID xnToAvatarJoint(XnSkeletonJoint joint) {
switch (joint) {
case XN_SKEL_HEAD: return AVATAR_JOINT_HEAD_TOP;
case XN_SKEL_NECK: return AVATAR_JOINT_HEAD_BASE;
case XN_SKEL_TORSO: return AVATAR_JOINT_CHEST;
case XN_SKEL_LEFT_SHOULDER: return AVATAR_JOINT_RIGHT_ELBOW;
case XN_SKEL_LEFT_ELBOW: return AVATAR_JOINT_RIGHT_WRIST;
case XN_SKEL_RIGHT_SHOULDER: return AVATAR_JOINT_LEFT_ELBOW;
case XN_SKEL_RIGHT_ELBOW: return AVATAR_JOINT_LEFT_WRIST;
case XN_SKEL_LEFT_HIP: return AVATAR_JOINT_RIGHT_KNEE;
case XN_SKEL_LEFT_KNEE: return AVATAR_JOINT_RIGHT_HEEL;
case XN_SKEL_LEFT_FOOT: return AVATAR_JOINT_RIGHT_TOES;
case XN_SKEL_RIGHT_HIP: return AVATAR_JOINT_LEFT_KNEE;
case XN_SKEL_RIGHT_KNEE: return AVATAR_JOINT_LEFT_HEEL;
case XN_SKEL_RIGHT_FOOT: return AVATAR_JOINT_LEFT_TOES;
default: return AVATAR_JOINT_NULL;
}
}
static int getParentJoint(XnSkeletonJoint joint) {
switch (joint) {
case XN_SKEL_HEAD: return XN_SKEL_NECK;
case XN_SKEL_TORSO: return -1;
case XN_SKEL_LEFT_ELBOW: return XN_SKEL_LEFT_SHOULDER;
case XN_SKEL_LEFT_HAND: return XN_SKEL_LEFT_ELBOW;
case XN_SKEL_RIGHT_ELBOW: return XN_SKEL_RIGHT_SHOULDER;
case XN_SKEL_RIGHT_HAND: return XN_SKEL_RIGHT_ELBOW;
case XN_SKEL_LEFT_KNEE: return XN_SKEL_LEFT_HIP;
case XN_SKEL_LEFT_FOOT: return XN_SKEL_LEFT_KNEE;
case XN_SKEL_RIGHT_KNEE: return XN_SKEL_RIGHT_HIP;
case XN_SKEL_RIGHT_FOOT: return XN_SKEL_RIGHT_KNEE;
default: return XN_SKEL_TORSO;
}
}
static glm::vec3 xnToGLM(const XnVector3D& vector, bool flip = false) {
return glm::vec3(vector.X * (flip ? -1 : 1), vector.Y, vector.Z);
}
static glm::quat xnToGLM(const XnMatrix3X3& matrix) {
glm::quat rotation = glm::quat_cast(glm::mat3(
matrix.elements[0], matrix.elements[1], matrix.elements[2],
matrix.elements[3], matrix.elements[4], matrix.elements[5],
matrix.elements[6], matrix.elements[7], matrix.elements[8]));
return glm::quat(rotation.w, -rotation.x, rotation.y, rotation.z);
}
static void XN_CALLBACK_TYPE newUser(UserGenerator& generator, XnUserID id, void* cookie) {
printLog("Found user %d.\n", id);
generator.GetSkeletonCap().RequestCalibration(id, false);
}
static void XN_CALLBACK_TYPE lostUser(UserGenerator& generator, XnUserID id, void* cookie) {
printLog("Lost user %d.\n", id);
}
static void XN_CALLBACK_TYPE calibrationStarted(SkeletonCapability& capability, XnUserID id, void* cookie) {
printLog("Calibration started for user %d.\n", id);
}
static void XN_CALLBACK_TYPE calibrationCompleted(SkeletonCapability& capability,
XnUserID id, XnCalibrationStatus status, void* cookie) {
if (status == XN_CALIBRATION_STATUS_OK) {
printLog("Calibration completed for user %d.\n", id);
capability.StartTracking(id);
} else {
printLog("Calibration failed to user %d.\n", id);
capability.RequestCalibration(id, true);
}
}
#endif
void FrameGrabber::reset() {
_searchWindow = cv::Rect(0, 0, 0, 0);
#ifdef HAVE_OPENNI
if (_userGenerator.IsValid() && _userGenerator.GetSkeletonCap().IsTracking(_userID)) {
_userGenerator.GetSkeletonCap().RequestCalibration(_userID, true);
}
#endif
}
void FrameGrabber::grabFrame() {
if (_capture == 0) {
if ((_capture = cvCaptureFromCAM(-1)) == 0) {
printLog("Failed to open webcam.\n");
return;
}
const int IDEAL_FRAME_WIDTH = 320;
const int IDEAL_FRAME_HEIGHT = 240;
cvSetCaptureProperty(_capture, CV_CAP_PROP_FRAME_WIDTH, IDEAL_FRAME_WIDTH);
cvSetCaptureProperty(_capture, CV_CAP_PROP_FRAME_HEIGHT, IDEAL_FRAME_HEIGHT);
if (!(_initialized || init())) {
return;
}
int format = GL_BGR;
Mat frame;
JointVector joints;
#ifdef HAVE_OPENNI
if (_depthGenerator.IsValid()) {
_xnContext.WaitAnyUpdateAll();
frame = Mat(_imageMetaData.YRes(), _imageMetaData.XRes(), CV_8UC3, (void*)_imageGenerator.GetImageMap());
format = GL_RGB;
#ifdef __APPLE__
configureCamera(0x5ac, 0x8510, false, 0.975, 0.5, 1.0, 0.5, true, 0.5);
#else
cvSetCaptureProperty(_capture, CV_CAP_PROP_EXPOSURE, 0.5);
cvSetCaptureProperty(_capture, CV_CAP_PROP_CONTRAST, 0.5);
cvSetCaptureProperty(_capture, CV_CAP_PROP_SATURATION, 0.5);
cvSetCaptureProperty(_capture, CV_CAP_PROP_BRIGHTNESS, 0.5);
cvSetCaptureProperty(_capture, CV_CAP_PROP_HUE, 0.5);
cvSetCaptureProperty(_capture, CV_CAP_PROP_GAIN, 0.5);
#endif
switchToResourcesParentIfRequired();
if (!_faceCascade.load("resources/haarcascades/haarcascade_frontalface_alt.xml")) {
printLog("Failed to load Haar cascade for face tracking.\n");
Mat depth = Mat(_depthMetaData.YRes(), _depthMetaData.XRes(), CV_16UC1, (void*)_depthGenerator.GetDepthMap());
const double EIGHT_BIT_MAX = 255;
const double ELEVEN_BIT_MAX = 2047;
depth.convertTo(_grayDepthFrame, CV_8UC1, EIGHT_BIT_MAX / ELEVEN_BIT_MAX);
_userID = 0;
XnUInt16 userCount = 1;
_userGenerator.GetUsers(&_userID, userCount);
if (userCount > 0 && _userGenerator.GetSkeletonCap().IsTracking(_userID)) {
joints.resize(NUM_AVATAR_JOINTS);
const int MAX_ACTIVE_JOINTS = 16;
XnSkeletonJoint activeJoints[MAX_ACTIVE_JOINTS];
XnUInt16 activeJointCount = MAX_ACTIVE_JOINTS;
_userGenerator.GetSkeletonCap().EnumerateActiveJoints(activeJoints, activeJointCount);
XnSkeletonJointTransformation transform;
for (int i = 0; i < activeJointCount; i++) {
AvatarJointID avatarJoint = xnToAvatarJoint(activeJoints[i]);
if (avatarJoint == AVATAR_JOINT_NULL) {
continue;
}
_userGenerator.GetSkeletonCap().GetSkeletonJoint(_userID, activeJoints[i], transform);
XnVector3D projected;
_depthGenerator.ConvertRealWorldToProjective(1, &transform.position.position, &projected);
glm::quat rotation = xnToGLM(transform.orientation.orientation);
int parentJoint = getParentJoint(activeJoints[i]);
if (parentJoint != -1) {
XnSkeletonJointOrientation parentOrientation;
_userGenerator.GetSkeletonCap().GetSkeletonJointOrientation(
_userID, (XnSkeletonJoint)parentJoint, parentOrientation);
rotation = glm::inverse(xnToGLM(parentOrientation.orientation)) * rotation;
}
const float METERS_PER_MM = 1.0f / 1000.0f;
joints[avatarJoint] = Joint(xnToGLM(transform.position.position, true) * METERS_PER_MM,
rotation, xnToGLM(projected));
}
}
}
IplImage* image = cvQueryFrame(_capture);
if (image == 0) {
// try again later
QMetaObject::invokeMethod(this, "grabFrame", Qt::QueuedConnection);
return;
}
// make sure it's in the format we expect
if (image->nChannels != 3 || image->depth != IPL_DEPTH_8U || image->dataOrder != IPL_DATA_ORDER_PIXEL ||
image->origin != 0) {
printLog("Invalid webcam image format.\n");
return;
#endif
if (frame.empty()) {
IplImage* image = cvQueryFrame(_capture);
if (image == 0) {
// try again later
QMetaObject::invokeMethod(this, "grabFrame", Qt::QueuedConnection);
return;
}
// make sure it's in the format we expect
if (image->nChannels != 3 || image->depth != IPL_DEPTH_8U || image->dataOrder != IPL_DATA_ORDER_PIXEL ||
image->origin != 0) {
printLog("Invalid webcam image format.\n");
return;
}
frame = image;
}
// if we don't have a search window (yet), try using the face cascade
Mat frame = image;
int channels = 0;
float ranges[] = { 0, 180 };
const float* range = ranges;
@ -239,7 +444,7 @@ void FrameGrabber::grabFrame() {
_faceCascade.detectMultiScale(frame, faces, 1.1, 6);
if (!faces.empty()) {
_searchWindow = faces.front();
updateHSVFrame(frame);
updateHSVFrame(frame, format);
Mat faceHsv(_hsvFrame, _searchWindow);
Mat faceMask(_mask, _searchWindow);
@ -252,7 +457,7 @@ void FrameGrabber::grabFrame() {
}
RotatedRect faceRect;
if (_searchWindow.area() > 0) {
updateHSVFrame(frame);
updateHSVFrame(frame, format);
calcBackProject(&_hsvFrame, 1, &channels, _histogram, _backProject, &range);
bitwise_and(_backProject, _mask, _backProject);
@ -261,10 +466,74 @@ void FrameGrabber::grabFrame() {
_searchWindow = faceRect.boundingRect();
}
QMetaObject::invokeMethod(Application::getInstance()->getWebcam(), "setFrame",
Q_ARG(cv::Mat, frame), Q_ARG(cv::RotatedRect, faceRect));
Q_ARG(cv::Mat, frame), Q_ARG(int, format), Q_ARG(cv::Mat, _grayDepthFrame),
Q_ARG(cv::RotatedRect, faceRect), Q_ARG(JointVector, joints));
}
void FrameGrabber::updateHSVFrame(const Mat& frame) {
cvtColor(frame, _hsvFrame, CV_BGR2HSV);
bool FrameGrabber::init() {
_initialized = true;
// load our face cascade
switchToResourcesParentIfRequired();
if (!_faceCascade.load("resources/haarcascades/haarcascade_frontalface_alt.xml")) {
printLog("Failed to load Haar cascade for face tracking.\n");
return false;
}
// first try for a Kinect
#ifdef HAVE_OPENNI
_xnContext.Init();
if (_depthGenerator.Create(_xnContext) == XN_STATUS_OK && _imageGenerator.Create(_xnContext) == XN_STATUS_OK &&
_userGenerator.Create(_xnContext) == XN_STATUS_OK &&
_userGenerator.IsCapabilitySupported(XN_CAPABILITY_SKELETON)) {
_depthGenerator.GetMetaData(_depthMetaData);
_imageGenerator.SetPixelFormat(XN_PIXEL_FORMAT_RGB24);
_imageGenerator.GetMetaData(_imageMetaData);
XnCallbackHandle userCallbacks, calibrationStartCallback, calibrationCompleteCallback;
_userGenerator.RegisterUserCallbacks(newUser, lostUser, 0, userCallbacks);
_userGenerator.GetSkeletonCap().RegisterToCalibrationStart(calibrationStarted, 0, calibrationStartCallback);
_userGenerator.GetSkeletonCap().RegisterToCalibrationComplete(calibrationCompleted, 0, calibrationCompleteCallback);
_userGenerator.GetSkeletonCap().SetSkeletonProfile(XN_SKEL_PROFILE_UPPER);
_xnContext.StartGeneratingAll();
return true;
}
#endif
// next, an ordinary webcam
if ((_capture = cvCaptureFromCAM(-1)) == 0) {
printLog("Failed to open webcam.\n");
return false;
}
const int IDEAL_FRAME_WIDTH = 320;
const int IDEAL_FRAME_HEIGHT = 240;
cvSetCaptureProperty(_capture, CV_CAP_PROP_FRAME_WIDTH, IDEAL_FRAME_WIDTH);
cvSetCaptureProperty(_capture, CV_CAP_PROP_FRAME_HEIGHT, IDEAL_FRAME_HEIGHT);
#ifdef __APPLE__
configureCamera(0x5ac, 0x8510, false, 0.975, 0.5, 1.0, 0.5, true, 0.5);
#else
cvSetCaptureProperty(_capture, CV_CAP_PROP_EXPOSURE, 0.5);
cvSetCaptureProperty(_capture, CV_CAP_PROP_CONTRAST, 0.5);
cvSetCaptureProperty(_capture, CV_CAP_PROP_SATURATION, 0.5);
cvSetCaptureProperty(_capture, CV_CAP_PROP_BRIGHTNESS, 0.5);
cvSetCaptureProperty(_capture, CV_CAP_PROP_HUE, 0.5);
cvSetCaptureProperty(_capture, CV_CAP_PROP_GAIN, 0.5);
#endif
return true;
}
void FrameGrabber::updateHSVFrame(const Mat& frame, int format) {
cvtColor(frame, _hsvFrame, format == GL_RGB ? CV_RGB2HSV : CV_BGR2HSV);
inRange(_hsvFrame, Scalar(0, 55, 65), Scalar(180, 256, 256), _mask);
}
Joint::Joint(const glm::vec3& position, const glm::quat& rotation, const glm::vec3& projected) :
isValid(true), position(position), rotation(rotation), projected(projected) {
}
Joint::Joint() : isValid(false) {
}

View file

@ -12,11 +12,17 @@
#include <QMetaType>
#include <QObject>
#include <QThread>
#include <QVector>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <opencv2/opencv.hpp>
#ifdef HAVE_OPENNI
#include <XnCppWrapper.h>
#endif
#include "InterfaceConfig.h"
class QImage;
@ -24,6 +30,9 @@ class QImage;
struct CvCapture;
class FrameGrabber;
class Joint;
typedef QVector<Joint> JointVector;
class Webcam : public QObject {
Q_OBJECT
@ -36,6 +45,7 @@ public:
const bool isActive() const { return _active; }
const glm::vec3& getEstimatedPosition() const { return _estimatedPosition; }
const glm::vec3& getEstimatedRotation() const { return _estimatedRotation; }
const JointVector& getEstimatedJoints() const { return _estimatedJoints; }
void reset();
void renderPreview(int screenWidth, int screenHeight);
@ -43,7 +53,8 @@ public:
public slots:
void setEnabled(bool enabled);
void setFrame(const cv::Mat& image, const cv::RotatedRect& faceRect);
void setFrame(const cv::Mat& video, int format, const cv::Mat& depth,
const cv::RotatedRect& faceRect, const JointVector& joints);
private:
@ -54,17 +65,22 @@ private:
bool _active;
int _frameWidth;
int _frameHeight;
int _depthWidth;
int _depthHeight;
GLuint _frameTextureID;
GLuint _depthTextureID;
cv::RotatedRect _faceRect;
cv::RotatedRect _initialFaceRect;
JointVector _joints;
long long _startTimestamp;
uint64_t _startTimestamp;
int _frameCount;
long long _lastFrameTimestamp;
uint64_t _lastFrameTimestamp;
glm::vec3 _estimatedPosition;
glm::vec3 _estimatedRotation;
JointVector _estimatedJoints;
};
class FrameGrabber : public QObject {
@ -82,8 +98,10 @@ public slots:
private:
void updateHSVFrame(const cv::Mat& frame);
bool init();
void updateHSVFrame(const cv::Mat& frame, int format);
bool _initialized;
CvCapture* _capture;
cv::CascadeClassifier _faceCascade;
cv::Mat _hsvFrame;
@ -91,8 +109,32 @@ private:
cv::SparseMat _histogram;
cv::Mat _backProject;
cv::Rect _searchWindow;
cv::Mat _grayDepthFrame;
#ifdef HAVE_OPENNI
xn::Context _xnContext;
xn::DepthGenerator _depthGenerator;
xn::ImageGenerator _imageGenerator;
xn::UserGenerator _userGenerator;
xn::DepthMetaData _depthMetaData;
xn::ImageMetaData _imageMetaData;
XnUserID _userID;
#endif
};
class Joint {
public:
Joint(const glm::vec3& position, const glm::quat& rotation, const glm::vec3& projected);
Joint();
bool isValid;
glm::vec3 position;
glm::quat rotation;
glm::vec3 projected;
};
Q_DECLARE_METATYPE(JointVector)
Q_DECLARE_METATYPE(cv::Mat)
Q_DECLARE_METATYPE(cv::RotatedRect)

View file

@ -114,7 +114,7 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination
injectorSocket->send(destinationSocket, dataPacket, sizeof(dataPacket));
long long usecToSleep = usecTimestamp(&startTime) + (++nextFrame * INJECT_INTERVAL_USECS) - usecTimestampNow();
int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * INJECT_INTERVAL_USECS) - usecTimestampNow();
if (usecToSleep > 0) {
usleep(usecToSleep);
}

View file

@ -21,7 +21,7 @@ const int STREAM_IDENTIFIER_NUM_BYTES = 8;
const int MAX_INJECTOR_VOLUME = 0xFF;
const long long INJECT_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000);
const int INJECT_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000);
class AudioInjector {
public:

View file

@ -32,10 +32,9 @@ AvatarData::AvatarData(Node* owningNode) :
_cameraNearClip(0.0f),
_cameraFarClip(0.0f),
_keyState(NO_KEY_DOWN),
_wantResIn(false),
_wantColor(true),
_wantDelta(false),
_wantOcclusionCulling(false),
_wantOcclusionCulling(true),
_headData(NULL),
_handData(NULL)
{
@ -113,7 +112,6 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
// bitMask of less than byte wide items
unsigned char bitItems = 0;
if (_wantResIn) { setAtBit(bitItems, WANT_RESIN_AT_BIT); }
if (_wantColor) { setAtBit(bitItems, WANT_COLOR_AT_BIT); }
if (_wantDelta) { setAtBit(bitItems, WANT_DELTA_AT_BIT); }
if (_wantOcclusionCulling) { setAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT); }
@ -152,6 +150,13 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
}
}
// skeleton joints
*destinationBuffer++ = (unsigned char)_joints.size();
for (vector<JointData>::iterator it = _joints.begin(); it != _joints.end(); it++) {
*destinationBuffer++ = (unsigned char)it->jointID;
destinationBuffer += packOrientationQuatToBytes(destinationBuffer, it->rotation);
}
return destinationBuffer - bufferStart;
}
@ -234,7 +239,6 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
// voxel sending features...
unsigned char bitItems = 0;
bitItems = (unsigned char)*sourceBuffer++;
_wantResIn = oneAtBit(bitItems, WANT_RESIN_AT_BIT);
_wantColor = oneAtBit(bitItems, WANT_COLOR_AT_BIT);
_wantDelta = oneAtBit(bitItems, WANT_DELTA_AT_BIT);
_wantOcclusionCulling = oneAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT);
@ -264,6 +268,16 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
_handData->setFingerRoots(fingerRoots);
}
// skeleton joints
if (sourceBuffer - startPosition < numBytes) // safety check
{
_joints.resize(*sourceBuffer++);
for (vector<JointData>::iterator it = _joints.begin(); it != _joints.end(); it++) {
it->jointID = *sourceBuffer++;
sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, it->rotation);
}
}
return sourceBuffer - startPosition;
}

View file

@ -11,6 +11,7 @@
#include <string>
#include <inttypes.h>
#include <vector>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
@ -19,7 +20,7 @@
#include "HeadData.h"
#include "HandData.h"
const int WANT_RESIN_AT_BIT = 0;
const int UNUSED_BIT = 0; // this bit is available to use
const int WANT_COLOR_AT_BIT = 1;
const int WANT_DELTA_AT_BIT = 2;
const int KEY_STATE_START_BIT = 3; // 4th and 5th bits
@ -36,6 +37,8 @@ enum KeyState
DELETE_KEY_DOWN
};
class JointData;
class AvatarData : public NodeData {
public:
AvatarData(Node* owningNode = NULL);
@ -88,11 +91,9 @@ public:
const std::string& chatMessage () const { return _chatMessage; }
// related to Voxel Sending strategies
bool getWantResIn() const { return _wantResIn; }
bool getWantColor() const { return _wantColor; }
bool getWantDelta() const { return _wantDelta; }
bool getWantOcclusionCulling() const { return _wantOcclusionCulling; }
void setWantResIn(bool wantResIn) { _wantResIn = wantResIn; }
void setWantColor(bool wantColor) { _wantColor = wantColor; }
void setWantDelta(bool wantDelta) { _wantDelta = wantDelta; }
void setWantOcclusionCulling(bool wantOcclusionCulling) { _wantOcclusionCulling = wantOcclusionCulling; }
@ -127,19 +128,27 @@ protected:
std::string _chatMessage;
// voxel server sending items
bool _wantResIn;
bool _wantColor;
bool _wantDelta;
bool _wantOcclusionCulling;
std::vector<JointData> _joints;
HeadData* _headData;
HandData* _handData;
private:
// privatize the copy constructor and assignment operator so they cannot be called
AvatarData(const AvatarData&);
AvatarData& operator= (const AvatarData&);
};
class JointData {
public:
int jointID;
glm::quat rotation;
};
// These pack/unpack functions are designed to start specific known types in as efficient a manner
// as possible. Taking advantage of the known characteristics of the semantic types.

View file

@ -37,11 +37,11 @@ public:
uint16_t getNodeID() const { return _nodeID; }
void setNodeID(uint16_t nodeID) { _nodeID = nodeID;}
long long getWakeMicrostamp() const { return _wakeMicrostamp; }
void setWakeMicrostamp(long long wakeMicrostamp) { _wakeMicrostamp = wakeMicrostamp; }
uint64_t getWakeMicrostamp() const { return _wakeMicrostamp; }
void setWakeMicrostamp(uint64_t wakeMicrostamp) { _wakeMicrostamp = wakeMicrostamp; }
long long getLastHeardMicrostamp() const { return _lastHeardMicrostamp; }
void setLastHeardMicrostamp(long long lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; }
uint64_t getLastHeardMicrostamp() const { return _lastHeardMicrostamp; }
void setLastHeardMicrostamp(uint64_t lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; }
sockaddr* getPublicSocket() const { return _publicSocket; }
void setPublicSocket(sockaddr* publicSocket) { _publicSocket = publicSocket; }
@ -74,8 +74,8 @@ private:
char _type;
uint16_t _nodeID;
long long _wakeMicrostamp;
long long _lastHeardMicrostamp;
uint64_t _wakeMicrostamp;
uint64_t _lastHeardMicrostamp;
sockaddr* _publicSocket;
sockaddr* _localSocket;
sockaddr* _activeSocket;

View file

@ -80,7 +80,9 @@ void NodeList::timePingReply(sockaddr *nodeAddress, unsigned char *packetData) {
for(NodeList::iterator node = begin(); node != end(); node++) {
if (socketMatch(node->getPublicSocket(), nodeAddress) ||
socketMatch(node->getLocalSocket(), nodeAddress)) {
int pingTime = usecTimestampNow() - *(long long *)(packetData + numBytesForPacketHeader(packetData));
int pingTime = usecTimestampNow() - *(uint64_t *)(packetData + numBytesForPacketHeader(packetData));
node->setPingMs(pingTime / 1000);
break;
}
@ -411,7 +413,8 @@ Node* NodeList::soloNodeOfType(char nodeType) {
void *removeSilentNodes(void *args) {
NodeList* nodeList = (NodeList*) args;
long long checkTimeUSecs, sleepTime;
uint64_t checkTimeUSecs;
int sleepTime;
while (!silentNodeThreadStopFlag) {
checkTimeUSecs = usecTimestampNow();

View file

@ -105,17 +105,17 @@ int PerfStat::DumpStats(char** array) {
// Destructor handles recording all of our stats
PerformanceWarning::~PerformanceWarning() {
long long end = usecTimestampNow();
uint64_t end = usecTimestampNow();
double elapsedmsec = (end - _start) / 1000.0;
if ((_alwaysDisplay || _renderWarningsOn) && elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - _start) / 1000000.0;
printLog("WARNING! %s took %lf seconds\n", _message, elapsedsec);
printLog("%s%s took %lf seconds\n", (_alwaysDisplay ? "" : "WARNING!"), _message, elapsedsec);
} else {
printLog("WARNING! %s took %lf milliseconds\n", _message, elapsedmsec);
printLog("%s%s took %lf milliseconds\n", (_alwaysDisplay ? "" : "WARNING!"), _message, elapsedmsec);
}
} else if (_alwaysDisplay) {
printLog("WARNING! %s took %lf milliseconds\n", _message, elapsedmsec);
printLog("%s took %lf milliseconds\n", _message, elapsedmsec);
}
};

View file

@ -85,7 +85,7 @@ typedef std::map<std::string,PerfStatHistory,std::less<std::string> >::iterator
class PerformanceWarning {
private:
long long _start;
uint64_t _start;
const char* _message;
bool _renderWarningsOn;
bool _alwaysDisplay;

View file

@ -25,11 +25,11 @@
#include "PacketHeaders.h"
#include "SharedUtil.h"
long long usecTimestamp(timeval *time) {
uint64_t usecTimestamp(timeval *time) {
return (time->tv_sec * 1000000 + time->tv_usec);
}
long long usecTimestampNow() {
uint64_t usecTimestampNow() {
timeval now;
gettimeofday(&now, NULL);
return (now.tv_sec * 1000000 + now.tv_usec);
@ -445,3 +445,25 @@ int insertIntoSortedArrays(void* value, float key, int originalIndex,
return -1; // error case
}
int removeFromSortedArrays(void* value, void** valueArray, float* keyArray, int* originalIndexArray,
int currentCount, int maxCount) {
int i = 0;
if (currentCount > 0) {
while (i < currentCount && value != valueArray[i]) {
i++;
}
if (value == valueArray[i] && i < currentCount) {
// i is the location of the item we were looking for
// shift array elements to the left
memmove(&valueArray[i], &valueArray[i + 1], sizeof(void*) * ((currentCount-1) - i));
memmove(&keyArray[i], &keyArray[i + 1], sizeof(float) * ((currentCount-1) - i));
if (originalIndexArray) {
memmove(&originalIndexArray[i], &originalIndexArray[i + 1], sizeof(int) * ((currentCount-1) - i));
}
return currentCount-1;
}
}
return -1; // error case
}

View file

@ -36,8 +36,8 @@ static const float DECIMETER = 0.1f;
static const float CENTIMETER = 0.01f;
static const float MILLIIMETER = 0.001f;
long long usecTimestamp(timeval *time);
long long usecTimestampNow();
uint64_t usecTimestamp(timeval *time);
uint64_t usecTimestampNow();
float randFloat();
int randIntInRange (int min, int max);
@ -88,6 +88,11 @@ int insertIntoSortedArrays(void* value, float key, int originalIndex,
void** valueArray, float* keyArray, int* originalIndexArray,
int currentCount, int maxCount);
int removeFromSortedArrays(void* value, void** valueArray, float* keyArray, int* originalIndexArray,
int currentCount, int maxCount);
// Helper Class for debugging
class debug {
public:

View file

@ -22,7 +22,7 @@ int SimpleMovingAverage::updateAverage(float sample) {
if (_numSamples > 0) {
_average = (ONE_MINUS_WEIGHTING * _average) + (WEIGHTING * sample);
float eventDelta = (usecTimestampNow() - _lastEventTimestamp) / 1000000;
float eventDelta = (usecTimestampNow() - _lastEventTimestamp) / 1000000.0f;
if (_numSamples > 1) {
_eventDeltaAverage = (ONE_MINUS_WEIGHTING * _eventDeltaAverage) +
@ -46,7 +46,7 @@ void SimpleMovingAverage::reset() {
float SimpleMovingAverage::getEventDeltaAverage() {
return (ONE_MINUS_WEIGHTING * _eventDeltaAverage) +
(WEIGHTING * ((usecTimestampNow() - _lastEventTimestamp) / 1000000));
(WEIGHTING * ((usecTimestampNow() - _lastEventTimestamp) / 1000000.0f));
}
float SimpleMovingAverage::getAverageSampleValuePerSecond() {

View file

@ -11,7 +11,7 @@
#ifndef __hifi__Stats__
#define __hifi__Stats__
#include <iostream>
#include <stdint.h>
class SimpleMovingAverage {
public:
@ -26,7 +26,7 @@ public:
float getAverageSampleValuePerSecond();
private:
int _numSamples;
long long _lastEventTimestamp;
uint64_t _lastEventTimestamp;
float _average;
float _eventDeltaAverage;

View file

@ -6,8 +6,9 @@
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include "StdDev.h"
#include <limits>
#include <cmath>
#include "StdDev.h"
const int MAX_STDEV_SAMPLES = 1000;
@ -34,6 +35,16 @@ float StDev::getAverage() {
return average/(float)sampleCount;
else return 0;
}
/*
float StDev::getMax() {
float average = -FLT_MAX;
for (int i = 0; i < sampleCount; i++) {
average += data[i];
}
if (sampleCount > 0)
return average/(float)sampleCount;
else return 0;
}*/
float StDev::getStDev() {
float average = getAverage();

View file

@ -15,4 +15,9 @@ include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
# link ZLIB
find_package(ZLIB)
include_directories(${ZLIB_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})

View file

@ -13,9 +13,12 @@
int CoverageMap::_mapCount = 0;
int CoverageMap::_checkMapRootCalls = 0;
int CoverageMap::_notAllInView = 0;
bool CoverageMap::wantDebugging = false;
const BoundingBox CoverageMap::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-2.f,-2.f), glm::vec2(4.f,4.f));
const int MAX_POLYGONS_PER_REGION = 50;
const BoundingBox CoverageMap::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-1.f,-1.f), glm::vec2(2.f,2.f));
// Coverage Map's polygon coordinates are from -1 to 1 in the following mapping to screen space.
//
@ -64,6 +67,22 @@ CoverageMap::~CoverageMap() {
erase();
};
void CoverageMap::printStats() {
printLog("CoverageMap::printStats()...\n");
printLog("MINIMUM_POLYGON_AREA_TO_STORE=%f\n",MINIMUM_POLYGON_AREA_TO_STORE);
printLog("_mapCount=%d\n",_mapCount);
printLog("_checkMapRootCalls=%d\n",_checkMapRootCalls);
printLog("_notAllInView=%d\n",_notAllInView);
printLog("_maxPolygonsUsed=%d\n",CoverageRegion::_maxPolygonsUsed);
printLog("_totalPolygons=%d\n",CoverageRegion::_totalPolygons);
printLog("_occlusionTests=%d\n",CoverageRegion::_occlusionTests);
printLog("_regionSkips=%d\n",CoverageRegion::_regionSkips);
printLog("_tooSmallSkips=%d\n",CoverageRegion::_tooSmallSkips);
printLog("_regionFullSkips=%d\n",CoverageRegion::_regionFullSkips);
printLog("_outOfOrderPolygon=%d\n",CoverageRegion::_outOfOrderPolygon);
printLog("_clippedPolygons=%d\n",CoverageRegion::_clippedPolygons);
}
void CoverageMap::erase() {
// tell our regions to erase()
_topHalf.erase();
@ -81,19 +100,19 @@ void CoverageMap::erase() {
if (_isRoot && wantDebugging) {
printLog("CoverageMap last to be deleted...\n");
printLog("MINIMUM_POLYGON_AREA_TO_STORE=%f\n",MINIMUM_POLYGON_AREA_TO_STORE);
printLog("_mapCount=%d\n",_mapCount);
printLog("_checkMapRootCalls=%d\n",_checkMapRootCalls);
printLog("_maxPolygonsUsed=%d\n",CoverageRegion::_maxPolygonsUsed);
printLog("_totalPolygons=%d\n",CoverageRegion::_totalPolygons);
printLog("_occlusionTests=%d\n",CoverageRegion::_occlusionTests);
printLog("_outOfOrderPolygon=%d\n",CoverageRegion::_outOfOrderPolygon);
printStats();
CoverageRegion::_maxPolygonsUsed = 0;
CoverageRegion::_totalPolygons = 0;
CoverageRegion::_occlusionTests = 0;
CoverageRegion::_regionSkips = 0;
CoverageRegion::_tooSmallSkips = 0;
CoverageRegion::_regionFullSkips = 0;
CoverageRegion::_outOfOrderPolygon = 0;
CoverageRegion::_clippedPolygons = 0;
_mapCount = 0;
_checkMapRootCalls = 0;
_notAllInView = 0;
}
}
@ -121,16 +140,60 @@ BoundingBox CoverageMap::getChildBoundingBox(int childIndex) {
return result;
}
int CoverageMap::getPolygonCount() const {
return (_topHalf.getPolygonCount() +
_bottomHalf.getPolygonCount() +
_leftHalf.getPolygonCount() +
_rightHalf.getPolygonCount() +
_remainder.getPolygonCount());
}
VoxelProjectedPolygon* CoverageMap::getPolygon(int index) const {
int base = 0;
if ((index - base) < _topHalf.getPolygonCount()) {
return _topHalf.getPolygon((index - base));
}
base += _topHalf.getPolygonCount();
if ((index - base) < _bottomHalf.getPolygonCount()) {
return _bottomHalf.getPolygon((index - base));
}
base += _bottomHalf.getPolygonCount();
if ((index - base) < _leftHalf.getPolygonCount()) {
return _leftHalf.getPolygon((index - base));
}
base += _leftHalf.getPolygonCount();
if ((index - base) < _rightHalf.getPolygonCount()) {
return _rightHalf.getPolygon((index - base));
}
base += _rightHalf.getPolygonCount();
if ((index - base) < _remainder.getPolygonCount()) {
return _remainder.getPolygon((index - base));
}
return NULL;
}
// possible results = STORED/NOT_STORED, OCCLUDED, DOESNT_FIT
CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, bool storeIt) {
if (_isRoot) {
_checkMapRootCalls++;
//printLog("CoverageMap::checkMap()... storeIt=%s\n", debug::valueOf(storeIt));
//polygon->printDebugDetails();
}
// short circuit: we don't handle polygons that aren't all in view, so, if the polygon in question is
// not in view, then we just discard it with a DOESNT_FIT, this saves us time checking values later.
if (!polygon->getAllInView()) {
_notAllInView++;
//printLog("CoverageMap2::checkMap()... V2_OCCLUDED\n");
return DOESNT_FIT;
}
@ -142,22 +205,25 @@ CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, b
bool fitsInAHalf = false;
// Check each half of the box independently
if (_topHalf.contains(polygonBox)) {
result = _topHalf.checkRegion(polygon, polygonBox, storeIt);
storeIn = &_topHalf;
fitsInAHalf = true;
} else if (_bottomHalf.contains(polygonBox)) {
result = _bottomHalf.checkRegion(polygon, polygonBox, storeIt);
storeIn = &_bottomHalf;
fitsInAHalf = true;
} else if (_leftHalf.contains(polygonBox)) {
result = _leftHalf.checkRegion(polygon, polygonBox, storeIt);
storeIn = &_leftHalf;
fitsInAHalf = true;
} else if (_rightHalf.contains(polygonBox)) {
result = _rightHalf.checkRegion(polygon, polygonBox, storeIt);
storeIn = &_rightHalf;
fitsInAHalf = true;
const bool useRegions = true; // for now we will continue to use regions
if (useRegions) {
if (_topHalf.contains(polygonBox)) {
result = _topHalf.checkRegion(polygon, polygonBox, storeIt);
storeIn = &_topHalf;
fitsInAHalf = true;
} else if (_bottomHalf.contains(polygonBox)) {
result = _bottomHalf.checkRegion(polygon, polygonBox, storeIt);
storeIn = &_bottomHalf;
fitsInAHalf = true;
} else if (_leftHalf.contains(polygonBox)) {
result = _leftHalf.checkRegion(polygon, polygonBox, storeIt);
storeIn = &_leftHalf;
fitsInAHalf = true;
} else if (_rightHalf.contains(polygonBox)) {
result = _rightHalf.checkRegion(polygon, polygonBox, storeIt);
storeIn = &_rightHalf;
fitsInAHalf = true;
}
}
// if we got this far, there are one of two possibilities, either a polygon doesn't fit
@ -171,36 +237,77 @@ CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, b
// It's possible that this first set of checks might have resulted in an out of order polygon
// in which case we just return..
if (result == STORED || result == OCCLUDED) {
/*
if (result == STORED)
printLog("CoverageMap2::checkMap()... STORED\n");
else
printLog("CoverageMap2::checkMap()... OCCLUDED\n");
*/
return result;
}
// if we made it here, then it means the polygon being stored is not occluded
// at this level of the quad tree, so we can continue to insert it into the map.
// First we check to see if it fits in any of our sub maps
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
BoundingBox childMapBoundingBox = getChildBoundingBox(i);
if (childMapBoundingBox.contains(polygon->getBoundingBox())) {
// if no child map exists yet, then create it
if (!_childMaps[i]) {
_childMaps[i] = new CoverageMap(childMapBoundingBox, NOT_ROOT, _managePolygons);
const bool useChildMaps = true; // for now we will continue to use child maps
if (useChildMaps) {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
BoundingBox childMapBoundingBox = getChildBoundingBox(i);
if (childMapBoundingBox.contains(polygon->getBoundingBox())) {
// if no child map exists yet, then create it
if (!_childMaps[i]) {
_childMaps[i] = new CoverageMap(childMapBoundingBox, NOT_ROOT, _managePolygons);
}
result = _childMaps[i]->checkMap(polygon, storeIt);
/*
switch (result) {
case STORED:
printLog("checkMap() = STORED\n");
break;
case NOT_STORED:
printLog("checkMap() = NOT_STORED\n");
break;
case OCCLUDED:
printLog("checkMap() = OCCLUDED\n");
break;
default:
printLog("checkMap() = ????? \n");
break;
}
*/
return result;
}
return _childMaps[i]->checkMap(polygon, storeIt);
}
}
// if we got this far, then the polygon is in our bounding box, but doesn't fit in
// any of our child bounding boxes, so we should add it here.
if (storeIt) {
if (polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) {
//printLog("storing polygon of area: %f\n",polygon->getBoundingBox().area());
storeIn->storeInArray(polygon);
return STORED;
//printLog("storing polygon of area: %f\n",polygon->getBoundingBox().area());
if (storeIn->getPolygonCount() < MAX_POLYGONS_PER_REGION) {
storeIn->storeInArray(polygon);
//printLog("CoverageMap2::checkMap()... STORED\n");
return STORED;
} else {
CoverageRegion::_regionFullSkips++;
//printLog("CoverageMap2::checkMap()... NOT_STORED\n");
return NOT_STORED;
}
} else {
CoverageRegion::_tooSmallSkips++;
//printLog("CoverageMap2::checkMap()... NOT_STORED\n");
return NOT_STORED;
}
} else {
//printLog("CoverageMap2::checkMap()... NOT_STORED\n");
return NOT_STORED;
}
}
//printLog("CoverageMap2::checkMap()... DOESNT_FIT\n");
return DOESNT_FIT;
}
@ -223,12 +330,13 @@ void CoverageRegion::init() {
_polygonArraySize = 0;
_polygons = NULL;
_polygonDistances = NULL;
_polygonSizes = NULL;
}
void CoverageRegion::erase() {
/*
/**
if (_polygonCount) {
printLog("CoverageRegion::erase()...\n");
printLog("_polygonCount=%d\n",_polygonCount);
@ -238,7 +346,7 @@ void CoverageRegion::erase() {
// _polygons[i]->getBoundingBox().printDebugDetails();
//}
}
*/
**/
// If we're in charge of managing the polygons, then clean them up first
if (_managePolygons) {
for (int i = 0; i < _polygonCount; i++) {
@ -258,11 +366,16 @@ void CoverageRegion::erase() {
delete[] _polygonDistances;
_polygonDistances = NULL;
}
if (_polygonSizes) {
delete[] _polygonSizes;
_polygonSizes = NULL;
}
}
void CoverageRegion::growPolygonArray() {
VoxelProjectedPolygon** newPolygons = new VoxelProjectedPolygon*[_polygonArraySize + DEFAULT_GROW_SIZE];
float* newDistances = new float[_polygonArraySize + DEFAULT_GROW_SIZE];
float* newDistances = new float[_polygonArraySize + DEFAULT_GROW_SIZE];
float* newSizes = new float[_polygonArraySize + DEFAULT_GROW_SIZE];
if (_polygons) {
@ -270,9 +383,12 @@ void CoverageRegion::growPolygonArray() {
delete[] _polygons;
memcpy(newDistances, _polygonDistances, sizeof(float) * _polygonCount);
delete[] _polygonDistances;
memcpy(newSizes, _polygonSizes, sizeof(float) * _polygonCount);
delete[] _polygonSizes;
}
_polygons = newPolygons;
_polygons = newPolygons;
_polygonDistances = newDistances;
_polygonSizes = newSizes;
_polygonArraySize = _polygonArraySize + DEFAULT_GROW_SIZE;
//printLog("CoverageMap::growPolygonArray() _polygonArraySize=%d...\n",_polygonArraySize);
}
@ -297,26 +413,87 @@ const char* CoverageRegion::getRegionName() const {
int CoverageRegion::_maxPolygonsUsed = 0;
int CoverageRegion::_totalPolygons = 0;
int CoverageRegion::_occlusionTests = 0;
int CoverageRegion::_regionSkips = 0;
int CoverageRegion::_tooSmallSkips = 0;
int CoverageRegion::_regionFullSkips = 0;
int CoverageRegion::_outOfOrderPolygon = 0;
int CoverageRegion::_clippedPolygons = 0;
bool CoverageRegion::mergeItemsInArray(VoxelProjectedPolygon* seed, bool seedInArray) {
for (int i = 0; i < _polygonCount; i++) {
VoxelProjectedPolygon* otherPolygon = _polygons[i];
if (otherPolygon->canMerge(*seed)) {
otherPolygon->merge(*seed);
if (seedInArray) {
const int IGNORED = NULL;
// remove this otherOtherPolygon for our polygon array
_polygonCount = removeFromSortedArrays((void*)seed,
(void**)_polygons, _polygonDistances, IGNORED,
_polygonCount, _polygonArraySize);
_totalPolygons--;
}
//printLog("_polygonCount=%d\n",_polygonCount);
// clean up
if (_managePolygons) {
delete seed;
}
// Now run again using our newly merged polygon as the seed
mergeItemsInArray(otherPolygon, true);
return true;
}
}
return false;
}
// just handles storage in the array, doesn't test for occlusion or
// determining if this is the correct map to store in!
void CoverageRegion::storeInArray(VoxelProjectedPolygon* polygon) {
_currentCoveredBounds.explandToInclude(polygon->getBoundingBox());
// Before we actually store this polygon in the array, check to see if this polygon can be merged to any of the existing
// polygons already in our array.
if (mergeItemsInArray(polygon, false)) {
return; // exit early
}
// only after we attempt to merge!
_totalPolygons++;
if (_polygonArraySize < _polygonCount + 1) {
growPolygonArray();
}
// This old code assumes that polygons will always be added in z-buffer order, but that doesn't seem to
// be a good assumption. So instead, we will need to sort this by distance. Use a binary search to find the
// insertion point in this array, and shift the array accordingly
// As an experiment we're going to see if we get an improvement by storing the polygons in coverage area sorted order
// this means the bigger polygons are earlier in the array. We should have a higher probability of being occluded earlier
// in the list. We still check to see if the polygon is "in front" of the target polygon before we test occlusion. Since
// sometimes things come out of order.
const bool SORT_BY_SIZE = false;
const int IGNORED = NULL;
_polygonCount = insertIntoSortedArrays((void*)polygon, polygon->getDistance(), IGNORED,
(void**)_polygons, _polygonDistances, IGNORED,
_polygonCount, _polygonArraySize);
if (SORT_BY_SIZE) {
// This old code assumes that polygons will always be added in z-buffer order, but that doesn't seem to
// be a good assumption. So instead, we will need to sort this by distance. Use a binary search to find the
// insertion point in this array, and shift the array accordingly
float area = polygon->getBoundingBox().area();
float reverseArea = 4.0f - area;
//printLog("store by size area=%f reverse area=%f\n", area, reverseArea);
_polygonCount = insertIntoSortedArrays((void*)polygon, reverseArea, IGNORED,
(void**)_polygons, _polygonSizes, IGNORED,
_polygonCount, _polygonArraySize);
} else {
const int IGNORED = NULL;
_polygonCount = insertIntoSortedArrays((void*)polygon, polygon->getDistance(), IGNORED,
(void**)_polygons, _polygonDistances, IGNORED,
_polygonCount, _polygonArraySize);
}
// Debugging and Optimization Tuning code.
if (_polygonCount > _maxPolygonsUsed) {
_maxPolygonsUsed = _polygonCount;
@ -335,37 +512,47 @@ CoverageMapStorageResult CoverageRegion::checkRegion(VoxelProjectedPolygon* poly
if (_isRoot || _myBoundingBox.contains(polygonBox)) {
result = NOT_STORED; // if we got here, then we DO fit...
// only actually check the polygons if this polygon is in the covered bounds for this region
if (!_currentCoveredBounds.contains(polygonBox)) {
_regionSkips += _polygonCount;
} else {
// check to make sure this polygon isn't occluded by something at this level
for (int i = 0; i < _polygonCount; i++) {
VoxelProjectedPolygon* polygonAtThisLevel = _polygons[i];
// check to make sure this polygon isn't occluded by something at this level
for (int i = 0; i < _polygonCount; i++) {
VoxelProjectedPolygon* polygonAtThisLevel = _polygons[i];
// Check to make sure that the polygon in question is "behind" the polygon in the list
// otherwise, we don't need to test it's occlusion (although, it means we've potentially
// added an item previously that may be occluded??? Is that possible? Maybe not, because two
// voxels can't have the exact same outline. So one occludes the other, they can't both occlude
// each other.
_occlusionTests++;
if (polygonAtThisLevel->occludes(*polygon)) {
// if the polygonAtThisLevel is actually behind the one we're inserting, then we don't
// want to report our inserted one as occluded, but we do want to add our inserted one.
if (polygonAtThisLevel->getDistance() >= polygon->getDistance()) {
_outOfOrderPolygon++;
if (storeIt) {
if (polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) {
//printLog("storing polygon of area: %f\n",polygon->getBoundingBox().area());
storeInArray(polygon);
return STORED;
// Check to make sure that the polygon in question is "behind" the polygon in the list
// otherwise, we don't need to test it's occlusion (although, it means we've potentially
// added an item previously that may be occluded??? Is that possible? Maybe not, because two
// voxels can't have the exact same outline. So one occludes the other, they can't both occlude
// each other.
_occlusionTests++;
if (polygonAtThisLevel->occludes(*polygon)) {
// if the polygonAtThisLevel is actually behind the one we're inserting, then we don't
// want to report our inserted one as occluded, but we do want to add our inserted one.
if (polygonAtThisLevel->getDistance() >= polygon->getDistance()) {
_outOfOrderPolygon++;
if (storeIt) {
if (polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) {
if (getPolygonCount() < MAX_POLYGONS_PER_REGION) {
storeInArray(polygon);
return STORED;
} else {
CoverageRegion::_regionFullSkips++;
return NOT_STORED;
}
} else {
_tooSmallSkips++;
return NOT_STORED;
}
} else {
return NOT_STORED;
}
} else {
return NOT_STORED;
}
// this polygon is occluded by a closer polygon, so don't store it, and let the caller know
return OCCLUDED;
}
// this polygon is occluded by a closer polygon, so don't store it, and let the caller know
return OCCLUDED;
}
}
}

View file

@ -31,24 +31,38 @@ public:
static int _maxPolygonsUsed;
static int _totalPolygons;
static int _occlusionTests;
static int _regionSkips;
static int _tooSmallSkips;
static int _regionFullSkips;
static int _outOfOrderPolygon;
static int _clippedPolygons;
const char* getRegionName() const;
int getPolygonCount() const { return _polygonCount; };
VoxelProjectedPolygon* getPolygon(int index) const { return _polygons[index]; };
private:
void init();
bool _isRoot; // is this map the root, if so, it never returns DOESNT_FIT
BoundingBox _myBoundingBox;
BoundingBox _currentCoveredBounds; // area in this region currently covered by some polygon
bool _managePolygons; // will the coverage map delete the polygons on destruct
RegionName _regionName;
int _polygonCount; // how many polygons at this level
int _polygonArraySize; // how much room is there to store polygons at this level
VoxelProjectedPolygon** _polygons;
// we will use one or the other of these depending on settings in the code.
float* _polygonDistances;
float* _polygonSizes;
void growPolygonArray();
static const int DEFAULT_GROW_SIZE = 100;
bool mergeItemsInArray(VoxelProjectedPolygon* seed, bool seedInArray);
};
class CoverageMap {
@ -68,9 +82,14 @@ public:
BoundingBox getChildBoundingBox(int childIndex);
void erase(); // erase the coverage map
void printStats();
static bool wantDebugging;
int getPolygonCount() const;
VoxelProjectedPolygon* getPolygon(int index) const;
CoverageMap* getChild(int childIndex) const { return _childMaps[childIndex]; };
private:
void init();
@ -89,6 +108,7 @@ private:
static int _mapCount;
static int _checkMapRootCalls;
static int _notAllInView;
};

View file

@ -0,0 +1,246 @@
//
// CoverageMapV2.cpp -
// hifi
//
// Added by Brad Hefta-Gaub on 06/11/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <algorithm>
#include "CoverageMapV2.h"
#include <SharedUtil.h>
#include <cstring>
#include "Log.h"
int CoverageMapV2::_mapCount = 0;
int CoverageMapV2::_checkMapRootCalls = 0;
int CoverageMapV2::_notAllInView = 0;
bool CoverageMapV2::wantDebugging = false;
const BoundingBox CoverageMapV2::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-1.f,-1.f), glm::vec2(2.f,2.f));
// Coverage Map's polygon coordinates are from -1 to 1 in the following mapping to screen space.
//
// (0,0) (windowWidth, 0)
// -1,1 1,1
// +-----------------------+
// | | |
// | | |
// | -1,0 | |
// |-----------+-----------|
// | 0,0 |
// | | |
// | | |
// | | |
// +-----------------------+
// -1,-1 1,-1
// (0,windowHeight) (windowWidth,windowHeight)
//
// Choosing a minimum sized polygon. Since we know a typical window is approximately 1500 pixels wide
// then a pixel on our screen will be ~ 2.0/1500 or 0.0013 "units" wide, similarly pixels are typically
// about that tall as well. If we say that polygons should be at least 10x10 pixels to be considered "big enough"
// then we can calculate a reasonable polygon area
const int TYPICAL_SCREEN_WIDTH_IN_PIXELS = 1500;
const int MINIMUM_POLYGON_AREA_SIDE_IN_PIXELS = 10;
const float TYPICAL_SCREEN_PIXEL_WIDTH = (2.0f / TYPICAL_SCREEN_WIDTH_IN_PIXELS);
const float CoverageMapV2::MINIMUM_POLYGON_AREA_TO_STORE = (TYPICAL_SCREEN_PIXEL_WIDTH * MINIMUM_POLYGON_AREA_SIDE_IN_PIXELS) *
(TYPICAL_SCREEN_PIXEL_WIDTH * MINIMUM_POLYGON_AREA_SIDE_IN_PIXELS);
const float CoverageMapV2::NOT_COVERED = FLT_MAX;
const float CoverageMapV2::MINIMUM_OCCLUSION_CHECK_AREA = MINIMUM_POLYGON_AREA_TO_STORE/10.0f; // one quarter the size of poly
CoverageMapV2::CoverageMapV2(BoundingBox boundingBox, bool isRoot, bool isCovered, float coverageDistance) :
_isRoot(isRoot),
_myBoundingBox(boundingBox),
_isCovered(isCovered),
_coveredDistance(coverageDistance)
{
_mapCount++;
init();
//printLog("CoverageMapV2 created... _mapCount=%d\n",_mapCount);
};
CoverageMapV2::~CoverageMapV2() {
erase();
};
void CoverageMapV2::erase() {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (_childMaps[i]) {
delete _childMaps[i];
_childMaps[i] = NULL;
}
}
if (_isRoot && wantDebugging) {
printLog("CoverageMapV2 last to be deleted...\n");
printLog("MINIMUM_POLYGON_AREA_TO_STORE=%f\n",MINIMUM_POLYGON_AREA_TO_STORE);
printLog("_mapCount=%d\n",_mapCount);
printLog("_checkMapRootCalls=%d\n",_checkMapRootCalls);
printLog("_notAllInView=%d\n",_notAllInView);
_mapCount = 0;
_checkMapRootCalls = 0;
_notAllInView = 0;
}
}
void CoverageMapV2::init() {
memset(_childMaps,0,sizeof(_childMaps));
}
// 0 = bottom, left
// 1 = bottom, right
// 2 = top, left
// 3 = top, right
BoundingBox CoverageMapV2::getChildBoundingBox(int childIndex) {
const int RIGHT_BIT = 1;
const int TOP_BIT = 2;
// initialize to our corner, and half our size
BoundingBox result(_myBoundingBox.corner,_myBoundingBox.size/2.0f);
// if our "right" bit is set, then add size.x to the corner
if ((childIndex & RIGHT_BIT) == RIGHT_BIT) {
result.corner.x += result.size.x;
}
// if our "top" bit is set, then add size.y to the corner
if ((childIndex & TOP_BIT) == TOP_BIT) {
result.corner.y += result.size.y;
}
return result;
}
// possible results = STORED/NOT_STORED, OCCLUDED, DOESNT_FIT
CoverageMapV2StorageResult CoverageMapV2::checkMap(const VoxelProjectedPolygon* polygon, bool storeIt) {
assert(_isRoot); // you can only call this on the root map!!!
_checkMapRootCalls++;
// short circuit: if we're the root node (only case we're here), and we're covered, and this polygon is deeper than our
// covered depth, then this polygon is occluded!
if (_isCovered && _coveredDistance < polygon->getDistance()) {
return V2_OCCLUDED;
}
// short circuit: we don't handle polygons that aren't all in view, so, if the polygon in question is
// not in view, then we just discard it with a DOESNT_FIT, this saves us time checking values later.
if (!polygon->getAllInView()) {
_notAllInView++;
return V2_DOESNT_FIT;
}
// Here's where we recursively check the polygon against the coverage map. We need to maintain two pieces of state.
// The first state is: have we seen at least one "fully occluded" map items. If we haven't then we don't track the covered
// state of the polygon.
// The second piece of state is: Are all of our "fully occluded" map items "covered". If even one of these occluded map
// items is not covered, then our polygon is not covered.
bool seenOccludedMapNodes = false;
bool allOccludedMapNodesCovered = false;
recurseMap(polygon, storeIt, seenOccludedMapNodes, allOccludedMapNodesCovered);
// Ok, no matter how we were called, if all our occluded map nodes are covered, then we know this polygon
// is occluded, otherwise, we will report back to the caller about whether or not we stored the polygon
if (allOccludedMapNodesCovered) {
return V2_OCCLUDED;
}
if (storeIt) {
return V2_STORED; // otherwise report that we STORED it
}
return V2_NOT_STORED; // unless we weren't asked to store it, then we didn't
}
void CoverageMapV2::recurseMap(const VoxelProjectedPolygon* polygon, bool storeIt,
bool& seenOccludedMapNodes, bool& allOccludedMapNodesCovered) {
// if we are really small, then we act like we don't intersect, this allows us to stop
// recusing as we get to the smalles edge of the polygon
if (_myBoundingBox.area() < MINIMUM_OCCLUSION_CHECK_AREA) {
return; // stop recursion, we're done!
}
// Determine if this map node intersects the polygon and/or is fully covered by the polygon
// There are a couple special cases: If we're the root, we are assumed to intersect with all
// polygons. Also, any map node that is fully occluded also intersects.
bool nodeIsCoveredByPolygon = polygon->occludes(_myBoundingBox);
bool nodeIsIntersectedByPolygon = nodeIsCoveredByPolygon || _isRoot || polygon->intersects(_myBoundingBox);
// If we don't intersect, then we can just return, we're done recursing
if (!nodeIsIntersectedByPolygon) {
return; // stop recursion, we're done!
}
// At this point, we know our node intersects with the polygon. If this node is covered, then we want to treat it
// as if the node was fully covered, because this allows us to short circuit further recursion...
if (_isCovered && _coveredDistance < polygon->getDistance()) {
nodeIsCoveredByPolygon = true; // fake it till you make it
}
// If this node in the map is fully covered by our polygon, then we don't need to recurse any further, but
// we do need to do some bookkeeping.
if (nodeIsCoveredByPolygon) {
// If this is the very first fully covered node we've seen, then we're initialize our allOccludedMapNodesCovered
// to be our current covered state. This has the following effect: if this node isn't already covered, then by
// definition, we know that at least one node for this polygon isn't covered, and therefore we aren't fully covered.
if (!seenOccludedMapNodes) {
allOccludedMapNodesCovered = (_isCovered && _coveredDistance < polygon->getDistance());
// We need to mark that we've seen at least one node of our polygon! ;)
seenOccludedMapNodes = true;
} else {
// If this is our second or later node of our polygon, then we need to track our allOccludedMapNodesCovered state
allOccludedMapNodesCovered = allOccludedMapNodesCovered &&
(_isCovered && _coveredDistance < polygon->getDistance());
}
// if we're in store mode then we want to record that this node is covered.
if (storeIt) {
_isCovered = true;
// store the minimum distance of our previous known distance, or our current polygon's distance. This is because
// we know that we're at least covered at this distance, but if we had previously identified that we're covered
// at a shallower distance, then we want to maintain that distance
_coveredDistance = std::min(polygon->getDistance(), _coveredDistance);
// Note: this might be a good chance to delete child maps, but we're not going to do that at this point because
// we're trying to maintain the known distances in the lower portion of the tree.
}
// and since this node of the quad map is covered, we can safely stop recursion. because we know all smaller map
// nodes will also be covered.
return;
}
// If we got here, then it means we know that this node is not fully covered by the polygon, but it does intersect
// with the polygon.
// Another case is that we aren't yet marked as covered, and so we should recurse and process smaller quad tree nodes.
// Note: we use this to determine if we can collapse the child quad trees and mark this node as covered
bool allChildrenOccluded = true;
float maxChildCoveredDepth = NOT_COVERED;
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
BoundingBox childMapBoundingBox = getChildBoundingBox(i);
// if no child map exists yet, then create it
if (!_childMaps[i]) {
// children get created with the coverage state of their parent.
_childMaps[i] = new CoverageMapV2(childMapBoundingBox, NOT_ROOT, _isCovered, _coveredDistance);
}
_childMaps[i]->recurseMap(polygon, storeIt, seenOccludedMapNodes, allOccludedMapNodesCovered);
// if so far, all of our children are covered, then record our furthest coverage distance
if (allChildrenOccluded && _childMaps[i]->_isCovered) {
maxChildCoveredDepth = std::max(maxChildCoveredDepth, _childMaps[i]->_coveredDistance);
} else {
// otherwise, at least one of our children is not covered, so not all are covered
allChildrenOccluded = false;
}
}
// if all the children are covered, this makes our quad tree "shallower" because it records that
// entire quad is covered, it uses the "furthest" z-order so that if a shalower polygon comes through
// we won't assume its occluded
if (allChildrenOccluded && storeIt) {
_isCovered = true;
_coveredDistance = maxChildCoveredDepth;
}
// normal exit case... return...
}

View file

@ -0,0 +1,69 @@
//
// CoverageMapV2.h - 2D CoverageMapV2 Quad tree for storage of VoxelProjectedPolygons
// hifi
//
// Added by Brad Hefta-Gaub on 06/11/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef _COVERAGE_MAP_V2_
#define _COVERAGE_MAP_V2_
#include <glm/glm.hpp>
#include "VoxelProjectedPolygon.h"
typedef enum {
V2_DOESNT_FIT, V2_STORED, V2_NOT_STORED,
V2_INTERSECT, V2_NO_INTERSECT,
V2_OCCLUDED, V2_NOT_OCCLUDED
} CoverageMapV2StorageResult;
class CoverageMapV2 {
public:
static const int NUMBER_OF_CHILDREN = 4;
static const bool NOT_ROOT = false;
static const bool IS_ROOT = true;
static const BoundingBox ROOT_BOUNDING_BOX;
static const float MINIMUM_POLYGON_AREA_TO_STORE;
static const float NOT_COVERED;
static const float MINIMUM_OCCLUSION_CHECK_AREA;
static bool wantDebugging;
CoverageMapV2(BoundingBox boundingBox = ROOT_BOUNDING_BOX, bool isRoot = IS_ROOT,
bool isCovered = false, float coverageDistance = NOT_COVERED);
~CoverageMapV2();
CoverageMapV2StorageResult checkMap(const VoxelProjectedPolygon* polygon, bool storeIt = true);
BoundingBox getChildBoundingBox(int childIndex);
const BoundingBox& getBoundingBox() const { return _myBoundingBox; };
CoverageMapV2* getChild(int childIndex) const { return _childMaps[childIndex]; };
bool isCovered() const { return _isCovered; };
void erase(); // erase the coverage map
void render();
private:
void recurseMap(const VoxelProjectedPolygon* polygon, bool storeIt,
bool& seenOccludedMapNodes, bool& allOccludedMapNodesCovered);
void init();
bool _isRoot;
BoundingBox _myBoundingBox;
CoverageMapV2* _childMaps[NUMBER_OF_CHILDREN];
bool _isCovered;
float _coveredDistance;
static int _mapCount;
static int _checkMapRootCalls;
static int _notAllInView;
};
#endif // _COVERAGE_MAP_V2_

View file

@ -5,6 +5,9 @@
// Created by Andrzej Kapolka on 5/21/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
#include <cstring>
#include <Log.h>
#include <SharedUtil.h>
#include "GeometryUtil.h"
@ -117,6 +120,7 @@ glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3&
}
// Do line segments (r1p1.x, r1p1.y)--(r1p2.x, r1p2.y) and (r2p1.x, r2p1.y)--(r2p2.x, r2p2.y) intersect?
// from: http://ptspts.blogspot.com/2010/06/how-to-determine-if-two-line-segments.html
bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm::vec2 r2p2) {
int d1 = computeDirection(r2p1.x, r2p1.y, r2p2.x, r2p2.y, r1p1.x, r1p1.y);
int d2 = computeDirection(r2p1.x, r2p1.y, r2p2.x, r2p2.y, r1p2.x, r1p2.y);
@ -140,3 +144,193 @@ int computeDirection(float xi, float yi, float xj, float yj, float xk, float yk)
float b = (xj - xi) * (yk - yi);
return a < b ? -1 : a > b ? 1 : 0;
}
//
// Polygon Clipping routines inspired by, pseudo code found here: http://www.cs.rit.edu/~icss571/clipTrans/PolyClipBack.html
//
// Coverage Map's polygon coordinates are from -1 to 1 in the following mapping to screen space.
//
// (0,0) (windowWidth, 0)
// -1,1 1,1
// +-----------------------+
// | | |
// | | |
// | -1,0 | |
// |-----------+-----------|
// | 0,0 |
// | | |
// | | |
// | | |
// +-----------------------+
// -1,-1 1,-1
// (0,windowHeight) (windowWidth,windowHeight)
//
const float PolygonClip::TOP_OF_CLIPPING_WINDOW = 1.0f;
const float PolygonClip::BOTTOM_OF_CLIPPING_WINDOW = -1.0f;
const float PolygonClip::LEFT_OF_CLIPPING_WINDOW = -1.0f;
const float PolygonClip::RIGHT_OF_CLIPPING_WINDOW = 1.0f;
const glm::vec2 PolygonClip::TOP_LEFT_CLIPPING_WINDOW ( LEFT_OF_CLIPPING_WINDOW , TOP_OF_CLIPPING_WINDOW );
const glm::vec2 PolygonClip::TOP_RIGHT_CLIPPING_WINDOW ( RIGHT_OF_CLIPPING_WINDOW, TOP_OF_CLIPPING_WINDOW );
const glm::vec2 PolygonClip::BOTTOM_LEFT_CLIPPING_WINDOW ( LEFT_OF_CLIPPING_WINDOW , BOTTOM_OF_CLIPPING_WINDOW );
const glm::vec2 PolygonClip::BOTTOM_RIGHT_CLIPPING_WINDOW ( RIGHT_OF_CLIPPING_WINDOW, BOTTOM_OF_CLIPPING_WINDOW );
void PolygonClip::clipToScreen(const glm::vec2* inputVertexArray, int inLength, glm::vec2*& outputVertexArray, int& outLength) {
int tempLengthA = inLength;
int tempLengthB;
int maxLength = inLength * 2;
glm::vec2* tempVertexArrayA = new glm::vec2[maxLength];
glm::vec2* tempVertexArrayB = new glm::vec2[maxLength];
// set up our temporary arrays
memcpy(tempVertexArrayA, inputVertexArray, sizeof(glm::vec2) * inLength);
// Left edge
LineSegment2 edge;
edge[0] = TOP_LEFT_CLIPPING_WINDOW;
edge[1] = BOTTOM_LEFT_CLIPPING_WINDOW;
// clip the array from tempVertexArrayA and copy end result to tempVertexArrayB
sutherlandHodgmanPolygonClip(tempVertexArrayA, tempVertexArrayB, tempLengthA, tempLengthB, edge);
// clean the array from tempVertexArrayA and copy cleaned result to tempVertexArrayA
copyCleanArray(tempLengthA, tempVertexArrayA, tempLengthB, tempVertexArrayB);
// Bottom Edge
edge[0] = BOTTOM_LEFT_CLIPPING_WINDOW;
edge[1] = BOTTOM_RIGHT_CLIPPING_WINDOW;
// clip the array from tempVertexArrayA and copy end result to tempVertexArrayB
sutherlandHodgmanPolygonClip(tempVertexArrayA, tempVertexArrayB, tempLengthA, tempLengthB, edge);
// clean the array from tempVertexArrayA and copy cleaned result to tempVertexArrayA
copyCleanArray(tempLengthA, tempVertexArrayA, tempLengthB, tempVertexArrayB);
// Right Edge
edge[0] = BOTTOM_RIGHT_CLIPPING_WINDOW;
edge[1] = TOP_RIGHT_CLIPPING_WINDOW;
// clip the array from tempVertexArrayA and copy end result to tempVertexArrayB
sutherlandHodgmanPolygonClip(tempVertexArrayA, tempVertexArrayB, tempLengthA, tempLengthB, edge);
// clean the array from tempVertexArrayA and copy cleaned result to tempVertexArrayA
copyCleanArray(tempLengthA, tempVertexArrayA, tempLengthB, tempVertexArrayB);
// Top Edge
edge[0] = TOP_RIGHT_CLIPPING_WINDOW;
edge[1] = TOP_LEFT_CLIPPING_WINDOW;
// clip the array from tempVertexArrayA and copy end result to tempVertexArrayB
sutherlandHodgmanPolygonClip(tempVertexArrayA, tempVertexArrayB, tempLengthA, tempLengthB, edge);
// clean the array from tempVertexArrayA and copy cleaned result to tempVertexArrayA
copyCleanArray(tempLengthA, tempVertexArrayA, tempLengthB, tempVertexArrayB);
// copy final output to outputVertexArray
outputVertexArray = tempVertexArrayA;
outLength = tempLengthA;
// cleanup our unused temporary buffer...
delete[] tempVertexArrayB;
// Note: we don't delete tempVertexArrayA, because that's the caller's responsibility
}
void PolygonClip::sutherlandHodgmanPolygonClip(glm::vec2* inVertexArray, glm::vec2* outVertexArray,
int inLength, int& outLength, const LineSegment2& clipBoundary) {
glm::vec2 start, end; // Start, end point of current polygon edge
glm::vec2 intersection; // Intersection point with a clip boundary
outLength = 0;
start = inVertexArray[inLength - 1]; // Start with the last vertex in inVertexArray
for (int j = 0; j < inLength; j++) {
end = inVertexArray[j]; // Now start and end correspond to the vertices
// Cases 1 and 4 - the endpoint is inside the boundary
if (pointInsideBoundary(end,clipBoundary)) {
// Case 1 - Both inside
if (pointInsideBoundary(start, clipBoundary)) {
appendPoint(end, outLength, outVertexArray);
} else { // Case 4 - end is inside, but start is outside
segmentIntersectsBoundary(start, end, clipBoundary, intersection);
appendPoint(intersection, outLength, outVertexArray);
appendPoint(end, outLength, outVertexArray);
}
} else { // Cases 2 and 3 - end is outside
if (pointInsideBoundary(start, clipBoundary)) {
// Cases 2 - start is inside, end is outside
segmentIntersectsBoundary(start, end, clipBoundary, intersection);
appendPoint(intersection, outLength, outVertexArray);
} else {
// Case 3 - both are outside, No action
}
}
start = end; // Advance to next pair of vertices
}
}
bool PolygonClip::pointInsideBoundary(const glm::vec2& testVertex, const LineSegment2& clipBoundary) {
// bottom edge
if (clipBoundary[1].x > clipBoundary[0].x) {
if (testVertex.y >= clipBoundary[0].y) {
return true;
}
}
// top edge
if (clipBoundary[1].x < clipBoundary[0].x) {
if (testVertex.y <= clipBoundary[0].y) {
return true;
}
}
// right edge
if (clipBoundary[1].y > clipBoundary[0].y) {
if (testVertex.x <= clipBoundary[1].x) {
return true;
}
}
// left edge
if (clipBoundary[1].y < clipBoundary[0].y) {
if (testVertex.x >= clipBoundary[1].x) {
return true;
}
}
return false;
}
void PolygonClip::segmentIntersectsBoundary(const glm::vec2& first, const glm::vec2& second,
const LineSegment2& clipBoundary, glm::vec2& intersection) {
// horizontal
if (clipBoundary[0].y==clipBoundary[1].y) {
intersection.y = clipBoundary[0].y;
intersection.x = first.x + (clipBoundary[0].y - first.y) * (second.x - first.x) / (second.y - first.y);
} else { // Vertical
intersection.x = clipBoundary[0].x;
intersection.y = first.y + (clipBoundary[0].x - first.x) * (second.y - first.y) / (second.x - first.x);
}
}
void PolygonClip::appendPoint(glm::vec2 newVertex, int& outLength, glm::vec2* outVertexArray) {
outVertexArray[outLength].x = newVertex.x;
outVertexArray[outLength].y = newVertex.y;
outLength++;
}
// The copyCleanArray() function sets the resulting polygon of the previous step up to be the input polygon for next step of the
// clipping algorithm. As the Sutherland-Hodgman algorithm is a polygon clipping algorithm, it does not handle line
// clipping very well. The modification so that lines may be clipped as well as polygons is included in this function.
// when completed vertexArrayA will be ready for output and/or next step of clipping
void PolygonClip::copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& lengthB, glm::vec2* vertexArrayB) {
// Fix lines: they will come back with a length of 3, from an original of length of 2
if ((lengthA == 2) && (lengthB == 3)) {
// The first vertex should be copied as is.
vertexArrayA[0] = vertexArrayB[0];
// If the first two vertices of the "B" array are same, then collapse them down to be the 2nd vertex
if (vertexArrayB[0].x == vertexArrayB[1].x) {
vertexArrayA[1] = vertexArrayB[2];
} else {
// Otherwise the first vertex should be the same as third vertex
vertexArrayA[1] = vertexArrayB[1];
}
lengthA=2;
} else {
// for all other polygons, then just copy the vertexArrayB to vertextArrayA for next step
lengthA = lengthB;
for (int i = 0; i < lengthB; i++) {
vertexArrayA[i] = vertexArrayB[i];
}
}
}

View file

@ -43,4 +43,39 @@ bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm
bool isOnSegment(float xi, float yi, float xj, float yj, float xk, float yk);
int computeDirection(float xi, float yi, float xj, float yj, float xk, float yk);
typedef glm::vec2 LineSegment2[2];
// Polygon Clipping routines inspired by, pseudo code found here: http://www.cs.rit.edu/~icss571/clipTrans/PolyClipBack.html
class PolygonClip {
public:
static void clipToScreen(const glm::vec2* inputVertexArray, int length, glm::vec2*& outputVertexArray, int& outLength);
static const float TOP_OF_CLIPPING_WINDOW;
static const float BOTTOM_OF_CLIPPING_WINDOW;
static const float LEFT_OF_CLIPPING_WINDOW;
static const float RIGHT_OF_CLIPPING_WINDOW;
static const glm::vec2 TOP_LEFT_CLIPPING_WINDOW;
static const glm::vec2 TOP_RIGHT_CLIPPING_WINDOW;
static const glm::vec2 BOTTOM_LEFT_CLIPPING_WINDOW;
static const glm::vec2 BOTTOM_RIGHT_CLIPPING_WINDOW;
private:
static void sutherlandHodgmanPolygonClip(glm::vec2* inVertexArray, glm::vec2* outVertexArray,
int inLength, int& outLength, const LineSegment2& clipBoundary);
static bool pointInsideBoundary(const glm::vec2& testVertex, const LineSegment2& clipBoundary);
static void segmentIntersectsBoundary(const glm::vec2& first, const glm::vec2& second,
const LineSegment2& clipBoundary, glm::vec2& intersection);
static void appendPoint(glm::vec2 newVertex, int& outLength, glm::vec2* outVertexArray);
static void copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& lengthB, glm::vec2* vertexArrayB);
};
#endif /* defined(__interface__GeometryUtil__) */

View file

@ -3,12 +3,15 @@
// hifi
//
// Created by Brad Hefta-Gaub on 5/7/2013.
//
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include "SceneUtils.h"
#include <algorithm>
#include <glm/gtc/noise.hpp>
#include "SceneUtils.h"
void addCornersAndAxisLines(VoxelTree* tree) {
// 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;

View file

@ -3,12 +3,15 @@
// hifi
//
// Created by Tomáš Horáček on 6/25/13.
//
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include "SquarePixelMap.h"
#include <string.h>
#include <algorithm>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "SquarePixelMap.h"
#define CHILD_COORD_X_IS_1 0x1
#define CHILD_COORD_Y_IS_1 0x2

View file

@ -0,0 +1,245 @@
//
// Tags.h
// hifi
//
// Created by Clement Brisset on 7/3/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include "Tags.h"
#include <Log.h>
#include <zlib.h>
#include <zconf.h>
#include <iostream>
Tag::Tag(int tagId, std::stringstream &ss) : _tagId(tagId) {
int size = ss.get() << 8 | ss.get();
_name.clear();
for (int i = 0; i < size; ++i) { _name += ss.get();
}
}
Tag* Tag::readTag(int tagId, std::stringstream &ss) {
switch (tagId) {
case TAG_Byte:
return new TagByte(ss);
case TAG_Short:
return new TagShort(ss);
case TAG_Int:
return new TagInt(ss);
case TAG_Long:
return new TagLong(ss);
case TAG_Float:
return new TagFloat(ss);
case TAG_Double:
return new TagDouble(ss);
case TAG_Byte_Array:
return new TagByteArray(ss);
case TAG_String:
return new TagString(ss);
case TAG_List:
return new TagList(ss);
case TAG_Compound:
return new TagCompound(ss);
case TAG_Int_Array:
return new TagIntArray(ss);
default:
return NULL;
}
}
TagByte::TagByte(std::stringstream &ss) : Tag(TAG_Byte, ss) {
_data = ss.get();
}
TagShort::TagShort(std::stringstream &ss) : Tag(TAG_Short, ss) {
_data = ss.get() << 8 | ss.get();
}
TagInt::TagInt(std::stringstream &ss) : Tag(TAG_Int, ss) {
_data = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get();
}
TagLong::TagLong(std::stringstream &ss) : Tag(TAG_Long, ss) {
_data = (((int64_t) ss.get()) << 56 | ((int64_t) ss.get()) << 48
|((int64_t) ss.get()) << 40 | ((int64_t) ss.get()) << 32
| ss.get() << 24 | ss.get() << 16
| ss.get() << 8 | ss.get());
}
// We don't need Float and double, so we just ignore the bytes
TagFloat::TagFloat(std::stringstream &ss) : Tag(TAG_Float, ss) {
ss.seekg(4, ss.cur);
}
TagDouble::TagDouble(std::stringstream &ss) : Tag(TAG_Double, ss) {
ss.seekg(8, ss.cur);
}
TagByteArray::TagByteArray(std::stringstream &ss) : Tag(TAG_Byte_Array, ss) {
_size = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get();
_data = new char[_size];
for (int i = 0; i < _size; ++i) {
_data[i] = ss.get();
}
}
TagString::TagString(std::stringstream &ss) : Tag(TAG_String, ss) {
_size = ss.get() << 8 | ss.get();
for (int i = 0; i < _size; ++i) {
_data += ss.get();
}
}
TagList::TagList(std::stringstream &ss) :
Tag(TAG_List, ss) {
_tagId = ss.get();
_size = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get();
for (int i = 0; i < _size; ++i) {
ss.putback(0);
ss.putback(0);
_data.push_back(readTag(_tagId, ss));
}
}
TagCompound::TagCompound(std::stringstream &ss) :
Tag(TAG_Compound, ss),
_size(0),
_width(0),
_length(0),
_height(0),
_blocksData(NULL),
_blocksId(NULL)
{
int tagId;
while (TAG_End != (tagId = ss.get())) {
_data.push_back(readTag(tagId, ss));
++_size;
if (NULL == _data.back()) {
_blocksId = NULL;
_blocksData = NULL;
return;
} else if (TAG_Short == tagId) {
if ("Width" == _data.back()->getName()) {
_width = ((TagShort*) _data.back())->getData();
} else if ("Height" == _data.back()->getName()) {
_height = ((TagShort*) _data.back())->getData();
} else if ("Length" == _data.back()->getName()) {
_length = ((TagShort*) _data.back())->getData();
}
} else if (TAG_Byte_Array == tagId) {
if ("Blocks" == _data.back()->getName()) {
_blocksId = ((TagByteArray*) _data.back())->getData();
} else if ("Data" == _data.back()->getName()) {
_blocksData = ((TagByteArray*) _data.back())->getData();
}
}
}
}
TagIntArray::TagIntArray(std::stringstream &ss) : Tag(TAG_Int_Array, ss) {
_size = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get();
_data = new int[_size];
for (int i = 0; i < _size; ++i) {
_data[i] = ss.get();
}
}
int retrieveData(std::string filename, std::stringstream &ss) {
std::ifstream file(filename.c_str(), std::ios::binary);
int type = file.peek();
if (type == 0x0A) {
ss.flush();
ss << file;
return 0;
}
if (type == 0x1F) {
return ungzip(file, ss);
}
return 1;
}
int ungzip(std::ifstream &file, std::stringstream &ss) {
std::string gzipedBytes;
gzipedBytes.clear();
ss.flush();
while (!file.eof()) {
gzipedBytes += (char) file.get();
}
file.close();
if (gzipedBytes.size() == 0) {
ss << gzipedBytes;
return 0;
}
unsigned int full_length = gzipedBytes.size();
unsigned int half_length = gzipedBytes.size()/2;
unsigned int uncompLength = full_length;
char* uncomp = (char*) calloc(sizeof(char), uncompLength);
z_stream strm;
strm.next_in = (Bytef *) gzipedBytes.c_str();
strm.avail_in = full_length;
strm.total_out = 0;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
bool done = false;
if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) {
free(uncomp);
return 1;
}
while (!done) {
// If our output buffer is too small
if (strm.total_out >= uncompLength) {
// Increase size of output buffer
char* uncomp2 = (char*) calloc(sizeof(char), uncompLength + half_length);
memcpy(uncomp2, uncomp, uncompLength);
uncompLength += half_length;
free(uncomp);
uncomp = uncomp2;
}
strm.next_out = (Bytef *) (uncomp + strm.total_out);
strm.avail_out = uncompLength - strm.total_out;
// Inflate another chunk.
int err = inflate (&strm, Z_SYNC_FLUSH);
if (err == Z_STREAM_END) {
done = true;
} else if (err != Z_OK) {
break;
}
}
if (inflateEnd (&strm) != Z_OK) {
free(uncomp);
return 1;
}
for (size_t i = 0; i < strm.total_out; ++i) {
ss << uncomp[i];
}
free(uncomp);
return 0;
}

175
libraries/voxels/src/Tags.h Normal file
View file

@ -0,0 +1,175 @@
//
// Tags.h
// hifi
//
// Created by Clement Brisset on 7/3/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __hifi__Tags__
#define __hifi__Tags__
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <list>
#define TAG_End 0
#define TAG_Byte 1
#define TAG_Short 2
#define TAG_Int 3
#define TAG_Long 4
#define TAG_Float 5
#define TAG_Double 6
#define TAG_Byte_Array 7
#define TAG_String 8
#define TAG_List 9
#define TAG_Compound 10
#define TAG_Int_Array 11
int retrieveData(std::string filename, std::stringstream &ss);
int ungzip(std::ifstream &file, std::stringstream &ss);
class Tag {
public:
Tag(int tagId, std::stringstream &ss);
int getTagId() const {return _tagId;}
std::string getName () const {return _name; }
static Tag* readTag(int tagId, std::stringstream &ss);
protected:
int _tagId;
std::string _name;
};
class TagByte : public Tag {
public:
TagByte(std::stringstream &ss);
int8_t getData() const {return _data;}
private:
int8_t _data;
};
class TagShort : public Tag {
public:
TagShort(std::stringstream &ss);
int16_t getData() const {return _data;}
private:
int16_t _data;
};
class TagInt : public Tag {
public:
TagInt(std::stringstream &ss);
int32_t getData() const {return _data;}
private:
int32_t _data;
};
class TagLong : public Tag {
public:
TagLong(std::stringstream &ss);
int64_t getData() const {return _data;}
private:
int64_t _data;
};
class TagFloat : public Tag {
public:
TagFloat(std::stringstream &ss);
};
class TagDouble : public Tag {
public:
TagDouble(std::stringstream &ss);
};
class TagByteArray : public Tag {
public:
TagByteArray(std::stringstream &ss);
int getSize() const {return _size;}
char* getData() const {return _data;}
private:
int _size;
char* _data;
};
class TagString : public Tag {
public:
TagString(std::stringstream &ss);
int getSize() const {return _size;}
std::string getData() const {return _data;}
private:
int _size;
std::string _data;
};
class TagList : public Tag {
public:
TagList(std::stringstream &ss);
int getTagId() const {return _tagId;}
int getSize () const {return _size; }
std::list<Tag*> getData () const {return _data; }
private:
int _tagId;
int _size;
std::list<Tag*> _data;
};
class TagCompound : public Tag {
public:
TagCompound(std::stringstream &ss);
int getSize () const {return _size; }
std::list<Tag*> getData () const {return _data; }
int getWidth () const {return _width; }
int getLength () const {return _length; }
int getHeight () const {return _height; }
char* getBlocksId () const {return _blocksId; }
char* getBlocksData() const {return _blocksData;}
private:
int _size;
std::list<Tag*> _data;
// Specific to schematics file
int _width;
int _length;
int _height;
char* _blocksData;
char* _blocksId;
};
class TagIntArray : public Tag {
public:
TagIntArray(std::stringstream &ss);
~TagIntArray() {delete _data;}
int getSize() const {return _size;}
int* getData() const {return _data;}
private:
int _size;
int* _data;
};
#endif /* defined(__hifi__Tags__) */

View file

@ -12,11 +12,15 @@
#include <glm/gtx/transform.hpp>
#include "ViewFrustum.h"
#include "VoxelConstants.h"
#include "SharedUtil.h"
#include "Log.h"
#include "CoverageMap.h"
#include "GeometryUtil.h"
#include "ViewFrustum.h"
#include "VoxelConstants.h"
using namespace std;
ViewFrustum::ViewFrustum() :
@ -262,6 +266,7 @@ ViewFrustum::location ViewFrustum::sphereInFrustum(const glm::vec3& center, floa
ViewFrustum::location ViewFrustum::boxInFrustum(const AABox& box) const {
ViewFrustum::location regularResult = INSIDE;
ViewFrustum::location keyholeResult = OUTSIDE;
@ -274,11 +279,11 @@ ViewFrustum::location ViewFrustum::boxInFrustum(const AABox& box) const {
}
for(int i=0; i < 6; i++) {
glm::vec3 normal = _planes[i].getNormal();
glm::vec3 boxVertexP = box.getVertexP(normal);
const glm::vec3& normal = _planes[i].getNormal();
const glm::vec3& boxVertexP = box.getVertexP(normal);
float planeToBoxVertexPDistance = _planes[i].distance(boxVertexP);
glm::vec3 boxVertexN = box.getVertexN(normal);
const glm::vec3& boxVertexN = box.getVertexN(normal);
float planeToBoxVertexNDistance = _planes[i].distance(boxVertexN);
if (planeToBoxVertexPDistance < 0) {
@ -451,16 +456,22 @@ glm::vec2 ViewFrustum::projectPoint(glm::vec3 point, bool& pointInView) const {
const int MAX_POSSIBLE_COMBINATIONS = 43;
const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_SHADOW_VERTEX_COUNT+1] = {
const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_PROJECTED_POLYGON_VERTEX_COUNT+1] = {
// Number of vertices in shadow polygon for the visible faces, then a list of the index of each vertice from the AABox
//0
{0}, // inside
{4, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR}, // right
{4, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // left
{4, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR }, // left
{0}, // n/a
{4, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR}, // bottom
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR},//bottom, right
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR},//bottom, left
//4
{4, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR}, // bottom
//5
{6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR },//bottom, right
{6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, },//bottom, left
{0}, // n/a
//8
{4, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // top
{6, TOP_RIGHT_NEAR, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // top, right
{6, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR}, // top, left
@ -469,32 +480,52 @@ const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_SHADOW_VERTEX_COUNT+1]
{0}, // n/a
{0}, // n/a
{0}, // n/a
{4, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front or near
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front, right
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR}, // front, left
//16
{4, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }, // front or near
{6, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }, // front, right
{6, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, }, // front, left
{0}, // n/a
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR}, // front,bottom
{6, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR}, //front,bottom,right
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR}, //front,bottom,left
//20
{6, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }, // front,bottom
//21
{6, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }, //front,bottom,right
//22
{6, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR }, //front,bottom,left
{0}, // n/a
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front, top
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front, top, right
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR}, // front, top, left
{6, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // front, top
{6, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR }, // front, top, right
{6, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR }, // front, top, left
{0}, // n/a
{0}, // n/a
{0}, // n/a
{0}, // n/a
{0}, // n/a
{4, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR}, // back
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR}, // back, right
{6, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR}, // back, left
//32
{4, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_RIGHT_FAR }, // back
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR}, // back, right
//34
{6, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, TOP_RIGHT_FAR }, // back, left
{0}, // n/a
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR}, // back, bottom
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR},//back, bottom, right
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR},//back, bottom, left
//36
{6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR}, // back, bottom
{6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR},//back, bottom, right
// 38
{6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR },//back, bottom, left
{0}, // n/a
{6, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR}, // back, top
// 40
{6, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR}, // back, top
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR}, // back, top, right
//42
{6, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR}, // back, top, left
};
@ -508,9 +539,11 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const {
+ ((_position.z < bottomNearRight.z) << 4) // 16 = front/near | the 6 defining
+ ((_position.z > topFarLeft.z ) << 5); // 32 = back/far | planes
int vertexCount = hullVertexLookup[lookUp][0]; //look up number of vertices
//printLog(">>>>>>>>> ViewFrustum::getProjectedPolygon() lookup=%d\n",lookUp);
VoxelProjectedPolygon shadow(vertexCount);
int vertexCount = hullVertexLookup[lookUp][0]; //look up number of vertices
VoxelProjectedPolygon projectedPolygon(vertexCount);
bool pointInView = true;
bool allPointsInView = false; // assume the best, but wait till we know we have a vertex
@ -523,13 +556,40 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const {
glm::vec2 projectedPoint = projectPoint(point, pointInView);
allPointsInView = allPointsInView && pointInView;
anyPointsInView = anyPointsInView || pointInView;
shadow.setVertex(i, projectedPoint);
projectedPolygon.setVertex(i, projectedPoint);
}
/***
// Now that we've got the polygon, if it extends beyond the clipping window, then let's clip it
// NOTE: This clipping does not improve our overall performance. It basically causes more polygons to
// end up in the same quad/half and so the polygon lists get longer, and that's more calls to polygon.occludes()
if ( (projectedPolygon.getMaxX() > PolygonClip::RIGHT_OF_CLIPPING_WINDOW ) ||
(projectedPolygon.getMaxY() > PolygonClip::TOP_OF_CLIPPING_WINDOW ) ||
(projectedPolygon.getMaxX() < PolygonClip::LEFT_OF_CLIPPING_WINDOW ) ||
(projectedPolygon.getMaxY() < PolygonClip::BOTTOM_OF_CLIPPING_WINDOW) ) {
CoverageRegion::_clippedPolygons++;
glm::vec2* clippedVertices;
int clippedVertexCount;
PolygonClip::clipToScreen(projectedPolygon.getVertices(), vertexCount, clippedVertices, clippedVertexCount);
// Now reset the vertices of our projectedPolygon object
projectedPolygon.setVertexCount(clippedVertexCount);
for(int i = 0; i < clippedVertexCount; i++) {
projectedPolygon.setVertex(i, clippedVertices[i]);
}
delete[] clippedVertices;
lookUp += PROJECTION_CLIPPED;
}
***/
}
// set the distance from our camera position, to the closest vertex
float distance = glm::distance(getPosition(), box.getCenter());
shadow.setDistance(distance);
shadow.setAnyInView(anyPointsInView);
shadow.setAllInView(allPointsInView);
return shadow;
projectedPolygon.setDistance(distance);
projectedPolygon.setAnyInView(anyPointsInView);
projectedPolygon.setAllInView(allPointsInView);
projectedPolygon.setProjectionType(lookUp); // remember the projection type
return projectedPolygon;
}

View file

@ -17,7 +17,7 @@
#include "AABox.h"
#include "VoxelProjectedPolygon.h"
const float DEFAULT_KEYHOLE_RADIUS = 2.0f;
const float DEFAULT_KEYHOLE_RADIUS = 3.0f;
class ViewFrustum {
public:

View file

@ -34,7 +34,7 @@ const int COLOR_VALUES_PER_VOXEL = NUMBER_OF_COLORS * VERTICES_PER_VOXEL;
typedef unsigned long int glBufferIndex;
const glBufferIndex GLBUFFER_INDEX_UNKNOWN = ULONG_MAX;
const double SIXTY_FPS_IN_MILLISECONDS = 1000.0/60;
const double VIEW_CULLING_RATE_IN_MILLISECONDS = 1000.0; // once a second is fine
const float SIXTY_FPS_IN_MILLISECONDS = 1000.0f / 60.0f;
const float VIEW_CULLING_RATE_IN_MILLISECONDS = 1000.0f; // once a second is fine
#endif

View file

@ -29,7 +29,7 @@ private:
#endif
glBufferIndex _glBufferIndex;
bool _isDirty;
long long _lastChanged;
uint64_t _lastChanged;
bool _shouldRender;
bool _isStagedForDeletion;
AABox _box;
@ -62,7 +62,7 @@ public:
const glm::vec3& getCenter() const { return _box.getCenter(); };
const glm::vec3& getCorner() const { return _box.getCorner(); };
float getScale() const { return _box.getSize().x; /* voxelScale = (1 / powf(2, *node->getOctalCode())); */ };
int getLevel() const { return *_octalCode + 1; /* one based or zero based? */ };
int getLevel() const { return *_octalCode + 1; /* one based or zero based? this doesn't correctly handle 2 byte case */ };
float getEnclosingRadius() const;
@ -80,7 +80,7 @@ public:
void printDebugDetails(const char* label) const;
bool isDirty() const { return _isDirty; };
void clearDirtyBit() { _isDirty = false; };
bool hasChangedSince(long long time) const { return (_lastChanged > time); };
bool hasChangedSince(uint64_t time) const { return (_lastChanged > time); };
void markWithChangedTime() { _lastChanged = usecTimestampNow(); };
void handleSubtreeChanged(VoxelTree* myTree);

File diff suppressed because it is too large Load diff

View file

@ -10,29 +10,58 @@
#include <glm/glm.hpp>
const int MAX_SHADOW_VERTEX_COUNT = 6;
typedef glm::vec2 ShadowVertices[MAX_SHADOW_VERTEX_COUNT];
// there's a max of 6 vertices of a project polygon, and a max of twice that when clipped to the screen
const int MAX_PROJECTED_POLYGON_VERTEX_COUNT = 6;
const int MAX_CLIPPED_PROJECTED_POLYGON_VERTEX_COUNT = MAX_PROJECTED_POLYGON_VERTEX_COUNT * 2;
typedef glm::vec2 ProjectedVertices[MAX_CLIPPED_PROJECTED_POLYGON_VERTEX_COUNT];
class BoundingBox {
public:
BoundingBox(glm::vec2 corner, glm::vec2 size) : corner(corner), size(size) {};
enum { BOTTOM_LEFT, BOTTOM_RIGHT, TOP_RIGHT, TOP_LEFT, VERTEX_COUNT };
BoundingBox(glm::vec2 corner, glm::vec2 size) : corner(corner), size(size), _set(true) {};
BoundingBox() : _set(false) {};
glm::vec2 corner;
glm::vec2 size;
bool contains(const BoundingBox& box) const;
bool contains(const glm::vec2& point) const;
bool pointInside(const glm::vec2& point) const { return contains(point); };
void explandToInclude(const BoundingBox& box);
float area() const { return size.x * size.y; };
int getVertexCount() const { return VERTEX_COUNT; };
glm::vec2 getVertex(int vertexNumber) const;
BoundingBox topHalf() const;
BoundingBox bottomHalf() const;
BoundingBox leftHalf() const;
BoundingBox rightHalf() const;
float getMaxX() const { return corner.x + size.x; }
float getMaxY() const { return corner.y + size.y; }
float getMinX() const { return corner.x; }
float getMinY() const { return corner.y; }
void printDebugDetails(const char* label=NULL) const;
private:
bool _set;
};
const int PROJECTION_RIGHT = 1;
const int PROJECTION_LEFT = 2;
const int PROJECTION_BOTTOM = 4;
const int PROJECTION_TOP = 8;
const int PROJECTION_NEAR = 16;
const int PROJECTION_FAR = 32;
const int PROJECTION_CLIPPED = 64;
class VoxelProjectedPolygon {
public:
VoxelProjectedPolygon(const BoundingBox& box);
VoxelProjectedPolygon(int vertexCount = 0) :
_vertexCount(vertexCount),
_maxX(-FLT_MAX), _maxY(-FLT_MAX), _minX(FLT_MAX), _minY(FLT_MAX),
@ -40,22 +69,33 @@ public:
{ };
~VoxelProjectedPolygon() { };
const ShadowVertices& getVerices() const { return _vertices; };
const ProjectedVertices& getVertices() const { return _vertices; };
const glm::vec2& getVertex(int i) const { return _vertices[i]; };
void setVertex(int vertex, const glm::vec2& point);
int getVertexCount() const { return _vertexCount; };
void setVertexCount(int vertexCount) { _vertexCount = vertexCount; };
float getDistance() const { return _distance; }
void setDistance(float distance) { _distance = distance; }
int getVertexCount() const { return _vertexCount; };
void setVertexCount(int vertexCount) { _vertexCount = vertexCount; };
float getDistance() const { return _distance; }
void setDistance(float distance) { _distance = distance; }
bool getAnyInView() const { return _anyInView; };
void setAnyInView(bool anyInView) { _anyInView = anyInView; };
bool getAllInView() const { return _allInView; };
void setAllInView(bool allInView) { _allInView = allInView; };
void setProjectionType(unsigned char type) { _projectionType = type; };
unsigned char getProjectionType() const { return _projectionType; };
bool getAnyInView() const { return _anyInView; };
void setAnyInView(bool anyInView) { _anyInView = anyInView; };
bool getAllInView() const { return _allInView; };
void setAllInView(bool allInView) { _allInView = allInView; };
bool pointInside(const glm::vec2& point, bool* matchesVertex = NULL) const;
bool occludes(const VoxelProjectedPolygon& occludee, bool checkAllInView = false) const;
bool pointInside(const glm::vec2& point) const;
bool occludes(const BoundingBox& occludee) const;
bool intersects(const VoxelProjectedPolygon& testee) const;
bool intersects(const BoundingBox& box) const;
bool matches(const VoxelProjectedPolygon& testee) const;
bool matches(const BoundingBox& testee) const;
bool intersectsOnAxes(const VoxelProjectedPolygon& testee) const;
bool canMerge(const VoxelProjectedPolygon& that) const;
void merge(const VoxelProjectedPolygon& that); // replaces vertices of this with new merged version
float getMaxX() const { return _maxX; }
float getMaxY() const { return _maxY; }
@ -67,10 +107,14 @@ public:
};
void printDebugDetails() const;
static long pointInside_calls;
static long occludes_calls;
static long intersects_calls;
private:
int _vertexCount;
ShadowVertices _vertices;
ProjectedVertices _vertices;
float _maxX;
float _maxY;
float _minX;
@ -78,6 +122,7 @@ private:
float _distance;
bool _anyInView; // if any points are in view
bool _allInView; // if all points are in view
unsigned char _projectionType;
};

File diff suppressed because it is too large Load diff

View file

@ -14,6 +14,7 @@
#include "VoxelNode.h"
#include "VoxelNodeBag.h"
#include "CoverageMap.h"
#include "PointerStack.h"
// Callback function, for recuseTreeWithOperation
typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, void* extraData);
@ -36,6 +37,7 @@ typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
class EncodeBitstreamParams {
public:
int maxEncodeLevel;
int maxLevelReached;
const ViewFrustum* viewFrustum;
bool includeColor;
bool includeExistsBits;
@ -43,11 +45,13 @@ public:
bool deltaViewFrustum;
const ViewFrustum* lastViewFrustum;
bool wantOcclusionCulling;
long childWasInViewDiscarded;
CoverageMap* map;
EncodeBitstreamParams(
int maxEncodeLevel = INT_MAX,
const ViewFrustum* viewFrustum = IGNORE_VIEW_FRUSTUM,
const ViewFrustum* viewFrustum = IGNORE_VIEW_FRUSTUM,
bool includeColor = WANT_COLOR,
bool includeExistsBits = WANT_EXISTS_BITS,
int chopLevels = 0,
@ -55,8 +59,8 @@ public:
const ViewFrustum* lastViewFrustum = IGNORE_VIEW_FRUSTUM,
bool wantOcclusionCulling= NO_OCCLUSION_CULLING,
CoverageMap* map = IGNORE_COVERAGE_MAP) :
maxEncodeLevel (maxEncodeLevel),
maxLevelReached (0),
viewFrustum (viewFrustum),
includeColor (includeColor),
includeExistsBits (includeExistsBits),
@ -64,6 +68,7 @@ public:
deltaViewFrustum (deltaViewFrustum),
lastViewFrustum (lastViewFrustum),
wantOcclusionCulling(wantOcclusionCulling),
childWasInViewDiscarded(0),
map (map)
{}
};
@ -135,6 +140,8 @@ public:
bool readFromSVOFile(const char* filename);
// reads voxels from square image with alpha as a Y-axis
bool readFromSquareARGB32Pixels(const uint32_t* pixels, int dimension);
bool readFromSchematicFile(const char* filename);
void computeBlockColor(int id, int data, int& r, int& g, int& b, int& create);
unsigned long getVoxelCount();
@ -146,6 +153,11 @@ public:
void recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData);
void recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseVoxelTreeOperation operation,
const glm::vec3& point, void* extraData);
void recurseTreeWithOperationDistanceSortedTimed(PointerStack* stackOfNodes, long allowedTime,
RecurseVoxelTreeOperation operation,
const glm::vec3& point, void* extraData);
private:
void deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraData);
@ -170,6 +182,7 @@ private:
bool _shouldReaverage;
};
int boundaryDistanceForRenderLevel(unsigned int renderLevel);
float boundaryDistanceForRenderLevel(unsigned int renderLevel);
float boundaryDistanceSquaredForRenderLevel(unsigned int renderLevel);
#endif /* defined(__hifi__VoxelTree__) */

View file

@ -50,9 +50,8 @@ public:
bool getViewSent() const { return _viewSent; };
void setViewSent(bool viewSent) { _viewSent = viewSent; }
long long getLastTimeBagEmpty() const { return _lastTimeBagEmpty; };
void setLastTimeBagEmpty(long long lastTimeBagEmpty) { _lastTimeBagEmpty = lastTimeBagEmpty; };
uint64_t getLastTimeBagEmpty() const { return _lastTimeBagEmpty; };
void setLastTimeBagEmpty(uint64_t lastTimeBagEmpty) { _lastTimeBagEmpty = lastTimeBagEmpty; };
private:
VoxelNodeData(const VoxelNodeData &);
@ -67,7 +66,7 @@ private:
int _maxLevelReachedInLastSearch;
ViewFrustum _currentViewFrustum;
ViewFrustum _lastKnownViewFrustum;
long long _lastTimeBagEmpty;
uint64_t _lastTimeBagEmpty;
};

View file

@ -32,7 +32,7 @@
const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo";
const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.svo";
const long long VOXEL_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
const int VOXEL_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
const int VOXEL_LISTEN_PORT = 40106;
@ -110,134 +110,6 @@ void eraseVoxelTreeAndCleanupNodeVisitData() {
}
}
// Version of voxel distributor that sends each LOD level at a time
void resInVoxelDistributor(NodeList* nodeList,
NodeList::iterator& node,
VoxelNodeData* nodeData) {
ViewFrustum viewFrustum = nodeData->getCurrentViewFrustum();
bool searchReset = false;
int searchLoops = 0;
int searchLevelWas = nodeData->getMaxSearchLevel();
long long start = usecTimestampNow();
while (!searchReset && nodeData->nodeBag.isEmpty()) {
searchLoops++;
searchLevelWas = nodeData->getMaxSearchLevel();
int maxLevelReached = serverTree.searchForColoredNodes(nodeData->getMaxSearchLevel(), serverTree.rootNode,
viewFrustum, nodeData->nodeBag);
nodeData->setMaxLevelReached(maxLevelReached);
// If nothing got added, then we bump our levels.
if (nodeData->nodeBag.isEmpty()) {
if (nodeData->getMaxLevelReached() < nodeData->getMaxSearchLevel()) {
nodeData->resetMaxSearchLevel();
searchReset = true;
} else {
nodeData->incrementMaxSearchLevel();
}
}
}
long long end = usecTimestampNow();
int elapsedmsec = (end - start)/1000;
if (elapsedmsec > 100) {
if (elapsedmsec > 1000) {
int elapsedsec = (end - start)/1000000;
printf("WARNING! searchForColoredNodes() took %d seconds to identify %d nodes at level %d in %d loops\n",
elapsedsec, nodeData->nodeBag.count(), searchLevelWas, searchLoops);
} else {
printf("WARNING! searchForColoredNodes() took %d milliseconds to identify %d nodes at level %d in %d loops\n",
elapsedmsec, nodeData->nodeBag.count(), searchLevelWas, searchLoops);
}
} else if (::debugVoxelSending) {
printf("searchForColoredNodes() took %d milliseconds to identify %d nodes at level %d in %d loops\n",
elapsedmsec, nodeData->nodeBag.count(), searchLevelWas, searchLoops);
}
// If we have something in our nodeBag, then turn them into packets and send them out...
if (!nodeData->nodeBag.isEmpty()) {
static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static
int bytesWritten = 0;
int packetsSentThisInterval = 0;
int truePacketsSent = 0;
int trueBytesSent = 0;
long long start = usecTimestampNow();
bool shouldSendEnvironments = shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS);
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) {
if (!nodeData->nodeBag.isEmpty()) {
VoxelNode* subTree = nodeData->nodeBag.extract();
EncodeBitstreamParams params(nodeData->getMaxSearchLevel(), &viewFrustum,
nodeData->getWantColor(), WANT_EXISTS_BITS);
bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
nodeData->nodeBag, params);
if (nodeData->getAvailable() >= bytesWritten) {
nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
} else {
nodeList->getNodeSocket()->send(node->getActiveSocket(),
nodeData->getPacket(), nodeData->getPacketLength());
trueBytesSent += nodeData->getPacketLength();
truePacketsSent++;
packetsSentThisInterval++;
nodeData->resetVoxelPacket();
nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
}
} else {
if (nodeData->isPacketWaiting()) {
nodeList->getNodeSocket()->send(node->getActiveSocket(),
nodeData->getPacket(), nodeData->getPacketLength());
trueBytesSent += nodeData->getPacketLength();
truePacketsSent++;
nodeData->resetVoxelPacket();
}
packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left
}
}
// send the environment packets
if (shouldSendEnvironments) {
int envPacketLength = 1;
int numBytesPacketHeader = populateTypeAndVersion(tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA);
for (int i = 0; i < sizeof(environmentData) / numBytesPacketHeader; i++) {
envPacketLength += environmentData[i].getBroadcastData(tempOutputBuffer + envPacketLength);
}
nodeList->getNodeSocket()->send(node->getActiveSocket(), tempOutputBuffer, envPacketLength);
trueBytesSent += envPacketLength;
truePacketsSent++;
}
long long end = usecTimestampNow();
int elapsedmsec = (end - start)/1000;
if (elapsedmsec > 100) {
if (elapsedmsec > 1000) {
int elapsedsec = (end - start)/1000000;
printf("WARNING! packetLoop() took %d seconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n",
elapsedsec, trueBytesSent, truePacketsSent, searchLevelWas, nodeData->nodeBag.count());
} else {
printf("WARNING! packetLoop() took %d milliseconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n",
elapsedmsec, trueBytesSent, truePacketsSent, searchLevelWas, nodeData->nodeBag.count());
}
} else if (::debugVoxelSending) {
printf("packetLoop() took %d milliseconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n",
elapsedmsec, trueBytesSent, truePacketsSent, searchLevelWas, nodeData->nodeBag.count());
}
// if during this last pass, we emptied our bag, then we want to move to the next level.
if (nodeData->nodeBag.isEmpty()) {
if (nodeData->getMaxLevelReached() < nodeData->getMaxSearchLevel()) {
nodeData->resetMaxSearchLevel();
} else {
nodeData->incrementMaxSearchLevel();
}
}
}
}
pthread_mutex_t treeLock;
// Version of voxel distributor that sends the deepest LOD level at once
@ -250,10 +122,10 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
pthread_mutex_lock(&::treeLock);
int maxLevelReached = 0;
long long start = usecTimestampNow();
uint64_t start = usecTimestampNow();
// FOR NOW... node tells us if it wants to receive only view frustum deltas
bool wantDelta = nodeData->getWantDelta();
bool wantDelta = viewFrustumChanged && nodeData->getWantDelta();
const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL;
if (::debugVoxelSending) {
@ -269,10 +141,9 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
if (::debugVoxelSending) {
printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n",
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()));
long long now = usecTimestampNow();
uint64_t now = usecTimestampNow();
if (nodeData->getLastTimeBagEmpty() > 0) {
float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f;
if (viewFrustumChanged) {
printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend);
} else {
@ -306,9 +177,8 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
} else {
nodeData->nodeBag.insert(serverTree.rootNode);
}
}
long long end = usecTimestampNow();
uint64_t end = usecTimestampNow();
int elapsedmsec = (end - start)/1000;
if (elapsedmsec > 100) {
if (elapsedmsec > 1000) {
@ -331,12 +201,12 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
int packetsSentThisInterval = 0;
int truePacketsSent = 0;
int trueBytesSent = 0;
long long start = usecTimestampNow();
uint64_t start = usecTimestampNow();
bool shouldSendEnvironments = shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS);
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) {
// Check to see if we're taking too long, and if so bail early...
long long now = usecTimestampNow();
uint64_t now = usecTimestampNow();
long elapsedUsec = (now - start);
long elapsedUsecPerPacket = (truePacketsSent == 0) ? 0 : (elapsedUsec / truePacketsSent);
long usecRemaining = (VOXEL_SEND_INTERVAL_USECS - elapsedUsec);
@ -352,7 +222,6 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
if (!nodeData->nodeBag.isEmpty()) {
VoxelNode* subTree = nodeData->nodeBag.extract();
bool wantOcclusionCulling = nodeData->getWantOcclusionCulling();
CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP;
@ -362,12 +231,16 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
nodeData->nodeBag, params);
if (::debugVoxelSending && wantDelta) {
printf("encodeTreeBitstream() childWasInViewDiscarded=%ld\n", params.childWasInViewDiscarded);
}
if (nodeData->getAvailable() >= bytesWritten) {
nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
} else {
nodeList->getNodeSocket()->send(node->getActiveSocket(),
nodeData->getPacket(), nodeData->getPacketLength());
nodeData->getPacket(), nodeData->getPacketLength());
trueBytesSent += nodeData->getPacketLength();
truePacketsSent++;
packetsSentThisInterval++;
@ -377,7 +250,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
} else {
if (nodeData->isPacketWaiting()) {
nodeList->getNodeSocket()->send(node->getActiveSocket(),
nodeData->getPacket(), nodeData->getPacketLength());
nodeData->getPacket(), nodeData->getPacketLength());
trueBytesSent += nodeData->getPacketLength();
truePacketsSent++;
nodeData->resetVoxelPacket();
@ -401,7 +274,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
truePacketsSent++;
}
long long end = usecTimestampNow();
uint64_t end = usecTimestampNow();
int elapsedmsec = (end - start)/1000;
if (elapsedmsec > 100) {
if (elapsedmsec > 1000) {
@ -422,6 +295,9 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
if (nodeData->nodeBag.isEmpty()) {
nodeData->updateLastKnownViewFrustum();
nodeData->setViewSent(true);
if (::debugVoxelSending) {
nodeData->map.printStats();
}
nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes
}
@ -430,10 +306,10 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
pthread_mutex_unlock(&::treeLock);
}
long long lastPersistVoxels = 0;
uint64_t lastPersistVoxels = 0;
void persistVoxelsWhenDirty() {
long long now = usecTimestampNow();
long long sinceLastTime = (now - ::lastPersistVoxels) / 1000;
uint64_t now = usecTimestampNow();
int sinceLastTime = (now - ::lastPersistVoxels) / 1000;
// check the dirty bit and persist here...
if (::wantVoxelPersist && ::serverTree.isDirty() && sinceLastTime > VOXEL_PERSIST_INTERVAL) {
@ -468,17 +344,12 @@ void *distributeVoxelsToListeners(void *args) {
if (::debugVoxelSending) {
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
}
if (nodeData->getWantResIn()) {
resInVoxelDistributor(nodeList, node, nodeData);
} else {
deepestLevelVoxelDistributor(nodeList, node, nodeData, viewFrustumChanged);
}
deepestLevelVoxelDistributor(nodeList, node, nodeData, viewFrustumChanged);
}
}
// dynamically sleep until we need to fire off the next set of voxels
long long usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime));
int usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime));
if (usecToSleep > 0) {
usleep(usecToSleep);
@ -766,3 +637,5 @@ int main(int argc, const char * argv[]) {
return 0;
}