diff --git a/examples/editEntities.js b/examples/editEntities.js
index e6d4534b86..689f73eed4 100644
--- a/examples/editEntities.js
+++ b/examples/editEntities.js
@@ -385,7 +385,7 @@ var toolBar = (function () {
} else if (browseModelsButtonDown) {
var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
if (browseModelsButton === toolBar.clicked(clickedOverlay)) {
- url = Window.s3Browse(".*(fbx|FBX)");
+ url = Window.s3Browse(".*(fbx|FBX|obj|OBJ)");
if (url !== null && url !== "") {
addModel(url);
}
diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html
index cb49b86975..596bf5c9d5 100644
--- a/examples/html/entityProperties.html
+++ b/examples/html/entityProperties.html
@@ -158,6 +158,7 @@
var elModelSections = document.querySelectorAll(".model-section");
var elModelURL = document.getElementById("property-model-url");
+ var elCollisionModelURL = document.getElementById("property-collision-model-url");
var elModelAnimationURL = document.getElementById("property-model-animation-url");
var elModelAnimationPlaying = document.getElementById("property-model-animation-playing");
var elModelAnimationFPS = document.getElementById("property-model-animation-fps");
@@ -287,6 +288,7 @@
}
elModelURL.value = properties.modelURL;
+ elCollisionModelURL.value = properties.collisionModelURL;
elModelAnimationURL.value = properties.animationURL;
elModelAnimationPlaying.checked = properties.animationIsPlaying;
elModelAnimationFPS.value = properties.animationFPS;
@@ -411,6 +413,7 @@
elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff'));
elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL'));
+ elCollisionModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionModelURL'));
elModelAnimationURL.addEventListener('change', createEmitTextPropertyUpdateFunction('animationURL'));
elModelAnimationPlaying.addEventListener('change', createEmitCheckedPropertyUpdateFunction('animationIsPlaying'));
elModelAnimationFPS.addEventListener('change', createEmitNumberPropertyUpdateFunction('animationFPS'));
@@ -642,6 +645,12 @@
+
+
Collision Model URL
+
+
+
+
Animation URL
diff --git a/examples/libraries/ToolTip.js b/examples/libraries/ToolTip.js
index f12525af57..680f617436 100644
--- a/examples/libraries/ToolTip.js
+++ b/examples/libraries/ToolTip.js
@@ -53,6 +53,7 @@ function Tooltip() {
text += "ID: " + properties.id + "\n"
if (properties.type == "Model") {
text += "Model URL: " + properties.modelURL + "\n"
+ text += "Collision Model URL: " + properties.collisionModelURL + "\n"
text += "Animation URL: " + properties.animationURL + "\n"
text += "Animation is playing: " + properties.animationIsPlaying + "\n"
if (properties.sittingPoints && properties.sittingPoints.length > 0) {
diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js
index 7b0d3eb382..9d934ee677 100644
--- a/examples/libraries/entityPropertyDialogBox.js
+++ b/examples/libraries/entityPropertyDialogBox.js
@@ -52,6 +52,8 @@ EntityPropertyDialogBox = (function () {
if (properties.type == "Model") {
array.push({ label: "Model URL:", value: properties.modelURL });
index++;
+ array.push({ label: "Collision Model URL:", value: properties.collisionModelURL });
+ index++;
array.push({ label: "Animation URL:", value: properties.animationURL });
index++;
array.push({ label: "Animation is playing:", type: "checkbox", value: properties.animationIsPlaying });
@@ -275,6 +277,7 @@ EntityPropertyDialogBox = (function () {
properties.locked = array[index++].value;
if (properties.type == "Model") {
properties.modelURL = array[index++].value;
+ properties.collisionModelURL = array[index++].value;
properties.animationURL = array[index++].value;
var newAnimationIsPlaying = array[index++].value;
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 6bb4a20095..d8ccf1bbf2 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1143,6 +1143,10 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
}
+
+ case Qt::Key_Comma: {
+ _myAvatar->togglePhysicsEnabled();
+ }
default:
event->ignore();
@@ -1807,6 +1811,9 @@ void Application::init() {
tree->setSimulation(&_physicsEngine);
_physicsEngine.init(&_entityEditSender);
+
+ _physicsEngine.setAvatarData(_myAvatar);
+
auto entityScriptingInterface = DependencyManager::get
();
connect(&_physicsEngine, &EntitySimulation::entityCollisionWithEntity,
@@ -2869,6 +2876,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
DependencyManager::get()->setAmbientLightMode(getRenderAmbientLight());
auto skyStage = DependencyManager::get()->getSkyStage();
DependencyManager::get()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity());
+ DependencyManager::get()->setGlobalAtmosphere(skyStage->getAtmosphere());
PROFILE_RANGE("DeferredLighting");
PerformanceTimer perfTimer("lighting");
@@ -3908,8 +3916,8 @@ void Application::takeSnapshot() {
}
void Application::setVSyncEnabled() {
- bool vsyncOn = Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerateVSyncOn);
#if defined(Q_OS_WIN)
+ bool vsyncOn = Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerateVSyncOn);
if (wglewGetExtension("WGL_EXT_swap_control")) {
wglSwapIntervalEXT(vsyncOn);
int swapInterval = wglGetSwapIntervalEXT();
@@ -3932,7 +3940,6 @@ void Application::setVSyncEnabled() {
#else
qDebug("V-Sync is FORCED ON on this system\n");
#endif
- vsyncOn = true; // Turns off unused variable warning
}
bool Application::isVSyncOn() const {
diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp
index f1c5def149..4093b3e610 100644
--- a/interface/src/ModelUploader.cpp
+++ b/interface/src/ModelUploader.cpp
@@ -332,8 +332,7 @@ void ModelUploader::populateBasicMapping(QVariantHash& mapping, QString filename
// mixamo blendshapes - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will
// be rewritten, so we detect the existence of several different blendshapes which indicate we're likely a mixamo file
bool likelyMixamoFile = geometry.applicationName == "mixamo.com" ||
- (geometry.blendshapeChannelNames.contains("Facial_Blends") &&
- geometry.blendshapeChannelNames.contains("BrowsDown_Right") &&
+ (geometry.blendshapeChannelNames.contains("BrowsDown_Right") &&
geometry.blendshapeChannelNames.contains("MouthOpen") &&
geometry.blendshapeChannelNames.contains("Blink_Left") &&
geometry.blendshapeChannelNames.contains("Blink_Right") &&
diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index cac071a677..3333c0ab92 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -63,7 +63,6 @@ Avatar::Avatar() :
_skeletonModel(this),
_skeletonOffset(0.0f),
_bodyYawDelta(0.0f),
- _velocity(0.0f),
_positionDeltaAccumulator(0.0f),
_lastVelocity(0.0f),
_acceleration(0.0f),
diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h
index 432d8ad3ab..2951208d95 100644
--- a/interface/src/avatar/Avatar.h
+++ b/interface/src/avatar/Avatar.h
@@ -155,7 +155,6 @@ public:
Q_INVOKABLE glm::vec3 getNeckPosition() const;
- Q_INVOKABLE glm::vec3 getVelocity() const { return _velocity; }
Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; }
Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; }
Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; }
@@ -184,11 +183,9 @@ protected:
QVector _attachmentModels;
float _bodyYawDelta;
- glm::vec3 _velocity;
-
// These position histories and derivatives are in the world-frame.
// The derivatives are the MEASURED results of all external and internal forces
- // and are therefor READ-ONLY --> motion control of the Avatar is NOT obtained
+ // and are therefore READ-ONLY --> motion control of the Avatar is NOT obtained
// by setting these values.
// Floating point error prevents us from accurately measuring velocity using a naive approach
// (e.g. vel = (pos - lastPos)/dt) so instead we use _positionDeltaAccumulator.
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index c2a957dc72..82aa4e8177 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -181,7 +181,11 @@ void MyAvatar::simulate(float deltaTime) {
{
PerformanceTimer perfTimer("transform");
updateOrientation(deltaTime);
- updatePosition(deltaTime);
+ if (isPhysicsEnabled()) {
+ updatePositionWithPhysics(deltaTime);
+ } else {
+ updatePosition(deltaTime);
+ }
}
{
@@ -1397,6 +1401,25 @@ void MyAvatar::updatePosition(float deltaTime) {
measureMotionDerivatives(deltaTime);
}
+void MyAvatar::updatePositionWithPhysics(float deltaTime) {
+ // rotate velocity into camera frame
+ glm::quat rotation = getHead()->getCameraOrientation();
+ glm::vec3 localVelocity = glm::inverse(rotation) * _velocity;
+
+ bool hasFloor = false;
+ glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, hasFloor);
+ newLocalVelocity = applyScriptedMotor(deltaTime, newLocalVelocity);
+
+ // cap avatar speed
+ float speed = glm::length(newLocalVelocity);
+ if (speed > MAX_WALKING_SPEED) {
+ newLocalVelocity *= MAX_WALKING_SPEED / speed;
+ }
+
+ // rotate back into world-frame
+ _velocity = rotation * newLocalVelocity;
+}
+
void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) {
glm::vec3 up = getBodyUpDirection();
const float ENVIRONMENT_SURFACE_ELASTICITY = 0.0f;
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index 4255f40712..7e7281f6a1 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -164,8 +164,6 @@ public slots:
glm::vec3 getThrust() { return _thrust; };
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
- void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
-
void updateMotionBehavior();
void onToggleRagdoll();
@@ -233,6 +231,7 @@ private:
glm::vec3 applyKeyboardMotor(float deltaTime, const glm::vec3& velocity, bool walkingOnFloor);
glm::vec3 applyScriptedMotor(float deltaTime, const glm::vec3& velocity);
void updatePosition(float deltaTime);
+ void updatePositionWithPhysics(float deltaTime);
void updateCollisionWithAvatars(float deltaTime);
void updateCollisionWithEnvironment(float deltaTime, float radius);
void updateCollisionWithVoxels(float deltaTime, float radius);
diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index a3d330f84b..5b391b33f8 100644
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -55,7 +55,8 @@ AvatarData::AvatarData() :
_billboard(),
_errorLogExpiry(0),
_owningAvatarMixer(),
- _lastUpdateTimer()
+ _lastUpdateTimer(),
+ _velocity(0.0f)
{
}
diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h
index a8a330485c..c131588fb0 100644
--- a/libraries/avatars/src/AvatarData.h
+++ b/libraries/avatars/src/AvatarData.h
@@ -43,6 +43,7 @@ typedef unsigned long long quint64;
#include
#include
#include
+#include
#include
#include
@@ -300,6 +301,19 @@ public:
const Referential* getReferential() const { return _referential; }
+ void togglePhysicsEnabled() { _enablePhysics = !_enablePhysics; }
+ bool isPhysicsEnabled() { return _enablePhysics; }
+ void setPhysicsEnabled(bool enablePhysics) { _enablePhysics = enablePhysics; }
+
+ void lockForRead() { _lock.lockForRead(); }
+ bool tryLockForRead() { return _lock.tryLockForRead(); }
+ void lockForWrite() { _lock.lockForWrite(); }
+ bool tryLockForWrite() { return _lock.tryLockForWrite(); }
+ void unlock() { _lock.unlock(); }
+
+ void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
+ Q_INVOKABLE glm::vec3 getVelocity() const { return _velocity; }
+
public slots:
void sendAvatarDataPacket();
void sendIdentityPacket();
@@ -389,10 +403,15 @@ protected:
virtual void updateJointMappings();
void changeReferential(Referential* ref);
+ glm::vec3 _velocity;
+
private:
// privatize the copy constructor and assignment operator so they cannot be called
AvatarData(const AvatarData&);
AvatarData& operator= (const AvatarData&);
+
+ QReadWriteLock _lock;
+ bool _enablePhysics = false;
};
Q_DECLARE_METATYPE(AvatarData*)
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 093f8cf84c..4f74438a45 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -501,7 +501,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
EntityPropertyFlags propertyFlags = encodedPropertyFlags;
dataAt += propertyFlags.getEncodedLength();
bytesRead += propertyFlags.getEncodedLength();
- bool useMeters = (args.bitstreamVersion == VERSION_ENTITIES_USE_METERS_AND_RADIANS);
+ bool useMeters = (args.bitstreamVersion >= VERSION_ENTITIES_USE_METERS_AND_RADIANS);
if (useMeters) {
READ_ENTITY_PROPERTY_SETTER(PROP_POSITION, glm::vec3, updatePosition);
} else {
diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp
index 3a33128c47..2b8e82d28f 100644
--- a/libraries/entities/src/EntityItemProperties.cpp
+++ b/libraries/entities/src/EntityItemProperties.cpp
@@ -40,6 +40,7 @@ EntityItemProperties::EntityItemProperties() :
CONSTRUCT_PROPERTY(script, ENTITY_ITEM_DEFAULT_SCRIPT),
CONSTRUCT_PROPERTY(color, ),
CONSTRUCT_PROPERTY(modelURL, ""),
+ CONSTRUCT_PROPERTY(collisionModelURL, ""),
CONSTRUCT_PROPERTY(animationURL, ""),
CONSTRUCT_PROPERTY(animationFPS, ModelEntityItem::DEFAULT_ANIMATION_FPS),
CONSTRUCT_PROPERTY(animationFrameIndex, ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX),
@@ -158,6 +159,7 @@ void EntityItemProperties::debugDump() const {
qDebug() << " _position=" << _position.x << "," << _position.y << "," << _position.z;
qDebug() << " _dimensions=" << getDimensions();
qDebug() << " _modelURL=" << _modelURL;
+ qDebug() << " _collisionModelURL=" << _collisionModelURL;
qDebug() << " changed properties...";
EntityPropertyFlags props = getChangedProperties();
props.debugDumpBits();
@@ -213,6 +215,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_SCRIPT, script);
CHECK_PROPERTY_CHANGE(PROP_COLOR, color);
CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL);
+ CHECK_PROPERTY_CHANGE(PROP_COLLISION_MODEL_URL, collisionModelURL);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_URL, animationURL);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_PLAYING, animationIsPlaying);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FRAME_INDEX, animationFrameIndex);
@@ -276,6 +279,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
COPY_PROPERTY_TO_QSCRIPTVALUE(visible);
COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR(color);
COPY_PROPERTY_TO_QSCRIPTVALUE(modelURL);
+ COPY_PROPERTY_TO_QSCRIPTVALUE(collisionModelURL);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationURL);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationIsPlaying);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationFPS);
@@ -356,6 +360,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(visible, setVisible);
COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(color, setColor);
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(modelURL, setModelURL);
+ COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(collisionModelURL, setCollisionModelURL);
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(animationURL, setAnimationURL);
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(animationIsPlaying, setAnimationIsPlaying);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(animationFPS, setAnimationFPS);
@@ -541,6 +546,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
if (properties.getType() == EntityTypes::Model) {
APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, appendValue, properties.getModelURL());
+ APPEND_ENTITY_PROPERTY(PROP_COLLISION_MODEL_URL, appendValue, properties.getCollisionModelURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, appendValue, properties.getAnimationURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, appendValue, properties.getAnimationFPS());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, properties.getAnimationFrameIndex());
@@ -769,6 +775,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
if (properties.getType() == EntityTypes::Model) {
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MODEL_URL, setModelURL);
+ READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_COLLISION_MODEL_URL, setCollisionModelURL);
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ANIMATION_URL, setAnimationURL);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FPS, float, setAnimationFPS);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FRAME_INDEX, float, setAnimationFrameIndex);
@@ -845,6 +852,7 @@ void EntityItemProperties::markAllChanged() {
_visibleChanged = true;
_colorChanged = true;
_modelURLChanged = true;
+ _collisionModelURLChanged = true;
_animationURLChanged = true;
_animationIsPlayingChanged = true;
_animationFrameIndexChanged = true;
diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h
index b769261033..308a2d23bb 100644
--- a/libraries/entities/src/EntityItemProperties.h
+++ b/libraries/entities/src/EntityItemProperties.h
@@ -102,6 +102,7 @@ enum EntityPropertyList {
PROP_TEXT = PROP_MODEL_URL,
PROP_LINE_HEIGHT = PROP_ANIMATION_URL,
PROP_BACKGROUND_COLOR = PROP_ANIMATION_FPS,
+ PROP_COLLISION_MODEL_URL,
};
typedef PropertyFlags EntityPropertyFlags;
@@ -164,6 +165,7 @@ public:
DEFINE_PROPERTY_REF(PROP_SCRIPT, Script, script, QString);
DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, xColor);
DEFINE_PROPERTY_REF(PROP_MODEL_URL, ModelURL, modelURL, QString);
+ DEFINE_PROPERTY_REF(PROP_COLLISION_MODEL_URL, CollisionModelURL, collisionModelURL, QString);
DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, AnimationURL, animationURL, QString);
DEFINE_PROPERTY(PROP_ANIMATION_FPS, AnimationFPS, animationFPS, float);
DEFINE_PROPERTY(PROP_ANIMATION_FRAME_INDEX, AnimationFrameIndex, animationFrameIndex, float);
@@ -291,6 +293,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Script, script, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Color, color, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ModelURL, modelURL, "");
+ DEBUG_PROPERTY_IF_CHANGED(debug, properties, CollisionModelURL, collisionModelURL, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationURL, animationURL, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationFPS, animationFPS, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationFrameIndex, animationFrameIndex, "");
diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h
index e7f2219c19..1eb4fdc951 100644
--- a/libraries/entities/src/EntitySimulation.h
+++ b/libraries/entities/src/EntitySimulation.h
@@ -35,7 +35,7 @@ const int DIRTY_SIMULATION_FLAGS =
class EntitySimulation : public QObject {
Q_OBJECT
public:
- EntitySimulation() : _mutex(QMutex::Recursive), _entityTree(NULL) { }
+ EntitySimulation() : _mutex(QMutex::Recursive), _entityTree(NULL), _nextExpiry(quint64(-1)) { }
virtual ~EntitySimulation() { setEntityTree(NULL); }
void lock() { _mutex.lock(); }
diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp
index f30d43c7d5..d2a2714982 100644
--- a/libraries/entities/src/ModelEntityItem.cpp
+++ b/libraries/entities/src/ModelEntityItem.cpp
@@ -19,6 +19,7 @@
#include "ModelEntityItem.h"
const QString ModelEntityItem::DEFAULT_MODEL_URL = QString("");
+const QString ModelEntityItem::DEFAULT_COLLISION_MODEL_URL = QString("");
const QString ModelEntityItem::DEFAULT_ANIMATION_URL = QString("");
const float ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX = 0.0f;
const bool ModelEntityItem::DEFAULT_ANIMATION_IS_PLAYING = false;
@@ -44,6 +45,7 @@ EntityItemProperties ModelEntityItem::getProperties() const {
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(modelURL, getModelURL);
+ COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionModelURL, getCollisionModelURL);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationURL, getAnimationURL);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationIsPlaying, getAnimationIsPlaying);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationFrameIndex, getAnimationFrameIndex);
@@ -61,6 +63,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(modelURL, setModelURL);
+ SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionModelURL, setCollisionModelURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationURL, setAnimationURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationIsPlaying, setAnimationIsPlaying);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFrameIndex, setAnimationFrameIndex);
@@ -92,6 +95,11 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
READ_ENTITY_PROPERTY_COLOR(PROP_COLOR, _color);
READ_ENTITY_PROPERTY_STRING(PROP_MODEL_URL, setModelURL);
+ if (args.bitstreamVersion < VERSION_ENTITIES_HAS_COLLISION_MODEL) {
+ setCollisionModelURL("");
+ } else {
+ READ_ENTITY_PROPERTY_STRING(PROP_COLLISION_MODEL_URL, setCollisionModelURL);
+ }
READ_ENTITY_PROPERTY_STRING(PROP_ANIMATION_URL, setAnimationURL);
// Because we're using AnimationLoop which will reset the frame index if you change it's running state
@@ -128,6 +136,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams&
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_MODEL_URL;
+ requestedProperties += PROP_COLLISION_MODEL_URL;
requestedProperties += PROP_ANIMATION_URL;
requestedProperties += PROP_ANIMATION_FPS;
requestedProperties += PROP_ANIMATION_FRAME_INDEX;
@@ -151,6 +160,7 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor());
APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, appendValue, getModelURL());
+ APPEND_ENTITY_PROPERTY(PROP_COLLISION_MODEL_URL, appendValue, getCollisionModelURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, appendValue, getAnimationURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, appendValue, getAnimationFPS());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, getAnimationFrameIndex());
@@ -258,6 +268,7 @@ void ModelEntityItem::debugDump() const {
qDebug() << " position:" << getPosition();
qDebug() << " dimensions:" << getDimensions();
qDebug() << " model URL:" << getModelURL();
+ qDebug() << " collision model URL:" << getCollisionModelURL();
}
void ModelEntityItem::updateShapeType(ShapeType type) {
diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h
index 94d262fc9f..081cb429ed 100644
--- a/libraries/entities/src/ModelEntityItem.h
+++ b/libraries/entities/src/ModelEntityItem.h
@@ -57,10 +57,14 @@ public:
const rgbColor& getColor() const { return _color; }
xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
bool hasModel() const { return !_modelURL.isEmpty(); }
+ bool hasCollisionModel() const { return !_collisionModelURL.isEmpty(); }
static const QString DEFAULT_MODEL_URL;
const QString& getModelURL() const { return _modelURL; }
+ static const QString DEFAULT_COLLISION_MODEL_URL;
+ const QString& getCollisionModelURL() const { return _collisionModelURL; }
+
bool hasAnimation() const { return !_animationURL.isEmpty(); }
static const QString DEFAULT_ANIMATION_URL;
const QString& getAnimationURL() const { return _animationURL; }
@@ -74,6 +78,7 @@ public:
// model related properties
void setModelURL(const QString& url) { _modelURL = url; }
+ void setCollisionModelURL(const QString& url) { _collisionModelURL = url; }
void setAnimationURL(const QString& url);
static const float DEFAULT_ANIMATION_FRAME_INDEX;
void setAnimationFrameIndex(float value);
@@ -121,6 +126,7 @@ protected:
rgbColor _color;
QString _modelURL;
+ QString _collisionModelURL;
quint64 _lastAnimated;
QString _animationURL;
diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp
index cbbaae3d82..a2c217c97d 100644
--- a/libraries/fbx/src/FBXReader.cpp
+++ b/libraries/fbx/src/FBXReader.cpp
@@ -295,7 +295,11 @@ public:
Tokenizer(QIODevice* device) : _device(device), _pushedBackToken(-1) { }
- enum SpecialToken { DATUM_TOKEN = 0x100 };
+ enum SpecialToken {
+ NO_TOKEN = -1,
+ NO_PUSHBACKED_TOKEN = -1,
+ DATUM_TOKEN = 0x100
+ };
int nextToken();
const QByteArray& getDatum() const { return _datum; }
@@ -311,9 +315,9 @@ private:
};
int Tokenizer::nextToken() {
- if (_pushedBackToken != -1) {
+ if (_pushedBackToken != NO_PUSHBACKED_TOKEN) {
int token = _pushedBackToken;
- _pushedBackToken = -1;
+ _pushedBackToken = NO_PUSHBACKED_TOKEN;
return token;
}
@@ -361,7 +365,7 @@ int Tokenizer::nextToken() {
return DATUM_TOKEN;
}
}
- return -1;
+ return NO_TOKEN;
}
FBXNode parseTextFBXNode(Tokenizer& tokenizer) {
@@ -378,7 +382,7 @@ FBXNode parseTextFBXNode(Tokenizer& tokenizer) {
int token;
bool expectingDatum = true;
- while ((token = tokenizer.nextToken()) != -1) {
+ while ((token = tokenizer.nextToken()) != Tokenizer::NO_TOKEN) {
if (token == '{') {
for (FBXNode child = parseTextFBXNode(tokenizer); !child.name.isNull(); child = parseTextFBXNode(tokenizer)) {
node.children.append(child);
diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h
index 6912dd730f..ecce607575 100644
--- a/libraries/fbx/src/FBXReader.h
+++ b/libraries/fbx/src/FBXReader.h
@@ -152,8 +152,6 @@ public:
bool hasSpecularTexture() const;
bool hasEmissiveTexture() const;
-
- model::Mesh _mesh;
};
/// A single animation frame extracted from an FBX document.
diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp
new file mode 100644
index 0000000000..4f0f1246d2
--- /dev/null
+++ b/libraries/fbx/src/OBJReader.cpp
@@ -0,0 +1,287 @@
+//
+// OBJReader.cpp
+// libraries/fbx/src/
+//
+// Created by Seth Alves on 3/7/15.
+// 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
+//
+// http://en.wikipedia.org/wiki/Wavefront_.obj_file
+// http://www.scratchapixel.com/old/lessons/3d-advanced-lessons/obj-file-format/obj-file-format/
+// http://paulbourke.net/dataformats/obj/
+
+
+#include
+#include
+
+#include "FBXReader.h"
+#include "OBJReader.h"
+#include "Shape.h"
+
+
+class OBJTokenizer {
+public:
+ OBJTokenizer(QIODevice* device) : _device(device), _pushedBackToken(-1) { }
+ enum SpecialToken {
+ NO_TOKEN = -1,
+ NO_PUSHBACKED_TOKEN = -1,
+ DATUM_TOKEN = 0x100
+ };
+ int nextToken();
+ const QByteArray& getDatum() const { return _datum; }
+ void skipLine() { _device->readLine(); }
+ void pushBackToken(int token) { _pushedBackToken = token; }
+ void ungetChar(char ch) { _device->ungetChar(ch); }
+
+private:
+ QIODevice* _device;
+ QByteArray _datum;
+ int _pushedBackToken;
+};
+
+
+int OBJTokenizer::nextToken() {
+ if (_pushedBackToken != NO_PUSHBACKED_TOKEN) {
+ int token = _pushedBackToken;
+ _pushedBackToken = NO_PUSHBACKED_TOKEN;
+ return token;
+ }
+
+ char ch;
+ while (_device->getChar(&ch)) {
+ if (QChar(ch).isSpace()) {
+ continue; // skip whitespace
+ }
+ switch (ch) {
+ case '#':
+ _device->readLine(); // skip the comment
+ break;
+
+ case '\"':
+ _datum = "";
+ while (_device->getChar(&ch)) {
+ if (ch == '\"') { // end on closing quote
+ break;
+ }
+ if (ch == '\\') { // handle escaped quotes
+ if (_device->getChar(&ch) && ch != '\"') {
+ _datum.append('\\');
+ }
+ }
+ _datum.append(ch);
+ }
+ return DATUM_TOKEN;
+
+ default:
+ _datum = "";
+ _datum.append(ch);
+ while (_device->getChar(&ch)) {
+ if (QChar(ch).isSpace() || ch == '\"') {
+ ungetChar(ch); // read until we encounter a special character, then replace it
+ break;
+ }
+ _datum.append(ch);
+ }
+
+ return DATUM_TOKEN;
+ }
+ }
+ return NO_TOKEN;
+}
+
+
+bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, FBXGeometry &geometry) {
+ FBXMesh &mesh = geometry.meshes[0];
+ mesh.parts.append(FBXMeshPart());
+ FBXMeshPart &meshPart = mesh.parts.last();
+ bool sawG = false;
+ bool result = true;
+
+ meshPart.materialID = QString("dontknow") + QString::number(mesh.parts.count());
+ meshPart.opacity = 1.0;
+ meshPart._material = model::MaterialPointer(new model::Material());
+ meshPart._material->setDiffuse(glm::vec3(1.0, 1.0, 1.0));
+ meshPart._material->setOpacity(1.0);
+ meshPart._material->setSpecular(glm::vec3(1.0, 1.0, 1.0));
+ meshPart._material->setShininess(96.0);
+ meshPart._material->setEmissive(glm::vec3(0.0, 0.0, 0.0));
+
+ while (true) {
+ if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
+ result = false;
+ break;
+ }
+ QByteArray token = tokenizer.getDatum();
+ if (token == "g") {
+ if (sawG) {
+ // we've encountered the beginning of the next group.
+ tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN);
+ break;
+ }
+ sawG = true;
+ if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
+ break;
+ }
+ QByteArray groupName = tokenizer.getDatum();
+ meshPart.materialID = groupName;
+ } else if (token == "v") {
+ if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
+ break;
+ }
+ float x = std::stof(tokenizer.getDatum().data());
+ // notice the order of z and y -- in OBJ files, up is the 3rd value
+ if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
+ break;
+ }
+ float z = std::stof(tokenizer.getDatum().data());
+ if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
+ break;
+ }
+ float y = std::stof(tokenizer.getDatum().data());
+ if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
+ break;
+ }
+ // the spec gets vague here. might be w, might be a color... chop it off.
+ tokenizer.skipLine();
+ mesh.vertices.append(glm::vec3(x, y, z));
+ mesh.colors.append(glm::vec3(1, 1, 1));
+ } else if (token == "f") {
+ // a face can have 3 or more vertices
+ QVector indices;
+ while (true) {
+ if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { goto done; }
+ try {
+ int vertexIndex = std::stoi(tokenizer.getDatum().data());
+ // negative indexes count backward from the current end of the vertex list
+ vertexIndex = (vertexIndex >= 0 ? vertexIndex : mesh.vertices.count() + vertexIndex + 1);
+ // obj index is 1 based
+ assert(vertexIndex >= 1);
+ indices.append(vertexIndex - 1);
+ }
+ catch(const std::exception& e) {
+ // wasn't a number, but it back.
+ tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN);
+ break;
+ }
+ }
+
+ if (indices.count() == 3) {
+ // flip these around (because of the y/z swap above) so our triangles face outward
+ meshPart.triangleIndices.append(indices[0]);
+ meshPart.triangleIndices.append(indices[2]);
+ meshPart.triangleIndices.append(indices[1]);
+ } else if (indices.count() == 4) {
+ meshPart.quadIndices << indices;
+ } else {
+ qDebug() << "no support for more than 4 vertices on a face in OBJ files";
+ }
+ } else {
+ // something we don't (yet) care about
+ qDebug() << "OBJ parser is skipping a line with" << token;
+ tokenizer.skipLine();
+ }
+ }
+
+ done:
+ return result;
+}
+
+
+FBXGeometry extractOBJGeometry(const FBXNode& node, const QVariantHash& mapping) {
+ FBXGeometry geometry;
+ return geometry;
+}
+
+
+FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping) {
+ QBuffer buffer(const_cast(&model));
+ buffer.open(QIODevice::ReadOnly);
+ return readOBJ(&buffer, mapping);
+}
+
+
+FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) {
+ FBXGeometry geometry;
+ OBJTokenizer tokenizer(device);
+
+ geometry.meshExtents.reset();
+ geometry.meshes.append(FBXMesh());
+
+
+
+ try {
+ // call parseOBJGroup as long as it's returning true. Each successful call will
+ // add a new meshPart to the geometry's single mesh.
+ bool success = true;
+ while (success) {
+ success = parseOBJGroup(tokenizer, mapping, geometry);
+ }
+
+ FBXMesh &mesh = geometry.meshes[0];
+
+ mesh.meshExtents.reset();
+ foreach (const glm::vec3& vertex, mesh.vertices) {
+ mesh.meshExtents.addPoint(vertex);
+ geometry.meshExtents.addPoint(vertex);
+ }
+
+ geometry.joints.resize(1);
+ geometry.joints[0].isFree = false;
+ // geometry.joints[0].freeLineage;
+ geometry.joints[0].parentIndex = -1;
+ geometry.joints[0].distanceToParent = 0;
+ geometry.joints[0].boneRadius = 0;
+ geometry.joints[0].translation = glm::vec3(0, 0, 0);
+ // geometry.joints[0].preTransform = ;
+ geometry.joints[0].preRotation = glm::quat(0, 0, 0, 1);
+ geometry.joints[0].rotation = glm::quat(0, 0, 0, 1);
+ geometry.joints[0].postRotation = glm::quat(0, 0, 0, 1);
+ // geometry.joints[0].postTransform = ;
+ // geometry.joints[0].transform = ;
+ geometry.joints[0].rotationMin = glm::vec3(0, 0, 0);
+ geometry.joints[0].rotationMax = glm::vec3(0, 0, 0);
+ geometry.joints[0].inverseDefaultRotation = glm::quat(0, 0, 0, 1);
+ geometry.joints[0].inverseBindRotation = glm::quat(0, 0, 0, 1);
+ // geometry.joints[0].bindTransform = ;
+ geometry.joints[0].name = "OBJ";
+ geometry.joints[0].shapePosition = glm::vec3(0, 0, 0);
+ geometry.joints[0].shapeRotation = glm::quat(0, 0, 0, 1);
+ geometry.joints[0].shapeType = SPHERE_SHAPE;
+ geometry.joints[0].isSkeletonJoint = false;
+
+ // add bogus normal data for this mesh
+ mesh.normals.fill(glm::vec3(0,0,0), mesh.vertices.count());
+ mesh.tangents.fill(glm::vec3(0,0,0), mesh.vertices.count());
+
+ foreach (FBXMeshPart meshPart, mesh.parts) {
+ int triCount = meshPart.triangleIndices.count() / 3;
+ for (int i = 0; i < triCount; i++) {
+ int p0Index = meshPart.triangleIndices[i*3];
+ int p1Index = meshPart.triangleIndices[i*3+1];
+ int p2Index = meshPart.triangleIndices[i*3+2];
+
+ glm::vec3 p0 = mesh.vertices[p0Index];
+ glm::vec3 p1 = mesh.vertices[p1Index];
+ glm::vec3 p2 = mesh.vertices[p2Index];
+
+ glm::vec3 n = glm::cross(p1 - p0, p2 - p0);
+ glm::vec3 t = glm::cross(p2 - p0, n);
+
+ mesh.normals[p0Index] = n;
+ mesh.normals[p1Index] = n;
+ mesh.normals[p2Index] = n;
+
+ mesh.tangents[p0Index] = t;
+ mesh.tangents[p1Index] = t;
+ mesh.tangents[p2Index] = t;
+ }
+ }
+ }
+ catch(const std::exception& e) {
+ qDebug() << "something went wrong in OBJ reader";
+ }
+
+ return geometry;
+}
diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h
new file mode 100644
index 0000000000..2ff55a9a61
--- /dev/null
+++ b/libraries/fbx/src/OBJReader.h
@@ -0,0 +1,6 @@
+
+
+#include "FBXReader.h"
+
+FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping);
+FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping);
diff --git a/libraries/model/src/model/Atmosphere.slh b/libraries/model/src/model/Atmosphere.slh
index 11b64dde70..cc1b7017b0 100755
--- a/libraries/model/src/model/Atmosphere.slh
+++ b/libraries/model/src/model/Atmosphere.slh
@@ -114,13 +114,14 @@ vec4 evalAtmosphereContribution(Atmosphere atmospheric, vec3 position, vec3 came
//gl_FrontColor = vec4(0.0, 0.0, 0.0, 0.0);
float fSampleLength = fFar / fSamples;
float fScaledLength = fSampleLength * fScale;
+
vec3 v3SampleRay = v3Ray * fSampleLength;
vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5;
// Now loop through the sample rays
vec3 v3FrontColor = vec3(0.0, 0.0, 0.0);
- int nSamples = numSamples;
- // int nSamples = int(fSamples);
+ // int nSamples = numSamples;
+ int nSamples = int(fSamples);
for(int i=0; i
void eachNodeHashIterator(IteratorLambda functor) {
diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp
index c7f1f4ec88..a14191bf09 100644
--- a/libraries/networking/src/PacketHeaders.cpp
+++ b/libraries/networking/src/PacketHeaders.cpp
@@ -74,7 +74,7 @@ PacketVersion versionForPacketType(PacketType type) {
return 1;
case PacketTypeEntityAddOrEdit:
case PacketTypeEntityData:
- return VERSION_ENTITIES_USE_METERS_AND_RADIANS;
+ return VERSION_ENTITIES_HAS_COLLISION_MODEL;
case PacketTypeEntityErase:
return 2;
case PacketTypeAudioStreamStats:
diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h
index 3133fd9649..f930fd9632 100644
--- a/libraries/networking/src/PacketHeaders.h
+++ b/libraries/networking/src/PacketHeaders.h
@@ -131,6 +131,7 @@ const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_SHAPE_TYPE = 8;
const PacketVersion VERSION_ENTITIES_LIGHT_HAS_INTENSITY_AND_COLOR_PROPERTIES = 9;
const PacketVersion VERSION_ENTITIES_HAS_PARTICLES = 10;
const PacketVersion VERSION_ENTITIES_USE_METERS_AND_RADIANS = 11;
+const PacketVersion VERSION_ENTITIES_HAS_COLLISION_MODEL = 12;
const PacketVersion VERSION_OCTREE_HAS_FILE_BREAKS = 1;
#endif // hifi_PacketHeaders_h
diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp
index 10c7f42546..66f8afb226 100644
--- a/libraries/physics/src/PhysicsEngine.cpp
+++ b/libraries/physics/src/PhysicsEngine.cpp
@@ -13,6 +13,7 @@
#include "ShapeInfoUtil.h"
#include "PhysicsHelpers.h"
#include "ThreadSafeDynamicsWorld.h"
+#include "AvatarData.h"
static uint32_t _numSubsteps;
@@ -286,6 +287,20 @@ void PhysicsEngine::stepSimulation() {
_clock.reset();
float timeStep = btMin(dt, MAX_TIMESTEP);
+ if (_avatarData->isPhysicsEnabled()) {
+ _avatarGhostObject->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()),
+ glmToBullet(_avatarData->getPosition())));
+ // WORKAROUND: there is a bug in the debug Bullet-2.82 libs where a zero length walk velocity will trigger
+ // an assert when the getNormalizedVector() helper function in btKinematicCharacterController.cpp tries to
+ // first normalize a vector before checking its length. Here we workaround the problem by checking the
+ // length first. NOTE: the character's velocity is reset to zero after each step, so when we DON'T set
+ // the velocity for this time interval it is the same thing as setting its velocity to zero.
+ btVector3 walkVelocity = glmToBullet(_avatarData->getVelocity());
+ if (walkVelocity.length2() > FLT_EPSILON * FLT_EPSILON) {
+ _characterController->setVelocityForTimeInterval(walkVelocity, timeStep);
+ }
+ }
+
// This is step (2).
int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP);
_numSubsteps += (uint32_t)numSubsteps;
@@ -302,9 +317,18 @@ void PhysicsEngine::stepSimulation() {
//
// TODO: untangle these lock sequences.
_entityTree->lockForWrite();
+ _avatarData->lockForWrite();
lock();
_dynamicsWorld->synchronizeMotionStates();
+
+ if (_avatarData->isPhysicsEnabled()) {
+ const btTransform& avatarTransform = _avatarGhostObject->getWorldTransform();
+ _avatarData->setOrientation(bulletToGLM(avatarTransform.getRotation()));
+ _avatarData->setPosition(bulletToGLM(avatarTransform.getOrigin()));
+ }
+
unlock();
+ _avatarData->unlock();
_entityTree->unlock();
computeCollisionEvents();
@@ -582,3 +606,32 @@ bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
body->activate();
return true;
}
+
+
+
+void PhysicsEngine::setAvatarData(AvatarData *avatarData) {
+ _avatarData = avatarData;
+ _avatarGhostObject = new btPairCachingGhostObject();
+ _avatarGhostObject->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()),
+ glmToBullet(_avatarData->getPosition())));
+
+ // XXX these values should be computed from the character model.
+ btScalar characterRadius = 0.3;
+ btScalar characterHeight = 1.75 - 2.0f * characterRadius;
+ btScalar stepHeight = btScalar(0.35);
+
+ btConvexShape* capsule = new btCapsuleShape(characterRadius, characterHeight);
+ _avatarGhostObject->setCollisionShape(capsule);
+ _avatarGhostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT);
+
+ _characterController = new btKinematicCharacterController(_avatarGhostObject, capsule, stepHeight);
+
+ _dynamicsWorld->addCollisionObject(_avatarGhostObject, btBroadphaseProxy::CharacterFilter,
+ btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter);
+ _dynamicsWorld->addAction(_characterController);
+ _characterController->reset (_dynamicsWorld);
+ // _characterController->warp (btVector3(10.210001,-2.0306311,16.576973));
+
+ btGhostPairCallback* ghostPairCallback = new btGhostPairCallback();
+ _dynamicsWorld->getPairCache()->setInternalGhostPairCallback(ghostPairCallback);
+}
diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h
index 2a7baa3ec6..649aec2755 100644
--- a/libraries/physics/src/PhysicsEngine.h
+++ b/libraries/physics/src/PhysicsEngine.h
@@ -16,6 +16,11 @@
#include
#include
+#include
+#include
+#include
+#include
+#include
#include
#include
@@ -25,6 +30,7 @@
#include "EntityMotionState.h"
#include "ShapeManager.h"
#include "ThreadSafeDynamicsWorld.h"
+#include "AvatarData.h"
const float HALF_SIMULATION_EXTENT = 512.0f; // meters
@@ -82,6 +88,8 @@ public:
/// process queue of changed from external sources
void relayIncomingChangesToSimulation();
+ void setAvatarData(AvatarData *avatarData);
+
private:
/// \param motionState pointer to Object's MotionState
void removeObjectFromBullet(ObjectMotionState* motionState);
@@ -113,6 +121,11 @@ private:
ContactMap _contactMap;
uint32_t _numContactFrames = 0;
uint32_t _lastNumSubstepsAtUpdateInternal = 0;
+
+ /// character collisions
+ btCharacterControllerInterface* _characterController = 0;
+ class btPairCachingGhostObject* _avatarGhostObject = 0;
+ AvatarData *_avatarData = 0;
};
#endif // hifi_PhysicsEngine_h
diff --git a/libraries/physics/src/PhysicsSimulation.h b/libraries/physics/src/PhysicsSimulation.h
index 90d6e38187..404731e589 100644
--- a/libraries/physics/src/PhysicsSimulation.h
+++ b/libraries/physics/src/PhysicsSimulation.h
@@ -61,6 +61,8 @@ public:
bool getShapeCollisions(const Shape* shape, CollisionList& collisions) const;
+ void setupAvatarCollision();
+
protected:
void integrate(float deltaTime);
diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp
index 9ecdfa43e1..b80cc04b3f 100644
--- a/libraries/render-utils/src/GeometryCache.cpp
+++ b/libraries/render-utils/src/GeometryCache.cpp
@@ -2084,6 +2084,7 @@ void GeometryReader::run() {
urlValid &= !urlname.isEmpty();
urlValid &= !_url.path().isEmpty();
urlValid &= _url.path().toLower().endsWith(".fbx")
+ || _url.path().toLower().endsWith(".obj")
|| _url.path().toLower().endsWith(".svo");
if (urlValid) {
@@ -2101,6 +2102,8 @@ void GeometryReader::run() {
lightmapLevel = 3.5f;
}
fbxgeo = readFBX(_reply, _mapping, grabLightmaps, lightmapLevel);
+ } else if (_url.path().toLower().endsWith(".obj")) {
+ fbxgeo = readOBJ(_reply, _mapping);
}
QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo));
} else {
diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h
index 92b6d44b6c..ba7c16bc10 100644
--- a/libraries/render-utils/src/GeometryCache.h
+++ b/libraries/render-utils/src/GeometryCache.h
@@ -22,6 +22,7 @@
#include
#include
+#include
#include
diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index 80781c3522..be2e7b4aec 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -198,51 +198,51 @@ void Model::initProgram(ProgramObject& program, Model::Locations& locations, boo
locations.emissiveTextureUnit = -1;
}
- // bindable uniform version
-#if defined(Q_OS_MAC)
- loc = program.uniformLocation("materialBuffer");
- if (loc >= 0) {
- locations.materialBufferUnit = loc;
- } else {
- locations.materialBufferUnit = -1;
- }
-#elif defined(Q_OS_WIN)
- loc = glGetUniformBlockIndex(program.programId(), "materialBuffer");
- if (loc >= 0) {
- glUniformBlockBinding(program.programId(), loc, 1);
- locations.materialBufferUnit = 1;
- } else {
- locations.materialBufferUnit = -1;
- }
-#else
- loc = program.uniformLocation("materialBuffer");
- if (loc >= 0) {
- locations.materialBufferUnit = loc;
- } else {
- locations.materialBufferUnit = -1;
- }
-#endif
-
-#if defined(Q_OS_WIN)
- loc = glGetUniformBlockIndex(program.programId(), "transformObjectBuffer");
- if (loc >= 0) {
- glUniformBlockBinding(program.programId(), loc, gpu::TRANSFORM_OBJECT_SLOT);
- // locations.materialBufferUnit = 1;
- }
-#endif
-
-#if defined(Q_OS_WIN)
- loc = glGetUniformBlockIndex(program.programId(), "transformCameraBuffer");
- if (loc >= 0) {
- glUniformBlockBinding(program.programId(), loc, gpu::TRANSFORM_CAMERA_SLOT);
- // locations.materialBufferUnit = 1;
- }
-#endif
-
+ // bindable uniform version
+#if defined(Q_OS_MAC)
+ loc = program.uniformLocation("materialBuffer");
+ if (loc >= 0) {
+ locations.materialBufferUnit = loc;
+ } else {
+ locations.materialBufferUnit = -1;
+ }
+#elif defined(Q_OS_WIN)
+ loc = glGetUniformBlockIndex(program.programId(), "materialBuffer");
+ if (loc >= 0) {
+ glUniformBlockBinding(program.programId(), loc, 1);
+ locations.materialBufferUnit = 1;
+ } else {
+ locations.materialBufferUnit = -1;
+ }
+#else
+ loc = program.uniformLocation("materialBuffer");
+ if (loc >= 0) {
+ locations.materialBufferUnit = loc;
+ } else {
+ locations.materialBufferUnit = -1;
+ }
+#endif
+
+#if defined(Q_OS_WIN)
+ loc = glGetUniformBlockIndex(program.programId(), "transformObjectBuffer");
+ if (loc >= 0) {
+ glUniformBlockBinding(program.programId(), loc, gpu::TRANSFORM_OBJECT_SLOT);
+ // locations.materialBufferUnit = 1;
+ }
+#endif
+
+#if defined(Q_OS_WIN)
+ loc = glGetUniformBlockIndex(program.programId(), "transformCameraBuffer");
+ if (loc >= 0) {
+ glUniformBlockBinding(program.programId(), loc, gpu::TRANSFORM_CAMERA_SLOT);
+ // locations.materialBufferUnit = 1;
+ }
+#endif
+
//program.link();
- if (!program.isLinked()) {
- program.release();
- }
+ if (!program.isLinked()) {
+ program.release();
+ }
program.release();
}
@@ -385,7 +385,6 @@ void Model::init() {
_program.addShaderFromSourceCode(QGLShader::Vertex, model_vert);
_program.addShaderFromSourceCode(QGLShader::Fragment, model_frag);
initProgram(_program, _locations);
-
_normalMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_normal_map_vert);
_normalMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_normal_map_frag);
@@ -450,7 +449,7 @@ void Model::init() {
_skinTranslucentProgram.addShaderFromSourceCode(QGLShader::Vertex, skin_model_vert);
- _skinTranslucentProgram.addShaderFromSourceCode(QGLShader::Fragment, model_translucent_frag);
+ _skinTranslucentProgram.addShaderFromSourceCode(QGLShader::Fragment, model_translucent_frag);
initSkinProgram(_skinTranslucentProgram, _skinTranslucentLocations);
}
}
@@ -2554,16 +2553,16 @@ int Model::renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMod
qDebug() << "part INDEX:" << j;
qDebug() << "NEW part.materialID:" << part.materialID;
}
-
- if (locations->glowIntensity >= 0) {
- GLBATCH(glUniform1f)(locations->glowIntensity, glowEffect->getIntensity());
+
+ if (locations->glowIntensity >= 0) {
+ GLBATCH(glUniform1f)(locations->glowIntensity, glowEffect->getIntensity());
}
if (!(translucent && alphaThreshold == 0.0f)) {
GLBATCH(glAlphaFunc)(GL_EQUAL, glowEffect->getIntensity());
}
-
- if (locations->materialBufferUnit >= 0) {
- batch.setUniformBuffer(locations->materialBufferUnit, material->getSchemaBuffer());
+
+ if (locations->materialBufferUnit >= 0) {
+ batch.setUniformBuffer(locations->materialBufferUnit, material->getSchemaBuffer());
}
Texture* diffuseMap = networkPart.diffuseTexture.data();
diff --git a/tests/octree/src/OctreeTests.cpp b/tests/octree/src/OctreeTests.cpp
index 4fc543d5d3..705a50aa10 100644
--- a/tests/octree/src/OctreeTests.cpp
+++ b/tests/octree/src/OctreeTests.cpp
@@ -32,6 +32,7 @@ enum ExamplePropertyList {
EXAMPLE_PROP_POSITION,
EXAMPLE_PROP_RADIUS,
EXAMPLE_PROP_MODEL_URL,
+ EXAMPLE_PROP_COLLISION_MODEL_URL,
EXAMPLE_PROP_ROTATION,
EXAMPLE_PROP_COLOR,
EXAMPLE_PROP_SCRIPT,
@@ -73,6 +74,7 @@ void OctreeTests::propertyFlagsTests(bool verbose) {
props.setHasProperty(PROP_POSITION);
props.setHasProperty(PROP_RADIUS);
props.setHasProperty(PROP_MODEL_URL);
+ props.setHasProperty(PROP_COLLISION_MODEL_URL);
props.setHasProperty(PROP_ROTATION);
QByteArray encoded = props.encode();
diff --git a/tools/vhacd/CMakeLists.txt b/tools/vhacd/CMakeLists.txt
index 31a7f7c8e2..f003b685e0 100644
--- a/tools/vhacd/CMakeLists.txt
+++ b/tools/vhacd/CMakeLists.txt
@@ -1,5 +1,5 @@
-set(TARGET_NAME vhacd)
-setup_hifi_project()
+set(TARGET_NAME vhacd-util)
+setup_hifi_project(Core Widgets)
link_hifi_libraries(shared model fbx gpu networking octree)
#find_package(VHACD REQUIRED) done in CMakeList.txt in parent directory
diff --git a/tools/vhacd/src/VHACDUtil.cpp b/tools/vhacd/src/VHACDUtil.cpp
index 63b3bba459..bc9ad09bde 100644
--- a/tools/vhacd/src/VHACDUtil.cpp
+++ b/tools/vhacd/src/VHACDUtil.cpp
@@ -14,7 +14,7 @@
//Read all the meshes from provided FBX file
-bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *results){
+bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *results) {
// open the fbx file
QFile fbx(filename);
@@ -24,11 +24,25 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *re
std::cout << "Reading FBX.....\n";
QByteArray fbxContents = fbx.readAll();
- FBXGeometry geometry = readFBX(fbxContents, QVariantHash());
+
+
+ FBXGeometry geometry;
+
+ if (filename.toLower().endsWith(".obj")) {
+ geometry = readOBJ(fbxContents, QVariantHash());
+ } else if (filename.toLower().endsWith(".fbx")) {
+ geometry = readFBX(fbxContents, QVariantHash());
+ } else {
+ qDebug() << "unknown file extension";
+ return false;
+ }
+
+
//results->meshCount = geometry.meshes.count();
+ // qDebug() << "read in" << geometry.meshes.count() << "meshes";
int count = 0;
- foreach(FBXMesh mesh, geometry.meshes){
+ foreach(FBXMesh mesh, geometry.meshes) {
//get vertices for each mesh
QVector vertices = mesh.vertices;
@@ -40,9 +54,9 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *re
}
//only read meshes with triangles
- if (triangles.count() <= 0){
- continue;
- }
+ if (triangles.count() <= 0){
+ continue;
+ }
results->perMeshVertices.append(vertices);
results->perMeshTriangleIndices.append(triangles);
count++;
@@ -82,6 +96,23 @@ bool vhacd::VHACDUtil::computeVHACD(vhacd::LoadFBXResults *meshes, VHACD::IVHACD
for (unsigned int j = 0; j < nConvexHulls; j++){
VHACD::IVHACD::ConvexHull hull;
interfaceVHACD->GetConvexHull(j, hull);
+
+ double *m_points_copy = new double[hull.m_nPoints * 3];
+ // std::copy(std::begin(hull.m_points), std::end(hull.m_points), std::begin(m_points_copy));
+ for (unsigned int i=0; iconvexHullList.append(convexHulls);
diff --git a/tools/vhacd/src/VHACDUtil.h b/tools/vhacd/src/VHACDUtil.h
index b87ba07ff0..b0b9da9720 100644
--- a/tools/vhacd/src/VHACDUtil.h
+++ b/tools/vhacd/src/VHACDUtil.h
@@ -19,30 +19,31 @@
#include //c++11 feature
#include
#include
+#include
#include
-namespace vhacd{
+namespace vhacd {
- typedef struct{
+ typedef struct {
int meshCount;
QVector convexHullsCountList;
QVector> convexHullList;
- }ComputeResults;
+ } ComputeResults;
- typedef struct{
+ typedef struct {
int meshCount;
QVector> perMeshVertices;
QVector> perMeshTriangleIndices;
- }LoadFBXResults;
+ } LoadFBXResults;
- class VHACDUtil{
+ class VHACDUtil {
public:
bool loadFBX(const QString filename, vhacd::LoadFBXResults *results);
bool computeVHACD(vhacd::LoadFBXResults *meshes, VHACD::IVHACD::Parameters params, vhacd::ComputeResults *results)const;
~VHACDUtil();
};
- class ProgressCallback : public VHACD::IVHACD::IUserCallback{
+ class ProgressCallback : public VHACD::IVHACD::IUserCallback {
public:
ProgressCallback(void);
~ProgressCallback();
@@ -52,4 +53,4 @@ namespace vhacd{
const char * const stage, const char * const operation);
};
}
-#endif //hifi_VHACDUtil_h
\ No newline at end of file
+#endif //hifi_VHACDUtil_h
diff --git a/tools/vhacd/src/VHACDUtilApp.cpp b/tools/vhacd/src/VHACDUtilApp.cpp
new file mode 100644
index 0000000000..958a91dbfe
--- /dev/null
+++ b/tools/vhacd/src/VHACDUtilApp.cpp
@@ -0,0 +1,224 @@
+//
+// VHACDUtil.h
+// tools/vhacd/src
+//
+// Created by Seth Alves on 3/5/15.
+// Copyright 2015 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
+#include
+#include "VHACDUtilApp.h"
+#include "VHACDUtil.h"
+
+using namespace std;
+using namespace VHACD;
+
+
+
+QString formatFloat(double n) {
+ // limit precision to 6, but don't output trailing zeros.
+ QString s = QString::number(n, 'f', 6);
+ while (s.endsWith("0")) {
+ s.remove(s.size() - 1, 1);
+ }
+ if (s.endsWith(".")) {
+ s.remove(s.size() - 1, 1);
+ }
+ return s;
+}
+
+
+bool writeOBJ(QString outFileName, QVector>& meshList, bool outputOneMesh) {
+ QFile file(outFileName);
+ if (!file.open(QIODevice::WriteOnly)) {
+ qDebug() << "Unable to write to " << outFileName;
+ return false;
+ }
+
+ QTextStream out(&file);
+
+ unsigned int pointStartOffset = 0;
+
+ foreach (QVector hulls, meshList) {
+ unsigned int nth = 0;
+ foreach (VHACD::IVHACD::ConvexHull hull, hulls) {
+ out << "g hull-" << nth++ << "\n";
+ for (unsigned int i = 0; i < hull.m_nPoints; i++) {
+ out << "v ";
+ out << formatFloat(hull.m_points[i*3]) << " ";
+ // swap y and z because up is 3rd value in OBJ
+ out << formatFloat(hull.m_points[i*3+2]) << " ";
+ out << formatFloat(hull.m_points[i*3+1]) << "\n";
+ }
+ for (unsigned int i = 0; i < hull.m_nTriangles; i++) {
+ out << "f ";
+ // change order to flip normal (due to swapping y and z, above)
+ out << hull.m_triangles[i*3+1] + 1 + pointStartOffset << " ";
+ out << hull.m_triangles[i*3] + 1 + pointStartOffset << " ";
+ out << hull.m_triangles[i*3+2] + 1 + pointStartOffset << "\n";
+ }
+ out << "\n";
+ pointStartOffset += hull.m_nPoints;
+ }
+ }
+
+ return true;
+}
+
+
+
+
+VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
+ QCoreApplication(argc, argv)
+{
+ vector triangles; // array of indexes
+ vector points; // array of coordinates
+ vhacd::VHACDUtil vUtil;
+ vhacd::LoadFBXResults fbx; //mesh data from loaded fbx file
+ vhacd::ComputeResults results; // results after computing vhacd
+ VHACD::IVHACD::Parameters params;
+ vhacd::ProgressCallback pCallBack;
+
+
+ // parse command-line
+ QCommandLineParser parser;
+ parser.setApplicationDescription("High Fidelity Object Decomposer");
+ parser.addHelpOption();
+
+ const QCommandLineOption helpOption = parser.addHelpOption();
+
+ const QCommandLineOption outputOneMeshOption("1", "output hulls as single mesh");
+ parser.addOption(outputOneMeshOption);
+
+ const QCommandLineOption inputFilenameOption("i", "input file", "filename.fbx");
+ parser.addOption(inputFilenameOption);
+
+ const QCommandLineOption outputFilenameOption("o", "output file", "filename.obj");
+ parser.addOption(outputFilenameOption);
+
+
+ if (!parser.parse(QCoreApplication::arguments())) {
+ qCritical() << parser.errorText() << endl;
+ parser.showHelp();
+ Q_UNREACHABLE();
+ }
+
+ if (parser.isSet(helpOption)) {
+ parser.showHelp();
+ Q_UNREACHABLE();
+ }
+
+
+ bool outputOneMesh = parser.isSet(outputOneMeshOption);
+
+ QString inputFilename;
+ // check for an assignment pool passed on the command line or in the config
+ if (parser.isSet(inputFilenameOption)) {
+ inputFilename = parser.value(inputFilenameOption);
+ }
+
+ QString outputFilename;
+ // check for an assignment pool passed on the command line or in the config
+ if (parser.isSet(outputFilenameOption)) {
+ outputFilename = parser.value(outputFilenameOption);
+ }
+
+
+ if (inputFilename == "") {
+ cerr << "input filename is required.";
+ parser.showHelp();
+ Q_UNREACHABLE();
+ }
+
+ if (outputFilename == "") {
+ cerr << "output filename is required.";
+ parser.showHelp();
+ Q_UNREACHABLE();
+ }
+
+
+ //set parameters for V-HACD
+ params.m_callback = &pCallBack; //progress callback
+ params.m_resolution = 100000; // 100000
+ params.m_depth = 20; // 20
+ params.m_concavity = 0.001; // 0.001
+ params.m_delta = 0.01; // 0.05
+ params.m_planeDownsampling = 4; // 4
+ params.m_convexhullDownsampling = 4; // 4
+ params.m_alpha = 0.05; // 0.05 // controls the bias toward clipping along symmetry planes
+ params.m_beta = 0.05; // 0.05
+ params.m_gamma = 0.0005; // 0.0005
+ params.m_pca = 0; // 0 enable/disable normalizing the mesh before applying the convex decomposition
+ params.m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based
+ params.m_maxNumVerticesPerCH = 64; // 64
+ params.m_minVolumePerCH = 0.00001; // 0.0001
+ params.m_callback = 0; // 0
+ params.m_logger = 0; // 0
+ params.m_convexhullApproximation = true; // true
+ params.m_oclAcceleration = true; // true
+
+ // load the mesh
+
+ auto begin = std::chrono::high_resolution_clock::now();
+ if (!vUtil.loadFBX(inputFilename, &fbx)){
+ cout << "Error in opening FBX file....";
+ }
+ auto end = std::chrono::high_resolution_clock::now();
+ auto loadDuration = std::chrono::duration_cast(end - begin).count();
+
+ //perform vhacd computation
+ begin = std::chrono::high_resolution_clock::now();
+
+
+ if (!vUtil.computeVHACD(&fbx, params, &results)){
+ cout << "Compute Failed...";
+ }
+ end = std::chrono::high_resolution_clock::now();
+ auto computeDuration = std::chrono::duration_cast(end - begin).count();
+
+ int totalVertices = 0;
+ for (int i = 0; i < fbx.meshCount; i++){
+ totalVertices += fbx.perMeshVertices.at(i).count();
+ }
+
+ int totalTriangles = 0;
+ for (int i = 0; i < fbx.meshCount; i++){
+ totalTriangles += fbx.perMeshTriangleIndices.at(i).count();
+ }
+
+ int totalHulls = 0;
+ QVector hullCounts = results.convexHullsCountList;
+ for (int i = 0; i < results.meshCount; i++){
+ totalHulls += hullCounts.at(i);
+ }
+ cout << endl << "Summary of V-HACD Computation..................." << endl;
+ cout << "File Path : " << inputFilename.toStdString() << endl;
+ cout << "Number Of Meshes : " << fbx.meshCount << endl;
+ cout << "Processed Meshes : " << results.meshCount << endl;
+ cout << "Total vertices : " << totalVertices << endl;
+ cout << "Total Triangles : " << totalTriangles << endl;
+ cout << "Total Convex Hulls : " << totalHulls << endl;
+ cout << "Total FBX load time: " << (double)loadDuration / 1000000000.00 << " seconds" << endl;
+ cout << "V-HACD Compute time: " << (double)computeDuration / 1000000000.00 << " seconds" << endl;
+ cout << endl << "Summary per convex hull ........................" << endl < chList = results.convexHullList.at(i);
+ cout << "\t" << "Number Of Hulls : " << chList.count() << endl;
+
+ for (int j = 0; j < results.convexHullList.at(i).count(); j++){
+ cout << "\tHUll : " << j + 1 << endl;
+ cout << "\t\tNumber Of Points : " << chList.at(j).m_nPoints << endl;
+ cout << "\t\tNumber Of Triangles : " << chList.at(j).m_nTriangles << endl;
+ }
+ }
+
+ writeOBJ(outputFilename, results.convexHullList, outputOneMesh);
+}
+
+VHACDUtilApp::~VHACDUtilApp() {
+}
diff --git a/tools/vhacd/src/VHACDUtilApp.h b/tools/vhacd/src/VHACDUtilApp.h
new file mode 100644
index 0000000000..016b7b7b2f
--- /dev/null
+++ b/tools/vhacd/src/VHACDUtilApp.h
@@ -0,0 +1,28 @@
+//
+// VHACDUtil.h
+// tools/vhacd/src
+//
+// Created by Seth Alves on 3/5/15.
+// Copyright 2015 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_VHACDUtilApp_h
+#define hifi_VHACDUtilApp_h
+
+#include
+
+
+class VHACDUtilApp : public QCoreApplication {
+ Q_OBJECT
+ public:
+ VHACDUtilApp(int argc, char* argv[]);
+ ~VHACDUtilApp();
+};
+
+
+
+#endif //hifi_VHACDUtilApp_h
diff --git a/tools/vhacd/src/main.cpp b/tools/vhacd/src/main.cpp
index e43f712fa0..0e8d72abd3 100644
--- a/tools/vhacd/src/main.cpp
+++ b/tools/vhacd/src/main.cpp
@@ -8,104 +8,20 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-#include
+// #include
#include
#include
#include
#include
#include
-#include "VHACDUtil.h"
+
+#include "VHACDUtilApp.h"
using namespace std;
using namespace VHACD;
-int main(int argc, char * argv[]){
- vector triangles; // array of indexes
- vector points; // array of coordinates
- vhacd::VHACDUtil vUtil;
- vhacd::LoadFBXResults fbx; //mesh data from loaded fbx file
- vhacd::ComputeResults results; // results after computing vhacd
- VHACD::IVHACD::Parameters params;
- vhacd::ProgressCallback pCallBack;
- if (argc < 2){
- cout << "please provide a FBX file as argument\n ";
- return 1;
- }
- string filename(argv[1]);
- if (filename.empty()){
- cout << "please provide a FBX file as argument\n ";
- return 1;
- }
- QString fname = QString::fromStdString(filename);
-
- //set parameters for V-HACD
- params.m_callback = &pCallBack; //progress callback
- params.m_resolution = 50000;
- params.m_depth = 10;
- params.m_concavity = 0.003;
- params.m_alpha = 0.05; // controls the bias toward clipping along symmetry planes
- params.m_pca = 1; // enable/disable normalizing the mesh before applying the convex decomposition
- params.m_mode = 1; // 0: voxel - based approximate convex decomposition, 1 : tetrahedron - based approximate convex decomposition
- params.m_maxNumVerticesPerCH = 128;
- params.m_minVolumePerCH = 0.0001; // controls the adaptive sampling of the generated convex - hulls
-
- // load the mesh
-
- auto begin = std::chrono::high_resolution_clock::now();
- if (!vUtil.loadFBX(fname, &fbx)){
- cout << "Error in opening FBX file....";
- return 1;
- }
- auto end = std::chrono::high_resolution_clock::now();
- auto loadDuration = std::chrono::duration_cast(end - begin).count();
-
- //perform vhacd computation
- begin = std::chrono::high_resolution_clock::now();
- if (!vUtil.computeVHACD(&fbx, params, &results)){
- cout << "Compute Failed...";
- return 1;
- }
- end = std::chrono::high_resolution_clock::now();
- auto computeDuration = std::chrono::duration_cast(end - begin).count();
-
- int totalVertices = 0;
- for (int i = 0; i < fbx.meshCount; i++){
- totalVertices += fbx.perMeshVertices.at(i).count();
- }
-
- int totalTriangles = 0;
- for (int i = 0; i < fbx.meshCount; i++){
- totalTriangles += fbx.perMeshTriangleIndices.at(i).count();
- }
-
- int totalHulls = 0;
- QVector hullCounts = results.convexHullsCountList;
- for (int i = 0; i < results.meshCount; i++){
- totalHulls += hullCounts.at(i);
- }
- cout << endl << "Summary of V-HACD Computation..................." << endl;
- cout << "File Path : " << fname.toStdString() << endl;
- cout << "Number Of Meshes : " << fbx.meshCount << endl;
- cout << "Processed Meshes : " << results.meshCount << endl;
- cout << "Total vertices : " << totalVertices << endl;
- cout << "Total Triangles : " << totalTriangles << endl;
- cout << "Total Convex Hulls : " << totalHulls << endl;
- cout << "Total FBX load time: " << (double)loadDuration / 1000000000.00 << " seconds" << endl;
- cout << "V-HACD Compute time: " << (double)computeDuration / 1000000000.00 << " seconds" << endl;
- cout << endl << "Summary per convex hull ........................" << endl < chList = results.convexHullList.at(i);
- cout << "\t" << "Number Of Hulls : " << chList.count() << endl;
-
- for (int j = 0; j < results.convexHullList.at(i).count(); j++){
- cout << "\tHUll : " << j + 1 << endl;
- cout << "\t\tNumber Of Points : " << chList.at(j).m_nPoints << endl;
- cout << "\t\tNumber Of Triangles : " << chList.at(j).m_nTriangles << endl;
- }
- }
-
- getchar();
+int main(int argc, char * argv[]) {
+ VHACDUtilApp app(argc, argv);
return 0;
-}
\ No newline at end of file
+}