mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 09:44:21 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi
This commit is contained in:
commit
909c4ad919
55 changed files with 551 additions and 374 deletions
|
@ -149,7 +149,8 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
// about a given otherNode to this node
|
||||
// FIXME does this mean we should sort the othernodes by distance before iterating
|
||||
// over them?
|
||||
float outputBandwidth = node->getOutboundBandwidth();
|
||||
// float outputBandwidth =
|
||||
node->getOutboundBandwidth();
|
||||
|
||||
// this is an AGENT we have received head data from
|
||||
// send back a packet with other active node data to this node
|
||||
|
@ -169,7 +170,7 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
return true;
|
||||
},
|
||||
[&](const SharedNodePointer& otherNode) {
|
||||
AvatarMixerClientData* otherNodeData = otherNodeData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData());
|
||||
AvatarMixerClientData* otherNodeData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData());
|
||||
MutexTryLocker lock(otherNodeData->getMutex());
|
||||
if (!lock.isLocked()) {
|
||||
return;
|
||||
|
|
|
@ -718,13 +718,8 @@ function mouseClickEvent(event) {
|
|||
var result = findClickedEntity(event);
|
||||
if (result) {
|
||||
var properties = Entities.getEntityProperties(result.entityID);
|
||||
var data = {};
|
||||
try {
|
||||
data = JSON.parse(properties.attribution);
|
||||
} catch (e) {
|
||||
}
|
||||
if (data.marketplaceID) {
|
||||
propertyMenu.marketplaceID = data.marketplaceID;
|
||||
if (properties.marketplaceID) {
|
||||
propertyMenu.marketplaceID = properties.marketplaceID;
|
||||
propertyMenu.updateMenuItemText(showMenuItem, "Show in Marketplace");
|
||||
} else {
|
||||
propertyMenu.marketplaceID = null;
|
||||
|
|
|
@ -140,7 +140,6 @@
|
|||
var elLifetime = document.getElementById("property-lifetime");
|
||||
var elScriptURL = document.getElementById("property-script-url");
|
||||
var elUserData = document.getElementById("property-user-data");
|
||||
var elAttribution = document.getElementById("property-attribution");
|
||||
|
||||
var elBoxSections = document.querySelectorAll(".box-section");
|
||||
var elBoxColorRed = document.getElementById("property-box-red");
|
||||
|
@ -264,7 +263,6 @@
|
|||
elLifetime.value = properties.lifetime;
|
||||
elScriptURL.value = properties.script;
|
||||
elUserData.value = properties.userData;
|
||||
elAttribution.value = properties.attribution;
|
||||
|
||||
if (properties.type != "Box") {
|
||||
for (var i = 0; i < elBoxSections.length; i++) {
|
||||
|
@ -395,7 +393,6 @@
|
|||
elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime'));
|
||||
elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script'));
|
||||
elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData'));
|
||||
elAttribution.addEventListener('change', createEmitTextPropertyUpdateFunction('attribution'));
|
||||
|
||||
var boxColorChangeFunction = createEmitColorPropertyUpdateFunction(
|
||||
'color', elBoxColorRed, elBoxColorGreen, elBoxColorBlue);
|
||||
|
@ -630,13 +627,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<div class="label">Attribution</div>
|
||||
<div class="value">
|
||||
<textarea id="property-attribution"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="box-section property">
|
||||
<div class="label">Color</div>
|
||||
|
|
|
@ -92,11 +92,13 @@ var NotificationType = {
|
|||
SNAPSHOT: 2,
|
||||
WINDOW_RESIZE: 3,
|
||||
LOD_WARNING: 4,
|
||||
CONNECTION_REFUSED: 5,
|
||||
properties: [
|
||||
{ text: "Mute Toggle" },
|
||||
{ text: "Snapshot" },
|
||||
{ text: "Window Resize" },
|
||||
{ text: "Level of Detail" }
|
||||
{ text: "Level of Detail" },
|
||||
{ text: "Connection Refused" }
|
||||
],
|
||||
getTypeFromMenuItem: function(menuItemName) {
|
||||
if (menuItemName.substr(menuItemName.length - NOTIFICATION_MENU_ITEM_POST.length) !== NOTIFICATION_MENU_ITEM_POST) {
|
||||
|
@ -501,6 +503,10 @@ function onMuteStateChanged() {
|
|||
createNotification(muteString, NotificationType.MUTE_TOGGLE);
|
||||
}
|
||||
|
||||
function onDomainConnectionRefused(reason) {
|
||||
createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED );
|
||||
}
|
||||
|
||||
// handles mouse clicks on buttons
|
||||
function mousePressEvent(event) {
|
||||
var pickRay,
|
||||
|
@ -608,5 +614,6 @@ Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
|||
Script.update.connect(update);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
Menu.menuItemEvent.connect(menuItemEvent);
|
||||
Window.domainConnectionRefused.connect(onDomainConnectionRefused);
|
||||
|
||||
setup();
|
||||
|
|
|
@ -138,7 +138,7 @@
|
|||
<h3>Import models</h3>
|
||||
<img class="grid-img" src="img/models.png" alt"Import models"></img>
|
||||
<p>
|
||||
Use the editEntitles.js script to<br>
|
||||
Use the <strong>edit.js</strong> script to<br>
|
||||
add FBX models in-world. You<br>
|
||||
can use grids and fine tune<br>
|
||||
placement-related parameters<br>
|
||||
|
|
|
@ -297,7 +297,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
_lastSendDownstreamAudioStats(usecTimestampNow()),
|
||||
_isVSyncOn(true),
|
||||
_aboutToQuit(false),
|
||||
_notifiedPacketVersionMismatchThisDomain(false)
|
||||
_notifiedPacketVersionMismatchThisDomain(false),
|
||||
_domainConnectionRefusals(QList<QString>())
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
installNativeEventFilter(&MyNativeEventFilter::getInstance());
|
||||
|
@ -344,6 +345,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
// put the NodeList and datagram processing on the node thread
|
||||
nodeList->moveToThread(nodeThread);
|
||||
|
||||
// geometry background downloads need to happen on the Datagram Processor Thread. The idle loop will
|
||||
// emit checkBackgroundDownloads to cause the GeometryCache to check it's queue for requested background
|
||||
// downloads.
|
||||
QSharedPointer<GeometryCache> geometryCacheP = DependencyManager::get<GeometryCache>();
|
||||
ResourceCache *geometryCache = geometryCacheP.data();
|
||||
connect(this, &Application::checkBackgroundDownloads, geometryCache, &ResourceCache::checkAsynchronousGets);
|
||||
|
||||
// connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal
|
||||
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _datagramProcessor, &DatagramProcessor::processDatagrams);
|
||||
|
||||
|
@ -987,12 +995,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
resetSensors();
|
||||
break;
|
||||
|
||||
case Qt::Key_G:
|
||||
if (isShifted) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::ObeyEnvironmentalGravity);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_A:
|
||||
if (isShifted) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::Atmosphere);
|
||||
|
@ -1165,10 +1167,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_Comma: {
|
||||
_myAvatar->togglePhysicsEnabled();
|
||||
}
|
||||
|
||||
default:
|
||||
event->ignore();
|
||||
break;
|
||||
|
@ -1566,6 +1564,9 @@ void Application::idle() {
|
|||
idleTimer->start(2);
|
||||
}
|
||||
}
|
||||
|
||||
// check for any requested background downloads.
|
||||
emit checkBackgroundDownloads();
|
||||
}
|
||||
|
||||
void Application::setFullscreen(bool fullscreen) {
|
||||
|
@ -2192,7 +2193,6 @@ void Application::update(float deltaTime) {
|
|||
|
||||
{
|
||||
PerformanceTimer perfTimer("physics");
|
||||
_myAvatar->preSimulation();
|
||||
_physicsEngine.stepSimulation();
|
||||
}
|
||||
|
||||
|
@ -3136,7 +3136,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
|
|||
int viewport[4];
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
|
||||
bool eyeRelativeCamera = false;
|
||||
// bool eyeRelativeCamera = false;
|
||||
if (billboard) {
|
||||
_mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW); // degees
|
||||
_mirrorCamera.setPosition(_myAvatar->getPosition() +
|
||||
|
@ -3289,6 +3289,14 @@ void Application::clearDomainOctreeDetails() {
|
|||
void Application::domainChanged(const QString& domainHostname) {
|
||||
updateWindowTitle();
|
||||
clearDomainOctreeDetails();
|
||||
_domainConnectionRefusals.clear();
|
||||
}
|
||||
|
||||
void Application::domainConnectionDenied(const QString& reason) {
|
||||
if (!_domainConnectionRefusals.contains(reason)) {
|
||||
_domainConnectionRefusals.append(reason);
|
||||
emit domainConnectionRefused(reason);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::connectedToDomain(const QString& hostname) {
|
||||
|
|
|
@ -333,6 +333,9 @@ signals:
|
|||
|
||||
void svoImportRequested(const QString& url);
|
||||
|
||||
void checkBackgroundDownloads();
|
||||
void domainConnectionRefused(const QString& reason);
|
||||
|
||||
public slots:
|
||||
void domainChanged(const QString& domainHostname);
|
||||
void updateWindowTitle();
|
||||
|
@ -383,6 +386,8 @@ public slots:
|
|||
|
||||
void setActiveFaceTracker();
|
||||
|
||||
void domainConnectionDenied(const QString& reason);
|
||||
|
||||
private slots:
|
||||
void clearDomainOctreeDetails();
|
||||
void checkFPS();
|
||||
|
@ -607,6 +612,8 @@ private:
|
|||
int _menuBarHeight;
|
||||
|
||||
QHash<QString, AcceptURLMethod> _acceptedExtensions;
|
||||
|
||||
QList<QString> _domainConnectionRefusals;
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -127,6 +127,7 @@ void DatagramProcessor::processDatagrams() {
|
|||
// and check and signal for an access token so that we can make sure they are logged in
|
||||
qDebug() << "The domain-server denied a connection request: " << reason;
|
||||
qDebug() << "You may need to re-log to generate a keypair so you can provide a username signature.";
|
||||
application->domainConnectionDenied(reason);
|
||||
AccountManager::getInstance().checkAndSignalForAccessToken();
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -201,9 +201,7 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::GlowWhenSpeaking, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::BlueSpeechSphere, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, false,
|
||||
avatar, SLOT(updateMotionBehavior()));
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::StandOnNearbyFloors, 0, true,
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::EnableCharacterController, 0, true,
|
||||
avatar, SLOT(updateMotionBehavior()));
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ShiftHipsForIdleAnimations, 0, false,
|
||||
avatar, SLOT(updateMotionBehavior()));
|
||||
|
|
|
@ -153,6 +153,7 @@ namespace MenuOption {
|
|||
const QString EchoServerAudio = "Echo Server Audio";
|
||||
const QString EditEntitiesHelp = "Edit Entities Help...";
|
||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||
const QString EnableCharacterController = "Enable avatar collisions";
|
||||
const QString EnableGlowEffect = "Enable Glow Effect (Warning: Poor Oculus Performance)";
|
||||
const QString EnableVRMode = "Enable VR Mode";
|
||||
const QString Entities = "Entities";
|
||||
|
@ -185,7 +186,6 @@ namespace MenuOption {
|
|||
const QString MuteAudio = "Mute Microphone";
|
||||
const QString MuteEnvironment = "Mute Environment";
|
||||
const QString NoFaceTracking = "None";
|
||||
const QString ObeyEnvironmentalGravity = "Obey Environmental Gravity";
|
||||
const QString OctreeStats = "Entity Statistics";
|
||||
const QString OffAxisProjection = "Off-Axis Projection";
|
||||
const QString OnlyDisplayTopTen = "Only Display Top Ten";
|
||||
|
@ -236,7 +236,6 @@ namespace MenuOption {
|
|||
const QString SixenseEnabled = "Enable Hydra Support";
|
||||
const QString SixenseMouseInput = "Enable Sixense Mouse Input";
|
||||
const QString SixenseLasers = "Enable Sixense UI Lasers";
|
||||
const QString StandOnNearbyFloors = "Stand on nearby floors";
|
||||
const QString ShiftHipsForIdleAnimations = "Shift hips for idle animations";
|
||||
const QString Stars = "Stars";
|
||||
const QString Stats = "Stats";
|
||||
|
|
|
@ -81,7 +81,6 @@ MyAvatar::MyAvatar() :
|
|||
_scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE),
|
||||
_scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME),
|
||||
_motionBehaviors(AVATAR_MOTION_DEFAULTS),
|
||||
_enablePhysics(false),
|
||||
_characterController(this),
|
||||
_lookAtTargetAvatar(),
|
||||
_shouldRender(true),
|
||||
|
@ -101,6 +100,7 @@ MyAvatar::MyAvatar() :
|
|||
// connect to AddressManager signal for location jumps
|
||||
connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired,
|
||||
this, &MyAvatar::goToLocation);
|
||||
_characterController.setEnabled(true);
|
||||
}
|
||||
|
||||
MyAvatar::~MyAvatar() {
|
||||
|
@ -147,10 +147,6 @@ void MyAvatar::update(float deltaTime) {
|
|||
head->setAudioLoudness(audio->getLastInputLoudness());
|
||||
head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
|
||||
|
||||
if (_motionBehaviors & AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY) {
|
||||
setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()));
|
||||
}
|
||||
|
||||
simulate(deltaTime);
|
||||
if (_feetTouchFloor) {
|
||||
_skeletonModel.updateStandingFoot();
|
||||
|
@ -174,11 +170,7 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
{
|
||||
PerformanceTimer perfTimer("transform");
|
||||
updateOrientation(deltaTime);
|
||||
if (isPhysicsEnabled()) {
|
||||
updatePositionWithPhysics(deltaTime);
|
||||
} else {
|
||||
updatePosition(deltaTime);
|
||||
}
|
||||
updatePosition(deltaTime);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -484,26 +476,6 @@ void MyAvatar::loadLastRecording() {
|
|||
_player->loadRecording(_recorder->getRecording());
|
||||
}
|
||||
|
||||
void MyAvatar::setLocalGravity(glm::vec3 gravity) {
|
||||
_motionBehaviors |= AVATAR_MOTION_OBEY_LOCAL_GRAVITY;
|
||||
// Environmental and Local gravities are incompatible. Since Local is being set here
|
||||
// the environmental setting must be removed.
|
||||
_motionBehaviors &= ~AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY;
|
||||
setGravity(gravity);
|
||||
}
|
||||
|
||||
void MyAvatar::setGravity(const glm::vec3& gravity) {
|
||||
_gravity = gravity;
|
||||
|
||||
// use the gravity to determine the new world up direction, if possible
|
||||
float gravityLength = glm::length(gravity);
|
||||
if (gravityLength > EPSILON) {
|
||||
_worldUpDirection = _gravity / -gravityLength;
|
||||
}
|
||||
// NOTE: the else case here it to leave _worldUpDirection unchanged
|
||||
// so it continues to point opposite to the previous gravity setting.
|
||||
}
|
||||
|
||||
AnimationHandlePointer MyAvatar::addAnimationHandle() {
|
||||
AnimationHandlePointer handle = _skeletonModel.createAnimationHandle();
|
||||
_animationHandles.append(handle);
|
||||
|
@ -1258,128 +1230,38 @@ glm::vec3 MyAvatar::applyScriptedMotor(float deltaTime, const glm::vec3& localVe
|
|||
return localVelocity + motorEfficiency * deltaVelocity;
|
||||
}
|
||||
|
||||
const float NEARBY_FLOOR_THRESHOLD = 5.0f;
|
||||
|
||||
void MyAvatar::updatePosition(float deltaTime) {
|
||||
|
||||
// check for floor by casting a ray straight down from avatar's position
|
||||
float heightAboveFloor = FLT_MAX;
|
||||
bool hasFloor = false;
|
||||
const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape();
|
||||
const float maxFloorDistance = boundingShape.getBoundingRadius() * NEARBY_FLOOR_THRESHOLD;
|
||||
|
||||
RayIntersectionInfo intersection;
|
||||
// NOTE: avatar is center of PhysicsSimulation, so rayStart is the origin for the purposes of the raycast
|
||||
intersection._rayStart = glm::vec3(0.0f);
|
||||
intersection._rayDirection = - _worldUpDirection;
|
||||
intersection._rayLength = 4.0f * boundingShape.getBoundingRadius();
|
||||
|
||||
// velocity is initialized to the measured _velocity but will be modified by friction, external thrust, etc
|
||||
glm::vec3 velocity = _velocity;
|
||||
|
||||
bool pushingUp = (_driveKeys[UP] - _driveKeys[DOWN] > 0.0f) || _scriptedMotorVelocity.y > 0.0f;
|
||||
if (_motionBehaviors & AVATAR_MOTION_STAND_ON_NEARBY_FLOORS) {
|
||||
const float MAX_SPEED_UNDER_GRAVITY = 2.0f * _scale * MAX_WALKING_SPEED;
|
||||
if (pushingUp || glm::length2(velocity) > MAX_SPEED_UNDER_GRAVITY * MAX_SPEED_UNDER_GRAVITY) {
|
||||
// we're pushing up or moving quickly, so disable gravity
|
||||
setLocalGravity(glm::vec3(0.0f));
|
||||
hasFloor = false;
|
||||
} else {
|
||||
if (heightAboveFloor > maxFloorDistance) {
|
||||
// disable local gravity when floor is too far away
|
||||
setLocalGravity(glm::vec3(0.0f));
|
||||
hasFloor = false;
|
||||
} else {
|
||||
// enable gravity
|
||||
setLocalGravity(-_worldUpDirection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool zeroDownwardVelocity = false;
|
||||
bool gravityEnabled = (glm::length2(_gravity) > EPSILON);
|
||||
if (gravityEnabled) {
|
||||
const float SLOP = 0.002f;
|
||||
if (heightAboveFloor < SLOP) {
|
||||
if (heightAboveFloor < 0.0) {
|
||||
// Gravity is in effect so we assume that the avatar is colliding against the world and we need
|
||||
// to lift avatar out of floor, but we don't want to do it too fast (keep it smooth).
|
||||
float distanceToLift = glm::min(-heightAboveFloor, MAX_WALKING_SPEED * deltaTime);
|
||||
|
||||
// We don't use applyPositionDelta() for this lift distance because we don't want the avatar
|
||||
// to come flying out of the floor. Instead we update position directly, and set a boolean
|
||||
// that will remind us later to zero any downward component of the velocity.
|
||||
_position += distanceToLift * _worldUpDirection;
|
||||
}
|
||||
zeroDownwardVelocity = true;
|
||||
}
|
||||
if (!zeroDownwardVelocity) {
|
||||
velocity += (deltaTime * GRAVITY_EARTH) * _gravity;
|
||||
}
|
||||
}
|
||||
|
||||
// rotate velocity into camera frame
|
||||
glm::quat rotation = getHead()->getCameraOrientation();
|
||||
glm::vec3 localVelocity = glm::inverse(rotation) * velocity;
|
||||
|
||||
// apply motors in camera frame
|
||||
glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, hasFloor);
|
||||
newLocalVelocity = applyScriptedMotor(deltaTime, newLocalVelocity);
|
||||
|
||||
// rotate back into world-frame
|
||||
velocity = rotation * newLocalVelocity;
|
||||
|
||||
// apply thrust
|
||||
velocity += _thrust * deltaTime;
|
||||
_thrust = glm::vec3(0.0f);
|
||||
|
||||
// remove downward velocity so we don't push into floor
|
||||
if (zeroDownwardVelocity) {
|
||||
float verticalSpeed = glm::dot(velocity, _worldUpDirection);
|
||||
if (verticalSpeed < 0.0f || !pushingUp) {
|
||||
velocity -= verticalSpeed * _worldUpDirection;
|
||||
}
|
||||
}
|
||||
|
||||
// cap avatar speed
|
||||
float speed = glm::length(velocity);
|
||||
if (speed > MAX_AVATAR_SPEED) {
|
||||
velocity *= MAX_AVATAR_SPEED / speed;
|
||||
speed = MAX_AVATAR_SPEED;
|
||||
}
|
||||
|
||||
// update position
|
||||
if (speed > MIN_AVATAR_SPEED) {
|
||||
applyPositionDelta(deltaTime * velocity);
|
||||
}
|
||||
|
||||
// update _moving flag based on speed
|
||||
const float MOVING_SPEED_THRESHOLD = 0.01f;
|
||||
_moving = speed > MOVING_SPEED_THRESHOLD;
|
||||
|
||||
measureMotionDerivatives(deltaTime);
|
||||
}
|
||||
|
||||
void MyAvatar::updatePositionWithPhysics(float deltaTime) {
|
||||
// rotate velocity into camera frame
|
||||
glm::quat rotation = getHead()->getCameraOrientation();
|
||||
glm::vec3 localVelocity = glm::inverse(rotation) * _velocity;
|
||||
|
||||
bool hasFloor = false;
|
||||
glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, hasFloor);
|
||||
bool isOnGround = _characterController.onGround();
|
||||
glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, isOnGround);
|
||||
newLocalVelocity = applyScriptedMotor(deltaTime, newLocalVelocity);
|
||||
|
||||
// cap avatar speed
|
||||
float speed = glm::length(newLocalVelocity);
|
||||
if (speed > MAX_WALKING_SPEED) {
|
||||
newLocalVelocity *= MAX_WALKING_SPEED / speed;
|
||||
}
|
||||
|
||||
// rotate back into world-frame
|
||||
_velocity = rotation * newLocalVelocity;
|
||||
|
||||
_velocity += _thrust * deltaTime;
|
||||
_thrust = glm::vec3(0.0f);
|
||||
|
||||
// cap avatar speed
|
||||
float speed = glm::length(_velocity);
|
||||
if (speed > MAX_AVATAR_SPEED) {
|
||||
_velocity *= MAX_AVATAR_SPEED / speed;
|
||||
speed = MAX_AVATAR_SPEED;
|
||||
}
|
||||
|
||||
if (speed > MIN_AVATAR_SPEED && !_characterController.isEnabled()) {
|
||||
// update position ourselves
|
||||
applyPositionDelta(deltaTime * _velocity);
|
||||
measureMotionDerivatives(deltaTime);
|
||||
} // else physics will move avatar later
|
||||
|
||||
// update _moving flag based on speed
|
||||
const float MOVING_SPEED_THRESHOLD = 0.01f;
|
||||
_moving = speed > MOVING_SPEED_THRESHOLD;
|
||||
|
||||
}
|
||||
|
||||
void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, float frequency) {
|
||||
|
@ -1496,23 +1378,6 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition,
|
|||
|
||||
void MyAvatar::updateMotionBehavior() {
|
||||
Menu* menu = Menu::getInstance();
|
||||
if (menu->isOptionChecked(MenuOption::ObeyEnvironmentalGravity)) {
|
||||
_motionBehaviors |= AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY;
|
||||
// Environmental and Local gravities are incompatible. Environmental setting trumps local.
|
||||
_motionBehaviors &= ~AVATAR_MOTION_OBEY_LOCAL_GRAVITY;
|
||||
} else {
|
||||
_motionBehaviors &= ~AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY;
|
||||
}
|
||||
if (! (_motionBehaviors & (AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY | AVATAR_MOTION_OBEY_LOCAL_GRAVITY))) {
|
||||
setGravity(glm::vec3(0.0f));
|
||||
}
|
||||
if (menu->isOptionChecked(MenuOption::StandOnNearbyFloors)) {
|
||||
_motionBehaviors |= AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
|
||||
// standing on floors requires collision with voxels
|
||||
// TODO: determine what to do with this now that voxels are gone
|
||||
} else {
|
||||
_motionBehaviors &= ~AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
|
||||
}
|
||||
if (menu->isOptionChecked(MenuOption::KeyboardMotorControl)) {
|
||||
_motionBehaviors |= AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED;
|
||||
} else {
|
||||
|
@ -1523,6 +1388,7 @@ void MyAvatar::updateMotionBehavior() {
|
|||
} else {
|
||||
_motionBehaviors &= ~AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
|
||||
}
|
||||
_characterController.setEnabled(menu->isOptionChecked(MenuOption::EnableCharacterController));
|
||||
_feetTouchFloor = menu->isOptionChecked(MenuOption::ShiftHipsForIdleAnimations);
|
||||
}
|
||||
|
||||
|
@ -1581,10 +1447,6 @@ glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) {
|
|||
return palm->getPosition();
|
||||
}
|
||||
|
||||
void MyAvatar::preSimulation() {
|
||||
_characterController.setEnabled(_enablePhysics);
|
||||
}
|
||||
|
||||
void MyAvatar::clearDriveKeys() {
|
||||
for (int i = 0; i < MAX_DRIVE_KEYS; ++i) {
|
||||
_driveKeys[i] = 0.0f;
|
||||
|
|
|
@ -25,7 +25,7 @@ class MyAvatar : public Avatar {
|
|||
Q_PROPERTY(glm::vec3 motorVelocity READ getScriptedMotorVelocity WRITE setScriptedMotorVelocity)
|
||||
Q_PROPERTY(float motorTimescale READ getScriptedMotorTimescale WRITE setScriptedMotorTimescale)
|
||||
Q_PROPERTY(QString motorReferenceFrame READ getScriptedMotorFrame WRITE setScriptedMotorFrame)
|
||||
Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setLocalGravity)
|
||||
//TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity)
|
||||
|
||||
public:
|
||||
MyAvatar();
|
||||
|
@ -44,13 +44,11 @@ public:
|
|||
|
||||
// setters
|
||||
void setLeanScale(float scale) { _leanScale = scale; }
|
||||
void setLocalGravity(glm::vec3 gravity);
|
||||
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; }
|
||||
void setRealWorldFieldOfView(float realWorldFov) { _realWorldFieldOfView.set(realWorldFov); }
|
||||
|
||||
// getters
|
||||
float getLeanScale() const { return _leanScale; }
|
||||
glm::vec3 getGravity() const { return _gravity; }
|
||||
Q_INVOKABLE glm::vec3 getDefaultEyePosition() const;
|
||||
bool getShouldRenderLocally() const { return _shouldRender; }
|
||||
float getRealWorldFieldOfView() { return _realWorldFieldOfView.get(); }
|
||||
|
@ -148,11 +146,6 @@ public:
|
|||
|
||||
const RecorderPointer getRecorder() const { return _recorder; }
|
||||
const PlayerPointer getPlayer() const { return _player; }
|
||||
|
||||
void togglePhysicsEnabled() { _enablePhysics = !_enablePhysics; }
|
||||
bool isPhysicsEnabled() { return _enablePhysics; }
|
||||
void setPhysicsEnabled(bool enablePhysics) { _enablePhysics = enablePhysics; }
|
||||
void preSimulation();
|
||||
|
||||
public slots:
|
||||
void increaseSize();
|
||||
|
@ -209,7 +202,6 @@ private:
|
|||
int _scriptedMotorFrame;
|
||||
quint32 _motionBehaviors;
|
||||
|
||||
bool _enablePhysics;
|
||||
CharacterController _characterController;
|
||||
|
||||
QWeakPointer<AvatarData> _lookAtTargetAvatar;
|
||||
|
@ -234,10 +226,8 @@ private:
|
|||
glm::vec3 applyKeyboardMotor(float deltaTime, const glm::vec3& velocity, bool walkingOnFloor);
|
||||
glm::vec3 applyScriptedMotor(float deltaTime, const glm::vec3& velocity);
|
||||
void updatePosition(float deltaTime);
|
||||
void updatePositionWithPhysics(float deltaTime);
|
||||
void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency);
|
||||
void maybeUpdateBillboard();
|
||||
void setGravity(const glm::vec3& gravity);
|
||||
};
|
||||
|
||||
#endif // hifi_MyAvatar_h
|
||||
|
|
|
@ -353,7 +353,7 @@ void InputController::update() {
|
|||
// TODO for now the InputController is only supporting a JointTracker from a MotionTracker
|
||||
MotionTracker* motionTracker = dynamic_cast< MotionTracker*> (DeviceTracker::getDevice(_deviceTrackerId));
|
||||
if (motionTracker) {
|
||||
if (_subTrackerId < motionTracker->numJointTrackers()) {
|
||||
if ((int)_subTrackerId < motionTracker->numJointTrackers()) {
|
||||
const MotionTracker::JointTracker* joint = motionTracker->getJointTracker(_subTrackerId);
|
||||
|
||||
if (joint->isActive()) {
|
||||
|
|
|
@ -33,6 +33,7 @@ WindowScriptingInterface::WindowScriptingInterface() :
|
|||
const DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
|
||||
connect(&domainHandler, &DomainHandler::hostnameChanged, this, &WindowScriptingInterface::domainChanged);
|
||||
connect(Application::getInstance(), &Application::svoImportRequested, this, &WindowScriptingInterface::svoImportRequested);
|
||||
connect(Application::getInstance(), &Application::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused);
|
||||
}
|
||||
|
||||
WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& title, const QString& url, int width, int height, bool isToolWindow) {
|
||||
|
|
|
@ -62,6 +62,7 @@ signals:
|
|||
void inlineButtonClicked(const QString& name);
|
||||
void nonBlockingFormClosed();
|
||||
void svoImportRequested(const QString& url);
|
||||
void domainConnectionRefused(const QString& reason);
|
||||
|
||||
private slots:
|
||||
QScriptValue showAlert(const QString& message);
|
||||
|
|
|
@ -139,6 +139,7 @@ ApplicationOverlay::ApplicationOverlay() :
|
|||
_magnifier(true),
|
||||
_alpha(1.0f),
|
||||
_oculusUIRadius(1.0f),
|
||||
_trailingAudioLoudness(0.0f),
|
||||
_crosshairTexture(0),
|
||||
_previousBorderWidth(-1),
|
||||
_previousBorderHeight(-1),
|
||||
|
|
|
@ -88,9 +88,9 @@ template< typename T >
|
|||
void AudioFrameBuffer< T >::deallocateFrames() {
|
||||
if (_frameBuffer) {
|
||||
for (uint32_t i = 0; i < _channelCountMax; ++i) {
|
||||
delete _frameBuffer[i];
|
||||
delete[] _frameBuffer[i];
|
||||
}
|
||||
delete _frameBuffer;
|
||||
delete[] _frameBuffer;
|
||||
}
|
||||
_frameBuffer = NULL;
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ public:
|
|||
}
|
||||
|
||||
void loadProfile(int profileIndex) {
|
||||
if (profileIndex >= 0 && profileIndex < _profileCount) {
|
||||
if (profileIndex >= 0 && profileIndex < (int)_profileCount) {
|
||||
|
||||
for (uint32_t i = 0; i < _filterCount; ++i) {
|
||||
FilterParameter p = _profiles[profileIndex][i];
|
||||
|
|
|
@ -61,21 +61,13 @@ typedef unsigned long long quint64;
|
|||
const quint32 AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED = 1U << 0;
|
||||
const quint32 AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED = 1U << 1;
|
||||
|
||||
const quint32 AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY = 1U << 2;
|
||||
const quint32 AVATAR_MOTION_OBEY_LOCAL_GRAVITY = 1U << 3;
|
||||
const quint32 AVATAR_MOTION_STAND_ON_NEARBY_FLOORS = 1U << 4;
|
||||
|
||||
const quint32 AVATAR_MOTION_DEFAULTS =
|
||||
AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED |
|
||||
AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED |
|
||||
AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
|
||||
AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
|
||||
|
||||
// these bits will be expanded as features are exposed
|
||||
const quint32 AVATAR_MOTION_SCRIPTABLE_BITS =
|
||||
AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED |
|
||||
AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY |
|
||||
AVATAR_MOTION_OBEY_LOCAL_GRAVITY |
|
||||
AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
|
||||
AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
|
||||
|
||||
|
||||
// Bitset of state flags - we store the key state, hand state, faceshift, chat circling, and existance of
|
||||
|
@ -365,8 +357,8 @@ protected:
|
|||
HeadData* _headData;
|
||||
HandData* _handData;
|
||||
|
||||
QUrl _faceModelURL = DEFAULT_HEAD_MODEL_URL;
|
||||
QUrl _skeletonModelURL = DEFAULT_BODY_MODEL_URL;
|
||||
QUrl _faceModelURL; // These need to be empty so that on first time setting them they will not short circuit
|
||||
QUrl _skeletonModelURL; // These need to be empty so that on first time setting them they will not short circuit
|
||||
QVector<AttachmentData> _attachmentData;
|
||||
QString _displayName;
|
||||
|
||||
|
|
|
@ -266,6 +266,26 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori
|
|||
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, extraInfo, precisionPicking);
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::setCollisionModelURL(const QString& url) {
|
||||
ModelEntityItem::setCollisionModelURL(url);
|
||||
if (_model) {
|
||||
_model->setCollisionModelURL(QUrl(url));
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::hasCollisionModel() const {
|
||||
if (_model) {
|
||||
return ! _model->getCollisionURL().isEmpty();
|
||||
} else {
|
||||
return !_collisionModelURL.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
const QString& RenderableModelEntityItem::getCollisionModelURL() const {
|
||||
assert (!_model || _collisionModelURL == _model->getCollisionURL().toString());
|
||||
return _collisionModelURL;
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::isReadyToComputeShape() {
|
||||
|
||||
if (!_model) {
|
||||
|
@ -294,13 +314,67 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
const QSharedPointer<NetworkGeometry> collisionNetworkGeometry = _model->getCollisionGeometry();
|
||||
const FBXGeometry& fbxGeometry = collisionNetworkGeometry->getFBXGeometry();
|
||||
|
||||
AABox aaBox;
|
||||
_points.clear();
|
||||
unsigned int i = 0;
|
||||
foreach (const FBXMesh& mesh, fbxGeometry.meshes) {
|
||||
_points << mesh.vertices;
|
||||
|
||||
foreach (const FBXMeshPart &meshPart, mesh.parts) {
|
||||
QVector<glm::vec3> pointsInPart;
|
||||
unsigned int triangleCount = meshPart.triangleIndices.size() / 3;
|
||||
assert((unsigned int)meshPart.triangleIndices.size() == triangleCount*3);
|
||||
for (unsigned int j = 0; j < triangleCount; j++) {
|
||||
unsigned int p0Index = meshPart.triangleIndices[j*3];
|
||||
unsigned int p1Index = meshPart.triangleIndices[j*3+1];
|
||||
unsigned int p2Index = meshPart.triangleIndices[j*3+2];
|
||||
|
||||
assert(p0Index < (unsigned int)mesh.vertices.size());
|
||||
assert(p1Index < (unsigned int)mesh.vertices.size());
|
||||
assert(p2Index < (unsigned int)mesh.vertices.size());
|
||||
|
||||
glm::vec3 p0 = mesh.vertices[p0Index];
|
||||
glm::vec3 p1 = mesh.vertices[p1Index];
|
||||
glm::vec3 p2 = mesh.vertices[p2Index];
|
||||
|
||||
aaBox += p0;
|
||||
aaBox += p1;
|
||||
aaBox += p2;
|
||||
|
||||
if (!pointsInPart.contains(p0)) {
|
||||
pointsInPart << p0;
|
||||
}
|
||||
if (!pointsInPart.contains(p1)) {
|
||||
pointsInPart << p1;
|
||||
}
|
||||
if (!pointsInPart.contains(p2)) {
|
||||
pointsInPart << p2;
|
||||
}
|
||||
}
|
||||
|
||||
QVector<glm::vec3> newMeshPoints;
|
||||
_points << newMeshPoints;
|
||||
_points[i++] << pointsInPart;
|
||||
}
|
||||
}
|
||||
|
||||
info.setParams(getShapeType(), 0.5f * getDimensions(), _collisionModelURL);
|
||||
info.setConvexHull(_points);
|
||||
// make sure we aren't about to divide by zero
|
||||
glm::vec3 aaBoxDim = aaBox.getDimensions();
|
||||
aaBoxDim = glm::clamp(aaBoxDim, glm::vec3(FLT_EPSILON), aaBoxDim);
|
||||
|
||||
glm::vec3 scale = _dimensions / aaBoxDim;
|
||||
|
||||
// multiply each point by scale before handing the point-set off to the physics engine
|
||||
for (int i = 0; i < _points.size(); i++) {
|
||||
for (int j = 0; j < _points[i].size(); j++) {
|
||||
// compensate for registraion
|
||||
_points[i][j] += _model->getOffset();
|
||||
// scale so the collision points match the model points
|
||||
_points[i][j] *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
info.setParams(getShapeType(), _dimensions, _collisionModelURL);
|
||||
info.setConvexHulls(_points);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -308,7 +382,9 @@ ShapeType RenderableModelEntityItem::getShapeType() const {
|
|||
// XXX make hull an option in edit.js ?
|
||||
if (!_model || _model->getCollisionURL().isEmpty()) {
|
||||
return _shapeType;
|
||||
} else {
|
||||
} else if (_points.size() == 1) {
|
||||
return SHAPE_TYPE_CONVEX_HULL;
|
||||
} else {
|
||||
return SHAPE_TYPE_COMPOUND;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,10 @@ public:
|
|||
|
||||
bool needsToCallUpdate() const;
|
||||
|
||||
virtual void setCollisionModelURL(const QString& url);
|
||||
virtual bool hasCollisionModel() const;
|
||||
virtual const QString& getCollisionModelURL() const;
|
||||
|
||||
bool isReadyToComputeShape();
|
||||
void computeShapeInfo(ShapeInfo& info);
|
||||
ShapeType getShapeType() const;
|
||||
|
@ -66,7 +70,7 @@ private:
|
|||
QString _currentTextures;
|
||||
QStringList _originalTextures;
|
||||
bool _originalTexturesRead;
|
||||
QVector<glm::vec3> _points;
|
||||
QVector<QVector<glm::vec3>> _points;
|
||||
};
|
||||
|
||||
#endif // hifi_RenderableModelEntityItem_h
|
||||
|
|
|
@ -57,7 +57,7 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) {
|
|||
_collisionsWillMove = ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE;
|
||||
_locked = ENTITY_ITEM_DEFAULT_LOCKED;
|
||||
_userData = ENTITY_ITEM_DEFAULT_USER_DATA;
|
||||
_attribution = ENTITY_ITEM_DEFAULT_ATTRIBUTION;
|
||||
_marketplaceID = ENTITY_ITEM_DEFAULT_MARKETPLACE_ID;
|
||||
}
|
||||
|
||||
EntityItem::EntityItem(const EntityItemID& entityItemID) {
|
||||
|
@ -117,7 +117,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
|||
requestedProperties += PROP_COLLISIONS_WILL_MOVE;
|
||||
requestedProperties += PROP_LOCKED;
|
||||
requestedProperties += PROP_USER_DATA;
|
||||
requestedProperties += PROP_ATTRIBUTION;
|
||||
requestedProperties += PROP_MARKETPLACE_ID;
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, appendValue, getCollisionsWillMove());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LOCKED, appendValue, getLocked());
|
||||
APPEND_ENTITY_PROPERTY(PROP_USER_DATA, appendValue, getUserData());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ATTRIBUTION, appendValue, getAttribution());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, appendValue, getMarketplaceID());
|
||||
|
||||
appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
|
||||
requestedProperties,
|
||||
|
@ -555,8 +555,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
READ_ENTITY_PROPERTY(PROP_LOCKED, bool, _locked);
|
||||
READ_ENTITY_PROPERTY_STRING(PROP_USER_DATA, setUserData);
|
||||
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_ATTRIBUTION) {
|
||||
READ_ENTITY_PROPERTY_STRING(PROP_ATTRIBUTION, setAttribution);
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_MARKETPLACE_ID) {
|
||||
READ_ENTITY_PROPERTY_STRING(PROP_MARKETPLACE_ID, setMarketplaceID);
|
||||
}
|
||||
|
||||
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData);
|
||||
|
@ -568,8 +568,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
// by doing this parsing here... but it's not likely going to fully recover the content.
|
||||
//
|
||||
// TODO: Remove this conde once we've sufficiently migrated content past this damaged version
|
||||
if (args.bitstreamVersion == VERSION_ENTITIES_HAS_ATTRIBUTION_DAMAGED) {
|
||||
READ_ENTITY_PROPERTY_STRING(PROP_ATTRIBUTION, setAttribution);
|
||||
if (args.bitstreamVersion == VERSION_ENTITIES_HAS_MARKETPLACE_ID_DAMAGED) {
|
||||
READ_ENTITY_PROPERTY_STRING(PROP_MARKETPLACE_ID, setMarketplaceID);
|
||||
}
|
||||
|
||||
if (overwriteLocalData && (getDirtyFlags() & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY))) {
|
||||
|
@ -838,7 +838,7 @@ EntityItemProperties EntityItem::getProperties() const {
|
|||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionsWillMove, getCollisionsWillMove);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(locked, getLocked);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(userData, getUserData);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(attribution, getAttribution);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(marketplaceID, getMarketplaceID);
|
||||
|
||||
properties._defaultSettings = false;
|
||||
|
||||
|
@ -867,7 +867,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, updateCollisionsWillMove);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, setLocked);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(attribution, setAttribution);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(marketplaceID, setMarketplaceID);
|
||||
|
||||
if (somethingChanged) {
|
||||
somethingChangedNotification(); // notify derived classes that something has changed
|
||||
|
|
|
@ -251,8 +251,8 @@ public:
|
|||
const QString& getUserData() const { return _userData; }
|
||||
void setUserData(const QString& value) { _userData = value; }
|
||||
|
||||
const QString& getAttribution() const { return _attribution; }
|
||||
void setAttribution(const QString& value) { _attribution = value; }
|
||||
const QString& getMarketplaceID() const { return _marketplaceID; }
|
||||
void setMarketplaceID(const QString& value) { _marketplaceID = value; }
|
||||
|
||||
// TODO: get rid of users of getRadius()...
|
||||
float getRadius() const;
|
||||
|
@ -342,7 +342,7 @@ protected:
|
|||
bool _collisionsWillMove;
|
||||
bool _locked;
|
||||
QString _userData;
|
||||
QString _attribution;
|
||||
QString _marketplaceID;
|
||||
|
||||
// NOTE: Damping is applied like this: v *= pow(1 - damping, dt)
|
||||
//
|
||||
|
|
|
@ -70,7 +70,7 @@ EntityItemProperties::EntityItemProperties() :
|
|||
CONSTRUCT_PROPERTY(emitStrength, ParticleEffectEntityItem::DEFAULT_EMIT_STRENGTH),
|
||||
CONSTRUCT_PROPERTY(localGravity, ParticleEffectEntityItem::DEFAULT_LOCAL_GRAVITY),
|
||||
CONSTRUCT_PROPERTY(particleRadius, ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS),
|
||||
CONSTRUCT_PROPERTY(attribution, ENTITY_ITEM_DEFAULT_ATTRIBUTION),
|
||||
CONSTRUCT_PROPERTY(marketplaceID, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID),
|
||||
|
||||
_id(UNKNOWN_ENTITY_ID),
|
||||
_idSet(false),
|
||||
|
@ -260,7 +260,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_EMIT_STRENGTH, emitStrength);
|
||||
CHECK_PROPERTY_CHANGE(PROP_LOCAL_GRAVITY, localGravity);
|
||||
CHECK_PROPERTY_CHANGE(PROP_PARTICLE_RADIUS, particleRadius);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ATTRIBUTION, attribution);
|
||||
CHECK_PROPERTY_CHANGE(PROP_MARKETPLACE_ID, marketplaceID);
|
||||
|
||||
return changedProperties;
|
||||
}
|
||||
|
@ -323,7 +323,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(emitStrength);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(localGravity);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(particleRadius);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(attribution);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(marketplaceID);
|
||||
|
||||
// Sitting properties support
|
||||
QScriptValue sittingPoints = engine->newObject();
|
||||
|
@ -405,7 +405,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(emitStrength, setEmitStrength);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(localGravity, setLocalGravity);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(particleRadius, setParticleRadius);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(attribution, setAttribution);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(marketplaceID, setMarketplaceID);
|
||||
|
||||
_lastEdited = usecTimestampNow();
|
||||
}
|
||||
|
@ -591,7 +591,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, appendValue, properties.getParticleRadius());
|
||||
}
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_ATTRIBUTION, appendValue, properties.getAttribution());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, appendValue, properties.getMarketplaceID());
|
||||
}
|
||||
if (propertyCount > 0) {
|
||||
int endOfEntityItemData = packetData->getUncompressedByteOffset();
|
||||
|
@ -822,7 +822,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_RADIUS, float, setParticleRadius);
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ATTRIBUTION, setAttribution);
|
||||
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MARKETPLACE_ID, setMarketplaceID);
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
@ -905,7 +905,7 @@ void EntityItemProperties::markAllChanged() {
|
|||
_localGravityChanged = true;
|
||||
_particleRadiusChanged = true;
|
||||
|
||||
_attributionChanged = true;
|
||||
_marketplaceIDChanged = true;
|
||||
}
|
||||
|
||||
/// The maximum bounding cube for the entity, independent of it's rotation.
|
||||
|
|
|
@ -94,11 +94,11 @@ enum EntityPropertyList {
|
|||
PROP_PARTICLE_RADIUS,
|
||||
|
||||
PROP_COLLISION_MODEL_URL,
|
||||
PROP_ATTRIBUTION,
|
||||
PROP_MARKETPLACE_ID,
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ATTENTION: add new properties ABOVE this line and then modify PROP_LAST_ITEM below
|
||||
PROP_LAST_ITEM = PROP_ATTRIBUTION,
|
||||
PROP_LAST_ITEM = PROP_MARKETPLACE_ID,
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
@ -205,7 +205,7 @@ public:
|
|||
DEFINE_PROPERTY(PROP_EMIT_STRENGTH, EmitStrength, emitStrength, float);
|
||||
DEFINE_PROPERTY(PROP_LOCAL_GRAVITY, LocalGravity, localGravity, float);
|
||||
DEFINE_PROPERTY(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float);
|
||||
DEFINE_PROPERTY_REF(PROP_ATTRIBUTION, Attribution, attribution, QString);
|
||||
DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString);
|
||||
|
||||
public:
|
||||
float getMaxDimension() const { return glm::max(_dimensions.x, _dimensions.y, _dimensions.z); }
|
||||
|
@ -333,7 +333,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
|
|||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitStrength, emitStrength, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, LocalGravity, localGravity, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParticleRadius, particleRadius, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Attribution, attribution, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, MarketplaceID, marketplaceID, "");
|
||||
|
||||
debug << " last edited:" << properties.getLastEdited() << "\n";
|
||||
debug << " edited ago:" << properties.getEditedAgo() << "\n";
|
||||
|
|
|
@ -22,7 +22,7 @@ const glm::vec3 ENTITY_ITEM_ZERO_VEC3(0.0f);
|
|||
|
||||
const bool ENTITY_ITEM_DEFAULT_LOCKED = false;
|
||||
const QString ENTITY_ITEM_DEFAULT_USER_DATA = QString("");
|
||||
const QString ENTITY_ITEM_DEFAULT_ATTRIBUTION = QString("");
|
||||
const QString ENTITY_ITEM_DEFAULT_MARKETPLACE_ID = QString("");
|
||||
|
||||
const float ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA = 1.0f;
|
||||
const float ENTITY_ITEM_DEFAULT_GLOW_LEVEL = 0.0f;
|
||||
|
|
|
@ -148,6 +148,7 @@ bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemPro
|
|||
}
|
||||
}
|
||||
} else {
|
||||
QString entityScriptBefore = entity->getScript();
|
||||
uint32_t preFlags = entity->getDirtyFlags();
|
||||
UpdateEntityOperator theOperator(this, containingElement, entity, properties);
|
||||
recurseTreeWithOperator(&theOperator);
|
||||
|
@ -166,6 +167,11 @@ bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemPro
|
|||
entity->clearDirtyFlags();
|
||||
}
|
||||
}
|
||||
|
||||
QString entityScriptAfter = entity->getScript();
|
||||
if (entityScriptBefore != entityScriptAfter) {
|
||||
emitEntityScriptChanging(entity->getEntityItemID()); // the entity script has changed
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this final containingElement check should eventually be removed (or wrapped in an #ifdef DEBUG).
|
||||
|
|
|
@ -95,7 +95,6 @@ public:
|
|||
|
||||
void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = false);
|
||||
void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false, bool ignoreWarnings = false);
|
||||
void removeEntityFromSimulation(EntityItem* entity);
|
||||
|
||||
/// \param position point of query in world-frame (meters)
|
||||
/// \param targetRadius radius of query (meters)
|
||||
|
|
|
@ -281,6 +281,13 @@ void ModelEntityItem::updateShapeType(ShapeType type) {
|
|||
}
|
||||
}
|
||||
|
||||
void ModelEntityItem::setCollisionModelURL(const QString& url) {
|
||||
if (_collisionModelURL != url) {
|
||||
_collisionModelURL = url;
|
||||
_dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS;
|
||||
}
|
||||
}
|
||||
|
||||
void ModelEntityItem::setAnimationURL(const QString& url) {
|
||||
_dirtyFlags |= EntityItem::DIRTY_UPDATEABLE;
|
||||
_animationURL = url;
|
||||
|
|
|
@ -57,13 +57,13 @@ public:
|
|||
const rgbColor& getColor() const { return _color; }
|
||||
xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
|
||||
bool hasModel() const { return !_modelURL.isEmpty(); }
|
||||
bool hasCollisionModel() const { return !_collisionModelURL.isEmpty(); }
|
||||
virtual bool hasCollisionModel() const { return !_collisionModelURL.isEmpty(); }
|
||||
|
||||
static const QString DEFAULT_MODEL_URL;
|
||||
const QString& getModelURL() const { return _modelURL; }
|
||||
|
||||
static const QString DEFAULT_COLLISION_MODEL_URL;
|
||||
const QString& getCollisionModelURL() const { return _collisionModelURL; }
|
||||
virtual const QString& getCollisionModelURL() const { return _collisionModelURL; }
|
||||
|
||||
bool hasAnimation() const { return !_animationURL.isEmpty(); }
|
||||
static const QString DEFAULT_ANIMATION_URL;
|
||||
|
@ -78,7 +78,7 @@ public:
|
|||
|
||||
// model related properties
|
||||
void setModelURL(const QString& url) { _modelURL = url; }
|
||||
void setCollisionModelURL(const QString& url) { _collisionModelURL = url; }
|
||||
virtual void setCollisionModelURL(const QString& url);
|
||||
void setAnimationURL(const QString& url);
|
||||
static const float DEFAULT_ANIMATION_FRAME_INDEX;
|
||||
void setAnimationFrameIndex(float value);
|
||||
|
|
|
@ -249,7 +249,20 @@ bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping,
|
|||
} else if (indices.count() == 4) {
|
||||
meshPart.quadIndices << indices;
|
||||
} else {
|
||||
qDebug() << "no support for more than 4 vertices on a face in OBJ files";
|
||||
// some obj writers (maya) will write a face with lots of points.
|
||||
for (int i = 1; i < indices.count() - 1; i++) {
|
||||
// break the face into triangles
|
||||
meshPart.triangleIndices.append(indices[0]);
|
||||
meshPart.triangleIndices.append(indices[i]);
|
||||
meshPart.triangleIndices.append(indices[i+1]);
|
||||
}
|
||||
if (indices.count() == normalIndices.count()) {
|
||||
for (int i = 1; i < normalIndices.count() - 1; i++) {
|
||||
faceNormalIndexes.append(normalIndices[0]);
|
||||
faceNormalIndexes.append(normalIndices[i]);
|
||||
faceNormalIndexes.append(normalIndices[i+1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// something we don't (yet) care about
|
||||
|
|
|
@ -282,7 +282,7 @@ void AccountManager::processReply() {
|
|||
} else {
|
||||
passErrorToCallback(requestReply);
|
||||
}
|
||||
delete requestReply;
|
||||
requestReply->deleteLater();
|
||||
}
|
||||
|
||||
void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) {
|
||||
|
|
|
@ -74,7 +74,7 @@ PacketVersion versionForPacketType(PacketType type) {
|
|||
return 1;
|
||||
case PacketTypeEntityAddOrEdit:
|
||||
case PacketTypeEntityData:
|
||||
return VERSION_ENTITIES_HAS_ATTRIBUTION;
|
||||
return VERSION_ENTITIES_HAS_MARKETPLACE_ID;
|
||||
case PacketTypeEntityErase:
|
||||
return 2;
|
||||
case PacketTypeAudioStreamStats:
|
||||
|
|
|
@ -132,8 +132,8 @@ const PacketVersion VERSION_ENTITIES_LIGHT_HAS_INTENSITY_AND_COLOR_PROPERTIES =
|
|||
const PacketVersion VERSION_ENTITIES_HAS_PARTICLES = 10;
|
||||
const PacketVersion VERSION_ENTITIES_USE_METERS_AND_RADIANS = 11;
|
||||
const PacketVersion VERSION_ENTITIES_HAS_COLLISION_MODEL = 12;
|
||||
const PacketVersion VERSION_ENTITIES_HAS_ATTRIBUTION_DAMAGED = 13;
|
||||
const PacketVersion VERSION_ENTITIES_HAS_ATTRIBUTION = 14;
|
||||
const PacketVersion VERSION_ENTITIES_HAS_MARKETPLACE_ID_DAMAGED = 13;
|
||||
const PacketVersion VERSION_ENTITIES_HAS_MARKETPLACE_ID = 14;
|
||||
const PacketVersion VERSION_OCTREE_HAS_FILE_BREAKS = 1;
|
||||
|
||||
#endif // hifi_PacketHeaders_h
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <QTimer>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "NetworkAccessManager.h"
|
||||
#include "ResourceCache.h"
|
||||
|
@ -48,32 +49,40 @@ void ResourceCache::refresh(const QUrl& url) {
|
|||
}
|
||||
}
|
||||
|
||||
void ResourceCache::getResourceAsynchronously(const QUrl& url) {
|
||||
qDebug() << "ResourceCache::getResourceAsynchronously" << url.toString();
|
||||
_resourcesToBeGottenLock.lockForWrite();
|
||||
_resourcesToBeGotten.enqueue(QUrl(url));
|
||||
_resourcesToBeGottenLock.unlock();
|
||||
}
|
||||
|
||||
void ResourceCache::checkAsynchronousGets() {
|
||||
assert(QThread::currentThread() == thread());
|
||||
if (!_resourcesToBeGotten.isEmpty()) {
|
||||
_resourcesToBeGottenLock.lockForWrite();
|
||||
QUrl url = _resourcesToBeGotten.dequeue();
|
||||
_resourcesToBeGottenLock.unlock();
|
||||
getResource(url);
|
||||
}
|
||||
}
|
||||
|
||||
QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl& fallback,
|
||||
bool delayLoad, void* extra, bool block) {
|
||||
bool delayLoad, void* extra) {
|
||||
QSharedPointer<Resource> resource = _resources.value(url);
|
||||
if (!resource.isNull()) {
|
||||
return resource;
|
||||
}
|
||||
|
||||
if (QThread::currentThread() != thread()) {
|
||||
// This will re-call this method in the main thread. If block is true and the main thread
|
||||
// is waiting on a lock, we'll deadlock here.
|
||||
if (block) {
|
||||
QSharedPointer<Resource> result;
|
||||
QMetaObject::invokeMethod(this, "getResource", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QSharedPointer<Resource>, result), Q_ARG(const QUrl&, url),
|
||||
Q_ARG(const QUrl&, fallback), Q_ARG(bool, delayLoad), Q_ARG(void*, extra));
|
||||
return result;
|
||||
} else {
|
||||
// Queue the re-invocation of this method, but if the main thread is blocked, don't wait. The
|
||||
// return value may be NULL -- it's expected that this will be called again later, in order
|
||||
// to receive the actual Resource.
|
||||
QMetaObject::invokeMethod(this, "getResource", Qt::QueuedConnection,
|
||||
Q_ARG(const QUrl&, url),
|
||||
Q_ARG(const QUrl&, fallback), Q_ARG(bool, delayLoad), Q_ARG(void*, extra));
|
||||
return _resources.value(url);
|
||||
}
|
||||
assert(delayLoad);
|
||||
getResourceAsynchronously(url);
|
||||
return QSharedPointer<Resource>();
|
||||
}
|
||||
|
||||
if (!url.isValid() && !url.isEmpty() && fallback.isValid()) {
|
||||
return getResource(fallback, QUrl(), delayLoad);
|
||||
}
|
||||
QSharedPointer<Resource> resource = _resources.value(url);
|
||||
|
||||
if (resource.isNull()) {
|
||||
resource = createResource(url, fallback.isValid() ?
|
||||
getResource(fallback, QUrl(), true) : QSharedPointer<Resource>(), delayLoad, extra);
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include <QSharedPointer>
|
||||
#include <QUrl>
|
||||
#include <QWeakPointer>
|
||||
#include <QReadWriteLock>
|
||||
#include <QQueue>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
|
@ -79,6 +81,9 @@ public:
|
|||
|
||||
void refresh(const QUrl& url);
|
||||
|
||||
public slots:
|
||||
void checkAsynchronousGets();
|
||||
|
||||
protected:
|
||||
qint64 _unusedResourcesMaxSize = DEFAULT_UNUSED_MAX_SIZE;
|
||||
qint64 _unusedResourcesSize = 0;
|
||||
|
@ -89,7 +94,7 @@ protected:
|
|||
/// \param delayLoad if true, don't load the resource immediately; wait until load is first requested
|
||||
/// \param extra extra data to pass to the creator, if appropriate
|
||||
Q_INVOKABLE QSharedPointer<Resource> getResource(const QUrl& url, const QUrl& fallback = QUrl(),
|
||||
bool delayLoad = false, void* extra = NULL, bool block = true);
|
||||
bool delayLoad = false, void* extra = NULL);
|
||||
|
||||
/// Creates a new resource.
|
||||
virtual QSharedPointer<Resource> createResource(const QUrl& url,
|
||||
|
@ -109,6 +114,11 @@ private:
|
|||
int _lastLRUKey = 0;
|
||||
|
||||
static int _requestLimit;
|
||||
|
||||
void getResourceAsynchronously(const QUrl& url);
|
||||
QReadWriteLock _resourcesToBeGottenLock;
|
||||
QQueue<QUrl> _resourcesToBeGotten;
|
||||
|
||||
};
|
||||
|
||||
/// Base class for resources.
|
||||
|
|
|
@ -40,6 +40,24 @@ static btVector3 getNormalizedVector(const btVector3& v) {
|
|||
return n;
|
||||
}
|
||||
|
||||
class btKinematicClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback {
|
||||
public:
|
||||
btKinematicClosestNotMeRayResultCallback (btCollisionObject* me) :
|
||||
btCollisionWorld::ClosestRayResultCallback(btVector3(0.0f, 0.0f, 0.0f), btVector3(0.0f, 0.0f, 0.0f)) {
|
||||
_me = me;
|
||||
}
|
||||
|
||||
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) {
|
||||
if (rayResult.m_collisionObject == _me) {
|
||||
return 1.0f;
|
||||
}
|
||||
return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace);
|
||||
}
|
||||
protected:
|
||||
btCollisionObject* _me;
|
||||
};
|
||||
|
||||
|
||||
class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback {
|
||||
public:
|
||||
btKinematicClosestNotMeConvexResultCallback(btCollisionObject* me, const btVector3& up, btScalar minSlopeDot)
|
||||
|
@ -199,9 +217,7 @@ CharacterController::CharacterController(AvatarData* avatarData) {
|
|||
assert(avatarData);
|
||||
_avatarData = avatarData;
|
||||
|
||||
// cache the "PhysicsEnabled" state of _avatarData
|
||||
_enabled = false;
|
||||
|
||||
_ghostObject = NULL;
|
||||
_convexShape = NULL;
|
||||
|
||||
|
@ -210,14 +226,18 @@ CharacterController::CharacterController(AvatarData* avatarData) {
|
|||
_velocityTimeInterval = 0.0f;
|
||||
_verticalVelocity = 0.0f;
|
||||
_verticalOffset = 0.0f;
|
||||
_gravity = 9.8f;
|
||||
_gravity = 5.0f; // slower than Earth's
|
||||
_maxFallSpeed = 55.0f; // Terminal velocity of a sky diver in m/s.
|
||||
_jumpSpeed = 7.0f;
|
||||
_wasOnGround = false;
|
||||
_wasJumping = false;
|
||||
_jumpSpeed = 5.0f;
|
||||
_isOnGround = false;
|
||||
_isJumping = false;
|
||||
_isHovering = true;
|
||||
_jumpToHoverStart = 0;
|
||||
setMaxSlope(btRadians(45.0f));
|
||||
_lastStepUp = 0.0f;
|
||||
_pendingFlags = 0;
|
||||
|
||||
_pendingFlags = PENDING_FLAG_UPDATE_SHAPE;
|
||||
updateShapeIfNecessary();
|
||||
}
|
||||
|
||||
CharacterController::~CharacterController() {
|
||||
|
@ -325,6 +345,26 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl
|
|||
return penetration;
|
||||
}
|
||||
|
||||
void CharacterController::scanDown(btCollisionWorld* world) {
|
||||
// we test with downward raycast and if we don't find floor close enough then turn on "hover"
|
||||
btKinematicClosestNotMeRayResultCallback callback(_ghostObject);
|
||||
callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
|
||||
callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
|
||||
|
||||
btVector3 up = quatRotate(_currentRotation, LOCAL_UP_AXIS);
|
||||
btVector3 start = _currentPosition;
|
||||
const btScalar MAX_SCAN_HEIGHT = 20.0f + _halfHeight + _radius; // closest possible floor for disabling hover
|
||||
const btScalar MIN_HOVER_HEIGHT = 3.0f + _halfHeight + _radius; // distance to floor for enabling hover
|
||||
btVector3 end = start - MAX_SCAN_HEIGHT * up;
|
||||
|
||||
world->rayTest(start, end, callback);
|
||||
if (!callback.hasHit()) {
|
||||
_isHovering = true;
|
||||
} else if (_isHovering && callback.m_closestHitFraction * MAX_SCAN_HEIGHT < MIN_HOVER_HEIGHT) {
|
||||
_isHovering = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterController::stepUp(btCollisionWorld* world) {
|
||||
// phase 1: up
|
||||
|
||||
|
@ -334,7 +374,7 @@ void CharacterController::stepUp(btCollisionWorld* world) {
|
|||
btVector3 up = quatRotate(_currentRotation, LOCAL_UP_AXIS);
|
||||
start.setOrigin(_currentPosition + up * (_convexShape->getMargin() + _addedMargin));
|
||||
|
||||
_targetPosition = _currentPosition + up * _stepHeight;
|
||||
_targetPosition = _currentPosition + up * _stepUpHeight;
|
||||
end.setIdentity();
|
||||
end.setOrigin(_targetPosition);
|
||||
|
||||
|
@ -352,15 +392,15 @@ void CharacterController::stepUp(btCollisionWorld* world) {
|
|||
|
||||
// Only modify the position if the hit was a slope and not a wall or ceiling.
|
||||
if (callback.m_hitNormalWorld.dot(up) > 0.0f) {
|
||||
_lastStepUp = _stepHeight * callback.m_closestHitFraction;
|
||||
_lastStepUp = _stepUpHeight * callback.m_closestHitFraction;
|
||||
_currentPosition.setInterpolate3(_currentPosition, _targetPosition, callback.m_closestHitFraction);
|
||||
} else {
|
||||
_lastStepUp = _stepHeight;
|
||||
_lastStepUp = _stepUpHeight;
|
||||
_currentPosition = _targetPosition;
|
||||
}
|
||||
} else {
|
||||
_currentPosition = _targetPosition;
|
||||
_lastStepUp = _stepHeight;
|
||||
_lastStepUp = _stepUpHeight;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -411,12 +451,12 @@ void CharacterController::stepForward(btCollisionWorld* collisionWorld, const bt
|
|||
btScalar margin = _convexShape->getMargin();
|
||||
_convexShape->setMargin(margin + _addedMargin);
|
||||
|
||||
const btScalar MIN_STEP_DISTANCE = 0.0001f;
|
||||
const btScalar MIN_STEP_DISTANCE_SQUARED = 1.0e-6f;
|
||||
btVector3 step = _targetPosition - _currentPosition;
|
||||
btScalar stepLength2 = step.length2();
|
||||
int maxIter = 10;
|
||||
|
||||
while (stepLength2 > MIN_STEP_DISTANCE && maxIter-- > 0) {
|
||||
while (stepLength2 > MIN_STEP_DISTANCE_SQUARED && maxIter-- > 0) {
|
||||
start.setOrigin(_currentPosition);
|
||||
end.setOrigin(_targetPosition);
|
||||
|
||||
|
@ -477,13 +517,16 @@ void CharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt
|
|||
end.setOrigin(_targetPosition);
|
||||
_ghostObject->convexSweepTest(_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
|
||||
|
||||
_isOnGround = false;
|
||||
if (callback.hasHit()) {
|
||||
_currentPosition += callback.m_closestHitFraction * step;
|
||||
_verticalVelocity = 0.0f;
|
||||
_verticalOffset = 0.0f;
|
||||
_wasJumping = false;
|
||||
} else if (!_wasJumping) {
|
||||
_isJumping = false;
|
||||
_isOnGround = true;
|
||||
} else if (!_isJumping) {
|
||||
// sweep again for floor within downStep threshold
|
||||
step = -_stepDownHeight * up;
|
||||
StepDownConvexResultCallback callback2 (_ghostObject,
|
||||
up,
|
||||
_currentPosition, step,
|
||||
|
@ -495,8 +538,6 @@ void CharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt
|
|||
callback2.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
|
||||
|
||||
_currentPosition = _targetPosition;
|
||||
btVector3 oldPosition = _currentPosition;
|
||||
step = (- _stepHeight) * up;
|
||||
_targetPosition = _currentPosition + step;
|
||||
|
||||
start.setOrigin(_currentPosition);
|
||||
|
@ -507,10 +548,10 @@ void CharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt
|
|||
_currentPosition += callback2.m_closestHitFraction * step;
|
||||
_verticalVelocity = 0.0f;
|
||||
_verticalOffset = 0.0f;
|
||||
_wasJumping = false;
|
||||
_isJumping = false;
|
||||
_isOnGround = true;
|
||||
} else {
|
||||
// nothing to step down on, so remove the stepUp effect
|
||||
_currentPosition = oldPosition - _lastStepUp * up;
|
||||
// nothing to step down on
|
||||
_lastStepUp = 0.0f;
|
||||
}
|
||||
} else {
|
||||
|
@ -534,8 +575,8 @@ void CharacterController::setVelocityForTimeInterval(const btVector3& velocity,
|
|||
void CharacterController::reset(btCollisionWorld* collisionWorld) {
|
||||
_verticalVelocity = 0.0;
|
||||
_verticalOffset = 0.0;
|
||||
_wasOnGround = false;
|
||||
_wasJumping = false;
|
||||
_isOnGround = false;
|
||||
_isJumping = false;
|
||||
_walkDirection.setValue(0,0,0);
|
||||
_velocityTimeInterval = 0.0;
|
||||
|
||||
|
@ -581,14 +622,17 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar
|
|||
return; // no motion
|
||||
}
|
||||
|
||||
_wasOnGround = onGround();
|
||||
|
||||
// Update fall velocity.
|
||||
_verticalVelocity -= _gravity * dt;
|
||||
if (_verticalVelocity > _jumpSpeed) {
|
||||
_verticalVelocity = _jumpSpeed;
|
||||
} else if (_verticalVelocity < -_maxFallSpeed) {
|
||||
_verticalVelocity = -_maxFallSpeed;
|
||||
if (_isHovering) {
|
||||
const btScalar HOVER_RELAXATION_TIMESCALE = 1.0f;
|
||||
_verticalVelocity *= (1.0f - dt / HOVER_RELAXATION_TIMESCALE);
|
||||
} else {
|
||||
_verticalVelocity -= _gravity * dt;
|
||||
if (_verticalVelocity > _jumpSpeed) {
|
||||
_verticalVelocity = _jumpSpeed;
|
||||
} else if (_verticalVelocity < -_maxFallSpeed) {
|
||||
_verticalVelocity = -_maxFallSpeed;
|
||||
}
|
||||
}
|
||||
_verticalOffset = _verticalVelocity * dt;
|
||||
|
||||
|
@ -600,11 +644,14 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar
|
|||
// (2) step the character forward
|
||||
// (3) step the character down looking for new ledges, the original floor, or a floor one step below where we started
|
||||
|
||||
scanDown(collisionWorld);
|
||||
|
||||
stepUp(collisionWorld);
|
||||
|
||||
// compute substep and decrement total interval
|
||||
btScalar dtMoving = (dt < _velocityTimeInterval) ? dt : _velocityTimeInterval;
|
||||
_velocityTimeInterval -= dt;
|
||||
_stepDt += dt;
|
||||
|
||||
// stepForward substep
|
||||
btVector3 move = _walkDirection * dtMoving;
|
||||
|
@ -629,11 +676,25 @@ void CharacterController::setMaxJumpHeight(btScalar maxJumpHeight) {
|
|||
}
|
||||
|
||||
bool CharacterController::canJump() const {
|
||||
return onGround();
|
||||
return _isOnGround;
|
||||
}
|
||||
|
||||
void CharacterController::jump() {
|
||||
_pendingFlags |= PENDING_FLAG_JUMP;
|
||||
|
||||
// check for case where user is holding down "jump" key...
|
||||
// we'll eventually tansition to "hover"
|
||||
if (!_isHovering) {
|
||||
if (!_isJumping) {
|
||||
_jumpToHoverStart = usecTimestampNow();
|
||||
} else {
|
||||
quint64 now = usecTimestampNow();
|
||||
const quint64 JUMP_TO_HOVER_PERIOD = USECS_PER_SECOND;
|
||||
if (now - _jumpToHoverStart < JUMP_TO_HOVER_PERIOD) {
|
||||
_isHovering = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterController::setGravity(btScalar gravity) {
|
||||
|
@ -654,7 +715,7 @@ btScalar CharacterController::getMaxSlope() const {
|
|||
}
|
||||
|
||||
bool CharacterController::onGround() const {
|
||||
return _enabled && _verticalVelocity == 0.0f && _verticalOffset == 0.0f;
|
||||
return _isOnGround;
|
||||
}
|
||||
|
||||
void CharacterController::debugDraw(btIDebugDraw* debugDrawer) {
|
||||
|
@ -684,10 +745,12 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm
|
|||
if (radiusDelta < FLT_EPSILON && heightDelta < FLT_EPSILON) {
|
||||
// shape hasn't changed --> nothing to do
|
||||
} else {
|
||||
// we always need to: REMOVE when UPDATE_SHAPE, to avoid deleting shapes out from under the PhysicsEngine
|
||||
_pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION
|
||||
| PENDING_FLAG_UPDATE_SHAPE;
|
||||
// but only need to ADD back when we happen to be enabled
|
||||
if (_dynamicsWorld) {
|
||||
// must REMOVE from world prior to shape update
|
||||
_pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION;
|
||||
}
|
||||
_pendingFlags |= PENDING_FLAG_UPDATE_SHAPE;
|
||||
// only need to ADD back when we happen to be enabled
|
||||
if (_enabled) {
|
||||
_pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION;
|
||||
}
|
||||
|
@ -711,11 +774,13 @@ void CharacterController::setEnabled(bool enabled) {
|
|||
// Don't bother clearing REMOVE bit since it might be paired with an UPDATE_SHAPE bit.
|
||||
// Setting the ADD bit here works for all cases so we don't even bother checking other bits.
|
||||
_pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION;
|
||||
_isHovering = true;
|
||||
} else {
|
||||
// Always set REMOVE bit when going disabled, and we always clear the ADD bit just in case
|
||||
// it was previously set by something else (e.g. an UPDATE_SHAPE event).
|
||||
_pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION;
|
||||
if (_dynamicsWorld) {
|
||||
_pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION;
|
||||
}
|
||||
_pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION;
|
||||
_isOnGround = false;
|
||||
}
|
||||
_enabled = enabled;
|
||||
}
|
||||
|
@ -723,23 +788,32 @@ void CharacterController::setEnabled(bool enabled) {
|
|||
|
||||
void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
|
||||
if (_dynamicsWorld != world) {
|
||||
if (_dynamicsWorld) {
|
||||
_dynamicsWorld->removeCollisionObject(getGhostObject());
|
||||
_dynamicsWorld->removeAction(this);
|
||||
if (_dynamicsWorld) {
|
||||
if (_ghostObject) {
|
||||
_dynamicsWorld->removeCollisionObject(_ghostObject);
|
||||
_dynamicsWorld->removeAction(this);
|
||||
}
|
||||
_dynamicsWorld = NULL;
|
||||
}
|
||||
_dynamicsWorld = world;
|
||||
if (_dynamicsWorld) {
|
||||
_pendingFlags &= ~ (PENDING_FLAG_ADD_TO_SIMULATION | PENDING_FLAG_JUMP);
|
||||
_dynamicsWorld->addCollisionObject(getGhostObject(),
|
||||
if (world && _ghostObject) {
|
||||
_dynamicsWorld = world;
|
||||
_pendingFlags &= ~ PENDING_FLAG_JUMP;
|
||||
_dynamicsWorld->addCollisionObject(_ghostObject,
|
||||
btBroadphaseProxy::CharacterFilter,
|
||||
btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter);
|
||||
_dynamicsWorld->addAction(this);
|
||||
reset(_dynamicsWorld);
|
||||
}
|
||||
}
|
||||
if (_dynamicsWorld) {
|
||||
if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) {
|
||||
// shouldn't fall in here, but if we do make sure both ADD and REMOVE bits are still set
|
||||
_pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION | PENDING_FLAG_REMOVE_FROM_SIMULATION;
|
||||
} else {
|
||||
_pendingFlags &= ~ PENDING_FLAG_REMOVE_FROM_SIMULATION;
|
||||
_pendingFlags &= ~PENDING_FLAG_ADD_TO_SIMULATION;
|
||||
}
|
||||
} else {
|
||||
_pendingFlags &= ~ (PENDING_FLAG_REMOVE_FROM_SIMULATION | PENDING_FLAG_ADD_TO_SIMULATION);
|
||||
_pendingFlags &= ~ PENDING_FLAG_REMOVE_FROM_SIMULATION;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -772,10 +846,8 @@ void CharacterController::updateShapeIfNecessary() {
|
|||
_ghostObject->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()),
|
||||
glmToBullet(_avatarData->getPosition())));
|
||||
// stepHeight affects the heights of ledges that the character can ascend
|
||||
// however the actual ledge height is some function of _stepHeight
|
||||
// due to character shape and this CharacterController algorithm
|
||||
// (the function is approximately 2*_stepHeight)
|
||||
_stepHeight = 0.1f * (_radius + _halfHeight) + 0.1f;
|
||||
_stepUpHeight = _radius + 0.25f * _halfHeight + 0.1f;
|
||||
_stepDownHeight = _radius;
|
||||
|
||||
// create new shape
|
||||
_convexShape = new btCapsuleShape(_radius, 2.0f * _halfHeight);
|
||||
|
@ -799,19 +871,37 @@ void CharacterController::preSimulation(btScalar timeStep) {
|
|||
_pendingFlags &= ~ PENDING_FLAG_JUMP;
|
||||
if (canJump()) {
|
||||
_verticalVelocity = _jumpSpeed;
|
||||
_wasJumping = true;
|
||||
_isJumping = true;
|
||||
}
|
||||
}
|
||||
// remember last position so we can throttle the total motion from the next step
|
||||
_lastPosition = position;
|
||||
_stepDt = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterController::postSimulation() {
|
||||
if (_enabled) {
|
||||
if (_enabled && _ghostObject) {
|
||||
const btTransform& avatarTransform = _ghostObject->getWorldTransform();
|
||||
glm::quat rotation = bulletToGLM(avatarTransform.getRotation());
|
||||
glm::vec3 offset = rotation * _shapeLocalOffset;
|
||||
glm::vec3 position = bulletToGLM(avatarTransform.getOrigin());
|
||||
|
||||
// cap the velocity of the step so that the character doesn't POP! so hard on steps
|
||||
glm::vec3 finalStep = position - _lastPosition;
|
||||
btVector3 finalVelocity = _walkDirection;
|
||||
btVector3 up = quatRotate(_currentRotation, LOCAL_UP_AXIS);
|
||||
finalVelocity += _verticalVelocity * up;
|
||||
const btScalar MAX_RESOLUTION_SPEED = 5.0f; // m/sec
|
||||
btScalar maxStepLength = glm::max(MAX_RESOLUTION_SPEED, 2.0f * finalVelocity.length()) * _stepDt;
|
||||
btScalar stepLength = glm::length(finalStep);
|
||||
if (stepLength > maxStepLength) {
|
||||
position = _lastPosition + (maxStepLength / stepLength) * finalStep;
|
||||
// NOTE: we don't need to move ghostObject to throttled position unless
|
||||
// we want to support do async ray-traces/collision-queries against character
|
||||
}
|
||||
|
||||
_avatarData->setOrientation(rotation);
|
||||
_avatarData->setPosition(bulletToGLM(avatarTransform.getOrigin()) - offset);
|
||||
_avatarData->setPosition(position - rotation * _shapeLocalOffset);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,8 @@ protected:
|
|||
btScalar _maxSlopeCosine; // Cosine equivalent of _maxSlopeRadians (calculated once when set, for optimization)
|
||||
btScalar _gravity;
|
||||
|
||||
btScalar _stepHeight; // height of stepUp prior to stepForward
|
||||
btScalar _stepUpHeight; // height of stepUp prior to stepForward
|
||||
btScalar _stepDownHeight; // height of stepDown
|
||||
|
||||
btScalar _addedMargin;//@todo: remove this and fix the code
|
||||
|
||||
|
@ -71,6 +72,7 @@ protected:
|
|||
btVector3 _currentPosition;
|
||||
btQuaternion _currentRotation;
|
||||
btVector3 _targetPosition;
|
||||
glm::vec3 _lastPosition;
|
||||
btScalar _lastStepUp;
|
||||
|
||||
///keep track of the contact manifolds
|
||||
|
@ -80,9 +82,12 @@ protected:
|
|||
btVector3 _floorNormal; // points from object to character
|
||||
|
||||
bool _enabled;
|
||||
bool _wasOnGround;
|
||||
bool _wasJumping;
|
||||
bool _isOnGround;
|
||||
bool _isJumping;
|
||||
bool _isHovering;
|
||||
quint64 _jumpToHoverStart;
|
||||
btScalar _velocityTimeInterval;
|
||||
btScalar _stepDt;
|
||||
uint32_t _pendingFlags;
|
||||
|
||||
glm::vec3 _shapeLocalOffset;
|
||||
|
@ -95,6 +100,7 @@ protected:
|
|||
btVector3 perpindicularComponent(const btVector3& direction, const btVector3& normal);
|
||||
|
||||
bool recoverFromPenetration(btCollisionWorld* collisionWorld);
|
||||
void scanDown(btCollisionWorld* collisionWorld);
|
||||
void stepUp(btCollisionWorld* collisionWorld);
|
||||
void updateTargetPositionBasedOnCollision(const btVector3& hit_normal, btScalar tangentMag = btScalar(0.0), btScalar normalMag = btScalar(1.0));
|
||||
void stepForward(btCollisionWorld* collisionWorld, const btVector3& walkMove);
|
||||
|
@ -161,6 +167,7 @@ public:
|
|||
bool needsRemoval() const;
|
||||
bool needsAddition() const;
|
||||
void setEnabled(bool enabled);
|
||||
bool isEnabled() const { return _enabled; }
|
||||
void setDynamicsWorld(btDynamicsWorld* world);
|
||||
|
||||
void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale);
|
||||
|
|
|
@ -169,7 +169,9 @@ void EntityMotionState::updateObjectVelocities() {
|
|||
}
|
||||
|
||||
void EntityMotionState::computeShapeInfo(ShapeInfo& shapeInfo) {
|
||||
_entity->computeShapeInfo(shapeInfo);
|
||||
if (_entity->isReadyToComputeShape()) {
|
||||
_entity->computeShapeInfo(shapeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
float EntityMotionState::computeMass(const ShapeInfo& shapeInfo) const {
|
||||
|
|
|
@ -29,6 +29,12 @@ PhysicsEngine::PhysicsEngine(const glm::vec3& offset)
|
|||
|
||||
PhysicsEngine::~PhysicsEngine() {
|
||||
// TODO: delete engine components... if we ever plan to create more than one instance
|
||||
delete _collisionConfig;
|
||||
delete _collisionDispatcher;
|
||||
delete _broadphaseFilter;
|
||||
delete _constraintSolver;
|
||||
delete _dynamicsWorld;
|
||||
// delete _ghostPairCallback;
|
||||
}
|
||||
|
||||
// begin EntitySimulation overrides
|
||||
|
|
|
@ -29,6 +29,9 @@ int ShapeInfoUtil::toBulletShapeType(int shapeInfoType) {
|
|||
case SHAPE_TYPE_CONVEX_HULL:
|
||||
bulletShapeType = CONVEX_HULL_SHAPE_PROXYTYPE;
|
||||
break;
|
||||
case SHAPE_TYPE_COMPOUND:
|
||||
bulletShapeType = COMPOUND_SHAPE_PROXYTYPE;
|
||||
break;
|
||||
}
|
||||
return bulletShapeType;
|
||||
}
|
||||
|
@ -48,6 +51,9 @@ int ShapeInfoUtil::fromBulletShapeType(int bulletShapeType) {
|
|||
case CONVEX_HULL_SHAPE_PROXYTYPE:
|
||||
shapeInfoType = SHAPE_TYPE_CONVEX_HULL;
|
||||
break;
|
||||
case COMPOUND_SHAPE_PROXYTYPE:
|
||||
shapeInfoType = SHAPE_TYPE_COMPOUND;
|
||||
break;
|
||||
}
|
||||
return shapeInfoType;
|
||||
}
|
||||
|
@ -70,12 +76,34 @@ void ShapeInfoUtil::collectInfoFromShape(const btCollisionShape* shape, ShapeInf
|
|||
const btConvexHullShape* convexHullShape = static_cast<const btConvexHullShape*>(shape);
|
||||
const int numPoints = convexHullShape->getNumPoints();
|
||||
const btVector3* btPoints = convexHullShape->getUnscaledPoints();
|
||||
QVector<glm::vec3> points;
|
||||
QVector<QVector<glm::vec3>> points;
|
||||
QVector<glm::vec3> childPoints;
|
||||
for (int i = 0; i < numPoints; i++) {
|
||||
glm::vec3 point(btPoints->getX(), btPoints->getY(), btPoints->getZ());
|
||||
points << point;
|
||||
childPoints << point;
|
||||
}
|
||||
info.setConvexHull(points);
|
||||
points << childPoints;
|
||||
info.setConvexHulls(points);
|
||||
}
|
||||
break;
|
||||
case SHAPE_TYPE_COMPOUND: {
|
||||
const btCompoundShape* compoundShape = static_cast<const btCompoundShape*>(shape);
|
||||
const int numChildShapes = compoundShape->getNumChildShapes();
|
||||
QVector<QVector<glm::vec3>> points;
|
||||
for (int i = 0; i < numChildShapes; i ++) {
|
||||
const btCollisionShape* childShape = compoundShape->getChildShape(i);
|
||||
const btConvexHullShape* convexHullShape = static_cast<const btConvexHullShape*>(childShape);
|
||||
const int numPoints = convexHullShape->getNumPoints();
|
||||
const btVector3* btPoints = convexHullShape->getUnscaledPoints();
|
||||
|
||||
QVector<glm::vec3> childPoints;
|
||||
for (int j = 0; j < numPoints; j++) {
|
||||
glm::vec3 point(btPoints->getX(), btPoints->getY(), btPoints->getZ());
|
||||
childPoints << point;
|
||||
}
|
||||
points << childPoints;
|
||||
}
|
||||
info.setConvexHulls(points);
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
|
@ -108,12 +136,32 @@ btCollisionShape* ShapeInfoUtil::createShapeFromInfo(const ShapeInfo& info) {
|
|||
}
|
||||
break;
|
||||
case SHAPE_TYPE_CONVEX_HULL: {
|
||||
shape = new btConvexHullShape();
|
||||
const QVector<glm::vec3>& points = info.getPoints();
|
||||
foreach (glm::vec3 point, points) {
|
||||
auto hull = new btConvexHullShape();
|
||||
const QVector<QVector<glm::vec3>>& points = info.getPoints();
|
||||
foreach (glm::vec3 point, points[0]) {
|
||||
btVector3 btPoint(point[0], point[1], point[2]);
|
||||
static_cast<btConvexHullShape*>(shape)->addPoint(btPoint);
|
||||
hull->addPoint(btPoint, false);
|
||||
}
|
||||
hull->recalcLocalAabb();
|
||||
shape = hull;
|
||||
}
|
||||
break;
|
||||
case SHAPE_TYPE_COMPOUND: {
|
||||
auto compound = new btCompoundShape();
|
||||
const QVector<QVector<glm::vec3>>& points = info.getPoints();
|
||||
|
||||
btTransform trans;
|
||||
trans.setIdentity();
|
||||
foreach (QVector<glm::vec3> hullPoints, points) {
|
||||
auto hull = new btConvexHullShape();
|
||||
foreach (glm::vec3 point, hullPoints) {
|
||||
btVector3 btPoint(point[0], point[1], point[2]);
|
||||
hull->addPoint(btPoint, false);
|
||||
}
|
||||
hull->recalcLocalAabb();
|
||||
compound->addChildShape (trans, hull);
|
||||
}
|
||||
shape = compound;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
// translates between ShapeInfo and btShape
|
||||
|
||||
namespace ShapeInfoUtil {
|
||||
|
||||
// XXX is collectInfoFromShape no longer strictly needed?
|
||||
void collectInfoFromShape(const btCollisionShape* shape, ShapeInfo& info);
|
||||
|
||||
btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
#include "ShapeInfoUtil.h"
|
||||
|
@ -35,6 +37,7 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
|||
const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube
|
||||
const float MAX_SHAPE_DIAGONAL_SQUARED = 3.0e4f; // 100 m cube
|
||||
if (diagonal < MIN_SHAPE_DIAGONAL_SQUARED || diagonal > MAX_SHAPE_DIAGONAL_SQUARED) {
|
||||
// qDebug() << "ShapeManager::getShape -- not making shape due to size" << diagonal;
|
||||
return NULL;
|
||||
}
|
||||
DoubleHashKey key = info.getHash();
|
||||
|
@ -100,6 +103,18 @@ void ShapeManager::collectGarbage() {
|
|||
DoubleHashKey& key = _pendingGarbage[i];
|
||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||
if (shapeRef && shapeRef->refCount == 0) {
|
||||
// if the shape we're about to delete is compound, delete the children first.
|
||||
auto shapeType = ShapeInfoUtil::fromBulletShapeType(shapeRef->shape->getShapeType());
|
||||
if (shapeType == SHAPE_TYPE_COMPOUND) {
|
||||
const btCompoundShape* compoundShape = static_cast<const btCompoundShape*>(shapeRef->shape);
|
||||
const int numChildShapes = compoundShape->getNumChildShapes();
|
||||
QVector<QVector<glm::vec3>> points;
|
||||
for (int i = 0; i < numChildShapes; i ++) {
|
||||
const btCollisionShape* childShape = compoundShape->getChildShape(i);
|
||||
delete childShape;
|
||||
}
|
||||
}
|
||||
|
||||
delete shapeRef->shape;
|
||||
_shapeMap.remove(key);
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ void DeferredLightingEffect::addPointLight(const glm::vec3& position, float radi
|
|||
void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radius, const glm::vec3& color,
|
||||
float intensity, const glm::quat& orientation, float exponent, float cutoff) {
|
||||
|
||||
int lightID = _pointLights.size() + _spotLights.size() + _globalLights.size();
|
||||
unsigned int lightID = _pointLights.size() + _spotLights.size() + _globalLights.size();
|
||||
if (lightID >= _allocatedLights.size()) {
|
||||
_allocatedLights.push_back(model::LightPointer(new model::Light()));
|
||||
}
|
||||
|
|
|
@ -1771,8 +1771,8 @@ void GeometryCache::renderLine(const glm::vec2& p1, const glm::vec2& p2,
|
|||
}
|
||||
|
||||
|
||||
QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback, bool delayLoad, bool block) {
|
||||
return getResource(url, fallback, delayLoad, NULL, block).staticCast<NetworkGeometry>();
|
||||
QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback, bool delayLoad) {
|
||||
return getResource(url, fallback, delayLoad, NULL).staticCast<NetworkGeometry>();
|
||||
}
|
||||
|
||||
QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url,
|
||||
|
|
|
@ -203,8 +203,7 @@ public:
|
|||
/// Loads geometry from the specified URL.
|
||||
/// \param fallback a fallback URL to load if the desired one is unavailable
|
||||
/// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested
|
||||
QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url, const QUrl& fallback = QUrl(),
|
||||
bool delayLoad = false, bool block = true);
|
||||
QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false);
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
@ -325,6 +325,8 @@ void Model::init() {
|
|||
_skinTranslucentProgram = gpu::ShaderPointer(gpu::Shader::createProgram(skinModelVertex, modelTranslucentPixel));
|
||||
makeResult = gpu::Shader::makeProgram(*_skinTranslucentProgram, slotBindings);
|
||||
initSkinProgram(_skinTranslucentProgram, _skinTranslucentLocations);
|
||||
|
||||
(void) makeResult; // quiet compiler
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1032,12 +1034,22 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo
|
|||
}
|
||||
}
|
||||
|
||||
void Model::setCollisionModelURL(const QUrl& url, const QUrl& fallback, bool delayLoad) {
|
||||
|
||||
const QSharedPointer<NetworkGeometry> Model::getCollisionGeometry(bool delayLoad)
|
||||
{
|
||||
if (_collisionGeometry.isNull() && !_collisionUrl.isEmpty()) {
|
||||
_collisionGeometry = DependencyManager::get<GeometryCache>()->getGeometry(_collisionUrl, QUrl(), delayLoad);
|
||||
}
|
||||
|
||||
return _collisionGeometry;
|
||||
}
|
||||
|
||||
void Model::setCollisionModelURL(const QUrl& url) {
|
||||
if (_collisionUrl == url) {
|
||||
return;
|
||||
}
|
||||
_collisionUrl = url;
|
||||
_collisionGeometry = DependencyManager::get<GeometryCache>()->getGeometry(url, fallback, delayLoad);
|
||||
_collisionGeometry = DependencyManager::get<GeometryCache>()->getGeometry(url, QUrl(), true);
|
||||
}
|
||||
|
||||
bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {
|
||||
|
|
|
@ -109,7 +109,7 @@ public:
|
|||
const QUrl& getURL() const { return _url; }
|
||||
|
||||
// Set the model to use for collisions
|
||||
Q_INVOKABLE void setCollisionModelURL(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false);
|
||||
Q_INVOKABLE void setCollisionModelURL(const QUrl& url);
|
||||
const QUrl& getCollisionURL() const { return _collisionUrl; }
|
||||
|
||||
/// Sets the distance parameter used for LOD computations.
|
||||
|
@ -134,7 +134,7 @@ public:
|
|||
const QSharedPointer<NetworkGeometry>& getGeometry() const { return _geometry; }
|
||||
|
||||
/// Returns a reference to the shared collision geometry.
|
||||
const QSharedPointer<NetworkGeometry> getCollisionGeometry() {return _collisionGeometry; }
|
||||
const QSharedPointer<NetworkGeometry> getCollisionGeometry(bool delayLoad = true);
|
||||
|
||||
/// Returns the number of joint states in the model.
|
||||
int getJointStateCount() const { return _jointStates.size(); }
|
||||
|
|
|
@ -360,10 +360,6 @@ void ScriptEngine::init() {
|
|||
globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
|
||||
globalObject().setProperty("COLLISION_GROUP_ENVIRONMENT", newVariant(QVariant(COLLISION_GROUP_ENVIRONMENT)));
|
||||
globalObject().setProperty("COLLISION_GROUP_AVATARS", newVariant(QVariant(COLLISION_GROUP_AVATARS)));
|
||||
|
||||
globalObject().setProperty("AVATAR_MOTION_OBEY_LOCAL_GRAVITY", newVariant(QVariant(AVATAR_MOTION_OBEY_LOCAL_GRAVITY)));
|
||||
globalObject().setProperty("AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY", newVariant(QVariant(AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY)));
|
||||
|
||||
}
|
||||
|
||||
QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* object) {
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "XMLHttpRequestClass.h"
|
||||
#include "ScriptEngine.h"
|
||||
|
||||
const QString METAVERSE_API_URL = "https://metaverse.highfidelity.com/api/";
|
||||
|
||||
Q_DECLARE_METATYPE(QByteArray*)
|
||||
|
||||
XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) :
|
||||
|
@ -207,7 +209,7 @@ void XMLHttpRequestClass::open(const QString& method, const QString& url, bool a
|
|||
notImplemented();
|
||||
}
|
||||
} else {
|
||||
if (url.toLower().left(33) == "https://metaverse.highfidelity.com/api/") {
|
||||
if (url.toLower().left(METAVERSE_API_URL.length()) == METAVERSE_API_URL) {
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
if (accountManager.hasValidAccessToken()) {
|
||||
|
|
|
@ -23,6 +23,7 @@ void ShapeInfo::clear() {
|
|||
|
||||
void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) {
|
||||
_type = type;
|
||||
_points.clear();
|
||||
switch(type) {
|
||||
case SHAPE_TYPE_NONE:
|
||||
_halfExtents = glm::vec3(0.0f);
|
||||
|
@ -37,6 +38,12 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
|
|||
break;
|
||||
}
|
||||
case SHAPE_TYPE_CONVEX_HULL:
|
||||
_url = QUrl(url);
|
||||
// halfExtents aren't used by convex-hull or compound convex-hull except as part of
|
||||
// the generation of the key for the ShapeManager.
|
||||
_halfExtents = halfExtents;
|
||||
break;
|
||||
case SHAPE_TYPE_COMPOUND:
|
||||
_url = QUrl(url);
|
||||
_halfExtents = halfExtents;
|
||||
break;
|
||||
|
@ -47,31 +54,44 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
|
|||
}
|
||||
|
||||
void ShapeInfo::setBox(const glm::vec3& halfExtents) {
|
||||
_url = "";
|
||||
_type = SHAPE_TYPE_BOX;
|
||||
_halfExtents = halfExtents;
|
||||
_points.clear();
|
||||
_doubleHashKey.clear();
|
||||
}
|
||||
|
||||
void ShapeInfo::setSphere(float radius) {
|
||||
_url = "";
|
||||
_type = SHAPE_TYPE_SPHERE;
|
||||
_halfExtents = glm::vec3(radius, radius, radius);
|
||||
_points.clear();
|
||||
_doubleHashKey.clear();
|
||||
}
|
||||
|
||||
void ShapeInfo::setEllipsoid(const glm::vec3& halfExtents) {
|
||||
_url = "";
|
||||
_type = SHAPE_TYPE_ELLIPSOID;
|
||||
_halfExtents = halfExtents;
|
||||
_points.clear();
|
||||
_doubleHashKey.clear();
|
||||
}
|
||||
|
||||
void ShapeInfo::setConvexHull(const QVector<glm::vec3>& points) {
|
||||
_type = SHAPE_TYPE_CONVEX_HULL;
|
||||
void ShapeInfo::setConvexHulls(const QVector<QVector<glm::vec3>>& points) {
|
||||
if (points.size() == 1) {
|
||||
_type = SHAPE_TYPE_CONVEX_HULL;
|
||||
} else {
|
||||
_type = SHAPE_TYPE_COMPOUND;
|
||||
}
|
||||
_points = points;
|
||||
_doubleHashKey.clear();
|
||||
}
|
||||
|
||||
void ShapeInfo::setCapsuleY(float radius, float halfHeight) {
|
||||
_url = "";
|
||||
_type = SHAPE_TYPE_CAPSULE_Y;
|
||||
_halfExtents = glm::vec3(radius, halfHeight, radius);
|
||||
_points.clear();
|
||||
_doubleHashKey.clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -44,14 +44,14 @@ public:
|
|||
void setBox(const glm::vec3& halfExtents);
|
||||
void setSphere(float radius);
|
||||
void setEllipsoid(const glm::vec3& halfExtents);
|
||||
void setConvexHull(const QVector<glm::vec3>& points);
|
||||
void setConvexHulls(const QVector<QVector<glm::vec3>>& points);
|
||||
void setCapsuleY(float radius, float halfHeight);
|
||||
|
||||
const int getType() const { return _type; }
|
||||
|
||||
const glm::vec3& getHalfExtents() const { return _halfExtents; }
|
||||
|
||||
const QVector<glm::vec3>& getPoints() const { return _points; }
|
||||
const QVector<QVector<glm::vec3>>& getPoints() const { return _points; }
|
||||
|
||||
void clearPoints () { _points.clear(); }
|
||||
void appendToPoints (const QVector<glm::vec3>& newPoints) { _points << newPoints; }
|
||||
|
@ -64,8 +64,8 @@ protected:
|
|||
ShapeType _type = SHAPE_TYPE_NONE;
|
||||
glm::vec3 _halfExtents = glm::vec3(0.0f);
|
||||
DoubleHashKey _doubleHashKey;
|
||||
QVector<glm::vec3> _points; // points for convex collision hull
|
||||
QUrl _url; // url for model of convex collision hull
|
||||
QVector<QVector<glm::vec3>> _points; // points for convex collision hulls
|
||||
QUrl _url; // url for model of convex collision hulls
|
||||
};
|
||||
|
||||
#endif // hifi_ShapeInfo_h
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
SimpleMovingAverage::SimpleMovingAverage(int numSamplesToAverage) :
|
||||
_numSamples(0),
|
||||
_lastEventTimestamp(0),
|
||||
_average(0.0f),
|
||||
_eventDeltaAverage(0.0f),
|
||||
WEIGHTING(1.0f / numSamplesToAverage),
|
||||
|
|
|
@ -8,5 +8,5 @@ set_target_properties(scribe PROPERTIES FOLDER "Tools")
|
|||
find_package(VHACD)
|
||||
if(VHACD_FOUND)
|
||||
add_subdirectory(vhacd)
|
||||
set_target_properties(vhacd PROPERTIES FOLDER "Tools")
|
||||
# set_target_properties(vhacd PROPERTIES FOLDER "Tools")
|
||||
endif()
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
rm -f TAGS
|
||||
|
||||
find . -name *.h -print | while read I
|
||||
find . -name *.h -print | grep -v build-ext |while read I
|
||||
do
|
||||
etags --append "$I"
|
||||
done
|
||||
|
||||
|
||||
find . -name *.cpp -print | grep -v 'moc_' | while read I
|
||||
find . -name *.cpp -print | grep -v 'moc_' | grep -v build-ext | while read I
|
||||
do
|
||||
etags --append "$I"
|
||||
done
|
||||
|
|
Loading…
Reference in a new issue