split out entity rendering into subclasses, improved rendering of models

This commit is contained in:
ZappoMan 2014-08-20 15:49:50 -07:00
parent ddae85ce4a
commit ef86d86234
24 changed files with 886 additions and 466 deletions

View file

@ -1088,6 +1088,7 @@ var active;
var newModel;
var browser;
var newBox;
var newSphere;
function initToolBar() {
toolBar = new ToolBar(0, 0, ToolBar.VERTICAL);
// New Model
@ -1118,6 +1119,14 @@ function initToolBar() {
visible: true,
alpha: 0.9
});
newSphere = toolBar.addTool({
imageURL: toolIconUrl + "add-model-tool.svg",
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
width: toolWidth, height: toolHeight,
visible: true,
alpha: 0.9
});
}
function moveOverlays() {
@ -1261,6 +1270,20 @@ function mousePressEvent(event) {
print("Can't create box: Box would be out of bounds.");
}
} else if (newSphere == toolBar.clicked(clickedOverlay)) {
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE));
if (position.x > 0 && position.y > 0 && position.z > 0) {
Entities.addEntity({
type: "Sphere",
position: position,
radius: radiusDefault,
color: { red: 255, green: 0, blue: 0 }
});
} else {
print("Can't create box: Box would be out of bounds.");
}
} else if (browser == toolBar.clicked(clickedOverlay)) {
var url = Window.s3Browse(".*(fbx|FBX)");
if (url == null || url == "") {

View file

@ -2668,9 +2668,20 @@ void Application::updateShadowMap() {
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.1f, 4.0f); // magic numbers courtesy http://www.eecs.berkeley.edu/~ravir/6160/papers/shadowmaps.ppt
_avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE);
_particles.render(OctreeRenderer::SHADOW_RENDER_MODE);
_entities.render(OctreeRenderer::SHADOW_RENDER_MODE);
{
PerformanceTimer perfTimer("avatarManager");
_avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE);
}
{
PerformanceTimer perfTimer("particles");
_particles.render(OctreeRenderer::SHADOW_RENDER_MODE);
}
{
PerformanceTimer perfTimer("entities");
_entities.render(OctreeRenderer::SHADOW_RENDER_MODE);
}
glDisable(GL_POLYGON_OFFSET_FILL);
@ -2867,9 +2878,9 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
// render models...
if (Menu::getInstance()->isOptionChecked(MenuOption::Models)) {
PerformanceTimer perfTimer("models");
PerformanceTimer perfTimer("entities");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... models...");
"Application::displaySide() ... entities...");
_entities.render();
}

View file

