Merge branch 'master' of https://github.com/worklist/hifi into aacube

This commit is contained in:
ZappoMan 2014-05-27 13:39:45 -07:00
commit c63c0c0ce3
37 changed files with 905 additions and 216 deletions

View file

@ -213,6 +213,8 @@ void Agent::run() {
loop.exec();
// let the AvatarData and ResourceCache classes use our QNetworkAccessManager
AvatarData::setNetworkAccessManager(networkManager);
ResourceCache::setNetworkAccessManager(networkManager);

View file

@ -735,7 +735,7 @@ void DomainServer::setupPendingAssignmentCredits() {
const float CREDITS_PER_HOUR = 0.10f;
const float CREDITS_PER_MSEC = CREDITS_PER_HOUR / (60 * 60 * 1000);
const int SATOSHIS_PER_MSEC = CREDITS_PER_MSEC * powf(10.0f, 8.0f);
const int SATOSHIS_PER_MSEC = CREDITS_PER_MSEC * SATOSHIS_PER_CREDIT;
float pendingCredits = elapsedMsecsSinceLastPayment * SATOSHIS_PER_MSEC;
@ -884,10 +884,10 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) {
if (!nodeData->getWalletUUID().isNull()) {
TransactionHash::iterator i = _pendingAssignmentCredits.find(nodeData->getWalletUUID());
double pendingCreditAmount = 0;
float pendingCreditAmount = 0;
while (i != _pendingAssignmentCredits.end() && i.key() == nodeData->getWalletUUID()) {
pendingCreditAmount += i.value()->getAmount();
pendingCreditAmount += i.value()->getAmount() / SATOSHIS_PER_CREDIT;
++i;
}
@ -1004,6 +1004,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
return true;
} else {
// check if this is for json stats for a node
const QString NODE_JSON_REGEX_STRING = QString("\\%1\\/(%2).json\\/?$").arg(URI_NODES).arg(UUID_REGEX_STRING);
QRegExp nodeShowRegex(NODE_JSON_REGEX_STRING);
@ -1028,6 +1029,40 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
// tell the caller we processed the request
return true;
}
return false;
}
// check if this is a request for a scripted assignment (with a temp unique UUID)
const QString ASSIGNMENT_REGEX_STRING = QString("\\%1\\/(%2)\\/?$").arg(URI_ASSIGNMENT).arg(UUID_REGEX_STRING);
QRegExp assignmentRegex(ASSIGNMENT_REGEX_STRING);
if (assignmentRegex.indexIn(url.path()) != -1) {
QUuid matchingUUID = QUuid(assignmentRegex.cap(1));
SharedAssignmentPointer matchingAssignment = _allAssignments.value(matchingUUID);
if (!matchingAssignment) {
// check if we have a pending assignment that matches this temp UUID, and it is a scripted assignment
PendingAssignedNodeData* pendingData = _pendingAssignedNodes.value(matchingUUID);
if (pendingData) {
matchingAssignment = _allAssignments.value(pendingData->getAssignmentUUID());
if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) {
// we have a matching assignment and it is for the right type, have the HTTP manager handle it
// via correct URL for the script so the client can download
QUrl scriptURL = url;
scriptURL.setPath(URI_ASSIGNMENT + "/"
+ uuidStringWithoutCurlyBraces(pendingData->getAssignmentUUID()));
// have the HTTPManager serve the appropriate script file
return _httpManager.handleHTTPRequest(connection, scriptURL);
}
}
}
// request not handled
return false;
}
}
} else if (connection->requestOperation() == QNetworkAccessManager::PostOperation) {

View file

@ -18,13 +18,13 @@
WalletTransaction::WalletTransaction() :
_uuid(),
_destinationUUID(),
_amount(),
_amount(0),
_isFinalized(false)
{
}
WalletTransaction::WalletTransaction(const QUuid& destinationUUID, double amount) :
WalletTransaction::WalletTransaction(const QUuid& destinationUUID, qint64 amount) :
_uuid(QUuid::createUuid()),
_destinationUUID(destinationUUID),
_amount(amount),
@ -63,5 +63,5 @@ void WalletTransaction::loadFromJson(const QJsonObject& jsonObject) {
_uuid = QUuid(transactionObject.value(TRANSACTION_ID_KEY).toString());
_destinationUUID = QUuid(transactionObject.value(TRANSACTION_DESTINATION_WALLET_ID_KEY).toString());
_amount = transactionObject.value(TRANSACTION_AMOUNT_KEY).toDouble();
_amount = transactionObject.value(TRANSACTION_AMOUNT_KEY).toInt();
}

View file

@ -19,16 +19,16 @@
class WalletTransaction : public QObject {
public:
WalletTransaction();
WalletTransaction(const QUuid& destinationUUID, double amount);
WalletTransaction(const QUuid& destinationUUID, qint64 amount);
const QUuid& getUUID() const { return _uuid; }
void setDestinationUUID(const QUuid& destinationUUID) { _destinationUUID = destinationUUID; }
const QUuid& getDestinationUUID() const { return _destinationUUID; }
double getAmount() const { return _amount; }
void setAmount(double amount) { _amount = amount; }
void incrementAmount(double increment) { _amount += increment; }
qint64 getAmount() const { return _amount; }
void setAmount(qint64 amount) { _amount = amount; }
void incrementAmount(qint64 increment) { _amount += increment; }
bool isFinalized() const { return _isFinalized; }
void setIsFinalized(bool isFinalized) { _isFinalized = isFinalized; }
@ -39,7 +39,7 @@ public:
private:
QUuid _uuid;
QUuid _destinationUUID;
double _amount;
qint64 _amount;
bool _isFinalized;
};

View file

@ -479,6 +479,7 @@ function mousePressEvent(event) {
var pickRay = Camera.computePickRay(event.x, event.y);
Vec3.print("[Mouse] Looking at: ", pickRay.origin);
var foundModels = Models.findModels(pickRay.origin, LASER_LENGTH_FACTOR);
var closest = -1.0;
for (var i = 0; i < foundModels.length; i++) {
if (!foundModels[i].isKnownID) {
var identify = Models.identifyModel(foundModels[i]);
@ -514,35 +515,51 @@ function mousePressEvent(event) {
var d = Vec3.length(Vec3.subtract(P, X));
if (d < properties.radius && 0 < x && x < LASER_LENGTH_FACTOR) {
modelSelected = true;
selectedModelID = foundModels[i];
selectedModelProperties = properties;
selectedModelProperties.oldRadius = selectedModelProperties.radius;
selectedModelProperties.oldPosition = {
x: selectedModelProperties.position.x,
y: selectedModelProperties.position.y,
z: selectedModelProperties.position.z,
};
selectedModelProperties.oldRotation = {
x: selectedModelProperties.modelRotation.x,
y: selectedModelProperties.modelRotation.y,
z: selectedModelProperties.modelRotation.z,
w: selectedModelProperties.modelRotation.w,
};
orientation = MyAvatar.orientation;
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
print("Clicked on " + selectedModelID.id + " " + modelSelected);
return;
if (closest < 0.0) {
closest = x;
}
if (x <= closest) {
modelSelected = true;
selectedModelID = foundModels[i];
selectedModelProperties = properties;
orientation = MyAvatar.orientation;
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
}
}
}
}
if (modelSelected) {
selectedModelProperties.oldRadius = selectedModelProperties.radius;
selectedModelProperties.oldPosition = {
x: selectedModelProperties.position.x,
y: selectedModelProperties.position.y,
z: selectedModelProperties.position.z,
};
selectedModelProperties.oldRotation = {
x: selectedModelProperties.modelRotation.x,
y: selectedModelProperties.modelRotation.y,
z: selectedModelProperties.modelRotation.z,
w: selectedModelProperties.modelRotation.w,
};
selectedModelProperties.glowLevel = 0.1;
Models.editModel(selectedModelID, { glowLevel: selectedModelProperties.glowLevel});
print("Clicked on " + selectedModelID.id + " " + modelSelected);
}
}
}
Controller.mouseReleaseEvent.connect(function() {
if (modelSelected) {
Models.editModel(selectedModelID, { glowLevel: 0.0 });
modelSelected = false;
}
});
var oldModifier = 0;
var modifier = 0;
var wasShifted = false;

View file

@ -78,7 +78,7 @@ function createDebugOverlay() {
position: defaultPosition,
size: RADIUS,
color: GRAY_COLOR,
alpha: 1,
alpha: 0.75,
visible: true,
solid: true,
anchor: "MyAvatar"
@ -87,7 +87,7 @@ function createDebugOverlay() {
position: defaultPosition,
size: RADIUS,
color: RED_COLOR,
alpha: 1,
alpha: 0.5,
visible: true,
solid: true,
anchor: "MyAvatar"
@ -111,7 +111,7 @@ function displayDebug() {
}
} else {
// update debug indicator
if (greenSphere == -1) {
if (greenSphere == -1) {
createDebugOverlay();
}
@ -149,8 +149,8 @@ function getGrabRotation() {
// When move button is pressed, process results
function handleGrabBehavior(deltaTime) {
// check for and handle grab behaviors
grabbingWithRightHand = Controller.isButtonPressed(RIGHT_BUTTON_FWD) || Controller.isButtonPressed(RIGHT_BUTTON_4);
grabbingWithLeftHand = Controller.isButtonPressed(LEFT_BUTTON_FWD) || Controller.isButtonPressed(LEFT_BUTTON_4);
grabbingWithRightHand = Controller.isButtonPressed(RIGHT_BUTTON_4);
grabbingWithLeftHand = Controller.isButtonPressed(LEFT_BUTTON_4);
stoppedGrabbingWithLeftHand = false;
stoppedGrabbingWithRightHand = false;
@ -201,20 +201,22 @@ function handleGrabBehavior(deltaTime) {
printVector("grabDelta: ", grabDelta, 3);
}
var THRUST_GRAB_SCALING = 300000.0;
var thrust = Vec3.multiply(grabDelta, Math.abs(Vec3.length(grabDelta)));
var THRUST_GRAB_SCALING = 100000.0;
var thrustFront = Vec3.multiply(front, MyAvatar.scale * -grabDelta.z * THRUST_GRAB_SCALING * deltaTime);
var thrustFront = Vec3.multiply(front, MyAvatar.scale * -thrust.z * THRUST_GRAB_SCALING * deltaTime);
MyAvatar.addThrust(thrustFront);
var thrustRight = Vec3.multiply(right, MyAvatar.scale * grabDelta.x * THRUST_GRAB_SCALING * deltaTime);
var thrustRight = Vec3.multiply(right, MyAvatar.scale * thrust.x * THRUST_GRAB_SCALING * deltaTime);
MyAvatar.addThrust(thrustRight);
var thrustUp = Vec3.multiply(up, MyAvatar.scale * grabDelta.y * THRUST_GRAB_SCALING * deltaTime);
var thrustUp = Vec3.multiply(up, MyAvatar.scale * thrust.y * THRUST_GRAB_SCALING * deltaTime);
MyAvatar.addThrust(thrustUp);
// add some rotation...
var deltaRotation = getGrabRotation();
var PITCH_SCALING = 2.0;
var PITCH_SCALING = 2.5;
var PITCH_DEAD_ZONE = 2.0;
var YAW_SCALING = 2.0;
var YAW_SCALING = 2.5;
var ROLL_SCALING = 2.0;
var euler = Quat.safeEulerAngles(deltaRotation);

View file

@ -3,22 +3,22 @@
// examples
//
// Copyright 2014 High Fidelity, Inc.
// This sample script loads a sound file and plays it at the 'fingertip' of the
// Plays a sample audio file at the avatar's current location
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// First, load the clap sound from a URL
var clap = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw");
// First, load a sample sound from a URL
var bird = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw");
function maybePlaySound(deltaTime) {
if (Math.random() < 0.01) {
// Set the location and other info for the sound to play
var options = new AudioInjectionOptions();
var palmPosition = Controller.getSpatialControlPosition(0);
options.position = palmPosition;
var position = MyAvatar.position;
options.position = position;
options.volume = 0.5;
Audio.playSound(clap, options);
Audio.playSound(bird, options);
}
}

View file

@ -31,6 +31,9 @@ void main(void) {
// and the texture coordinates
gl_TexCoord[0] = gl_MultiTexCoord0;
// and the shadow texture coordinates
gl_TexCoord[1] = vec4(dot(gl_EyePlaneS[0], position), dot(gl_EyePlaneT[0], position), dot(gl_EyePlaneR[0], position), 1.0);
// use standard pipeline transform
gl_Position = ftransform();
}

View file

@ -36,6 +36,10 @@ void main(void) {
// and the texture coordinates
gl_TexCoord[0] = gl_MultiTexCoord0;
// and the shadow texture coordinates
gl_TexCoord[1] = vec4(dot(gl_EyePlaneS[0], interpolatedPosition), dot(gl_EyePlaneT[0], interpolatedPosition),
dot(gl_EyePlaneR[0], interpolatedPosition), 1.0);
// use standard pipeline transform
gl_Position = ftransform();
}

View file

@ -0,0 +1,48 @@
#version 120
//
// model_shadow_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 5/23/14.
// 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
//
// the diffuse texture
uniform sampler2D diffuseMap;
// the shadow texture
uniform sampler2DShadow shadowMap;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
// the interpolated position
varying vec4 position;
// the interpolated normal
varying vec4 normal;
void main(void) {
// compute the base color based on OpenGL lighting model
vec4 normalizedNormal = normalize(normal);
float diffuse = dot(normalizedNormal, gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r);
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
// compute the specular component (sans exponent)
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - normalize(vec4(position.xyz, 0.0))),
normalizedNormal));
// modulate texture by base color and add specular contribution
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) +
vec4(pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular.rgb, 0.0);
}

View file

@ -0,0 +1,60 @@
#version 120
//
// model_shadow_normal_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 5/23/14.
// 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
//
// the diffuse texture
uniform sampler2D diffuseMap;
// the normal map texture
uniform sampler2D normalMap;
// the shadow texture
uniform sampler2DShadow shadowMap;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
// the interpolated position
varying vec4 interpolatedPosition;
// the interpolated normal
varying vec4 interpolatedNormal;
// the interpolated tangent
varying vec4 interpolatedTangent;
void main(void) {
vec3 normalizedNormal = normalize(vec3(interpolatedNormal));
vec3 normalizedTangent = normalize(vec3(interpolatedTangent));
vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent));
vec3 localNormal = vec3(texture2D(normalMap, gl_TexCoord[0].st)) * 2.0 - vec3(1.0, 1.0, 1.0);
// compute the base color based on OpenGL lighting model
vec4 viewNormal = vec4(normalizedTangent * localNormal.x +
normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0);
float diffuse = dot(viewNormal, gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r);
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
// compute the specular component (sans exponent)
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position -
normalize(vec4(vec3(interpolatedPosition), 0.0))), viewNormal));
// modulate texture by base color and add specular contribution
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) +
vec4(pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular.rgb, 0.0);
}

