Merge pull request #146 from ey6es/master

Basic text chat.  This adds fields to AvatarData and will thus break existing clients.
This commit is contained in:
Philip Rosedale 2013-04-25 19:50:27 -07:00
commit d1589270e5
8 changed files with 236 additions and 4 deletions

View file

@ -51,7 +51,7 @@ void attachAvatarDataToAgent(Agent *newAgent) {
}
}
int main(int argc, char* argv[])
int main(int argc, const char* argv[])
{
AgentList* agentList = AgentList::createInstance(AGENT_TYPE_AVATAR_MIXER, AVATAR_LISTEN_PORT);
setvbuf(stdout, NULL, _IOLBF, 0);

View file

@ -517,6 +517,7 @@ float Audio::getInputLoudness() const {
void Audio::render(int screenWidth, int screenHeight)
{
if (initialized) {
glLineWidth(3);
glBegin(GL_LINES);
glColor3f(1,1,1);

View file

@ -37,6 +37,9 @@ bool usingBigSphereCollisionTest = true;
char iris_texture_file[] = "resources/images/green_eye.png";
float chatMessageScale = 0.00025;
float chatMessageHeight = 0.4;
vector<unsigned char> iris_texture;
unsigned int iris_texture_width = 512;
unsigned int iris_texture_height = 256;
@ -631,6 +634,45 @@ void Avatar::render(bool lookingInMirror) {
glEnd();
}
}
if (!_chatMessage.empty()) {
float width = 0;
float lastWidth;
for (string::iterator it = _chatMessage.begin(); it != _chatMessage.end(); it++) {
width += (lastWidth = glutStrokeWidth(GLUT_STROKE_ROMAN, *it)*chatMessageScale);
}
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(_position.x, _position.y + chatMessageHeight, _position.z);
glRotatef(atan2(-modelview[2], -modelview[10]) * 180 / PI, 0, 1, 0);
glTranslatef(width * 0.5, 0, 0);
glDisable(GL_LIGHTING);
if (_keyState == NoKeyDown) {
drawtext(0, 0, chatMessageScale, 180, 1.0, 0, _chatMessage.c_str(), 0, 1, 0);
} else {
// rather than using substr and allocating a new string, just replace the last
// character with a null, then restore it
int lastIndex = _chatMessage.size() - 1;
char lastChar = _chatMessage[lastIndex];
_chatMessage[lastIndex] = '\0';
drawtext(0, 0, chatMessageScale, 180, 1.0, 0, _chatMessage.c_str(), 0, 1, 0);
_chatMessage[lastIndex] = lastChar;
glTranslatef(lastWidth - width, 0, 0);
drawtext(0, 0, chatMessageScale, 180, 3.0,
0, _chatMessage.c_str() + lastIndex, 0, 1, 0);
}
glEnable(GL_LIGHTING);
glPopMatrix();
}
}
@ -1227,4 +1269,4 @@ glm::vec3 Avatar::getGravity(glm::vec3 pos) {
// If flying in space, turn gravity OFF
return glm::vec3(0.f, 0.f, 0.f);
}
}
}

View file