@ -15,17 +15,34 @@
#include "InterfaceConfig.h"
#include <ModelEntityItem.h>
#include <BoxEntityItem.h>
#include <ModelEntityItem.h>
#include <PerfStat.h>
#include "Menu.h"
#include "EntityTreeRenderer.h"
#include "RenderableBoxEntityItem.h"
#include "RenderableModelEntityItem.h"
#include "RenderableSphereEntityItem.h"
QThread* EntityTreeRenderer::getMainThread() {
return Application::getInstance()->getEntities()->thread();
}
EntityTreeRenderer::EntityTreeRenderer() :
OctreeRenderer() {
qDebug() << "--- Overriding Entity Factories NOW ---";
REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, RenderableBoxEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, RenderableSphereEntityItem::factory)
qDebug() << "--- DONE Overriding Entity Factories ---";
}
EntityTreeRenderer::~EntityTreeRenderer() {
@ -40,6 +57,7 @@ void EntityTreeRenderer::clear() {
void EntityTreeRenderer::clearModelsCache() {
qDebug() << "EntityTreeRenderer::clearModelsCache()...";
/*
// delete the models in _knownEntityItemModels
foreach(Model* model, _knownEntityItemModels) {
delete model;
@ -50,12 +68,15 @@ void EntityTreeRenderer::clearModelsCache() {
delete model;
}
_unknownEntityItemModels.clear();
foreach(Model* model, _entityItemModels) {
delete model;
}
_entityItemModels.clear();
*/
}
void EntityTreeRenderer::init() {
//REGISTER_ENTITY_TYPE_RENDERER(Model, this->renderEntityTypeModel);
//REGISTER_ENTITY_TYPE_RENDERER(Box, this->renderEntityTypeBox);
OctreeRenderer::init();
static_cast<EntityTree*>(_tree)->setFBXService(this);
}
@ -84,9 +105,10 @@ const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(const EntityItem* en
const FBXGeometry* result = NULL;
if (entityItem->getType() == EntityTypes::Model) {
const ModelEntityItem* modelEntityItem = static_cast<const ModelEntityItem*>(entityItem);
Model* model = getModel(modelEntityItem);
const RenderableModelEntityItem* constModelEntityItem = dynamic_cast<const RenderableModelEntityItem*>(entityItem);
RenderableModelEntityItem* modelEntityItem = const_cast<RenderableModelEntityItem*>(constModelEntityItem);
assert(modelEntityItem); // we need this!!!
Model* model = modelEntityItem->getModel();
if (model) {
result = &model->getGeometry()->getFBXGeometry();
}
@ -97,73 +119,17 @@ const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(const EntityItem* en
const Model* EntityTreeRenderer::getModelForEntityItem(const EntityItem* entityItem) {
const Model* result = NULL;
if (entityItem->getType() == EntityTypes::Model) {
const ModelEntityItem* modelEntityItem = static_cast<const ModelEntityItem*>(entityItem);
const RenderableModelEntityItem* constModelEntityItem = dynamic_cast<const RenderableModelEntityItem*>(entityItem);
RenderableModelEntityItem* modelEntityItem = const_cast<RenderableModelEntityItem*>(constModelEntityItem);
assert(modelEntityItem); // we need this!!!
result = getModel(modelEntityItem);
result = modelEntityItem->getModel();
}
return result;
}
Model* EntityTreeRenderer::getModel(const ModelEntityItem* modelEntityItem) {
Model* model = NULL;
if (!modelEntityItem->getModelURL().isEmpty()) {
if (modelEntityItem->isKnownID()) {
if (_knownEntityItemModels.find(modelEntityItem->getID()) != _knownEntityItemModels.end()) {
model = _knownEntityItemModels[modelEntityItem->getID()];
if (QUrl(modelEntityItem->getModelURL()) != model->getURL()) {
delete model; // delete the old model...
model = NULL;
_knownEntityItemModels.remove(modelEntityItem->getID());
}
}
// if we don't have a model... but our item does have a model URL
if (!model) {
// Make sure we only create new models on the thread that owns the EntityTreeRenderer
if (QThread::currentThread() != thread()) {
qDebug() << "about to call QMetaObject::invokeMethod(this, 'getModel', Qt::BlockingQueuedConnection,...";
QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(Model*, model), Q_ARG(const ModelEntityItem*, modelEntityItem));
qDebug() << "got it... model=" << model;
return model;
}
model = new Model();
model->init();
model->setURL(QUrl(modelEntityItem->getModelURL()));
_knownEntityItemModels[modelEntityItem->getID()] = model;
}
} else {
if (_unknownEntityItemModels.find(modelEntityItem->getCreatorTokenID()) != _unknownEntityItemModels.end()) {
model = _unknownEntityItemModels[modelEntityItem->getCreatorTokenID()];
if (QUrl(modelEntityItem->getModelURL()) != model->getURL()) {
delete model; // delete the old model...
model = NULL;
_unknownEntityItemModels.remove(modelEntityItem->getCreatorTokenID());
}
}
if (!model) {
// Make sure we only create new models on the thread that owns the EntityTreeRenderer
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(Model*, model), Q_ARG(const ModelEntityItem*, modelEntityItem));
return model;
}
model = new Model();
model->init();
model->setURL(QUrl(modelEntityItem->getModelURL()));
_unknownEntityItemModels[modelEntityItem->getCreatorTokenID()] = model;
}
}
}
return model;
}
void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) {
//PerformanceTimer perfTimer("renderElement");
args->_elementsTouched++;
// actually render it here...
// we need to iterate the actual entityItems of the element
@ -271,13 +237,7 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
if (entityItem->getGlowLevel() > 0.0f) {
glower = new Glower(entityItem->getGlowLevel());
}
if (entityItem->getType() == EntityTypes::Model) {
renderEntityTypeModel(entityItem, args);
} else if (entityItem->getType() == EntityTypes::Box) {
renderEntityTypeBox(entityItem, args);
}
entityItem->render(args);
if (glower) {
delete glower;
}
@ -288,217 +248,6 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
}
}
void EntityTreeRenderer::renderEntityTypeModel(EntityItem* entity, RenderArgs* args) {
assert(entity->getType() == EntityTypes::Model);
ModelEntityItem* entityItem = static_cast<ModelEntityItem*>(entity);
bool drawAsModel = entityItem->hasModel();
glm::vec3 position = entityItem->getPosition() * (float)TREE_SCALE;
float radius = entityItem->getRadius() * (float)TREE_SCALE;
float size = entity->getSize() * (float)TREE_SCALE;
if (drawAsModel) {
glPushMatrix();
{
const float alpha = 1.0f;
Model* model = getModel(entityItem);
if (model) {
model->setScaleToFit(true, radius * 2.0f);
model->setSnapModelToCenter(true);
// set the rotation
glm::quat rotation = entityItem->getRotation();
model->setRotation(rotation);
// set the position
model->setTranslation(position);
// handle animations..
if (entityItem->hasAnimation()) {
//qDebug() << "entityItem->hasAnimation()...";
if (!entityItem->jointsMapped()) {
QStringList modelJointNames = model->getJointNames();
entityItem->mapJoints(modelJointNames);
//qDebug() << "entityItem->mapJoints()...";
}
if (entityItem->jointsMapped()) {
//qDebug() << "model->setJointState()...";
QVector<glm::quat> frameData = entityItem->getAnimationFrame();
for (int i = 0; i < frameData.size(); i++) {
model->setJointState(i, true, frameData[i]);
}
}
}
// make sure to simulate so everything gets set up correctly for rendering
model->simulate(0.0f);
// TODO: should we allow entityItems to have alpha on their models?
Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE
? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
if (model->isActive()) {
model->render(alpha, modelRenderMode);
} else {
// if we couldn't get a model, then just draw a sphere
glColor3ub(entityItem->getColor()[RED_INDEX],entityItem->getColor()[GREEN_INDEX],entityItem->getColor()[BLUE_INDEX]);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glutSolidSphere(radius, 15, 15);
glPopMatrix();
}
bool isShadowMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE;
bool displayModelBounds = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelBounds);
if (!isShadowMode && displayModelBounds) {
glm::vec3 unRotatedMinimum = model->getUnscaledMeshExtents().minimum;
glm::vec3 unRotatedMaximum = model->getUnscaledMeshExtents().maximum;
glm::vec3 unRotatedExtents = unRotatedMaximum - unRotatedMinimum;
float width = unRotatedExtents.x;
float height = unRotatedExtents.y;
float depth = unRotatedExtents.z;
Extents rotatedExtents = model->getUnscaledMeshExtents();
calculateRotatedExtents(rotatedExtents, rotation);
glm::vec3 rotatedSize = rotatedExtents.maximum - rotatedExtents.minimum;
const glm::vec3& modelScale = model->getScale();
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
// draw the orignal bounding cube
glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
glutWireCube(size);
// draw the rotated bounding cube
glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
glPushMatrix();
glScalef(rotatedSize.x * modelScale.x, rotatedSize.y * modelScale.y, rotatedSize.z * modelScale.z);
glutWireCube(1.0);
glPopMatrix();
// draw the model relative bounding box
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glScalef(width * modelScale.x, height * modelScale.y, depth * modelScale.z);
glColor3f(0.0f, 1.0f, 0.0f);
glutWireCube(1.0);
glPopMatrix();
}
} else {
// if we couldn't get a model, then just draw a sphere
glColor3ub(entityItem->getColor()[RED_INDEX],entityItem->getColor()[GREEN_INDEX],entityItem->getColor()[BLUE_INDEX]);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glutSolidSphere(radius, 15, 15);
glPopMatrix();
}
}
glPopMatrix();
} else {
glColor3f(1.0f, 0.0f, 0.0f);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glutSolidSphere(radius, 15, 15);
glPopMatrix();
}
}
// vertex array for glDrawElements() and glDrawRangeElement() =================
// Notice that the sizes of these arrays become samller than the arrays for
// glDrawArrays() because glDrawElements() uses an additional index array to
// choose designated vertices with the indices. The size of vertex array is now
// 24 instead of 36, but the index array size is 36, same as the number of
// vertices required to draw a cube.
GLfloat vertices2[] = { 1, 1, 1, -1, 1, 1, -1,-1, 1, 1,-1, 1, // v0,v1,v2,v3 (front)
1, 1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, // v0,v3,v4,v5 (right)
1, 1, 1, 1, 1,-1, -1, 1,-1, -1, 1, 1, // v0,v5,v6,v1 (top)
-1, 1, 1, -1, 1,-1, -1,-1,-1, -1,-1, 1, // v1,v6,v7,v2 (left)
-1,-1,-1, 1,-1,-1, 1,-1, 1, -1,-1, 1, // v7,v4,v3,v2 (bottom)
1,-1,-1, -1,-1,-1, -1, 1,-1, 1, 1,-1 }; // v4,v7,v6,v5 (back)
// normal array
GLfloat normals2[] = { 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, // v0,v1,v2,v3 (front)
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0,v3,v4,v5 (right)
0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, // v0,v5,v6,v1 (top)
-1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, // v1,v6,v7,v2 (left)
0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, // v7,v4,v3,v2 (bottom)
0, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1 }; // v4,v7,v6,v5 (back)
// color array
GLfloat colors2[] = { 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, // v0,v1,v2,v3 (front)
1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, // v0,v3,v4,v5 (right)
1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, // v0,v5,v6,v1 (top)
1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, // v1,v6,v7,v2 (left)
0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, // v7,v4,v3,v2 (bottom)
0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1 }; // v4,v7,v6,v5 (back)
// index array of vertex array for glDrawElements() & glDrawRangeElement()
GLubyte indices[] = { 0, 1, 2, 2, 3, 0, // front
4, 5, 6, 6, 7, 4, // right
8, 9,10, 10,11, 8, // top
12,13,14, 14,15,12, // left
16,17,18, 18,19,16, // bottom
20,21,22, 22,23,20 }; // back
void EntityTreeRenderer::renderEntityTypeBox(EntityItem* entity, RenderArgs* args) {
assert(entity->getType() == EntityTypes::Box);
glm::vec3 position = entity->getPosition() * (float)TREE_SCALE;
float size = entity->getSize() * (float)TREE_SCALE;
glm::quat rotation = entity->getRotation();
BoxEntityItem* boxEntityItem = static_cast<BoxEntityItem*>(entity);
/*
glColor3f(0.0f, 1.0f, 0.0f);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glutSolidCube(size);
glPopMatrix();
*/
// enable and specify pointers to vertex arrays
glEnableClientState(GL_NORMAL_ARRAY);
//glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glNormalPointer(GL_FLOAT, 0, normals2);
//glColorPointer(3, GL_FLOAT, 0, colors2);
glVertexPointer(3, GL_FLOAT, 0, vertices2);
//glEnable(GL_BLEND);
glColor3ub(boxEntityItem->getColor()[RED_INDEX], boxEntityItem->getColor()[GREEN_INDEX], boxEntityItem->getColor()[BLUE_INDEX]);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glScalef(size, size, size);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, indices);
glPopMatrix();
glDisableClientState(GL_VERTEX_ARRAY); // disable vertex arrays
//glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
};
float EntityTreeRenderer::getSizeScale() const {
return Menu::getInstance()->getVoxelSizeScale();
}

