mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 06:38:29 +02:00
Merge pull request #169 from ey6es/master
New text rendering method. This introduces a dependency on Qt 4.
This commit is contained in:
commit
b12e01322a
19 changed files with 327 additions and 84 deletions
|
@ -30,8 +30,12 @@ include_glm(${TARGET_NAME} ${ROOT_DIR})
|
||||||
# create the InterfaceConfig.h file based on GL_HEADERS above
|
# create the InterfaceConfig.h file based on GL_HEADERS above
|
||||||
configure_file(InterfaceConfig.h.in ${PROJECT_BINARY_DIR}/includes/InterfaceConfig.h)
|
configure_file(InterfaceConfig.h.in ${PROJECT_BINARY_DIR}/includes/InterfaceConfig.h)
|
||||||
|
|
||||||
# grab the implementation and header files from src dir
|
# grab the implementation and header files from src dirs
|
||||||
file(GLOB INTERFACE_SRCS src/*.cpp src/*.h)
|
file(GLOB INTERFACE_SRCS src/*.cpp src/*.h)
|
||||||
|
foreach(SUBDIR ui)
|
||||||
|
file(GLOB SUBDIR_SRCS src/${SUBDIR}/*.cpp src/${SUBDIR}/*.h)
|
||||||
|
set(INTERFACE_SRCS ${INTERFACE_SRCS} ${SUBDIR_SRCS})
|
||||||
|
endforeach(SUBDIR)
|
||||||
|
|
||||||
# project subdirectories
|
# project subdirectories
|
||||||
add_subdirectory(src/starfield)
|
add_subdirectory(src/starfield)
|
||||||
|
@ -73,6 +77,10 @@ include_directories(
|
||||||
${LODEPNG_INCLUDE_DIRS}
|
${LODEPNG_INCLUDE_DIRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
find_package(Qt4 REQUIRED QtCore QtGui)
|
||||||
|
include(${QT_USE_FILE})
|
||||||
|
target_link_libraries(${TARGET_NAME} ${QT_LIBRARIES})
|
||||||
|
|
||||||
if (NOT APPLE)
|
if (NOT APPLE)
|
||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
find_package(GLUT REQUIRED)
|
find_package(GLUT REQUIRED)
|
||||||
|
|
|
@ -13,12 +13,15 @@
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
#include "Avatar.h"
|
#include "Avatar.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
#include "ui/TextRenderer.h"
|
||||||
#include <AgentList.h>
|
#include <AgentList.h>
|
||||||
#include <AgentTypes.h>
|
#include <AgentTypes.h>
|
||||||
#include <PacketHeaders.h>
|
#include <PacketHeaders.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
const bool BALLS_ON = false;
|
||||||
|
|
||||||
const bool AVATAR_GRAVITY = true;
|
const bool AVATAR_GRAVITY = true;
|
||||||
const float DECAY = 0.1;
|
const float DECAY = 0.1;
|
||||||
const float THRUST_MAG = 1200.0;
|
const float THRUST_MAG = 1200.0;
|
||||||
|
@ -29,7 +32,6 @@ const float MY_HAND_HOLDING_PULL = 0.2;
|
||||||
const float YOUR_HAND_HOLDING_PULL = 1.0;
|
const float YOUR_HAND_HOLDING_PULL = 1.0;
|
||||||
const float BODY_SPRING_FORCE = 6.0f;
|
const float BODY_SPRING_FORCE = 6.0f;
|
||||||
const float BODY_SPRING_DECAY = 16.0f;
|
const float BODY_SPRING_DECAY = 16.0f;
|
||||||
|
|
||||||
//const float COLLISION_FRICTION = 0.5;
|
//const float COLLISION_FRICTION = 0.5;
|
||||||
//const float COLLISION_RADIUS_SCALAR = 1.8;
|
//const float COLLISION_RADIUS_SCALAR = 1.8;
|
||||||
//const float COLLISION_BALL_FORCE = 0.1;
|
//const float COLLISION_BALL_FORCE = 0.1;
|
||||||
|
@ -40,9 +42,7 @@ const float COLLISION_BALL_FORCE = 0.6;
|
||||||
const float COLLISION_BODY_FORCE = 6.0;
|
const float COLLISION_BODY_FORCE = 6.0;
|
||||||
const float COLLISION_BALL_FRICTION = 200.0;
|
const float COLLISION_BALL_FRICTION = 200.0;
|
||||||
const float COLLISION_BODY_FRICTION = 0.5;
|
const float COLLISION_BODY_FRICTION = 0.5;
|
||||||
|
|
||||||
const bool BALLS_ON = false;
|
|
||||||
|
|
||||||
float skinColor[] = {1.0, 0.84, 0.66};
|
float skinColor[] = {1.0, 0.84, 0.66};
|
||||||
float lightBlue[] = { 0.7, 0.8, 1.0 };
|
float lightBlue[] = { 0.7, 0.8, 1.0 };
|
||||||
float browColor[] = {210.0/255.0, 105.0/255.0, 30.0/255.0};
|
float browColor[] = {210.0/255.0, 105.0/255.0, 30.0/255.0};
|
||||||
|
@ -61,7 +61,7 @@ bool usingBigSphereCollisionTest = true;
|
||||||
|
|
||||||
char iris_texture_file[] = "resources/images/green_eye.png";
|
char iris_texture_file[] = "resources/images/green_eye.png";
|
||||||
|
|
||||||
float chatMessageScale = 0.00025;
|
float chatMessageScale = 0.001;
|
||||||
float chatMessageHeight = 0.4;
|
float chatMessageHeight = 0.4;
|
||||||
|
|
||||||
vector<unsigned char> iris_texture;
|
vector<unsigned char> iris_texture;
|
||||||
|
@ -676,6 +676,11 @@ void Avatar::setDisplayingHead( bool displayingHead ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static TextRenderer* textRenderer() {
|
||||||
|
static TextRenderer* renderer = new TextRenderer(SANS_FONT_FAMILY, 24);
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
void Avatar::render(bool lookingInMirror) {
|
void Avatar::render(bool lookingInMirror) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -725,10 +730,10 @@ void Avatar::render(bool lookingInMirror) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_chatMessage.empty()) {
|
if (!_chatMessage.empty()) {
|
||||||
float width = 0;
|
int width = 0;
|
||||||
float lastWidth;
|
int lastWidth;
|
||||||
for (string::iterator it = _chatMessage.begin(); it != _chatMessage.end(); it++) {
|
for (string::iterator it = _chatMessage.begin(); it != _chatMessage.end(); it++) {
|
||||||
width += (lastWidth = glutStrokeWidth(GLUT_STROKE_ROMAN, *it)*chatMessageScale);
|
width += (lastWidth = textRenderer()->computeWidth(*it));
|
||||||
}
|
}
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
|
|
||||||
|
@ -740,11 +745,14 @@ void Avatar::render(bool lookingInMirror) {
|
||||||
|
|
||||||
glTranslatef(_position.x, _position.y + chatMessageHeight, _position.z);
|
glTranslatef(_position.x, _position.y + chatMessageHeight, _position.z);
|
||||||
glRotatef(atan2(-modelview[2], -modelview[10]) * 180 / PI, 0, 1, 0);
|
glRotatef(atan2(-modelview[2], -modelview[10]) * 180 / PI, 0, 1, 0);
|
||||||
glTranslatef(width * 0.5, 0, 0);
|
|
||||||
|
|
||||||
|
glColor3f(0, 0.8, 0);
|
||||||
|
glRotatef(180, 0, 0, 1);
|
||||||
|
glScalef(chatMessageScale, chatMessageScale, 1.0f);
|
||||||
|
|
||||||
glDisable(GL_LIGHTING);
|
glDisable(GL_LIGHTING);
|
||||||
if (_keyState == NO_KEY_DOWN) {
|
if (_keyState == NO_KEY_DOWN) {
|
||||||
drawtext(0, 0, chatMessageScale, 180, 1.0, 0, _chatMessage.c_str(), 0, 1, 0);
|
textRenderer()->draw(-width/2, 0, _chatMessage.c_str());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// rather than using substr and allocating a new string, just replace the last
|
// rather than using substr and allocating a new string, just replace the last
|
||||||
|
@ -752,11 +760,10 @@ void Avatar::render(bool lookingInMirror) {
|
||||||
int lastIndex = _chatMessage.size() - 1;
|
int lastIndex = _chatMessage.size() - 1;
|
||||||
char lastChar = _chatMessage[lastIndex];
|
char lastChar = _chatMessage[lastIndex];
|
||||||
_chatMessage[lastIndex] = '\0';
|
_chatMessage[lastIndex] = '\0';
|
||||||
drawtext(0, 0, chatMessageScale, 180, 1.0, 0, _chatMessage.c_str(), 0, 1, 0);
|
textRenderer()->draw(-width/2, 0, _chatMessage.c_str());
|
||||||
_chatMessage[lastIndex] = lastChar;
|
_chatMessage[lastIndex] = lastChar;
|
||||||
glTranslatef(lastWidth - width, 0, 0);
|
glColor3f(0, 1, 0);
|
||||||
drawtext(0, 0, chatMessageScale, 180, 3.0,
|
textRenderer()->draw(width/2 - lastWidth, 0, _chatMessage.c_str() + lastIndex);
|
||||||
0, _chatMessage.c_str() + lastIndex, 0, 1, 0);
|
|
||||||
}
|
}
|
||||||
glEnable(GL_LIGHTING);
|
glEnable(GL_LIGHTING);
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,7 @@ public:
|
||||||
const bool getHeadReturnToCenter() const { return _returnHeadToCenter; };
|
const bool getHeadReturnToCenter() const { return _returnHeadToCenter; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
struct AvatarBone
|
struct AvatarBone
|
||||||
{
|
{
|
||||||
AvatarBoneID parent; // which bone is this bone connected to?
|
AvatarBoneID parent; // which bone is this bone connected to?
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "AvatarTouch.h"
|
#include "AvatarTouch.h"
|
||||||
#include "InterfaceConfig.h"
|
#include "InterfaceConfig.h"
|
||||||
|
|
||||||
|
const float THREAD_RADIUS = 0.007;
|
||||||
|
|
||||||
AvatarTouch::AvatarTouch() {
|
AvatarTouch::AvatarTouch() {
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,6 @@ public:
|
||||||
void setYourHandPosition( glm::vec3 position );
|
void setYourHandPosition( glm::vec3 position );
|
||||||
void simulate(float deltaTime);
|
void simulate(float deltaTime);
|
||||||
void render();
|
void render();
|
||||||
|
|
||||||
const float THREAD_RADIUS = 0.007;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
#include "ui/TextRenderer.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// anonymous namespace - everything in here only exists within this very .cpp file
|
// anonymous namespace - everything in here only exists within this very .cpp file
|
||||||
|
@ -23,6 +24,8 @@ namespace {
|
||||||
unsigned const LINE_BUFFER_SIZE = 256; // number of lines that are buffered
|
unsigned const LINE_BUFFER_SIZE = 256; // number of lines that are buffered
|
||||||
unsigned const MAX_MESSAGE_LENGTH = 512; // maximum number of characters for a message
|
unsigned const MAX_MESSAGE_LENGTH = 512; // maximum number of characters for a message
|
||||||
|
|
||||||
|
const char* FONT_FAMILY = SANS_FONT_FAMILY;
|
||||||
|
|
||||||
bool const TEXT_MONOSPACED = true;
|
bool const TEXT_MONOSPACED = true;
|
||||||
|
|
||||||
float const TEXT_RED = 0.7f;
|
float const TEXT_RED = 0.7f;
|
||||||
|
@ -194,6 +197,11 @@ void Log::setCharacterSize(unsigned width, unsigned height) {
|
||||||
pthread_mutex_unlock(& _mtx);
|
pthread_mutex_unlock(& _mtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TextRenderer* textRenderer() {
|
||||||
|
static TextRenderer* renderer = new TextRenderer(FONT_FAMILY);
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
void Log::render(unsigned screenWidth, unsigned screenHeight) {
|
void Log::render(unsigned screenWidth, unsigned screenHeight) {
|
||||||
|
|
||||||
// rendering might take some time, so create a local copy of the portion we need
|
// rendering might take some time, so create a local copy of the portion we need
|
||||||
|
@ -261,10 +269,8 @@ void Log::render(unsigned screenWidth, unsigned screenHeight) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get values for rendering
|
// get values for rendering
|
||||||
float scaleFactor = _valCharScale;
|
int yStep = textRenderer()->metrics().lineSpacing();
|
||||||
int yStart = int((screenHeight - _valCharYoffset) / _valCharAspect);
|
int yStart = screenHeight - textRenderer()->metrics().descent();
|
||||||
int yStep = int(_valCharHeight / _valCharAspect);
|
|
||||||
float yScale = _valCharAspect;
|
|
||||||
|
|
||||||
// render text
|
// render text
|
||||||
char** line = _ptrLinesEnd + showLines;
|
char** line = _ptrLinesEnd + showLines;
|
||||||
|
@ -273,11 +279,6 @@ void Log::render(unsigned screenWidth, unsigned screenHeight) {
|
||||||
pthread_mutex_unlock(& _mtx);
|
pthread_mutex_unlock(& _mtx);
|
||||||
// ok, we got all we need
|
// ok, we got all we need
|
||||||
|
|
||||||
GLint matrixMode;
|
|
||||||
glGetIntegerv(GL_MATRIX_MODE, & matrixMode);
|
|
||||||
glPushMatrix();
|
|
||||||
glScalef(1.0f, yScale, 1.0f);
|
|
||||||
|
|
||||||
for (int y = yStart; y > 0; y -= yStep) {
|
for (int y = yStart; y > 0; y -= yStep) {
|
||||||
|
|
||||||
// debug mode: check line pointer is valid
|
// debug mode: check line pointer is valid
|
||||||
|
@ -299,14 +300,11 @@ void Log::render(unsigned screenWidth, unsigned screenHeight) {
|
||||||
assert(! (chars < _ptrCharsEnd || chars >= _ptrCharsEnd + (_ptrCharsEnd - _arrChars)));
|
assert(! (chars < _ptrCharsEnd || chars >= _ptrCharsEnd + (_ptrCharsEnd - _arrChars)));
|
||||||
|
|
||||||
// render the string
|
// render the string
|
||||||
drawtext(x, y, scaleFactor, 0.0f, 1.0f, int(TEXT_MONOSPACED),
|
glColor3f(TEXT_RED, TEXT_GREEN, TEXT_BLUE);
|
||||||
chars, TEXT_RED, TEXT_GREEN, TEXT_BLUE);
|
textRenderer()->draw(x, y, chars);
|
||||||
|
|
||||||
//fprintf(stderr, "Log::render, message = \"%s\"\n", chars);
|
//fprintf(stderr, "Log::render, message = \"%s\"\n", chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
glPopMatrix();
|
|
||||||
glMatrixMode(matrixMode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Log logger;
|
Log logger;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
#include "ui/TextRenderer.h"
|
||||||
#include "world.h"
|
#include "world.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
|
||||||
|
@ -151,19 +152,18 @@ double diffclock(timeval *clock1,timeval *clock2)
|
||||||
return diffms;
|
return diffms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TextRenderer* textRenderer(int mono) {
|
||||||
|
static TextRenderer* monoRenderer = new TextRenderer(MONO_FONT_FAMILY);
|
||||||
|
static TextRenderer* proportionalRenderer = new TextRenderer(SANS_FONT_FAMILY);
|
||||||
|
return mono ? monoRenderer : proportionalRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
int widthText(float scale, int mono, char const* string) {
|
int widthText(float scale, int mono, char const* string) {
|
||||||
int width = 0;
|
return textRenderer(mono)->computeWidth(string) * (scale / 0.10);
|
||||||
if (!mono) {
|
}
|
||||||
width = scale * glutStrokeLength(GLUT_STROKE_ROMAN, (const unsigned char *) string);
|
|
||||||
} else {
|
float widthChar(float scale, int mono, char ch) {
|
||||||
#ifndef WORKAROUND_BROKEN_GLUT_STROKES
|
return textRenderer(mono)->computeWidth(ch) * (scale / 0.10);
|
||||||
width = scale * glutStrokeLength(GLUT_STROKE_MONO_ROMAN, (const unsigned char *) string);
|
|
||||||
#else
|
|
||||||
// return value is unreliable, so just calculate it
|
|
||||||
width = scale * float(strlen(string)) * MONO_STROKE_WIDTH_GLUT;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return width;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawtext(int x, int y, float scale, float rotate, float thick, int mono,
|
void drawtext(int x, int y, float scale, float rotate, float thick, int mono,
|
||||||
|
@ -176,35 +176,12 @@ void drawtext(int x, int y, float scale, float rotate, float thick, int mono,
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glTranslatef( static_cast<float>(x), static_cast<float>(y), 0.0f);
|
glTranslatef( static_cast<float>(x), static_cast<float>(y), 0.0f);
|
||||||
glColor3f(r,g,b);
|
glColor3f(r,g,b);
|
||||||
glRotated(180+rotate,0,0,1);
|
glRotated(rotate,0,0,1);
|
||||||
glRotated(180,0,1,0);
|
// glLineWidth(thick);
|
||||||
glLineWidth(thick);
|
glScalef(scale / 0.10, scale / 0.10, 1.0);
|
||||||
glScalef(scale, scale, 1.0);
|
|
||||||
len = (int) strlen(string);
|
textRenderer(mono)->draw(0, 0, string);
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
{
|
|
||||||
if (!mono) {
|
|
||||||
glutStrokeCharacter(GLUT_STROKE_ROMAN, int(string[i]));
|
|
||||||
} else {
|
|
||||||
#ifdef WORKAROUND_BROKEN_GLUT_STROKES
|
|
||||||
if (string[i] != 'm') {
|
|
||||||
#endif
|
|
||||||
glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, int(string[i]));
|
|
||||||
#ifdef WORKAROUND_BROKEN_GLUT_STROKES
|
|
||||||
} else {
|
|
||||||
// some glut implementations have a broken 'm'...
|
|
||||||
unsigned char tmpStr[2];
|
|
||||||
tmpStr[0] = string[i];
|
|
||||||
tmpStr[1] = '\0';
|
|
||||||
float scale = MONO_STROKE_WIDTH_GLUT / glutStrokeLength(GLUT_STROKE_ROMAN, tmpStr);
|
|
||||||
glScalef(scale, 1.0f, 1.0f);
|
|
||||||
glutStrokeCharacter(GLUT_STROKE_ROMAN, int(string[i]));
|
|
||||||
// staying humble on the stack - might be in projection mode
|
|
||||||
glScalef(1.0f / scale, 1.0f, 1.0f);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,13 @@
|
||||||
|
|
||||||
#include <Orientation.h>
|
#include <Orientation.h>
|
||||||
|
|
||||||
|
// the standard sans serif font family
|
||||||
|
#define SANS_FONT_FAMILY "Helvetica"
|
||||||
|
|
||||||
|
// the standard mono font family
|
||||||
|
#define MONO_FONT_FAMILY "Courier"
|
||||||
|
|
||||||
|
|
||||||
void eulerToOrthonormals(glm::vec3 * angles, glm::vec3 * fwd, glm::vec3 * left, glm::vec3 * up);
|
void eulerToOrthonormals(glm::vec3 * angles, glm::vec3 * fwd, glm::vec3 * left, glm::vec3 * up);
|
||||||
|
|
||||||
float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos);
|
float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos);
|
||||||
|
@ -28,6 +35,7 @@ float randFloat();
|
||||||
void render_world_box();
|
void render_world_box();
|
||||||
void render_vector(glm::vec3 * vec);
|
void render_vector(glm::vec3 * vec);
|
||||||
int widthText(float scale, int mono, char const* string);
|
int widthText(float scale, int mono, char const* string);
|
||||||
|
float widthChar(float scale, int mono, char ch);
|
||||||
void drawtext(int x, int y, float scale, float rotate, float thick, int mono,
|
void drawtext(int x, int y, float scale, float rotate, float thick, int mono,
|
||||||
char const* string, float r=1.0, float g=1.0, float b=1.0);
|
char const* string, float r=1.0, float g=1.0, float b=1.0);
|
||||||
void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, glm::vec3 vec,
|
void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, glm::vec3 vec,
|
||||||
|
|
|
@ -39,6 +39,8 @@
|
||||||
#include <ifaddrs.h>
|
#include <ifaddrs.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
@ -59,11 +61,13 @@
|
||||||
#include "AngleUtil.h"
|
#include "AngleUtil.h"
|
||||||
#include "Stars.h"
|
#include "Stars.h"
|
||||||
|
|
||||||
#include "MenuRow.h"
|
#include "ui/ChatEntry.h"
|
||||||
#include "MenuColumn.h"
|
#include "ui/MenuRow.h"
|
||||||
#include "Menu.h"
|
#include "ui/MenuColumn.h"
|
||||||
|
#include "ui/Menu.h"
|
||||||
|
#include "ui/TextRenderer.h"
|
||||||
|
|
||||||
#include "Camera.h"
|
#include "Camera.h"
|
||||||
#include "ChatEntry.h"
|
|
||||||
#include "Avatar.h"
|
#include "Avatar.h"
|
||||||
#include "Texture.h"
|
#include "Texture.h"
|
||||||
#include <AgentList.h>
|
#include <AgentList.h>
|
||||||
|
@ -86,6 +90,8 @@ using namespace std;
|
||||||
void reshape(int width, int height); // will be defined below
|
void reshape(int width, int height); // will be defined below
|
||||||
void loadViewFrustum(ViewFrustum& viewFrustum); // will be defined below
|
void loadViewFrustum(ViewFrustum& viewFrustum); // will be defined below
|
||||||
|
|
||||||
|
QApplication* app;
|
||||||
|
|
||||||
bool enableNetworkThread = true;
|
bool enableNetworkThread = true;
|
||||||
pthread_t networkReceiveThread;
|
pthread_t networkReceiveThread;
|
||||||
bool stopNetworkReceiveThread = false;
|
bool stopNetworkReceiveThread = false;
|
||||||
|
@ -1329,7 +1335,7 @@ void key(unsigned char k, int x, int y)
|
||||||
if (chatEntry.key(k)) {
|
if (chatEntry.key(k)) {
|
||||||
myAvatar.setKeyState(k == '\b' || k == 127 ? // backspace or delete
|
myAvatar.setKeyState(k == '\b' || k == 127 ? // backspace or delete
|
||||||
DELETE_KEY_DOWN : INSERT_KEY_DOWN);
|
DELETE_KEY_DOWN : INSERT_KEY_DOWN);
|
||||||
myAvatar.setChatMessage(string(chatEntry.getContents().size(), 'X'));
|
myAvatar.setChatMessage(string(chatEntry.getContents().size(), SOLID_BLOCK_CHAR));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
myAvatar.setChatMessage(chatEntry.getContents());
|
myAvatar.setChatMessage(chatEntry.getContents());
|
||||||
|
@ -1617,6 +1623,9 @@ int main(int argc, const char * argv[])
|
||||||
voxels_lib::printLog = & ::printLog;
|
voxels_lib::printLog = & ::printLog;
|
||||||
avatars_lib::printLog = & ::printLog;
|
avatars_lib::printLog = & ::printLog;
|
||||||
|
|
||||||
|
// we need to create a QApplication instance in order to use Qt's font rendering
|
||||||
|
app = new QApplication(argc, const_cast<char**>(argv));
|
||||||
|
|
||||||
unsigned int listenPort = AGENT_SOCKET_LISTEN_PORT;
|
unsigned int listenPort = AGENT_SOCKET_LISTEN_PORT;
|
||||||
const char* portStr = getCmdOption(argc, argv, "--listenPort");
|
const char* portStr = getCmdOption(argc, argv, "--listenPort");
|
||||||
if (portStr) {
|
if (portStr) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ using namespace std;
|
||||||
|
|
||||||
const int MAX_CONTENT_LENGTH = 140;
|
const int MAX_CONTENT_LENGTH = 140;
|
||||||
|
|
||||||
void ChatEntry::clear () {
|
void ChatEntry::clear() {
|
||||||
_contents.clear();
|
_contents.clear();
|
||||||
_cursorPos = 0;
|
_cursorPos = 0;
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ void ChatEntry::render(int screenWidth, int screenHeight) {
|
||||||
|
|
||||||
float width = 0;
|
float width = 0;
|
||||||
for (string::iterator it = _contents.begin(), end = it + _cursorPos; it != end; it++) {
|
for (string::iterator it = _contents.begin(), end = it + _cursorPos; it != end; it++) {
|
||||||
width += glutStrokeWidth(GLUT_STROKE_ROMAN, *it)*0.10;
|
width += widthChar(0.10, 0, *it);
|
||||||
}
|
}
|
||||||
glDisable(GL_LINE_SMOOTH);
|
glDisable(GL_LINE_SMOOTH);
|
||||||
glBegin(GL_LINE_STRIP);
|
glBegin(GL_LINE_STRIP);
|
|
@ -14,9 +14,9 @@
|
||||||
class ChatEntry {
|
class ChatEntry {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
const std::string& getContents () const { return _contents; }
|
const std::string& getContents() const { return _contents; }
|
||||||
|
|
||||||
void clear ();
|
void clear();
|
||||||
|
|
||||||
bool key(unsigned char k);
|
bool key(unsigned char k);
|
||||||
void specialKey(unsigned char k);
|
void specialKey(unsigned char k);
|
|
@ -16,6 +16,7 @@
|
||||||
#include "MenuColumn.h"
|
#include "MenuColumn.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
|
|
||||||
|
#include "ui/TextRenderer.h"
|
||||||
|
|
||||||
MenuColumn::MenuColumn() {
|
MenuColumn::MenuColumn() {
|
||||||
}
|
}
|
||||||
|
@ -137,9 +138,12 @@ int MenuColumn::getMaxRowWidth() {
|
||||||
return maxColumnWidth;
|
return maxColumnWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TextRenderer* textRenderer() {
|
||||||
|
static TextRenderer* renderer = new TextRenderer(SANS_FONT_FAMILY, 11);
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
void MenuColumn::render(int yOffset, int menuHeight, int lineHeight) {
|
void MenuColumn::render(int yOffset, int menuHeight, int lineHeight) {
|
||||||
float scale = 0.09;
|
|
||||||
int mono = 0;
|
|
||||||
int numberOfRows = rows.size();
|
int numberOfRows = rows.size();
|
||||||
if (numberOfRows > 0) {
|
if (numberOfRows > 0) {
|
||||||
|
|
||||||
|
@ -158,7 +162,8 @@ void MenuColumn::render(int yOffset, int menuHeight, int lineHeight) {
|
||||||
char* rowName;
|
char* rowName;
|
||||||
for (unsigned int i = 0; i < rows.size(); ++i) {
|
for (unsigned int i = 0; i < rows.size(); ++i) {
|
||||||
rowName = rows[i].getName();
|
rowName = rows[i].getName();
|
||||||
drawtext(leftPosition + SPACE_BEFORE_ROW_NAME, y+5 + yOffset, scale, 0, 1.0, mono, rowName, 0, 0, 0);
|
glColor3f(0, 0, 0);
|
||||||
|
textRenderer()->draw(leftPosition + SPACE_BEFORE_ROW_NAME, y + 5 + yOffset, rowName);
|
||||||
y += lineHeight;
|
y += lineHeight;
|
||||||
}
|
}
|
||||||
renderMouseOver(yOffset);
|
renderMouseOver(yOffset);
|
142
interface/src/ui/TextRenderer.cpp
Normal file
142
interface/src/ui/TextRenderer.cpp
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
//
|
||||||
|
// TextRenderer.cpp
|
||||||
|
// interface
|
||||||
|
//
|
||||||
|
// Created by Andrzej Kapolka on 4/24/13.
|
||||||
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
|
|
||||||
|
#include <QFont>
|
||||||
|
#include <QPaintEngine>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
|
#include "InterfaceConfig.h"
|
||||||
|
#include "TextRenderer.h"
|
||||||
|
|
||||||
|
// the width/height of the cached glyph textures
|
||||||
|
const int IMAGE_SIZE = 256;
|
||||||
|
|
||||||
|
Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int width) :
|
||||||
|
_textureID(textureID), _location(location), _bounds(bounds), _width(width) {
|
||||||
|
}
|
||||||
|
|
||||||
|
TextRenderer::TextRenderer(const char* family, int pointSize, int weight, bool italic)
|
||||||
|
: _font(family, pointSize, weight, italic),
|
||||||
|
_metrics(_font), _x(IMAGE_SIZE), _y(IMAGE_SIZE), _rowHeight(0) {
|
||||||
|
_font.setKerning(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextRenderer::~TextRenderer() {
|
||||||
|
glDeleteTextures(_allTextureIDs.size(), _allTextureIDs.constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextRenderer::draw(int x, int y, const char* str) {
|
||||||
|
|
||||||
|
glEnable(GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
for (const char* ch = str; *ch != 0; ch++) {
|
||||||
|
const Glyph& glyph = getGlyph(*ch);
|
||||||
|
if (glyph.textureID() == 0) {
|
||||||
|
x += glyph.width();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, glyph.textureID());
|
||||||
|
|
||||||
|
int left = x + glyph.bounds().x();
|
||||||
|
int right = x + glyph.bounds().x() + glyph.bounds().width();
|
||||||
|
int bottom = y + glyph.bounds().y();
|
||||||
|
int top = y + glyph.bounds().y() + glyph.bounds().height();
|
||||||
|
|
||||||
|
float scale = 1.0 / IMAGE_SIZE;
|
||||||
|
float ls = glyph.location().x() * scale;
|
||||||
|
float rs = (glyph.location().x() + glyph.bounds().width()) * scale;
|
||||||
|
float bt = glyph.location().y() * scale;
|
||||||
|
float tt = (glyph.location().y() + glyph.bounds().height()) * scale;
|
||||||
|
|
||||||
|
glBegin(GL_QUADS);
|
||||||
|
glTexCoord2f(ls, bt);
|
||||||
|
glVertex2f(left, bottom);
|
||||||
|
glTexCoord2f(rs, bt);
|
||||||
|
glVertex2f(right, bottom);
|
||||||
|
glTexCoord2f(rs, tt);
|
||||||
|
glVertex2f(right, top);
|
||||||
|
glTexCoord2f(ls, tt);
|
||||||
|
glVertex2f(left, top);
|
||||||
|
glEnd();
|
||||||
|
|
||||||
|
x += glyph.width();
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
glDisable(GL_TEXTURE_2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
int TextRenderer::computeWidth(char ch)
|
||||||
|
{
|
||||||
|
return getGlyph(ch).width();
|
||||||
|
}
|
||||||
|
|
||||||
|
int TextRenderer::computeWidth(const char* str)
|
||||||
|
{
|
||||||
|
int width = 0;
|
||||||
|
for (const char* ch = str; *ch != 0; ch++) {
|
||||||
|
width += computeWidth(*ch);
|
||||||
|
}
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Glyph& TextRenderer::getGlyph(char c) {
|
||||||
|
Glyph& glyph = _glyphs[c];
|
||||||
|
if (glyph.isValid()) {
|
||||||
|
return glyph;
|
||||||
|
}
|
||||||
|
// we use 'J' as a representative size for the solid block character
|
||||||
|
QChar ch = (c == SOLID_BLOCK_CHAR) ? QChar('J') : QChar(c);
|
||||||
|
QRect bounds = _metrics.boundingRect(ch);
|
||||||
|
if (bounds.isEmpty()) {
|
||||||
|
glyph = Glyph(0, QPoint(), QRect(), _metrics.width(ch));
|
||||||
|
return glyph;
|
||||||
|
}
|
||||||
|
// grow the bounds to account for antialiasing
|
||||||
|
bounds.adjust(-1, -1, 1, 1);
|
||||||
|
|
||||||
|
if (_x + bounds.width() > IMAGE_SIZE) {
|
||||||
|
// we can't fit it on the current row; move to next
|
||||||
|
_y += _rowHeight;
|
||||||
|
_x = _rowHeight = 0;
|
||||||
|
}
|
||||||
|
if (_y + bounds.height() > IMAGE_SIZE) {
|
||||||
|
// can't fit it on current texture; make a new one
|
||||||
|
glGenTextures(1, &_currentTextureID);
|
||||||
|
_x = _y = _rowHeight = 0;
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, _currentTextureID);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||||
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
_allTextureIDs.append(_currentTextureID);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, _currentTextureID);
|
||||||
|
}
|
||||||
|
// render the glyph into an image and copy it into the texture
|
||||||
|
QImage image(bounds.width(), bounds.height(), QImage::Format_ARGB32);
|
||||||
|
if (c == SOLID_BLOCK_CHAR) {
|
||||||
|
image.fill(QColor(255, 255, 255));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
image.fill(0);
|
||||||
|
QPainter painter(&image);
|
||||||
|
painter.setFont(_font);
|
||||||
|
painter.setPen(QColor(255, 255, 255));
|
||||||
|
painter.drawText(-bounds.x(), -bounds.y(), ch);
|
||||||
|
}
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, bounds.width(), bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());
|
||||||
|
|
||||||
|
glyph = Glyph(_currentTextureID, QPoint(_x, _y), bounds, _metrics.width(ch));
|
||||||
|
_x += bounds.width();
|
||||||
|
_rowHeight = qMax(_rowHeight, bounds.height());
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
return glyph;
|
||||||
|
}
|
89
interface/src/ui/TextRenderer.h
Normal file
89
interface/src/ui/TextRenderer.h
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
//
|
||||||
|
// TextRenderer.h
|
||||||
|
// interface
|
||||||
|
//
|
||||||
|
// Created by Andrzej Kapolka on 4/26/13.
|
||||||
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __interface__TextRenderer__
|
||||||
|
#define __interface__TextRenderer__
|
||||||
|
|
||||||
|
#include <QFont>
|
||||||
|
#include <QFontMetrics>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
// a special "character" that renders as a solid block
|
||||||
|
const char SOLID_BLOCK_CHAR = 127;
|
||||||
|
|
||||||
|
class Glyph;
|
||||||
|
|
||||||
|
class TextRenderer {
|
||||||
|
public:
|
||||||
|
|
||||||
|
TextRenderer(const char* family, int pointSize = -1, int weight = -1, bool italic = false);
|
||||||
|
~TextRenderer();
|
||||||
|
|
||||||
|
const QFontMetrics& metrics() const { return _metrics; }
|
||||||
|
|
||||||
|
void draw(int x, int y, const char* str);
|
||||||
|
|
||||||
|
int computeWidth(char ch);
|
||||||
|
int computeWidth(const char* str);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const Glyph& getGlyph (char c);
|
||||||
|
|
||||||
|
// the font to render
|
||||||
|
QFont _font;
|
||||||
|
|
||||||
|
// the font metrics
|
||||||
|
QFontMetrics _metrics;
|
||||||
|
|
||||||
|
// maps characters to cached glyph info
|
||||||
|
QHash<char, Glyph> _glyphs;
|
||||||
|
|
||||||
|
// the id of the glyph texture to which we're currently writing
|
||||||
|
GLuint _currentTextureID;
|
||||||
|
|
||||||
|
// the position within the current glyph texture
|
||||||
|
int _x, _y;
|
||||||
|
|
||||||
|
// the height of the current row of characters
|
||||||
|
int _rowHeight;
|
||||||
|
|
||||||
|
// the list of all texture ids for which we're responsible
|
||||||
|
QVector<GLuint> _allTextureIDs;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Glyph {
|
||||||
|
public:
|
||||||
|
|
||||||
|
Glyph(int textureID = 0, const QPoint& location = QPoint(), const QRect& bounds = QRect(), int width = 0);
|
||||||
|
|
||||||
|
GLuint textureID() const { return _textureID; }
|
||||||
|
const QPoint& location () const { return _location; }
|
||||||
|
const QRect& bounds() const { return _bounds; }
|
||||||
|
int width () const { return _width; }
|
||||||
|
|
||||||
|
bool isValid() { return _width != 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// the id of the OpenGL texture containing the glyph
|
||||||
|
GLuint _textureID;
|
||||||
|
|
||||||
|
// the location of the character within the texture
|
||||||
|
QPoint _location;
|
||||||
|
|
||||||
|
// the bounds of the character
|
||||||
|
QRect _bounds;
|
||||||
|
|
||||||
|
// the width of the character (distance to next, as opposed to bounds width)
|
||||||
|
int _width;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(__interface__TextRenderer__) */
|
Loading…
Reference in a new issue