@ -0,0 +1,78 @@
//
// ChatEntry.cpp
// interface
//
// Created by Andrzej Kapolka on 4/24/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
#include "InterfaceConfig.h"
#include "ChatEntry.h"
#include "Util.h"
using namespace std;
const int MAX_CONTENT_LENGTH = 140;
void ChatEntry::clear () {
contents.clear();
cursorPos = 0;
}
bool ChatEntry::key(unsigned char k) {
switch (k) {
case '\r':
return false;
case '\b':
if (cursorPos != 0) {
contents.erase(cursorPos - 1, 1);
cursorPos--;
}
return true;
case 127: // delete
if (cursorPos < contents.size()) {
contents.erase(cursorPos, 1);
}
return true;
default:
if (contents.size() != MAX_CONTENT_LENGTH) {
contents.insert(cursorPos, 1, k);
cursorPos++;
}
return true;
}
}
void ChatEntry::specialKey(unsigned char k) {
switch (k) {
case GLUT_KEY_LEFT:
if (cursorPos != 0) {
cursorPos--;
}
break;
case GLUT_KEY_RIGHT:
if (cursorPos != contents.size()) {
cursorPos++;
}
break;
}
}
void ChatEntry::render(int screenWidth, int screenHeight) {
drawtext(20, screenHeight - 150, 0.10, 0, 1.0, 0, contents.c_str(), 1, 1, 1);
float width = 0;
for (string::iterator it = contents.begin(), end = it + cursorPos; it != end; it++) {
width += glutStrokeWidth(GLUT_STROKE_ROMAN, *it)*0.10;
}
glDisable(GL_LINE_SMOOTH);
glBegin(GL_LINE_STRIP);
glVertex2f(20 + width, screenHeight - 165);
glVertex2f(20 + width, screenHeight - 150);
glEnd();
glEnable(GL_LINE_SMOOTH);
}

33
interface/src/ChatEntry.h Normal file
View file

@ -0,0 +1,33 @@
//
// ChatEntry.h
// interface
//
// Created by Andrzej Kapolka on 4/24/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__ChatEntry__
#define __interface__ChatEntry__
#include <string>
class ChatEntry {
public:
const std::string& getContents () const { return contents; }
void clear ();
bool key(unsigned char k);
void specialKey(unsigned char k);
void render(int screenWidth, int screenHeight);
private:
std::string contents;
int cursorPos;
};
#endif /* defined(__interface__ChatEntry__) */

View file