View file

@ -25,8 +25,6 @@
#include "renderer/Model.h"
class ModelEntityItem;
// Generic client side Octree renderer class.
class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService {
Q_OBJECT
@ -58,16 +56,19 @@ public:
/// clears the tree
virtual void clear();
Q_INVOKABLE Model* getModel(const ModelEntityItem* modelEntityItem);
//Q_INVOKABLE Model* getModel(const ModelEntityItem* modelEntityItem);
// renderers for various types of entities
void renderEntityTypeBox(EntityItem* entity, RenderArgs* args);
void renderEntityTypeModel(EntityItem* entity, RenderArgs* args);
static QThread* getMainThread();
protected:
void clearModelsCache();
QMap<QUuid, Model*> _knownEntityItemModels;
QMap<uint32_t, Model*> _unknownEntityItemModels;
//QMap<QUuid, Model*> _knownEntityItemModels;
//QMap<uint32_t, Model*> _unknownEntityItemModels;
//QMap<const ModelEntityItem*, Model*> _entityItemModels;
};
#endif // hifi_EntityTreeRenderer_h

View file

@ -0,0 +1,119 @@
//
// EntityTreeRenderer.cpp
// interface/src
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <glm/gtx/quaternion.hpp>
#include <FBXReader.h>
#include "InterfaceConfig.h"
#include <BoxEntityItem.h>
#include <ModelEntityItem.h>
#include <PerfStat.h>
#include "Menu.h"
#include "EntityTreeRenderer.h"
#include "RenderableBoxEntityItem.h"
// vertex array for glDrawElements() and glDrawRangeElement() =================
// Notice that the sizes of these arrays become samller than the arrays for
// glDrawArrays() because glDrawElements() uses an additional index array to
// choose designated vertices with the indices. The size of vertex array is now
// 24 instead of 36, but the index array size is 36, same as the number of
// vertices required to draw a cube.
GLfloat vertices2[] = { 1, 1, 1, -1, 1, 1, -1,-1, 1, 1,-1, 1, // v0,v1,v2,v3 (front)
1, 1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, // v0,v3,v4,v5 (right)
1, 1, 1, 1, 1,-1, -1, 1,-1, -1, 1, 1, // v0,v5,v6,v1 (top)
-1, 1, 1, -1, 1,-1, -1,-1,-1, -1,-1, 1, // v1,v6,v7,v2 (left)
-1,-1,-1, 1,-1,-1, 1,-1, 1, -1,-1, 1, // v7,v4,v3,v2 (bottom)
1,-1,-1, -1,-1,-1, -1, 1,-1, 1, 1,-1 }; // v4,v7,v6,v5 (back)
// normal array
GLfloat normals2[] = { 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, // v0,v1,v2,v3 (front)
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0,v3,v4,v5 (right)
0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, // v0,v5,v6,v1 (top)
-1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, // v1,v6,v7,v2 (left)
0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, // v7,v4,v3,v2 (bottom)
0, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1 }; // v4,v7,v6,v5 (back)
// color array
GLfloat colors2[] = { 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, // v0,v1,v2,v3 (front)
1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, // v0,v3,v4,v5 (right)
1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, // v0,v5,v6,v1 (top)
1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, // v1,v6,v7,v2 (left)
0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, // v7,v4,v3,v2 (bottom)
0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1 }; // v4,v7,v6,v5 (back)
// index array of vertex array for glDrawElements() & glDrawRangeElement()
GLubyte indices[] = { 0, 1, 2, 2, 3, 0, // front
4, 5, 6, 6, 7, 4, // right
8, 9,10, 10,11, 8, // top
12,13,14, 14,15,12, // left
16,17,18, 18,19,16, // bottom
20,21,22, 22,23,20 }; // back
EntityItem* RenderableBoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
qDebug() << "RenderableBoxEntityItem::factory(const EntityItemID& entityItemID, const EntityItemProperties& properties)...";
return new RenderableBoxEntityItem(entityID, properties);
}
void RenderableBoxEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableBoxEntityItem::render");
assert(getType() == EntityTypes::Box);
glm::vec3 position = getPosition() * (float)TREE_SCALE;
float size = getSize() * (float)TREE_SCALE;
glm::quat rotation = getRotation();
/*
glColor3ub(getColor()[RED_INDEX], getColor()[GREEN_INDEX], getColor()[BLUE_INDEX]);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glutSolidCube(size);
glPopMatrix();
*/
// enable and specify pointers to vertex arrays
glEnableClientState(GL_NORMAL_ARRAY);
//glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glNormalPointer(GL_FLOAT, 0, normals2);
//glColorPointer(3, GL_FLOAT, 0, colors2);
glVertexPointer(3, GL_FLOAT, 0, vertices2);
//glEnable(GL_BLEND);
glColor3ub(getColor()[RED_INDEX], getColor()[GREEN_INDEX], getColor()[BLUE_INDEX]);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
// we need to do half the size because the geometry in the VBOs are from -1,-1,-1 to 1,1,1
float halfSize = size/2.0f;
glScalef(halfSize, halfSize, halfSize);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, indices);
glPopMatrix();
glDisableClientState(GL_VERTEX_ARRAY); // disable vertex arrays
//glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
};

