mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 12:28:02 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into editHandleTriggerValues
This commit is contained in:
commit
398c530214
37 changed files with 477 additions and 716 deletions
|
@ -344,8 +344,6 @@ void Agent::scriptRequestFinished() {
|
||||||
void Agent::executeScript() {
|
void Agent::executeScript() {
|
||||||
_scriptEngine = scriptEngineFactory(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload);
|
_scriptEngine = scriptEngineFactory(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload);
|
||||||
|
|
||||||
DependencyManager::get<RecordingScriptingInterface>()->setScriptEngine(_scriptEngine);
|
|
||||||
|
|
||||||
// setup an Avatar for the script to use
|
// setup an Avatar for the script to use
|
||||||
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
||||||
|
|
||||||
|
|
|
@ -522,11 +522,8 @@ void EntityServer::startDynamicDomainVerification() {
|
||||||
qCDebug(entities) << "Entity passed dynamic domain verification:" << entityID;
|
qCDebug(entities) << "Entity passed dynamic domain verification:" << entityID;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << entityID
|
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; NOT deleting entity" << entityID
|
||||||
<< "More info:" << jsonObject;
|
<< "More info:" << jsonObject;
|
||||||
tree->withWriteLock([&] {
|
|
||||||
tree->deleteEntity(entityID, true);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
networkReply->deleteLater();
|
networkReply->deleteLater();
|
||||||
|
|
|
@ -480,7 +480,7 @@ Rectangle {
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
popup.showSpecifyAvatarUrl(function() {
|
popup.showSpecifyAvatarUrl(currentAvatar.avatarUrl, function() {
|
||||||
var url = popup.inputText.text;
|
var url = popup.inputText.text;
|
||||||
emitSendToScript({'method' : 'applyExternalAvatar', 'avatarURL' : url})
|
emitSendToScript({'method' : 'applyExternalAvatar', 'avatarURL' : url})
|
||||||
}, function(link) {
|
}, function(link) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import QtQuick 2.5
|
||||||
MessageBox {
|
MessageBox {
|
||||||
id: popup
|
id: popup
|
||||||
|
|
||||||
function showSpecifyAvatarUrl(callback, linkCallback) {
|
function showSpecifyAvatarUrl(url, callback, linkCallback) {
|
||||||
popup.onButton2Clicked = callback;
|
popup.onButton2Clicked = callback;
|
||||||
popup.titleText = 'Specify Avatar URL'
|
popup.titleText = 'Specify Avatar URL'
|
||||||
popup.bodyText = 'This will not overwrite your existing favorite if you are wearing one.<br>' +
|
popup.bodyText = 'This will not overwrite your existing favorite if you are wearing one.<br>' +
|
||||||
|
@ -12,6 +12,8 @@ MessageBox {
|
||||||
'</a>'
|
'</a>'
|
||||||
popup.inputText.visible = true;
|
popup.inputText.visible = true;
|
||||||
popup.inputText.placeholderText = 'Enter Avatar Url';
|
popup.inputText.placeholderText = 'Enter Avatar Url';
|
||||||
|
popup.inputText.text = url;
|
||||||
|
popup.inputText.selectAll();
|
||||||
popup.button1text = 'CANCEL';
|
popup.button1text = 'CANCEL';
|
||||||
popup.button2text = 'CONFIRM';
|
popup.button2text = 'CONFIRM';
|
||||||
|
|
||||||
|
|
|
@ -4844,12 +4844,6 @@ void Application::loadSettings() {
|
||||||
|
|
||||||
isFirstPerson = (qApp->isHMDMode());
|
isFirstPerson = (qApp->isHMDMode());
|
||||||
|
|
||||||
// Flying should be disabled by default in HMD mode on first run, and it
|
|
||||||
// should be enabled by default in desktop mode.
|
|
||||||
|
|
||||||
auto myAvatar = getMyAvatar();
|
|
||||||
myAvatar->setFlyingEnabled(!isFirstPerson);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// if this is not the first run, the camera will be initialized differently depending on user settings
|
// if this is not the first run, the camera will be initialized differently depending on user settings
|
||||||
|
|
||||||
|
@ -6539,9 +6533,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
||||||
entityScriptingInterface->setPacketSender(&_entityEditSender);
|
entityScriptingInterface->setPacketSender(&_entityEditSender);
|
||||||
entityScriptingInterface->setEntityTree(getEntities()->getTree());
|
entityScriptingInterface->setEntityTree(getEntities()->getTree());
|
||||||
|
|
||||||
// give the script engine to the RecordingScriptingInterface for its callbacks
|
|
||||||
DependencyManager::get<RecordingScriptingInterface>()->setScriptEngine(scriptEngine);
|
|
||||||
|
|
||||||
if (property(hifi::properties::TEST).isValid()) {
|
if (property(hifi::properties::TEST).isValid()) {
|
||||||
scriptEngine->registerGlobalObject("Test", TestScriptingInterface::getInstance());
|
scriptEngine->registerGlobalObject("Test", TestScriptingInterface::getInstance());
|
||||||
}
|
}
|
||||||
|
|
|
@ -577,9 +577,11 @@ void MyAvatar::updateChildCauterization(SpatiallyNestablePointer object, bool ca
|
||||||
|
|
||||||
void MyAvatar::simulate(float deltaTime) {
|
void MyAvatar::simulate(float deltaTime) {
|
||||||
PerformanceTimer perfTimer("simulate");
|
PerformanceTimer perfTimer("simulate");
|
||||||
|
|
||||||
animateScaleChanges(deltaTime);
|
animateScaleChanges(deltaTime);
|
||||||
|
|
||||||
|
setFlyingEnabled(getFlyingEnabled());
|
||||||
|
|
||||||
if (_cauterizationNeedsUpdate) {
|
if (_cauterizationNeedsUpdate) {
|
||||||
_cauterizationNeedsUpdate = false;
|
_cauterizationNeedsUpdate = false;
|
||||||
|
|
||||||
|
@ -724,16 +726,18 @@ void MyAvatar::simulate(float deltaTime) {
|
||||||
properties.setQueryAACubeDirty();
|
properties.setQueryAACubeDirty();
|
||||||
properties.setLastEdited(now);
|
properties.setLastEdited(now);
|
||||||
|
|
||||||
packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entity->getID(), properties);
|
packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree,
|
||||||
|
entity->getID(), properties);
|
||||||
entity->setLastBroadcast(usecTimestampNow());
|
entity->setLastBroadcast(usecTimestampNow());
|
||||||
|
|
||||||
entity->forEachDescendant([&](SpatiallyNestablePointer descendant) {
|
entity->forEachDescendant([&](SpatiallyNestablePointer descendant) {
|
||||||
EntityItemPointer entityDescendant = std::static_pointer_cast<EntityItem>(descendant);
|
EntityItemPointer entityDescendant = std::dynamic_pointer_cast<EntityItem>(descendant);
|
||||||
if (!entityDescendant->getClientOnly() && descendant->updateQueryAACube()) {
|
if (entityDescendant && !entityDescendant->getClientOnly() && descendant->updateQueryAACube()) {
|
||||||
EntityItemProperties descendantProperties;
|
EntityItemProperties descendantProperties;
|
||||||
descendantProperties.setQueryAACube(descendant->getQueryAACube());
|
descendantProperties.setQueryAACube(descendant->getQueryAACube());
|
||||||
descendantProperties.setLastEdited(now);
|
descendantProperties.setLastEdited(now);
|
||||||
packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entityDescendant->getID(), descendantProperties);
|
packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree,
|
||||||
|
entityDescendant->getID(), descendantProperties);
|
||||||
entityDescendant->setLastBroadcast(now); // for debug/physics status icons
|
entityDescendant->setLastBroadcast(now); // for debug/physics status icons
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1131,6 +1135,8 @@ void MyAvatar::saveData() {
|
||||||
settings.setValue("collisionSoundURL", _collisionSoundURL);
|
settings.setValue("collisionSoundURL", _collisionSoundURL);
|
||||||
settings.setValue("useSnapTurn", _useSnapTurn);
|
settings.setValue("useSnapTurn", _useSnapTurn);
|
||||||
settings.setValue("userHeight", getUserHeight());
|
settings.setValue("userHeight", getUserHeight());
|
||||||
|
settings.setValue("flyingDesktop", getFlyingDesktopPref());
|
||||||
|
settings.setValue("flyingHMD", getFlyingHMDPref());
|
||||||
settings.setValue("enabledFlying", getFlyingEnabled());
|
settings.setValue("enabledFlying", getFlyingEnabled());
|
||||||
|
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
@ -1281,8 +1287,13 @@ void MyAvatar::loadData() {
|
||||||
settings.remove("avatarEntityData");
|
settings.remove("avatarEntityData");
|
||||||
}
|
}
|
||||||
setAvatarEntityDataChanged(true);
|
setAvatarEntityDataChanged(true);
|
||||||
|
|
||||||
|
// Flying preferences must be loaded before calling setFlyingEnabled()
|
||||||
Setting::Handle<bool> firstRunVal { Settings::firstRun, true };
|
Setting::Handle<bool> firstRunVal { Settings::firstRun, true };
|
||||||
setFlyingEnabled(firstRunVal.get() ? getFlyingEnabled() : settings.value("enabledFlying").toBool());
|
setFlyingDesktopPref(firstRunVal.get() ? true : settings.value("flyingDesktop").toBool());
|
||||||
|
setFlyingHMDPref(firstRunVal.get() ? false : settings.value("flyingHMD").toBool());
|
||||||
|
setFlyingEnabled(getFlyingEnabled());
|
||||||
|
|
||||||
setDisplayName(settings.value("displayName").toString());
|
setDisplayName(settings.value("displayName").toString());
|
||||||
setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString());
|
setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString());
|
||||||
setSnapTurn(settings.value("useSnapTurn", _useSnapTurn).toBool());
|
setSnapTurn(settings.value("useSnapTurn", _useSnapTurn).toBool());
|
||||||
|
@ -2825,6 +2836,12 @@ void MyAvatar::setFlyingEnabled(bool enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (qApp->isHMDMode()) {
|
||||||
|
setFlyingHMDPref(enabled);
|
||||||
|
} else {
|
||||||
|
setFlyingDesktopPref(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
_enableFlying = enabled;
|
_enableFlying = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2840,7 +2857,33 @@ bool MyAvatar::isInAir() {
|
||||||
|
|
||||||
bool MyAvatar::getFlyingEnabled() {
|
bool MyAvatar::getFlyingEnabled() {
|
||||||
// May return true even if client is not allowed to fly in the zone.
|
// May return true even if client is not allowed to fly in the zone.
|
||||||
return _enableFlying;
|
return (qApp->isHMDMode() ? getFlyingHMDPref() : getFlyingDesktopPref());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyAvatar::setFlyingDesktopPref(bool enabled) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "setFlyingDesktopPref", Q_ARG(bool, enabled));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_flyingPrefDesktop = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyAvatar::getFlyingDesktopPref() {
|
||||||
|
return _flyingPrefDesktop;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyAvatar::setFlyingHMDPref(bool enabled) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "setFlyingHMDPref", Q_ARG(bool, enabled));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_flyingPrefHMD = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyAvatar::getFlyingHMDPref() {
|
||||||
|
return _flyingPrefHMD;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public interface for targetscale
|
// Public interface for targetscale
|
||||||
|
@ -3042,7 +3085,7 @@ static glm::vec3 dampenCgMovement(glm::vec3 cgUnderHeadHandsAvatarSpace, float b
|
||||||
}
|
}
|
||||||
|
|
||||||
// computeCounterBalance returns the center of gravity in Avatar space
|
// computeCounterBalance returns the center of gravity in Avatar space
|
||||||
glm::vec3 MyAvatar::computeCounterBalance() const {
|
glm::vec3 MyAvatar::computeCounterBalance() {
|
||||||
struct JointMass {
|
struct JointMass {
|
||||||
QString name;
|
QString name;
|
||||||
float weight;
|
float weight;
|
||||||
|
@ -3060,7 +3103,8 @@ glm::vec3 MyAvatar::computeCounterBalance() const {
|
||||||
JointMass cgLeftHandMass(QString("LeftHand"), DEFAULT_AVATAR_LEFTHAND_MASS, glm::vec3(0.0f, 0.0f, 0.0f));
|
JointMass cgLeftHandMass(QString("LeftHand"), DEFAULT_AVATAR_LEFTHAND_MASS, glm::vec3(0.0f, 0.0f, 0.0f));
|
||||||
JointMass cgRightHandMass(QString("RightHand"), DEFAULT_AVATAR_RIGHTHAND_MASS, glm::vec3(0.0f, 0.0f, 0.0f));
|
JointMass cgRightHandMass(QString("RightHand"), DEFAULT_AVATAR_RIGHTHAND_MASS, glm::vec3(0.0f, 0.0f, 0.0f));
|
||||||
glm::vec3 tposeHead = DEFAULT_AVATAR_HEAD_POS;
|
glm::vec3 tposeHead = DEFAULT_AVATAR_HEAD_POS;
|
||||||
glm::vec3 tposeHips = glm::vec3(0.0f, 0.0f, 0.0f);
|
glm::vec3 tposeHips = DEFAULT_AVATAR_HIPS_POS;
|
||||||
|
glm::vec3 tposeRightFoot = DEFAULT_AVATAR_RIGHTFOOT_POS;
|
||||||
|
|
||||||
if (_skeletonModel->getRig().indexOfJoint(cgHeadMass.name) != -1) {
|
if (_skeletonModel->getRig().indexOfJoint(cgHeadMass.name) != -1) {
|
||||||
cgHeadMass.position = getAbsoluteJointTranslationInObjectFrame(_skeletonModel->getRig().indexOfJoint(cgHeadMass.name));
|
cgHeadMass.position = getAbsoluteJointTranslationInObjectFrame(_skeletonModel->getRig().indexOfJoint(cgHeadMass.name));
|
||||||
|
@ -3079,6 +3123,9 @@ glm::vec3 MyAvatar::computeCounterBalance() const {
|
||||||
if (_skeletonModel->getRig().indexOfJoint("Hips") != -1) {
|
if (_skeletonModel->getRig().indexOfJoint("Hips") != -1) {
|
||||||
tposeHips = getAbsoluteDefaultJointTranslationInObjectFrame(_skeletonModel->getRig().indexOfJoint("Hips"));
|
tposeHips = getAbsoluteDefaultJointTranslationInObjectFrame(_skeletonModel->getRig().indexOfJoint("Hips"));
|
||||||
}
|
}
|
||||||
|
if (_skeletonModel->getRig().indexOfJoint("RightFoot") != -1) {
|
||||||
|
tposeRightFoot = getAbsoluteDefaultJointTranslationInObjectFrame(_skeletonModel->getRig().indexOfJoint("RightFoot"));
|
||||||
|
}
|
||||||
|
|
||||||
// find the current center of gravity position based on head and hand moments
|
// find the current center of gravity position based on head and hand moments
|
||||||
glm::vec3 sumOfMoments = (cgHeadMass.weight * cgHeadMass.position) + (cgLeftHandMass.weight * cgLeftHandMass.position) + (cgRightHandMass.weight * cgRightHandMass.position);
|
glm::vec3 sumOfMoments = (cgHeadMass.weight * cgHeadMass.position) + (cgLeftHandMass.weight * cgLeftHandMass.position) + (cgRightHandMass.weight * cgRightHandMass.position);
|
||||||
|
@ -3099,9 +3146,12 @@ glm::vec3 MyAvatar::computeCounterBalance() const {
|
||||||
glm::vec3 counterBalancedCg = (1.0f / DEFAULT_AVATAR_HIPS_MASS) * counterBalancedForHead;
|
glm::vec3 counterBalancedCg = (1.0f / DEFAULT_AVATAR_HIPS_MASS) * counterBalancedForHead;
|
||||||
|
|
||||||
// find the height of the hips
|
// find the height of the hips
|
||||||
|
const float UPPER_LEG_FRACTION = 0.3333f;
|
||||||
glm::vec3 xzDiff((cgHeadMass.position.x - counterBalancedCg.x), 0.0f, (cgHeadMass.position.z - counterBalancedCg.z));
|
glm::vec3 xzDiff((cgHeadMass.position.x - counterBalancedCg.x), 0.0f, (cgHeadMass.position.z - counterBalancedCg.z));
|
||||||
float headMinusHipXz = glm::length(xzDiff);
|
float headMinusHipXz = glm::length(xzDiff);
|
||||||
float headHipDefault = glm::length(tposeHead - tposeHips);
|
float headHipDefault = glm::length(tposeHead - tposeHips);
|
||||||
|
float hipFootDefault = tposeHips.y - tposeRightFoot.y;
|
||||||
|
float sitSquatThreshold = tposeHips.y - (UPPER_LEG_FRACTION * hipFootDefault);
|
||||||
float hipHeight = 0.0f;
|
float hipHeight = 0.0f;
|
||||||
if (headHipDefault > headMinusHipXz) {
|
if (headHipDefault > headMinusHipXz) {
|
||||||
hipHeight = sqrtf((headHipDefault * headHipDefault) - (headMinusHipXz * headMinusHipXz));
|
hipHeight = sqrtf((headHipDefault * headHipDefault) - (headMinusHipXz * headMinusHipXz));
|
||||||
|
@ -3113,6 +3163,10 @@ glm::vec3 MyAvatar::computeCounterBalance() const {
|
||||||
if (counterBalancedCg.y > (tposeHips.y + 0.05f)) {
|
if (counterBalancedCg.y > (tposeHips.y + 0.05f)) {
|
||||||
// if the height is higher than default hips, clamp to default hips
|
// if the height is higher than default hips, clamp to default hips
|
||||||
counterBalancedCg.y = tposeHips.y + 0.05f;
|
counterBalancedCg.y = tposeHips.y + 0.05f;
|
||||||
|
} else if (counterBalancedCg.y < sitSquatThreshold) {
|
||||||
|
//do a height reset
|
||||||
|
setResetMode(true);
|
||||||
|
_follow.activate(FollowHelper::Vertical);
|
||||||
}
|
}
|
||||||
return counterBalancedCg;
|
return counterBalancedCg;
|
||||||
}
|
}
|
||||||
|
@ -3162,7 +3216,7 @@ static void drawBaseOfSupport(float baseOfSupportScale, float footLocal, glm::ma
|
||||||
// this function finds the hips position using a center of gravity model that
|
// this function finds the hips position using a center of gravity model that
|
||||||
// balances the head and hands with the hips over the base of support
|
// balances the head and hands with the hips over the base of support
|
||||||
// returns the rotation (-z forward) and position of the Avatar in Sensor space
|
// returns the rotation (-z forward) and position of the Avatar in Sensor space
|
||||||
glm::mat4 MyAvatar::deriveBodyUsingCgModel() const {
|
glm::mat4 MyAvatar::deriveBodyUsingCgModel() {
|
||||||
glm::mat4 sensorToWorldMat = getSensorToWorldMatrix();
|
glm::mat4 sensorToWorldMat = getSensorToWorldMatrix();
|
||||||
glm::mat4 worldToSensorMat = glm::inverse(sensorToWorldMat);
|
glm::mat4 worldToSensorMat = glm::inverse(sensorToWorldMat);
|
||||||
auto headPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
|
auto headPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
|
||||||
|
@ -3180,7 +3234,7 @@ glm::mat4 MyAvatar::deriveBodyUsingCgModel() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the new center of gravity
|
// get the new center of gravity
|
||||||
const glm::vec3 cgHipsPosition = computeCounterBalance();
|
glm::vec3 cgHipsPosition = computeCounterBalance();
|
||||||
|
|
||||||
// find the new hips rotation using the new head-hips axis as the up axis
|
// find the new hips rotation using the new head-hips axis as the up axis
|
||||||
glm::mat4 avatarHipsMat = computeNewHipsMatrix(glmExtractRotation(avatarHeadMat), extractTranslation(avatarHeadMat), cgHipsPosition);
|
glm::mat4 avatarHipsMat = computeNewHipsMatrix(glmExtractRotation(avatarHeadMat), extractTranslation(avatarHeadMat), cgHipsPosition);
|
||||||
|
|
|
@ -955,6 +955,30 @@ public:
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE bool getFlyingEnabled();
|
Q_INVOKABLE bool getFlyingEnabled();
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* @function MyAvatar.setFlyingDesktopPref
|
||||||
|
* @param {boolean} enabled
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void setFlyingDesktopPref(bool enabled);
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* @function MyAvatar.getFlyingDesktopPref
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE bool getFlyingDesktopPref();
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* @function MyAvatar.setFlyingDesktopPref
|
||||||
|
* @param {boolean} enabled
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void setFlyingHMDPref(bool enabled);
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* @function MyAvatar.getFlyingDesktopPref
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE bool getFlyingHMDPref();
|
||||||
|
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* @function MyAvatar.getAvatarScale
|
* @function MyAvatar.getAvatarScale
|
||||||
|
@ -1019,12 +1043,12 @@ public:
|
||||||
// results are in sensor frame (-z forward)
|
// results are in sensor frame (-z forward)
|
||||||
glm::mat4 deriveBodyFromHMDSensor() const;
|
glm::mat4 deriveBodyFromHMDSensor() const;
|
||||||
|
|
||||||
glm::vec3 computeCounterBalance() const;
|
glm::vec3 computeCounterBalance();
|
||||||
|
|
||||||
// derive avatar body position and orientation from using the current HMD Sensor location in relation to the previous
|
// derive avatar body position and orientation from using the current HMD Sensor location in relation to the previous
|
||||||
// location of the base of support of the avatar.
|
// location of the base of support of the avatar.
|
||||||
// results are in sensor frame (-z foward)
|
// results are in sensor frame (-z foward)
|
||||||
glm::mat4 deriveBodyUsingCgModel() const;
|
glm::mat4 deriveBodyUsingCgModel();
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* @function MyAvatar.isUp
|
* @function MyAvatar.isUp
|
||||||
|
@ -1505,6 +1529,8 @@ private:
|
||||||
std::bitset<MAX_DRIVE_KEYS> _disabledDriveKeys;
|
std::bitset<MAX_DRIVE_KEYS> _disabledDriveKeys;
|
||||||
|
|
||||||
bool _enableFlying { false };
|
bool _enableFlying { false };
|
||||||
|
bool _flyingPrefDesktop { true };
|
||||||
|
bool _flyingPrefHMD { false };
|
||||||
bool _wasPushing { false };
|
bool _wasPushing { false };
|
||||||
bool _isPushing { false };
|
bool _isPushing { false };
|
||||||
bool _isBeingPushed { false };
|
bool _isBeingPushed { false };
|
||||||
|
|
|
@ -313,8 +313,8 @@ void setupPreferences() {
|
||||||
getter, setter));
|
getter, setter));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto getter = [=]()->bool { return myAvatar->getFlyingEnabled(); };
|
auto getter = [=]()->bool { return myAvatar->getFlyingHMDPref(); };
|
||||||
auto setter = [=](bool value) { myAvatar->setFlyingEnabled(value); };
|
auto setter = [=](bool value) { myAvatar->setFlyingHMDPref(value); };
|
||||||
preferences->addPreference(new CheckPreference(VR_MOVEMENT, "Flying & jumping", getter, setter));
|
preferences->addPreference(new CheckPreference(VR_MOVEMENT, "Flying & jumping", getter, setter));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|
|
@ -699,14 +699,6 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
|
||||||
adjustShapeInfoByRegistration(shapeInfo);
|
adjustShapeInfoByRegistration(shapeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderableModelEntityItem::setCollisionShape(const btCollisionShape* shape) {
|
|
||||||
const void* key = static_cast<const void*>(shape);
|
|
||||||
if (_collisionMeshKey != key) {
|
|
||||||
_collisionMeshKey = key;
|
|
||||||
emit requestCollisionGeometryUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderableModelEntityItem::setJointMap(std::vector<int> jointMap) {
|
void RenderableModelEntityItem::setJointMap(std::vector<int> jointMap) {
|
||||||
if (jointMap.size() > 0) {
|
if (jointMap.size() > 0) {
|
||||||
_jointMap = jointMap;
|
_jointMap = jointMap;
|
||||||
|
@ -1278,10 +1270,6 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelEntityRenderer::setCollisionMeshKey(const void*key) {
|
|
||||||
_collisionMeshKey = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||||
DETAILED_PROFILE_RANGE(simulation_physics, __FUNCTION__);
|
DETAILED_PROFILE_RANGE(simulation_physics, __FUNCTION__);
|
||||||
if (_hasModel != entity->hasModel()) {
|
if (_hasModel != entity->hasModel()) {
|
||||||
|
|
|
@ -78,8 +78,6 @@ public:
|
||||||
virtual bool isReadyToComputeShape() const override;
|
virtual bool isReadyToComputeShape() const override;
|
||||||
virtual void computeShapeInfo(ShapeInfo& shapeInfo) override;
|
virtual void computeShapeInfo(ShapeInfo& shapeInfo) override;
|
||||||
|
|
||||||
void setCollisionShape(const btCollisionShape* shape) override;
|
|
||||||
|
|
||||||
virtual bool contains(const glm::vec3& point) const override;
|
virtual bool contains(const glm::vec3& point) const override;
|
||||||
void stopModelOverrideIfNoParent();
|
void stopModelOverrideIfNoParent();
|
||||||
|
|
||||||
|
@ -112,10 +110,6 @@ public:
|
||||||
virtual QStringList getJointNames() const override;
|
virtual QStringList getJointNames() const override;
|
||||||
|
|
||||||
bool getMeshes(MeshProxyList& result) override; // deprecated
|
bool getMeshes(MeshProxyList& result) override; // deprecated
|
||||||
const void* getCollisionMeshKey() const { return _collisionMeshKey; }
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void requestCollisionGeometryUpdate();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool needsUpdateModelBounds() const;
|
bool needsUpdateModelBounds() const;
|
||||||
|
@ -130,7 +124,6 @@ private:
|
||||||
QVariantMap _originalTextures;
|
QVariantMap _originalTextures;
|
||||||
bool _dimensionsInitialized { true };
|
bool _dimensionsInitialized { true };
|
||||||
bool _needsJointSimulation { false };
|
bool _needsJointSimulation { false };
|
||||||
const void* _collisionMeshKey { nullptr };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace render { namespace entities {
|
namespace render { namespace entities {
|
||||||
|
@ -161,7 +154,6 @@ protected:
|
||||||
virtual bool needsRenderUpdate() const override;
|
virtual bool needsRenderUpdate() const override;
|
||||||
virtual void doRender(RenderArgs* args) override;
|
virtual void doRender(RenderArgs* args) override;
|
||||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||||
void setCollisionMeshKey(const void* key);
|
|
||||||
|
|
||||||
render::hifi::Tag getTagMask() const override;
|
render::hifi::Tag getTagMask() const override;
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,10 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
|
||||||
_timeUntilNextEmit = 0;
|
_timeUntilNextEmit = 0;
|
||||||
withWriteLock([&]{
|
withWriteLock([&]{
|
||||||
_particleProperties = newParticleProperties;
|
_particleProperties = newParticleProperties;
|
||||||
|
if (!_prevEmitterShouldTrailInitialized) {
|
||||||
|
_prevEmitterShouldTrailInitialized = true;
|
||||||
|
_prevEmitterShouldTrail = _particleProperties.emission.shouldTrail;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_emitting = entity->getIsEmitting();
|
_emitting = entity->getIsEmitting();
|
||||||
|
@ -144,7 +148,12 @@ void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEn
|
||||||
particleUniforms.color.middle = _particleProperties.getColorMiddle();
|
particleUniforms.color.middle = _particleProperties.getColorMiddle();
|
||||||
particleUniforms.color.finish = _particleProperties.getColorFinish();
|
particleUniforms.color.finish = _particleProperties.getColorFinish();
|
||||||
particleUniforms.color.spread = _particleProperties.getColorSpread();
|
particleUniforms.color.spread = _particleProperties.getColorSpread();
|
||||||
|
particleUniforms.spin.start = _particleProperties.spin.range.start;
|
||||||
|
particleUniforms.spin.middle = _particleProperties.spin.gradient.target;
|
||||||
|
particleUniforms.spin.finish = _particleProperties.spin.range.finish;
|
||||||
|
particleUniforms.spin.spread = _particleProperties.spin.gradient.spread;
|
||||||
particleUniforms.lifespan = _particleProperties.lifespan;
|
particleUniforms.lifespan = _particleProperties.lifespan;
|
||||||
|
particleUniforms.rotateWithEntity = _particleProperties.rotateWithEntity ? 1 : 0;
|
||||||
});
|
});
|
||||||
// Update particle uniforms
|
// Update particle uniforms
|
||||||
memcpy(&_uniformBuffer.edit<ParticleUniforms>(), &particleUniforms, sizeof(ParticleUniforms));
|
memcpy(&_uniformBuffer.edit<ParticleUniforms>(), &particleUniforms, sizeof(ParticleUniforms));
|
||||||
|
@ -176,7 +185,7 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa
|
||||||
const auto& azimuthFinish = particleProperties.azimuth.finish;
|
const auto& azimuthFinish = particleProperties.azimuth.finish;
|
||||||
const auto& emitDimensions = particleProperties.emission.dimensions;
|
const auto& emitDimensions = particleProperties.emission.dimensions;
|
||||||
const auto& emitAcceleration = particleProperties.emission.acceleration.target;
|
const auto& emitAcceleration = particleProperties.emission.acceleration.target;
|
||||||
auto emitOrientation = particleProperties.emission.orientation;
|
auto emitOrientation = baseTransform.getRotation() * particleProperties.emission.orientation;
|
||||||
const auto& emitRadiusStart = glm::max(particleProperties.radiusStart, EPSILON); // Avoid math complications at center
|
const auto& emitRadiusStart = glm::max(particleProperties.radiusStart, EPSILON); // Avoid math complications at center
|
||||||
const auto& emitSpeed = particleProperties.emission.speed.target;
|
const auto& emitSpeed = particleProperties.emission.speed.target;
|
||||||
const auto& speedSpread = particleProperties.emission.speed.spread;
|
const auto& speedSpread = particleProperties.emission.speed.spread;
|
||||||
|
@ -185,10 +194,9 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa
|
||||||
|
|
||||||
particle.seed = randFloatInRange(-1.0f, 1.0f);
|
particle.seed = randFloatInRange(-1.0f, 1.0f);
|
||||||
particle.expiration = now + (uint64_t)(particleProperties.lifespan * USECS_PER_SECOND);
|
particle.expiration = now + (uint64_t)(particleProperties.lifespan * USECS_PER_SECOND);
|
||||||
if (particleProperties.emission.shouldTrail) {
|
|
||||||
particle.position = baseTransform.getTranslation();
|
particle.relativePosition = glm::vec3(0.0f);
|
||||||
emitOrientation = baseTransform.getRotation() * emitOrientation;
|
particle.basePosition = baseTransform.getTranslation();
|
||||||
}
|
|
||||||
|
|
||||||
// Position, velocity, and acceleration
|
// Position, velocity, and acceleration
|
||||||
if (polarStart == 0.0f && polarFinish == 0.0f && emitDimensions.z == 0.0f) {
|
if (polarStart == 0.0f && polarFinish == 0.0f && emitDimensions.z == 0.0f) {
|
||||||
|
@ -237,7 +245,7 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa
|
||||||
radii.y > 0.0f ? y / (radii.y * radii.y) : 0.0f,
|
radii.y > 0.0f ? y / (radii.y * radii.y) : 0.0f,
|
||||||
radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f
|
radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f
|
||||||
));
|
));
|
||||||
particle.position += emitOrientation * emitPosition;
|
particle.relativePosition += emitOrientation * emitPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
particle.velocity = (emitSpeed + randFloatInRange(-1.0f, 1.0f) * speedSpread) * (emitOrientation * emitDirection);
|
particle.velocity = (emitSpeed + randFloatInRange(-1.0f, 1.0f) * speedSpread) * (emitOrientation * emitDirection);
|
||||||
|
@ -262,8 +270,8 @@ void ParticleEffectEntityRenderer::stepSimulation() {
|
||||||
particleProperties = _particleProperties;
|
particleProperties = _particleProperties;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const auto& modelTransform = getModelTransform();
|
||||||
if (_emitting && particleProperties.emitting()) {
|
if (_emitting && particleProperties.emitting()) {
|
||||||
const auto& modelTransform = getModelTransform();
|
|
||||||
uint64_t emitInterval = particleProperties.emitIntervalUsecs();
|
uint64_t emitInterval = particleProperties.emitIntervalUsecs();
|
||||||
if (emitInterval > 0 && interval >= _timeUntilNextEmit) {
|
if (emitInterval > 0 && interval >= _timeUntilNextEmit) {
|
||||||
auto timeRemaining = interval;
|
auto timeRemaining = interval;
|
||||||
|
@ -288,15 +296,23 @@ void ParticleEffectEntityRenderer::stepSimulation() {
|
||||||
const float deltaTime = (float)interval / (float)USECS_PER_SECOND;
|
const float deltaTime = (float)interval / (float)USECS_PER_SECOND;
|
||||||
// update the particles
|
// update the particles
|
||||||
for (auto& particle : _cpuParticles) {
|
for (auto& particle : _cpuParticles) {
|
||||||
|
if (_prevEmitterShouldTrail != particleProperties.emission.shouldTrail) {
|
||||||
|
if (_prevEmitterShouldTrail) {
|
||||||
|
particle.relativePosition = particle.relativePosition + particle.basePosition - modelTransform.getTranslation();
|
||||||
|
}
|
||||||
|
particle.basePosition = modelTransform.getTranslation();
|
||||||
|
}
|
||||||
particle.integrate(deltaTime);
|
particle.integrate(deltaTime);
|
||||||
}
|
}
|
||||||
|
_prevEmitterShouldTrail = particleProperties.emission.shouldTrail;
|
||||||
|
|
||||||
// Build particle primitives
|
// Build particle primitives
|
||||||
static GpuParticles gpuParticles;
|
static GpuParticles gpuParticles;
|
||||||
gpuParticles.clear();
|
gpuParticles.clear();
|
||||||
gpuParticles.reserve(_cpuParticles.size()); // Reserve space
|
gpuParticles.reserve(_cpuParticles.size()); // Reserve space
|
||||||
std::transform(_cpuParticles.begin(), _cpuParticles.end(), std::back_inserter(gpuParticles), [](const CpuParticle& particle) {
|
std::transform(_cpuParticles.begin(), _cpuParticles.end(), std::back_inserter(gpuParticles), [&particleProperties, &modelTransform](const CpuParticle& particle) {
|
||||||
return GpuParticle(particle.position, glm::vec2(particle.lifetime, particle.seed));
|
glm::vec3 position = particle.relativePosition + (particleProperties.emission.shouldTrail ? particle.basePosition : modelTransform.getTranslation());
|
||||||
|
return GpuParticle(position, glm::vec2(particle.lifetime, particle.seed));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update particle buffer
|
// Update particle buffer
|
||||||
|
@ -324,15 +340,11 @@ void ParticleEffectEntityRenderer::doRender(RenderArgs* args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Transform transform;
|
Transform transform;
|
||||||
// In trail mode, the particles are created in world space.
|
// The particles are in world space, so the transform is unused, except for the rotation, which we use
|
||||||
// so we only set a transform if they're not in trail mode
|
// if the particles are marked rotateWithEntity
|
||||||
if (!_particleProperties.emission.shouldTrail) {
|
withReadLock([&] {
|
||||||
|
transform.setRotation(_renderTransform.getRotation());
|
||||||
withReadLock([&] {
|
});
|
||||||
transform = _renderTransform;
|
|
||||||
});
|
|
||||||
transform.setScale(vec3(1));
|
|
||||||
}
|
|
||||||
batch.setModelTransform(transform);
|
batch.setModelTransform(transform);
|
||||||
batch.setUniformBuffer(PARTICLE_UNIFORM_SLOT, _uniformBuffer);
|
batch.setUniformBuffer(PARTICLE_UNIFORM_SLOT, _uniformBuffer);
|
||||||
batch.setInputFormat(_vertexFormat);
|
batch.setInputFormat(_vertexFormat);
|
||||||
|
@ -341,5 +353,3 @@ void ParticleEffectEntityRenderer::doRender(RenderArgs* args) {
|
||||||
auto numParticles = _particleBuffer->getSize() / sizeof(GpuParticle);
|
auto numParticles = _particleBuffer->getSize() / sizeof(GpuParticle);
|
||||||
batch.drawInstanced((gpu::uint32)numParticles, gpu::TRIANGLE_STRIP, (gpu::uint32)VERTEX_PER_PARTICLE);
|
batch.drawInstanced((gpu::uint32)numParticles, gpu::TRIANGLE_STRIP, (gpu::uint32)VERTEX_PER_PARTICLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -49,13 +49,14 @@ private:
|
||||||
float seed { 0.0f };
|
float seed { 0.0f };
|
||||||
uint64_t expiration { 0 };
|
uint64_t expiration { 0 };
|
||||||
float lifetime { 0.0f };
|
float lifetime { 0.0f };
|
||||||
glm::vec3 position;
|
glm::vec3 basePosition;
|
||||||
|
glm::vec3 relativePosition;
|
||||||
glm::vec3 velocity;
|
glm::vec3 velocity;
|
||||||
glm::vec3 acceleration;
|
glm::vec3 acceleration;
|
||||||
|
|
||||||
void integrate(float deltaTime) {
|
void integrate(float deltaTime) {
|
||||||
glm::vec3 atSquared = (0.5f * deltaTime * deltaTime) * acceleration;
|
glm::vec3 atSquared = (0.5f * deltaTime * deltaTime) * acceleration;
|
||||||
position += velocity * deltaTime + atSquared;
|
relativePosition += velocity * deltaTime + atSquared;
|
||||||
velocity += acceleration * deltaTime;
|
velocity += acceleration * deltaTime;
|
||||||
lifetime += deltaTime;
|
lifetime += deltaTime;
|
||||||
}
|
}
|
||||||
|
@ -74,15 +75,18 @@ private:
|
||||||
struct ParticleUniforms {
|
struct ParticleUniforms {
|
||||||
InterpolationData<float> radius;
|
InterpolationData<float> radius;
|
||||||
InterpolationData<glm::vec4> color; // rgba
|
InterpolationData<glm::vec4> color; // rgba
|
||||||
|
InterpolationData<float> spin;
|
||||||
float lifespan;
|
float lifespan;
|
||||||
glm::vec3 spare;
|
int rotateWithEntity;
|
||||||
|
glm::vec2 spare;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static CpuParticle createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties);
|
static CpuParticle createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties);
|
||||||
void stepSimulation();
|
void stepSimulation();
|
||||||
|
|
||||||
particle::Properties _particleProperties;
|
particle::Properties _particleProperties;
|
||||||
|
bool _prevEmitterShouldTrail;
|
||||||
|
bool _prevEmitterShouldTrailInitialized { false };
|
||||||
CpuParticles _cpuParticles;
|
CpuParticles _cpuParticles;
|
||||||
bool _emitting { false };
|
bool _emitting { false };
|
||||||
uint64_t _timeUntilNextEmit { 0 };
|
uint64_t _timeUntilNextEmit { 0 };
|
||||||
|
|
|
@ -178,10 +178,6 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
|
||||||
|
|
||||||
|
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
if (_contentType == ContentType::NoContent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This work must be done on the main thread
|
// This work must be done on the main thread
|
||||||
// If we couldn't create a new web surface, exit
|
// If we couldn't create a new web surface, exit
|
||||||
if (!hasWebSurface() && !buildWebSurface(entity)) {
|
if (!hasWebSurface() && !buildWebSurface(entity)) {
|
||||||
|
@ -315,7 +311,13 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
||||||
});
|
});
|
||||||
} else if (_contentType == ContentType::QmlContent) {
|
} else if (_contentType == ContentType::QmlContent) {
|
||||||
_webSurface->load(_lastSourceUrl);
|
_webSurface->load(_lastSourceUrl);
|
||||||
|
} else if (_contentType == ContentType::NoContent) {
|
||||||
|
// Show empty white panel
|
||||||
|
_webSurface->load("controls/WebEntityView.qml", [this](QQmlContext* context, QObject* item) {
|
||||||
|
item->setProperty(URL_PROPERTY, "");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_fadeStartTime = usecTimestampNow();
|
_fadeStartTime = usecTimestampNow();
|
||||||
_webSurface->resume();
|
_webSurface->resume();
|
||||||
|
|
||||||
|
|
|
@ -27,11 +27,20 @@ struct Colors {
|
||||||
vec4 finish;
|
vec4 finish;
|
||||||
vec4 spread;
|
vec4 spread;
|
||||||
};
|
};
|
||||||
|
struct Spin {
|
||||||
|
float start;
|
||||||
|
float middle;
|
||||||
|
float finish;
|
||||||
|
float spread;
|
||||||
|
};
|
||||||
|
|
||||||
struct ParticleUniforms {
|
struct ParticleUniforms {
|
||||||
Radii radius;
|
Radii radius;
|
||||||
Colors color;
|
Colors color;
|
||||||
vec4 lifespan; // x is lifespan, 3 spare floats
|
Spin spin;
|
||||||
|
float lifespan;
|
||||||
|
int rotateWithEntity;
|
||||||
|
vec2 spare;
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(std140) uniform particleBuffer {
|
layout(std140) uniform particleBuffer {
|
||||||
|
@ -44,15 +53,6 @@ layout(location=2) in vec2 inColor; // This is actual Lifetime + Seed
|
||||||
out vec4 varColor;
|
out vec4 varColor;
|
||||||
out vec2 varTexcoord;
|
out vec2 varTexcoord;
|
||||||
|
|
||||||
const int NUM_VERTICES_PER_PARTICLE = 4;
|
|
||||||
// This ordering ensures that un-rotated particles render upright in the viewer.
|
|
||||||
const vec4 UNIT_QUAD[NUM_VERTICES_PER_PARTICLE] = vec4[NUM_VERTICES_PER_PARTICLE](
|
|
||||||
vec4(-1.0, 1.0, 0.0, 0.0),
|
|
||||||
vec4(-1.0, -1.0, 0.0, 0.0),
|
|
||||||
vec4(1.0, 1.0, 0.0, 0.0),
|
|
||||||
vec4(1.0, -1.0, 0.0, 0.0)
|
|
||||||
);
|
|
||||||
|
|
||||||
float bezierInterpolate(float y1, float y2, float y3, float u) {
|
float bezierInterpolate(float y1, float y2, float y3, float u) {
|
||||||
// https://en.wikipedia.org/wiki/Bezier_curve
|
// https://en.wikipedia.org/wiki/Bezier_curve
|
||||||
return (1.0 - u) * (1.0 - u) * y1 + 2.0 * (1.0 - u) * u * y2 + u * u * y3;
|
return (1.0 - u) * (1.0 - u) * y1 + 2.0 * (1.0 - u) * u * y2 + u * u * y3;
|
||||||
|
@ -103,6 +103,15 @@ vec4 interpolate3Vec4(vec4 y1, vec4 y2, vec4 y3, float u) {
|
||||||
interpolate3Points(y1.w, y2.w, y3.w, u));
|
interpolate3Points(y1.w, y2.w, y3.w, u));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int NUM_VERTICES_PER_PARTICLE = 4;
|
||||||
|
const vec2 TEX_COORDS[NUM_VERTICES_PER_PARTICLE] = vec2[NUM_VERTICES_PER_PARTICLE](
|
||||||
|
vec2(-1.0, 0.0),
|
||||||
|
vec2(-1.0, 1.0),
|
||||||
|
vec2(0.0, 0.0),
|
||||||
|
vec2(0.0, 1.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
TransformCamera cam = getTransformCamera();
|
TransformCamera cam = getTransformCamera();
|
||||||
TransformObject obj = getTransformObject();
|
TransformObject obj = getTransformObject();
|
||||||
|
@ -113,28 +122,54 @@ void main(void) {
|
||||||
int twoTriID = gl_VertexID - particleID * NUM_VERTICES_PER_PARTICLE;
|
int twoTriID = gl_VertexID - particleID * NUM_VERTICES_PER_PARTICLE;
|
||||||
|
|
||||||
// Particle properties
|
// Particle properties
|
||||||
float age = inColor.x / particle.lifespan.x;
|
float age = inColor.x / particle.lifespan;
|
||||||
float seed = inColor.y;
|
float seed = inColor.y;
|
||||||
|
|
||||||
// Pass the texcoord and the z texcoord is representing the texture icon
|
// Pass the texcoord
|
||||||
// Offset for corrected vertex ordering.
|
varTexcoord = TEX_COORDS[twoTriID].xy;
|
||||||
varTexcoord = vec2((UNIT_QUAD[twoTriID].xy -1.0) * vec2(0.5, -0.5));
|
|
||||||
varColor = interpolate3Vec4(particle.color.start, particle.color.middle, particle.color.finish, age);
|
varColor = interpolate3Vec4(particle.color.start, particle.color.middle, particle.color.finish, age);
|
||||||
vec3 colorSpread = 2.0 * vec3(hifi_hash(seed), hifi_hash(seed * 2.0), hifi_hash(seed * 3.0)) - 1.0;
|
vec3 colorSpread = 2.0 * vec3(hifi_hash(seed), hifi_hash(seed * 2.0), hifi_hash(seed * 3.0)) - 1.0;
|
||||||
varColor.rgb = clamp(varColor.rgb + colorSpread * particle.color.spread.rgb, vec3(0), vec3(1));
|
varColor.rgb = clamp(varColor.rgb + colorSpread * particle.color.spread.rgb, vec3(0), vec3(1));
|
||||||
float alphaSpread = 2.0 * hifi_hash(seed * 4.0) - 1.0;
|
float alphaSpread = 2.0 * hifi_hash(seed * 4.0) - 1.0;
|
||||||
varColor.a = clamp(varColor.a + alphaSpread * particle.color.spread.a, 0.0, 1.0);
|
varColor.a = clamp(varColor.a + alphaSpread * particle.color.spread.a, 0.0, 1.0);
|
||||||
|
|
||||||
|
float spin = interpolate3Points(particle.spin.start, particle.spin.middle, particle.spin.finish, age);
|
||||||
|
float spinSpread = 2.0 * hifi_hash(seed * 5.0) - 1.0;
|
||||||
|
spin = spin + spinSpread * particle.spin.spread;
|
||||||
|
|
||||||
// anchor point in eye space
|
// anchor point in eye space
|
||||||
float radius = interpolate3Points(particle.radius.start, particle.radius.middle, particle.radius.finish, age);
|
float radius = interpolate3Points(particle.radius.start, particle.radius.middle, particle.radius.finish, age);
|
||||||
float radiusSpread = 2.0 * hifi_hash(seed * 5.0) - 1.0;
|
float radiusSpread = 2.0 * hifi_hash(seed * 6.0) - 1.0;
|
||||||
radius = max(radius + radiusSpread * particle.radius.spread, 0.0);
|
radius = max(radius + radiusSpread * particle.radius.spread, 0.0);
|
||||||
vec4 quadPos = radius * UNIT_QUAD[twoTriID];
|
|
||||||
|
|
||||||
vec4 anchorPoint;
|
// inPosition is in world space
|
||||||
vec4 _inPosition = vec4(inPosition, 1.0);
|
vec4 anchorPoint = cam._view * vec4(inPosition, 1.0);
|
||||||
<$transformModelToEyePos(cam, obj, _inPosition, anchorPoint)$>
|
|
||||||
|
|
||||||
vec4 eyePos = anchorPoint + quadPos;
|
mat3 view3 = mat3(cam._view);
|
||||||
|
vec3 UP = vec3(0, 1, 0);
|
||||||
|
vec3 modelUpWorld;
|
||||||
|
<$transformModelToWorldDir(cam, obj, UP, modelUpWorld)$>
|
||||||
|
vec3 upWorld = mix(UP, normalize(modelUpWorld), particle.rotateWithEntity);
|
||||||
|
vec3 upEye = normalize(view3 * upWorld);
|
||||||
|
vec3 FORWARD = vec3(0, 0, -1);
|
||||||
|
vec3 particleRight = normalize(cross(FORWARD, upEye));
|
||||||
|
vec3 particleUp = cross(particleRight, FORWARD); // don't need to normalize
|
||||||
|
// This ordering ensures that un-rotated particles render upright in the viewer.
|
||||||
|
vec3 UNIT_QUAD[NUM_VERTICES_PER_PARTICLE] = vec3[NUM_VERTICES_PER_PARTICLE](
|
||||||
|
normalize(-particleRight + particleUp),
|
||||||
|
normalize(-particleRight - particleUp),
|
||||||
|
normalize(particleRight + particleUp),
|
||||||
|
normalize(particleRight - particleUp)
|
||||||
|
);
|
||||||
|
float c = cos(spin);
|
||||||
|
float s = sin(spin);
|
||||||
|
mat4 rotation = mat4(
|
||||||
|
c, -s, 0, 0,
|
||||||
|
s, c, 0, 0,
|
||||||
|
0, 0, 1, 0,
|
||||||
|
0, 0, 0, 1
|
||||||
|
);
|
||||||
|
vec4 quadPos = radius * vec4(UNIT_QUAD[twoTriID], 0.0);
|
||||||
|
vec4 eyePos = anchorPoint + rotation * quadPos;
|
||||||
<$transformEyeToClipPos(cam, eyePos, gl_Position)$>
|
<$transformEyeToClipPos(cam, eyePos, gl_Position)$>
|
||||||
}
|
}
|
||||||
|
|
|
@ -378,8 +378,6 @@ public:
|
||||||
/// return preferred shape type (actual physical shape may differ)
|
/// return preferred shape type (actual physical shape may differ)
|
||||||
virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; }
|
virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; }
|
||||||
|
|
||||||
virtual void setCollisionShape(const btCollisionShape* shape) {}
|
|
||||||
|
|
||||||
void setPosition(const glm::vec3& value);
|
void setPosition(const glm::vec3& value);
|
||||||
virtual void setParentID(const QUuid& parentID) override;
|
virtual void setParentID(const QUuid& parentID) override;
|
||||||
virtual void setShapeType(ShapeType type) { /* do nothing */ }
|
virtual void setShapeType(ShapeType type) { /* do nothing */ }
|
||||||
|
|
|
@ -369,6 +369,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
||||||
CHECK_PROPERTY_CHANGE(PROP_MATERIAL_MAPPING_ROT, materialMappingRot);
|
CHECK_PROPERTY_CHANGE(PROP_MATERIAL_MAPPING_ROT, materialMappingRot);
|
||||||
CHECK_PROPERTY_CHANGE(PROP_MATERIAL_DATA, materialData);
|
CHECK_PROPERTY_CHANGE(PROP_MATERIAL_DATA, materialData);
|
||||||
CHECK_PROPERTY_CHANGE(PROP_VISIBLE_IN_SECONDARY_CAMERA, isVisibleInSecondaryCamera);
|
CHECK_PROPERTY_CHANGE(PROP_VISIBLE_IN_SECONDARY_CAMERA, isVisibleInSecondaryCamera);
|
||||||
|
CHECK_PROPERTY_CHANGE(PROP_PARTICLE_SPIN, particleSpin);
|
||||||
|
CHECK_PROPERTY_CHANGE(PROP_SPIN_SPREAD, spinSpread);
|
||||||
|
CHECK_PROPERTY_CHANGE(PROP_SPIN_START, spinStart);
|
||||||
|
CHECK_PROPERTY_CHANGE(PROP_SPIN_FINISH, spinFinish);
|
||||||
|
CHECK_PROPERTY_CHANGE(PROP_PARTICLE_ROTATE_WITH_ENTITY, rotateWithEntity);
|
||||||
|
|
||||||
// Certifiable Properties
|
// Certifiable Properties
|
||||||
CHECK_PROPERTY_CHANGE(PROP_ITEM_NAME, itemName);
|
CHECK_PROPERTY_CHANGE(PROP_ITEM_NAME, itemName);
|
||||||
|
@ -908,6 +913,15 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
||||||
* <code>alpha</code> value is used.
|
* <code>alpha</code> value is used.
|
||||||
* @property {number} alphaSpread=0 - The spread in alpha that each particle is given. If <code>alpha == 0.5</code>
|
* @property {number} alphaSpread=0 - The spread in alpha that each particle is given. If <code>alpha == 0.5</code>
|
||||||
* and <code>alphaSpread == 0.25</code>, each particle will have an alpha in the range <code>0.25</code> – <code>0.75</code>.
|
* and <code>alphaSpread == 0.25</code>, each particle will have an alpha in the range <code>0.25</code> – <code>0.75</code>.
|
||||||
|
* @property {number} particleSpin=0 - The spin of each particle at the middle of its life. In the range <code>-2*PI</code> – <code>2*PI</code>.
|
||||||
|
* @property {number} spinStart=NaN - The spin of each particle at the start of its life. In the range <code>-2*PI</code> – <code>2*PI</code>.
|
||||||
|
* If <code>NaN</code>, the <code>particleSpin</code> value is used.
|
||||||
|
* @property {number} spinFinish=NaN - The spin of each particle at the end of its life. In the range <code>-2*PI</code> – <code>2*PI</code>.
|
||||||
|
* If <code>NaN</code>, the <code>particleSpin</code> value is used.
|
||||||
|
* @property {number} spinSpread=0 - The spread in spin that each particle is given. In the range <code>0</code> – <code>2*PI</code>. If <code>particleSpin == PI</code>
|
||||||
|
* and <code>spinSpread == PI/2</code>, each particle will have a spin in the range <code>PI/2</code> – <code>3*PI/2</code>.
|
||||||
|
* @property {boolean} rotateWithEntity=false - Whether or not the particles' spin will rotate with the entity. If false, when <code>particleSpin == 0</code>, the particles will point
|
||||||
|
* up in the world. If true, they will point towards the entity's up vector, based on its orientation.
|
||||||
*
|
*
|
||||||
* @property {ShapeType} shapeType="none" - <em>Currently not used.</em> <em>Read-only.</em>
|
* @property {ShapeType} shapeType="none" - <em>Currently not used.</em> <em>Read-only.</em>
|
||||||
*
|
*
|
||||||
|
@ -1291,6 +1305,11 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_START, alphaStart);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_START, alphaStart);
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_FINISH, alphaFinish);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_FINISH, alphaFinish);
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMITTER_SHOULD_TRAIL, emitterShouldTrail);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMITTER_SHOULD_TRAIL, emitterShouldTrail);
|
||||||
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARTICLE_SPIN, particleSpin);
|
||||||
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SPIN_SPREAD, spinSpread);
|
||||||
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SPIN_START, spinStart);
|
||||||
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SPIN_FINISH, spinFinish);
|
||||||
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARTICLE_ROTATE_WITH_ENTITY, rotateWithEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Models only
|
// Models only
|
||||||
|
@ -1583,6 +1602,11 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(materialMappingRot, float, setMaterialMappingRot);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(materialMappingRot, float, setMaterialMappingRot);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(materialData, QString, setMaterialData);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(materialData, QString, setMaterialData);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(isVisibleInSecondaryCamera, bool, setIsVisibleInSecondaryCamera);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(isVisibleInSecondaryCamera, bool, setIsVisibleInSecondaryCamera);
|
||||||
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(particleSpin, float, setParticleSpin);
|
||||||
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(spinSpread, float, setSpinSpread);
|
||||||
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(spinStart, float, setSpinStart);
|
||||||
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(spinFinish, float, setSpinFinish);
|
||||||
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(rotateWithEntity, bool, setRotateWithEntity);
|
||||||
|
|
||||||
// Certifiable Properties
|
// Certifiable Properties
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(itemName, QString, setItemName);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(itemName, QString, setItemName);
|
||||||
|
@ -1751,6 +1775,11 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
|
||||||
COPY_PROPERTY_IF_CHANGED(radiusSpread);
|
COPY_PROPERTY_IF_CHANGED(radiusSpread);
|
||||||
COPY_PROPERTY_IF_CHANGED(radiusStart);
|
COPY_PROPERTY_IF_CHANGED(radiusStart);
|
||||||
COPY_PROPERTY_IF_CHANGED(radiusFinish);
|
COPY_PROPERTY_IF_CHANGED(radiusFinish);
|
||||||
|
COPY_PROPERTY_IF_CHANGED(particleSpin);
|
||||||
|
COPY_PROPERTY_IF_CHANGED(spinSpread);
|
||||||
|
COPY_PROPERTY_IF_CHANGED(spinStart);
|
||||||
|
COPY_PROPERTY_IF_CHANGED(spinFinish);
|
||||||
|
COPY_PROPERTY_IF_CHANGED(rotateWithEntity);
|
||||||
|
|
||||||
// Certifiable Properties
|
// Certifiable Properties
|
||||||
COPY_PROPERTY_IF_CHANGED(itemName);
|
COPY_PROPERTY_IF_CHANGED(itemName);
|
||||||
|
@ -1964,6 +1993,12 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
|
||||||
|
|
||||||
ADD_PROPERTY_TO_MAP(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool);
|
ADD_PROPERTY_TO_MAP(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool);
|
||||||
|
|
||||||
|
ADD_PROPERTY_TO_MAP(PROP_PARTICLE_SPIN, ParticleSpin, particleSpin, float);
|
||||||
|
ADD_PROPERTY_TO_MAP(PROP_SPIN_SPREAD, SpinSpread, spinSpread, float);
|
||||||
|
ADD_PROPERTY_TO_MAP(PROP_SPIN_START, SpinStart, spinStart, float);
|
||||||
|
ADD_PROPERTY_TO_MAP(PROP_SPIN_FINISH, SpinFinish, spinFinish, float);
|
||||||
|
ADD_PROPERTY_TO_MAP(PROP_PARTICLE_ROTATE_WITH_ENTITY, RotateWithEntity, rotateWithEntity, float);
|
||||||
|
|
||||||
// Certifiable Properties
|
// Certifiable Properties
|
||||||
ADD_PROPERTY_TO_MAP(PROP_ITEM_NAME, ItemName, itemName, QString);
|
ADD_PROPERTY_TO_MAP(PROP_ITEM_NAME, ItemName, itemName, QString);
|
||||||
ADD_PROPERTY_TO_MAP(PROP_ITEM_DESCRIPTION, ItemDescription, itemDescription, QString);
|
ADD_PROPERTY_TO_MAP(PROP_ITEM_DESCRIPTION, ItemDescription, itemDescription, QString);
|
||||||
|
@ -2292,6 +2327,11 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
||||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA_START, properties.getAlphaStart());
|
APPEND_ENTITY_PROPERTY(PROP_ALPHA_START, properties.getAlphaStart());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA_FINISH, properties.getAlphaFinish());
|
APPEND_ENTITY_PROPERTY(PROP_ALPHA_FINISH, properties.getAlphaFinish());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, properties.getEmitterShouldTrail());
|
APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, properties.getEmitterShouldTrail());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_PARTICLE_SPIN, properties.getParticleSpin());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_SPIN_SPREAD, properties.getSpinSpread());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_SPIN_START, properties.getSpinStart());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_SPIN_FINISH, properties.getSpinFinish());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_PARTICLE_ROTATE_WITH_ENTITY, properties.getRotateWithEntity())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties.getType() == EntityTypes::Zone) {
|
if (properties.getType() == EntityTypes::Zone) {
|
||||||
|
@ -2667,6 +2707,11 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_START, float, setAlphaStart);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_START, float, setAlphaStart);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_FINISH, float, setAlphaFinish);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_FINISH, float, setAlphaFinish);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail);
|
||||||
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_SPIN, float, setParticleSpin);
|
||||||
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SPIN_SPREAD, float, setSpinSpread);
|
||||||
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SPIN_START, float, setSpinStart);
|
||||||
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SPIN_FINISH, float, setSpinFinish);
|
||||||
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_ROTATE_WITH_ENTITY, bool, setRotateWithEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties.getType() == EntityTypes::Zone) {
|
if (properties.getType() == EntityTypes::Zone) {
|
||||||
|
@ -2936,7 +2981,7 @@ void EntityItemProperties::markAllChanged() {
|
||||||
_shapeTypeChanged = true;
|
_shapeTypeChanged = true;
|
||||||
|
|
||||||
_isEmittingChanged = true;
|
_isEmittingChanged = true;
|
||||||
_emitterShouldTrail = true;
|
_emitterShouldTrailChanged = true;
|
||||||
_maxParticlesChanged = true;
|
_maxParticlesChanged = true;
|
||||||
_lifespanChanged = true;
|
_lifespanChanged = true;
|
||||||
_emitRateChanged = true;
|
_emitRateChanged = true;
|
||||||
|
@ -2961,6 +3006,11 @@ void EntityItemProperties::markAllChanged() {
|
||||||
_colorFinishChanged = true;
|
_colorFinishChanged = true;
|
||||||
_alphaStartChanged = true;
|
_alphaStartChanged = true;
|
||||||
_alphaFinishChanged = true;
|
_alphaFinishChanged = true;
|
||||||
|
_particleSpinChanged = true;
|
||||||
|
_spinStartChanged = true;
|
||||||
|
_spinFinishChanged = true;
|
||||||
|
_spinSpreadChanged = true;
|
||||||
|
_rotateWithEntityChanged = true;
|
||||||
|
|
||||||
_materialURLChanged = true;
|
_materialURLChanged = true;
|
||||||
_materialMappingModeChanged = true;
|
_materialMappingModeChanged = true;
|
||||||
|
@ -3309,6 +3359,21 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
||||||
if (radiusFinishChanged()) {
|
if (radiusFinishChanged()) {
|
||||||
out += "radiusFinish";
|
out += "radiusFinish";
|
||||||
}
|
}
|
||||||
|
if (particleSpinChanged()) {
|
||||||
|
out += "particleSpin";
|
||||||
|
}
|
||||||
|
if (spinSpreadChanged()) {
|
||||||
|
out += "spinSpread";
|
||||||
|
}
|
||||||
|
if (spinStartChanged()) {
|
||||||
|
out += "spinStart";
|
||||||
|
}
|
||||||
|
if (spinFinishChanged()) {
|
||||||
|
out += "spinFinish";
|
||||||
|
}
|
||||||
|
if (rotateWithEntityChanged()) {
|
||||||
|
out += "rotateWithEntity";
|
||||||
|
}
|
||||||
if (materialURLChanged()) {
|
if (materialURLChanged()) {
|
||||||
out += "materialURL";
|
out += "materialURL";
|
||||||
}
|
}
|
||||||
|
|
|
@ -235,6 +235,12 @@ public:
|
||||||
|
|
||||||
DEFINE_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool, ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA);
|
DEFINE_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool, ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA);
|
||||||
|
|
||||||
|
DEFINE_PROPERTY(PROP_PARTICLE_SPIN, ParticleSpin, particleSpin, float, particle::DEFAULT_PARTICLE_SPIN);
|
||||||
|
DEFINE_PROPERTY(PROP_SPIN_SPREAD, SpinSpread, spinSpread, float, particle::DEFAULT_SPIN_SPREAD);
|
||||||
|
DEFINE_PROPERTY(PROP_SPIN_START, SpinStart, spinStart, float, particle::DEFAULT_SPIN_START);
|
||||||
|
DEFINE_PROPERTY(PROP_SPIN_FINISH, SpinFinish, spinFinish, float, particle::DEFAULT_SPIN_FINISH);
|
||||||
|
DEFINE_PROPERTY(PROP_PARTICLE_ROTATE_WITH_ENTITY, RotateWithEntity, rotateWithEntity, bool, particle::DEFAULT_ROTATE_WITH_ENTITY);
|
||||||
|
|
||||||
// Certifiable Properties - related to Proof of Purchase certificates
|
// Certifiable Properties - related to Proof of Purchase certificates
|
||||||
DEFINE_PROPERTY_REF(PROP_ITEM_NAME, ItemName, itemName, QString, ENTITY_ITEM_DEFAULT_ITEM_NAME);
|
DEFINE_PROPERTY_REF(PROP_ITEM_NAME, ItemName, itemName, QString, ENTITY_ITEM_DEFAULT_ITEM_NAME);
|
||||||
DEFINE_PROPERTY_REF(PROP_ITEM_DESCRIPTION, ItemDescription, itemDescription, QString, ENTITY_ITEM_DEFAULT_ITEM_DESCRIPTION);
|
DEFINE_PROPERTY_REF(PROP_ITEM_DESCRIPTION, ItemDescription, itemDescription, QString, ENTITY_ITEM_DEFAULT_ITEM_DESCRIPTION);
|
||||||
|
|
|
@ -251,6 +251,12 @@ enum EntityPropertyList {
|
||||||
|
|
||||||
PROP_VISIBLE_IN_SECONDARY_CAMERA, // not sent over the wire, only used locally
|
PROP_VISIBLE_IN_SECONDARY_CAMERA, // not sent over the wire, only used locally
|
||||||
|
|
||||||
|
PROP_PARTICLE_SPIN,
|
||||||
|
PROP_SPIN_START,
|
||||||
|
PROP_SPIN_FINISH,
|
||||||
|
PROP_SPIN_SPREAD,
|
||||||
|
PROP_PARTICLE_ROTATE_WITH_ENTITY,
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// ATTENTION: add new properties to end of list just ABOVE this line
|
// ATTENTION: add new properties to end of list just ABOVE this line
|
||||||
PROP_AFTER_LAST_ITEM,
|
PROP_AFTER_LAST_ITEM,
|
||||||
|
|
|
@ -91,6 +91,8 @@ bool operator==(const Properties& a, const Properties& b) {
|
||||||
(a.color == b.color) &&
|
(a.color == b.color) &&
|
||||||
(a.alpha == b.alpha) &&
|
(a.alpha == b.alpha) &&
|
||||||
(a.radius == b.radius) &&
|
(a.radius == b.radius) &&
|
||||||
|
(a.spin == b.spin) &&
|
||||||
|
(a.rotateWithEntity == b.rotateWithEntity) &&
|
||||||
(a.radiusStart == b.radiusStart) &&
|
(a.radiusStart == b.radiusStart) &&
|
||||||
(a.lifespan == b.lifespan) &&
|
(a.lifespan == b.lifespan) &&
|
||||||
(a.maxParticles == b.maxParticles) &&
|
(a.maxParticles == b.maxParticles) &&
|
||||||
|
@ -130,7 +132,11 @@ bool Properties::valid() const {
|
||||||
(radius.gradient.target == glm::clamp(radius.gradient.target, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) &&
|
(radius.gradient.target == glm::clamp(radius.gradient.target, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) &&
|
||||||
(radius.range.start == glm::clamp(radius.range.start, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) &&
|
(radius.range.start == glm::clamp(radius.range.start, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) &&
|
||||||
(radius.range.finish == glm::clamp(radius.range.finish, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) &&
|
(radius.range.finish == glm::clamp(radius.range.finish, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) &&
|
||||||
(radius.gradient.spread == glm::clamp(radius.gradient.spread, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS));
|
(radius.gradient.spread == glm::clamp(radius.gradient.spread, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) &&
|
||||||
|
(spin.gradient.target == glm::clamp(spin.gradient.target, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) &&
|
||||||
|
(spin.range.start == glm::clamp(spin.range.start, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) &&
|
||||||
|
(spin.range.finish == glm::clamp(spin.range.finish, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) &&
|
||||||
|
(spin.gradient.spread == glm::clamp(spin.gradient.spread, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Properties::emitting() const {
|
bool Properties::emitting() const {
|
||||||
|
@ -332,6 +338,43 @@ void ParticleEffectEntityItem::setRadiusSpread(float radiusSpread) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ParticleEffectEntityItem::setParticleSpin(float particleSpin) {
|
||||||
|
particleSpin = glm::clamp(particleSpin, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN);
|
||||||
|
if (particleSpin != _particleProperties.spin.gradient.target) {
|
||||||
|
withWriteLock([&] {
|
||||||
|
_particleProperties.spin.gradient.target = particleSpin;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleEffectEntityItem::setSpinStart(float spinStart) {
|
||||||
|
spinStart =
|
||||||
|
glm::isnan(spinStart) ? spinStart : glm::clamp(spinStart, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN);
|
||||||
|
if (spinStart != _particleProperties.spin.range.start) {
|
||||||
|
withWriteLock([&] {
|
||||||
|
_particleProperties.spin.range.start = spinStart;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleEffectEntityItem::setSpinFinish(float spinFinish) {
|
||||||
|
spinFinish =
|
||||||
|
glm::isnan(spinFinish) ? spinFinish : glm::clamp(spinFinish, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN);
|
||||||
|
if (spinFinish != _particleProperties.spin.range.finish) {
|
||||||
|
withWriteLock([&] {
|
||||||
|
_particleProperties.spin.range.finish = spinFinish;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleEffectEntityItem::setSpinSpread(float spinSpread) {
|
||||||
|
spinSpread = glm::clamp(spinSpread, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN);
|
||||||
|
if (spinSpread != _particleProperties.spin.gradient.spread) {
|
||||||
|
withWriteLock([&] {
|
||||||
|
_particleProperties.spin.gradient.spread = spinSpread;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ParticleEffectEntityItem::computeAndUpdateDimensions() {
|
void ParticleEffectEntityItem::computeAndUpdateDimensions() {
|
||||||
particle::Properties particleProperties;
|
particle::Properties particleProperties;
|
||||||
|
@ -398,6 +441,11 @@ EntityItemProperties ParticleEffectEntityItem::getProperties(EntityPropertyFlags
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaFinish, getAlphaFinish);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaFinish, getAlphaFinish);
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures);
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitterShouldTrail, getEmitterShouldTrail);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitterShouldTrail, getEmitterShouldTrail);
|
||||||
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleSpin, getParticleSpin);
|
||||||
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(spinSpread, getSpinSpread);
|
||||||
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(spinStart, getSpinStart);
|
||||||
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(spinFinish, getSpinFinish);
|
||||||
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(rotateWithEntity, getRotateWithEntity);
|
||||||
|
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
@ -435,6 +483,11 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaFinish, setAlphaFinish);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaFinish, setAlphaFinish);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitterShouldTrail, setEmitterShouldTrail);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitterShouldTrail, setEmitterShouldTrail);
|
||||||
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleSpin, setParticleSpin);
|
||||||
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(spinSpread, setSpinSpread);
|
||||||
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(spinStart, setSpinStart);
|
||||||
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(spinFinish, setSpinFinish);
|
||||||
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotateWithEntity, setRotateWithEntity);
|
||||||
|
|
||||||
if (somethingChanged) {
|
if (somethingChanged) {
|
||||||
bool wantDebug = false;
|
bool wantDebug = false;
|
||||||
|
@ -515,6 +568,12 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
|
||||||
|
|
||||||
READ_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail);
|
READ_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail);
|
||||||
|
|
||||||
|
READ_ENTITY_PROPERTY(PROP_PARTICLE_SPIN, float, setParticleSpin);
|
||||||
|
READ_ENTITY_PROPERTY(PROP_SPIN_SPREAD, float, setSpinSpread);
|
||||||
|
READ_ENTITY_PROPERTY(PROP_SPIN_START, float, setSpinStart);
|
||||||
|
READ_ENTITY_PROPERTY(PROP_SPIN_FINISH, float, setSpinFinish);
|
||||||
|
READ_ENTITY_PROPERTY(PROP_PARTICLE_ROTATE_WITH_ENTITY, bool, setRotateWithEntity);
|
||||||
|
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -551,6 +610,11 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea
|
||||||
requestedProperties += PROP_AZIMUTH_START;
|
requestedProperties += PROP_AZIMUTH_START;
|
||||||
requestedProperties += PROP_AZIMUTH_FINISH;
|
requestedProperties += PROP_AZIMUTH_FINISH;
|
||||||
requestedProperties += PROP_EMITTER_SHOULD_TRAIL;
|
requestedProperties += PROP_EMITTER_SHOULD_TRAIL;
|
||||||
|
requestedProperties += PROP_PARTICLE_SPIN;
|
||||||
|
requestedProperties += PROP_SPIN_SPREAD;
|
||||||
|
requestedProperties += PROP_SPIN_START;
|
||||||
|
requestedProperties += PROP_SPIN_FINISH;
|
||||||
|
requestedProperties += PROP_PARTICLE_ROTATE_WITH_ENTITY;
|
||||||
|
|
||||||
return requestedProperties;
|
return requestedProperties;
|
||||||
}
|
}
|
||||||
|
@ -594,6 +658,11 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData,
|
||||||
APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_START, getAzimuthStart());
|
APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_START, getAzimuthStart());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, getAzimuthFinish());
|
APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, getAzimuthFinish());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, getEmitterShouldTrail());
|
APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, getEmitterShouldTrail());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_PARTICLE_SPIN, getParticleSpin());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_SPIN_SPREAD, getSpinSpread());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_SPIN_START, getSpinStart());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_SPIN_FINISH, getSpinFinish());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_PARTICLE_ROTATE_WITH_ENTITY, getRotateWithEntity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -665,6 +734,12 @@ void ParticleEffectEntityItem::setEmitterShouldTrail(bool emitterShouldTrail) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ParticleEffectEntityItem::setRotateWithEntity(bool rotateWithEntity) {
|
||||||
|
withWriteLock([&] {
|
||||||
|
_particleProperties.rotateWithEntity = rotateWithEntity;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
particle::Properties ParticleEffectEntityItem::getParticleProperties() const {
|
particle::Properties ParticleEffectEntityItem::getParticleProperties() const {
|
||||||
particle::Properties result;
|
particle::Properties result;
|
||||||
withReadLock([&] {
|
withReadLock([&] {
|
||||||
|
@ -689,6 +764,12 @@ particle::Properties ParticleEffectEntityItem::getParticleProperties() const {
|
||||||
if (glm::isnan(result.radius.range.finish)) {
|
if (glm::isnan(result.radius.range.finish)) {
|
||||||
result.radius.range.finish = getParticleRadius();
|
result.radius.range.finish = getParticleRadius();
|
||||||
}
|
}
|
||||||
|
if (glm::isnan(result.spin.range.start)) {
|
||||||
|
result.spin.range.start = getParticleSpin();
|
||||||
|
}
|
||||||
|
if (glm::isnan(result.spin.range.finish)) {
|
||||||
|
result.spin.range.finish = getParticleSpin();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.valid()) {
|
if (!result.valid()) {
|
||||||
|
|
|
@ -39,7 +39,7 @@ namespace particle {
|
||||||
static const float MINIMUM_EMIT_RATE = 0.0f;
|
static const float MINIMUM_EMIT_RATE = 0.0f;
|
||||||
static const float MAXIMUM_EMIT_RATE = 100000.0f;
|
static const float MAXIMUM_EMIT_RATE = 100000.0f;
|
||||||
static const float DEFAULT_EMIT_SPEED = 5.0f;
|
static const float DEFAULT_EMIT_SPEED = 5.0f;
|
||||||
static const float MINIMUM_EMIT_SPEED = 0.0f;
|
static const float MINIMUM_EMIT_SPEED = -1000.0f;
|
||||||
static const float MAXIMUM_EMIT_SPEED = 1000.0f; // Approx mach 3
|
static const float MAXIMUM_EMIT_SPEED = 1000.0f; // Approx mach 3
|
||||||
static const float DEFAULT_SPEED_SPREAD = 1.0f;
|
static const float DEFAULT_SPEED_SPREAD = 1.0f;
|
||||||
static const glm::quat DEFAULT_EMIT_ORIENTATION = glm::angleAxis(-PI_OVER_TWO, Vectors::UNIT_X); // Vertical
|
static const glm::quat DEFAULT_EMIT_ORIENTATION = glm::angleAxis(-PI_OVER_TWO, Vectors::UNIT_X); // Vertical
|
||||||
|
@ -69,8 +69,15 @@ namespace particle {
|
||||||
static const float DEFAULT_RADIUS_SPREAD = 0.0f;
|
static const float DEFAULT_RADIUS_SPREAD = 0.0f;
|
||||||
static const float DEFAULT_RADIUS_START = UNINITIALIZED;
|
static const float DEFAULT_RADIUS_START = UNINITIALIZED;
|
||||||
static const float DEFAULT_RADIUS_FINISH = UNINITIALIZED;
|
static const float DEFAULT_RADIUS_FINISH = UNINITIALIZED;
|
||||||
|
static const float DEFAULT_PARTICLE_SPIN = 0.0f;
|
||||||
|
static const float DEFAULT_SPIN_START = UNINITIALIZED;
|
||||||
|
static const float DEFAULT_SPIN_FINISH = UNINITIALIZED;
|
||||||
|
static const float DEFAULT_SPIN_SPREAD = 0.0f;
|
||||||
|
static const float MINIMUM_PARTICLE_SPIN = -2.0f * SCRIPT_MAXIMUM_PI;
|
||||||
|
static const float MAXIMUM_PARTICLE_SPIN = 2.0f * SCRIPT_MAXIMUM_PI;
|
||||||
static const QString DEFAULT_TEXTURES = "";
|
static const QString DEFAULT_TEXTURES = "";
|
||||||
static const bool DEFAULT_EMITTER_SHOULD_TRAIL = false;
|
static const bool DEFAULT_EMITTER_SHOULD_TRAIL = false;
|
||||||
|
static const bool DEFAULT_ROTATE_WITH_ENTITY = false;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct Range {
|
struct Range {
|
||||||
|
@ -151,6 +158,8 @@ namespace particle {
|
||||||
RangeGradient<float> alpha { DEFAULT_ALPHA, DEFAULT_ALPHA_START, DEFAULT_ALPHA_FINISH, DEFAULT_ALPHA_SPREAD };
|
RangeGradient<float> alpha { DEFAULT_ALPHA, DEFAULT_ALPHA_START, DEFAULT_ALPHA_FINISH, DEFAULT_ALPHA_SPREAD };
|
||||||
float radiusStart { DEFAULT_EMIT_RADIUS_START };
|
float radiusStart { DEFAULT_EMIT_RADIUS_START };
|
||||||
RangeGradient<float> radius { DEFAULT_PARTICLE_RADIUS, DEFAULT_RADIUS_START, DEFAULT_RADIUS_FINISH, DEFAULT_RADIUS_SPREAD };
|
RangeGradient<float> radius { DEFAULT_PARTICLE_RADIUS, DEFAULT_RADIUS_START, DEFAULT_RADIUS_FINISH, DEFAULT_RADIUS_SPREAD };
|
||||||
|
RangeGradient<float> spin { DEFAULT_PARTICLE_SPIN, DEFAULT_SPIN_START, DEFAULT_SPIN_FINISH, DEFAULT_SPIN_SPREAD };
|
||||||
|
bool rotateWithEntity { DEFAULT_ROTATE_WITH_ENTITY };
|
||||||
float lifespan { DEFAULT_LIFESPAN };
|
float lifespan { DEFAULT_LIFESPAN };
|
||||||
uint32_t maxParticles { DEFAULT_MAX_PARTICLES };
|
uint32_t maxParticles { DEFAULT_MAX_PARTICLES };
|
||||||
EmitProperties emission;
|
EmitProperties emission;
|
||||||
|
@ -168,6 +177,8 @@ namespace particle {
|
||||||
Properties& operator =(const Properties& other) {
|
Properties& operator =(const Properties& other) {
|
||||||
color = other.color;
|
color = other.color;
|
||||||
alpha = other.alpha;
|
alpha = other.alpha;
|
||||||
|
spin = other.spin;
|
||||||
|
rotateWithEntity = other.rotateWithEntity;
|
||||||
radius = other.radius;
|
radius = other.radius;
|
||||||
lifespan = other.lifespan;
|
lifespan = other.lifespan;
|
||||||
maxParticles = other.maxParticles;
|
maxParticles = other.maxParticles;
|
||||||
|
@ -306,6 +317,21 @@ public:
|
||||||
void setRadiusSpread(float radiusSpread);
|
void setRadiusSpread(float radiusSpread);
|
||||||
float getRadiusSpread() const { return _particleProperties.radius.gradient.spread; }
|
float getRadiusSpread() const { return _particleProperties.radius.gradient.spread; }
|
||||||
|
|
||||||
|
void setParticleSpin(float particleSpin);
|
||||||
|
float getParticleSpin() const { return _particleProperties.spin.gradient.target; }
|
||||||
|
|
||||||
|
void setSpinStart(float spinStart);
|
||||||
|
float getSpinStart() const { return _particleProperties.spin.range.start; }
|
||||||
|
|
||||||
|
void setSpinFinish(float spinFinish);
|
||||||
|
float getSpinFinish() const { return _particleProperties.spin.range.finish; }
|
||||||
|
|
||||||
|
void setSpinSpread(float spinSpread);
|
||||||
|
float getSpinSpread() const { return _particleProperties.spin.gradient.spread; }
|
||||||
|
|
||||||
|
void setRotateWithEntity(bool rotateWithEntity);
|
||||||
|
bool getRotateWithEntity() const { return _particleProperties.rotateWithEntity; }
|
||||||
|
|
||||||
void computeAndUpdateDimensions();
|
void computeAndUpdateDimensions();
|
||||||
|
|
||||||
void setTextures(const QString& textures);
|
void setTextures(const QString& textures);
|
||||||
|
|
|
@ -238,7 +238,7 @@ TransformObject getTransformObject() {
|
||||||
<@endfunc@>
|
<@endfunc@>
|
||||||
|
|
||||||
<@func transformModelToWorldDir(cameraTransform, objectTransform, modelDir, worldDir)@>
|
<@func transformModelToWorldDir(cameraTransform, objectTransform, modelDir, worldDir)@>
|
||||||
{ // transformModelToEyeDir
|
{ // transformModelToWorldDir
|
||||||
vec3 mr0 = <$objectTransform$>._modelInverse[0].xyz;
|
vec3 mr0 = <$objectTransform$>._modelInverse[0].xyz;
|
||||||
vec3 mr1 = <$objectTransform$>._modelInverse[1].xyz;
|
vec3 mr1 = <$objectTransform$>._modelInverse[1].xyz;
|
||||||
vec3 mr2 = <$objectTransform$>._modelInverse[2].xyz;
|
vec3 mr2 = <$objectTransform$>._modelInverse[2].xyz;
|
||||||
|
|
|
@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
case PacketType::EntityEdit:
|
case PacketType::EntityEdit:
|
||||||
case PacketType::EntityData:
|
case PacketType::EntityData:
|
||||||
case PacketType::EntityPhysics:
|
case PacketType::EntityPhysics:
|
||||||
return static_cast<PacketVersion>(EntityVersion::ParticleEntityFix);
|
return static_cast<PacketVersion>(EntityVersion::ParticleSpin);
|
||||||
case PacketType::EntityQuery:
|
case PacketType::EntityQuery:
|
||||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
|
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
|
||||||
case PacketType::AvatarIdentity:
|
case PacketType::AvatarIdentity:
|
||||||
|
|
|
@ -238,7 +238,8 @@ enum class EntityVersion : PacketVersion {
|
||||||
CloneableData,
|
CloneableData,
|
||||||
CollisionMask16Bytes,
|
CollisionMask16Bytes,
|
||||||
YieldSimulationOwnership,
|
YieldSimulationOwnership,
|
||||||
ParticleEntityFix
|
ParticleEntityFix,
|
||||||
|
ParticleSpin
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class EntityScriptCallMethodVersion : PacketVersion {
|
enum class EntityScriptCallMethodVersion : PacketVersion {
|
||||||
|
|
|
@ -1,217 +0,0 @@
|
||||||
//
|
|
||||||
// CollisionRenderMeshCache.cpp
|
|
||||||
// libraries/physics/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows 2016.07.13
|
|
||||||
// Copyright 2016 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 "CollisionRenderMeshCache.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include <btBulletDynamicsCommon.h>
|
|
||||||
#include <BulletCollision/CollisionShapes/btShapeHull.h>
|
|
||||||
|
|
||||||
#include <ShapeInfo.h> // for MAX_HULL_POINTS
|
|
||||||
|
|
||||||
const int32_t MAX_HULL_INDICES = 6 * MAX_HULL_POINTS;
|
|
||||||
const int32_t MAX_HULL_NORMALS = MAX_HULL_INDICES;
|
|
||||||
float tempVertices[MAX_HULL_NORMALS];
|
|
||||||
graphics::Index tempIndexBuffer[MAX_HULL_INDICES];
|
|
||||||
|
|
||||||
bool copyShapeToMesh(const btTransform& transform, const btConvexShape* shape,
|
|
||||||
gpu::BufferView& vertices, gpu::BufferView& indices, gpu::BufferView& parts,
|
|
||||||
gpu::BufferView& normals) {
|
|
||||||
assert(shape);
|
|
||||||
|
|
||||||
btShapeHull hull(shape);
|
|
||||||
if (!hull.buildHull(shape->getMargin())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t numHullIndices = hull.numIndices();
|
|
||||||
assert(numHullIndices <= MAX_HULL_INDICES);
|
|
||||||
|
|
||||||
int32_t numHullVertices = hull.numVertices();
|
|
||||||
assert(numHullVertices <= MAX_HULL_POINTS);
|
|
||||||
|
|
||||||
{ // new part
|
|
||||||
graphics::Mesh::Part part;
|
|
||||||
part._startIndex = (graphics::Index)indices.getNumElements();
|
|
||||||
part._numIndices = (graphics::Index)numHullIndices;
|
|
||||||
// FIXME: the render code cannot handle the case where part._baseVertex != 0
|
|
||||||
//part._baseVertex = vertices.getNumElements(); // DOES NOT WORK
|
|
||||||
part._baseVertex = 0;
|
|
||||||
|
|
||||||
gpu::BufferView::Size numBytes = sizeof(graphics::Mesh::Part);
|
|
||||||
const gpu::Byte* data = reinterpret_cast<const gpu::Byte*>(&part);
|
|
||||||
parts._buffer->append(numBytes, data);
|
|
||||||
parts._size = parts._buffer->getSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
const int32_t SIZE_OF_VEC3 = 3 * sizeof(float);
|
|
||||||
graphics::Index indexOffset = (graphics::Index)vertices.getNumElements();
|
|
||||||
|
|
||||||
{ // new indices
|
|
||||||
const uint32_t* hullIndices = hull.getIndexPointer();
|
|
||||||
// FIXME: the render code cannot handle the case where part._baseVertex != 0
|
|
||||||
// so we must add an offset to each index
|
|
||||||
for (int32_t i = 0; i < numHullIndices; ++i) {
|
|
||||||
tempIndexBuffer[i] = hullIndices[i] + indexOffset;
|
|
||||||
}
|
|
||||||
const gpu::Byte* data = reinterpret_cast<const gpu::Byte*>(tempIndexBuffer);
|
|
||||||
gpu::BufferView::Size numBytes = (gpu::BufferView::Size)(sizeof(graphics::Index) * numHullIndices);
|
|
||||||
indices._buffer->append(numBytes, data);
|
|
||||||
indices._size = indices._buffer->getSize();
|
|
||||||
}
|
|
||||||
{ // new vertices
|
|
||||||
const btVector3* hullVertices = hull.getVertexPointer();
|
|
||||||
assert(numHullVertices <= MAX_HULL_POINTS);
|
|
||||||
for (int32_t i = 0; i < numHullVertices; ++i) {
|
|
||||||
btVector3 transformedPoint = transform * hullVertices[i];
|
|
||||||
memcpy(tempVertices + 3 * i, transformedPoint.m_floats, SIZE_OF_VEC3);
|
|
||||||
}
|
|
||||||
gpu::BufferView::Size numBytes = sizeof(float) * (3 * numHullVertices);
|
|
||||||
const gpu::Byte* data = reinterpret_cast<const gpu::Byte*>(tempVertices);
|
|
||||||
vertices._buffer->append(numBytes, data);
|
|
||||||
vertices._size = vertices._buffer->getSize();
|
|
||||||
}
|
|
||||||
{ // new normals
|
|
||||||
// compute average point
|
|
||||||
btVector3 avgVertex(0.0f, 0.0f, 0.0f);
|
|
||||||
const btVector3* hullVertices = hull.getVertexPointer();
|
|
||||||
for (int i = 0; i < numHullVertices; ++i) {
|
|
||||||
avgVertex += hullVertices[i];
|
|
||||||
}
|
|
||||||
avgVertex = transform * (avgVertex * (1.0f / (float)numHullVertices));
|
|
||||||
|
|
||||||
for (int i = 0; i < numHullVertices; ++i) {
|
|
||||||
btVector3 norm = transform * hullVertices[i] - avgVertex;
|
|
||||||
btScalar normLength = norm.length();
|
|
||||||
if (normLength > FLT_EPSILON) {
|
|
||||||
norm /= normLength;
|
|
||||||
}
|
|
||||||
memcpy(tempVertices + 3 * i, norm.m_floats, SIZE_OF_VEC3);
|
|
||||||
}
|
|
||||||
gpu::BufferView::Size numBytes = sizeof(float) * (3 * numHullVertices);
|
|
||||||
const gpu::Byte* data = reinterpret_cast<const gpu::Byte*>(tempVertices);
|
|
||||||
normals._buffer->append(numBytes, data);
|
|
||||||
normals._size = vertices._buffer->getSize();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
graphics::MeshPointer createMeshFromShape(const void* pointer) {
|
|
||||||
graphics::MeshPointer mesh;
|
|
||||||
if (!pointer) {
|
|
||||||
return mesh;
|
|
||||||
}
|
|
||||||
|
|
||||||
// pointer must be a const btCollisionShape* (cast to void*), but it only
|
|
||||||
// needs to be valid here when its render mesh is created, after this call
|
|
||||||
// the cache doesn't care what happens to the shape behind the pointer
|
|
||||||
const btCollisionShape* shape = static_cast<const btCollisionShape*>(pointer);
|
|
||||||
|
|
||||||
int32_t shapeType = shape->getShapeType();
|
|
||||||
if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE || shape->isConvex()) {
|
|
||||||
// allocate buffers for it
|
|
||||||
gpu::BufferView vertices(new gpu::Buffer(), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
|
|
||||||
gpu::BufferView indices(new gpu::Buffer(), gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::INDEX));
|
|
||||||
gpu::BufferView parts(new gpu::Buffer(), gpu::Element(gpu::VEC4, gpu::UINT32, gpu::PART));
|
|
||||||
gpu::BufferView normals(new gpu::Buffer(), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
|
|
||||||
|
|
||||||
int32_t numSuccesses = 0;
|
|
||||||
if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE) {
|
|
||||||
const btCompoundShape* compoundShape = static_cast<const btCompoundShape*>(shape);
|
|
||||||
int32_t numSubShapes = compoundShape->getNumChildShapes();
|
|
||||||
for (int32_t i = 0; i < numSubShapes; ++i) {
|
|
||||||
const btCollisionShape* childShape = compoundShape->getChildShape(i);
|
|
||||||
if (childShape->isConvex()) {
|
|
||||||
const btConvexShape* convexShape = static_cast<const btConvexShape*>(childShape);
|
|
||||||
if (copyShapeToMesh(compoundShape->getChildTransform(i), convexShape, vertices, indices, parts, normals)) {
|
|
||||||
numSuccesses++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// shape is convex
|
|
||||||
const btConvexShape* convexShape = static_cast<const btConvexShape*>(shape);
|
|
||||||
btTransform transform;
|
|
||||||
transform.setIdentity();
|
|
||||||
if (copyShapeToMesh(transform, convexShape, vertices, indices, parts, normals)) {
|
|
||||||
numSuccesses++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (numSuccesses > 0) {
|
|
||||||
mesh = std::make_shared<graphics::Mesh>();
|
|
||||||
mesh->setVertexBuffer(vertices);
|
|
||||||
mesh->setIndexBuffer(indices);
|
|
||||||
mesh->setPartBuffer(parts);
|
|
||||||
mesh->addAttribute(gpu::Stream::NORMAL, normals);
|
|
||||||
} else {
|
|
||||||
// TODO: log failure message here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mesh;
|
|
||||||
}
|
|
||||||
|
|
||||||
CollisionRenderMeshCache::CollisionRenderMeshCache() {
|
|
||||||
}
|
|
||||||
|
|
||||||
CollisionRenderMeshCache::~CollisionRenderMeshCache() {
|
|
||||||
_meshMap.clear();
|
|
||||||
_pendingGarbage.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
graphics::MeshPointer CollisionRenderMeshCache::getMesh(CollisionRenderMeshCache::Key key) {
|
|
||||||
graphics::MeshPointer mesh;
|
|
||||||
if (key) {
|
|
||||||
CollisionMeshMap::const_iterator itr = _meshMap.find(key);
|
|
||||||
if (itr == _meshMap.end()) {
|
|
||||||
// make mesh and add it to map
|
|
||||||
mesh = createMeshFromShape(key);
|
|
||||||
if (mesh) {
|
|
||||||
_meshMap.insert(std::make_pair(key, mesh));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mesh = itr->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const uint32_t MAX_NUM_PENDING_GARBAGE = 20;
|
|
||||||
if (_pendingGarbage.size() > MAX_NUM_PENDING_GARBAGE) {
|
|
||||||
collectGarbage();
|
|
||||||
}
|
|
||||||
return mesh;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CollisionRenderMeshCache::releaseMesh(CollisionRenderMeshCache::Key key) {
|
|
||||||
if (!key) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
CollisionMeshMap::const_iterator itr = _meshMap.find(key);
|
|
||||||
if (itr != _meshMap.end()) {
|
|
||||||
_pendingGarbage.push_back(key);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CollisionRenderMeshCache::collectGarbage() {
|
|
||||||
uint32_t numShapes = (uint32_t)_pendingGarbage.size();
|
|
||||||
for (uint32_t i = 0; i < numShapes; ++i) {
|
|
||||||
CollisionRenderMeshCache::Key key = _pendingGarbage[i];
|
|
||||||
CollisionMeshMap::const_iterator itr = _meshMap.find(key);
|
|
||||||
if (itr != _meshMap.end()) {
|
|
||||||
if ((*itr).second.use_count() == 1) {
|
|
||||||
// we hold the only reference
|
|
||||||
_meshMap.erase(itr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_pendingGarbage.clear();
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
//
|
|
||||||
// CollisionRenderMeshCache.h
|
|
||||||
// libraries/physics/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows 2016.07.13
|
|
||||||
// Copyright 2016 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_CollisionRenderMeshCache_h
|
|
||||||
#define hifi_CollisionRenderMeshCache_h
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include <graphics/Geometry.h>
|
|
||||||
|
|
||||||
|
|
||||||
class CollisionRenderMeshCache {
|
|
||||||
public:
|
|
||||||
using Key = const void*; // must actually be a const btCollisionShape*
|
|
||||||
|
|
||||||
CollisionRenderMeshCache();
|
|
||||||
~CollisionRenderMeshCache();
|
|
||||||
|
|
||||||
/// \return pointer to geometry
|
|
||||||
graphics::MeshPointer getMesh(Key key);
|
|
||||||
|
|
||||||
/// \return true if geometry was found and released
|
|
||||||
bool releaseMesh(Key key);
|
|
||||||
|
|
||||||
/// delete geometries that have zero references
|
|
||||||
void collectGarbage();
|
|
||||||
|
|
||||||
// validation methods
|
|
||||||
uint32_t getNumMeshes() const { return (uint32_t)_meshMap.size(); }
|
|
||||||
bool hasMesh(Key key) const { return _meshMap.find(key) == _meshMap.end(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
using CollisionMeshMap = std::unordered_map<Key, graphics::MeshPointer>;
|
|
||||||
CollisionMeshMap _meshMap;
|
|
||||||
std::vector<Key> _pendingGarbage;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_CollisionRenderMeshCache_h
|
|
|
@ -307,13 +307,6 @@ const btCollisionShape* EntityMotionState::computeNewShape() {
|
||||||
return getShapeManager()->getShape(shapeInfo);
|
return getShapeManager()->getShape(shapeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::setShape(const btCollisionShape* shape) {
|
|
||||||
if (_shape != shape) {
|
|
||||||
ObjectMotionState::setShape(shape);
|
|
||||||
_entity->setCollisionShape(_shape);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
// NOTE: this method is only ever called when the entity simulation is locally owned
|
// NOTE: this method is only ever called when the entity simulation is locally owned
|
||||||
DETAILED_PROFILE_RANGE(simulation_physics, "CheckOutOfSync");
|
DETAILED_PROFILE_RANGE(simulation_physics, "CheckOutOfSync");
|
||||||
|
|
|
@ -118,7 +118,6 @@ protected:
|
||||||
|
|
||||||
bool isReadyToComputeShape() const override;
|
bool isReadyToComputeShape() const override;
|
||||||
const btCollisionShape* computeNewShape() override;
|
const btCollisionShape* computeNewShape() override;
|
||||||
void setShape(const btCollisionShape* shape) override;
|
|
||||||
void setMotionType(PhysicsMotionType motionType) override;
|
void setMotionType(PhysicsMotionType motionType) override;
|
||||||
|
|
||||||
// EntityMotionState keeps a SharedPointer to its EntityItem which is only set in the CTOR
|
// EntityMotionState keeps a SharedPointer to its EntityItem which is only set in the CTOR
|
||||||
|
|
|
@ -155,26 +155,25 @@ void ObjectMotionState::setMotionType(PhysicsMotionType motionType) {
|
||||||
// Update the Continuous Collision Detection (CCD) configuration settings of our RigidBody so that
|
// Update the Continuous Collision Detection (CCD) configuration settings of our RigidBody so that
|
||||||
// CCD will be enabled automatically when its speed surpasses a certain threshold.
|
// CCD will be enabled automatically when its speed surpasses a certain threshold.
|
||||||
void ObjectMotionState::updateCCDConfiguration() {
|
void ObjectMotionState::updateCCDConfiguration() {
|
||||||
if (_body) {
|
assert(_body);
|
||||||
if (_shape) {
|
if (_shape && _shape->getShapeType() != TRIANGLE_MESH_SHAPE_PROXYTYPE) {
|
||||||
// If this object moves faster than its bounding radius * RADIUS_MOTION_THRESHOLD_MULTIPLIER,
|
// find minumum dimension of shape
|
||||||
// CCD will be enabled for this object.
|
btVector3 aabbMin, aabbMax;
|
||||||
const auto RADIUS_MOTION_THRESHOLD_MULTIPLIER = 0.5f;
|
btTransform transform;
|
||||||
|
transform.setIdentity();
|
||||||
|
_shape->getAabb(transform, aabbMin, aabbMax);
|
||||||
|
aabbMin = aabbMax - aabbMin;
|
||||||
|
btScalar radius = *((btScalar*)(aabbMin) + aabbMin.minAxis());
|
||||||
|
|
||||||
btVector3 center;
|
// use the minimum dimension as the radius of the CCD proxy sphere
|
||||||
btScalar radius;
|
_body->setCcdSweptSphereRadius(radius);
|
||||||
_shape->getBoundingSphere(center, radius);
|
|
||||||
_body->setCcdMotionThreshold(radius * RADIUS_MOTION_THRESHOLD_MULTIPLIER);
|
|
||||||
|
|
||||||
// TODO: Ideally the swept sphere radius would be contained by the object. Using the bounding sphere
|
// also use the radius as the motion threshold for enabling CCD
|
||||||
// radius works well for spherical objects, but may cause issues with other shapes. For arbitrary
|
_body->setCcdMotionThreshold(radius);
|
||||||
// objects we may want to consider a different approach, such as grouping rigid bodies together.
|
} else {
|
||||||
|
// disable CCD
|
||||||
_body->setCcdSweptSphereRadius(radius);
|
_body->setCcdSweptSphereRadius(0.0f);
|
||||||
} else {
|
_body->setCcdMotionThreshold(0.0f);
|
||||||
// Disable CCD
|
|
||||||
_body->setCcdMotionThreshold(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,8 +187,8 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) {
|
||||||
if (_body) {
|
if (_body) {
|
||||||
_body->setUserPointer(this);
|
_body->setUserPointer(this);
|
||||||
assert(_body->getCollisionShape() == _shape);
|
assert(_body->getCollisionShape() == _shape);
|
||||||
|
updateCCDConfiguration();
|
||||||
}
|
}
|
||||||
updateCCDConfiguration();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,6 +198,9 @@ void ObjectMotionState::setShape(const btCollisionShape* shape) {
|
||||||
getShapeManager()->releaseShape(_shape);
|
getShapeManager()->releaseShape(_shape);
|
||||||
}
|
}
|
||||||
_shape = shape;
|
_shape = shape;
|
||||||
|
if (_body) {
|
||||||
|
updateCCDConfiguration();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,7 +314,6 @@ bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine*
|
||||||
} else {
|
} else {
|
||||||
_body->setCollisionShape(const_cast<btCollisionShape*>(newShape));
|
_body->setCollisionShape(const_cast<btCollisionShape*>(newShape));
|
||||||
setShape(newShape);
|
setShape(newShape);
|
||||||
updateCCDConfiguration();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
|
if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
|
||||||
|
|
|
@ -59,7 +59,7 @@ void RecordingScriptingInterface::playClip(NetworkClipLoaderPointer clipLoader,
|
||||||
|
|
||||||
if (callback.isFunction()) {
|
if (callback.isFunction()) {
|
||||||
QScriptValueList args { true, url };
|
QScriptValueList args { true, url };
|
||||||
callback.call(_scriptEngine->globalObject(), args);
|
callback.call(QScriptValue(), args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue
|
||||||
auto weakClipLoader = clipLoader.toWeakRef();
|
auto weakClipLoader = clipLoader.toWeakRef();
|
||||||
|
|
||||||
// when clip loaded, call the callback with the URL and success boolean
|
// when clip loaded, call the callback with the URL and success boolean
|
||||||
connect(clipLoader.data(), &recording::NetworkClipLoader::clipLoaded, this,
|
connect(clipLoader.data(), &recording::NetworkClipLoader::clipLoaded, callback.engine(),
|
||||||
[this, weakClipLoader, url, callback]() mutable {
|
[this, weakClipLoader, url, callback]() mutable {
|
||||||
|
|
||||||
if (auto clipLoader = weakClipLoader.toStrongRef()) {
|
if (auto clipLoader = weakClipLoader.toStrongRef()) {
|
||||||
|
@ -92,12 +92,12 @@ void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue
|
||||||
});
|
});
|
||||||
|
|
||||||
// when clip load fails, call the callback with the URL and failure boolean
|
// when clip load fails, call the callback with the URL and failure boolean
|
||||||
connect(clipLoader.data(), &recording::NetworkClipLoader::failed, this, [this, weakClipLoader, url, callback](QNetworkReply::NetworkError error) mutable {
|
connect(clipLoader.data(), &recording::NetworkClipLoader::failed, callback.engine(), [this, weakClipLoader, url, callback](QNetworkReply::NetworkError error) mutable {
|
||||||
qCDebug(scriptengine) << "Failed to load recording from" << url;
|
qCDebug(scriptengine) << "Failed to load recording from" << url;
|
||||||
|
|
||||||
if (callback.isFunction()) {
|
if (callback.isFunction()) {
|
||||||
QScriptValueList args { false, url };
|
QScriptValueList args { false, url };
|
||||||
callback.call(_scriptEngine->currentContext()->thisObject(), args);
|
callback.call(QScriptValue(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto clipLoader = weakClipLoader.toStrongRef()) {
|
if (auto clipLoader = weakClipLoader.toStrongRef()) {
|
||||||
|
|
|
@ -36,8 +36,6 @@ class RecordingScriptingInterface : public QObject, public Dependency {
|
||||||
public:
|
public:
|
||||||
RecordingScriptingInterface();
|
RecordingScriptingInterface();
|
||||||
|
|
||||||
void setScriptEngine(QSharedPointer<BaseScriptEngine> scriptEngine) { _scriptEngine = scriptEngine; }
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
@ -246,7 +244,6 @@ protected:
|
||||||
Flag _useSkeletonModel { false };
|
Flag _useSkeletonModel { false };
|
||||||
recording::ClipPointer _lastClip;
|
recording::ClipPointer _lastClip;
|
||||||
|
|
||||||
QSharedPointer<BaseScriptEngine> _scriptEngine;
|
|
||||||
QSet<recording::NetworkClipLoaderPointer> _clipLoaders;
|
QSet<recording::NetworkClipLoaderPointer> _clipLoaders;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -23,10 +23,10 @@ const float DEFAULT_AVATAR_EYE_HEIGHT = DEFAULT_AVATAR_HEIGHT - DEFAULT_AVATAR_E
|
||||||
const float DEFAULT_AVATAR_SUPPORT_BASE_LEFT = -0.25f;
|
const float DEFAULT_AVATAR_SUPPORT_BASE_LEFT = -0.25f;
|
||||||
const float DEFAULT_AVATAR_SUPPORT_BASE_RIGHT = 0.25f;
|
const float DEFAULT_AVATAR_SUPPORT_BASE_RIGHT = 0.25f;
|
||||||
const float DEFAULT_AVATAR_SUPPORT_BASE_FRONT = -0.20f;
|
const float DEFAULT_AVATAR_SUPPORT_BASE_FRONT = -0.20f;
|
||||||
const float DEFAULT_AVATAR_SUPPORT_BASE_BACK = 0.10f;
|
const float DEFAULT_AVATAR_SUPPORT_BASE_BACK = 0.12f;
|
||||||
const float DEFAULT_AVATAR_LATERAL_STEPPING_THRESHOLD = 0.10f;
|
const float DEFAULT_AVATAR_LATERAL_STEPPING_THRESHOLD = 0.10f;
|
||||||
const float DEFAULT_AVATAR_ANTERIOR_STEPPING_THRESHOLD = 0.04f;
|
const float DEFAULT_AVATAR_ANTERIOR_STEPPING_THRESHOLD = 0.04f;
|
||||||
const float DEFAULT_AVATAR_POSTERIOR_STEPPING_THRESHOLD = 0.07f;
|
const float DEFAULT_AVATAR_POSTERIOR_STEPPING_THRESHOLD = 0.05f;
|
||||||
const float DEFAULT_AVATAR_HEAD_ANGULAR_VELOCITY_STEPPING_THRESHOLD = 0.3f;
|
const float DEFAULT_AVATAR_HEAD_ANGULAR_VELOCITY_STEPPING_THRESHOLD = 0.3f;
|
||||||
const float DEFAULT_AVATAR_MODE_HEIGHT_STEPPING_THRESHOLD = -0.02f;
|
const float DEFAULT_AVATAR_MODE_HEIGHT_STEPPING_THRESHOLD = -0.02f;
|
||||||
const float DEFAULT_HANDS_VELOCITY_DIRECTION_STEPPING_THRESHOLD = 0.4f;
|
const float DEFAULT_HANDS_VELOCITY_DIRECTION_STEPPING_THRESHOLD = 0.4f;
|
||||||
|
|
|
@ -325,7 +325,7 @@ Script.include("/~/system/libraries/controllers.js");
|
||||||
} else if (target === TARGET.SURFACE) {
|
} else if (target === TARGET.SURFACE) {
|
||||||
var offset = getAvatarFootOffset();
|
var offset = getAvatarFootOffset();
|
||||||
result.intersection.y += offset;
|
result.intersection.y += offset;
|
||||||
MyAvatar.goToLocation(result.intersection, false, {x: 0, y: 0, z: 0, w: 1}, false);
|
MyAvatar.goToLocation(result.intersection, true, HMD.orientation, false);
|
||||||
HMD.centerUI();
|
HMD.centerUI();
|
||||||
MyAvatar.centerBody();
|
MyAvatar.centerBody();
|
||||||
}
|
}
|
||||||
|
|
|
@ -392,6 +392,8 @@ var toolBar = (function () {
|
||||||
entityListTool.sendUpdate();
|
entityListTool.sendUpdate();
|
||||||
selectionManager.setSelections([entityID]);
|
selectionManager.setSelections([entityID]);
|
||||||
|
|
||||||
|
Window.setFocus();
|
||||||
|
|
||||||
return entityID;
|
return entityID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -361,6 +361,55 @@
|
||||||
type: "Row"
|
type: "Row"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
Spin: [
|
||||||
|
{
|
||||||
|
id: "particleSpin",
|
||||||
|
name: "Particle Spin",
|
||||||
|
type: "SliderRadian",
|
||||||
|
min: -360.0,
|
||||||
|
max: 360.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "Row"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "spinSpread",
|
||||||
|
name: "Spin Spread",
|
||||||
|
type: "SliderRadian",
|
||||||
|
max: 360.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "Row"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "spinStart",
|
||||||
|
name: "Spin Start",
|
||||||
|
type: "SliderRadian",
|
||||||
|
min: -360.0,
|
||||||
|
max: 360.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "Row"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "spinFinish",
|
||||||
|
name: "Spin Finish",
|
||||||
|
type: "SliderRadian",
|
||||||
|
min: -360.0,
|
||||||
|
max: 360.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "Row"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "rotateWithEntity",
|
||||||
|
name: "Rotate with Entity",
|
||||||
|
type: "Boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "Row"
|
||||||
|
}
|
||||||
|
],
|
||||||
Polar: [
|
Polar: [
|
||||||
{
|
{
|
||||||
id: "polarStart",
|
id: "polarStart",
|
||||||
|
|
|
@ -75,6 +75,12 @@ ParticleExplorerTool = function(createToolsWindow) {
|
||||||
if (isNaN(properties.colorFinish.red)) {
|
if (isNaN(properties.colorFinish.red)) {
|
||||||
properties.colorFinish = properties.color;
|
properties.colorFinish = properties.color;
|
||||||
}
|
}
|
||||||
|
if (isNaN(properties.spinStart)) {
|
||||||
|
properties.spinStart = properties.particleSpin;
|
||||||
|
}
|
||||||
|
if (isNaN(properties.spinFinish)) {
|
||||||
|
properties.spinFinish = properties.particleSpin;
|
||||||
|
}
|
||||||
sendParticleProperties(properties);
|
sendParticleProperties(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,8 +94,8 @@ ParticleExplorerTool = function(createToolsWindow) {
|
||||||
if (data.messageType === "settings_update") {
|
if (data.messageType === "settings_update") {
|
||||||
var updatedSettings = data.updatedSettings;
|
var updatedSettings = data.updatedSettings;
|
||||||
|
|
||||||
var optionalProps = ["alphaStart", "alphaFinish", "radiusStart", "radiusFinish", "colorStart", "colorFinish"];
|
var optionalProps = ["alphaStart", "alphaFinish", "radiusStart", "radiusFinish", "colorStart", "colorFinish", "spinStart", "spinFinish"];
|
||||||
var fallbackProps = ["alpha", "particleRadius", "color"];
|
var fallbackProps = ["alpha", "particleRadius", "color", "particleSpin"];
|
||||||
for (var i = 0; i < optionalProps.length; i++) {
|
for (var i = 0; i < optionalProps.length; i++) {
|
||||||
var fallbackProp = fallbackProps[Math.floor(i / 2)];
|
var fallbackProp = fallbackProps[Math.floor(i / 2)];
|
||||||
var optionalValue = updatedSettings[optionalProps[i]];
|
var optionalValue = updatedSettings[optionalProps[i]];
|
||||||
|
|
|
@ -1,277 +0,0 @@
|
||||||
//
|
|
||||||
// CollisionRenderMeshCacheTests.cpp
|
|
||||||
// tests/physics/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows on 2014.10.30
|
|
||||||
// Copyright 2014 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 "CollisionRenderMeshCacheTests.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
#include <btBulletDynamicsCommon.h>
|
|
||||||
#include <BulletCollision/CollisionShapes/btShapeHull.h>
|
|
||||||
|
|
||||||
#include <CollisionRenderMeshCache.h>
|
|
||||||
#include <ShapeInfo.h> // for MAX_HULL_POINTS
|
|
||||||
|
|
||||||
#include "MeshUtil.h"
|
|
||||||
|
|
||||||
|
|
||||||
QTEST_MAIN(CollisionRenderMeshCacheTests)
|
|
||||||
|
|
||||||
const float INV_SQRT_THREE = 0.577350269f;
|
|
||||||
|
|
||||||
const uint32_t numSphereDirections = 6 + 8;
|
|
||||||
btVector3 sphereDirections[] = {
|
|
||||||
btVector3(1.0f, 0.0f, 0.0f),
|
|
||||||
btVector3(-1.0f, 0.0f, 0.0f),
|
|
||||||
btVector3(0.0f, 1.0f, 0.0f),
|
|
||||||
btVector3(0.0f, -1.0f, 0.0f),
|
|
||||||
btVector3(0.0f, 0.0f, 1.0f),
|
|
||||||
btVector3(0.0f, 0.0f, -1.0f),
|
|
||||||
btVector3(INV_SQRT_THREE, INV_SQRT_THREE, INV_SQRT_THREE),
|
|
||||||
btVector3(INV_SQRT_THREE, INV_SQRT_THREE, -INV_SQRT_THREE),
|
|
||||||
btVector3(INV_SQRT_THREE, -INV_SQRT_THREE, INV_SQRT_THREE),
|
|
||||||
btVector3(INV_SQRT_THREE, -INV_SQRT_THREE, -INV_SQRT_THREE),
|
|
||||||
btVector3(-INV_SQRT_THREE, INV_SQRT_THREE, INV_SQRT_THREE),
|
|
||||||
btVector3(-INV_SQRT_THREE, INV_SQRT_THREE, -INV_SQRT_THREE),
|
|
||||||
btVector3(-INV_SQRT_THREE, -INV_SQRT_THREE, INV_SQRT_THREE),
|
|
||||||
btVector3(-INV_SQRT_THREE, -INV_SQRT_THREE, -INV_SQRT_THREE)
|
|
||||||
};
|
|
||||||
|
|
||||||
float randomFloat() {
|
|
||||||
return 2.0f * ((float)rand() / (float)RAND_MAX) - 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
btBoxShape* createBoxShape(const btVector3& extent) {
|
|
||||||
btBoxShape* shape = new btBoxShape(0.5f * extent);
|
|
||||||
return shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
btConvexHullShape* createConvexHull(float radius) {
|
|
||||||
btConvexHullShape* hull = new btConvexHullShape();
|
|
||||||
for (uint32_t i = 0; i < numSphereDirections; ++i) {
|
|
||||||
btVector3 point = radius * sphereDirections[i];
|
|
||||||
hull->addPoint(point, false);
|
|
||||||
}
|
|
||||||
hull->recalcLocalAabb();
|
|
||||||
return hull;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CollisionRenderMeshCacheTests::testShapeHullManifold() {
|
|
||||||
// make a box shape
|
|
||||||
btVector3 extent(1.0f, 2.0f, 3.0f);
|
|
||||||
btBoxShape* box = createBoxShape(extent);
|
|
||||||
|
|
||||||
// wrap it with a ShapeHull
|
|
||||||
btShapeHull hull(box);
|
|
||||||
const float MARGIN = 0.0f;
|
|
||||||
hull.buildHull(MARGIN);
|
|
||||||
|
|
||||||
// verify the vertex count is capped
|
|
||||||
uint32_t numVertices = (uint32_t)hull.numVertices();
|
|
||||||
QVERIFY(numVertices <= MAX_HULL_POINTS);
|
|
||||||
|
|
||||||
// verify the mesh is inside the radius
|
|
||||||
btVector3 halfExtents = box->getHalfExtentsWithMargin();
|
|
||||||
float ACCEPTABLE_EXTENTS_ERROR = 0.01f;
|
|
||||||
float maxRadius = halfExtents.length() + ACCEPTABLE_EXTENTS_ERROR;
|
|
||||||
const btVector3* meshVertices = hull.getVertexPointer();
|
|
||||||
for (uint32_t i = 0; i < numVertices; ++i) {
|
|
||||||
btVector3 vertex = meshVertices[i];
|
|
||||||
QVERIFY(vertex.length() <= maxRadius);
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify the index count is capped
|
|
||||||
uint32_t numIndices = (uint32_t)hull.numIndices();
|
|
||||||
QVERIFY(numIndices < 6 * MAX_HULL_POINTS);
|
|
||||||
|
|
||||||
// verify the index count is a multiple of 3
|
|
||||||
QVERIFY(numIndices % 3 == 0);
|
|
||||||
|
|
||||||
// verify the mesh is closed
|
|
||||||
const uint32_t* meshIndices = hull.getIndexPointer();
|
|
||||||
bool isClosed = MeshUtil::isClosedManifold(meshIndices, numIndices);
|
|
||||||
QVERIFY(isClosed);
|
|
||||||
|
|
||||||
// verify the triangle normals are outward using right-hand-rule
|
|
||||||
const uint32_t INDICES_PER_TRIANGLE = 3;
|
|
||||||
for (uint32_t i = 0; i < numIndices; i += INDICES_PER_TRIANGLE) {
|
|
||||||
btVector3 A = meshVertices[meshIndices[i]];
|
|
||||||
btVector3 B = meshVertices[meshIndices[i+1]];
|
|
||||||
btVector3 C = meshVertices[meshIndices[i+2]];
|
|
||||||
|
|
||||||
btVector3 face = (B - A).cross(C - B);
|
|
||||||
btVector3 center = (A + B + C) / 3.0f;
|
|
||||||
QVERIFY(face.dot(center) > 0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete unmanaged memory
|
|
||||||
delete box;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CollisionRenderMeshCacheTests::testCompoundShape() {
|
|
||||||
uint32_t numSubShapes = 3;
|
|
||||||
|
|
||||||
btVector3 centers[] = {
|
|
||||||
btVector3(1.0f, 0.0f, 0.0f),
|
|
||||||
btVector3(0.0f, -2.0f, 0.0f),
|
|
||||||
btVector3(0.0f, 0.0f, 3.0f),
|
|
||||||
};
|
|
||||||
|
|
||||||
float radii[] = { 3.0f, 2.0f, 1.0f };
|
|
||||||
|
|
||||||
btCompoundShape* compoundShape = new btCompoundShape();
|
|
||||||
for (uint32_t i = 0; i < numSubShapes; ++i) {
|
|
||||||
btTransform transform;
|
|
||||||
transform.setOrigin(centers[i]);
|
|
||||||
btConvexHullShape* hull = createConvexHull(radii[i]);
|
|
||||||
compoundShape->addChildShape(transform, hull);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the cache
|
|
||||||
CollisionRenderMeshCache cache;
|
|
||||||
QVERIFY(cache.getNumMeshes() == 0);
|
|
||||||
|
|
||||||
// get the mesh once
|
|
||||||
graphics::MeshPointer mesh = cache.getMesh(compoundShape);
|
|
||||||
QVERIFY((bool)mesh);
|
|
||||||
QVERIFY(cache.getNumMeshes() == 1);
|
|
||||||
|
|
||||||
// get the mesh again
|
|
||||||
graphics::MeshPointer mesh2 = cache.getMesh(compoundShape);
|
|
||||||
QVERIFY(mesh2 == mesh);
|
|
||||||
QVERIFY(cache.getNumMeshes() == 1);
|
|
||||||
|
|
||||||
// forget the mesh once
|
|
||||||
cache.releaseMesh(compoundShape);
|
|
||||||
mesh.reset();
|
|
||||||
QVERIFY(cache.getNumMeshes() == 1);
|
|
||||||
|
|
||||||
// collect garbage (should still cache mesh)
|
|
||||||
cache.collectGarbage();
|
|
||||||
QVERIFY(cache.getNumMeshes() == 1);
|
|
||||||
|
|
||||||
// forget the mesh a second time (should still cache mesh)
|
|
||||||
cache.releaseMesh(compoundShape);
|
|
||||||
mesh2.reset();
|
|
||||||
QVERIFY(cache.getNumMeshes() == 1);
|
|
||||||
|
|
||||||
// collect garbage (should no longer cache mesh)
|
|
||||||
cache.collectGarbage();
|
|
||||||
QVERIFY(cache.getNumMeshes() == 0);
|
|
||||||
|
|
||||||
// delete unmanaged memory
|
|
||||||
for (int i = 0; i < compoundShape->getNumChildShapes(); ++i) {
|
|
||||||
delete compoundShape->getChildShape(i);
|
|
||||||
}
|
|
||||||
delete compoundShape;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CollisionRenderMeshCacheTests::testMultipleShapes() {
|
|
||||||
// shapeA is compound of hulls
|
|
||||||
uint32_t numSubShapes = 3;
|
|
||||||
btVector3 centers[] = {
|
|
||||||
btVector3(1.0f, 0.0f, 0.0f),
|
|
||||||
btVector3(0.0f, -2.0f, 0.0f),
|
|
||||||
btVector3(0.0f, 0.0f, 3.0f),
|
|
||||||
};
|
|
||||||
float radii[] = { 3.0f, 2.0f, 1.0f };
|
|
||||||
btCompoundShape* shapeA = new btCompoundShape();
|
|
||||||
for (uint32_t i = 0; i < numSubShapes; ++i) {
|
|
||||||
btTransform transform;
|
|
||||||
transform.setOrigin(centers[i]);
|
|
||||||
btConvexHullShape* hull = createConvexHull(radii[i]);
|
|
||||||
shapeA->addChildShape(transform, hull);
|
|
||||||
}
|
|
||||||
|
|
||||||
// shapeB is compound of boxes
|
|
||||||
btVector3 extents[] = {
|
|
||||||
btVector3(1.0f, 2.0f, 3.0f),
|
|
||||||
btVector3(2.0f, 3.0f, 1.0f),
|
|
||||||
btVector3(3.0f, 1.0f, 2.0f),
|
|
||||||
};
|
|
||||||
btCompoundShape* shapeB = new btCompoundShape();
|
|
||||||
for (uint32_t i = 0; i < numSubShapes; ++i) {
|
|
||||||
btTransform transform;
|
|
||||||
transform.setOrigin(centers[i]);
|
|
||||||
btBoxShape* box = createBoxShape(extents[i]);
|
|
||||||
shapeB->addChildShape(transform, box);
|
|
||||||
}
|
|
||||||
|
|
||||||
// shapeC is just a box
|
|
||||||
btVector3 extentC(7.0f, 3.0f, 5.0f);
|
|
||||||
btBoxShape* shapeC = createBoxShape(extentC);
|
|
||||||
|
|
||||||
// create the cache
|
|
||||||
CollisionRenderMeshCache cache;
|
|
||||||
QVERIFY(cache.getNumMeshes() == 0);
|
|
||||||
|
|
||||||
// get the meshes
|
|
||||||
graphics::MeshPointer meshA = cache.getMesh(shapeA);
|
|
||||||
graphics::MeshPointer meshB = cache.getMesh(shapeB);
|
|
||||||
graphics::MeshPointer meshC = cache.getMesh(shapeC);
|
|
||||||
QVERIFY((bool)meshA);
|
|
||||||
QVERIFY((bool)meshB);
|
|
||||||
QVERIFY((bool)meshC);
|
|
||||||
QVERIFY(cache.getNumMeshes() == 3);
|
|
||||||
|
|
||||||
// get the meshes again
|
|
||||||
graphics::MeshPointer meshA2 = cache.getMesh(shapeA);
|
|
||||||
graphics::MeshPointer meshB2 = cache.getMesh(shapeB);
|
|
||||||
graphics::MeshPointer meshC2 = cache.getMesh(shapeC);
|
|
||||||
QVERIFY(meshA == meshA2);
|
|
||||||
QVERIFY(meshB == meshB2);
|
|
||||||
QVERIFY(meshC == meshC2);
|
|
||||||
QVERIFY(cache.getNumMeshes() == 3);
|
|
||||||
|
|
||||||
// forget the meshes once
|
|
||||||
cache.releaseMesh(shapeA);
|
|
||||||
cache.releaseMesh(shapeB);
|
|
||||||
cache.releaseMesh(shapeC);
|
|
||||||
meshA2.reset();
|
|
||||||
meshB2.reset();
|
|
||||||
meshC2.reset();
|
|
||||||
QVERIFY(cache.getNumMeshes() == 3);
|
|
||||||
|
|
||||||
// collect garbage (should still cache mesh)
|
|
||||||
cache.collectGarbage();
|
|
||||||
QVERIFY(cache.getNumMeshes() == 3);
|
|
||||||
|
|
||||||
// forget again, one mesh at a time...
|
|
||||||
// shapeA...
|
|
||||||
cache.releaseMesh(shapeA);
|
|
||||||
meshA.reset();
|
|
||||||
QVERIFY(cache.getNumMeshes() == 3);
|
|
||||||
cache.collectGarbage();
|
|
||||||
QVERIFY(cache.getNumMeshes() == 2);
|
|
||||||
// shapeB...
|
|
||||||
cache.releaseMesh(shapeB);
|
|
||||||
meshB.reset();
|
|
||||||
QVERIFY(cache.getNumMeshes() == 2);
|
|
||||||
cache.collectGarbage();
|
|
||||||
QVERIFY(cache.getNumMeshes() == 1);
|
|
||||||
// shapeC...
|
|
||||||
cache.releaseMesh(shapeC);
|
|
||||||
meshC.reset();
|
|
||||||
QVERIFY(cache.getNumMeshes() == 1);
|
|
||||||
cache.collectGarbage();
|
|
||||||
QVERIFY(cache.getNumMeshes() == 0);
|
|
||||||
|
|
||||||
// delete unmanaged memory
|
|
||||||
for (int i = 0; i < shapeA->getNumChildShapes(); ++i) {
|
|
||||||
delete shapeA->getChildShape(i);
|
|
||||||
}
|
|
||||||
delete shapeA;
|
|
||||||
for (int i = 0; i < shapeB->getNumChildShapes(); ++i) {
|
|
||||||
delete shapeB->getChildShape(i);
|
|
||||||
}
|
|
||||||
delete shapeB;
|
|
||||||
delete shapeC;
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
//
|
|
||||||
// CollisionRenderMeshCacheTests.h
|
|
||||||
// tests/physics/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows on 2014.10.30
|
|
||||||
// Copyright 2014 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_CollisionRenderMeshCacheTests_h
|
|
||||||
#define hifi_CollisionRenderMeshCacheTests_h
|
|
||||||
|
|
||||||
#include <QtTest/QtTest>
|
|
||||||
|
|
||||||
class CollisionRenderMeshCacheTests : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void testShapeHullManifold();
|
|
||||||
void testCompoundShape();
|
|
||||||
void testMultipleShapes();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_CollisionRenderMeshCacheTests_h
|
|
Loading…
Reference in a new issue