View file

@ -0,0 +1,63 @@
#version 120
//
// model_shadow_normal_specular_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 5/23/14.
// 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
//
// the diffuse texture
uniform sampler2D diffuseMap;
// the normal map texture
uniform sampler2D normalMap;
// the specular map texture
uniform sampler2D specularMap;
// the shadow texture
uniform sampler2DShadow shadowMap;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
// the interpolated position
varying vec4 interpolatedPosition;
// the interpolated normal
varying vec4 interpolatedNormal;
// the interpolated tangent
varying vec4 interpolatedTangent;
void main(void) {
vec3 normalizedNormal = normalize(vec3(interpolatedNormal));
vec3 normalizedTangent = normalize(vec3(interpolatedTangent));
vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent));
vec3 localNormal = vec3(texture2D(normalMap, gl_TexCoord[0].st)) * 2.0 - vec3(1.0, 1.0, 1.0);
// compute the base color based on OpenGL lighting model
vec4 viewNormal = vec4(normalizedTangent * localNormal.x +
normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0);
float diffuse = dot(viewNormal, gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r);
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
// compute the specular component (sans exponent)
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position -
normalize(vec4(interpolatedPosition.xyz, 0.0))), viewNormal));
// modulate texture by base color and add specular contribution
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + vec4(pow(specular, gl_FrontMaterial.shininess) *
gl_FrontLightProduct[0].specular.rgb * texture2D(specularMap, gl_TexCoord[0].st).rgb, 0.0);
}

View file

@ -0,0 +1,51 @@
#version 120
//
// model_shadow_specular_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 5/23/14.
// 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
//
// the diffuse texture
uniform sampler2D diffuseMap;
// the specular texture
uniform sampler2D specularMap;
// the shadow texture
uniform sampler2DShadow shadowMap;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
// the interpolated position in view space
varying vec4 position;
// the interpolated normal
varying vec4 normal;
void main(void) {
// compute the base color based on OpenGL lighting model
vec4 normalizedNormal = normalize(normal);
float diffuse = dot(normalizedNormal, gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r);
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
// compute the specular component (sans exponent)
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - normalize(vec4(position.xyz, 0.0))),
normalizedNormal));
// modulate texture by base color and add specular contribution
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + vec4(pow(specular, gl_FrontMaterial.shininess) *
gl_FrontLightProduct[0].specular.rgb * texture2D(specularMap, gl_TexCoord[0].st).rgb, 0.0);
}

View file

@ -43,5 +43,8 @@ void main(void) {
// and the texture coordinates
gl_TexCoord[0] = gl_MultiTexCoord0;
// and the shadow texture coordinates
gl_TexCoord[1] = vec4(dot(gl_EyePlaneS[0], position), dot(gl_EyePlaneT[0], position), dot(gl_EyePlaneR[0], position), 1.0);
gl_Position = gl_ProjectionMatrix * position;
}

View file

@ -52,5 +52,9 @@ void main(void) {
// and the texture coordinates
gl_TexCoord[0] = gl_MultiTexCoord0;
// and the shadow texture coordinates
gl_TexCoord[1] = vec4(dot(gl_EyePlaneS[0], interpolatedPosition), dot(gl_EyePlaneT[0], interpolatedPosition),
dot(gl_EyePlaneR[0], interpolatedPosition), 1.0);
gl_Position = gl_ProjectionMatrix * interpolatedPosition;
}

View file

