diff --git a/animation-server/src/main.cpp b/animation-server/src/main.cpp index a26b1a4674..32a6393525 100644 --- a/animation-server/src/main.cpp +++ b/animation-server/src/main.cpp @@ -33,6 +33,7 @@ bool includeBillboard = true; bool includeBorderTracer = true; bool includeMovingBug = true; bool includeBlinkingVoxel = false; +bool includeDanceFloor = true; const int ANIMATION_LISTEN_PORT = 40107; @@ -392,6 +393,131 @@ static void sendBlinkingStringOfLights() { } } +bool danceFloorInitialized = false; +const float DANCE_FLOOR_LIGHT_SIZE = 1.0f / TREE_SCALE; // approximately 1 meter +const int DANCE_FLOOR_LENGTH = 10; +const int DANCE_FLOOR_WIDTH = 10; +glm::vec3 danceFloorPosition(100.0f / TREE_SCALE, 30.0f / TREE_SCALE, 10.0f / TREE_SCALE); +glm::vec3 danceFloorLights[DANCE_FLOOR_LENGTH][DANCE_FLOOR_WIDTH]; +unsigned char danceFloorOffColor[3] = { 240, 240, 240 }; +const int DANCE_FLOOR_COLORS = 6; + +unsigned char danceFloorOnColorA[DANCE_FLOOR_COLORS][3] = { + { 255, 0, 0 }, { 0, 255, 0 }, { 0, 0, 255 }, + { 0, 191, 255 }, { 0, 250, 154 }, { 255, 69, 0 }, +}; +unsigned char danceFloorOnColorB[DANCE_FLOOR_COLORS][3] = { + { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } , + { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } +}; +float danceFloorGradient = 0.5f; +const float BEATS_PER_MINUTE = 118.0f; +const float SECONDS_PER_MINUTE = 60.0f; +const float FRAMES_PER_BEAT = (SECONDS_PER_MINUTE * ACTUAL_FPS) / BEATS_PER_MINUTE; +float danceFloorGradientIncrement = 1.0f / FRAMES_PER_BEAT; +const float DANCE_FLOOR_MAX_GRADIENT = 1.0f; +const float DANCE_FLOOR_MIN_GRADIENT = 0.0f; +const int DANCE_FLOOR_VOXELS_PER_PACKET = 100; +const int PACKETS_PER_DANCE_FLOOR = DANCE_FLOOR_VOXELS_PER_PACKET / (DANCE_FLOOR_WIDTH * DANCE_FLOOR_LENGTH); +int danceFloorColors[DANCE_FLOOR_WIDTH][DANCE_FLOOR_LENGTH]; + +void sendDanceFloor() { + PACKET_HEADER message = PACKET_HEADER_SET_VOXEL_DESTRUCTIVE; // we're a bully! + float lightScale = DANCE_FLOOR_LIGHT_SIZE; + static VoxelDetail details[DANCE_FLOOR_VOXELS_PER_PACKET]; + unsigned char* bufferOut; + int sizeOut; + + // first initialized the billboard of lights if needed... + if (!::danceFloorInitialized) { + for (int i = 0; i < DANCE_FLOOR_WIDTH; i++) { + for (int j = 0; j < DANCE_FLOOR_LENGTH; j++) { + + int randomColorIndex = randIntInRange( -(DANCE_FLOOR_COLORS), (DANCE_FLOOR_COLORS + 1)); + ::danceFloorColors[i][j] = randomColorIndex; + ::danceFloorLights[i][j] = ::danceFloorPosition + + glm::vec3(i * DANCE_FLOOR_LIGHT_SIZE, 0, j * DANCE_FLOOR_LIGHT_SIZE); + } + } + ::danceFloorInitialized = true; + } + + ::danceFloorGradient += ::danceFloorGradientIncrement; + + if (::danceFloorGradient >= DANCE_FLOOR_MAX_GRADIENT) { + ::danceFloorGradient = DANCE_FLOOR_MAX_GRADIENT; + ::danceFloorGradientIncrement = -::danceFloorGradientIncrement; + } + if (::danceFloorGradient <= DANCE_FLOOR_MIN_GRADIENT) { + ::danceFloorGradient = DANCE_FLOOR_MIN_GRADIENT; + ::danceFloorGradientIncrement = -::danceFloorGradientIncrement; + } + + for (int i = 0; i < DANCE_FLOOR_LENGTH; i++) { + for (int j = 0; j < DANCE_FLOOR_WIDTH; j++) { + + int nthVoxel = ((i * DANCE_FLOOR_WIDTH) + j); + int item = nthVoxel % DANCE_FLOOR_VOXELS_PER_PACKET; + + ::danceFloorLights[i][j] = ::danceFloorPosition + + glm::vec3(i * DANCE_FLOOR_LIGHT_SIZE, 0, j * DANCE_FLOOR_LIGHT_SIZE); + + details[item].s = lightScale; + details[item].x = ::danceFloorLights[i][j].x; + details[item].y = ::danceFloorLights[i][j].y; + details[item].z = ::danceFloorLights[i][j].z; + + if (danceFloorColors[i][j] > 0) { + int color = danceFloorColors[i][j] - 1; + details[item].red = (::danceFloorOnColorA[color][0] + + ((::danceFloorOnColorB[color][0] - ::danceFloorOnColorA[color][0]) + * ::danceFloorGradient)); + details[item].green = (::danceFloorOnColorA[color][1] + + ((::danceFloorOnColorB[color][1] - ::danceFloorOnColorA[color][1]) + * ::danceFloorGradient)); + details[item].blue = (::danceFloorOnColorA[color][2] + + ((::danceFloorOnColorB[color][2] - ::danceFloorOnColorA[color][2]) + * ::danceFloorGradient)); + } else if (::danceFloorColors[i][j] < 0) { + int color = -(::danceFloorColors[i][j] + 1); + details[item].red = (::danceFloorOnColorB[color][0] + + ((::danceFloorOnColorA[color][0] - ::danceFloorOnColorB[color][0]) + * ::danceFloorGradient)); + details[item].green = (::danceFloorOnColorB[color][1] + + ((::danceFloorOnColorA[color][1] - ::danceFloorOnColorB[color][1]) + * ::danceFloorGradient)); + details[item].blue = (::danceFloorOnColorB[color][2] + + ((::danceFloorOnColorA[color][2] - ::danceFloorOnColorB[color][2]) + * ::danceFloorGradient)); + } else { + int color = 0; + details[item].red = (::danceFloorOnColorB[color][0] + + ((::danceFloorOnColorA[color][0] - ::danceFloorOnColorB[color][0]) + * ::danceFloorGradient)); + details[item].green = (::danceFloorOnColorB[color][1] + + ((::danceFloorOnColorA[color][1] - ::danceFloorOnColorB[color][1]) + * ::danceFloorGradient)); + details[item].blue = (::danceFloorOnColorB[color][2] + + ((::danceFloorOnColorA[color][2] - ::danceFloorOnColorB[color][2]) + * ::danceFloorGradient)); + } + + if (item == DANCE_FLOOR_VOXELS_PER_PACKET - 1) { + if (createVoxelEditMessage(message, 0, DANCE_FLOOR_VOXELS_PER_PACKET, + (VoxelDetail*)&details, bufferOut, sizeOut)){ + ::packetsSent++; + ::bytesSent += sizeOut; + if (::shouldShowPacketsPerSecond) { + printf("sending packet of size=%d\n", sizeOut); + } + AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL, 1); + delete[] bufferOut; + } + } + } + } +} + bool billboardInitialized = false; const int BILLBOARD_HEIGHT = 9; const int BILLBOARD_WIDTH = 45; @@ -405,7 +531,7 @@ float billboardGradientIncrement = 0.01f; const float BILLBOARD_MAX_GRADIENT = 1.0f; const float BILLBOARD_MIN_GRADIENT = 0.0f; const float BILLBOARD_LIGHT_SIZE = 0.125f / TREE_SCALE; // approximately 1/8 meter per light -const int VOXELS_PER_PACKET = 135; +const int VOXELS_PER_PACKET = 100; const int PACKETS_PER_BILLBOARD = VOXELS_PER_PACKET / (BILLBOARD_HEIGHT * BILLBOARD_WIDTH); @@ -514,6 +640,9 @@ void* animateVoxels(void* args) { if (::includeBlinkingVoxel) { sendVoxelBlinkMessage(); } + if (::includeDanceFloor) { + sendDanceFloor(); + } double end = usecTimestampNow(); double elapsedSeconds = (end - ::start) / 1000000.0; @@ -555,6 +684,9 @@ int main(int argc, const char * argv[]) const char* INCLUDE_BLINKING_VOXEL = "--includeBlinkingVoxel"; ::includeBlinkingVoxel = cmdOptionExists(argc, argv, INCLUDE_BLINKING_VOXEL); + const char* NO_DANCE_FLOOR = "--NoDanceFloor"; + ::includeDanceFloor = !cmdOptionExists(argc, argv, NO_DANCE_FLOOR); + // Handle Local Domain testing with the --local command line const char* showPPS = "--showPPS"; ::shouldShowPacketsPerSecond = cmdOptionExists(argc, argv, showPPS); diff --git a/audio-mixer/src/main.cpp b/audio-mixer/src/main.cpp index e4a4f83b07..982ebc526c 100644 --- a/audio-mixer/src/main.cpp +++ b/audio-mixer/src/main.cpp @@ -55,7 +55,7 @@ const float BUFFER_SEND_INTERVAL_USECS = (BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SA const long MAX_SAMPLE_VALUE = std::numeric_limits::max(); const long MIN_SAMPLE_VALUE = std::numeric_limits::min(); -const float DISTANCE_RATIO = 3.0f / 0.3f; +const float DISTANCE_SCALE = 2.5f; const float PHASE_AMPLITUDE_RATIO_AT_90 = 0.5; const int PHASE_DELAY_AT_90 = 20; @@ -166,8 +166,8 @@ int main(int argc, const char* argv[]) { powf(agentPosition.z - otherAgentPosition.z, 2)); float minCoefficient = std::min(1.0f, - powf(0.5, - (logf(DISTANCE_RATIO * distanceToAgent) / logf(2.5)) + powf(0.3, + (logf(DISTANCE_SCALE * distanceToAgent) / logf(2.5)) - 1)); distanceCoefficients[lowAgentIndex][highAgentIndex] = minCoefficient; } @@ -214,8 +214,8 @@ int main(int argc, const char* argv[]) { (OFF_AXIS_ATTENUATION_FORMULA_STEP * (fabsf(angleOfDelivery) / 90.0f)); attenuationCoefficient = distanceCoefficients[lowAgentIndex][highAgentIndex] - * otherAgentBuffer->getAttenuationRatio() - * offAxisCoefficient; + * otherAgentBuffer->getAttenuationRatio() + * offAxisCoefficient; bearingRelativeAngleToSource *= (M_PI / 180); @@ -225,15 +225,15 @@ int main(int argc, const char* argv[]) { } int16_t* goodChannel = bearingRelativeAngleToSource > 0.0f - ? clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL - : clientSamples; + ? clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL + : clientSamples; int16_t* delayedChannel = bearingRelativeAngleToSource > 0.0f - ? clientSamples - : clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL; + ? clientSamples + : clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL; int16_t* delaySamplePointer = otherAgentBuffer->getNextOutput() == otherAgentBuffer->getBuffer() - ? otherAgentBuffer->getBuffer() + RING_BUFFER_SAMPLES - numSamplesDelay - : otherAgentBuffer->getNextOutput() - numSamplesDelay; + ? otherAgentBuffer->getBuffer() + RING_BUFFER_SAMPLES - numSamplesDelay + : otherAgentBuffer->getNextOutput() - numSamplesDelay; for (int s = 0; s < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; s++) { @@ -246,7 +246,7 @@ int main(int argc, const char* argv[]) { int16_t currentSample = (otherAgentBuffer->getNextOutput()[s] * attenuationCoefficient); plateauAdditionOfSamples(goodChannel[s], currentSample); - if (s + numSamplesDelay < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { + if (s + numSamplesDelay < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { plateauAdditionOfSamples(delayedChannel[s + numSamplesDelay], currentSample * weakChannelAmplitudeRatio); } @@ -287,6 +287,11 @@ int main(int argc, const char* argv[]) { } agentList->updateAgentWithData(agentAddress, packetData, receivedBytes); + + if (std::isnan(((AudioRingBuffer *)avatarAgent->getLinkedData())->getBearing())) { + // kill off this agent - temporary solution to mixer crash on mac sleep + avatarAgent->setAlive(false); + } } else if (packetData[0] == PACKET_HEADER_INJECT_AUDIO) { Agent* matchingInjector = NULL; diff --git a/avatar-mixer/src/main.cpp b/avatar-mixer/src/main.cpp index eca698e673..caa9f609ce 100644 --- a/avatar-mixer/src/main.cpp +++ b/avatar-mixer/src/main.cpp @@ -52,9 +52,18 @@ void attachAvatarDataToAgent(Agent* newAgent) { } int main(int argc, const char* argv[]) { + AgentList* agentList = AgentList::createInstance(AGENT_TYPE_AVATAR_MIXER, AVATAR_LISTEN_PORT); setvbuf(stdout, NULL, _IOLBF, 0); + // Handle Local Domain testing with the --local command line + const char* local = "--local"; + if (cmdOptionExists(argc, argv, local)) { + printf("Local Domain MODE!\n"); + int ip = getLocalAddress(); + sprintf(DOMAIN_IP,"%d.%d.%d.%d", (ip & 0xFF), ((ip >> 8) & 0xFF),((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF)); + } + agentList->linkedDataCreateCallback = attachAvatarDataToAgent; agentList->startDomainServerCheckInThread(); @@ -84,7 +93,7 @@ int main(int argc, const char* argv[]) { // parse positional data from an agent agentList->updateAgentWithData(avatarAgent, packetData, receivedBytes); - + case PACKET_HEADER_INJECT_AUDIO: currentBufferPosition = broadcastPacket + 1; // send back a packet with other active agent data to this agent diff --git a/eve/src/main.cpp b/eve/src/main.cpp index bf32474b33..c670be050e 100644 --- a/eve/src/main.cpp +++ b/eve/src/main.cpp @@ -204,6 +204,4 @@ int main(int argc, const char* argv[]) { agentList->stopDomainServerCheckInThread(); agentList->stopPingUnknownAgentsThread(); agentList->stopSilentAgentRemovalThread(); -} - - +} \ No newline at end of file diff --git a/injector/CMakeLists.txt b/injector/CMakeLists.txt index ba3393956a..2c022b0e92 100644 --- a/injector/CMakeLists.txt +++ b/injector/CMakeLists.txt @@ -18,4 +18,5 @@ include_glm(${TARGET_NAME} ${ROOT_DIR}) # link the shared hifi library include(${MACRO_DIR}/LinkHifiLibrary.cmake) link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) -link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR}) \ No newline at end of file +link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) \ No newline at end of file diff --git a/injector/src/main.cpp b/injector/src/main.cpp index df751f50a1..32a8442d8f 100644 --- a/injector/src/main.cpp +++ b/injector/src/main.cpp @@ -14,14 +14,16 @@ #include #include +#include +#include +#include #include #include #include #include #include -char EC2_WEST_AUDIO_SERVER[] = "54.241.92.53"; -const int AUDIO_UDP_LISTEN_PORT = 55443; +const int AVATAR_MIXER_DATA_SEND_INTERVAL_MSECS = 15; const int DEFAULT_INJECTOR_VOLUME = 0xFF; @@ -30,12 +32,12 @@ bool loopAudio = true; float sleepIntervalMin = 1.00; float sleepIntervalMax = 2.00; char *sourceAudioFile = NULL; -const char *allowedParameters = ":rb::t::c::a::f:"; +const char *allowedParameters = ":rb::t::c::a::f::d:"; float floatArguments[4] = {0.0f, 0.0f, 0.0f, 0.0f}; unsigned char volume = DEFAULT_INJECTOR_VOLUME; +float triggerDistance = 0; -void usage(void) -{ +void usage(void) { std::cout << "High Fidelity - Interface audio injector" << std::endl; std::cout << " -r Random sleep mode. If not specified will default to constant loop." << std::endl; std::cout << " -b FLOAT Min. number of seconds to sleep. Only valid in random sleep mode. Default 1.0" << std::endl; @@ -43,10 +45,10 @@ void usage(void) std::cout << " -c FLOAT,FLOAT,FLOAT,FLOAT X,Y,Z,YAW position in universe where audio will be originating from and direction. Defaults to 0,0,0,0" << std::endl; std::cout << " -a 0-255 Attenuation curve modifier, defaults to 255" << std::endl; std::cout << " -f FILENAME Name of audio source file. Required - RAW format, 22050hz 16bit signed mono" << std::endl; + std::cout << " -d FLOAT Trigger distance for injection. If not specified will loop constantly" << std::endl; } -bool processParameters(int parameterCount, char* parameterData[]) -{ +bool processParameters(int parameterCount, char* parameterData[]) { int p; while ((p = getopt(parameterCount, parameterData, allowedParameters)) != -1) { switch (p) { @@ -86,6 +88,10 @@ bool processParameters(int parameterCount, char* parameterData[]) ::volume = atoi(optarg); std::cout << "[DEBUG] Attenuation modifier: " << optarg << std::endl; break; + case 'd': + ::triggerDistance = atof(optarg); + std::cout << "[DEBUG] Trigger distance: " << optarg << std::endl; + break; default: usage(); return false; @@ -94,45 +100,149 @@ bool processParameters(int parameterCount, char* parameterData[]) return true; }; -int main(int argc, char* argv[]) { +bool stopReceiveAgentDataThread; +void *receiveAgentData(void *args) { + sockaddr senderAddress; + ssize_t bytesReceived; + unsigned char incomingPacket[MAX_PACKET_SIZE]; + + AgentList* agentList = AgentList::getInstance(); + + while (!::stopReceiveAgentDataThread) { + if (agentList->getAgentSocket()->receive(&senderAddress, incomingPacket, &bytesReceived)) { + switch (incomingPacket[0]) { + case PACKET_HEADER_BULK_AVATAR_DATA: + // this is the positional data for other agents + // pass that off to the agentList processBulkAgentData method + agentList->processBulkAgentData(&senderAddress, incomingPacket, bytesReceived); + break; + default: + // have the agentList handle list of agents from DS, replies from other agents, etc. + agentList->processAgentData(&senderAddress, incomingPacket, bytesReceived); + break; + } + } + } + + pthread_exit(0); + return NULL; +} + +void createAvatarDataForAgent(Agent* agent) { + if (!agent->getLinkedData()) { + agent->setLinkedData(new AvatarData(agent)); + } +} + +int main(int argc, char* argv[]) { + // new seed for random audio sleep times srand(time(0)); + int AUDIO_UDP_SEND_PORT = 1500 + (rand() % (int)(1500 - 2000 + 1)); - UDPSocket streamSocket(AUDIO_UDP_SEND_PORT); - - sockaddr_in mixerSocket; - mixerSocket.sin_family = AF_INET; - mixerSocket.sin_addr.s_addr = inet_addr(EC2_WEST_AUDIO_SERVER); - mixerSocket.sin_port = htons((uint16_t)AUDIO_UDP_LISTEN_PORT); - - - if (processParameters(argc, argv)) { + if (processParameters(argc, argv)) { if (::sourceAudioFile == NULL) { std::cout << "[FATAL] Source audio file not specified" << std::endl; exit(-1); } else { AudioInjector injector(sourceAudioFile); + // create an AgentList instance to handle communication with other agents + AgentList* agentList = AgentList::createInstance(AGENT_TYPE_AUDIO_INJECTOR, AUDIO_UDP_SEND_PORT); + + pthread_t receiveAgentDataThread; + pthread_create(&receiveAgentDataThread, NULL, receiveAgentData, NULL); + + // start telling the domain server that we are alive + agentList->startDomainServerCheckInThread(); + + // start the agent list thread that will kill off agents when they stop talking + agentList->startSilentAgentRemovalThread(); + injector.setPosition(glm::vec3(::floatArguments[0], ::floatArguments[1], ::floatArguments[2])); injector.setBearing(*(::floatArguments + 3)); injector.setVolume(::volume); - - float delay = 0; - int usecDelay = 0; + + // register the callback for agent data creation + agentList->linkedDataCreateCallback = createAvatarDataForAgent; + + unsigned char broadcastPacket = PACKET_HEADER_INJECT_AUDIO; + + timeval thisSend; + double numMicrosecondsSleep = 0; while (true) { - injector.injectAudio(&streamSocket, (sockaddr*) &mixerSocket); - - if (!::loopAudio) { - delay = randFloatInRange(::sleepIntervalMin, ::sleepIntervalMax); - usecDelay = delay * 1000 * 1000; - usleep(usecDelay); + if (::triggerDistance) { + + // update the thisSend timeval to the current time + gettimeofday(&thisSend, NULL); + + // find the current avatar mixer + Agent* avatarMixer = agentList->soloAgentOfType(AGENT_TYPE_AVATAR_MIXER); + + // make sure we actually have an avatar mixer with an active socket + if (avatarMixer && avatarMixer->getActiveSocket() != NULL) { + // use the UDPSocket instance attached to our agent list to ask avatar mixer for a list of avatars + agentList->getAgentSocket()->send(avatarMixer->getActiveSocket(), + &broadcastPacket, + sizeof(broadcastPacket)); + } + + if (!injector.isInjectingAudio()) { + // enumerate the other agents to decide if one is close enough that we should inject + for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) { + AvatarData* avatarData = (AvatarData*) agent->getLinkedData(); + + if (avatarData) { + glm::vec3 tempVector = injector.getPosition() - avatarData->getPosition(); + float squareDistance = glm::dot(tempVector, tempVector); + + if (squareDistance <= ::triggerDistance) { + // look for an audio mixer in our agent list + Agent* audioMixer = AgentList::getInstance()->soloAgentOfType(AGENT_TYPE_AUDIO_MIXER); + + if (audioMixer) { + // we have an active audio mixer we can send data to + AudioInjectionManager::threadInjector(&injector); + } + } + } + } + } + + // sleep for the correct amount of time to have data send be consistently timed + if ((numMicrosecondsSleep = (AVATAR_MIXER_DATA_SEND_INTERVAL_MSECS * 1000) - + (usecTimestampNow() - usecTimestamp(&thisSend))) > 0) { + usleep(numMicrosecondsSleep); + } + } else { + // look for an audio mixer in our agent list + Agent* audioMixer = AgentList::getInstance()->soloAgentOfType(AGENT_TYPE_AUDIO_MIXER); + + if (audioMixer) { + injector.injectAudio(agentList->getAgentSocket(), audioMixer->getActiveSocket()); + } + + float delay = 0; + int usecDelay = 0; + + if (!::loopAudio) { + delay = randFloatInRange(::sleepIntervalMin, ::sleepIntervalMax); + usecDelay = delay * 1000 * 1000; + usleep(usecDelay); + } } } - } + + // stop the receive agent data thread + stopReceiveAgentDataThread = true; + pthread_join(receiveAgentDataThread, NULL); + + // stop the agent list's threads + agentList->stopDomainServerCheckInThread(); + agentList->stopSilentAgentRemovalThread(); + } } - return 0; } - diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7f5eeccd76..b6aba80c06 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -255,8 +255,6 @@ void Application::initializeGL() { printLog("Network receive thread created.\n"); } - _myAvatar.readAvatarDataFromFile(); - // call terminate before exiting connect(this, SIGNAL(aboutToQuit()), SLOT(terminate())); @@ -270,6 +268,8 @@ void Application::initializeGL() { connect(idleTimer, SIGNAL(timeout()), SLOT(idle())); idleTimer->start(0); + readSettings(); + if (_justStarted) { float startupTime = (usecTimestampNow() - usecTimestamp(&_applicationStartupTime))/1000000.0; _justStarted = false; @@ -289,33 +289,22 @@ void Application::paintGL() { if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _myCamera.setTightness (100.0f); _myCamera.setTargetPosition(_myAvatar.getSpringyHeadPosition()); - _myCamera.setTargetRotation(_myAvatar.getBodyYaw() - 180.0f, - 0.0f, - 0.0f); + _myCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); + } else if (OculusManager::isConnected()) { _myCamera.setUpShift (0.0f); _myCamera.setDistance (0.0f); _myCamera.setTightness (100.0f); _myCamera.setTargetPosition(_myAvatar.getHeadPosition()); - _myCamera.setTargetRotation(_myAvatar.getAbsoluteHeadYaw(), - _myAvatar.getHead().getPitch(), - -_myAvatar.getHead().getRoll()); + _myCamera.setTargetRotation(_myAvatar.getHead().getOrientation()); + } else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { _myCamera.setTargetPosition(_myAvatar.getSpringyHeadPosition()); - _myCamera.setTargetRotation(_myAvatar.getAbsoluteHeadYaw(), - _myAvatar.getAbsoluteHeadPitch(), - 0.0f); - // Take a look at whether we are inside head, don't render it if so. - const float HEAD_RENDER_DISTANCE = 0.5; - glm::vec3 distanceToHead(_myCamera.getPosition() - _myAvatar.getSpringyHeadPosition()); + _myCamera.setTargetRotation(_myAvatar.getHead().getWorldAlignedOrientation()); - if (glm::length(distanceToHead) < HEAD_RENDER_DISTANCE) { - } } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { _myCamera.setTargetPosition(_myAvatar.getHeadPosition()); - _myCamera.setTargetRotation(_myAvatar.getAbsoluteHeadYaw(), - _myAvatar.getAbsoluteHeadPitch(), - 0.0f); + _myCamera.setTargetRotation(_myAvatar.getHead().getWorldAlignedOrientation()); } // important... @@ -345,11 +334,12 @@ void Application::paintGL() { if (_viewFrustumFromOffset->isChecked() && _frustumOn->isChecked()) { // set the camera to third-person view but offset so we can see the frustum - _viewFrustumOffsetCamera.setTargetYaw(_viewFrustumOffsetYaw + _myAvatar.getBodyYaw()); - _viewFrustumOffsetCamera.setPitch (_viewFrustumOffsetPitch ); - _viewFrustumOffsetCamera.setRoll (_viewFrustumOffsetRoll ); + _viewFrustumOffsetCamera.setTargetPosition(_myCamera.getTargetPosition()); + _viewFrustumOffsetCamera.setTargetRotation(_myCamera.getTargetRotation() * glm::quat(glm::radians(glm::vec3( + _viewFrustumOffsetPitch, _viewFrustumOffsetYaw, _viewFrustumOffsetRoll)))); _viewFrustumOffsetCamera.setUpShift (_viewFrustumOffsetUp ); _viewFrustumOffsetCamera.setDistance (_viewFrustumOffsetDistance); + _viewFrustumOffsetCamera.initialize(); // force immediate snap to ideal position and orientation _viewFrustumOffsetCamera.update(1.f/_fps); whichCamera = _viewFrustumOffsetCamera; } @@ -734,7 +724,178 @@ void Application::wheelEvent(QWheelEvent* event) { decreaseVoxelSize(); } } + +const char AVATAR_DATA_FILENAME[] = "avatar.ifd"; + +void Application::readSettingsFile() { + FILE* settingsFile = fopen(AVATAR_DATA_FILENAME, "rt"); + if (settingsFile) { + char line[LINE_MAX]; + + while (fgets(line, LINE_MAX, settingsFile) != NULL) + { + if (strcmp(line, " \n") > 0) { + char* token = NULL; + char* settingLine = NULL; + char* toFree = NULL; + + settingLine = strdup(line); + + if (settingLine != NULL) { + toFree = settingLine; + + int i = 0; + + char key[128]; + char value[128]; + + + while ((token = strsep(&settingLine, "=")) != NULL) + { + switch (i) { + case 0: + strcpy(key, token); + _settingsTable[key] = ""; + break; + + case 1: + strcpy(value, token); + _settingsTable[key] = token; + break; + + default: + break; + } + + i++; + } + + free(toFree); + } + } + } + + fclose(settingsFile); + } +} + +void Application::saveSettingsFile() { + FILE* settingsFile = fopen(AVATAR_DATA_FILENAME, "wt"); + + if (settingsFile) { + for (std::map::iterator i = _settingsTable.begin(); i != _settingsTable.end(); i++) + { + fprintf(settingsFile, "\n%s=%s", i->first.data(), i->second.data()); + } + } + + fclose(settingsFile); +} + +bool Application::getSetting(const char* setting, bool& value, const bool defaultSetting) const { + std::map::const_iterator iter = _settingsTable.find(setting); + + if (iter != _settingsTable.end()) { + int readBool; + + int res = sscanf(iter->second.data(), "%d", &readBool); + + const char EXPECTED_ITEMS = 1; + + if (res == EXPECTED_ITEMS) { + if (readBool == 1) { + value = true; + } else if (readBool == 0) { + value = false; + } + } + } else { + value = defaultSetting; + return false; + } + + return true; +} + +bool Application::getSetting(const char* setting, float& value, const float defaultSetting) const { + std::map::const_iterator iter = _settingsTable.find(setting); + + if (iter != _settingsTable.end()) { + float readFloat; + + int res = sscanf(iter->second.data(), "%f", &readFloat); + + const char EXPECTED_ITEMS = 1; + + if (res == EXPECTED_ITEMS) { + if (!isnan(readFloat)) { + value = readFloat; + } else { + value = defaultSetting; + return false; + } + } else { + value = defaultSetting; + return false; + } + } else { + value = defaultSetting; + return false; + } + + return true; +} + +bool Application::getSetting(const char* setting, glm::vec3& value, const glm::vec3& defaultSetting) const { + std::map::const_iterator iter = _settingsTable.find(setting); + + if (iter != _settingsTable.end()) { + glm::vec3 readVec; + + int res = sscanf(iter->second.data(), "%f,%f,%f", &readVec.x, &readVec.y, &readVec.z); + + const char EXPECTED_ITEMS = 3; + + if (res == EXPECTED_ITEMS) { + if (!isnan(readVec.x) && !isnan(readVec.y) && !isnan(readVec.z)) { + value = readVec; + } else { + value = defaultSetting; + return false; + } + } else { + value = defaultSetting; + return false; + } + } else { + value = defaultSetting; + return false; + } + + return true; +} + +const short MAX_SETTINGS_LENGTH = 128; + +void Application::setSetting(const char* setting, const bool value) { + char settingValues[MAX_SETTINGS_LENGTH]; + sprintf(settingValues, "%d", value); + _settingsTable[setting] = settingValues; +} + +void Application::setSetting(const char* setting, const float value) { + char settingValues[MAX_SETTINGS_LENGTH]; + sprintf(settingValues, "%f", value); + _settingsTable[setting] = settingValues; +} + +void Application::setSetting(const char* setting, const glm::vec3& value) { + char settingValues[MAX_SETTINGS_LENGTH]; + sprintf(settingValues, "%f,%f,%f", value.x, value.y, value.z); + _settingsTable[setting] = settingValues; +} + // Every second, check the frame rates and other stuff void Application::timer() { gettimeofday(&_timerEnd, NULL); @@ -891,6 +1052,8 @@ void Application::idle() { _serialPort.readData(deltaTime); } + // Update transmitter + // Sample hardware, update view frustum if needed, and send avatar data to mixer/agents updateAvatar(deltaTime); @@ -957,7 +1120,7 @@ void Application::terminate() { // Close serial port // close(serial_fd); - _myAvatar.writeAvatarDataToFile(); + saveSettings(); if (_enableNetworkThread) { _stopNetworkReceiveThread = true; @@ -969,11 +1132,7 @@ void Application::pair() { PairingHandler::sendPairRequest(); } -void Application::setHead(bool head) { - #ifndef _WIN32 - _audio.setMixerLoopbackFlag(head); - #endif - +void Application::setHead(bool head) { if (head) { _myCamera.setMode(CAMERA_MODE_MIRROR); _myCamera.setModeShiftRate(100.0f); @@ -1186,13 +1345,15 @@ void Application::initMenu() { _window->setMenuBar(menuBar); QMenu* fileMenu = menuBar->addMenu("File"); - fileMenu->addAction("Quit", this, SLOT(quit()), Qt::Key_Q); + fileMenu->addAction("Quit", this, SLOT(quit()), Qt::CTRL | Qt::Key_Q); QMenu* pairMenu = menuBar->addMenu("Pair"); pairMenu->addAction("Pair", this, SLOT(pair())); QMenu* optionsMenu = menuBar->addMenu("Options"); (_lookingInMirror = optionsMenu->addAction("Mirror", this, SLOT(setHead(bool)), Qt::Key_H))->setCheckable(true); + (_echoAudioMode = optionsMenu->addAction("Echo Audio"))->setCheckable(true); + optionsMenu->addAction("Noise", this, SLOT(setNoise(bool)), Qt::Key_N)->setCheckable(true); (_gyroLook = optionsMenu->addAction("Gyro Look"))->setCheckable(true); _gyroLook->setChecked(true); @@ -1354,7 +1515,7 @@ void Application::init() { void Application::updateAvatar(float deltaTime) { // Update my avatar's head position from gyros - _myAvatar.updateHeadFromGyros(deltaTime, &_serialPort, &_gravity); + _myAvatar.updateHeadFromGyros(deltaTime, &_serialPort); // Grab latest readings from the gyros float measuredPitchRate = _serialPort.getLastPitchRate(); @@ -1449,10 +1610,6 @@ void Application::updateAvatar(float deltaTime) { void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) { // We will use these below, from either the camera or head vectors calculated above glm::vec3 position; - glm::vec3 direction; - glm::vec3 up; - glm::vec3 right; - float fov, nearClip, farClip; // Camera or Head? if (_cameraFrustum->isChecked()) { @@ -1461,15 +1618,14 @@ void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) { position = _myAvatar.getHeadPosition(); } - fov = camera.getFieldOfView(); - nearClip = camera.getNearClip(); - farClip = camera.getFarClip(); + float fov = camera.getFieldOfView(); + float nearClip = camera.getNearClip(); + float farClip = camera.getFarClip(); - Orientation o = camera.getOrientation(); - - direction = o.getFront(); - up = o.getUp(); - right = o.getRight(); + glm::quat rotation = camera.getRotation(); + glm::vec3 direction = rotation * AVATAR_FRONT; + glm::vec3 up = rotation * AVATAR_UP; + glm::vec3 right = rotation * AVATAR_RIGHT; /* printf("position.x=%f, position.y=%f, position.z=%f\n", position.x, position.y, position.z); @@ -1655,11 +1811,10 @@ void Application::displaySide(Camera& whichCamera) { // transform view according to whichCamera // could be myCamera (if in normal mode) // or could be viewFrustumOffsetCamera if in offset mode - // I changed the ordering here - roll is FIRST (JJV) - glRotatef ( whichCamera.getRoll(), IDENTITY_FRONT.x, IDENTITY_FRONT.y, IDENTITY_FRONT.z); - glRotatef ( whichCamera.getPitch(), IDENTITY_RIGHT.x, IDENTITY_RIGHT.y, IDENTITY_RIGHT.z); - glRotatef (180.0 - whichCamera.getYaw(), IDENTITY_UP.x, IDENTITY_UP.y, IDENTITY_UP.z ); + glm::quat rotation = whichCamera.getRotation(); + glm::vec3 axis = glm::axis(rotation); + glRotatef(-glm::angle(rotation), axis.x, axis.y, axis.z); glTranslatef(-whichCamera.getPosition().x, -whichCamera.getPosition().y, -whichCamera.getPosition().z); @@ -1753,13 +1908,13 @@ void Application::displaySide(Camera& whichCamera) { for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) { if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) { Avatar *avatar = (Avatar *)agent->getLinkedData(); - avatar->render(false, _myCamera.getPosition()); + avatar->render(false); } } agentList->unlock(); // Render my own Avatar - _myAvatar.render(_lookingInMirror->isChecked(), _myCamera.getPosition()); + _myAvatar.render(_lookingInMirror->isChecked()); _myAvatar.setDisplayingLookatVectors(_renderLookatOn->isChecked()); } @@ -2270,3 +2425,95 @@ void* Application::networkReceive(void* args) { return NULL; } +void Application::saveSettings() +{ + // Handle any persistent settings saving here when we get a call to terminate. + // This should probably be moved to a map stored in memory at some point to cache settings. + _myAvatar.writeAvatarDataToFile(); + + setSetting("_gyroLook", _gyroLook->isChecked()); + + setSetting("_mouseLook", _mouseLook->isChecked()); + + setSetting("_transmitterDrives", _transmitterDrives->isChecked()); + + setSetting("_renderVoxels", _renderVoxels->isChecked()); + + setSetting("_renderVoxelTextures", _renderVoxelTextures->isChecked()); + + setSetting("_renderStarsOn", _renderStarsOn->isChecked()); + + setSetting("_renderAtmosphereOn", _renderAtmosphereOn->isChecked()); + + setSetting("_renderAvatarsOn", _renderAvatarsOn->isChecked()); + + setSetting("_renderStatsOn", _renderStatsOn->isChecked()); + + setSetting("_renderFrameTimerOn", _renderFrameTimerOn->isChecked()); + + setSetting("_renderLookatOn", _renderLookatOn->isChecked()); + + setSetting("_logOn", _logOn->isChecked()); + + setSetting("_frustumOn", _frustumOn->isChecked()); + + setSetting("_viewFrustumFromOffset", _viewFrustumFromOffset->isChecked()); + + setSetting("_cameraFrustum", _cameraFrustum->isChecked()); + + saveSettingsFile(); +} + +void Application::readSettings() +{ + readSettingsFile(); + _myAvatar.readAvatarDataFromFile(); + + bool settingState; + getSetting("_gyroLook", settingState, _gyroLook->isChecked()); + _gyroLook->setChecked(settingState); + + getSetting("_mouseLook", settingState, _mouseLook->isChecked()); + _mouseLook->setChecked(settingState); + + getSetting("_transmitterDrives", settingState, _transmitterDrives->isChecked()); + _transmitterDrives->setChecked(settingState); + + getSetting("_renderVoxels", settingState, _renderVoxels->isChecked()); + _renderVoxels->setChecked(settingState); + + getSetting("_renderVoxelTextures", settingState, _renderVoxelTextures->isChecked()); + _renderVoxelTextures->setChecked(settingState); + + getSetting("_renderStarsOn", settingState, _renderStarsOn->isChecked()); + _renderStarsOn->setChecked(settingState); + + getSetting("_renderAtmosphereOn", settingState, _renderAtmosphereOn->isChecked()); + _renderAtmosphereOn->setChecked(settingState); + + getSetting("_renderAvatarsOn", settingState, _renderAvatarsOn->isChecked()); + _renderAvatarsOn->setChecked(settingState); + + getSetting("_renderStatsOn", settingState, _renderStatsOn->isChecked()); + _renderStatsOn->setChecked(settingState); + + getSetting("_renderFrameTimerOn", settingState, _renderFrameTimerOn->isChecked()); + _renderFrameTimerOn->setChecked(settingState); + + getSetting("_renderLookatOn", settingState, _renderLookatOn->isChecked()); + _renderLookatOn->setChecked(settingState); + + getSetting("_logOn", settingState, _logOn->isChecked()); + _logOn->setChecked(settingState); + + getSetting("_frustumOn", settingState, _frustumOn->isChecked()); + _frustumOn->setChecked(settingState); + + getSetting("_viewFrustumFromOffset", settingState, _viewFrustumFromOffset->isChecked()); + _viewFrustumFromOffset->setChecked(settingState); + + getSetting("_cameraFrustum", settingState, _cameraFrustum->isChecked()); + _cameraFrustum->setChecked(settingState); + +} + diff --git a/interface/src/Application.h b/interface/src/Application.h index 85d5d7ef43..9a6d02ecc0 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -11,8 +11,10 @@ #include #include +#include #include +#include #include @@ -62,8 +64,61 @@ public: void wheelEvent(QWheelEvent* event); Avatar* getAvatar() { return &_myAvatar; } + Camera* getCamera() { return &_myCamera; } VoxelSystem* getVoxels() { return &_voxels; } Environment* getEnvironment() { return &_environment; } + bool shouldEchoAudio() { return _echoAudioMode->isChecked(); } + + /*! + @fn getSettingBool + @brief A function for getting boolean settings from the settings file. + @param settingName The desired setting to get the value for. + @param boolSetting The referenced variable where the setting will be stored. + @param defaultSetting The default setting to assign to boolSetting if this function fails to find the appropriate setting. Defaults to false. + */ + bool getSetting(const char* setting, bool &value, const bool defaultSetting = false) const; + + /*! + @fn getSettingFloat + @brief A function for getting float settings from the settings file. + @param settingName The desired setting to get the value for. + @param floatSetting The referenced variable where the setting will be stored. + @param defaultSetting The default setting to assign to boolSetting if this function fails to find the appropriate setting. Defaults to 0.0f. + */ + bool getSetting(const char* setting, float &value, const float defaultSetting = 0.0f) const; + + /*! + @fn getSettingVec3 + @brief A function for getting boolean settings from the settings file. + @param settingName The desired setting to get the value for. + @param vecSetting The referenced variable where the setting will be stored. + @param defaultSetting The default setting to assign to boolSetting if this function fails to find the appropriate setting. Defaults to <0.0f, 0.0f, 0.0f> + */ + bool getSetting(const char* setting, glm::vec3 &value, const glm::vec3& defaultSetting = glm::vec3(0.0f, 0.0f, 0.0f)) const; + + /*! + @fn setSettingBool + @brief A function for setting boolean setting values when saving the settings file. + @param settingName The desired setting to populate a value for. + @param boolSetting The value to set. + */ + void setSetting(const char* setting, const bool value); + + /*! + @fn setSettingFloat + @brief A function for setting boolean setting values when saving the settings file. + @param settingName The desired setting to populate a value for. + @param floatSetting The value to set. + */ + void setSetting(const char* setting, const float value); + + /*! + @fn setSettingVec3 + @brief A function for setting boolean setting values when saving the settings file. + @param settingName The desired setting to populate a value for. + @param vecSetting The value to set. + */ + void setSetting(const char* setting, const glm::vec3& value); private slots: @@ -138,10 +193,18 @@ private: static void attachNewHeadToAgent(Agent *newAgent); static void* networkReceive(void* args); + // These two functions are technically not necessary, but they help keep things in one place. + void readSettings(); //! This function is largely to help consolidate getting settings in one place. + void saveSettings(); //! This function is to consolidate any settings setting in one place. + + void readSettingsFile(); //! This function reads data from the settings file, splitting data into key value pairs using '=' as a delimiter. + void saveSettingsFile(); //! This function writes all changes in the settings table to the settings file, serializing all settings added through the setSetting functions. + QMainWindow* _window; QGLWidget* _glWidget; - QAction* _lookingInMirror; // Are we currently rendering one's own head as if in mirror? + QAction* _lookingInMirror; // Are we currently rendering one's own head as if in mirror? + QAction* _echoAudioMode; // Are we asking the mixer to echo back our audio? QAction* _gyroLook; // Whether to allow the gyro data from head to move your view QAction* _mouseLook; // Whether the have the mouse near edge of screen move your view QAction* _showHeadMouse; // Whether the have the mouse near edge of screen move your view @@ -258,6 +321,12 @@ private: int _packetsPerSecond; int _bytesPerSecond; int _bytesCount; + + /*! + * Store settings in a map, storing keys and values as strings. + * Interpret values as needed on demand. through the appropriate getters and setters. + */ + std::map _settingsTable; }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 8acb70ebcc..97929a99ca 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -158,10 +158,10 @@ int audioCallback (const void* inputBuffer, correctedYaw += 360; } - if (parentAudio->_mixerLoopbackFlag) { + if (Application::getInstance()->shouldEchoAudio()) { correctedYaw = correctedYaw > 0 - ? correctedYaw + AGENT_LOOPBACK_MODIFIER - : correctedYaw - AGENT_LOOPBACK_MODIFIER; + ? correctedYaw + AGENT_LOOPBACK_MODIFIER + : correctedYaw - AGENT_LOOPBACK_MODIFIER; } memcpy(currentPacketPtr, &correctedYaw, sizeof(float)); @@ -310,7 +310,6 @@ Audio::Audio(Oscilloscope* scope) : NUM_AUDIO_CHANNELS * (SAMPLE_RATE / 1000.0)), _wasStarved(0), _lastInputLoudness(0), - _mixerLoopbackFlag(false), _lastVelocity(0), _lastAcceleration(0), _totalPacketsReceived(0), diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 34a342a1d4..197daf0194 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -24,8 +24,6 @@ public: void render(int screenWidth, int screenHeight); - void setMixerLoopbackFlag(bool mixerLoopbackFlag) { _mixerLoopbackFlag = mixerLoopbackFlag; } - float getLastInputLoudness() const { return _lastInputLoudness; }; void setLastAcceleration(glm::vec3 lastAcceleration) { _lastAcceleration = lastAcceleration; }; @@ -52,7 +50,6 @@ private: short _jitterBufferSamples; int _wasStarved; float _lastInputLoudness; - bool _mixerLoopbackFlag; glm::vec3 _lastVelocity; glm::vec3 _lastAcceleration; int _totalPacketsReceived; diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index 25e1764025..3a4162d296 100644 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -51,7 +51,6 @@ const float HEAD_MIN_YAW = -85; const float PERIPERSONAL_RADIUS = 1.0f; const float AVATAR_BRAKING_STRENGTH = 40.0f; const float JOINT_TOUCH_RANGE = 0.0005f; -const float ANGULAR_RIGHTING_SPEED = 45.0f; const float FLOATING_HEIGHT = 0.13f; const bool USING_HEAD_LEAN = false; const float LEAN_SENSITIVITY = 0.15; @@ -78,7 +77,6 @@ Avatar::Avatar(Agent* owningAgent) : _bodyYawDelta(0.0f), _bodyRollDelta(0.0f), _movedHandOffset(0.0f, 0.0f, 0.0f), - _rotation(0.0f, 0.0f, 0.0f, 0.0f), _mode(AVATAR_MODE_STANDING), _cameraPosition(0.0f, 0.0f, 0.0f), _handHoldingPosition(0.0f, 0.0f, 0.0f), @@ -86,11 +84,11 @@ Avatar::Avatar(Agent* owningAgent) : _thrust(0.0f, 0.0f, 0.0f), _speed(0.0f), _maxArmLength(0.0f), - _orientation(), _pelvisStandingHeight(0.0f), _pelvisFloatingHeight(0.0f), _distanceToNearestAvatar(std::numeric_limits::max()), _gravity(0.0f, -1.0f, 0.0f), + _worldUpDirection(0.0f, 1.0f, 0.0), _mouseRayOrigin(0.0f, 0.0f, 0.0f), _mouseRayDirection(0.0f, 0.0f, 0.0f), _interactingOther(NULL), @@ -126,7 +124,7 @@ void Avatar::reset() { } // Update avatar head rotation with sensor data -void Avatar::updateHeadFromGyros(float deltaTime, SerialInterface* serialInterface, glm::vec3* gravity) { +void Avatar::updateHeadFromGyros(float deltaTime, SerialInterface* serialInterface) { const float AMPLIFY_PITCH = 2.f; const float AMPLIFY_YAW = 2.f; const float AMPLIFY_ROLL = 2.f; @@ -136,10 +134,9 @@ void Avatar::updateHeadFromGyros(float deltaTime, SerialInterface* serialInterfa float measuredRollRate = serialInterface->getLastRollRate(); // Update avatar head position based on measured gyro rates - _head.addPitch(measuredPitchRate * AMPLIFY_PITCH * deltaTime); - _head.addYaw (measuredYawRate * AMPLIFY_YAW * deltaTime); - _head.addRoll (measuredRollRate * AMPLIFY_ROLL * deltaTime); + _head.addYaw(measuredYawRate * AMPLIFY_YAW * deltaTime); + _head.addRoll(measuredRollRate * AMPLIFY_ROLL * deltaTime); // Update head lean distance based on accelerometer data glm::vec3 headRotationRates(_head.getPitch(), _head.getYaw(), _head.getRoll()); @@ -157,11 +154,19 @@ void Avatar::updateHeadFromGyros(float deltaTime, SerialInterface* serialInterfa } float Avatar::getAbsoluteHeadYaw() const { - return _bodyYaw + _head.getYaw(); + return glm::yaw(_head.getOrientation()); } float Avatar::getAbsoluteHeadPitch() const { - return _bodyPitch + _head.getPitch(); + return glm::pitch(_head.getOrientation()); +} + +glm::quat Avatar::getOrientation() const { + return glm::quat(glm::radians(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll))); +} + +glm::quat Avatar::getWorldAlignedOrientation () const { + return computeRotationFromBodyToWorldUp() * getOrientation(); } void Avatar::updateFromMouse(int mouseX, int mouseY, int screenWidth, int screenHeight) { @@ -218,9 +223,9 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { // if other avatar, update head position from network data - // update avatar skeleton - updateSkeleton(); - + // update avatar skeleton + updateSkeleton(); + //detect and respond to collisions with other avatars... if (!_owningAgent) { updateAvatarCollisions(deltaTime); @@ -238,7 +243,7 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { updateCollisionWithEnvironment(); } - // update body springs + // update body springs updateBodySprings(deltaTime); // test for avatar collision response with the big sphere @@ -251,23 +256,29 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { updateCollisionWithVoxels(); } + glm::quat orientation = getOrientation(); + glm::vec3 front = orientation * AVATAR_FRONT; + glm::vec3 right = orientation * AVATAR_RIGHT; + glm::vec3 up = orientation * AVATAR_UP; + // driving the avatar around should only apply if this is my avatar (as opposed to an avatar being driven remotely) if (!_owningAgent) { _thrust = glm::vec3(0.0f, 0.0f, 0.0f); // Add Thrusts from keyboard - if (_driveKeys[FWD ]) {_thrust += THRUST_MAG * deltaTime * _orientation.getFront();} - if (_driveKeys[BACK ]) {_thrust -= THRUST_MAG * deltaTime * _orientation.getFront();} - if (_driveKeys[RIGHT ]) {_thrust += THRUST_MAG * deltaTime * _orientation.getRight();} - if (_driveKeys[LEFT ]) {_thrust -= THRUST_MAG * deltaTime * _orientation.getRight();} - if (_driveKeys[UP ]) {_thrust += THRUST_MAG * deltaTime * _orientation.getUp();} - if (_driveKeys[DOWN ]) {_thrust -= THRUST_MAG * deltaTime * _orientation.getUp();} + if (_driveKeys[FWD ]) {_thrust += THRUST_MAG * deltaTime * front;} + if (_driveKeys[BACK ]) {_thrust -= THRUST_MAG * deltaTime * front;} + if (_driveKeys[RIGHT ]) {_thrust += THRUST_MAG * deltaTime * right;} + if (_driveKeys[LEFT ]) {_thrust -= THRUST_MAG * deltaTime * right;} + if (_driveKeys[UP ]) {_thrust += THRUST_MAG * deltaTime * up;} + if (_driveKeys[DOWN ]) {_thrust -= THRUST_MAG * deltaTime * up;} if (_driveKeys[ROT_RIGHT]) {_bodyYawDelta -= YAW_MAG * deltaTime;} if (_driveKeys[ROT_LEFT ]) {_bodyYawDelta += YAW_MAG * deltaTime;} // Add thrusts from Transmitter if (transmitter) { + transmitter->checkForLostTransmitter(); glm::vec3 rotation = transmitter->getEstimatedRotation(); const float TRANSMITTER_MIN_RATE = 1.f; const float TRANSMITTER_MIN_YAW_RATE = 4.f; @@ -277,10 +288,10 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { const float TRANSMITTER_LIFT_SCALE = 3.f; const float TOUCH_POSITION_RANGE_HALF = 32767.f; if (fabs(rotation.z) > TRANSMITTER_MIN_RATE) { - _thrust += rotation.z * TRANSMITTER_LATERAL_FORCE_SCALE * deltaTime * _orientation.getRight(); + _thrust += rotation.z * TRANSMITTER_LATERAL_FORCE_SCALE * deltaTime * right; } if (fabs(rotation.x) > TRANSMITTER_MIN_RATE) { - _thrust += -rotation.x * TRANSMITTER_FWD_FORCE_SCALE * deltaTime * _orientation.getFront(); + _thrust += -rotation.x * TRANSMITTER_FWD_FORCE_SCALE * deltaTime * front; } if (fabs(rotation.y) > TRANSMITTER_MIN_YAW_RATE) { _bodyYawDelta += rotation.y * TRANSMITTER_YAW_SCALE * deltaTime; @@ -290,118 +301,103 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { (float)(transmitter->getTouchState()->y - TOUCH_POSITION_RANGE_HALF) / TOUCH_POSITION_RANGE_HALF * TRANSMITTER_LIFT_SCALE * deltaTime * - _orientation.getUp(); + up; } } - } - - // update body yaw by body yaw delta - if (!_owningAgent) { - _bodyPitch += _bodyPitchDelta * deltaTime; - _bodyYaw += _bodyYawDelta * deltaTime; - _bodyRoll += _bodyRollDelta * deltaTime; - } - - // decay body rotation momentum - float bodySpinMomentum = 1.0 - BODY_SPIN_FRICTION * deltaTime; - if (bodySpinMomentum < 0.0f) { bodySpinMomentum = 0.0f; } - _bodyPitchDelta *= bodySpinMomentum; - _bodyYawDelta *= bodySpinMomentum; - _bodyRollDelta *= bodySpinMomentum; - - // add thrust to velocity - _velocity += _thrust * deltaTime; - - // calculate speed - _speed = glm::length(_velocity); - - //pitch and roll the body as a function of forward speed and turning delta - const float BODY_PITCH_WHILE_WALKING = 20.0; - const float BODY_ROLL_WHILE_TURNING = 0.2; - float forwardComponentOfVelocity = glm::dot(_orientation.getFront(), _velocity); - _bodyPitch += BODY_PITCH_WHILE_WALKING * deltaTime * forwardComponentOfVelocity; - _bodyRoll += BODY_ROLL_WHILE_TURNING * deltaTime * _speed * _bodyYawDelta; - - // these forces keep the body upright... - float tiltDecay = 1.0 - BODY_UPRIGHT_FORCE * deltaTime; - if (tiltDecay < 0.0f) {tiltDecay = 0.0f;} - _bodyPitch *= tiltDecay; - _bodyRoll *= tiltDecay; - - //the following will be used to make the avatar upright no matter what gravity is - float gravityLength = glm::length(_gravity); - if (gravityLength > 0.0f) { - glm::vec3 targetUp = _gravity / -gravityLength; - const glm::vec3& currentUp = _righting * glm::vec3(0.0f, 1.0f, 0.0f); - float angle = glm::degrees(acosf(glm::dot(currentUp, targetUp))); - if (angle > 0.0f) { - glm::vec3 axis; - if (angle > 180.0f - EPSILON) { // 180 degree rotation; must use another axis - axis = _orientation.getRight(); - } else { - axis = glm::normalize(glm::cross(currentUp, targetUp)); - } - //_righting = glm::angleAxis(min(deltaTime * ANGULAR_RIGHTING_SPEED, angle), axis) * _righting; - } - } - - // update position by velocity - _position += _velocity * deltaTime; - // decay velocity - float decay = 1.0 - VELOCITY_DECAY * deltaTime; - if ( decay < 0.0 ) { - _velocity = glm::vec3( 0.0f, 0.0f, 0.0f ); - } else { - _velocity *= decay; - } - - // If another avatar is near, dampen velocity as a function of closeness - if (!_owningAgent && (_distanceToNearestAvatar < PERIPERSONAL_RADIUS)) { - float closeness = 1.0f - (_distanceToNearestAvatar / PERIPERSONAL_RADIUS); - float drag = 1.0f - closeness * AVATAR_BRAKING_STRENGTH * deltaTime; - if ( drag > 0.0f ) { - _velocity *= drag; - } else { + // update body yaw by body yaw delta + orientation = orientation * glm::quat(glm::radians( + glm::vec3(_bodyPitchDelta, _bodyYawDelta, _bodyRollDelta) * deltaTime)); + + // decay body rotation momentum + float bodySpinMomentum = 1.0 - BODY_SPIN_FRICTION * deltaTime; + if (bodySpinMomentum < 0.0f) { bodySpinMomentum = 0.0f; } + _bodyPitchDelta *= bodySpinMomentum; + _bodyYawDelta *= bodySpinMomentum; + _bodyRollDelta *= bodySpinMomentum; + + // add thrust to velocity + _velocity += _thrust * deltaTime; + + // calculate speed + _speed = glm::length(_velocity); + + //pitch and roll the body as a function of forward speed and turning delta + const float BODY_PITCH_WHILE_WALKING = -20.0; + const float BODY_ROLL_WHILE_TURNING = 0.2; + float forwardComponentOfVelocity = glm::dot(getBodyFrontDirection(), _velocity); + orientation = orientation * glm::quat(glm::radians(glm::vec3( + BODY_PITCH_WHILE_WALKING * deltaTime * forwardComponentOfVelocity, 0.0f, + BODY_ROLL_WHILE_TURNING * deltaTime * _speed * _bodyYawDelta))); + + // these forces keep the body upright... + float tiltDecay = BODY_UPRIGHT_FORCE * deltaTime; + if (tiltDecay > 1.0f) {tiltDecay = 1.0f;} + + // update the euler angles + setOrientation(orientation); + + //the following will be used to make the avatar upright no matter what gravity is + setOrientation(computeRotationFromBodyToWorldUp(tiltDecay) * orientation); + + // update position by velocity + _position += _velocity * deltaTime; + + // decay velocity + float decay = 1.0 - VELOCITY_DECAY * deltaTime; + if ( decay < 0.0 ) { _velocity = glm::vec3( 0.0f, 0.0f, 0.0f ); + } else { + _velocity *= decay; + } + + // If another avatar is near, dampen velocity as a function of closeness + if (_distanceToNearestAvatar < PERIPERSONAL_RADIUS) { + float closeness = 1.0f - (_distanceToNearestAvatar / PERIPERSONAL_RADIUS); + float drag = 1.0f - closeness * AVATAR_BRAKING_STRENGTH * deltaTime; + if ( drag > 0.0f ) { + _velocity *= drag; + } else { + _velocity = glm::vec3( 0.0f, 0.0f, 0.0f ); + } + } + + // Compute instantaneous acceleration + float acceleration = glm::distance(getVelocity(), oldVelocity) / deltaTime; + const float ACCELERATION_PITCH_DECAY = 0.4f; + const float ACCELERATION_YAW_DECAY = 0.4f; + + const float OCULUS_ACCELERATION_PULL_THRESHOLD = 1.0f; + const int OCULUS_YAW_OFFSET_THRESHOLD = 10; + + // Decay HeadPitch as a function of acceleration, so that you look straight ahead when + // you start moving, but don't do this with an HMD like the Oculus. + if (!OculusManager::isConnected()) { + _head.setPitch(_head.getPitch() * (1.f - acceleration * ACCELERATION_PITCH_DECAY * deltaTime)); + _head.setYaw(_head.getYaw() * (1.f - acceleration * ACCELERATION_YAW_DECAY * deltaTime)); + } else if (fabsf(acceleration) > OCULUS_ACCELERATION_PULL_THRESHOLD + && fabs(_head.getYaw()) > OCULUS_YAW_OFFSET_THRESHOLD) { + // if we're wearing the oculus + // and this acceleration is above the pull threshold + // and the head yaw if off the body by more than OCULUS_YAW_OFFSET_THRESHOLD + + // match the body yaw to the oculus yaw + _bodyYaw = getAbsoluteHeadYaw(); + + // set the head yaw to zero for this draw + _head.setYaw(0); + + // correct the oculus yaw offset + OculusManager::updateYawOffset(); } } - // Compute instantaneous acceleration - float acceleration = glm::distance(getVelocity(), oldVelocity) / deltaTime; - const float ACCELERATION_PITCH_DECAY = 0.4f; - const float ACCELERATION_YAW_DECAY = 0.4f; - - const float OCULUS_ACCELERATION_PULL_THRESHOLD = 1.0f; - const int OCULUS_YAW_OFFSET_THRESHOLD = 10; - - // Decay HeadPitch as a function of acceleration, so that you look straight ahead when - // you start moving, but don't do this with an HMD like the Oculus. - if (!OculusManager::isConnected()) { - _head.setPitch(_head.getPitch() * (1.f - acceleration * ACCELERATION_PITCH_DECAY * deltaTime)); - _head.setYaw(_head.getYaw() * (1.f - acceleration * ACCELERATION_YAW_DECAY * deltaTime)); - } else if (fabsf(acceleration) > OCULUS_ACCELERATION_PULL_THRESHOLD - && fabs(_head.getYaw()) > OCULUS_YAW_OFFSET_THRESHOLD) { - // if we're wearing the oculus - // and this acceleration is above the pull threshold - // and the head yaw if off the body by more than OCULUS_YAW_OFFSET_THRESHOLD - - // match the body yaw to the oculus yaw - _bodyYaw = getAbsoluteHeadYaw(); - - // set the head yaw to zero for this draw - _head.setYaw(0); - - // correct the oculus yaw offset - OculusManager::updateYawOffset(); - } - //apply the head lean values to the springy position... if (USING_HEAD_LEAN) { if (fabs(_head.getLeanSideways() + _head.getLeanForward()) > 0.0f) { glm::vec3 headLean = - _orientation.getRight() * _head.getLeanSideways() + - _orientation.getFront() * _head.getLeanForward(); + right * _head.getLeanSideways() + + front * _head.getLeanForward(); _joint[ AVATAR_JOINT_TORSO ].springyPosition += headLean * 0.1f; _joint[ AVATAR_JOINT_CHEST ].springyPosition += headLean * 0.4f; @@ -431,18 +427,18 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { } } - _head.setBodyRotation (glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll)); + _head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll)); _head.setPosition(_joint[ AVATAR_JOINT_HEAD_BASE ].springyPosition); _head.setScale (_joint[ AVATAR_JOINT_HEAD_BASE ].radius); _head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2])); _head.simulate(deltaTime, !_owningAgent); // use speed and angular velocity to determine walking vs. standing - if (_speed + fabs(_bodyYawDelta) > 0.2) { - _mode = AVATAR_MODE_WALKING; - } else { - _mode = AVATAR_MODE_INTERACTING; - } + if (_speed + fabs(_bodyYawDelta) > 0.2) { + _mode = AVATAR_MODE_WALKING; + } else { + _mode = AVATAR_MODE_INTERACTING; + } } void Avatar::checkForMouseRayTouching() { @@ -465,19 +461,32 @@ void Avatar::setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction ) { _mouseRayDirection = direction; } +void Avatar::setOrientation(const glm::quat& orientation) { + glm::vec3 eulerAngles = safeEulerAngles(orientation); + _bodyPitch = eulerAngles.x; + _bodyYaw = eulerAngles.y; + _bodyRoll = eulerAngles.z; +} + void Avatar::updateHandMovementAndTouching(float deltaTime) { + glm::quat orientation = getOrientation(); + // reset hand and arm positions according to hand movement + glm::vec3 right = orientation * AVATAR_RIGHT; + glm::vec3 up = orientation * AVATAR_UP; + glm::vec3 front = orientation * AVATAR_FRONT; + glm::vec3 transformedHandMovement - = _orientation.getRight() * _movedHandOffset.x * 2.0f - + _orientation.getUp() * -_movedHandOffset.y * 1.0f - + _orientation.getFront() * -_movedHandOffset.y * 1.0f; + = right * _movedHandOffset.x * 2.0f + + up * -_movedHandOffset.y * 2.0f + + front * -_movedHandOffset.z * 2.0f; _joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position += transformedHandMovement; if (!_owningAgent) { _avatarTouch.setMyBodyPosition(_position); - _avatarTouch.setMyOrientation(_orientation); + _avatarTouch.setMyOrientation(orientation); float closestDistance = std::numeric_limits::max(); @@ -492,7 +501,7 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) { //Test: Show angle between your fwd vector and nearest avatar //glm::vec3 vectorBetweenUs = otherAvatar->getJointPosition(AVATAR_JOINT_PELVIS) - // getJointPosition(AVATAR_JOINT_PELVIS); - //printLog("Angle between: %f\n", angleBetween(vectorBetweenUs, _orientation.getFront())); + //printLog("Angle between: %f\n", angleBetween(vectorBetweenUs, getBodyFrontDirection())); // test whether shoulders are close enough to allow for reaching to touch hands glm::vec3 v(_position - otherAvatar->_position); @@ -511,7 +520,7 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) { _avatarTouch.setHasInteractingOther(true); _avatarTouch.setYourBodyPosition(_interactingOther->_position); - _avatarTouch.setYourOrientation (_interactingOther->_orientation); + _avatarTouch.setYourOrientation (_interactingOther->getOrientation()); _avatarTouch.setYourHandPosition(_interactingOther->_joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].springyPosition); _avatarTouch.setYourHandState (_interactingOther->_handState); @@ -616,11 +625,12 @@ void Avatar::updateCollisionWithSphere(glm::vec3 position, float radius, float d } void Avatar::updateCollisionWithEnvironment() { + glm::vec3 up = getBodyUpDirection(); float radius = _height * 0.125f; glm::vec3 penetration; if (Application::getInstance()->getEnvironment()->findCapsulePenetration( - _position - glm::vec3(0.0f, _pelvisFloatingHeight - radius, 0.0f), - _position + glm::vec3(0.0f, _height - _pelvisFloatingHeight - radius, 0.0f), radius, penetration)) { + _position - up * (_pelvisFloatingHeight - radius), + _position + up * (_height - _pelvisFloatingHeight - radius), radius, penetration)) { applyCollisionWithScene(penetration); } } @@ -740,11 +750,17 @@ static TextRenderer* textRenderer() { void Avatar::setGravity(glm::vec3 gravity) { _gravity = gravity; _head.setGravity(_gravity); + + // use the gravity to determine the new world up direction, if possible + float gravityLength = glm::length(gravity); + if (gravityLength > EPSILON) { + _worldUpDirection = _gravity / -gravityLength; + } } -void Avatar::render(bool lookingInMirror, glm::vec3 cameraPosition) { +void Avatar::render(bool lookingInMirror) { - _cameraPosition = cameraPosition; + _cameraPosition = Application::getInstance()->getCamera()->getPosition(); if (!_owningAgent && usingBigSphereCollisionTest) { // show TEST big sphere @@ -783,18 +799,14 @@ void Avatar::render(bool lookingInMirror, glm::vec3 cameraPosition) { } glPushMatrix(); - // extract the view direction from the modelview matrix: transform (0, 0, 1) by the - // transpose of the modelview to get its direction in world space, then use the X/Z - // components to determine the angle - float modelview[16]; - glGetFloatv(GL_MODELVIEW_MATRIX, modelview); - - glTranslatef(_joint[AVATAR_JOINT_HEAD_BASE].springyPosition.x, - _joint[AVATAR_JOINT_HEAD_BASE].springyPosition.y + chatMessageHeight, - _joint[AVATAR_JOINT_HEAD_BASE].springyPosition.z); - glRotatef(atan2(-modelview[2], -modelview[10]) * 180 / PI, 0, 1, 0); + glm::vec3 chatPosition = _joint[AVATAR_JOINT_HEAD_BASE].springyPosition + getBodyUpDirection() * chatMessageHeight; + glTranslatef(chatPosition.x, chatPosition.y, chatPosition.z); + glm::quat chatRotation = Application::getInstance()->getCamera()->getRotation(); + glm::vec3 chatAxis = glm::axis(chatRotation); + glRotatef(glm::angle(chatRotation), chatAxis.x, chatAxis.y, chatAxis.z); glColor3f(0, 0.8, 0); + glRotatef(180, 0, 1, 0); glRotatef(180, 0, 0, 1); glScalef(chatMessageScale, chatMessageScale, 1.0f); @@ -823,78 +835,74 @@ void Avatar::render(bool lookingInMirror, glm::vec3 cameraPosition) { void Avatar::initializeSkeleton() { - for (int b=0; b 0.0f) { // to avoid divide by zero glm::vec3 springDirection = springVector / length; - + float force = (length - _joint[b].length) * BODY_SPRING_FORCE * deltaTime; - + _joint[b].springyVelocity -= springDirection * force; if (_joint[b].parent != AVATAR_JOINT_NULL) { @@ -1051,7 +1051,7 @@ void Avatar::updateBodySprings(float deltaTime) { } // apply tightness force - (causing springy position to be close to rigid body position) - _joint[b].springyVelocity += (_joint[b].position - _joint[b].springyPosition) * _joint[b].springBodyTightness * deltaTime; + _joint[b].springyVelocity += (_joint[b].position - _joint[b].springyPosition) * _joint[b].springBodyTightness * deltaTime; // apply decay float decay = 1.0 - BODY_SPRING_DECAY * deltaTime; @@ -1082,7 +1082,7 @@ void Avatar::updateArmIKAndConstraints(float deltaTime) { // test to see if right hand is being dragged beyond maximum arm length float distance = glm::length(armVector); - + // don't let right hand get dragged beyond maximum arm length... if (distance > _maxArmLength) { // reset right hand to be constrained to maximum arm length @@ -1099,7 +1099,7 @@ void Avatar::updateArmIKAndConstraints(float deltaTime) { glm::vec3 newElbowPosition = _joint[ AVATAR_JOINT_RIGHT_SHOULDER ].position; newElbowPosition += armVector * ONE_HALF; - glm::vec3 perpendicular = glm::cross(_orientation.getFront(), armVector); + glm::vec3 perpendicular = glm::cross(getBodyFrontDirection(), armVector); newElbowPosition += perpendicular * (1.0f - (_maxArmLength / distance)) * ONE_HALF; _joint[ AVATAR_JOINT_RIGHT_ELBOW ].position = newElbowPosition; @@ -1111,19 +1111,42 @@ void Avatar::updateArmIKAndConstraints(float deltaTime) { _joint[ AVATAR_JOINT_RIGHT_WRIST ].position = newWristPosition; } +glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { + glm::quat orientation = getOrientation(); + glm::vec3 currentUp = orientation * AVATAR_UP; + float angle = glm::degrees(acosf(glm::clamp(glm::dot(currentUp, _worldUpDirection), -1.0f, 1.0f))); + if (angle < EPSILON) { + return glm::quat(); + } + glm::vec3 axis; + if (angle > 179.99f) { // 180 degree rotation; must use another axis + axis = orientation * AVATAR_RIGHT; + } else { + axis = glm::normalize(glm::cross(currentUp, _worldUpDirection)); + } + return glm::angleAxis(angle * proportion, axis); +} void Avatar::renderBody(bool lookingInMirror) { - const float RENDER_OPAQUE_BEYOND = 1.2f; // Meters beyond which body is shown opaque + const float RENDER_OPAQUE_BEYOND = 1.0f; // Meters beyond which body is shown opaque const float RENDER_TRANSLUCENT_BEYOND = 0.5f; // Render the body as balls and cones for (int b = 0; b < NUM_AVATAR_JOINTS; b++) { float distanceToCamera = glm::length(_cameraPosition - _joint[b].position); + + float alpha = lookingInMirror ? 1.0f : glm::clamp((distanceToCamera - RENDER_TRANSLUCENT_BEYOND) / + (RENDER_OPAQUE_BEYOND - RENDER_TRANSLUCENT_BEYOND), 0.f, 1.f); + + if (lookingInMirror || _owningAgent) { + alpha = 1.0f; + } + // Always render other people, and render myself when beyond threshold distance - if (b == AVATAR_JOINT_HEAD_BASE) { // the head is rendered as a special case - if (lookingInMirror || _owningAgent || distanceToCamera > RENDER_OPAQUE_BEYOND) { - _head.render(lookingInMirror, _cameraPosition); + if (b == AVATAR_JOINT_HEAD_BASE) { // the head is rendered as a special + if (lookingInMirror || _owningAgent || distanceToCamera > RENDER_OPAQUE_BEYOND * 0.5) { + _head.render(lookingInMirror, _cameraPosition, alpha); } } else if (_owningAgent || distanceToCamera > RENDER_TRANSLUCENT_BEYOND || b == AVATAR_JOINT_RIGHT_ELBOW @@ -1140,14 +1163,16 @@ void Avatar::renderBody(bool lookingInMirror) { glColor4f(SKIN_COLOR[0] + _joint[b].touchForce * 0.3f, SKIN_COLOR[1] - _joint[b].touchForce * 0.2f, SKIN_COLOR[2] - _joint[b].touchForce * 0.1f, - glm::clamp((distanceToCamera - RENDER_TRANSLUCENT_BEYOND) - / (RENDER_OPAQUE_BEYOND - RENDER_TRANSLUCENT_BEYOND), 0.f, 1.f)); + alpha); } - glPushMatrix(); - glTranslatef(_joint[b].springyPosition.x, _joint[b].springyPosition.y, _joint[b].springyPosition.z); - glutSolidSphere(_joint[b].radius, 20.0f, 20.0f); - glPopMatrix(); + if ((b != AVATAR_JOINT_HEAD_TOP ) + && (b != AVATAR_JOINT_HEAD_BASE )) { + glPushMatrix(); + glTranslatef(_joint[b].springyPosition.x, _joint[b].springyPosition.y, _joint[b].springyPosition.z); + glutSolidSphere(_joint[b].radius, 20.0f, 20.0f); + glPopMatrix(); + } // Render the cone connecting this joint to its parent if (_joint[b].parent != AVATAR_JOINT_NULL) { @@ -1212,36 +1237,25 @@ void Avatar::setHeadFromGyros(glm::vec3* eulerAngles, glm::vec3* angularVelocity } } -const char AVATAR_DATA_FILENAME[] = "avatar.ifd"; - void Avatar::writeAvatarDataToFile() { - // write the avatar position and yaw to a local file - FILE* avatarFile = fopen(AVATAR_DATA_FILENAME, "w"); - - if (avatarFile) { - fprintf(avatarFile, "%f,%f,%f %f", _position.x, _position.y, _position.z, _bodyYaw); - fclose(avatarFile); - } + Application::getInstance()->setSetting("avatarPos", _position); + Application::getInstance()->setSetting("avatarRotation", glm::vec3(_bodyYaw, _bodyPitch, _bodyRoll)); } void Avatar::readAvatarDataFromFile() { - FILE* avatarFile = fopen(AVATAR_DATA_FILENAME, "r"); + glm::vec3 readPosition; + glm::vec3 readRotation; - if (avatarFile) { - glm::vec3 readPosition; - float readYaw; - fscanf(avatarFile, "%f,%f,%f %f", &readPosition.x, &readPosition.y, &readPosition.z, &readYaw); - - // make sure these values are sane - if (!isnan(readPosition.x) && !isnan(readPosition.y) && !isnan(readPosition.z) && !isnan(readYaw)) { - _position = readPosition; - _bodyYaw = readYaw; - } - fclose(avatarFile); - } + Application::getInstance()->getSetting("avatarPos", readPosition, glm::vec3(6.1f, 0, 1.4f)); + Application::getInstance()->getSetting("avatarRotation", readRotation, glm::vec3(0, 0, 0)); + + _bodyYaw = readRotation.x; + _bodyPitch = readRotation.y; + _bodyRoll = readRotation.z; + _position = readPosition; } -// render a makeshift cone section that serves as a body part connecting joint spheres +// render a makeshift cone section that serves as a body part connecting joint spheres void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2) { glBegin(GL_TRIANGLES); diff --git a/interface/src/Avatar.h b/interface/src/Avatar.h index 1ef5675d9b..d272514ed1 100644 --- a/interface/src/Avatar.h +++ b/interface/src/Avatar.h @@ -11,7 +11,6 @@ #include #include #include -#include #include "world.h" #include "AvatarTouch.h" #include "InterfaceConfig.h" @@ -31,15 +30,15 @@ enum DriveKeys DOWN, ROT_LEFT, ROT_RIGHT, - MAX_DRIVE_KEYS + MAX_DRIVE_KEYS }; enum AvatarMode { - AVATAR_MODE_STANDING = 0, - AVATAR_MODE_WALKING, - AVATAR_MODE_INTERACTING, - NUM_AVATAR_MODES + AVATAR_MODE_STANDING = 0, + AVATAR_MODE_WALKING, + AVATAR_MODE_INTERACTING, + NUM_AVATAR_MODES }; class Avatar : public AvatarData { @@ -49,10 +48,10 @@ public: void reset(); void simulate(float deltaTime, Transmitter* transmitter); - void updateHeadFromGyros(float frametime, SerialInterface * serialInterface, glm::vec3 * gravity); + void updateHeadFromGyros(float frametime, SerialInterface * serialInterface); void updateFromMouse(int mouseX, int mouseY, int screenWidth, int screenHeight); void addBodyYaw(float y) {_bodyYaw += y;}; - void render(bool lookingInMirror, glm::vec3 cameraPosition); + void render(bool lookingInMirror); //setters void setMousePressed (bool mousePressed ) { _mousePressed = mousePressed;} @@ -62,22 +61,27 @@ public: void setDisplayingLookatVectors(bool displayingLookatVectors) { _head.setRenderLookatVectors(displayingLookatVectors);} void setGravity (glm::vec3 gravity); void setMouseRay (const glm::vec3 &origin, const glm::vec3 &direction); + void setOrientation (const glm::quat& orientation); //getters - float getHeadYawRate () const { return _head.yawRate;} - float getBodyYaw () const { return _bodyYaw;} - bool getIsNearInteractingOther() const { return _avatarTouch.getAbleToReachOtherAvatar();} - const glm::vec3& getHeadPosition () const { return _joint[ AVATAR_JOINT_HEAD_BASE ].position;} - const glm::vec3& getSpringyHeadPosition () const { return _joint[ AVATAR_JOINT_HEAD_BASE ].springyPosition;} - const glm::vec3& getJointPosition (AvatarJointID j) const { return _joint[j].springyPosition;} - const glm::vec3& getBodyUpDirection () const { return _orientation.getUp();} - const glm::vec3& getVelocity () const { return _velocity;} - float getSpeed () const { return _speed;} - float getHeight () const { return _height;} - AvatarMode getMode () const { return _mode;} - float getAbsoluteHeadYaw () const; - float getAbsoluteHeadPitch () const; - Head& getHead () {return _head; } + float getHeadYawRate () const { return _head.yawRate;} + float getBodyYaw () const { return _bodyYaw;} + bool getIsNearInteractingOther () const { return _avatarTouch.getAbleToReachOtherAvatar();} + const glm::vec3& getHeadPosition () const { return _joint[ AVATAR_JOINT_HEAD_BASE ].position;} + const glm::vec3& getSpringyHeadPosition () const { return _joint[ AVATAR_JOINT_HEAD_BASE ].springyPosition;} + const glm::vec3& getJointPosition (AvatarJointID j) const { return _joint[j].springyPosition;} + glm::vec3 getBodyRightDirection () const { return getOrientation() * AVATAR_RIGHT; } + glm::vec3 getBodyUpDirection () const { return getOrientation() * AVATAR_UP; } + glm::vec3 getBodyFrontDirection () const { return getOrientation() * AVATAR_FRONT; } + const glm::vec3& getVelocity () const { return _velocity;} + float getSpeed () const { return _speed;} + float getHeight () const { return _height;} + AvatarMode getMode () const { return _mode;} + float getAbsoluteHeadYaw () const; + float getAbsoluteHeadPitch () const; + Head& getHead () { return _head; } + glm::quat getOrientation () const; + glm::quat getWorldAlignedOrientation () const; // Set what driving keys are being pressed to control thrust levels void setDriveKeys(int key, bool val) { _driveKeys[key] = val; }; @@ -99,19 +103,15 @@ private: struct AvatarJoint { 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 "T-pose" - glm::vec3 springyPosition; // used for special effects (a 'flexible' variant of position) - glm::vec3 springyVelocity; // used for special effects ( the velocity of the springy position) - float springBodyTightness; // how tightly the springy position tries to stay on the position - glm::quat rotation; // this will eventually replace yaw, pitch and roll (and maybe orientation) - float yaw; // the yaw Euler angle of the joint rotation off the parent - float pitch; // the pitch Euler angle of the joint rotation off the parent - float roll; // the roll Euler angle of the joint rotation off the parent - Orientation orientation; // three orthogonal normals determined by yaw, pitch, roll - float length; // the length of vector connecting the joint and its parent - float radius; // used for detecting collisions for certain physical effects - bool isCollidable; // when false, the joint position will not register a collision + 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 "T-pose" + glm::vec3 springyPosition; // used for special effects (a 'flexible' variant of position) + glm::vec3 springyVelocity; // used for special effects ( the velocity of the springy position) + float springBodyTightness; // how tightly the springy position tries to stay on the position + glm::quat orientation; // this will eventually replace yaw, pitch and roll (and maybe orientation) + float length; // the length of vector connecting the joint and its parent + float radius; // used for detecting collisions for certain physical effects + bool isCollidable; // when false, the joint position will not register a collision float touchForce; // if being touched, what's the degree of influence? (0 to 1) }; @@ -124,16 +124,14 @@ private: float _bodyYawDelta; float _bodyRollDelta; glm::vec3 _movedHandOffset; - glm::quat _rotation; // the rotation of the avatar body as a whole expressed as a quaternion - AvatarJoint _joint[ NUM_AVATAR_JOINTS ]; + AvatarJoint _joint[ NUM_AVATAR_JOINTS ]; AvatarMode _mode; glm::vec3 _cameraPosition; glm::vec3 _handHoldingPosition; glm::vec3 _velocity; - glm::vec3 _thrust; + glm::vec3 _thrust; float _speed; - float _maxArmLength; - Orientation _orientation; + float _maxArmLength; glm::quat _righting; int _driveKeys[MAX_DRIVE_KEYS]; float _pelvisStandingHeight; @@ -143,6 +141,7 @@ private: AvatarTouch _avatarTouch; float _distanceToNearestAvatar; // How close is the nearest avatar? glm::vec3 _gravity; + glm::vec3 _worldUpDirection; glm::vec3 _mouseRayOrigin; glm::vec3 _mouseRayDirection; Avatar* _interactingOther; @@ -151,6 +150,7 @@ private: // private methods... glm::vec3 caclulateAverageEyePosition() { return _head.caclulateAverageEyePosition(); } // get the position smack-dab between the eyes (for lookat) + glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const; void renderBody(bool lookingInMirror); void initializeSkeleton(); void updateSkeleton(); diff --git a/interface/src/AvatarTouch.cpp b/interface/src/AvatarTouch.cpp index 499e132922..2415d030c8 100644 --- a/interface/src/AvatarTouch.cpp +++ b/interface/src/AvatarTouch.cpp @@ -13,7 +13,7 @@ const float THREAD_RADIUS = 0.007; const float HANDS_CLOSE_ENOUGH_TO_GRASP = 0.2; -const float AVATAR_FACING_THRESHOLD = 0.1f; // (-1 to 1) (larger value indicates narrower angle of influence +const float AVATAR_FACING_THRESHOLD = -0.5f; // (-1 to 1) (larger value indicates narrower angle of influence AvatarTouch::AvatarTouch() { @@ -29,10 +29,8 @@ AvatarTouch::AvatarTouch() { _canReachToOtherAvatar = false; _handsCloseEnoughToGrasp = false; _hasInteractingOther = false; - _myOrientation.setToIdentity(); - _yourOrientation.setToIdentity(); - for (int p=0; p AVATAR_FACING_THRESHOLD)) { // I'm facing you + glm::vec3 myFront = _myOrientation * AVATAR_FRONT; + glm::vec3 yourFront = _yourOrientation * AVATAR_FRONT; + + if (( glm::dot(myFront, yourFront) < -AVATAR_FACING_THRESHOLD) // we're facing each other + && ( glm::dot(myFront, directionBetweenBodies ) > AVATAR_FACING_THRESHOLD)) { // I'm facing you facingEachOther = true; } - if ((distanceBetweenBodies < _reachableRadius) - && (facingEachOther)) { + if (distanceBetweenBodies < _reachableRadius) { _canReachToOtherAvatar = true; _vectorBetweenHands = _yourHandPosition - _myHandPosition; @@ -136,7 +136,6 @@ void AvatarTouch::render(glm::vec3 cameraPosition) { } - void AvatarTouch::renderBeamBetweenHands() { glm::vec3 v1(_myHandPosition); @@ -150,9 +149,9 @@ void AvatarTouch::renderBeamBetweenHands() { glEnd(); glColor3f(0.5f, 0.3f, 0.0f); - for (int p=0; p -#include "Orientation.h" +#include + +#include enum AvatarHandState { @@ -31,8 +33,8 @@ public: void setHasInteractingOther(bool hasInteractingOther) { _hasInteractingOther = hasInteractingOther;} void setMyHandPosition (glm::vec3 position ) { _myHandPosition = position;} void setYourHandPosition (glm::vec3 position ) { _yourHandPosition = position;} - void setMyOrientation (Orientation orientation ) { _myOrientation = orientation;} - void setYourOrientation (Orientation orientation ) { _yourOrientation = orientation;} + void setMyOrientation (glm::quat orientation ) { _myOrientation = orientation;} + void setYourOrientation (glm::quat orientation ) { _yourOrientation = orientation;} void setMyBodyPosition (glm::vec3 position ) { _myBodyPosition = position;} void setYourBodyPosition (glm::vec3 position ) { _yourBodyPosition = position;} void setMyHandState (int state ) { _myHandState = state;} @@ -46,17 +48,17 @@ public: private: - static const int NUM_POINTS = 100; + static const int NUM_PARTICLE_POINTS = 100; bool _hasInteractingOther; bool _weAreHoldingHands; - glm::vec3 _point [NUM_POINTS]; + glm::vec3 _point [NUM_PARTICLE_POINTS]; glm::vec3 _myBodyPosition; glm::vec3 _yourBodyPosition; glm::vec3 _myHandPosition; glm::vec3 _yourHandPosition; - Orientation _myOrientation; - Orientation _yourOrientation; + glm::quat _myOrientation; + glm::quat _yourOrientation; glm::vec3 _vectorBetweenHands; int _myHandState; int _yourHandState; diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index fa0744bf49..21e60c0cc5 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -4,12 +4,14 @@ // // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +#include #include #include #include -// #include "Log.h" +#include "Log.h" #include "Camera.h" +#include "Util.h" const float CAMERA_MINIMUM_MODE_SHIFT_RATE = 0.5f; @@ -38,9 +40,6 @@ Camera::Camera() { _fieldOfView = 60.0f; // default _nearClip = 0.08f; // default _farClip = 50.0f * TREE_SCALE; // default - _yaw = 0.0f; - _pitch = 0.0f; - _roll = 0.0f; _upShift = 0.0f; _distance = 0.0f; _previousUpShift = 0.0f; @@ -49,13 +48,9 @@ Camera::Camera() { _newUpShift = 0.0f; _newDistance = 0.0f; _newTightness = 0.0f; - _idealYaw = 0.0f; - _idealPitch = 0.0f; - _idealRoll = 0.0f; _targetPosition = glm::vec3(0.0f, 0.0f, 0.0f); _position = glm::vec3(0.0f, 0.0f, 0.0f); _idealPosition = glm::vec3(0.0f, 0.0f, 0.0f); - _orientation.setToIdentity(); } void Camera::update(float deltaTime) { @@ -64,17 +59,6 @@ void Camera::update(float deltaTime) { // use iterative forces to push the camera towards the target position and angle updateFollowMode(deltaTime); } - - // do this AFTER making any changes to yaw pitch and roll.... - generateOrientation(); -} - -// generate the ortho-normals for the orientation based on the three Euler angles -void Camera::generateOrientation() { - _orientation.setToIdentity(); - _orientation.pitch(_pitch); - _orientation.yaw (_yaw ); - _orientation.roll (_roll ); } // use iterative forces to keep the camera at the desired position and angle @@ -104,26 +88,15 @@ void Camera::updateFollowMode(float deltaTime) { t = 1.0; } - // update Euler angles (before position!) + // update rotation (before position!) if (_needsToInitialize || OculusManager::isConnected()) { - _yaw = _idealYaw; - _pitch = _idealPitch; - _roll = _idealRoll; + _rotation = _targetRotation; } else { - // pull Euler angles towards ideal Euler angles - _yaw += (_idealYaw - _yaw ) * t; - _pitch += (_idealPitch - _pitch) * t; - _roll += (_idealRoll - _roll ) * t; + // pull rotation towards ideal + _rotation = safeMix(_rotation, _targetRotation, t); } - float radian = (_yaw / 180.0) * PIE; - - // update _position - double x = -_distance * sin(radian); - double z = -_distance * cos(radian); - double y = _upShift; - - _idealPosition = _targetPosition + glm::vec3(x, y, z); + _idealPosition = _targetPosition + _rotation * glm::vec3(0.0f, _upShift, _distance); if (_needsToInitialize) { _position = _idealPosition; @@ -171,10 +144,8 @@ void Camera::setMode(CameraMode m) { } -void Camera::setTargetRotation( float yaw, float pitch, float roll ) { - _idealYaw = yaw; - _idealPitch = pitch; - _idealRoll = roll; +void Camera::setTargetRotation( const glm::quat& targetRotation ) { + _targetRotation = targetRotation; } void Camera::setFieldOfView(float f) { diff --git a/interface/src/Camera.h b/interface/src/Camera.h index ae3e3ef7b2..1f36d33f58 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -8,7 +8,6 @@ #ifndef __interface__camera__ #define __interface__camera__ -#include "Orientation.h" #include #include @@ -30,40 +29,36 @@ public: void update( float deltaTime ); - void setYaw ( float y ) { _yaw = y; } - void setPitch ( float p ) { _pitch = p; } - void setRoll ( float r ) { _roll = r; } - void setUpShift ( float u ) { _upShift = u; } - void setDistance ( float d ) { _distance = d; } - void setTargetPosition( glm::vec3 t ) { _targetPosition = t; } - void setTargetYaw ( float y ) { _idealYaw = y; } - void setPosition ( glm::vec3 p ) { _position = p; } - void setTightness ( float t ) { _tightness = t; } - void setTargetRotation( float yaw, float pitch, float roll ); + void setUpShift ( float u ) { _upShift = u; } + void setDistance ( float d ) { _distance = d; } + void setTargetPosition( const glm::vec3& t ) { _targetPosition = t; } + void setPosition ( const glm::vec3& p ) { _position = p; } + void setTightness ( float t ) { _tightness = t; } + void setTargetRotation( const glm::quat& rotation ); - void setMode ( CameraMode m ); - void setModeShiftRate ( float r ); - void setFieldOfView ( float f ); - void setAspectRatio ( float a ); - void setNearClip ( float n ); - void setFarClip ( float f ); - void setEyeOffsetPosition ( const glm::vec3& p); - void setEyeOffsetOrientation ( const glm::quat& o); - - float getYaw () { return _yaw; } - float getPitch () { return _pitch; } - float getRoll () { return _roll; } - glm::vec3 getPosition () { return _position; } - Orientation getOrientation() { return _orientation; } - CameraMode getMode () { return _mode; } - float getFieldOfView() { return _fieldOfView; } - float getAspectRatio() { return _aspectRatio; } - float getNearClip () { return _nearClip; } - float getFarClip () { return _farClip; } - glm::vec3 getEyeOffsetPosition () { return _eyeOffsetPosition; } - glm::quat getEyeOffsetOrientation () { return _eyeOffsetOrientation; } - bool getFrustumNeedsReshape(); // call to find out if the view frustum needs to be reshaped - void setFrustumWasReshaped(); // call this after reshaping the view frustum. + void setMode ( CameraMode m ); + void setModeShiftRate ( float r ); + void setFieldOfView ( float f ); + void setAspectRatio ( float a ); + void setNearClip ( float n ); + void setFarClip ( float f ); + void setEyeOffsetPosition ( const glm::vec3& p ); + void setEyeOffsetOrientation( const glm::quat& o ); + + const glm::vec3& getTargetPosition () { return _targetPosition; } + const glm::vec3& getPosition () { return _position; } + const glm::quat& getTargetRotation () { return _targetRotation; } + const glm::quat& getRotation () { return _rotation; } + CameraMode getMode () { return _mode; } + float getFieldOfView () { return _fieldOfView; } + float getAspectRatio () { return _aspectRatio; } + float getNearClip () { return _nearClip; } + float getFarClip () { return _farClip; } + const glm::vec3& getEyeOffsetPosition () { return _eyeOffsetPosition; } + const glm::quat& getEyeOffsetOrientation () { return _eyeOffsetOrientation; } + + bool getFrustumNeedsReshape(); // call to find out if the view frustum needs to be reshaped + void setFrustumWasReshaped(); // call this after reshaping the view frustum. private: @@ -79,12 +74,8 @@ private: float _farClip; glm::vec3 _eyeOffsetPosition; glm::quat _eyeOffsetOrientation; - float _yaw; - float _pitch; - float _roll; - float _idealYaw; - float _idealPitch; - float _idealRoll; + glm::quat _rotation; + glm::quat _targetRotation; float _upShift; float _distance; float _tightness; @@ -94,12 +85,10 @@ private: float _newUpShift; float _newDistance; float _newTightness; - Orientation _orientation; float _modeShift; float _linearModeShift; float _modeShiftRate; - void generateOrientation(); void updateFollowMode( float deltaTime ); }; diff --git a/interface/src/Environment.cpp b/interface/src/Environment.cpp index fec4ba0529..f762aaeed1 100644 --- a/interface/src/Environment.cpp +++ b/interface/src/Environment.cpp @@ -71,7 +71,8 @@ glm::vec3 Environment::getGravity (const glm::vec3& position) { foreach (const ServerData& serverData, _data) { foreach (const EnvironmentData& environmentData, serverData) { glm::vec3 vector = environmentData.getAtmosphereCenter() - position; - if (glm::length(vector) < environmentData.getAtmosphereOuterRadius()) { + const float GRAVITY_RADIUS_MULTIPLIER = 1.5f; + if (glm::length(vector) < environmentData.getAtmosphereOuterRadius() * GRAVITY_RADIUS_MULTIPLIER) { gravity += glm::normalize(vector) * environmentData.getGravity(); } } diff --git a/interface/src/Head.cpp b/interface/src/Head.cpp index 51d85db5ab..d1ee699dc8 100644 --- a/interface/src/Head.cpp +++ b/interface/src/Head.cpp @@ -4,6 +4,8 @@ // // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +#include +#include "Avatar.h" #include "Head.h" #include "Util.h" #include @@ -41,6 +43,7 @@ vector irisTexture; Head::Head(Avatar* owningAvatar) : HeadData((AvatarData*)owningAvatar), yawRate(0.0f), + _renderAlpha(0.0), _returnHeadToCenter(false), _skinColor(0.0f, 0.0f, 0.0f), _position(0.0f, 0.0f, 0.0f), @@ -61,6 +64,7 @@ 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) @@ -83,13 +87,14 @@ void Head::reset() { void Head::resetHairPhysics() { + glm::vec3 up = getUpDirection(); for (int t = 0; t < NUM_HAIR_TUFTS; t ++) { _hairTuft[t].length = HAIR_LENGTH; _hairTuft[t].thickness = HAIR_THICKNESS; - _hairTuft[t].basePosition = _position + _orientation.getUp() * _scale * 0.9f; - _hairTuft[t].midPosition = _hairTuft[t].basePosition + _orientation.getUp() * _hairTuft[t].length * ONE_HALF; - _hairTuft[t].endPosition = _hairTuft[t].midPosition + _orientation.getUp() * _hairTuft[t].length * ONE_HALF; + _hairTuft[t].basePosition = _position + up * _scale * 0.9f; + _hairTuft[t].midPosition = _hairTuft[t].basePosition + up * _hairTuft[t].length * ONE_HALF; + _hairTuft[t].endPosition = _hairTuft[t].midPosition + up * _hairTuft[t].length * ONE_HALF; _hairTuft[t].midVelocity = glm::vec3(0.0f, 0.0f, 0.0f); _hairTuft[t].endVelocity = glm::vec3(0.0f, 0.0f, 0.0f); } @@ -155,7 +160,7 @@ void Head::determineIfLookingAtSomething() { _lookingAtSomething = false; } else { glm::vec3 targetLookatAxis = glm::normalize(_lookAtPosition - caclulateAverageEyePosition()); - float dot = glm::dot(targetLookatAxis, _orientation.getFront()); + float dot = glm::dot(targetLookatAxis, getFrontDirection()); if (dot < MINIMUM_EYE_ROTATION_DOT) { // too far off from center for the eyes to rotate _lookingAtSomething = false; } else { @@ -164,55 +169,48 @@ void Head::determineIfLookingAtSomething() { } } -void Head::calculateGeometry(bool lookingInMirror) { - //generate orientation directions based on Euler angles... - - float pitch = _pitch; - float yaw = _yaw; - float roll = _roll; - - if (lookingInMirror) { - yaw = -_yaw; - roll = -_roll; - } - - _orientation.setToIdentity(); - _orientation.roll (_bodyRotation.z + roll ); - _orientation.pitch(_bodyRotation.x + pitch); - _orientation.yaw (_bodyRotation.y + yaw ); +void Head::calculateGeometry() { + //generate orientation directions + glm::quat orientation = getOrientation(); + glm::vec3 right = orientation * AVATAR_RIGHT; + glm::vec3 up = orientation * AVATAR_UP; + glm::vec3 front = orientation * AVATAR_FRONT; //calculate the eye positions _leftEyePosition = _position - - _orientation.getRight() * _scale * EYE_RIGHT_OFFSET - + _orientation.getUp () * _scale * EYE_UP_OFFSET - + _orientation.getFront() * _scale * EYE_FRONT_OFFSET; + - right * _scale * EYE_RIGHT_OFFSET + + up * _scale * EYE_UP_OFFSET + + front * _scale * EYE_FRONT_OFFSET; _rightEyePosition = _position - + _orientation.getRight() * _scale * EYE_RIGHT_OFFSET - + _orientation.getUp () * _scale * EYE_UP_OFFSET - + _orientation.getFront() * _scale * EYE_FRONT_OFFSET; + + right * _scale * EYE_RIGHT_OFFSET + + up * _scale * EYE_UP_OFFSET + + front * _scale * EYE_FRONT_OFFSET; //calculate the eyebrow positions _leftEyeBrowPosition = _leftEyePosition; _rightEyeBrowPosition = _rightEyePosition; //calculate the ear positions - _leftEarPosition = _position - _orientation.getRight() * _scale * EAR_RIGHT_OFFSET; - _rightEarPosition = _position + _orientation.getRight() * _scale * EAR_RIGHT_OFFSET; + _leftEarPosition = _position - right * _scale * EAR_RIGHT_OFFSET; + _rightEarPosition = _position + right * _scale * EAR_RIGHT_OFFSET; //calculate the mouth position - _mouthPosition = _position + _orientation.getUp () * _scale * MOUTH_UP_OFFSET - + _orientation.getFront() * _scale; + _mouthPosition = _position + up * _scale * MOUTH_UP_OFFSET + + front * _scale; } -void Head::render(bool lookingInMirror, glm::vec3 cameraPosition) { +void Head::render(bool lookingInMirror, glm::vec3 cameraPosition, float alpha) { - calculateGeometry(lookingInMirror); + _renderAlpha = alpha; + _lookingInMirror = lookingInMirror; + + calculateGeometry(); glEnable(GL_DEPTH_TEST); glEnable(GL_RESCALE_NORMAL); - renderMohawk(lookingInMirror, cameraPosition); + renderMohawk(cameraPosition); renderHeadSphere(); renderEyeBalls(); renderEars(); @@ -256,7 +254,7 @@ void Head::createMohawk() { } } -void Head::renderMohawk(bool lookingInMirror, glm::vec3 cameraPosition) { +void Head::renderMohawk(glm::vec3 cameraPosition) { if (!_mohawkTriangleFan) { createMohawk(); @@ -294,8 +292,8 @@ void Head::renderMohawk(bool lookingInMirror, glm::vec3 cameraPosition) { } 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((_lookingInMirror ? (_bodyRotation.y - _yaw) : (_bodyRotation.y + _yaw)), 0, 1, 0); + glRotatef(_lookingInMirror ? _roll: -_roll, 0, 0, 1); glRotatef(-_pitch - _bodyRotation.x, 1, 0, 0); glBegin(GL_TRIANGLE_FAN); @@ -309,12 +307,22 @@ void Head::renderMohawk(bool lookingInMirror, glm::vec3 cameraPosition) { } } +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))); +} + +glm::quat Head::getWorldAlignedOrientation () const { + Avatar* owningAvatar = static_cast(_owningAvatar); + return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(_lookingInMirror ? + glm::vec3(_pitch, -_yaw, -_roll) : glm::vec3(_pitch, _yaw, _roll))); +} void Head::renderHeadSphere() { glPushMatrix(); glTranslatef(_position.x, _position.y, _position.z); //translate to head position glScalef(_scale, _scale, _scale); //scale to head size - glColor3f(_skinColor.x, _skinColor.y, _skinColor.z); + glColor4f(_skinColor.x, _skinColor.y, _skinColor.z, _renderAlpha); glutSolidSphere(1, 30, 30); glPopMatrix(); } @@ -322,13 +330,13 @@ void Head::renderHeadSphere() { void Head::renderEars() { glPushMatrix(); - glColor3f(_skinColor.x, _skinColor.y, _skinColor.z); + glColor4f(_skinColor.x, _skinColor.y, _skinColor.z, _renderAlpha); glTranslatef(_leftEarPosition.x, _leftEarPosition.y, _leftEarPosition.z); glutSolidSphere(0.02, 30, 30); glPopMatrix(); glPushMatrix(); - glColor3f(_skinColor.x, _skinColor.y, _skinColor.z); + glColor4f(_skinColor.x, _skinColor.y, _skinColor.z, _renderAlpha); glTranslatef(_rightEarPosition.x, _rightEarPosition.y, _rightEarPosition.z); glutSolidSphere(0.02, 30, 30); glPopMatrix(); @@ -338,9 +346,14 @@ void Head::renderMouth() { float s = sqrt(_averageLoudness); - glm::vec3 r = _orientation.getRight() * _scale * (0.30f + s * 0.0014f ); - glm::vec3 u = _orientation.getUp () * _scale * (0.05f + s * 0.0040f ); - glm::vec3 f = _orientation.getFront() * _scale * 0.09f; + glm::quat orientation = getOrientation(); + glm::vec3 right = orientation * AVATAR_RIGHT; + glm::vec3 up = orientation * AVATAR_UP; + glm::vec3 front = orientation * AVATAR_FRONT; + + glm::vec3 r = right * _scale * (0.30f + s * 0.0014f ); + glm::vec3 u = up * _scale * (0.05f + s * 0.0040f ); + glm::vec3 f = front * _scale * 0.09f; glm::vec3 middle = _mouthPosition; glm::vec3 leftCorner = _mouthPosition - r * 1.0f; @@ -351,7 +364,9 @@ void Head::renderMouth() { glm::vec3 rightBottom = _mouthPosition + r * 0.4f - u * 1.0f + f * 0.7f; // constrain all mouth vertices to a sphere slightly larger than the head... - float constrainedRadius = _scale + 0.001f; + const float MOUTH_OFFSET_OFF_FACE = 0.003f; + + float constrainedRadius = _scale + MOUTH_OFFSET_OFF_FACE; middle = _position + glm::normalize(middle - _position) * constrainedRadius; leftCorner = _position + glm::normalize(leftCorner - _position) * constrainedRadius; rightCorner = _position + glm::normalize(rightCorner - _position) * constrainedRadius; @@ -398,11 +413,16 @@ void Head::renderEyeBrows() { glm::vec3 rightTop = _leftEyePosition; glm::vec3 leftBottom = _leftEyePosition; glm::vec3 rightBottom = _leftEyePosition; - - glm::vec3 r = _orientation.getRight() * length; - glm::vec3 u = _orientation.getUp() * height; - glm::vec3 t = _orientation.getUp() * (height + width); - glm::vec3 f = _orientation.getFront() * _scale * -0.1f; + + glm::quat orientation = getOrientation(); + glm::vec3 right = orientation * AVATAR_RIGHT; + glm::vec3 up = orientation * AVATAR_UP; + glm::vec3 front = orientation * AVATAR_FRONT; + + glm::vec3 r = right * length; + glm::vec3 u = up * height; + glm::vec3 t = up * (height + width); + glm::vec3 f = front * _scale * -0.1f; for (int i = 0; i < 2; i++) { @@ -470,6 +490,8 @@ void Head::renderEyeBalls() { gluSphere(irisQuadric, EYEBALL_RADIUS, 30, 30); glPopMatrix(); + glm::vec3 front = getFrontDirection(); + // render left iris glPushMatrix(); { glTranslatef(_leftEyePosition.x, _leftEyePosition.y, _leftEyePosition.z); //translate to eyeball position @@ -480,20 +502,20 @@ void Head::renderEyeBalls() { //rotate the eyeball to aim towards the lookat position glm::vec3 targetLookatAxis = glm::normalize(_lookAtPosition - _leftEyePosition); // the lookat direction - glm::vec3 rotationAxis = glm::cross(targetLookatAxis, IDENTITY_UP); - float angle = 180.0f - angleBetween(targetLookatAxis, IDENTITY_UP); + glm::vec3 rotationAxis = glm::cross(targetLookatAxis, AVATAR_UP); + float angle = 180.0f - angleBetween(targetLookatAxis, AVATAR_UP); glRotatef(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z); glRotatef(180.0, 0.0f, 1.0f, 0.0f); //adjust roll to correct after previous rotations } else { //rotate the eyeball to aim straight ahead - glm::vec3 rotationAxisToHeadFront = glm::cross(_orientation.getFront(), IDENTITY_UP); - float angleToHeadFront = 180.0f - angleBetween(_orientation.getFront(), IDENTITY_UP); + glm::vec3 rotationAxisToHeadFront = glm::cross(front, AVATAR_UP); + float angleToHeadFront = 180.0f - angleBetween(front, AVATAR_UP); glRotatef(angleToHeadFront, rotationAxisToHeadFront.x, rotationAxisToHeadFront.y, rotationAxisToHeadFront.z); //set the amount of roll (for correction after previous rotations) - float rollRotation = angleBetween(_orientation.getFront(), IDENTITY_FRONT); - float dot = glm::dot(_orientation.getFront(), -IDENTITY_RIGHT); + float rollRotation = angleBetween(front, AVATAR_FRONT); + float dot = glm::dot(front, -AVATAR_RIGHT); if ( dot < 0.0f ) { rollRotation = -rollRotation; } glRotatef(rollRotation, 0.0f, 1.0f, 0.0f); //roll the iris or correct roll about the lookat vector } @@ -524,21 +546,21 @@ void Head::renderEyeBalls() { //rotate the eyeball to aim towards the lookat position glm::vec3 targetLookatAxis = glm::normalize(_lookAtPosition - _rightEyePosition); - glm::vec3 rotationAxis = glm::cross(targetLookatAxis, IDENTITY_UP); - float angle = 180.0f - angleBetween(targetLookatAxis, IDENTITY_UP); + glm::vec3 rotationAxis = glm::cross(targetLookatAxis, AVATAR_UP); + float angle = 180.0f - angleBetween(targetLookatAxis, AVATAR_UP); glRotatef(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z); glRotatef(180.0f, 0.0f, 1.0f, 0.0f); //adjust roll to correct after previous rotations } else { //rotate the eyeball to aim straight ahead - glm::vec3 rotationAxisToHeadFront = glm::cross(_orientation.getFront(), IDENTITY_UP); - float angleToHeadFront = 180.0f - angleBetween(_orientation.getFront(), IDENTITY_UP); + glm::vec3 rotationAxisToHeadFront = glm::cross(front, AVATAR_UP); + float angleToHeadFront = 180.0f - angleBetween(front, AVATAR_UP); glRotatef(angleToHeadFront, rotationAxisToHeadFront.x, rotationAxisToHeadFront.y, rotationAxisToHeadFront.z); //set the amount of roll (for correction after previous rotations) - float rollRotation = angleBetween(_orientation.getFront(), IDENTITY_FRONT); - float dot = glm::dot(_orientation.getFront(), -IDENTITY_RIGHT); + float rollRotation = angleBetween(front, AVATAR_FRONT); + float dot = glm::dot(front, -AVATAR_RIGHT); if ( dot < 0.0f ) { rollRotation = -rollRotation; } glRotatef(rollRotation, 0.0f, 1.0f, 0.0f); //roll the iris or correct roll about the lookat vector } @@ -573,6 +595,11 @@ void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosi void Head::updateHairPhysics(float deltaTime) { + glm::quat orientation = getOrientation(); + glm::vec3 right = orientation * AVATAR_RIGHT; + glm::vec3 up = orientation * AVATAR_UP; + glm::vec3 front = orientation * AVATAR_FRONT; + for (int t = 0; t < NUM_HAIR_TUFTS; t ++) { float fraction = (float)t / (float)(NUM_HAIR_TUFTS - 1); @@ -581,8 +608,8 @@ void Head::updateHairPhysics(float deltaTime) { float radian = angle * PI_OVER_180; glm::vec3 baseDirection - = _orientation.getFront() * sinf(radian) - + _orientation.getUp() * cosf(radian); + = front * sinf(radian) + + up * cosf(radian); _hairTuft[t].basePosition = _position + _scale * 0.9f * baseDirection; @@ -598,13 +625,13 @@ void Head::updateHairPhysics(float deltaTime) { if (midLength > 0.0f) { midDirection = midAxis / midLength; } else { - midDirection = _orientation.getUp(); + midDirection = up; } if (endLength > 0.0f) { endDirection = endAxis / endLength; } else { - endDirection = _orientation.getUp(); + endDirection = up; } // add spring force @@ -649,13 +676,13 @@ void Head::updateHairPhysics(float deltaTime) { if (newMidLength > 0.0f) { newMidDirection = newMidVector/newMidLength; } else { - newMidDirection = _orientation.getUp(); + newMidDirection = up; } if (newEndLength > 0.0f) { newEndDirection = newEndVector/newEndLength; } else { - newEndDirection = _orientation.getUp(); + newEndDirection = up; } _hairTuft[t].endPosition = _hairTuft[t].midPosition + newEndDirection * _hairTuft[t].length * ONE_HALF; diff --git a/interface/src/Head.h b/interface/src/Head.h index 07d7351cb3..f49e127caf 100644 --- a/interface/src/Head.h +++ b/interface/src/Head.h @@ -14,7 +14,6 @@ #include "world.h" #include "InterfaceConfig.h" #include "SerialInterface.h" -#include "Orientation.h" #include enum eyeContactTargets @@ -34,8 +33,8 @@ public: void reset(); void simulate(float deltaTime, bool isMine); - void render(bool lookingInMirror, glm::vec3 cameraPosition); - void renderMohawk(bool lookingInMirror, glm::vec3 cameraPosition); + void render(bool lookingInMirror, glm::vec3 cameraPosition, float alpha); + void renderMohawk(glm::vec3 cameraPosition); void setScale (float scale ) { _scale = scale; } void setPosition (glm::vec3 position ) { _position = position; } @@ -46,7 +45,14 @@ public: void setAverageLoudness(float averageLoudness ) { _averageLoudness = averageLoudness; } void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; } void setRenderLookatVectors(bool onOff ) { _renderLookatVectors = onOff; } - + + glm::quat getOrientation() const; + glm::quat getWorldAlignedOrientation () const; + + glm::vec3 getRightDirection() const { return getOrientation() * AVATAR_RIGHT; } + glm::vec3 getUpDirection () const { return getOrientation() * AVATAR_UP; } + glm::vec3 getFrontDirection() const { return getOrientation() * AVATAR_FRONT; } + const bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected) float getAverageLoudness() {return _averageLoudness;}; glm::vec3 caclulateAverageEyePosition() { return _leftEyePosition + (_rightEyePosition - _leftEyePosition ) * ONE_HALF; } @@ -71,6 +77,7 @@ private: glm::vec3 endVelocity; }; + float _renderAlpha; bool _returnHeadToCenter; glm::vec3 _skinColor; glm::vec3 _position; @@ -90,8 +97,8 @@ private: float _averageLoudness; float _audioAttack; float _returnSpringScale; //strength of return springs - Orientation _orientation; glm::vec3 _bodyRotation; + bool _lookingInMirror; bool _renderLookatVectors; HairTuft _hairTuft[NUM_HAIR_TUFTS]; glm::vec3* _mohawkTriangleFan; @@ -105,7 +112,7 @@ private: void renderEars(); void renderMouth(); void renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition); - void calculateGeometry( bool lookingInMirror); + void calculateGeometry(); void determineIfLookingAtSomething(); void resetHairPhysics(); void updateHairPhysics(float deltaTime); diff --git a/interface/src/Transmitter.cpp b/interface/src/Transmitter.cpp index 5f6def92f7..d44227982f 100644 --- a/interface/src/Transmitter.cpp +++ b/interface/src/Transmitter.cpp @@ -20,11 +20,25 @@ Transmitter::Transmitter() : _isConnected(false), _lastRotationRate(0,0,0), _lastAcceleration(0,0,0), - _estimatedRotation(0,0,0) + _estimatedRotation(0,0,0), + _lastReceivedPacket(NULL) { } +void Transmitter::checkForLostTransmitter() { + // If we are in motion, check for loss of transmitter packets + if (glm::length(_estimatedRotation) > 0.f) { + timeval now; + gettimeofday(&now, NULL); + const int TIME_TO_ASSUME_LOST_MSECS = 2000; + int msecsSinceLast = diffclock(_lastReceivedPacket, &now); + if (msecsSinceLast > TIME_TO_ASSUME_LOST_MSECS) { + resetLevels(); + printLog("Transmitter signal lost.\n"); + } + } +} void Transmitter::resetLevels() { _lastRotationRate *= 0.f; _estimatedRotation *= 0.f; @@ -34,6 +48,11 @@ void Transmitter::processIncomingData(unsigned char* packetData, int numBytes) { const int PACKET_HEADER_SIZE = 1; // Packet's first byte is 'T' const int ROTATION_MARKER_SIZE = 1; // 'R' = Rotation (clockwise about x,y,z) const int ACCELERATION_MARKER_SIZE = 1; // 'A' = Acceleration (x,y,z) + if (!_lastReceivedPacket) { + _lastReceivedPacket = new timeval; + } + gettimeofday(_lastReceivedPacket, NULL); + if (numBytes == PACKET_HEADER_SIZE + ROTATION_MARKER_SIZE + ACCELERATION_MARKER_SIZE + sizeof(_lastRotationRate) + sizeof(_lastAcceleration) + sizeof(_touchState.x) + sizeof(_touchState.y) + sizeof(_touchState.state)) { @@ -69,12 +88,12 @@ void Transmitter::processIncomingData(unsigned char* packetData, int numBytes) { _estimatedRotation.y *= (1.f - DECAY_RATE * DELTA_TIME); if (!_isConnected) { - printf("Transmitter V2 Connected.\n"); + printLog("Transmitter Connected.\n"); _isConnected = true; _estimatedRotation *= 0.0; } } else { - printf("Transmitter V2 packet read error, %d bytes.\n", numBytes); + printLog("Transmitter packet read error, %d bytes.\n", numBytes); } } diff --git a/interface/src/Transmitter.h b/interface/src/Transmitter.h index 0db0762ab1..95d80249e8 100644 --- a/interface/src/Transmitter.h +++ b/interface/src/Transmitter.h @@ -26,6 +26,7 @@ class Transmitter public: Transmitter(); void render(); + void checkForLostTransmitter(); void resetLevels(); void renderLevels(int width, int height); bool isConnected() { return _isConnected; }; @@ -41,6 +42,7 @@ private: glm::vec3 _lastAcceleration; glm::vec3 _estimatedRotation; TouchState _touchState; + timeval* _lastReceivedPacket; #endif /* defined(__hifi__Transmitter__) */ }; diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 2df1196a86..05dfc50502 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -12,7 +12,8 @@ #include #include -#include +#include +#include #include #include "Log.h" @@ -71,6 +72,65 @@ float angleBetween(const glm::vec3& v1, const glm::vec3& v2) { return acos((glm::dot(v1, v2)) / (glm::length(v1) * glm::length(v2))) * 180.f / PI; } +// Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's +// http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde, +// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java) +glm::vec3 safeEulerAngles(const glm::quat& q) { + float sy = 2.0f * (q.y * q.w - q.x * q.z); + if (sy < 1.0f - EPSILON) { + if (sy > -1.0f + EPSILON) { + return glm::degrees(glm::vec3( + atan2f(q.y * q.z + q.x * q.w, 0.5f - (q.x * q.x + q.y * q.y)), + asinf(sy), + atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z)))); + + } else { + // not a unique solution; x + z = atan2(-m21, m11) + return glm::degrees(glm::vec3( + 0.0f, + PIf * -0.5f, + atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)))); + } + } else { + // not a unique solution; x - z = atan2(-m21, m11) + return glm::degrees(glm::vec3( + 0.0f, + PIf * 0.5f, + -atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)))); + } +} + +// Safe version of glm::mix; based on the code in Nick Bobick's article, +// http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde, +// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java) +glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) { + float cosa = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w; + float ox = q2.x, oy = q2.y, oz = q2.z, ow = q2.w, s0, s1; + + // adjust signs if necessary + if (cosa < 0.0f) { + cosa = -cosa; + ox = -ox; + oy = -oy; + oz = -oz; + ow = -ow; + } + + // calculate coefficients; if the angle is too close to zero, we must fall back + // to linear interpolation + if ((1.0f - cosa) > EPSILON) { + float angle = acosf(cosa), sina = sinf(angle); + s0 = sinf((1.0f - proportion) * angle) / sina; + s1 = sinf(proportion * angle) / sina; + + } else { + s0 = 1.0f - proportion; + s1 = proportion; + } + + return glm::normalize(glm::quat(s0 * q1.w + s1 * ow, s0 * q1.x + s1 * ox, s0 * q1.y + s1 * oy, s0 * q1.z + s1 * oz)); +} + // Draw a 3D vector floating in space void drawVector(glm::vec3 * vector) { glDisable(GL_LIGHTING); @@ -333,10 +393,10 @@ void renderCircle(glm::vec3 position, float radius, glm::vec3 surfaceNormal, int } -void renderOrientationDirections(glm::vec3 position, Orientation orientation, float size) { - glm::vec3 pRight = position + orientation.getRight() * size; - glm::vec3 pUp = position + orientation.getUp () * size; - glm::vec3 pFront = position + orientation.getFront() * size; +void renderOrientationDirections(glm::vec3 position, const glm::quat& orientation, float size) { + glm::vec3 pRight = position + orientation * AVATAR_RIGHT * size; + glm::vec3 pUp = position + orientation * AVATAR_UP * size; + glm::vec3 pFront = position + orientation * AVATAR_FRONT * size; glColor3f(1.0f, 0.0f, 0.0f); glBegin(GL_LINE_STRIP); @@ -364,126 +424,5 @@ bool closeEnoughForGovernmentWork(float a, float b) { } -void testOrientationClass() { - printLog("\n----------\ntestOrientationClass()\n----------\n\n"); - - oTestCase tests[] = { - // - inputs ------------, outputs -------------------- ------------------- ---------------------------- - // -- front -------------------, -- up -------------, -- right ------------------- - // ( yaw , pitch, roll , front.x , front.y , front.z , up.x , up.y , up.z , right.x , right.y , right.z ) - - // simple yaw tests - oTestCase( 0.f , 0.f , 0.f , 0.f , 0.f , 1.0f , 0.f , 1.0f , 0.f , -1.0f , 0.f , 0.f ), - oTestCase(45.0f , 0.f , 0.f , 0.707107f , 0.f , 0.707107f , 0.f , 1.0f , 0.f , -0.707107f, 0.f , 0.707107f), - oTestCase( 90.0f, 0.f , 0.f , 1.0f , 0.f , 0.f , 0.f , 1.0f , 0.f , 0.0f , 0.f , 1.0f ), - oTestCase(135.0f, 0.f , 0.f , 0.707107f , 0.f ,-0.707107f , 0.f , 1.0f , 0.f , 0.707107f, 0.f , 0.707107f), - oTestCase(180.0f, 0.f , 0.f , 0.f , 0.f , -1.0f , 0.f , 1.0f , 0.f , 1.0f , 0.f , 0.f ), - oTestCase(225.0f, 0.f , 0.f , -0.707107f , 0.f ,-0.707107f , 0.f , 1.0f , 0.f , 0.707107f, 0.f , -0.707107f), - oTestCase(270.0f, 0.f , 0.f , -1.0f , 0.f , 0.f , 0.f , 1.0f , 0.f , 0.0f , 0.f , -1.0f ), - oTestCase(315.0f, 0.f , 0.f , -0.707107f , 0.f , 0.707107f , 0.f , 1.0f , 0.f , -0.707107f, 0.f , -0.707107f), - oTestCase(-45.0f, 0.f , 0.f , -0.707107f , 0.f , 0.707107f , 0.f , 1.0f , 0.f , -0.707107f, 0.f , -0.707107f), - oTestCase(-90.0f, 0.f , 0.f , -1.0f , 0.f , 0.f , 0.f , 1.0f , 0.f , 0.0f , 0.f , -1.0f ), - oTestCase(-135.0f,0.f , 0.f , -0.707107f , 0.f ,-0.707107f , 0.f , 1.0f , 0.f , 0.707107f, 0.f , -0.707107f), - oTestCase(-180.0f,0.f , 0.f , 0.f , 0.f , -1.0f , 0.f , 1.0f , 0.f , 1.0f , 0.f , 0.f ), - oTestCase(-225.0f,0.f , 0.f , 0.707107f , 0.f ,-0.707107f , 0.f , 1.0f , 0.f , 0.707107f, 0.f , 0.707107f), - oTestCase(-270.0f,0.f , 0.f , 1.0f , 0.f , 0.f , 0.f , 1.0f , 0.f , 0.0f , 0.f , 1.0f ), - oTestCase(-315.0f,0.f , 0.f , 0.707107f , 0.f , 0.707107f , 0.f , 1.0f , 0.f , -0.707107f, 0.f , 0.707107f), - - // simple pitch tests - oTestCase( 0.f , 0.f , 0.f , 0.f, 0.f , 1.0f , 0.f , 1.0f , 0.f , -1.0f , 0.f , 0.f ), - oTestCase( 0.f ,45.0f , 0.f , 0.f, 0.707107f , 0.707107f, 0.f ,0.707107f, -0.707107f, -1.0f , 0.f , 0.f ), - oTestCase( 0.f ,90.f , 0.f , 0.f, 1.0f , 0.0f , 0.f ,0.0f , -1.0f , -1.0f , 0.f , 0.f ), - oTestCase( 0.f ,135.0f, 0.f , 0.f, 0.707107f , -0.707107f, 0.f ,-0.707107f, -0.707107f, -1.0f , 0.f , 0.f ), - oTestCase( 0.f ,180.f , 0.f , 0.f, 0.0f ,-1.0f , 0.f ,-1.0f , 0.f , -1.0f , 0.f , 0.f ), - oTestCase( 0.f ,225.0f, 0.f , 0.f,-0.707107f , -0.707107f, 0.f ,-0.707107f, 0.707107f, -1.0f , 0.f , 0.f ), - oTestCase( 0.f ,270.f , 0.f , 0.f,-1.0f , 0.0f , 0.f ,0.0f , 1.0f , -1.0f , 0.f , 0.f ), - oTestCase( 0.f ,315.0f, 0.f , 0.f,-0.707107f , 0.707107f, 0.f , 0.707107f, 0.707107f, -1.0f , 0.f , 0.f ), - - // simple roll tests - oTestCase( 0.f , 0.f , 0.f , 0.f , 0.f , 1.0f , 0.f , 1.0f ,0.0f , -1.0f , 0.f , 0.0f ), - oTestCase( 0.f , 0.f ,45.0f , 0.f , 0.f , 1.0f , 0.707107f , 0.707107f ,0.0f , -0.707107f, 0.707107f, 0.0f ), - oTestCase( 0.f , 0.f ,90.f , 0.f , 0.f , 1.0f , 1.0f , 0.0f ,0.0f , 0.0f , 1.0f , 0.0f ), - oTestCase( 0.f , 0.f ,135.0f , 0.f , 0.f , 1.0f , 0.707107f , -0.707107f,0.0f , 0.707107f , 0.707107f, 0.0f ), - oTestCase( 0.f , 0.f ,180.f , 0.f , 0.f , 1.0f , 0.0f , -1.0f ,0.0f , 1.0f , 0.0f , 0.0f ), - oTestCase( 0.f , 0.f ,225.0f , 0.f , 0.f , 1.0f , -0.707107f, -0.707107f,0.0f , 0.707107f ,-0.707107f, 0.0f ), - oTestCase( 0.f , 0.f ,270.f , 0.f , 0.f , 1.0f , -1.0f , 0.0f ,0.0f , 0.0f , -1.0f , 0.0f ), - oTestCase( 0.f , 0.f ,315.0f , 0.f , 0.f , 1.0f , -0.707107f, 0.707107f ,0.0f , -0.707107f,-0.707107f, 0.0f ), - - // yaw combo tests - oTestCase( 90.f , 90.f , 0.f , 0.f , 1.0f , 0.0f , -1.0f , 0.0f , 0.f , 0.0f , 0.f , 1.0f ), - oTestCase( 90.f , 0.f , 90.f , 1.0f , 0.0f, 0.f , 0.0f , 0.0f , -1.f , 0.0f , 1.0f , 0.0f ), - }; - - int failedCount = 0; - int totalTests = sizeof(tests)/sizeof(oTestCase); - - for (int i=0; i < totalTests; i++) { - - bool passed = true; // I'm an optimist! - - float yaw = tests[i].yaw; - float pitch = tests[i].pitch; - float roll = tests[i].roll; - - Orientation o1; - o1.setToIdentity(); - o1.yaw(yaw); - o1.pitch(pitch); - o1.roll(roll); - - glm::vec3 front = o1.getFront(); - glm::vec3 up = o1.getUp(); - glm::vec3 right = o1.getRight(); - - printLog("\n-----\nTest: %d - yaw=%f , pitch=%f , roll=%f \n",i+1,yaw,pitch,roll); - - printLog("\nFRONT\n"); - printLog(" + received: front.x=%f, front.y=%f, front.z=%f\n",front.x,front.y,front.z); - - if (closeEnoughForGovernmentWork(front.x, tests[i].frontX) - && closeEnoughForGovernmentWork(front.y, tests[i].frontY) - && closeEnoughForGovernmentWork(front.z, tests[i].frontZ)) { - printLog(" front vector PASSES!\n"); - } else { - printLog(" expected: front.x=%f, front.y=%f, front.z=%f\n",tests[i].frontX,tests[i].frontY,tests[i].frontZ); - printLog(" front vector FAILED! \n"); - passed = false; - } - - printLog("\nUP\n"); - printLog(" + received: up.x=%f, up.y=%f, up.z=%f\n",up.x,up.y,up.z); - if (closeEnoughForGovernmentWork(up.x, tests[i].upX) - && closeEnoughForGovernmentWork(up.y, tests[i].upY) - && closeEnoughForGovernmentWork(up.z, tests[i].upZ)) { - printLog(" up vector PASSES!\n"); - } else { - printLog(" expected: up.x=%f, up.y=%f, up.z=%f\n",tests[i].upX,tests[i].upY,tests[i].upZ); - printLog(" up vector FAILED!\n"); - passed = false; - } - - - printLog("\nRIGHT\n"); - printLog(" + received: right.x=%f, right.y=%f, right.z=%f\n",right.x,right.y,right.z); - if (closeEnoughForGovernmentWork(right.x, tests[i].rightX) - && closeEnoughForGovernmentWork(right.y, tests[i].rightY) - && closeEnoughForGovernmentWork(right.z, tests[i].rightZ)) { - printLog(" right vector PASSES!\n"); - } else { - printLog(" expected: right.x=%f, right.y=%f, right.z=%f\n",tests[i].rightX,tests[i].rightY,tests[i].rightZ); - printLog(" right vector FAILED!\n"); - passed = false; - } - - if (!passed) { - printLog("\n-----\nTest: %d - FAILED! \n----------\n\n",i+1); - failedCount++; - } - } - printLog("\n-----\nTotal Failed: %d out of %d \n----------\n\n",failedCount,totalTests); - printLog("\n----------DONE----------\n\n"); -} - - diff --git a/interface/src/Util.h b/interface/src/Util.h index 3264039067..0823ac405b 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -16,8 +16,7 @@ #endif #include - -#include +#include // the standard sans serif font family #define SANS_FONT_FAMILY "Helvetica" @@ -46,50 +45,19 @@ void drawVector(glm::vec3* vector); float angleBetween(const glm::vec3& v1, const glm::vec3& v2); +glm::vec3 safeEulerAngles(const glm::quat& q); + +glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha); + double diffclock(timeval *clock1,timeval *clock2); void drawGroundPlaneGrid(float size); void renderDiskShadow(glm::vec3 position, glm::vec3 upDirection, float radius, float darkness); -void renderOrientationDirections( glm::vec3 position, Orientation orientation, float size ); +void renderOrientationDirections( glm::vec3 position, const glm::quat& orientation, float size ); void renderSphereOutline(glm::vec3 position, float radius, int numSides, glm::vec3 cameraPosition); void renderCircle(glm::vec3 position, float radius, glm::vec3 surfaceNormal, int numSides ); - -class oTestCase { -public: - float yaw; - float pitch; - float roll; - - float frontX; - float frontY; - float frontZ; - - float upX; - float upY; - float upZ; - - float rightX; - float rightY; - float rightZ; - - oTestCase( - float yaw, float pitch, float roll, - float frontX, float frontY, float frontZ, - float upX, float upY, float upZ, - float rightX, float rightY, float rightZ - ) : - yaw(yaw),pitch(pitch),roll(roll), - frontX(frontX),frontY(frontY),frontZ(frontZ), - upX(upX),upY(upY),upZ(upZ), - rightX(rightX),rightY(rightY),rightZ(rightZ) - {}; -}; - - -void testOrientationClass(); - #endif diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 359e5b77af..b99519e845 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -899,11 +899,14 @@ bool VoxelSystem::removeOutOfViewOperation(VoxelNode* node, void* extraData) { bool VoxelSystem::isViewChanging() { bool result = false; // assume the best + +/** TEMPORARY HACK ****** // If our viewFrustum has changed since our _lastKnowViewFrustum if (_viewFrustum && !_lastKnowViewFrustum.matches(_viewFrustum)) { result = true; _lastKnowViewFrustum = *_viewFrustum; // save last known } +**/ return result; } diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index 299ffcc2cb..006dd825bf 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -7,6 +7,7 @@ // #include +#include #include "PacketHeaders.h" @@ -56,7 +57,12 @@ int AudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) { memcpy(&_bearing, dataBuffer, sizeof(float)); dataBuffer += sizeof(_bearing); - if (_bearing > 180 || _bearing < -180) { + // if this agent sent us a NaN bearing then don't consider this good audio and bail + if (std::isnan(_bearing)) { + _endOfLastWrite = _nextOutput = _buffer; + _started = false; + return 0; + } else if (_bearing > 180 || _bearing < -180) { // we were passed an invalid bearing because this agent wants loopback (pressed the H key) _shouldLoopbackForAgent = true; @@ -66,7 +72,7 @@ int AudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) { : _bearing + AGENT_LOOPBACK_MODIFIER; } else { _shouldLoopbackForAgent = false; - } + } } // make sure we have enough bytes left for this to be the right amount of audio diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 18bbacf810..49a31a7e6a 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -20,6 +20,11 @@ const int WANT_RESIN_AT_BIT = 0; const int WANT_COLOR_AT_BIT = 1; const int WANT_DELTA_AT_BIT = 2; +// this is where the coordinate system is represented +const glm::vec3 AVATAR_RIGHT = glm::vec3(1.0f, 0.0f, 0.0f); +const glm::vec3 AVATAR_UP = glm::vec3(0.0f, 1.0f, 0.0f); +const glm::vec3 AVATAR_FRONT = glm::vec3(0.0f, 0.0f, -1.0f); + enum KeyState { NO_KEY_DOWN, diff --git a/libraries/avatars/src/Orientation.cpp b/libraries/avatars/src/Orientation.cpp deleted file mode 100755 index cae0f70201..0000000000 --- a/libraries/avatars/src/Orientation.cpp +++ /dev/null @@ -1,121 +0,0 @@ -//----------------------------------------------------------- -// -// Created by Jeffrey Ventrella -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -//----------------------------------------------------------- - -#include "Orientation.h" -#include "SharedUtil.h" - -static const bool USING_QUATERNIONS = true; - -Orientation::Orientation() { - setToIdentity(); -} - -void Orientation::setToIdentity() { - - quat = glm::quat(); - right = glm::vec3(IDENTITY_RIGHT); - up = glm::vec3(IDENTITY_UP ); - front = glm::vec3(IDENTITY_FRONT); -} - -void Orientation::set(Orientation o) { - - quat = o.quat; - right = o.right; - up = o.up; - front = o.front; -} - -void Orientation::yaw(float angle) { - - float radian = angle * PI_OVER_180; - - if (USING_QUATERNIONS) { - rotateAndGenerateDirections(glm::quat(glm::vec3(0.0f, -radian, 0.0f))); - } else { - float s = sin(radian); - float c = cos(radian); - - glm::vec3 cosineFront = front * c; - glm::vec3 cosineRight = right * c; - glm::vec3 sineFront = front * s; - glm::vec3 sineRight = right * s; - - front = cosineFront - sineRight; - right = cosineRight + sineFront; - } -} - -void Orientation::pitch(float angle) { - - float radian = angle * PI_OVER_180; - - if (USING_QUATERNIONS) { - rotateAndGenerateDirections(glm::quat(glm::vec3(radian, 0.0f, 0.0f))); - } else { - float s = sin(radian); - float c = cos(radian); - - glm::vec3 cosineUp = up * c; - glm::vec3 cosineFront = front * c; - glm::vec3 sineUp = up * s; - glm::vec3 sineFront = front * s; - - up = cosineUp - sineFront; - front = cosineFront + sineUp; - } -} - -void Orientation::roll(float angle) { - - float radian = angle * PI_OVER_180; - - if (USING_QUATERNIONS) { - rotateAndGenerateDirections(glm::quat(glm::vec3(0.0f, 0.0f, radian))); - } else { - float s = sin(radian); - float c = cos(radian); - - glm::vec3 cosineUp = up * c; - glm::vec3 cosineRight = right * c; - glm::vec3 sineUp = up * s; - glm::vec3 sineRight = right * s; - - up = cosineUp - sineRight; - right = cosineRight + sineUp; - } -} - -void Orientation::rotate(float pitch_change, float yaw_change, float roll_change) { - pitch(pitch_change); - yaw (yaw_change); - roll (roll_change); -} - -void Orientation::rotate(glm::vec3 eulerAngles) { - -//this needs to be optimized! - pitch(eulerAngles.x); - yaw (eulerAngles.y); - roll (eulerAngles.z); -} - -void Orientation::rotate( glm::quat rotation ) { - rotateAndGenerateDirections(rotation); -} - - -void Orientation::rotateAndGenerateDirections(glm::quat rotation) { - - quat = quat * rotation; - - glm::mat4 rotationMatrix = glm::mat4_cast(quat); - - right = glm::vec3(glm::vec4(IDENTITY_RIGHT, 0.0f) * rotationMatrix); - up = glm::vec3(glm::vec4(IDENTITY_UP, 0.0f) * rotationMatrix); - front = glm::vec3(glm::vec4(IDENTITY_FRONT, 0.0f) * rotationMatrix); -} diff --git a/libraries/avatars/src/Orientation.h b/libraries/avatars/src/Orientation.h deleted file mode 100644 index 56209db934..0000000000 --- a/libraries/avatars/src/Orientation.h +++ /dev/null @@ -1,55 +0,0 @@ -//----------------------------------------------------------- -// -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -//----------------------------------------------------------- - -#ifndef __interface__orientation__ -#define __interface__orientation__ - -#include -#include -#include - -// this is where the coordinate system is represented -const glm::vec3 IDENTITY_RIGHT = glm::vec3(-1.0f, 0.0f, 0.0f); -const glm::vec3 IDENTITY_UP = glm::vec3( 0.0f, 1.0f, 0.0f); -const glm::vec3 IDENTITY_FRONT = glm::vec3( 0.0f, 0.0f, 1.0f); - -class Orientation -{ -public: - Orientation(); - - void set(Orientation); - void setToIdentity(); - - void pitch(float pitch_change); - void yaw (float yaw_change); - void roll (float roll_change); - - void rotate(float pitch, float yaw, float roll); - void rotate(glm::vec3 EulerAngles); - void rotate(glm::quat quaternion); - - const glm::quat& getQuat() const {return quat;} - - const glm::vec3& getRight() const {return right;} - const glm::vec3& getUp () const {return up; } - const glm::vec3& getFront() const {return front;} - - const glm::vec3& getIdentityRight() const {return IDENTITY_RIGHT;} - const glm::vec3& getIdentityUp () const {return IDENTITY_UP;} - const glm::vec3& getIdentityFront() const {return IDENTITY_FRONT;} - -private: - - glm::quat quat; - glm::vec3 right; - glm::vec3 up; - glm::vec3 front; - - void rotateAndGenerateDirections(glm::quat rotation); -}; - -#endif diff --git a/libraries/shared/src/AgentList.cpp b/libraries/shared/src/AgentList.cpp index 170b86147b..2b28ac620d 100644 --- a/libraries/shared/src/AgentList.cpp +++ b/libraries/shared/src/AgentList.cpp @@ -250,7 +250,8 @@ Agent* AgentList::addOrUpdateAgent(sockaddr* publicSocket, sockaddr* localSocket if (agent->getType() == AGENT_TYPE_AUDIO_MIXER || agent->getType() == AGENT_TYPE_VOXEL || - agent->getType() == AGENT_TYPE_ANIMATION_SERVER) { + agent->getType() == AGENT_TYPE_ANIMATION_SERVER || + agent->getType() == AGENT_TYPE_AUDIO_INJECTOR) { // until the Audio class also uses our agentList, we need to update // the lastRecvTimeUsecs for the audio mixer so it doesn't get killed and re-added continously agent->setLastHeardMicrostamp(usecTimestampNow());