View file

@ -0,0 +1,40 @@
//
// RenderableBoxEntityItem.h
// interface/src/entities
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_RenderableBoxEntityItem_h
#define hifi_RenderableBoxEntityItem_h
#include <glm/glm.hpp>
#include <stdint.h>
#include <EntityTree.h>
#include <Octree.h>
#include <OctreePacketData.h>
#include <OctreeRenderer.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <ViewFrustum.h>
#include <BoxEntityItem.h>
class RenderableBoxEntityItem : public BoxEntityItem {
public:
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderableBoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
BoxEntityItem(entityItemID, properties)
{ };
virtual void render(RenderArgs* args);
};
#endif // hifi_RenderableBoxEntityItem_h

View file

@ -0,0 +1,241 @@
//
// EntityTreeRenderer.cpp
// interface/src
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <glm/gtx/quaternion.hpp>
#include <FBXReader.h>
#include "InterfaceConfig.h"
#include <BoxEntityItem.h>
#include <ModelEntityItem.h>
#include <PerfStat.h>
#include "Menu.h"
#include "EntityTreeRenderer.h"
#include "RenderableModelEntityItem.h"
EntityItem* RenderableModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
qDebug() << "RenderableModelEntityItem::factory(const EntityItemID& entityItemID, const EntityItemProperties& properties)...";
return new RenderableModelEntityItem(entityID, properties);
}
bool RenderableModelEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {
QString oldModelURL = getModelURL();
bool somethingChanged = ModelEntityItem::setProperties(properties, forceCopy);
if (somethingChanged && oldModelURL != getModelURL()) {
_needsModelReload = true;
}
return somethingChanged;
}
int RenderableModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData) {
QString oldModelURL = getModelURL();
int bytesRead = ModelEntityItem::readEntitySubclassDataFromBuffer(data, bytesLeftToRead,
args, propertyFlags, overwriteLocalData);
if (oldModelURL != getModelURL()) {
_needsModelReload = true;
}
return bytesRead;
}
void RenderableModelEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableModelEntityItem::render");
assert(getType() == EntityTypes::Model);
bool drawAsModel = hasModel();
glm::vec3 position = getPosition() * (float)TREE_SCALE;
float radius = getRadius() * (float)TREE_SCALE;
float size = getSize() * (float)TREE_SCALE;
if (drawAsModel) {
glPushMatrix();
{
const float alpha = 1.0f;
if (!_model || _needsModelReload) {
// TODO: this getModel() appears to be about 3% of model render time. We should optimize
PerformanceTimer perfTimer("getModel");
getModel();
}
if (_model) {
// handle animations..
if (hasAnimation()) {
//qDebug() << "hasAnimation()...";
if (!jointsMapped()) {
QStringList modelJointNames = _model->getJointNames();
mapJoints(modelJointNames);
//qDebug() << "mapJoints()...";
}
if (jointsMapped()) {
//qDebug() << "_model->setJointState()...";
QVector<glm::quat> frameData = getAnimationFrame();
for (int i = 0; i < frameData.size(); i++) {
_model->setJointState(i, true, frameData[i]);
}
}
}
glm::quat rotation = getRotation();
if (_needsSimulation && _model->isActive()) {
_model->setScaleToFit(true, radius * 2.0f);
_model->setSnapModelToCenter(true);
_model->setRotation(rotation);
_model->setTranslation(position);
// make sure to simulate so everything gets set up correctly for rendering
{
// TODO: _model->simulate() appears to be about 28% of model render time.
// do we really need to call this every frame? I think not. Look into how to
// reduce calls to this.
PerformanceTimer perfTimer("_model->simulate");
_model->simulate(0.0f);
}
_needsSimulation = false;
}
// TODO: should we allow entityItems to have alpha on their models?
Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE
? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
if (_model->isActive()) {
// TODO: this appears to be about 62% of model render time. Is there a way to call this that doesn't
// cost us as much? For example if the same model is used but rendered multiple places is it less
// expensive?
PerformanceTimer perfTimer("model->render");
_model->render(alpha, modelRenderMode);
} else {
// if we couldn't get a model, then just draw a sphere
glColor3ub(getColor()[RED_INDEX],getColor()[GREEN_INDEX],getColor()[BLUE_INDEX]);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glutSolidSphere(radius, 15, 15);
glPopMatrix();
}
bool isShadowMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE;
bool displayModelBounds = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelBounds);
if (!isShadowMode && displayModelBounds) {
PerformanceTimer perfTimer("displayModelBounds");
glm::vec3 unRotatedMinimum = _model->getUnscaledMeshExtents().minimum;
glm::vec3 unRotatedMaximum = _model->getUnscaledMeshExtents().maximum;
glm::vec3 unRotatedExtents = unRotatedMaximum - unRotatedMinimum;
float width = unRotatedExtents.x;
float height = unRotatedExtents.y;
float depth = unRotatedExtents.z;
Extents rotatedExtents = _model->getUnscaledMeshExtents();
calculateRotatedExtents(rotatedExtents, rotation);
glm::vec3 rotatedSize = rotatedExtents.maximum - rotatedExtents.minimum;
const glm::vec3& modelScale = _model->getScale();
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
// draw the orignal bounding cube
glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
glutWireCube(size);
// draw the rotated bounding cube
glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
glPushMatrix();
glScalef(rotatedSize.x * modelScale.x, rotatedSize.y * modelScale.y, rotatedSize.z * modelScale.z);
glutWireCube(1.0);
glPopMatrix();
// draw the model relative bounding box
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glScalef(width * modelScale.x, height * modelScale.y, depth * modelScale.z);
glColor3f(0.0f, 1.0f, 0.0f);
glutWireCube(1.0);
glPopMatrix();
}
} else {
// if we couldn't get a model, then just draw a sphere
glColor3ub(getColor()[RED_INDEX],getColor()[GREEN_INDEX],getColor()[BLUE_INDEX]);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glutSolidSphere(radius, 15, 15);
glPopMatrix();
}
}
glPopMatrix();
} else {
glColor3f(1.0f, 0.0f, 0.0f);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glutSolidSphere(radius, 15, 15);
glPopMatrix();
}
}
Model* RenderableModelEntityItem::getModel() {
_needsModelReload = false; // this is the reload
if (!getModelURL().isEmpty()) {
// double check our URLS match...
if (_model && QUrl(getModelURL()) != _model->getURL()) {
delete _model; // delete the old model...
_model = NULL;
_needsSimulation = true;
}
// if we don't have a model... but our item does have a model URL
if (!_model) {
// Make sure we only create new models on the thread that owns the EntityTreeRenderer
if (QThread::currentThread() != EntityTreeRenderer::getMainThread()) {
// TODO: how do we better handle this??? we may need this for scripting...
// possible go back to a getModel() service on the TreeRenderer, but have it take a URL
// this would allow the entity items to use that service for loading, and since the
// EntityTreeRenderer is a Q_OBJECT it can call invokeMethod()
qDebug() << "can't call getModel() on thread other than rendering thread...";
//qDebug() << "about to call QMetaObject::invokeMethod(this, 'getModel', Qt::BlockingQueuedConnection,...";
//QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection, Q_RETURN_ARG(Model*, _model));
//qDebug() << "got it... _model=" << _model;
return _model;
}
_model = new Model();
_model->init();
_model->setURL(QUrl(getModelURL()));
_needsSimulation = true;
}
} else {
// our URL is empty, we should clean up our old model
if (_model) {
delete _model; // delete the old model...
_model = NULL;
_needsSimulation = true;
}
}
return _model;
}