@ -172,7 +172,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_nodeBoundsDisplay(this),
_previousScriptLocation(),
_runningScriptsWidget(new RunningScriptsWidget(_window)),
_runningScriptsWidgetWasVisible(false)
_runningScriptsWidgetWasVisible(false),
_trayIcon(new QSystemTrayIcon(_window))
{
// read the ApplicationInfo.ini file for Name/Version/Domain information
QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat);
@ -242,7 +243,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
QTimer* locationUpdateTimer = new QTimer(this);
connect(locationUpdateTimer, &QTimer::timeout, this, &Application::updateLocationInServer);
locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS);
connect(nodeList, &NodeList::nodeAdded, this, &Application::nodeAdded);
connect(nodeList, &NodeList::nodeKilled, this, &Application::nodeKilled);
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer)));
@ -251,16 +252,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle);
connect(nodeList, SIGNAL(uuidChanged(const QUuid&)), _myAvatar, SLOT(setSessionUUID(const QUuid&)));
connect(nodeList, &NodeList::limitOfSilentDomainCheckInsReached, nodeList, &NodeList::reset);
// connect to appropriate slots on AccountManager
AccountManager& accountManager = AccountManager::getInstance();
const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * 1000;
QTimer* balanceUpdateTimer = new QTimer(this);
connect(balanceUpdateTimer, &QTimer::timeout, &accountManager, &AccountManager::updateBalance);
balanceUpdateTimer->start(BALANCE_UPDATE_INTERVAL_MSECS);
connect(&accountManager, &AccountManager::balanceChanged, this, &Application::updateWindowTitle);
connect(&accountManager, &AccountManager::authRequired, Menu::getInstance(), &Menu::loginForCurrentDomain);
@ -391,6 +392,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
OAuthWebViewHandler::getInstance();
// make sure the High Fidelity root CA is in our list of trusted certs
OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig();
_trayIcon->show();
}
Application::~Application() {
@ -562,7 +565,7 @@ void Application::paintGL() {
PerformanceWarning warn(showWarnings, "Application::paintGL()");
glEnable(GL_LINE_SMOOTH);
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
_myCamera.setTightness(0.0f); // In first person, camera follows (untweaked) head exactly without delay
_myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
@ -2325,10 +2328,15 @@ void Application::updateShadowMap() {
// store view matrix without translation, which we'll use for precision-sensitive objects
updateUntranslatedViewMatrix();
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.1f, 4.0f); // magic numbers courtesy http://www.eecs.berkeley.edu/~ravir/6160/papers/shadowmaps.ppt
_avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE);
_particles.render(OctreeRenderer::SHADOW_RENDER_MODE);
_models.render(OctreeRenderer::SHADOW_RENDER_MODE);
glDisable(GL_POLYGON_OFFSET_FILL);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
@ -2534,6 +2542,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
if (_voxelFades.size() > 0) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... voxel fades...");
_voxelFadesLock.lockForWrite();
for(std::vector<VoxelFade>::iterator fade = _voxelFades.begin(); fade != _voxelFades.end();) {
fade->render();
if(fade->isDone()) {
@ -2542,6 +2551,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
++fade;
}
}
_voxelFadesLock.unlock();
}
// give external parties a change to hook in
@ -3103,18 +3113,18 @@ void Application::updateWindowTitle(){
QString username = AccountManager::getInstance().getAccountInfo().getUsername();
QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())
+ nodeList->getDomainHandler().getHostname() + buildVersion;
AccountManager& accountManager = AccountManager::getInstance();
if (accountManager.getAccountInfo().hasBalance()) {
float creditBalance = accountManager.getAccountInfo().getBalance() * pow(10.0f, -8.0f);
float creditBalance = accountManager.getAccountInfo().getBalance() / SATOSHIS_PER_CREDIT;
QString creditBalanceString;
creditBalanceString.sprintf("%.8f", creditBalance);
title += " - ₵" + creditBalanceString;
}
qDebug("Application title set to: %s", title.toStdString().c_str());
qDebug("Application title set to: %s", title.toStdString().c_str());
_window->setWindowTitle(title);
}
@ -3208,7 +3218,9 @@ void Application::nodeKilled(SharedNodePointer node) {
fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99f;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFadesLock.lockForWrite();
_voxelFades.push_back(fade);
_voxelFadesLock.unlock();
}
// If the voxel server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server
@ -3239,7 +3251,9 @@ void Application::nodeKilled(SharedNodePointer node) {
fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99f;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFadesLock.lockForWrite();
_voxelFades.push_back(fade);
_voxelFadesLock.unlock();
}
// If the particle server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server
@ -3271,7 +3285,9 @@ void Application::nodeKilled(SharedNodePointer node) {
fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99f;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFadesLock.lockForWrite();
_voxelFades.push_back(fade);
_voxelFadesLock.unlock();
}
// If the model server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server
@ -3356,7 +3372,9 @@ int Application::parseOctreeStats(const QByteArray& packet, const SharedNodePoin
fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99f;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFadesLock.lockForWrite();
_voxelFades.push_back(fade);
_voxelFadesLock.unlock();
}
}
// store jurisdiction details for later use

View file

@ -28,6 +28,7 @@
#include <QHash>
#include <QTouchEvent>
#include <QUndoStack>
#include <QSystemTrayIcon>
#include <ModelEditPacketSender.h>
#include <NetworkPacket.h>
@ -203,6 +204,7 @@ public:
JoystickManager* getJoystickManager() { return &_joystickManager; }
BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; }
QUndoStack* getUndoStack() { return &_undoStack; }
QSystemTrayIcon* getTrayIcon() { return _trayIcon; }
/// if you need to access the application settings, use lockSettings()/unlockSettings()
QSettings* lockSettings() { _settingsMutex.lock(); return _settings; }
@ -467,7 +469,7 @@ private:
glm::mat4 _untranslatedViewMatrix;
glm::vec3 _viewMatrixTranslation;
glm::mat4 _projectionMatrix;
float _scaleMirror;
float _rotateMirror;
float _raiseMirror;
@ -533,6 +535,7 @@ private:
NodeBounds _nodeBoundsDisplay;
std::vector<VoxelFade> _voxelFades;
QReadWriteLock _voxelFadesLock;
ControllerScriptingInterface _controllerScriptingInterface;
QPointer<LogDialog> _logDialog;
QPointer<SnapshotShareDialog> _snapshotShareDialog;
@ -554,6 +557,8 @@ private:
RunningScriptsWidget* _runningScriptsWidget;
QHash<QString, ScriptEngine*> _scriptEnginesHash;
bool _runningScriptsWidgetWasVisible;
QSystemTrayIcon* _trayIcon;
};
#endif // hifi_Application_h

View file

