From 316da59da3957ce4a902f780c6309191210e840c Mon Sep 17 00:00:00 2001 From: ZappoMan <bradh@konamoxt.com> Date: Fri, 17 May 2013 12:44:39 -0700 Subject: [PATCH] first cut at animation server --- CMakeLists.txt | 3 +- animation-server/CMakeLists.txt | 26 +++ animation-server/src/main.cpp | 348 ++++++++++++++++++++++++++++++ libraries/shared/src/Agent.cpp | 3 + libraries/shared/src/AgentTypes.h | 1 + voxel-server/src/main.cpp | 43 +++- 6 files changed, 415 insertions(+), 9 deletions(-) create mode 100644 animation-server/CMakeLists.txt create mode 100644 animation-server/src/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 14692ef3f4..f55cd7d8b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,4 +11,5 @@ add_subdirectory(injector) add_subdirectory(pairing-server) add_subdirectory(space-server) add_subdirectory(voxel-edit) -add_subdirectory(voxel-server) \ No newline at end of file +add_subdirectory(voxel-server) +add_subdirectory(animation-server) \ No newline at end of file diff --git a/animation-server/CMakeLists.txt b/animation-server/CMakeLists.txt new file mode 100644 index 0000000000..e9662366af --- /dev/null +++ b/animation-server/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 2.8) + +set(TARGET_NAME animation-server) + +set(ROOT_DIR ..) +set(MACRO_DIR ${ROOT_DIR}/cmake/macros) + +# setup for find modules +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/") + +# set up the external glm library +include(${MACRO_DIR}/IncludeGLM.cmake) +include_glm(${TARGET_NAME} ${ROOT_DIR}) + +include(${MACRO_DIR}/SetupHifiProject.cmake) + +setup_hifi_project(${TARGET_NAME}) + +# link in the shared library +include(${MACRO_DIR}/LinkHifiLibrary.cmake) +link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) + +# link in the hifi voxels library +link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR}) + + diff --git a/animation-server/src/main.cpp b/animation-server/src/main.cpp new file mode 100644 index 0000000000..d272efd638 --- /dev/null +++ b/animation-server/src/main.cpp @@ -0,0 +1,348 @@ +// +// main.cpp +// Animation Server +// +// Created by Brad Hefta-Gaub on 05/16/2013 +// Copyright (c) 2012 High Fidelity, Inc. All rights reserved. +// + +#include <cmath> +#include <cstdlib> +#include <cstring> +#include <cstdio> +#include <OctalCode.h> +#include <AgentList.h> +#include <AgentTypes.h> +#include <EnvironmentData.h> +#include <VoxelTree.h> +#include <SharedUtil.h> +#include <PacketHeaders.h> +#include <SceneUtils.h> + +#ifdef _WIN32 +#include "Syssocket.h" +#include "Systime.h" +#else +#include <sys/time.h> +#include <arpa/inet.h> +#include <ifaddrs.h> +#endif + +const int ANIMATION_LISTEN_PORT = 40107; +const int ANIMATE_VOXELS_INTERVAL_USECS = SIXTY_FPS_IN_MILLISECONDS * 1000.0 * 1; + +bool wantLocalDomain = false; + +static void sendVoxelServerZMessage() { + char message[100]; + sprintf(message,"%c%s",'Z',"a message"); + int messageSize = strlen(message) + 1; + AgentList::getInstance()->broadcastToAgents((unsigned char*) message, messageSize, &AGENT_TYPE_VOXEL, 1); +} + +static void sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail) { + unsigned char* bufferOut; + int sizeOut; + + if (createVoxelEditMessage(header, 0, 1, &detail, bufferOut, sizeOut)){ + AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL, 1); + delete bufferOut; + } +} + +float intensity = 0.5f; +float intensityIncrement = 0.1f; +const float MAX_INTENSITY = 1.0f; +const float MIN_INTENSITY = 0.5f; + +static void sendVoxelBlinkMessage() { + VoxelDetail detail; + detail.s = 0.25f / TREE_SCALE; + + glm::vec3 position = glm::vec3(0, 0, detail.s); + + detail.x = detail.s * floor(position.x / detail.s); + detail.y = detail.s * floor(position.y / detail.s); + detail.z = detail.s * floor(position.z / detail.s); + + ::intensity += ::intensityIncrement; + if (::intensity >= MAX_INTENSITY) { + ::intensity = MAX_INTENSITY; + ::intensityIncrement = -::intensityIncrement; + } + if (::intensity <= MIN_INTENSITY) { + ::intensity = MIN_INTENSITY; + ::intensityIncrement = -::intensityIncrement; + } + + detail.red = 255 * ::intensity; + detail.green = 0 * ::intensity; + detail.blue = 0 * ::intensity; + + PACKET_HEADER message = PACKET_HEADER_SET_VOXEL_DESTRUCTIVE; + + sendVoxelEditMessage(message, detail); +} + +bool stringOfLightsInitialized = false; +int currentLight = 0; +int lightMovementDirection = 1; +const int LIGHT_COUNT = 64; +glm::vec3 stringOfLights[LIGHT_COUNT]; +unsigned char offColor[3] = { 240, 240, 240 }; +unsigned char onColor[3] = { 0, 255, 0 }; + +static void sendBlinkingStringOfLights() { + PACKET_HEADER message = PACKET_HEADER_SET_VOXEL_DESTRUCTIVE; // we're a bully! + float lightScale = 0.125f / TREE_SCALE; + VoxelDetail detail; + + detail.s = lightScale; + + // first initialized the string of lights if needed... + if (!stringOfLightsInitialized) { + for (int i = 0; i < LIGHT_COUNT; i++) { + stringOfLights[i] = glm::vec3(i * lightScale,0,0); + + detail.x = detail.s * floor(stringOfLights[i].x / detail.s); + detail.y = detail.s * floor(stringOfLights[i].y / detail.s); + detail.z = detail.s * floor(stringOfLights[i].z / detail.s); + + if (i == currentLight) { + detail.red = onColor[0]; + detail.green = onColor[1]; + detail.blue = onColor[2]; + } else { + detail.red = offColor[0]; + detail.green = offColor[1]; + detail.blue = offColor[2]; + } + sendVoxelEditMessage(message, detail); + } + stringOfLightsInitialized = true; + } else { + // turn off current light + detail.x = detail.s * floor(stringOfLights[currentLight].x / detail.s); + detail.y = detail.s * floor(stringOfLights[currentLight].y / detail.s); + detail.z = detail.s * floor(stringOfLights[currentLight].z / detail.s); + detail.red = offColor[0]; + detail.green = offColor[1]; + detail.blue = offColor[2]; + sendVoxelEditMessage(message, detail); + + // move current light... + // if we're at the end of our string, then change direction + if (currentLight == LIGHT_COUNT-1) { + lightMovementDirection = -1; + } + if (currentLight == 0) { + lightMovementDirection = 1; + } + currentLight += lightMovementDirection; + + // turn on new current light + detail.x = detail.s * floor(stringOfLights[currentLight].x / detail.s); + detail.y = detail.s * floor(stringOfLights[currentLight].y / detail.s); + detail.z = detail.s * floor(stringOfLights[currentLight].z / detail.s); + detail.red = onColor[0]; + detail.green = onColor[1]; + detail.blue = onColor[2]; + sendVoxelEditMessage(message, detail); + } +} + +bool billboardInitialized = false; +const int BILLBOARD_HEIGHT = 9; +const int BILLBOARD_WIDTH = 44; +glm::vec3 billboardPosition((0.125f / TREE_SCALE),(0.125f / TREE_SCALE),0); +glm::vec3 billboardLights[BILLBOARD_HEIGHT][BILLBOARD_WIDTH]; +unsigned char billboardOffColor[3] = { 240, 240, 240 }; +unsigned char billboardOnColorA[3] = { 0, 0, 255 }; +unsigned char billboardOnColorB[3] = { 0, 255, 0 }; +float billboardGradient = 0.5f; +float billboardGradientIncrement = 0.01f; +const float BILLBOARD_MAX_GRADIENT = 1.0f; +const float BILLBOARD_MIN_GRADIENT = 0.0f; + +// top to bottom... +bool billBoardMessage[BILLBOARD_HEIGHT][BILLBOARD_WIDTH] = { + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,1,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,1,1,1,1,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,0,0,0,0,0,0 }, + { 0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,1,0,1,0,0,0,1,1,1,0,0,0,0,0 }, + { 0,1,1,1,1,0,1,0,1,1,1,0,1,1,1,0,0,1,1,1,0,0,0,0,1,1,1,0,1,1,1,0,1,0,1,0,0,1,0,0,1,0,1,0 }, + { 0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,1,0,1,0 }, + { 0,1,0,0,1,0,1,0,1,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,1,1,1,0,1,1,1,0,1,0,1,0,0,1,0,0,1,1,1,0 }, + { 0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0 }, + { 0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0 }, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } +}; + +static void sendBillboard() { + PACKET_HEADER message = PACKET_HEADER_SET_VOXEL_DESTRUCTIVE; // we're a bully! + float lightScale = 0.125f / TREE_SCALE; + VoxelDetail detail; + + detail.s = lightScale; + + // first initialized the billboard of lights if needed... + if (!billboardInitialized) { + for (int i = 0; i < BILLBOARD_HEIGHT; i++) { + for (int j = 0; j < BILLBOARD_WIDTH; j++) { + + billboardLights[i][j] = billboardPosition + glm::vec3(j * lightScale, (float)((BILLBOARD_HEIGHT - i) * lightScale), 0); + } + } + billboardInitialized = true; + } + + // what will our animation be? + // fade in/out the message? + + //for now, just send it again..., but now, with intensity + + ::billboardGradient += ::billboardGradientIncrement; + + printf("billboardGradient=%f billboardGradientIncrement=%f\n",billboardGradient,billboardGradientIncrement); + + if (::billboardGradient >= BILLBOARD_MAX_GRADIENT) { + ::billboardGradient = BILLBOARD_MAX_GRADIENT; + ::billboardGradientIncrement = -::billboardGradientIncrement; + } + if (::billboardGradient <= BILLBOARD_MIN_GRADIENT) { + ::billboardGradient = BILLBOARD_MIN_GRADIENT; + ::billboardGradientIncrement = -::billboardGradientIncrement; + } + + for (int i = 0; i < BILLBOARD_HEIGHT; i++) { + for (int j = 0; j < BILLBOARD_WIDTH; j++) { + + billboardLights[i][j] = billboardPosition + glm::vec3(j * lightScale, (float)((BILLBOARD_HEIGHT - i) * lightScale), 0); + + detail.x = billboardLights[i][j].x; + detail.y = billboardLights[i][j].y; + detail.z = billboardLights[i][j].z; + + if (billBoardMessage[i][j]) { + detail.red = (billboardOnColorA[0] + ((billboardOnColorB[0] - billboardOnColorA[0]) * ::billboardGradient)); + detail.green = (billboardOnColorA[1] + ((billboardOnColorB[1] - billboardOnColorA[1]) * ::billboardGradient)); + detail.blue = (billboardOnColorA[2] + ((billboardOnColorB[2] - billboardOnColorA[2]) * ::billboardGradient)); + } else { + detail.red = billboardOffColor[0]; + detail.green = billboardOffColor[1]; + detail.blue = billboardOffColor[2]; + } + sendVoxelEditMessage(message, detail); + } + } +} + + +void* animateVoxels(void* args) { + + AgentList* agentList = AgentList::getInstance(); + timeval lastSendTime; + + while (true) { + gettimeofday(&lastSendTime, NULL); + + //printf("animateVoxels thread\n"); + + //printf("animateVoxels thread: Sending a Blink message to the voxel server\n"); + sendVoxelBlinkMessage(); + sendBlinkingStringOfLights(); + sendBillboard(); + + /** + int agentNumber = 0; + for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) { + agentNumber++; + printf("current agents[%d] id: %d type: %s \n", agentNumber, agent->getAgentID(), agent->getTypeName()); + } + **/ + + // dynamically sleep until we need to fire off the next set of voxels + double usecToSleep = ANIMATE_VOXELS_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime)); + + if (usecToSleep > 0) { + usleep(usecToSleep); + } else { + std::cout << "Last send took too much time, not sleeping!\n"; + } + } + + pthread_exit(0); +} + + +int main(int argc, const char * argv[]) +{ + AgentList* agentList = AgentList::createInstance(AGENT_TYPE_ANIMATION_SERVER, ANIMATION_LISTEN_PORT); + setvbuf(stdout, NULL, _IOLBF, 0); + + // Handle Local Domain testing with the --local command line + const char* local = "--local"; + ::wantLocalDomain = cmdOptionExists(argc, argv,local); + if (::wantLocalDomain) { + 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 = NULL; // do we need a callback? + agentList->startSilentAgentRemovalThread(); + agentList->startDomainServerCheckInThread(); + + srand((unsigned)time(0)); + + pthread_t animateVoxelThread; + pthread_create(&animateVoxelThread, NULL, animateVoxels, NULL); + + sockaddr agentPublicAddress; + + unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE]; + ssize_t receivedBytes; + + // loop to send to agents requesting data + while (true) { + + // Agents sending messages to us... + if (agentList->getAgentSocket()->receive(&agentPublicAddress, packetData, &receivedBytes)) { + + switch (packetData[0]) { + + case PACKET_HEADER_Z_COMMAND: { + + // the Z command is a special command that allows the sender to send the voxel server high level semantic + // requests, like erase all, or add sphere scene + char* command = (char*) &packetData[1]; // start of the command + int commandLength = strlen(command); // commands are null terminated strings + int totalLength = 1+commandLength+1; + + printf("got Z message len(%ld)= %s\n",receivedBytes,command); + + while (totalLength <= receivedBytes) { + if (0==strcmp(command,(char*)"erase all")) { + printf("got Z message == erase all, we don't support that\n"); + } + if (0==strcmp(command,(char*)"add scene")) { + printf("got Z message == add scene, we don't support that\n"); + } + totalLength += commandLength+1; + } + // If we wanted to rebroadcast this message we could do it here. + //printf("rebroadcasting Z message to connected agents... agentList.broadcastToAgents()\n"); + //agentList->broadcastToAgents(packetData,receivedBytes, &AGENT_TYPE_AVATAR, 1); + } break; + + default: { + AgentList::getInstance()->processAgentData(&agentPublicAddress, packetData, receivedBytes); + } break; + + } + } + } + + pthread_join(animateVoxelThread, NULL); + + return 0; +} diff --git a/libraries/shared/src/Agent.cpp b/libraries/shared/src/Agent.cpp index db4170515a..f8f61d3e30 100644 --- a/libraries/shared/src/Agent.cpp +++ b/libraries/shared/src/Agent.cpp @@ -130,6 +130,7 @@ const char* AGENT_TYPE_NAME_INTERFACE = "Client Interface"; const char* AGENT_TYPE_NAME_AUDIO_MIXER = "Audio Mixer"; const char* AGENT_TYPE_NAME_AVATAR_MIXER = "Avatar Mixer"; const char* AGENT_TYPE_NAME_AUDIO_INJECTOR = "Audio Injector"; +const char* AGENT_TYPE_NAME_ANIMATION_SERVER = "Animation Server"; const char* AGENT_TYPE_NAME_UNKNOWN = "Unknown"; const char* Agent::getTypeName() const { @@ -146,6 +147,8 @@ const char* Agent::getTypeName() const { return AGENT_TYPE_NAME_AVATAR_MIXER; case AGENT_TYPE_AUDIO_INJECTOR: return AGENT_TYPE_NAME_AUDIO_INJECTOR; + case AGENT_TYPE_ANIMATION_SERVER: + return AGENT_TYPE_NAME_ANIMATION_SERVER; default: return AGENT_TYPE_NAME_UNKNOWN; } diff --git a/libraries/shared/src/AgentTypes.h b/libraries/shared/src/AgentTypes.h index e095cb1035..4f3100fe03 100644 --- a/libraries/shared/src/AgentTypes.h +++ b/libraries/shared/src/AgentTypes.h @@ -24,5 +24,6 @@ const char AGENT_TYPE_AVATAR = 'I'; const char AGENT_TYPE_AUDIO_MIXER = 'M'; const char AGENT_TYPE_AVATAR_MIXER = 'W'; const char AGENT_TYPE_AUDIO_INJECTOR = 'A'; +const char AGENT_TYPE_ANIMATION_SERVER = 'a'; #endif diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 25e86f1f8a..1036b35900 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -19,6 +19,7 @@ #include <SharedUtil.h> #include <PacketHeaders.h> #include <SceneUtils.h> +#include <PerfStat.h> #ifdef _WIN32 #include "Syssocket.h" @@ -31,6 +32,7 @@ const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.hio2"; const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.hio2"; +const double VOXEL_PERSIST_INTERVAL = 1000.0 * 30; // every 30 seconds const int VOXEL_LISTEN_PORT = 40106; @@ -341,13 +343,31 @@ void deepestLevelVoxelDistributor(AgentList* agentList, } // end if bag wasn't empty, and so we sent stuff... } +double lastPersistVoxels = 0; void persistVoxelsWhenDirty() { + double now = usecTimestampNow(); + double sinceLastTime = (now - ::lastPersistVoxels) / 1000.0; + // check the dirty bit and persist here... - if (::wantVoxelPersist && ::randomTree.isDirty()) { - printf("saving voxels to file...\n"); - randomTree.writeToFileV2(::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE); - randomTree.clearDirtyBit(); // tree is clean after saving - printf("DONE saving voxels to file...\n"); + if (::wantVoxelPersist && ::randomTree.isDirty() && sinceLastTime > VOXEL_PERSIST_INTERVAL) { + + { + PerformanceWarning warn(true, "persistVoxelsWhenDirty() - reaverageVoxelColors()", true); + + // after done inserting all these voxels, then reaverage colors + randomTree.reaverageVoxelColors(randomTree.rootNode); + } + + + { + PerformanceWarning warn(true, "persistVoxelsWhenDirty() - writeToFileV2()", true); + + printf("saving voxels to file...\n"); + randomTree.writeToFileV2(::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE); + randomTree.clearDirtyBit(); // tree is clean after saving + printf("DONE saving voxels to file...\n"); + } + ::lastPersistVoxels = usecTimestampNow(); } } @@ -498,13 +518,19 @@ int main(int argc, const char * argv[]) // loop to send to agents requesting data while (true) { + // check to see if we need to persist our voxel state - persistVoxelsWhenDirty(); + persistVoxelsWhenDirty(); if (agentList->getAgentSocket()->receive(&agentPublicAddress, packetData, &receivedBytes)) { // XXXBHG: Hacked in support for 'S' SET command if (packetData[0] == PACKET_HEADER_SET_VOXEL || packetData[0] == PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) { bool destructive = (packetData[0] == PACKET_HEADER_SET_VOXEL_DESTRUCTIVE); + + PerformanceWarning warn(true, + destructive ? "PACKET_HEADER_SET_VOXEL_DESTRUCTIVE" : "PACKET_HEADER_SET_VOXEL", + true); + unsigned short int itemNumber = (*((unsigned short int*)&packetData[1])); printf("got %s - command from client receivedBytes=%ld itemNumber=%d\n", destructive ? "PACKET_HEADER_SET_VOXEL_DESTRUCTIVE" : "PACKET_HEADER_SET_VOXEL", @@ -541,8 +567,6 @@ int main(int argc, const char * argv[]) pVoxelData+=voxelDataSize; atByte+=voxelDataSize; } - // after done inserting all these voxels, then reaverage colors - randomTree.reaverageVoxelColors(randomTree.rootNode); } if (packetData[0] == PACKET_HEADER_ERASE_VOXEL) { @@ -575,6 +599,9 @@ int main(int argc, const char * argv[]) printf("got Z message == add scene\n"); addSphereScene(&randomTree); } + if (0==strcmp(command,(char*)"a message")) { + printf("got Z message == a message, nothing to do, just report\n"); + } totalLength += commandLength+1; }