View file

@ -0,0 +1,56 @@
//
// RenderableModelEntityItem.h
// interface/src/entities
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_RenderableModelEntityItem_h
#define hifi_RenderableModelEntityItem_h
#include <glm/glm.hpp>
#include <stdint.h>
#include <EntityTree.h>
#include <Octree.h>
#include <OctreePacketData.h>
#include <OctreeRenderer.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <ViewFrustum.h>
#include "renderer/Model.h"
#include <ModelEntityItem.h>
#include <BoxEntityItem.h>
class RenderableModelEntityItem : public ModelEntityItem {
public:
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderableModelEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
ModelEntityItem(entityItemID, properties),
_model(NULL),
_needsSimulation(true),
_needsModelReload(true) { };
virtual bool setProperties(const EntityItemProperties& properties, bool forceCopy);
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
virtual void somethingChangedNotification() { _needsSimulation = true; }
virtual void render(RenderArgs* args);
Model* getModel();
private:
Model* _model;
bool _needsSimulation;
bool _needsModelReload;
};
#endif // hifi_RenderableModelEntityItem_h

View file

@ -0,0 +1,50 @@
//
// RenderableSphereEntityItem.cpp
// interface/src
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <glm/gtx/quaternion.hpp>
#include <FBXReader.h>
#include "InterfaceConfig.h"
#include <PerfStat.h>
#include <SphereEntityItem.h>
#include "Menu.h"
#include "EntityTreeRenderer.h"
#include "RenderableSphereEntityItem.h"
EntityItem* RenderableSphereEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
qDebug() << "RenderableSphereEntityItem::factory(const EntityItemID& entityItemID, const EntityItemProperties& properties)...";
return new RenderableSphereEntityItem(entityID, properties);
}
void RenderableSphereEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableSphereEntityItem::render");
assert(getType() == EntityTypes::Sphere);
glm::vec3 position = getPosition() * (float)TREE_SCALE;
float radius = getRadius() * (float)TREE_SCALE;
glm::quat rotation = getRotation();
glColor3ub(getColor()[RED_INDEX], getColor()[GREEN_INDEX], getColor()[BLUE_INDEX]);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
// TODO: we can probably get rid of this rotation until we support different radius dimensions
//glm::vec3 axis = glm::axis(rotation);
//glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glutSolidSphere(radius, 15, 15);
glPopMatrix();
};

View file

@ -0,0 +1,40 @@
//
// RenderableSphereEntityItem.h
// interface/src/entities
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_RenderableSphereEntityItem_h
#define hifi_RenderableSphereEntityItem_h
#include <glm/glm.hpp>
#include <stdint.h>
#include <EntityTree.h>
#include <Octree.h>
#include <OctreePacketData.h>
#include <OctreeRenderer.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <ViewFrustum.h>
#include <SphereEntityItem.h>
class RenderableSphereEntityItem : public SphereEntityItem {
public:
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderableSphereEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
SphereEntityItem(entityItemID, properties)
{ };
virtual void render(RenderArgs* args);
};
#endif // hifi_RenderableSphereEntityItem_h

View file

@ -1242,8 +1242,6 @@ float Model::getLimbLength(int jointIndex) const {
return length;
}
const int BALL_SUBDIVISIONS = 10;
void Model::renderJointCollisionShapes(float alpha) {
// implement this when we have shapes for regular models
}

View file

