Merge branch 'master' of https://github.com/worklist/hifi into copy_and_paste_voxels

This commit is contained in:
ZappoMan 2013-05-30 21:41:55 -07:00
commit b0397e8eb9
29 changed files with 1263 additions and 919 deletions

View file

@ -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);

View file

@ -55,7 +55,7 @@ const float BUFFER_SEND_INTERVAL_USECS = (BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SA
const long MAX_SAMPLE_VALUE = std::numeric_limits<int16_t>::max();
const long MIN_SAMPLE_VALUE = std::numeric_limits<int16_t>::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;

View file

@ -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

View file

@ -204,6 +204,4 @@ int main(int argc, const char* argv[]) {
agentList->stopDomainServerCheckInThread();
agentList->stopPingUnknownAgentsThread();
agentList->stopSilentAgentRemovalThread();
}
}

View file

@ -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})
link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR})

View file

@ -14,14 +14,16 @@
#include <string.h>
#include <sstream>
#include <AgentList.h>
#include <AgentTypes.h>
#include <AvatarData.h>
#include <SharedUtil.h>
#include <PacketHeaders.h>
#include <UDPSocket.h>
#include <AudioInjector.h>
#include <AudioInjectionManager.h>
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;
}

View file

@ -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<std::string, std::string>::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<std::string, std::string>::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<std::string, std::string>::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<std::string, std::string>::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);
}

View file

@ -11,8 +11,10 @@
#include <pthread.h>
#include <time.h>
#include <map>
#include <QApplication>
#include <QAction>
#include <AgentList.h>
@ -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<std::string, std::string> _settingsTable;
};
#endif /* defined(__interface__Application__) */

View file

@ -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),

View file

@ -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;

View file