@ -64,6 +64,7 @@
#include "MenuColumn.h"
#include "Menu.h"
#include "Camera.h"
#include "ChatEntry.h"
#include "Avatar.h"
#include "Particle.h"
#include "Texture.h"
@ -180,6 +181,9 @@ int mousePressed = 0; // true if mouse has been pressed (clear when finished)
Menu menu; // main menu
int menuOn = 1; // Whether to show onscreen menu
ChatEntry chatEntry; // chat entry field
bool chatEntryOn = false; // Whether to show the chat entry
struct HandController
{
bool enabled;
@ -1004,6 +1008,11 @@ void display(void)
menu.render(WIDTH,HEIGHT);
}
// Show chat entry field
if (::chatEntryOn) {
chatEntry.render(WIDTH, HEIGHT);
}
// Stats at upper right of screen about who domain server is telling us about
glPointSize(1.0f);
char agents[100];
@ -1349,6 +1358,11 @@ void specialkeyUp(int k, int x, int y) {
void specialkey(int k, int x, int y)
{
if (::chatEntryOn) {
chatEntry.specialKey(k);
return;
}
if (k == GLUT_KEY_UP || k == GLUT_KEY_DOWN || k == GLUT_KEY_LEFT || k == GLUT_KEY_RIGHT) {
if (k == GLUT_KEY_UP) {
if (glutGetModifiers() == GLUT_ACTIVE_SHIFT) myAvatar.setDriveKeys(UP, 1);
@ -1374,6 +1388,11 @@ void specialkey(int k, int x, int y)
void keyUp(unsigned char k, int x, int y) {
if (::chatEntryOn) {
myAvatar.setKeyState(AvatarData::NoKeyDown);
return;
}
if (k == 'e') myAvatar.setDriveKeys(UP, 0);
if (k == 'c') myAvatar.setDriveKeys(DOWN, 0);
if (k == 'w') myAvatar.setDriveKeys(FWD, 0);
@ -1385,6 +1404,19 @@ void keyUp(unsigned char k, int x, int y) {
void key(unsigned char k, int x, int y)
{
if (::chatEntryOn) {
if (chatEntry.key(k)) {
myAvatar.setKeyState(k == '\b' || k == 127 ? // backspace or delete
AvatarData::DeleteKeyDown : AvatarData::InsertKeyDown);
myAvatar.setChatMessage(string(chatEntry.getContents().size(), 'X'));
} else {
myAvatar.setChatMessage(chatEntry.getContents());
chatEntry.clear();
::chatEntryOn = false;
}
return;
}
// Process keypresses
if (k == 'q' || k == 'Q') ::terminate();
@ -1451,6 +1483,12 @@ void key(unsigned char k, int x, int y)
if (k == 'g') renderPitchRate += KEYBOARD_PITCH_RATE;
if (k == 'a') myAvatar.setDriveKeys(ROT_LEFT, 1);
if (k == 'd') myAvatar.setDriveKeys(ROT_RIGHT, 1);
if (k == '\r') {
::chatEntryOn = true;
myAvatar.setKeyState(AvatarData::NoKeyDown);
myAvatar.setChatMessage(string());
}
}
// Receive packets from other agents/servers and decide what to do with them!
@ -1659,7 +1697,12 @@ int main(int argc, const char * argv[])
return EXIT_SUCCESS;
}
AgentList::createInstance(AGENT_TYPE_AVATAR);
unsigned int listenPort = AGENT_SOCKET_LISTEN_PORT;
const char* portStr = getCmdOption(argc, argv, "--listenPort");
if (portStr) {
listenPort = atoi(portStr);
}
AgentList::createInstance(AGENT_TYPE_AVATAR, listenPort);
gettimeofday(&applicationStartupTime, NULL);
const char* domainIP = getCmdOption(argc, argv, "--domain");

View file

@ -16,6 +16,7 @@
#include "AvatarData.h"
#include "avatars_Log.h"
using namespace std;
using avatars_lib::printLog;
@ -49,7 +50,8 @@ AvatarData::AvatarData() :
_cameraFov(0.0f),
_cameraAspectRatio(0.0f),
_cameraNearClip(0.0f),
_cameraFarClip(0.0f) {
_cameraFarClip(0.0f),
_keyState(NoKeyDown) {
}
@ -112,7 +114,14 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
memcpy(destinationBuffer, &_cameraFarClip, sizeof(_cameraFarClip));
destinationBuffer += sizeof(_cameraFarClip);
// key state
*destinationBuffer++ = _keyState;
// chat message
*destinationBuffer++ = _chatMessage.size();
memcpy(destinationBuffer, _chatMessage.data(), _chatMessage.size() * sizeof(char));
destinationBuffer += _chatMessage.size() * sizeof(char);
return destinationBuffer - bufferStart;
}
@ -168,6 +177,14 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
memcpy(&_cameraFarClip, sourceBuffer, sizeof(_cameraFarClip));
sourceBuffer += sizeof(_cameraFarClip);
// key state
_keyState = (KeyState)*sourceBuffer++;
// the rest is a chat message
int chatMessageSize = *sourceBuffer++;
_chatMessage = string((char*)sourceBuffer, chatMessageSize);
sourceBuffer += chatMessageSize * sizeof(char);
return sourceBuffer - startPosition;
}

View file

@ -9,6 +9,8 @@
#ifndef __hifi__AvatarData__
#define __hifi__AvatarData__
#include <string>
#include <glm/glm.hpp>
#include <AgentData.h>
@ -74,6 +76,16 @@ public:
void setCameraNearClip(float nearClip) { _cameraNearClip = nearClip; }
void setCameraFarClip(float farClip) { _cameraFarClip = farClip; }
enum KeyState { NoKeyDown, InsertKeyDown, DeleteKeyDown };
// key state
void setKeyState(KeyState s) { _keyState = s; }
KeyState keyState() const { return _keyState; }
// chat message
void setChatMessage(const std::string& msg) { _chatMessage = msg; }
const std::string& chatMessage () const { return _chatMessage; }
protected:
glm::vec3 _position;
glm::vec3 _handPosition;
@ -105,6 +117,12 @@ protected:
float _cameraAspectRatio;
float _cameraNearClip;
float _cameraFarClip;
// key state (nothing, down, up, backspace)
KeyState _keyState;
// chat message
std::string _chatMessage;
};
#endif /* defined(__hifi__AvatarData__) */