@ -56,12 +56,12 @@ EntityItemProperties BoxEntityItem::getProperties() const {
return properties;
}
void BoxEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {
bool BoxEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {
qDebug() << "BoxEntityItem::setProperties()...";
qDebug() << "BoxEntityItem::BoxEntityItem() properties.getModelURL()=" << properties.getModelURL();
bool somethingChanged = false;
EntityItem::setProperties(properties, forceCopy); // set the properties in our base class
somethingChanged = EntityItem::setProperties(properties, forceCopy); // set the properties in our base class
if (properties._colorChanged || forceCopy) {
setColor(properties._color);
@ -83,6 +83,7 @@ void BoxEntityItem::setProperties(const EntityItemProperties& properties, bool f
}
setLastEdited(properties._lastEdited);
}
return somethingChanged;
}
int BoxEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,

View file

@ -19,12 +19,12 @@ public:
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
BoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
virtual void somePureVirtualFunction() { }; // allow this class to be constructed
ALLOW_INSTANTIATION // This class can be instantiated
// methods for getting/setting all properties of an entity
virtual EntityItemProperties getProperties() const;
virtual void setProperties(const EntityItemProperties& properties, bool forceCopy = false);
virtual bool setProperties(const EntityItemProperties& properties, bool forceCopy = false);
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;

View file

@ -421,7 +421,7 @@ int EntityItem::expectedBytes() {
int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) {
bool wantDebug = true;
bool wantDebug = false;
if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU) {
qDebug() << "EntityItem::readEntityDataFromBuffer()... ERROR CASE...args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU";
@ -499,6 +499,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
}
_lastEdited = lastEditedFromBuffer;
somethingChangedNotification(); // notify derived classes that something has changed
}
// last updated is stored as ByteCountCoded delta from lastEdited
@ -828,7 +831,7 @@ EntityItemProperties EntityItem::getProperties() const {
return properties;
}
void EntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {
bool EntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {
//qDebug() << "EntityItem::setProperties()... forceCopy=" << forceCopy;
//qDebug() << "EntityItem::setProperties() properties.getDamping()=" << properties.getDamping();
//qDebug() << "EntityItem::setProperties() properties.getVelocity()=" << properties.getVelocity();
@ -886,6 +889,7 @@ void EntityItem::setProperties(const EntityItemProperties& properties, bool forc
}
if (somethingChanged) {
somethingChangedNotification(); // notify derived classes that something has changed
bool wantDebug = false;
if (wantDebug) {
uint64_t now = usecTimestampNow();
@ -895,6 +899,7 @@ void EntityItem::setProperties(const EntityItemProperties& properties, bool forc
}
setLastEdited(properties._lastEdited);
}
return somethingChanged;
}

View file

@ -27,15 +27,21 @@
class EntityTreeElementExtraEncodeData;
/// EntityItem class - this is the actual model item class.
#define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0;
#define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() { };
/// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available
/// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate
/// one directly, instead you must only construct one of it's derived classes with additional features.
class EntityItem {
public:
DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly
EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
virtual ~EntityItem();
virtual void somePureVirtualFunction() = 0;
// ID and EntityItemID related methods
QUuid getID() const { return _id; }
@ -48,7 +54,13 @@ public:
// methods for getting/setting all properties of an entity
virtual EntityItemProperties getProperties() const;
virtual void setProperties(const EntityItemProperties& properties, bool forceCopy = false);
/// returns true is something changed
virtual bool setProperties(const EntityItemProperties& properties, bool forceCopy = false);
/// override this in your derived class if you'd like to be informed when something about the state of the entity
/// has changed. This will be called with properties change or when new data is loaded from a stream
virtual void somethingChangedNotification() { }
quint64 getLastUpdated() const { return _lastUpdated; } /// Last simulated time of this entity universal usecs
quint64 getLastEdited() const { return _lastEdited; } /// Last edited time of this entity universal usecs
@ -78,7 +90,9 @@ public:
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData)
{ return 0; };
{ return 0; }
virtual void render(RenderArgs* args) { } // by default entity items don't know how to render
static int expectedBytes();
@ -164,7 +178,7 @@ public:
static const QString DEFAULT_SCRIPT;
const QString& getScript() const { return _script; }
void setScript(const QString& value) { _script = value; }
protected:
virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init
@ -188,52 +202,4 @@ protected:
QString _script;
};
class SphereEntityItem : public EntityItem {
public:
SphereEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
EntityItem(entityItemID, properties) { _type = EntityTypes::Sphere; }
virtual void somePureVirtualFunction() { }; // allow this class to be constructed
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
return new SphereEntityItem(entityID, properties);
}
};
class PlaneEntityItem : public EntityItem {
public:
PlaneEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
EntityItem(entityItemID, properties) { _type = EntityTypes::Plane; }
virtual void somePureVirtualFunction() { }; // allow this class to be constructed
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
return new PlaneEntityItem(entityID, properties);
}
};
class CylinderEntityItem : public EntityItem {
public:
CylinderEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
EntityItem(entityItemID, properties) { _type = EntityTypes::Cylinder; }
virtual void somePureVirtualFunction() { }; // allow this class to be constructed
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
return new CylinderEntityItem(entityID, properties);
}
};
class PyramidEntityItem : public EntityItem {
public:
PyramidEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
EntityItem(entityItemID, properties) { _type = EntityTypes::Pyramid; }
virtual void somePureVirtualFunction() { }; // allow this class to be constructed
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
return new PyramidEntityItem(entityID, properties);
}
};
#endif // hifi_EntityItem_h

View file

@ -180,6 +180,23 @@ public:
static bool decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes,
EntityItemID& entityID, EntityItemProperties& properties);
bool positionChanged() const { return _positionChanged; }
bool radiusChanged() const { return _radiusChanged; }
bool rotationChanged() const { return _rotationChanged; }
bool massChanged() const { return _massChanged; }
bool velocityChanged() const { return _velocityChanged; }
bool gravityChanged() const { return _gravityChanged; }
bool dampingChanged() const { return _dampingChanged; }
bool lifetimeChanged() const { return _lifetimeChanged; }
bool scriptChanged() const { return _scriptChanged; }
bool colorChanged() const { return _colorChanged; }
bool modelURLChanged() const { return _modelURLChanged; }
bool animationURLChanged() const { return _animationURLChanged; }
bool animationIsPlayingChanged() const { return _animationIsPlayingChanged; }
bool animationFrameIndexChanged() const { return _animationFrameIndexChanged; }
bool animationFPSChanged() const { return _animationFPSChanged; }
bool glowLevelChanged() const { return _glowLevelChanged; }
private:
void setLastEdited(quint64 lastEdited) { _lastEdited = lastEdited; }

View file