@ -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<float>::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<float>::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<NUM_AVATAR_JOINTS; b++) {
for (int b=0; b<NUM_AVATAR_JOINTS; b++) {
_joint[b].isCollidable = true;
_joint[b].parent = AVATAR_JOINT_NULL;
_joint[b].position = glm::vec3(0.0, 0.0, 0.0);
_joint[b].defaultPosePosition = glm::vec3(0.0, 0.0, 0.0);
_joint[b].springyPosition = glm::vec3(0.0, 0.0, 0.0);
_joint[b].springyVelocity = glm::vec3(0.0, 0.0, 0.0);
_joint[b].rotation = glm::quat(0.0f, 0.0f, 0.0f, 0.0f);
_joint[b].yaw = 0.0;
_joint[b].pitch = 0.0;
_joint[b].roll = 0.0;
_joint[b].orientation = glm::quat(0.0f, 0.0f, 0.0f, 1.0f);
_joint[b].length = 0.0;
_joint[b].radius = 0.0;
_joint[b].touchForce = 0.0;
_joint[b].springBodyTightness = BODY_SPRING_DEFAULT_TIGHTNESS;
_joint[b].orientation.setToIdentity();
}
// specify the parental hierarchy
_joint[ AVATAR_JOINT_PELVIS ].parent = AVATAR_JOINT_NULL;
_joint[ AVATAR_JOINT_PELVIS ].parent = AVATAR_JOINT_NULL;
_joint[ AVATAR_JOINT_TORSO ].parent = AVATAR_JOINT_PELVIS;
_joint[ AVATAR_JOINT_CHEST ].parent = AVATAR_JOINT_TORSO;
_joint[ AVATAR_JOINT_NECK_BASE ].parent = AVATAR_JOINT_CHEST;
_joint[ AVATAR_JOINT_CHEST ].parent = AVATAR_JOINT_TORSO;
_joint[ AVATAR_JOINT_NECK_BASE ].parent = AVATAR_JOINT_CHEST;
_joint[ AVATAR_JOINT_HEAD_BASE ].parent = AVATAR_JOINT_NECK_BASE;
_joint[ AVATAR_JOINT_HEAD_TOP ].parent = AVATAR_JOINT_HEAD_BASE;
_joint[ AVATAR_JOINT_LEFT_COLLAR ].parent = AVATAR_JOINT_CHEST;
_joint[ AVATAR_JOINT_LEFT_SHOULDER ].parent = AVATAR_JOINT_LEFT_COLLAR;
_joint[ AVATAR_JOINT_LEFT_ELBOW ].parent = AVATAR_JOINT_LEFT_SHOULDER;
_joint[ AVATAR_JOINT_LEFT_WRIST ].parent = AVATAR_JOINT_LEFT_ELBOW;
_joint[ AVATAR_JOINT_LEFT_ELBOW ].parent = AVATAR_JOINT_LEFT_SHOULDER;
_joint[ AVATAR_JOINT_LEFT_WRIST ].parent = AVATAR_JOINT_LEFT_ELBOW;
_joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].parent = AVATAR_JOINT_LEFT_WRIST;
_joint[ AVATAR_JOINT_RIGHT_COLLAR ].parent = AVATAR_JOINT_CHEST;
_joint[ AVATAR_JOINT_RIGHT_SHOULDER ].parent = AVATAR_JOINT_RIGHT_COLLAR;
_joint[ AVATAR_JOINT_RIGHT_ELBOW ].parent = AVATAR_JOINT_RIGHT_SHOULDER;
_joint[ AVATAR_JOINT_RIGHT_SHOULDER ].parent = AVATAR_JOINT_RIGHT_COLLAR;
_joint[ AVATAR_JOINT_RIGHT_ELBOW ].parent = AVATAR_JOINT_RIGHT_SHOULDER;
_joint[ AVATAR_JOINT_RIGHT_WRIST ].parent = AVATAR_JOINT_RIGHT_ELBOW;
_joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].parent = AVATAR_JOINT_RIGHT_WRIST;
_joint[ AVATAR_JOINT_LEFT_HIP ].parent = AVATAR_JOINT_PELVIS;
_joint[ AVATAR_JOINT_LEFT_KNEE ].parent = AVATAR_JOINT_LEFT_HIP;
_joint[ AVATAR_JOINT_LEFT_HEEL ].parent = AVATAR_JOINT_LEFT_KNEE;
_joint[ AVATAR_JOINT_LEFT_TOES ].parent = AVATAR_JOINT_LEFT_HEEL;
_joint[ AVATAR_JOINT_RIGHT_HIP ].parent = AVATAR_JOINT_PELVIS;
_joint[ AVATAR_JOINT_RIGHT_KNEE ].parent = AVATAR_JOINT_RIGHT_HIP;
_joint[ AVATAR_JOINT_RIGHT_HEEL ].parent = AVATAR_JOINT_RIGHT_KNEE;
_joint[ AVATAR_JOINT_RIGHT_TOES ].parent = AVATAR_JOINT_RIGHT_HEEL;
_joint[ AVATAR_JOINT_LEFT_HIP ].parent = AVATAR_JOINT_PELVIS;
_joint[ AVATAR_JOINT_LEFT_KNEE ].parent = AVATAR_JOINT_LEFT_HIP;
_joint[ AVATAR_JOINT_LEFT_HEEL ].parent = AVATAR_JOINT_LEFT_KNEE;
_joint[ AVATAR_JOINT_LEFT_TOES ].parent = AVATAR_JOINT_LEFT_HEEL;
_joint[ AVATAR_JOINT_RIGHT_HIP ].parent = AVATAR_JOINT_PELVIS;
_joint[ AVATAR_JOINT_RIGHT_KNEE ].parent = AVATAR_JOINT_RIGHT_HIP;
_joint[ AVATAR_JOINT_RIGHT_HEEL ].parent = AVATAR_JOINT_RIGHT_KNEE;
_joint[ AVATAR_JOINT_RIGHT_TOES ].parent = AVATAR_JOINT_RIGHT_HEEL;
// specify the default pose position
_joint[ AVATAR_JOINT_PELVIS ].defaultPosePosition = glm::vec3( 0.0, 0.0, 0.0 );
_joint[ AVATAR_JOINT_TORSO ].defaultPosePosition = glm::vec3( 0.0, 0.09, 0.01 );
_joint[ AVATAR_JOINT_CHEST ].defaultPosePosition = glm::vec3( 0.0, 0.09, 0.01 );
_joint[ AVATAR_JOINT_NECK_BASE ].defaultPosePosition = glm::vec3( 0.0, 0.14, -0.01 );
_joint[ AVATAR_JOINT_HEAD_BASE ].defaultPosePosition = glm::vec3( 0.0, 0.04, 0.00 );
_joint[ AVATAR_JOINT_PELVIS ].defaultPosePosition = glm::vec3( 0.0, 0.0, 0.0 );
_joint[ AVATAR_JOINT_TORSO ].defaultPosePosition = glm::vec3( 0.0, 0.09, -0.01 );
_joint[ AVATAR_JOINT_CHEST ].defaultPosePosition = glm::vec3( 0.0, 0.09, -0.01 );
_joint[ AVATAR_JOINT_NECK_BASE ].defaultPosePosition = glm::vec3( 0.0, 0.14, 0.01 );
_joint[ AVATAR_JOINT_HEAD_BASE ].defaultPosePosition = glm::vec3( 0.0, 0.04, 0.00 );
_joint[ AVATAR_JOINT_LEFT_COLLAR ].defaultPosePosition = glm::vec3( -0.06, 0.04, -0.01 );
_joint[ AVATAR_JOINT_LEFT_SHOULDER ].defaultPosePosition = glm::vec3( -0.05, 0.0, -0.01 );
_joint[ AVATAR_JOINT_LEFT_ELBOW ].defaultPosePosition = glm::vec3( 0.0, -0.16, 0.0 );
_joint[ AVATAR_JOINT_LEFT_WRIST ].defaultPosePosition = glm::vec3( 0.0, -0.117, 0.0 );
_joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].defaultPosePosition = glm::vec3( 0.0, -0.1, 0.0 );
_joint[ AVATAR_JOINT_LEFT_COLLAR ].defaultPosePosition = glm::vec3( -0.06, 0.04, 0.01 );
_joint[ AVATAR_JOINT_LEFT_SHOULDER ].defaultPosePosition = glm::vec3( -0.05, 0.0, 0.01 );
_joint[ AVATAR_JOINT_LEFT_ELBOW ].defaultPosePosition = glm::vec3( 0.0, -0.16, 0.0 );
_joint[ AVATAR_JOINT_LEFT_WRIST ].defaultPosePosition = glm::vec3( 0.0, -0.117, 0.0 );
_joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].defaultPosePosition = glm::vec3( 0.0, -0.1, 0.0 );
_joint[ AVATAR_JOINT_RIGHT_COLLAR ].defaultPosePosition = glm::vec3( 0.06, 0.04, -0.01 );
_joint[ AVATAR_JOINT_RIGHT_SHOULDER ].defaultPosePosition = glm::vec3( 0.05, 0.0, -0.01 );
_joint[ AVATAR_JOINT_RIGHT_ELBOW ].defaultPosePosition = glm::vec3( 0.0, -0.16, 0.0 );
_joint[ AVATAR_JOINT_RIGHT_COLLAR ].defaultPosePosition = glm::vec3( 0.06, 0.04, 0.01 );
_joint[ AVATAR_JOINT_RIGHT_SHOULDER ].defaultPosePosition = glm::vec3( 0.05, 0.0, 0.01 );
_joint[ AVATAR_JOINT_RIGHT_ELBOW ].defaultPosePosition = glm::vec3( 0.0, -0.16, 0.0 );
_joint[ AVATAR_JOINT_RIGHT_WRIST ].defaultPosePosition = glm::vec3( 0.0, -0.117, 0.0 );
_joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].defaultPosePosition = glm::vec3( 0.0, -0.1, 0.0 );
_joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].defaultPosePosition = glm::vec3( 0.0, -0.1, 0.0 );
_joint[ AVATAR_JOINT_LEFT_HIP ].defaultPosePosition = glm::vec3( -0.05, 0.0, -0.02 );
_joint[ AVATAR_JOINT_LEFT_KNEE ].defaultPosePosition = glm::vec3( 0.01, -0.25, 0.03 );
_joint[ AVATAR_JOINT_LEFT_HEEL ].defaultPosePosition = glm::vec3( 0.01, -0.22, -0.08 );
_joint[ AVATAR_JOINT_LEFT_TOES ].defaultPosePosition = glm::vec3( 0.00, -0.03, 0.05 );
_joint[ AVATAR_JOINT_LEFT_HIP ].defaultPosePosition = glm::vec3( -0.05, 0.0, 0.02 );
_joint[ AVATAR_JOINT_LEFT_KNEE ].defaultPosePosition = glm::vec3( 0.01, -0.25, -0.03 );
_joint[ AVATAR_JOINT_LEFT_HEEL ].defaultPosePosition = glm::vec3( 0.01, -0.22, 0.08 );
_joint[ AVATAR_JOINT_LEFT_TOES ].defaultPosePosition = glm::vec3( 0.00, -0.03, -0.05 );
_joint[ AVATAR_JOINT_RIGHT_HIP ].defaultPosePosition = glm::vec3( 0.05, 0.0, -0.02 );
_joint[ AVATAR_JOINT_RIGHT_KNEE ].defaultPosePosition = glm::vec3( -0.01, -0.25, 0.03 );
_joint[ AVATAR_JOINT_RIGHT_HEEL ].defaultPosePosition = glm::vec3( -0.01, -0.22, -0.08 );
_joint[ AVATAR_JOINT_RIGHT_TOES ].defaultPosePosition = glm::vec3( -0.00, -0.03, 0.05 );
_joint[ AVATAR_JOINT_RIGHT_HIP ].defaultPosePosition = glm::vec3( 0.05, 0.0, 0.02 );
_joint[ AVATAR_JOINT_RIGHT_KNEE ].defaultPosePosition = glm::vec3( -0.01, -0.25, -0.03 );
_joint[ AVATAR_JOINT_RIGHT_HEEL ].defaultPosePosition = glm::vec3( -0.01, -0.22, 0.08 );
_joint[ AVATAR_JOINT_RIGHT_TOES ].defaultPosePosition = glm::vec3( 0.00, -0.03, -0.05 );
// specify the radii of the joints
_joint[ AVATAR_JOINT_PELVIS ].radius = 0.07;
@ -905,29 +913,29 @@ void Avatar::initializeSkeleton() {
_joint[ AVATAR_JOINT_LEFT_COLLAR ].radius = 0.04;
_joint[ AVATAR_JOINT_LEFT_SHOULDER ].radius = 0.03;
_joint[ AVATAR_JOINT_LEFT_ELBOW ].radius = 0.02;
_joint[ AVATAR_JOINT_LEFT_ELBOW ].radius = 0.02;
_joint[ AVATAR_JOINT_LEFT_WRIST ].radius = 0.02;
_joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].radius = 0.01;
_joint[ AVATAR_JOINT_RIGHT_COLLAR ].radius = 0.04;
_joint[ AVATAR_JOINT_RIGHT_SHOULDER ].radius = 0.03;
_joint[ AVATAR_JOINT_RIGHT_ELBOW ].radius = 0.02;
_joint[ AVATAR_JOINT_RIGHT_WRIST ].radius = 0.02;
_joint[ AVATAR_JOINT_RIGHT_SHOULDER ].radius = 0.03;
_joint[ AVATAR_JOINT_RIGHT_ELBOW ].radius = 0.02;
_joint[ AVATAR_JOINT_RIGHT_WRIST ].radius = 0.02;
_joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].radius = 0.01;
_joint[ AVATAR_JOINT_LEFT_HIP ].radius = 0.04;
_joint[ AVATAR_JOINT_LEFT_KNEE ].radius = 0.025;
_joint[ AVATAR_JOINT_LEFT_HEEL ].radius = 0.025;
_joint[ AVATAR_JOINT_LEFT_TOES ].radius = 0.025;
_joint[ AVATAR_JOINT_LEFT_HIP ].radius = 0.04;
_joint[ AVATAR_JOINT_LEFT_KNEE ].radius = 0.025;
_joint[ AVATAR_JOINT_LEFT_HEEL ].radius = 0.025;
_joint[ AVATAR_JOINT_LEFT_TOES ].radius = 0.025;
_joint[ AVATAR_JOINT_RIGHT_HIP ].radius = 0.04;
_joint[ AVATAR_JOINT_RIGHT_KNEE ].radius = 0.025;
_joint[ AVATAR_JOINT_RIGHT_HEEL ].radius = 0.025;
_joint[ AVATAR_JOINT_RIGHT_TOES ].radius = 0.025;
_joint[ AVATAR_JOINT_RIGHT_HIP ].radius = 0.04;
_joint[ AVATAR_JOINT_RIGHT_KNEE ].radius = 0.025;
_joint[ AVATAR_JOINT_RIGHT_HEEL ].radius = 0.025;
_joint[ AVATAR_JOINT_RIGHT_TOES ].radius = 0.025;
// to aid in hand-shaking and hand-holding, the right hand is not collidable
_joint[ AVATAR_JOINT_RIGHT_ELBOW ].isCollidable = false;
_joint[ AVATAR_JOINT_RIGHT_WRIST ].isCollidable = false;
_joint[ AVATAR_JOINT_RIGHT_ELBOW ].isCollidable = false;
_joint[ AVATAR_JOINT_RIGHT_WRIST ].isCollidable = false;
_joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].isCollidable = false;
// calculate bone length
@ -970,27 +978,23 @@ void Avatar::calculateBoneLengths() {
_maxArmLength
= _joint[ AVATAR_JOINT_RIGHT_ELBOW ].length
+ _joint[ AVATAR_JOINT_RIGHT_WRIST ].length
+ _joint[ AVATAR_JOINT_RIGHT_WRIST ].length
+ _joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].length;
}
void Avatar::updateSkeleton() {
// rotate body...
_orientation.setToIdentity();
_orientation.yaw (_bodyYaw );
_orientation.pitch(_bodyPitch);
_orientation.roll (_bodyRoll );
_orientation.rotate(_righting);
glm::quat orientation = getOrientation();
// calculate positions of all bones by traversing the skeleton tree:
for (int b = 0; b < NUM_AVATAR_JOINTS; b++) {
if (_joint[b].parent == AVATAR_JOINT_NULL) {
_joint[b].orientation.set(_orientation);
_joint[b].orientation = orientation;
_joint[b].position = _position;
}
else {
_joint[b].orientation.set(_joint[ _joint[b].parent ].orientation);
_joint[b].orientation = _joint[ _joint[b].parent ].orientation;
_joint[b].position = _joint[ _joint[b].parent ].position;
}
@ -1000,11 +1004,7 @@ void Avatar::updateSkeleton() {
}
// the following will be replaced by a proper rotation...close
float xx = glm::dot(_joint[b].defaultPosePosition, _joint[b].orientation.getRight());
float yy = glm::dot(_joint[b].defaultPosePosition, _joint[b].orientation.getUp ());
float zz = glm::dot(_joint[b].defaultPosePosition, _joint[b].orientation.getFront());
glm::vec3 rotatedJointVector(xx, yy, zz);
glm::vec3 rotatedJointVector = _joint[b].orientation * _joint[b].defaultPosePosition;
//glm::vec3 myEuler (0.0f, 0.0f, 0.0f);
//glm::quat myQuat (myEuler);
@ -1037,12 +1037,12 @@ void Avatar::updateBodySprings(float deltaTime) {
}
float length = glm::length(springVector);
if (length > 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);

View file

@ -11,7 +11,6 @@
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <AvatarData.h>
#include <Orientation.h>
#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();

View file

@ -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<NUM_POINTS; p++) {
for (int p=0; p<NUM_PARTICLE_POINTS; p++) {
_point[p] = glm::vec3(0.0, 0.0, 0.0);
}
}
@ -49,13 +47,15 @@ void AvatarTouch::simulate (float deltaTime) {
bool facingEachOther = false;
if (( glm::dot(_myOrientation.getFront(), _yourOrientation.getFront()) < -AVATAR_FACING_THRESHOLD) // we're facing each other
&& ( glm::dot(_myOrientation.getFront(), directionBetweenBodies ) > 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<NUM_POINTS; p++) {
for (int p=0; p<NUM_PARTICLE_POINTS; p++) {
_point[p] = _myHandPosition + _vectorBetweenHands * ((float)p / (float)NUM_POINTS);
_point[p] = _myHandPosition + _vectorBetweenHands * ((float)p / (float)NUM_PARTICLE_POINTS);
_point[p].x += randFloatInRange(-THREAD_RADIUS, THREAD_RADIUS);
_point[p].y += randFloatInRange(-THREAD_RADIUS, THREAD_RADIUS);
_point[p].z += randFloatInRange(-THREAD_RADIUS, THREAD_RADIUS);

View file

@ -9,7 +9,9 @@
#define __interface__AvatarTouch__
#include <glm/glm.hpp>
#include "Orientation.h"
#include <glm/gtc/quaternion.hpp>
#include <AvatarData.h>
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;

View file

@ -4,12 +4,14 @@
//
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
#include <glm/gtx/quaternion.hpp>
#include <SharedUtil.h>
#include <VoxelConstants.h>
#include <OculusManager.h>
// #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) {

View file

@ -8,7 +8,6 @@
#ifndef __interface__camera__
#define __interface__camera__
#include "Orientation.h"
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
@ -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 );
};

View file

@ -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();
}
}

View file

@ -4,6 +4,8 @@
//
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
#include <glm/gtx/quaternion.hpp>
#include "Avatar.h"
#include "Head.h"
#include "Util.h"
#include <vector>
@ -41,6 +43,7 @@ vector<unsigned char> 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<Avatar*>(_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;

View file

@ -14,7 +14,6 @@
#include "world.h"
#include "InterfaceConfig.h"
#include "SerialInterface.h"
#include "Orientation.h"
#include <SharedUtil.h>
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);

View file

@ -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);
}
}

View file

@ -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__) */
};

View file

@ -12,7 +12,8 @@
#include <glm/glm.hpp>
#include <glm/gtc/noise.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>
#include <AvatarData.h>
#include <SharedUtil.h>
#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");
}

View file

@ -16,8 +16,7 @@
#endif
#include <glm/glm.hpp>
#include <Orientation.h>
#include <glm/gtc/quaternion.hpp>
// 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

View file

@ -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;
}

View file

@ -7,6 +7,7 @@
//
#include <cstring>
#include <math.h>
#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

View file

@ -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,

View file

@ -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);
}

View file

@ -1,55 +0,0 @@
//-----------------------------------------------------------
//
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//-----------------------------------------------------------
#ifndef __interface__orientation__
#define __interface__orientation__
#include <cmath>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
// 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

View file

@ -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());