@ -34,6 +34,7 @@
#include <UUID.h>
#include "Application.h"
#include "AccountManager.h"
#include "Menu.h"
#include "scripting/MenuScriptingInterface.h"
#include "Util.h"
@ -195,12 +196,13 @@ Menu::Menu() :
addActionToQMenuAndActionHash(editMenu, MenuOption::Attachments, 0, this, SLOT(editAttachments()));
addActionToQMenuAndActionHash(editMenu, MenuOption::Animations, 0, this, SLOT(editAnimations()));
addDisabledActionAndSeparator(editMenu, "Physics");
QObject* avatar = appInstance->getAvatar();
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, false,
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, false,
avatar, SLOT(updateMotionBehaviorsFromMenu()));
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::StandOnNearbyFloors, 0, true,
avatar, SLOT(updateMotionBehaviorsFromMenu()));
addAvatarCollisionSubMenu(editMenu);
@ -331,6 +333,7 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::AllowOculusCameraModeChange, 0, false);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::AvatarsReceiveShadows, 0, true);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderSkeletonCollisionShapes);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderHeadCollisionShapes);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderBoundingCollisionShapes);
@ -1025,24 +1028,24 @@ void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJson
void Menu::muteEnvironment() {
int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment);
int packetSize = headerSize + sizeof(glm::vec3) + sizeof(float);
glm::vec3 position = Application::getInstance()->getAvatar()->getPosition();
char* packet = (char*)malloc(packetSize);
populatePacketHeader(packet, PacketTypeMuteEnvironment);
memcpy(packet + headerSize, &position, sizeof(glm::vec3));
memcpy(packet + headerSize + sizeof(glm::vec3), &MUTE_RADIUS, sizeof(float));
QByteArray mutePacket(packet, packetSize);
// grab our audio mixer from the NodeList, if it exists
SharedNodePointer audioMixer = NodeList::getInstance()->soloNodeOfType(NodeType::AudioMixer);
if (audioMixer) {
// send off this mute packet
NodeList::getInstance()->writeDatagram(mutePacket, audioMixer);
}
free(packet);
}
@ -1200,19 +1203,24 @@ void Menu::showScriptEditor() {
}
void Menu::showChat() {
QMainWindow* mainWindow = Application::getInstance()->getWindow();
if (!_chatWindow) {
_chatWindow = new ChatWindow(mainWindow);
}
if (_chatWindow->isHidden()) {
_chatWindow->show();
if (AccountManager::getInstance().isLoggedIn()) {
QMainWindow* mainWindow = Application::getInstance()->getWindow();
if (!_chatWindow) {
_chatWindow = new ChatWindow(mainWindow);
}
if (_chatWindow->isHidden()) {
_chatWindow->show();
}
} else {
Application::getInstance()->getTrayIcon()->showMessage("Interface", "You need to login to be able to chat with others on this domain.");
}
}
void Menu::toggleChat() {
#ifdef HAVE_QXMPP
_chatAction->setEnabled(XmppClient::getInstance().getXMPPClient().isConnected());
if (!_chatAction->isEnabled() && _chatWindow) {
if (!_chatAction->isEnabled() && _chatWindow && AccountManager::getInstance().isLoggedIn()) {
if (_chatWindow->isHidden()) {
_chatWindow->show();
} else {

View file

@ -293,6 +293,7 @@ namespace MenuOption {
const QString AudioSpatialProcessingDontDistanceAttenuate = "Don't calculate distance attenuation";
const QString AudioSpatialProcessingAlternateDistanceAttenuate = "Alternate distance attenuation";
const QString Avatars = "Avatars";
const QString AvatarsReceiveShadows = "Avatars Receive Shadows";
const QString Bandwidth = "Bandwidth Display";
const QString BandwidthDetails = "Bandwidth Details";
const QString BuckyBalls = "Bucky Balls";
@ -376,6 +377,7 @@ namespace MenuOption {
const QString ShowBordersModelNodes = "Show Model Nodes";
const QString ShowBordersParticleNodes = "Show Particle Nodes";
const QString ShowIKConstraints = "Show IK Constraints";
const QString StandOnNearbyFloors = "Stand on nearby floors";
const QString Stars = "Stars";
const QString Stats = "Stats";
const QString StopAllScripts = "Stop All Scripts";

View file

@ -347,7 +347,6 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
void Avatar::renderBody(RenderMode renderMode, float glowLevel) {
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
{
Glower glower(glowLevel);
@ -356,7 +355,8 @@ void Avatar::renderBody(RenderMode renderMode, float glowLevel) {
renderBillboard();
return;
}
_skeletonModel.render(1.0f, modelRenderMode);
_skeletonModel.render(1.0f, modelRenderMode, Menu::getInstance()->isOptionChecked(MenuOption::AvatarsReceiveShadows));
renderAttachments(renderMode);
getHand()->render(false, modelRenderMode);
}
@ -390,8 +390,9 @@ void Avatar::simulateAttachments(float deltaTime) {
void Avatar::renderAttachments(RenderMode renderMode) {
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
bool receiveShadows = Menu::getInstance()->isOptionChecked(MenuOption::AvatarsReceiveShadows);
foreach (Model* model, _attachmentModels) {
model->render(1.0f, modelRenderMode);
model->render(1.0f, modelRenderMode, receiveShadows);
}
}

View file

@ -176,7 +176,8 @@ void Head::relaxLean(float deltaTime) {
}
void Head::render(float alpha, Model::RenderMode mode) {
if (_faceModel.render(alpha, mode) && _renderLookatVectors && mode != Model::SHADOW_RENDER_MODE) {
if (_faceModel.render(alpha, mode, Menu::getInstance()->isOptionChecked(MenuOption::AvatarsReceiveShadows)) &&
_renderLookatVectors && mode != Model::SHADOW_RENDER_MODE) {
renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition);
}
}

View file

@ -65,6 +65,7 @@ MyAvatar::MyAvatar() :
_distanceToNearestAvatar(std::numeric_limits<float>::max()),
_wasPushing(false),
_isPushing(false),
_isBraking(false),
_trapDuration(0.0f),
_thrust(0.0f),
_motorVelocity(0.0f),
@ -134,45 +135,7 @@ void MyAvatar::simulate(float deltaTime) {
_handState = HAND_STATE_NULL;
updateOrientation(deltaTime);
float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) +
fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT]) +
fabsf(_driveKeys[UP] - _driveKeys[DOWN]);
bool walkingOnFloor = false;
float gravityLength = glm::length(_gravity);
if (gravityLength > EPSILON) {
const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape();
glm::vec3 startCap;
boundingShape.getStartPoint(startCap);
glm::vec3 bottomOfBoundingCapsule = startCap + (boundingShape.getRadius() / gravityLength) * _gravity;
float fallThreshold = 2.0f * deltaTime * gravityLength;
walkingOnFloor = (glm::distance(bottomOfBoundingCapsule, _lastFloorContactPoint) < fallThreshold);
}
if (keyboardInput > 0.0f || glm::length2(_velocity) > 0.0f || glm::length2(_thrust) > 0.0f ||
! walkingOnFloor) {
// apply gravity
_velocity += _scale * _gravity * (GRAVITY_EARTH * deltaTime);
// update motor and thrust
updateMotorFromKeyboard(deltaTime, walkingOnFloor);
applyMotor(deltaTime);
applyThrust(deltaTime);
// update position
if (glm::length2(_velocity) < EPSILON) {
_velocity = glm::vec3(0.0f);
} else {
_position += _velocity * deltaTime;
}
}
// update moving flag based on speed
const float MOVING_SPEED_THRESHOLD = 0.01f;
_moving = glm::length(_velocity) > MOVING_SPEED_THRESHOLD;
updateChatCircle(deltaTime);
updatePosition(deltaTime);
// update avatar skeleton and simulate hand and head
getHand()->collideAgainstOurself();
@ -206,19 +169,17 @@ void MyAvatar::simulate(float deltaTime) {
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.0f));
radius *= COLLISION_RADIUS_SCALAR;
}
if (_collisionGroups) {
updateShapePositions();
if (_collisionGroups & COLLISION_GROUP_ENVIRONMENT) {
updateCollisionWithEnvironment(deltaTime, radius);
}
if (_collisionGroups & COLLISION_GROUP_VOXELS) {
updateCollisionWithVoxels(deltaTime, radius);
} else {
_trapDuration = 0.0f;
}
if (_collisionGroups & COLLISION_GROUP_AVATARS) {
updateCollisionWithAvatars(deltaTime);
}
updateShapePositions();
if (_collisionGroups & COLLISION_GROUP_ENVIRONMENT) {
updateCollisionWithEnvironment(deltaTime, radius);
}
if (_collisionGroups & COLLISION_GROUP_VOXELS) {
updateCollisionWithVoxels(deltaTime, radius);
} else {
_trapDuration = 0.0f;
}
if (_collisionGroups & COLLISION_GROUP_AVATARS) {
updateCollisionWithAvatars(deltaTime);
}
}
@ -423,9 +384,9 @@ void MyAvatar::setGravity(const glm::vec3& gravity) {
float gravityLength = glm::length(gravity);
if (gravityLength > EPSILON) {
_worldUpDirection = _gravity / -gravityLength;
} else {
_worldUpDirection = DEFAULT_UP_DIRECTION;
}
// NOTE: the else case here it to leave _worldUpDirection unchanged
// so it continues to point opposite to the previous gravity setting.
}
AnimationHandlePointer MyAvatar::addAnimationHandle() {
@ -812,7 +773,7 @@ void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) {
// Render the body's voxels and head
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
_skeletonModel.render(1.0f, modelRenderMode);
_skeletonModel.render(1.0f, modelRenderMode, Menu::getInstance()->isOptionChecked(MenuOption::AvatarsReceiveShadows));
renderAttachments(renderMode);
// Render head so long as the camera isn't inside it
@ -830,6 +791,15 @@ bool MyAvatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode rend
(glm::length(cameraPosition - head->calculateAverageEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE * _scale);
}
float MyAvatar::computeDistanceToFloor(const glm::vec3& startPoint) {
glm::vec3 direction = -_worldUpDirection;
OctreeElement* elementHit; // output from findRayIntersection
float distance = FLT_MAX; // output from findRayIntersection
BoxFace face; // output from findRayIntersection
Application::getInstance()->getVoxelTree()->findRayIntersection(startPoint, direction, elementHit, distance, face);
return distance;
}
void MyAvatar::updateOrientation(float deltaTime) {
// Gather rotation information from keyboard
_bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime;
@ -890,8 +860,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
// We must adjust the body orientation using a delta rotation (rather than
// doing yaw math) because the body's yaw ranges are not the same
// as what the Oculus API provides.
glm::vec3 UP_AXIS = glm::vec3(0.0f, 1.0f, 0.0f);
glm::quat bodyCorrection = glm::angleAxis(glm::radians(delta), UP_AXIS);
glm::quat bodyCorrection = glm::angleAxis(glm::radians(delta), _worldUpDirection);
orientation = orientation * bodyCorrection;
}
Head* head = getHead();
@ -905,6 +874,96 @@ void MyAvatar::updateOrientation(float deltaTime) {
setOrientation(orientation);
}
const float NEARBY_FLOOR_THRESHOLD = 5.0f;
void MyAvatar::updatePosition(float deltaTime) {
float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) +
fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT]) +
fabsf(_driveKeys[UP] - _driveKeys[DOWN]);
bool walkingOnFloor = false;
float gravityLength = glm::length(_gravity) * GRAVITY_EARTH;
const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape();
glm::vec3 startCap;
boundingShape.getStartPoint(startCap);
glm::vec3 bottom = startCap - boundingShape.getRadius() * _worldUpDirection;
if (gravityLength > EPSILON) {
float speedFromGravity = _scale * deltaTime * gravityLength;
float distanceToFall = glm::distance(bottom, _lastFloorContactPoint);
walkingOnFloor = (distanceToFall < 2.0f * deltaTime * speedFromGravity);
if (walkingOnFloor) {
// BEGIN HACK: to prevent the avatar from bouncing on a floor surface
if (distanceToFall < deltaTime * speedFromGravity) {
float verticalSpeed = glm::dot(_velocity, _worldUpDirection);
if (fabs(verticalSpeed) < speedFromGravity) {
// we're standing on a floor, and nearly at rest so we zero the vertical velocity component
_velocity -= verticalSpeed * _worldUpDirection;
}
} else {
// fall with gravity against floor
_velocity -= speedFromGravity * _worldUpDirection;
}
// END HACK
} else {
if (!_isBraking) {
// fall with gravity toward floor
_velocity -= speedFromGravity * _worldUpDirection;
}
if (_motionBehaviors & AVATAR_MOTION_STAND_ON_NEARBY_FLOORS) {
const float MAX_VERTICAL_FLOOR_DETECTION_SPEED = _scale * MAX_WALKING_SPEED;
if (keyboardInput && glm::dot(_motorVelocity, _worldUpDirection) > 0.0f &&
glm::dot(_velocity, _worldUpDirection) > MAX_VERTICAL_FLOOR_DETECTION_SPEED) {
// disable local gravity when flying up
setLocalGravity(glm::vec3(0.0f));
} else {
const float maxFloorDistance = _scale * NEARBY_FLOOR_THRESHOLD;
if (computeDistanceToFloor(bottom) > maxFloorDistance) {
// disable local gravity when floor is too far
setLocalGravity(glm::vec3(0.0f));
}
}
}
}
} else {
if ((_collisionGroups & COLLISION_GROUP_VOXELS) &&
_motionBehaviors & AVATAR_MOTION_STAND_ON_NEARBY_FLOORS) {
const float MIN_FLOOR_DETECTION_SPEED = _scale * 1.0f;
if (glm::length(_velocity) < MIN_FLOOR_DETECTION_SPEED ) {
// scan for floor under avatar
const float maxFloorDistance = _scale * NEARBY_FLOOR_THRESHOLD;
if (computeDistanceToFloor(bottom) < maxFloorDistance) {
// enable local gravity
setLocalGravity(-_worldUpDirection);
}
}
}
}
if (keyboardInput > 0.0f || glm::length2(_velocity) > 0.0f || glm::length2(_thrust) > 0.0f || ! walkingOnFloor) {
// update motor and thrust
updateMotorFromKeyboard(deltaTime, walkingOnFloor);
applyMotor(deltaTime);
applyThrust(deltaTime);
// update position
if (glm::length2(_velocity) < EPSILON) {
_velocity = glm::vec3(0.0f);
} else {
_position += _velocity * deltaTime;
}
}
// update moving flag based on speed
const float MOVING_SPEED_THRESHOLD = 0.01f;
_moving = glm::length(_velocity) > MOVING_SPEED_THRESHOLD;
updateChatCircle(deltaTime);
}
void MyAvatar::updateMotorFromKeyboard(float deltaTime, bool walking) {
// Increase motor velocity until its length is equal to _maxMotorSpeed.
if (!(_motionBehaviors & AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED)) {
@ -930,12 +989,12 @@ void MyAvatar::updateMotorFromKeyboard(float deltaTime, bool walking) {
if (directionLength > EPSILON) {
direction /= directionLength;
// the finalMotorSpeed depends on whether we are walking or not
float finalMaxMotorSpeed = walking ? MAX_WALKING_SPEED : _maxMotorSpeed;
float finalMaxMotorSpeed = walking ? _scale * MAX_WALKING_SPEED : _scale * _maxMotorSpeed;
float motorLength = glm::length(_motorVelocity);
if (motorLength < MIN_KEYBOARD_CONTROL_SPEED) {
if (motorLength < _scale * MIN_KEYBOARD_CONTROL_SPEED) {
// an active keyboard motor should never be slower than this
_motorVelocity = MIN_KEYBOARD_CONTROL_SPEED * direction;
_motorVelocity = _scale * MIN_KEYBOARD_CONTROL_SPEED * direction;
} else {
float MOTOR_LENGTH_TIMESCALE = 1.5f;
float tau = glm::clamp(deltaTime / MOTOR_LENGTH_TIMESCALE, 0.0f, 1.0f);
@ -963,32 +1022,30 @@ float MyAvatar::computeMotorTimescale() {
// (1) braking --> short timescale (aggressive motor assertion)
// (2) pushing --> medium timescale (mild motor assertion)
// (3) inactive --> long timescale (gentle friction for low speeds)
//
// TODO: recover extra braking behavior when flying close to nearest avatar
float MIN_MOTOR_TIMESCALE = 0.125f;
float MAX_MOTOR_TIMESCALE = 0.5f;
float MIN_BRAKE_SPEED = 0.4f;
float timescale = MAX_MOTOR_TIMESCALE;
float speed = glm::length(_velocity);
bool areThrusting = (glm::length2(_thrust) > EPSILON);
if (_wasPushing && !(_isPushing || areThrusting) && speed > MIN_BRAKE_SPEED) {
// we don't change _wasPushing for this case -->
// keeps the brakes on until we go below MIN_BRAKE_SPEED
timescale = MIN_MOTOR_TIMESCALE;
bool isThrust = (glm::length2(_thrust) > EPSILON);
if (_isPushing || isThrust) {
timescale = _motorTimescale;
_isBraking = false;
} else {
if (_isPushing) {
timescale = _motorTimescale;
}
_wasPushing = _isPushing || areThrusting;
float speed = glm::length(_velocity);
_isBraking = _wasPushing || (_isBraking && speed > MIN_BRAKE_SPEED);
if (_isBraking) {
timescale = MIN_MOTOR_TIMESCALE;
}
}
_wasPushing = _isPushing || isThrust;
_isPushing = false;
return timescale;
}
void MyAvatar::applyMotor(float deltaTime) {
// TODO: recover extra braking behavior when flying close to nearest avatar
if (!( _motionBehaviors & AVATAR_MOTION_MOTOR_ENABLED)) {
// nothing to do --> early exit
return;
@ -1178,6 +1235,8 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
const float MIN_STEP_HEIGHT = 0.0f;
glm::vec3 footBase = boundingShape.getPosition() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection;
float highestStep = 0.0f;
float lowestStep = MAX_STEP_HEIGHT;
glm::vec3 floorPoint;
glm::vec3 stepPenetration(0.0f);
glm::vec3 totalPenetration(0.0f);
@ -1211,8 +1270,15 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
highestStep = stepHeight;
stepPenetration = collision->_penetration;
}
if (stepHeight < lowestStep) {
lowestStep = stepHeight;
floorPoint = collision->_contactPoint - collision->_penetration;
}
}
}
if (lowestStep < MAX_STEP_HEIGHT) {
_lastFloorContactPoint = floorPoint;
}
float penetrationLength = glm::length(totalPenetration);
if (penetrationLength < EPSILON) {
@ -1251,8 +1317,9 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
}
const float VOXEL_COLLISION_FREQUENCY = 0.5f;
updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
// Don't make a collision sound against voxlels by default -- too annoying when walking
//const float VOXEL_COLLISION_FREQUENCY = 0.5f;
//updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
}
_trapDuration = isTrapped ? _trapDuration + deltaTime : 0.0f;
}
@ -1598,7 +1665,8 @@ void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) {
}
void MyAvatar::updateMotionBehaviorsFromMenu() {
if (Menu::getInstance()->isOptionChecked(MenuOption::ObeyEnvironmentalGravity)) {
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;
@ -1608,6 +1676,14 @@ void MyAvatar::updateMotionBehaviorsFromMenu() {
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
_collisionGroups |= COLLISION_GROUP_VOXELS;
menu->setIsOptionChecked(MenuOption::CollideWithVoxels, true);
} else {
_motionBehaviors &= ~AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
}
}
void MyAvatar::renderAttachments(RenderMode renderMode) {
@ -1619,10 +1695,11 @@ void MyAvatar::renderAttachments(RenderMode renderMode) {
QString headJointName = (geometry.headJointIndex == -1) ? QString() : geometry.joints.at(geometry.headJointIndex).name;
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
bool receiveShadows = Menu::getInstance()->isOptionChecked(MenuOption::AvatarsReceiveShadows);
for (int i = 0; i < _attachmentData.size(); i++) {
const QString& jointName = _attachmentData.at(i).jointName;
if (jointName != headJointName && jointName != "Head") {
_attachmentModels.at(i)->render(1.0f, modelRenderMode);
_attachmentModels.at(i)->render(1.0f, modelRenderMode, receiveShadows);
}
}
}
@ -1634,6 +1711,11 @@ void MyAvatar::setCollisionGroups(quint32 collisionGroups) {
menu->setIsOptionChecked(MenuOption::CollideWithAvatars, (bool)(_collisionGroups & COLLISION_GROUP_AVATARS));
menu->setIsOptionChecked(MenuOption::CollideWithVoxels, (bool)(_collisionGroups & COLLISION_GROUP_VOXELS));
menu->setIsOptionChecked(MenuOption::CollideWithParticles, (bool)(_collisionGroups & COLLISION_GROUP_PARTICLES));
if (! (_collisionGroups & COLLISION_GROUP_VOXELS)) {
// no collision with voxels --> disable standing on floors
_motionBehaviors &= ~AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
menu->setIsOptionChecked(MenuOption::StandOnNearbyFloors, false);
}
}
void MyAvatar::setMotionBehaviorsByScript(quint32 flags) {

View file

@ -150,11 +150,11 @@ private:
bool _shouldJump;
float _driveKeys[MAX_DRIVE_KEYS];
glm::vec3 _gravity;
glm::vec3 _environmentGravity;
float _distanceToNearestAvatar; // How close is the nearest avatar?
bool _wasPushing;
bool _isPushing;
bool _isBraking;
float _trapDuration; // seconds that avatar has been trapped by collisions
glm::vec3 _thrust; // final acceleration from outside sources for the current frame
@ -174,7 +174,9 @@ private:
QList<AnimationHandlePointer> _animationHandles;
// private methods
float computeDistanceToFloor(const glm::vec3& startPoint);
void updateOrientation(float deltaTime);
void updatePosition(float deltaTime);
void updateMotorFromKeyboard(float deltaTime, bool walking);
float computeMotorTimescale();
void applyMotor(float deltaTime);

View file

@ -220,7 +220,13 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
// TODO: should we allow modelItems to have alpha on their models?
Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE
? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
model->render(alpha, modelRenderMode);
if (modelItem.getGlowLevel() > 0.0f) {
Glower glower(modelItem.getGlowLevel());
model->render(alpha, modelRenderMode);
} else {
model->render(alpha, modelRenderMode);
}
if (!isShadowMode && displayModelBounds) {

View file

@ -59,18 +59,39 @@ ProgramObject Model::_program;
ProgramObject Model::_normalMapProgram;
ProgramObject Model::_specularMapProgram;
ProgramObject Model::_normalSpecularMapProgram;
ProgramObject Model::_shadowMapProgram;
ProgramObject Model::_shadowNormalMapProgram;
ProgramObject Model::_shadowSpecularMapProgram;
ProgramObject Model::_shadowNormalSpecularMapProgram;
ProgramObject Model::_shadowProgram;
ProgramObject Model::_skinProgram;
ProgramObject Model::_skinNormalMapProgram;
ProgramObject Model::_skinSpecularMapProgram;
ProgramObject Model::_skinNormalSpecularMapProgram;
ProgramObject Model::_skinShadowMapProgram;
ProgramObject Model::_skinShadowNormalMapProgram;
ProgramObject Model::_skinShadowSpecularMapProgram;
ProgramObject Model::_skinShadowNormalSpecularMapProgram;
ProgramObject Model::_skinShadowProgram;
int Model::_normalMapTangentLocation;
int Model::_normalSpecularMapTangentLocation;
int Model::_shadowNormalMapTangentLocation;
int Model::_shadowNormalSpecularMapTangentLocation;
Model::SkinLocations Model::_skinLocations;
Model::SkinLocations Model::_skinNormalMapLocations;
Model::SkinLocations Model::_skinSpecularMapLocations;
Model::SkinLocations Model::_skinNormalSpecularMapLocations;
Model::SkinLocations Model::_skinShadowMapLocations;
Model::SkinLocations Model::_skinShadowNormalMapLocations;
Model::SkinLocations Model::_skinShadowSpecularMapLocations;
Model::SkinLocations Model::_skinShadowNormalSpecularMapLocations;
Model::SkinLocations Model::_skinShadowLocations;
void Model::setScale(const glm::vec3& scale) {
@ -100,7 +121,8 @@ void Model::setOffset(const glm::vec3& offset) {
}
void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations, int specularTextureUnit) {
void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations,
int specularTextureUnit, int shadowTextureUnit) {
program.bind();
locations.clusterMatrices = program.uniformLocation("clusterMatrices");
locations.clusterIndices = program.attributeLocation("clusterIndices");
@ -109,6 +131,7 @@ void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locati
program.setUniformValue("diffuseMap", 0);
program.setUniformValue("normalMap", 1);
program.setUniformValue("specularMap", specularTextureUnit);
program.setUniformValue("shadowMap", shadowTextureUnit);
program.release();
}
@ -170,7 +193,7 @@ void Model::init() {
_program.link();
_program.bind();
_program.setUniformValue("texture", 0);
_program.setUniformValue("diffuseMap", 0);
_program.release();
_normalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
@ -209,11 +232,63 @@ void Model::init() {
_normalSpecularMapTangentLocation = _normalMapProgram.attributeLocation("tangent");
_normalSpecularMapProgram.release();
_shadowMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model.vert");
_shadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
"shaders/model_shadow_map.frag");
_shadowMapProgram.link();
_shadowMapProgram.bind();
_shadowMapProgram.setUniformValue("diffuseMap", 0);
_shadowMapProgram.setUniformValue("shadowMap", 1);
_shadowMapProgram.release();
_shadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/model_normal_map.vert");
_shadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_shadow_normal_map.frag");
_shadowNormalMapProgram.link();
_shadowNormalMapProgram.bind();
_shadowNormalMapProgram.setUniformValue("diffuseMap", 0);
_shadowNormalMapProgram.setUniformValue("normalMap", 1);
_shadowNormalMapProgram.setUniformValue("shadowMap", 2);
_shadowNormalMapTangentLocation = _shadowNormalMapProgram.attributeLocation("tangent");
_shadowNormalMapProgram.release();
_shadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/model.vert");
_shadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_shadow_specular_map.frag");
_shadowSpecularMapProgram.link();
_shadowSpecularMapProgram.bind();
_shadowSpecularMapProgram.setUniformValue("diffuseMap", 0);
_shadowSpecularMapProgram.setUniformValue("specularMap", 1);
_shadowSpecularMapProgram.setUniformValue("shadowMap", 2);
_shadowSpecularMapProgram.release();
_shadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/model_normal_map.vert");
_shadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_shadow_normal_specular_map.frag");
_shadowNormalSpecularMapProgram.link();
_shadowNormalSpecularMapProgram.bind();
_shadowNormalSpecularMapProgram.setUniformValue("diffuseMap", 0);
_shadowNormalSpecularMapProgram.setUniformValue("normalMap", 1);
_shadowNormalSpecularMapProgram.setUniformValue("specularMap", 2);
_shadowNormalSpecularMapProgram.setUniformValue("shadowMap", 3);
_shadowNormalSpecularMapTangentLocation = _normalMapProgram.attributeLocation("tangent");
_shadowNormalSpecularMapProgram.release();
_shadowProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_shadow.vert");
_shadowProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_shadow.frag");
_shadowProgram.link();
_skinProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/skin_model.vert");
_skinProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/model.frag");
_skinProgram.link();
@ -244,6 +319,40 @@ void Model::init() {
initSkinProgram(_skinNormalSpecularMapProgram, _skinNormalSpecularMapLocations, 2);
_skinShadowMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
"shaders/skin_model.vert");
_skinShadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
"shaders/model_shadow_map.frag");
_skinShadowMapProgram.link();
initSkinProgram(_skinShadowMapProgram, _skinShadowMapLocations);
_skinShadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/skin_model_normal_map.vert");
_skinShadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_shadow_normal_map.frag");
_skinShadowNormalMapProgram.link();
initSkinProgram(_skinShadowNormalMapProgram, _skinShadowNormalMapLocations, 1, 2);
_skinShadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/skin_model.vert");
_skinShadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_shadow_specular_map.frag");
_skinShadowSpecularMapProgram.link();
initSkinProgram(_skinShadowSpecularMapProgram, _skinShadowSpecularMapLocations, 1, 2);
_skinShadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/skin_model_normal_map.vert");
_skinShadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_shadow_normal_specular_map.frag");
_skinShadowNormalSpecularMapProgram.link();
initSkinProgram(_skinShadowNormalSpecularMapProgram, _skinShadowNormalSpecularMapLocations, 2, 3);
_skinShadowProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/skin_model_shadow.vert");
_skinShadowProgram.addShaderFromSourceFile(QGLShader::Fragment,
@ -361,7 +470,7 @@ bool Model::updateGeometry() {
return needFullUpdate;
}
bool Model::render(float alpha, RenderMode mode) {
bool Model::render(float alpha, RenderMode mode, bool receiveShadows) {
// render the attachments
foreach (Model* attachment, _attachments) {
attachment->render(alpha, mode);
@ -389,6 +498,9 @@ bool Model::render(float alpha, RenderMode mode) {
glDisable(GL_CULL_FACE);
} else {
glEnable(GL_CULL_FACE);
if (mode == SHADOW_RENDER_MODE) {
glCullFace(GL_FRONT);
}
}
// render opaque meshes with alpha testing
@ -396,16 +508,21 @@ bool Model::render(float alpha, RenderMode mode) {
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.5f * alpha);
renderMeshes(alpha, mode, false);
receiveShadows &= Menu::getInstance()->isOptionChecked(MenuOption::Shadows);
renderMeshes(alpha, mode, false, receiveShadows);
glDisable(GL_ALPHA_TEST);
// render translucent meshes afterwards
renderMeshes(alpha, mode, true);
renderMeshes(alpha, mode, true, receiveShadows);
glDisable(GL_CULL_FACE);
if (mode == SHADOW_RENDER_MODE) {
glCullFace(GL_BACK);
}
// deactivate vertex arrays after drawing
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
@ -1480,10 +1597,15 @@ void Model::deleteGeometry() {
}
}
void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool receiveShadows) {
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes();
if (receiveShadows) {
glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[0]);
glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[1]);
glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[2]);
}
for (int i = 0; i < networkMeshes.size(); i++) {
// exit early if the translucency doesn't match what we're drawing
const NetworkMesh& networkMesh = networkMeshes.at(i);
@ -1506,6 +1628,7 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
ProgramObject* skinProgram = &_skinProgram;
SkinLocations* skinLocations = &_skinLocations;
GLenum specularTextureUnit = 0;
GLenum shadowTextureUnit = 0;
if (mode == SHADOW_RENDER_MODE) {
program = &_shadowProgram;
skinProgram = &_skinShadowProgram;
@ -1513,21 +1636,46 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
} else if (!mesh.tangents.isEmpty()) {
if (mesh.hasSpecularTexture()) {
program = &_normalSpecularMapProgram;
skinProgram = &_skinNormalSpecularMapProgram;
skinLocations = &_skinNormalSpecularMapLocations;
if (receiveShadows) {
program = &_shadowNormalSpecularMapProgram;
skinProgram = &_skinShadowNormalSpecularMapProgram;
skinLocations = &_skinShadowNormalSpecularMapLocations;
shadowTextureUnit = GL_TEXTURE3;
} else {
program = &_normalSpecularMapProgram;
skinProgram = &_skinNormalSpecularMapProgram;
skinLocations = &_skinNormalSpecularMapLocations;
}
specularTextureUnit = GL_TEXTURE2;
} else if (receiveShadows) {
program = &_shadowNormalMapProgram;
skinProgram = &_skinShadowNormalMapProgram;
skinLocations = &_skinShadowNormalMapLocations;
shadowTextureUnit = GL_TEXTURE2;
} else {
program = &_normalMapProgram;
skinProgram = &_skinNormalMapProgram;
skinLocations = &_skinNormalMapLocations;
}
} else if (mesh.hasSpecularTexture()) {
program = &_specularMapProgram;
skinProgram = &_skinSpecularMapProgram;
skinLocations = &_skinSpecularMapLocations;
if (receiveShadows) {
program = &_shadowSpecularMapProgram;
skinProgram = &_skinShadowSpecularMapProgram;
skinLocations = &_skinShadowSpecularMapLocations;
shadowTextureUnit = GL_TEXTURE2;
} else {
program = &_specularMapProgram;
skinProgram = &_skinSpecularMapProgram;
skinLocations = &_skinSpecularMapLocations;
}
specularTextureUnit = GL_TEXTURE1;
} else if (receiveShadows) {
program = &_shadowMapProgram;
skinProgram = &_skinShadowMapProgram;
skinLocations = &_skinShadowMapLocations;
shadowTextureUnit = GL_TEXTURE1;
}
const MeshState& state = _meshStates.at(i);
@ -1616,8 +1764,7 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID());
if (!mesh.tangents.isEmpty()) {
specularTextureUnit = GL_TEXTURE2;
if (!mesh.tangents.isEmpty()) {
glActiveTexture(GL_TEXTURE1);
Texture* normalMap = networkPart.normalTexture.data();
glBindTexture(GL_TEXTURE_2D, !normalMap ?
@ -1632,6 +1779,12 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
Application::getInstance()->getTextureCache()->getWhiteTextureID() : specularMap->getID());
glActiveTexture(GL_TEXTURE0);
}
if (shadowTextureUnit) {
glActiveTexture(shadowTextureUnit);
glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getShadowDepthTextureID());
glActiveTexture(GL_TEXTURE0);
}
}
glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset);
offset += part.quadIndices.size() * sizeof(int);
@ -1661,6 +1814,12 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
glActiveTexture(GL_TEXTURE0);
}
if (shadowTextureUnit) {
glActiveTexture(shadowTextureUnit);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
}
if (state.clusterMatrices.size() > 1) {
skinProgram->disableAttributeArray(skinLocations->clusterIndices);
skinProgram->disableAttributeArray(skinLocations->clusterWeights);

View file

@ -79,7 +79,7 @@ public:
enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE };
bool render(float alpha = 1.0f, RenderMode mode = DEFAULT_RENDER_MODE);
bool render(float alpha = 1.0f, RenderMode mode = DEFAULT_RENDER_MODE, bool receiveShadows = true);
/// Sets the URL of the model to render.
/// \param fallback the URL of a fallback model to render if the requested model fails to load
@ -315,7 +315,7 @@ private:
void applyNextGeometry();
void deleteGeometry();
void renderMeshes(float alpha, RenderMode mode, bool translucent);
void renderMeshes(float alpha, RenderMode mode, bool translucent, bool receiveShadows);
QVector<JointState> createJointStates(const FBXGeometry& geometry);
QSharedPointer<NetworkGeometry> _baseGeometry; ///< reference required to prevent collection of base
@ -344,15 +344,30 @@ private:
static ProgramObject _normalMapProgram;
static ProgramObject _specularMapProgram;
static ProgramObject _normalSpecularMapProgram;
static ProgramObject _shadowMapProgram;
static ProgramObject _shadowNormalMapProgram;
static ProgramObject _shadowSpecularMapProgram;
static ProgramObject _shadowNormalSpecularMapProgram;
static ProgramObject _shadowProgram;
static ProgramObject _skinProgram;
static ProgramObject _skinNormalMapProgram;
static ProgramObject _skinSpecularMapProgram;
static ProgramObject _skinNormalSpecularMapProgram;
static ProgramObject _skinShadowMapProgram;
static ProgramObject _skinShadowNormalMapProgram;
static ProgramObject _skinShadowSpecularMapProgram;
static ProgramObject _skinShadowNormalSpecularMapProgram;
static ProgramObject _skinShadowProgram;
static int _normalMapTangentLocation;
static int _normalSpecularMapTangentLocation;
static int _shadowNormalMapTangentLocation;
static int _shadowNormalSpecularMapTangentLocation;
class SkinLocations {
public:
@ -366,9 +381,14 @@ private:
static SkinLocations _skinNormalMapLocations;
static SkinLocations _skinSpecularMapLocations;
static SkinLocations _skinNormalSpecularMapLocations;
static SkinLocations _skinShadowMapLocations;
static SkinLocations _skinShadowNormalMapLocations;
static SkinLocations _skinShadowSpecularMapLocations;
static SkinLocations _skinShadowNormalSpecularMapLocations;
static SkinLocations _skinShadowLocations;
static void initSkinProgram(ProgramObject& program, SkinLocations& locations, int specularTextureUnit = 1);
static void initSkinProgram(ProgramObject& program, SkinLocations& locations,
int specularTextureUnit = 1, int shadowTextureUnit = 1);
};
Q_DECLARE_METATYPE(QPointer<Model>)

View file

@ -80,7 +80,7 @@ ChatWindow::ChatWindow(QWidget* parent) :
} else {
ui->numOnlineLabel->hide();
ui->closeButton->hide();
ui->usersWidget->hide();
ui->usersArea->hide();
ui->messagesScrollArea->hide();
ui->messagePlainTextEdit->hide();
connect(&XmppClient::getInstance(), SIGNAL(joinedPublicChatRoom()), this, SLOT(connected()));
@ -208,7 +208,7 @@ void ChatWindow::connected() {
ui->connectingToXMPPLabel->hide();
ui->numOnlineLabel->show();
ui->closeButton->show();
ui->usersWidget->show();
ui->usersArea->show();
ui->messagesScrollArea->show();
ui->messagePlainTextEdit->show();
ui->messagePlainTextEdit->setFocus();
@ -248,6 +248,7 @@ void ChatWindow::notificationClicked() {
return;
}
}
Application::processEvents();
scrollToBottom();
}
@ -262,6 +263,8 @@ void ChatWindow::error(QXmppClient::Error error) {
}
void ChatWindow::participantsChanged() {
bool atBottom = isNearBottom();
QStringList participants = XmppClient::getInstance().getPublicChatRoom()->participants();
ui->numOnlineLabel->setText(tr("%1 online now:").arg(participants.count()));
@ -288,6 +291,11 @@ void ChatWindow::participantsChanged() {
userLabel->installEventFilter(this);
ui->usersWidget->layout()->addWidget(userLabel);
}
Application::processEvents();
if (atBottom) {
scrollToBottom();
}
}
void ChatWindow::messageReceived(const QXmppMessage& message) {
@ -306,7 +314,7 @@ void ChatWindow::messageReceived(const QXmppMessage& message) {
messageArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
messageArea->setReadOnly(true);
messageArea->setStyleSheet("padding-bottom: 2px;"
messageArea->setStyleSheet("QTextBrowser{ padding-bottom: 2px;"
"padding-left: 2px;"
"padding-top: 2px;"
"padding-right: 20px;"
@ -314,7 +322,8 @@ void ChatWindow::messageReceived(const QXmppMessage& message) {
"color: #333333;"
"font-size: 14pt;"
"background-color: rgba(0, 0, 0, 0%);"
"border: 0;");
"border: 0; }"
"QMenu{ border: 2px outset gray; }");
QString userLabel = getParticipantName(message.from());
if (fromSelf) {

View file

@ -31,6 +31,10 @@ const QString FORUM_REPLY_TO_TOPIC = "244";
const QString FORUM_POST_TEMPLATE = "<img src='%1'/><p>%2</p>";
const QString SHARE_DEFAULT_ERROR = "The server isn't responding. Please try again in a few minutes.";
const QString SUCCESS_LABEL_TEMPLATE = "Success!!! Go check out your image ...<br/><a style='color:#333;text-decoration:none' href='%1'>%1</a>";
const QString SHARE_BUTTON_STYLE = "border-width:0;border-radius:9px;border-radius:9px;font-family:Arial;font-size:18px;"
"font-weight:100;color:#FFFFFF;width: 120px;height: 50px;";
const QString SHARE_BUTTON_ENABLED_STYLE = "background-color: #333;";
const QString SHARE_BUTTON_DISABLED_STYLE = "background-color: #999;";
Q_DECLARE_METATYPE(QNetworkAccessManager::Operation)
@ -73,6 +77,10 @@ SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) :
}
void SnapshotShareDialog::accept() {
// prevent multiple clicks on share button
_ui.shareButton->setEnabled(false);
// gray out share button
_ui.shareButton->setStyleSheet(SHARE_BUTTON_STYLE + SHARE_BUTTON_DISABLED_STYLE);
uploadSnapshot();
}
@ -179,6 +187,8 @@ void SnapshotShareDialog::postRequestFinished() {
}
}
QMessageBox::warning(this, "", errorMessage);
_ui.shareButton->setEnabled(true);
_ui.shareButton->setStyleSheet(SHARE_BUTTON_STYLE + SHARE_BUTTON_ENABLED_STYLE);
}
}
@ -192,6 +202,8 @@ void SnapshotShareDialog::uploadRequestFinished() {
sendForumPost(responseObject["url"].toString());
} else {
QMessageBox::warning(this, "", SHARE_DEFAULT_ERROR);
_ui.shareButton->setEnabled(true);
_ui.shareButton->setStyleSheet(SHARE_BUTTON_STYLE + SHARE_BUTTON_ENABLED_STYLE);
}
delete requestReply;

View file

@ -22,16 +22,26 @@ Overlays::Overlays() : _nextOverlayID(1) {
}
Overlays::~Overlays() {
QMap<unsigned int, Overlay*>::iterator it;
for (it = _overlays2D.begin(); it != _overlays2D.end(); ++it) {
delete _overlays2D.take(it.key());
{
QWriteLocker lock(&_lock);
foreach(Overlay* thisOverlay, _overlays2D) {
delete thisOverlay;
}
_overlays2D.clear();
foreach(Overlay* thisOverlay, _overlays3D) {
delete thisOverlay;
}
_overlays3D.clear();
}
for (it = _overlays3D.begin(); it != _overlays3D.end(); ++it) {
delete _overlays3D.take(it.key());
}
while (!_overlaysToDelete.isEmpty()) {
delete _overlaysToDelete.takeLast();
if (!_overlaysToDelete.isEmpty()) {
QWriteLocker lock(&_deleteLock);
do {
delete _overlaysToDelete.takeLast();
} while (!_overlaysToDelete.isEmpty());
}
}
void Overlays::init(QGLWidget* parent) {

View file

@ -128,19 +128,43 @@ QPushButton:pressed {
</layout>
</item>
<item>
<widget class="QWidget" name="usersWidget" native="true">
<property name="styleSheet">
<string notr="true">#usersWidget {
margin-right: 20px;
}</string>
<widget class="QWidget" name="usersArea" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>12</number>
</property>
<item>
<widget class="QWidget" name="usersWidget" native="true"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QScrollArea" name="messagesScrollArea">
<property name="styleSheet">
<string notr="true">margin-top: 12px;</string>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>

View file

@ -53,23 +53,27 @@ typedef unsigned long long quint64;
#include "HandData.h"
// avatar motion behaviors
const quint32 AVATAR_MOTION_MOTOR_ENABLED = 1U << 0;
const quint32 AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED = 1U << 1;
const quint32 AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME = 1U << 2;
const quint32 AVATAR_MOTION_MOTOR_COLLISION_SURFACE_ONLY = 1U << 3;
const quint32 AVATAR_MOTION_MOTOR_ENABLED = 1U << 0;
const quint32 AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED = 1U << 1;
const quint32 AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME = 1U << 2;
const quint32 AVATAR_MOTION_MOTOR_COLLISION_SURFACE_ONLY = 1U << 3;
const quint32 AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY = 1U << 4;
const quint32 AVATAR_MOTION_OBEY_LOCAL_GRAVITY = 1U << 5;
const quint32 AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY = 1U << 4;
const quint32 AVATAR_MOTION_OBEY_LOCAL_GRAVITY = 1U << 5;
const quint32 AVATAR_MOTION_STAND_ON_NEARBY_FLOORS = 1U << 6;
const quint32 AVATAR_MOTION_DEFAULTS =
AVATAR_MOTION_MOTOR_ENABLED |
AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED |
AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME;
AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME |
AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
// these bits will be expanded as features are exposed
const quint32 AVATAR_MOTION_SCRIPTABLE_BITS =
AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY |
AVATAR_MOTION_OBEY_LOCAL_GRAVITY;
AVATAR_MOTION_OBEY_LOCAL_GRAVITY |
AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
// First bitset

View file

@ -19,6 +19,7 @@
const char* HTTPConnection::StatusCode200 = "200 OK";
const char* HTTPConnection::StatusCode301 = "301 Moved Permanently";
const char* HTTPConnection::StatusCode302 = "302 Found";
const char* HTTPConnection::StatusCode400 = "400 Bad Request";
const char* HTTPConnection::StatusCode404 = "404 Not Found";
const char* HTTPConnection::DefaultContentType = "text/plain; charset=ISO-8859-1";

View file

@ -44,6 +44,7 @@ class HTTPConnection : public QObject {
public:
static const char* StatusCode200;
static const char* StatusCode301;
static const char* StatusCode302;
static const char* StatusCode400;
static const char* StatusCode404;
static const char* DefaultContentType;

View file

@ -85,12 +85,13 @@ ModelItem::ModelItem(const ModelItemID& modelItemID, const ModelItemProperties&
_shouldDie = false;
_modelURL = MODEL_DEFAULT_MODEL_URL;
_modelRotation = MODEL_DEFAULT_MODEL_ROTATION;
// animation related
_animationURL = MODEL_DEFAULT_ANIMATION_URL;
_animationIsPlaying = false;
_animationFrameIndex = 0.0f;
_animationFPS = MODEL_DEFAULT_ANIMATION_FPS;
_glowLevel = 0.0f;
_jointMappingCompleted = false;
_lastAnimated = now;
@ -125,6 +126,7 @@ void ModelItem::init(glm::vec3 position, float radius, rgbColor color, uint32_t
_animationIsPlaying = false;
_animationFrameIndex = 0.0f;
_animationFPS = MODEL_DEFAULT_ANIMATION_FPS;
_glowLevel = 0.0f;
_jointMappingCompleted = false;
_lastAnimated = now;
}
@ -802,6 +804,7 @@ ModelItemProperties::ModelItemProperties() :
_animationIsPlaying(false),
_animationFrameIndex(0.0),
_animationFPS(MODEL_DEFAULT_ANIMATION_FPS),
_glowLevel(0.0f),
_id(UNKNOWN_MODEL_ID),
_idSet(false),
@ -817,6 +820,7 @@ ModelItemProperties::ModelItemProperties() :
_animationIsPlayingChanged(false),
_animationFrameIndexChanged(false),
_animationFPSChanged(false),
_glowLevelChanged(false),
_defaultSettings(true)
{
}
@ -890,6 +894,7 @@ QScriptValue ModelItemProperties::copyToScriptValue(QScriptEngine* engine) const
properties.setProperty("animationIsPlaying", _animationIsPlaying);
properties.setProperty("animationFrameIndex", _animationFrameIndex);
properties.setProperty("animationFPS", _animationFPS);
properties.setProperty("glowLevel", _glowLevel);
if (_idSet) {
properties.setProperty("id", _id);
@ -1015,7 +1020,7 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) {
_animationFrameIndexChanged = true;
}
}
QScriptValue animationFPS = object.property("animationFPS");
if (animationFPS.isValid()) {
float newFPS;
@ -1025,6 +1030,16 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) {
_animationFPSChanged = true;
}
}
QScriptValue glowLevel = object.property("glowLevel");
if (glowLevel.isValid()) {
float newGlowLevel;
newGlowLevel = glowLevel.toVariant().toFloat();
if (_defaultSettings || newGlowLevel != _glowLevel) {
_glowLevel = newGlowLevel;
_glowLevelChanged = true;
}
}
_lastEdited = usecTimestampNow();
}
@ -1075,11 +1090,16 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const {
modelItem.setAnimationFrameIndex(_animationFrameIndex);
somethingChanged = true;
}
if (_animationFPSChanged) {
modelItem.setAnimationFPS(_animationFPS);
somethingChanged = true;
}
if (_glowLevelChanged) {
modelItem.setGlowLevel(_glowLevel);
somethingChanged = true;
}
if (somethingChanged) {
bool wantDebug = false;
@ -1104,6 +1124,7 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) {
_animationIsPlaying = modelItem.getAnimationIsPlaying();
_animationFrameIndex = modelItem.getAnimationFrameIndex();
_animationFPS = modelItem.getAnimationFPS();
_glowLevel = modelItem.getGlowLevel();
_id = modelItem.getID();
_idSet = true;
@ -1119,6 +1140,7 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) {
_animationIsPlayingChanged = false;
_animationFrameIndexChanged = false;
_animationFPSChanged = false;
_glowLevelChanged = false;
_defaultSettings = false;
}

View file

@ -83,6 +83,7 @@ public:
float getAnimationFrameIndex() const { return _animationFrameIndex; }
bool getAnimationIsPlaying() const { return _animationIsPlaying; }
float getAnimationFPS() const { return _animationFPS; }
float getGlowLevel() const { return _glowLevel; }
quint64 getLastEdited() const { return _lastEdited; }
uint16_t getChangedBits() const;
@ -100,6 +101,7 @@ public:
void setAnimationFrameIndex(float value) { _animationFrameIndex = value; _animationFrameIndexChanged = true; }
void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; _animationIsPlayingChanged = true; }
void setAnimationFPS(float value) { _animationFPS = value; _animationFPSChanged = true; }
void setGlowLevel(float value) { _glowLevel = value; _glowLevelChanged = true; }
/// used by ModelScriptingInterface to return ModelItemProperties for unknown models
void setIsUnknownID() { _id = UNKNOWN_MODEL_ID; _idSet = true; }
@ -119,6 +121,7 @@ private:
bool _animationIsPlaying;
float _animationFrameIndex;
float _animationFPS;
float _glowLevel;
uint32_t _id;
bool _idSet;
@ -135,6 +138,7 @@ private:
bool _animationIsPlayingChanged;
bool _animationFrameIndexChanged;
bool _animationFPSChanged;
bool _glowLevelChanged;
bool _defaultSettings;
};
Q_DECLARE_METATYPE(ModelItemProperties);
@ -206,6 +210,7 @@ public:
const glm::quat& getModelRotation() const { return _modelRotation; }
bool hasAnimation() const { return !_animationURL.isEmpty(); }
const QString& getAnimationURL() const { return _animationURL; }
float getGlowLevel() const { return _glowLevel; }
ModelItemID getModelItemID() const { return ModelItemID(getID(), getCreatorTokenID(), getID() != UNKNOWN_MODEL_ID); }
ModelItemProperties getProperties() const;
@ -248,6 +253,7 @@ public:
void setAnimationFrameIndex(float value) { _animationFrameIndex = value; }
void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; }
void setAnimationFPS(float value) { _animationFPS = value; }
void setGlowLevel(float glowLevel) { _glowLevel = glowLevel; }
void setProperties(const ModelItemProperties& properties);
@ -293,6 +299,8 @@ protected:
// model related items
QString _modelURL;
glm::quat _modelRotation;
float _glowLevel;
uint32_t _creatorTokenID;
bool _newlyCreated;

View file

@ -83,7 +83,7 @@ void DataServerAccountInfo::setDiscourseApiKey(const QString& discourseApiKey) {
}
}
void DataServerAccountInfo::setBalance(quint64 balance) {
void DataServerAccountInfo::setBalance(qint64 balance) {
if (!_hasBalance || _balance != balance) {
_balance = balance;
_hasBalance = true;

View file

@ -16,6 +16,8 @@
#include "OAuthAccessToken.h"
const float SATOSHIS_PER_CREDIT = 100000000.0f;
class DataServerAccountInfo : public QObject {
Q_OBJECT
public:
@ -35,8 +37,8 @@ public:
const QString& getDiscourseApiKey() const { return _discourseApiKey; }
void setDiscourseApiKey(const QString& discourseApiKey);
quint64 getBalance() const { return _balance; }
void setBalance(quint64 balance);
qint64 getBalance() const { return _balance; }
void setBalance(qint64 balance);
bool hasBalance() const { return _hasBalance; }
void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; }
Q_INVOKABLE void setBalanceFromJSON(const QJsonObject& jsonObject);
@ -52,7 +54,7 @@ private:
QString _username;
QString _xmppPassword;
QString _discourseApiKey;
quint64 _balance;
qint64 _balance;
bool _hasBalance;
};