@ -20,23 +20,19 @@
#include "BoxEntityItem.h"
#include "ModelEntityItem.h"
#include "SphereEntityItem.h"
QMap<EntityTypes::EntityType, QString> EntityTypes::_typeToNameMap;
QMap<QString, EntityTypes::EntityType> EntityTypes::_nameToTypeMap;
EntityTypeFactory EntityTypes::_factories[EntityTypes::LAST];
bool EntityTypes::_factoriesInitialized = false;
EntityTypeRenderer EntityTypes::_renderers[EntityTypes::LAST];
bool EntityTypes::_renderersInitialized = false;
const QString ENTITY_TYPE_NAME_UNKNOWN = "Unknown";
// Register Entity Types here...
// Register Entity the default implementations of entity types here...
REGISTER_ENTITY_TYPE(Model)
REGISTER_ENTITY_TYPE(Box)
REGISTER_ENTITY_TYPE(Sphere)
REGISTER_ENTITY_TYPE(Plane)
REGISTER_ENTITY_TYPE(Cylinder)
REGISTER_ENTITY_TYPE(Pyramid)
const QString& EntityTypes::getEntityTypeName(EntityType entityType) {
@ -153,24 +149,3 @@ EntityItem* EntityTypes::constructEntityItem(const unsigned char* data, int byte
return NULL;
}
bool EntityTypes::registerEntityTypeRenderer(EntityType entityType, EntityTypeRenderer renderMethod) {
if (!_renderersInitialized) {
memset(&_renderers,0,sizeof(_renderers));
_renderersInitialized = true;
}
_renderers[entityType] = renderMethod;
return true;
}
void EntityTypes::renderEntityItem(EntityItem* entityItem, RenderArgs* args) {
EntityType entityType = entityItem->getType();
EntityTypeRenderer renderMethod = _renderers[entityType];
if (renderMethod) {
renderMethod(entityItem, args);
}
}

View file

@ -25,7 +25,6 @@ class EntityItemProperties;
class ReadBitstreamToTreeParams;
typedef EntityItem* (*EntityTypeFactory)(const EntityItemID& entityID, const EntityItemProperties& properties);
typedef void (*EntityTypeRenderer)(EntityItem* entity, RenderArgs* args);
class EntityTypes {
public:
@ -34,10 +33,7 @@ public:
Model,
Box,
Sphere,
Plane,
Cylinder,
Pyramid,
LAST = Pyramid
LAST = Sphere
} EntityType;
static const QString& getEntityTypeName(EntityType entityType);
@ -46,16 +42,11 @@ public:
static EntityItem* constructEntityItem(EntityType entityType, const EntityItemID& entityID, const EntityItemProperties& properties);
static EntityItem* constructEntityItem(const unsigned char* data, int bytesToRead, ReadBitstreamToTreeParams& args);
static bool registerEntityTypeRenderer(EntityType entityType, EntityTypeRenderer renderMethod);
static void renderEntityItem(EntityItem* entityItem, RenderArgs* args);
private:
static QMap<EntityType, QString> _typeToNameMap;
static QMap<QString, EntityTypes::EntityType> _nameToTypeMap;
static EntityTypeFactory _factories[LAST];
static bool _factoriesInitialized;
static EntityTypeRenderer _renderers[LAST];
static bool _renderersInitialized;
};
@ -66,7 +57,13 @@ private:
#define REGISTER_ENTITY_TYPE(x) static bool x##Registration = \
EntityTypes::registerEntityType(EntityTypes::x, #x, x##EntityItem::factory);
#define REGISTER_ENTITY_TYPE_RENDERER(x,y) EntityTypes::registerEntityTypeRenderer(EntityTypes::x, y);
/// Macro for registering entity types with an overloaded factory. Like using the REGISTER_ENTITY_TYPE macro: Make sure to add
/// an element to the EntityType enum with your name. But unlike REGISTER_ENTITY_TYPE, your class can be named anything
/// so long as you provide a static method passed to the macro, that takes an EnityItemID, and EntityItemProperties and
/// returns a newly constructed (heap allocated) instance of your type. e.g. The following prototype:
// static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
#define REGISTER_ENTITY_TYPE_WITH_FACTORY(x,y) static bool x##Registration = \
EntityTypes::registerEntityType(EntityTypes::x, #x, y);
#endif // hifi_EntityTypes_h

View file

@ -63,11 +63,11 @@ EntityItemProperties ModelEntityItem::getProperties() const {
return properties;
}
void ModelEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {
bool ModelEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {
//qDebug() << "ModelEntityItem::setProperties()...";
bool somethingChanged = false;
EntityItem::setProperties(properties, forceCopy); // set the properties in our base class
somethingChanged = EntityItem::setProperties(properties, forceCopy); // set the properties in our base class
if (properties._colorChanged || forceCopy) {
setColor(properties._color);
@ -114,6 +114,8 @@ void ModelEntityItem::setProperties(const EntityItemProperties& properties, bool
}
setLastEdited(properties._lastEdited);
}
return somethingChanged;
}

View file

@ -20,11 +20,11 @@ public:
ModelEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
virtual void somePureVirtualFunction() { }; // allow this class to be constructed
ALLOW_INSTANTIATION // This class can be instantiated
// methods for getting/setting all properties of an entity
virtual EntityItemProperties getProperties() const;
virtual void setProperties(const EntityItemProperties& properties, bool forceCopy = false);
virtual bool setProperties(const EntityItemProperties& properties, bool forceCopy = false);
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;

View file

@ -0,0 +1,128 @@
//
// SphereEntityItem.cpp
// libraries/entities/src
//
// Created by Brad Hefta-Gaub on 12/4/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QDebug>
#include <ByteCountCoding.h>
#include "EntityTree.h"
#include "EntityTreeElement.h"
#include "SphereEntityItem.h"
EntityItem* SphereEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
qDebug() << "SphereEntityItem::factory(const EntityItemID& entityItemID, const EntityItemProperties& properties)...";
return new SphereEntityItem(entityID, properties);
}
// our non-pure virtual subclass for now...
SphereEntityItem::SphereEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
EntityItem(entityItemID, properties)
{
_type = EntityTypes::Sphere;
setProperties(properties);
}
EntityItemProperties SphereEntityItem::getProperties() const {
EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class
properties.setColor(getXColor());
properties.setGlowLevel(getGlowLevel());
return properties;
}
bool SphereEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {
bool somethingChanged = EntityItem::setProperties(properties, forceCopy); // set the properties in our base class
if (properties.colorChanged() || forceCopy) {
setColor(properties.getColor());
somethingChanged = true;
}
if (properties.glowLevelChanged() || forceCopy) {
setGlowLevel(properties.getGlowLevel());
somethingChanged = true;
}
if (somethingChanged) {
bool wantDebug = false;
if (wantDebug) {
uint64_t now = usecTimestampNow();
int elapsed = now - _lastEdited;
qDebug() << "SphereEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
"now=" << now << " _lastEdited=" << _lastEdited;
}
setLastEdited(properties.getLastEdited());
}
return somethingChanged;
}
int SphereEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData) {
int bytesRead = 0;
const unsigned char* dataAt = data;
// PROP_COLOR
if (propertyFlags.getHasProperty(PROP_COLOR)) {
rgbColor color;
if (overwriteLocalData) {
memcpy(_color, dataAt, sizeof(_color));
}
dataAt += sizeof(color);
bytesRead += sizeof(color);
}
return bytesRead;
}
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
EntityPropertyFlags SphereEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_COLOR;
return requestedProperties;
}
void SphereEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit,
int& propertyCount,
OctreeElement::AppendState& appendState) const {
bool successPropertyFits = true;
// PROP_COLOR
if (requestedProperties.getHasProperty(PROP_COLOR)) {
//qDebug() << "PROP_COLOR requested...";
LevelDetails propertyLevel = packetData->startLevel();
successPropertyFits = packetData->appendColor(getColor());
if (successPropertyFits) {
propertyFlags |= PROP_COLOR;
propertiesDidntFit -= PROP_COLOR;
propertyCount++;
packetData->endLevel(propertyLevel);
} else {
//qDebug() << "PROP_COLOR didn't fit...";
packetData->discardLevel(propertyLevel);
appendState = OctreeElement::PARTIAL;
}
} else {
//qDebug() << "PROP_COLOR NOT requested...";
propertiesDidntFit -= PROP_COLOR;
}
}

View file

@ -0,0 +1,57 @@
//
// SphereEntityItem.h
// libraries/entities/src
//
// Created by Brad Hefta-Gaub on 12/4/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_SphereEntityItem_h
#define hifi_SphereEntityItem_h
#include "EntityItem.h"
class SphereEntityItem : public EntityItem {
public:
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
SphereEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
ALLOW_INSTANTIATION // This class can be instantiated
// methods for getting/setting all properties of an entity
virtual EntityItemProperties getProperties() const;
virtual bool setProperties(const EntityItemProperties& properties, bool forceCopy = false);
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit,
int& propertyCount,
OctreeElement::AppendState& appendState) const;
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
const rgbColor& getColor() const { return _color; }
xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); }
void setColor(const xColor& value) {
_color[RED_INDEX] = value.red;
_color[GREEN_INDEX] = value.green;
_color[BLUE_INDEX] = value.blue;
}
protected:
rgbColor _color;
};
#endif // hifi_SphereEntityItem_h

View file

@ -33,6 +33,12 @@ Model properties:
//
// REQUIRED TO DO:
0) render performance of models...
a) make getModel() faster... consider storing the Model* in the actual EntityItem class
b) figure out how to only call simulate when needed:
if the properties change
when animated?
1) verify lots of models in single element works
-- repro case - run editModelsExample.js -- create 10 models in the same octree element
@ -62,7 +68,6 @@ Model properties:
7) Handle the ID -> UUID swap in old files to new files
8) Test models -> attachments logic
9) What happens if the edit properties don't fit in a single message MTU???
10) animations not always working?????
@ -79,7 +84,6 @@ Model properties:
P) unit tests?
Pa) OctreeTests::modelItemTests()...????
G) why does is the Box entity not drawn in it's bounds
H) make the rotated model bounds work for other entity types?
I) maybe make "hasGeometry" be a property of EntityItem base class??
@ -237,3 +241,4 @@ Model properties:
// maybe remember the extra state before removing it and if it fails, always re-add it
// maybe add support for "reserving" bytes in the packet
// SOLVED -- BROKEN File persistence... -- added chunking support in SVO file persistence.
// SOLVED -- G) why does is the Box entity not drawn in it's bounds

View file

@ -236,13 +236,6 @@ OctreeElement* Octree::createMissingElement(OctreeElement* lastParentElement, co
int Octree::readElementData(OctreeElement* destinationElement, const unsigned char* nodeData, int bytesLeftToRead,
ReadBitstreamToTreeParams& args) {
bool wantDebug = true;
if (wantDebug) {
qDebug() << "Octree::readElementData()";
qDebug() << " destinationElement->getAACube()=" << destinationElement->getAACube();
qDebug() << " bytesLeftToRead=" << bytesLeftToRead;
}
// give this destination element the child mask from the packet
const unsigned char ALL_CHILDREN_ASSUMED_TO_EXIST = 0xFF;
unsigned char colorInPacketMask = *nodeData;
@ -287,8 +280,6 @@ if (wantDebug) {
int childIndex = 0;
bytesRead += args.includeExistsBits ? sizeof(childrenInTreeMask) + sizeof(childMask) : sizeof(childMask);
//qDebug() << "Octree::readElementData()... childrenInTreeMask=" << childrenInTreeMask;
while (bytesLeftToRead - bytesRead > 0 && childIndex < NUMBER_OF_CHILDREN) {
// check the exists mask to see if we have a child to traverse into
@ -324,33 +315,15 @@ if (wantDebug) {
// if this is the root, and there is more data to read, allow it to read it's element data...
if (destinationElement == _rootElement && rootElementHasData() && (bytesLeftToRead - bytesRead) > 0) {
// tell the element to read the subsequent data
if (wantDebug) {
qDebug() << "Octree::readElementData().... reading element data for root element.....";
qDebug() << " bytesRead=" << bytesRead;
qDebug() << " bytesLeftToRead - bytesRead=" << bytesLeftToRead - bytesRead;
qDebug() << " READING ROOT DATA....";
}
bytesRead += _rootElement->readElementDataFromBuffer(nodeData + bytesRead, bytesLeftToRead - bytesRead, args);
}
if (wantDebug) {
qDebug() << "Octree::readElementData()";
qDebug() << " bytesRead=" << bytesLeftToRead;
}
return bytesRead;
}
void Octree::readBitstreamToTree(const unsigned char * bitstream, unsigned long int bufferSizeBytes,
ReadBitstreamToTreeParams& args) {
bool wantDebug = true;
if (wantDebug) {
qDebug() << "Octree::readBitstreamToTree()";
qDebug() << " bufferSizeBytes=" << bufferSizeBytes;
}
int bytesRead = 0;
const unsigned char* bitstreamAt = bitstream;
@ -379,31 +352,15 @@ if (wantDebug) {
int octalCodeBytes = bytesRequiredForCodeLength(*bitstreamAt);
if (wantDebug) {
qDebug() << "Octree::readBitstreamToTree()";
qDebug() << " octalCodeBytes=" << octalCodeBytes;
}
int theseBytesRead = 0;
theseBytesRead += octalCodeBytes;
if (wantDebug) {
qDebug() << " --- calling readElementData() bytes available=" << (bufferSizeBytes - (bytesRead + octalCodeBytes)) << "---";
}
theseBytesRead += readElementData(bitstreamRootElement, bitstreamAt + octalCodeBytes,
bufferSizeBytes - (bytesRead + octalCodeBytes), args);
if (wantDebug) {
qDebug() << " --- AFTER calling readElementData() ---";
qDebug() << " theseBytesRead=" << theseBytesRead;
}
// skip bitstream to new startPoint
bitstreamAt += theseBytesRead;
bytesRead += theseBytesRead;
if (wantDebug) {
qDebug() << " bytesRead=" << bytesRead;
}
if (args.wantImportProgress) {
emit importProgress((100 * (bitstreamAt - bitstream)) / bufferSizeBytes);
}
@ -1891,12 +1848,6 @@ bool Octree::readFromSVOFile(const char* fileName) {
unsigned long headerLength = 0; // bytes in the header
bool wantDebug = true;
if (wantDebug) {
qDebug() << "Octree::readFromSVOFile()";
qDebug() << " fileLength=" << fileLength;
}
bool wantImportProgress = true;
// before reading the file, check to see if this version of the Octree supports file versions
@ -1964,11 +1915,6 @@ bool Octree::readFromSVOFile(const char* fileName) {
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0,
SharedNodePointer(), wantImportProgress, gotVersion);
if (wantDebug) {
qDebug() << " --- after reading header, type and version ---";
qDebug() << " dataLength=" << dataLength;
qDebug() << " --- calling readBitstreamToTree() ---";
}
readBitstreamToTree(dataAt, dataLength, args);
delete[] entireFileDataSection;
@ -1985,8 +1931,6 @@ bool Octree::readFromSVOFile(const char* fileName) {
file.read((char*)&chunkLength, sizeof(chunkLength)); // read the chunk size from the file
qDebug() << "read chunk size of:" << chunkLength;
remainingLength -= sizeof(chunkLength);
if (chunkLength > remainingLength) {
@ -2011,9 +1955,6 @@ bool Octree::readFromSVOFile(const char* fileName) {
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0,
SharedNodePointer(), wantImportProgress, gotVersion);
if (wantDebug) {
qDebug() << " --- calling readBitstreamToTree() dataLength:" << dataLength << "---";
}
readBitstreamToTree(dataAt, dataLength, args);
}
@ -2084,7 +2025,6 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) {
if (hasBufferBreaks) {
quint16 bufferSize = packetData.getFinalizedSize();
file.write((const char*)&bufferSize, sizeof(bufferSize));
qDebug() << "wrote chunk size of:" << bufferSize << "---";
}
file.write((const char*)packetData.getFinalizedData(), packetData.getFinalizedSize());
lastPacketWritten = true;
@ -2102,7 +2042,6 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) {
if (hasBufferBreaks) {
quint16 bufferSize = packetData.getFinalizedSize();
file.write((const char*)&bufferSize, sizeof(bufferSize));
qDebug() << "wrote FINAL chunk size of:" << bufferSize << "---";
}
file.write((const char*)packetData.getFinalizedData(), packetData.getFinalizedSize());
}