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(); loop.exec();
// let the AvatarData and ResourceCache classes use our QNetworkAccessManager // let the AvatarData and ResourceCache classes use our QNetworkAccessManager
AvatarData::setNetworkAccessManager(networkManager); AvatarData::setNetworkAccessManager(networkManager);
ResourceCache::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_HOUR = 0.10f;
const float CREDITS_PER_MSEC = CREDITS_PER_HOUR / (60 * 60 * 1000); 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; float pendingCredits = elapsedMsecsSinceLastPayment * SATOSHIS_PER_MSEC;
@ -884,10 +884,10 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) {
if (!nodeData->getWalletUUID().isNull()) { if (!nodeData->getWalletUUID().isNull()) {
TransactionHash::iterator i = _pendingAssignmentCredits.find(nodeData->getWalletUUID()); TransactionHash::iterator i = _pendingAssignmentCredits.find(nodeData->getWalletUUID());
double pendingCreditAmount = 0; float pendingCreditAmount = 0;
while (i != _pendingAssignmentCredits.end() && i.key() == nodeData->getWalletUUID()) { while (i != _pendingAssignmentCredits.end() && i.key() == nodeData->getWalletUUID()) {
pendingCreditAmount += i.value()->getAmount(); pendingCreditAmount += i.value()->getAmount() / SATOSHIS_PER_CREDIT;
++i; ++i;
} }
@ -1004,6 +1004,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
return true; return true;
} else { } 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); const QString NODE_JSON_REGEX_STRING = QString("\\%1\\/(%2).json\\/?$").arg(URI_NODES).arg(UUID_REGEX_STRING);
QRegExp nodeShowRegex(NODE_JSON_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 // tell the caller we processed the request
return true; 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) { } else if (connection->requestOperation() == QNetworkAccessManager::PostOperation) {

View file

@ -18,13 +18,13 @@
WalletTransaction::WalletTransaction() : WalletTransaction::WalletTransaction() :
_uuid(), _uuid(),
_destinationUUID(), _destinationUUID(),
_amount(), _amount(0),
_isFinalized(false) _isFinalized(false)
{ {
} }
WalletTransaction::WalletTransaction(const QUuid& destinationUUID, double amount) : WalletTransaction::WalletTransaction(const QUuid& destinationUUID, qint64 amount) :
_uuid(QUuid::createUuid()), _uuid(QUuid::createUuid()),
_destinationUUID(destinationUUID), _destinationUUID(destinationUUID),
_amount(amount), _amount(amount),
@ -63,5 +63,5 @@ void WalletTransaction::loadFromJson(const QJsonObject& jsonObject) {
_uuid = QUuid(transactionObject.value(TRANSACTION_ID_KEY).toString()); _uuid = QUuid(transactionObject.value(TRANSACTION_ID_KEY).toString());
_destinationUUID = QUuid(transactionObject.value(TRANSACTION_DESTINATION_WALLET_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 { class WalletTransaction : public QObject {
public: public:
WalletTransaction(); WalletTransaction();
WalletTransaction(const QUuid& destinationUUID, double amount); WalletTransaction(const QUuid& destinationUUID, qint64 amount);
const QUuid& getUUID() const { return _uuid; } const QUuid& getUUID() const { return _uuid; }
void setDestinationUUID(const QUuid& destinationUUID) { _destinationUUID = destinationUUID; } void setDestinationUUID(const QUuid& destinationUUID) { _destinationUUID = destinationUUID; }
const QUuid& getDestinationUUID() const { return _destinationUUID; } const QUuid& getDestinationUUID() const { return _destinationUUID; }
double getAmount() const { return _amount; } qint64 getAmount() const { return _amount; }
void setAmount(double amount) { _amount = amount; } void setAmount(qint64 amount) { _amount = amount; }
void incrementAmount(double increment) { _amount += increment; } void incrementAmount(qint64 increment) { _amount += increment; }
bool isFinalized() const { return _isFinalized; } bool isFinalized() const { return _isFinalized; }
void setIsFinalized(bool isFinalized) { _isFinalized = isFinalized; } void setIsFinalized(bool isFinalized) { _isFinalized = isFinalized; }
@ -39,7 +39,7 @@ public:
private: private:
QUuid _uuid; QUuid _uuid;
QUuid _destinationUUID; QUuid _destinationUUID;
double _amount; qint64 _amount;
bool _isFinalized; bool _isFinalized;
}; };

View file

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

View file

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

View file

@ -3,22 +3,22 @@
// examples // examples
// //
// Copyright 2014 High Fidelity, Inc. // 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. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// First, load the clap sound from a URL // First, load a sample sound from a URL
var clap = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw"); var bird = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw");
function maybePlaySound(deltaTime) { function maybePlaySound(deltaTime) {
if (Math.random() < 0.01) { if (Math.random() < 0.01) {
// Set the location and other info for the sound to play // Set the location and other info for the sound to play
var options = new AudioInjectionOptions(); var options = new AudioInjectionOptions();
var palmPosition = Controller.getSpatialControlPosition(0); var position = MyAvatar.position;
options.position = palmPosition; options.position = position;
options.volume = 0.5; 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 // and the texture coordinates
gl_TexCoord[0] = gl_MultiTexCoord0; 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 // use standard pipeline transform
gl_Position = ftransform(); gl_Position = ftransform();
} }

View file

@ -36,6 +36,10 @@ void main(void) {
// and the texture coordinates // and the texture coordinates
gl_TexCoord[0] = gl_MultiTexCoord0; 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 // use standard pipeline transform
gl_Position = ftransform(); 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 // and the texture coordinates
gl_TexCoord[0] = gl_MultiTexCoord0; 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; gl_Position = gl_ProjectionMatrix * position;
} }

View file

@ -52,5 +52,9 @@ void main(void) {
// and the texture coordinates // and the texture coordinates
gl_TexCoord[0] = gl_MultiTexCoord0; 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; gl_Position = gl_ProjectionMatrix * interpolatedPosition;
} }

View file

@ -172,7 +172,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_nodeBoundsDisplay(this), _nodeBoundsDisplay(this),
_previousScriptLocation(), _previousScriptLocation(),
_runningScriptsWidget(new RunningScriptsWidget(_window)), _runningScriptsWidget(new RunningScriptsWidget(_window)),
_runningScriptsWidgetWasVisible(false) _runningScriptsWidgetWasVisible(false),
_trayIcon(new QSystemTrayIcon(_window))
{ {
// read the ApplicationInfo.ini file for Name/Version/Domain information // read the ApplicationInfo.ini file for Name/Version/Domain information
QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat); QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat);
@ -391,6 +392,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
OAuthWebViewHandler::getInstance(); OAuthWebViewHandler::getInstance();
// make sure the High Fidelity root CA is in our list of trusted certs // make sure the High Fidelity root CA is in our list of trusted certs
OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig(); OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig();
_trayIcon->show();
} }
Application::~Application() { Application::~Application() {
@ -2325,10 +2328,15 @@ void Application::updateShadowMap() {
// store view matrix without translation, which we'll use for precision-sensitive objects // store view matrix without translation, which we'll use for precision-sensitive objects
updateUntranslatedViewMatrix(); 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); _avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE);
_particles.render(OctreeRenderer::SHADOW_RENDER_MODE); _particles.render(OctreeRenderer::SHADOW_RENDER_MODE);
_models.render(OctreeRenderer::SHADOW_RENDER_MODE); _models.render(OctreeRenderer::SHADOW_RENDER_MODE);
glDisable(GL_POLYGON_OFFSET_FILL);
glPopMatrix(); glPopMatrix();
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
@ -2534,6 +2542,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
if (_voxelFades.size() > 0) { if (_voxelFades.size() > 0) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... voxel fades..."); "Application::displaySide() ... voxel fades...");
_voxelFadesLock.lockForWrite();
for(std::vector<VoxelFade>::iterator fade = _voxelFades.begin(); fade != _voxelFades.end();) { for(std::vector<VoxelFade>::iterator fade = _voxelFades.begin(); fade != _voxelFades.end();) {
fade->render(); fade->render();
if(fade->isDone()) { if(fade->isDone()) {
@ -2542,6 +2551,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
++fade; ++fade;
} }
} }
_voxelFadesLock.unlock();
} }
// give external parties a change to hook in // give external parties a change to hook in
@ -3106,7 +3116,7 @@ void Application::updateWindowTitle(){
AccountManager& accountManager = AccountManager::getInstance(); AccountManager& accountManager = AccountManager::getInstance();
if (accountManager.getAccountInfo().hasBalance()) { if (accountManager.getAccountInfo().hasBalance()) {
float creditBalance = accountManager.getAccountInfo().getBalance() * pow(10.0f, -8.0f); float creditBalance = accountManager.getAccountInfo().getBalance() / SATOSHIS_PER_CREDIT;
QString creditBalanceString; QString creditBalanceString;
creditBalanceString.sprintf("%.8f", creditBalance); creditBalanceString.sprintf("%.8f", creditBalance);
@ -3208,7 +3218,9 @@ void Application::nodeKilled(SharedNodePointer node) {
fade.voxelDetails = rootDetails; fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99f; const float slightly_smaller = 0.99f;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller; fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFadesLock.lockForWrite();
_voxelFades.push_back(fade); _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 // 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; fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99f; const float slightly_smaller = 0.99f;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller; fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFadesLock.lockForWrite();
_voxelFades.push_back(fade); _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 // 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; fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99f; const float slightly_smaller = 0.99f;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller; fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFadesLock.lockForWrite();
_voxelFades.push_back(fade); _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 // 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; fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99f; const float slightly_smaller = 0.99f;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller; fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFadesLock.lockForWrite();
_voxelFades.push_back(fade); _voxelFades.push_back(fade);
_voxelFadesLock.unlock();
} }
} }
// store jurisdiction details for later use // store jurisdiction details for later use

View file

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

View file

@ -34,6 +34,7 @@
#include <UUID.h> #include <UUID.h>
#include "Application.h" #include "Application.h"
#include "AccountManager.h"
#include "Menu.h" #include "Menu.h"
#include "scripting/MenuScriptingInterface.h" #include "scripting/MenuScriptingInterface.h"
#include "Util.h" #include "Util.h"
@ -200,7 +201,8 @@ Menu::Menu() :
QObject* avatar = appInstance->getAvatar(); 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())); avatar, SLOT(updateMotionBehaviorsFromMenu()));
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::StandOnNearbyFloors, 0, true,
avatar, SLOT(updateMotionBehaviorsFromMenu()));
addAvatarCollisionSubMenu(editMenu); addAvatarCollisionSubMenu(editMenu);
@ -331,6 +333,7 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::AllowOculusCameraModeChange, 0, false); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::AllowOculusCameraModeChange, 0, false);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::AvatarsReceiveShadows, 0, true);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderSkeletonCollisionShapes); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderSkeletonCollisionShapes);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderHeadCollisionShapes); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderHeadCollisionShapes);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderBoundingCollisionShapes); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderBoundingCollisionShapes);
@ -1200,19 +1203,24 @@ void Menu::showScriptEditor() {
} }
void Menu::showChat() { void Menu::showChat() {
if (AccountManager::getInstance().isLoggedIn()) {
QMainWindow* mainWindow = Application::getInstance()->getWindow(); QMainWindow* mainWindow = Application::getInstance()->getWindow();
if (!_chatWindow) { if (!_chatWindow) {
_chatWindow = new ChatWindow(mainWindow); _chatWindow = new ChatWindow(mainWindow);
} }
if (_chatWindow->isHidden()) { if (_chatWindow->isHidden()) {
_chatWindow->show(); _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() { void Menu::toggleChat() {
#ifdef HAVE_QXMPP #ifdef HAVE_QXMPP
_chatAction->setEnabled(XmppClient::getInstance().getXMPPClient().isConnected()); _chatAction->setEnabled(XmppClient::getInstance().getXMPPClient().isConnected());
if (!_chatAction->isEnabled() && _chatWindow) { if (!_chatAction->isEnabled() && _chatWindow && AccountManager::getInstance().isLoggedIn()) {
if (_chatWindow->isHidden()) { if (_chatWindow->isHidden()) {
_chatWindow->show(); _chatWindow->show();
} else { } else {

View file

@ -293,6 +293,7 @@ namespace MenuOption {
const QString AudioSpatialProcessingDontDistanceAttenuate = "Don't calculate distance attenuation"; const QString AudioSpatialProcessingDontDistanceAttenuate = "Don't calculate distance attenuation";
const QString AudioSpatialProcessingAlternateDistanceAttenuate = "Alternate distance attenuation"; const QString AudioSpatialProcessingAlternateDistanceAttenuate = "Alternate distance attenuation";
const QString Avatars = "Avatars"; const QString Avatars = "Avatars";
const QString AvatarsReceiveShadows = "Avatars Receive Shadows";
const QString Bandwidth = "Bandwidth Display"; const QString Bandwidth = "Bandwidth Display";
const QString BandwidthDetails = "Bandwidth Details"; const QString BandwidthDetails = "Bandwidth Details";
const QString BuckyBalls = "Bucky Balls"; const QString BuckyBalls = "Bucky Balls";
@ -376,6 +377,7 @@ namespace MenuOption {
const QString ShowBordersModelNodes = "Show Model Nodes"; const QString ShowBordersModelNodes = "Show Model Nodes";
const QString ShowBordersParticleNodes = "Show Particle Nodes"; const QString ShowBordersParticleNodes = "Show Particle Nodes";
const QString ShowIKConstraints = "Show IK Constraints"; const QString ShowIKConstraints = "Show IK Constraints";
const QString StandOnNearbyFloors = "Stand on nearby floors";
const QString Stars = "Stars"; const QString Stars = "Stars";
const QString Stats = "Stats"; const QString Stats = "Stats";
const QString StopAllScripts = "Stop All Scripts"; 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) { void Avatar::renderBody(RenderMode renderMode, float glowLevel) {
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ? Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
{ {
Glower glower(glowLevel); Glower glower(glowLevel);
@ -356,7 +355,8 @@ void Avatar::renderBody(RenderMode renderMode, float glowLevel) {
renderBillboard(); renderBillboard();
return; return;
} }
_skeletonModel.render(1.0f, modelRenderMode);
_skeletonModel.render(1.0f, modelRenderMode, Menu::getInstance()->isOptionChecked(MenuOption::AvatarsReceiveShadows));
renderAttachments(renderMode); renderAttachments(renderMode);
getHand()->render(false, modelRenderMode); getHand()->render(false, modelRenderMode);
} }
@ -390,8 +390,9 @@ void Avatar::simulateAttachments(float deltaTime) {
void Avatar::renderAttachments(RenderMode renderMode) { void Avatar::renderAttachments(RenderMode renderMode) {
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ? Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
bool receiveShadows = Menu::getInstance()->isOptionChecked(MenuOption::AvatarsReceiveShadows);
foreach (Model* model, _attachmentModels) { 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) { 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); renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition);
} }
} }

View file

@ -65,6 +65,7 @@ MyAvatar::MyAvatar() :
_distanceToNearestAvatar(std::numeric_limits<float>::max()), _distanceToNearestAvatar(std::numeric_limits<float>::max()),
_wasPushing(false), _wasPushing(false),
_isPushing(false), _isPushing(false),
_isBraking(false),
_trapDuration(0.0f), _trapDuration(0.0f),
_thrust(0.0f), _thrust(0.0f),
_motorVelocity(0.0f), _motorVelocity(0.0f),
@ -134,45 +135,7 @@ void MyAvatar::simulate(float deltaTime) {
_handState = HAND_STATE_NULL; _handState = HAND_STATE_NULL;
updateOrientation(deltaTime); updateOrientation(deltaTime);
updatePosition(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);
// update avatar skeleton and simulate hand and head // update avatar skeleton and simulate hand and head
getHand()->collideAgainstOurself(); getHand()->collideAgainstOurself();
@ -206,7 +169,6 @@ void MyAvatar::simulate(float deltaTime) {
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.0f)); radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.0f));
radius *= COLLISION_RADIUS_SCALAR; radius *= COLLISION_RADIUS_SCALAR;
} }
if (_collisionGroups) {
updateShapePositions(); updateShapePositions();
if (_collisionGroups & COLLISION_GROUP_ENVIRONMENT) { if (_collisionGroups & COLLISION_GROUP_ENVIRONMENT) {
updateCollisionWithEnvironment(deltaTime, radius); updateCollisionWithEnvironment(deltaTime, radius);
@ -220,7 +182,6 @@ void MyAvatar::simulate(float deltaTime) {
updateCollisionWithAvatars(deltaTime); updateCollisionWithAvatars(deltaTime);
} }
} }
}
// consider updating our billboard // consider updating our billboard
maybeUpdateBillboard(); maybeUpdateBillboard();
@ -423,9 +384,9 @@ void MyAvatar::setGravity(const glm::vec3& gravity) {
float gravityLength = glm::length(gravity); float gravityLength = glm::length(gravity);
if (gravityLength > EPSILON) { if (gravityLength > EPSILON) {
_worldUpDirection = _gravity / -gravityLength; _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() { AnimationHandlePointer MyAvatar::addAnimationHandle() {
@ -812,7 +773,7 @@ void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) {
// Render the body's voxels and head // Render the body's voxels and head
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ? Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
Model::SHADOW_RENDER_MODE : Model::DEFAULT_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); renderAttachments(renderMode);
// Render head so long as the camera isn't inside it // 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); (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) { void MyAvatar::updateOrientation(float deltaTime) {
// Gather rotation information from keyboard // Gather rotation information from keyboard
_bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime; _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 // 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 // doing yaw math) because the body's yaw ranges are not the same
// as what the Oculus API provides. // 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), _worldUpDirection);
glm::quat bodyCorrection = glm::angleAxis(glm::radians(delta), UP_AXIS);
orientation = orientation * bodyCorrection; orientation = orientation * bodyCorrection;
} }
Head* head = getHead(); Head* head = getHead();
@ -905,6 +874,96 @@ void MyAvatar::updateOrientation(float deltaTime) {
setOrientation(orientation); 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) { void MyAvatar::updateMotorFromKeyboard(float deltaTime, bool walking) {
// Increase motor velocity until its length is equal to _maxMotorSpeed. // Increase motor velocity until its length is equal to _maxMotorSpeed.
if (!(_motionBehaviors & AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED)) { if (!(_motionBehaviors & AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED)) {
@ -930,12 +989,12 @@ void MyAvatar::updateMotorFromKeyboard(float deltaTime, bool walking) {
if (directionLength > EPSILON) { if (directionLength > EPSILON) {
direction /= directionLength; direction /= directionLength;
// the finalMotorSpeed depends on whether we are walking or not // 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); 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 // an active keyboard motor should never be slower than this
_motorVelocity = MIN_KEYBOARD_CONTROL_SPEED * direction; _motorVelocity = _scale * MIN_KEYBOARD_CONTROL_SPEED * direction;
} else { } else {
float MOTOR_LENGTH_TIMESCALE = 1.5f; float MOTOR_LENGTH_TIMESCALE = 1.5f;
float tau = glm::clamp(deltaTime / MOTOR_LENGTH_TIMESCALE, 0.0f, 1.0f); 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) // (1) braking --> short timescale (aggressive motor assertion)
// (2) pushing --> medium timescale (mild motor assertion) // (2) pushing --> medium timescale (mild motor assertion)
// (3) inactive --> long timescale (gentle friction for low speeds) // (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 MIN_MOTOR_TIMESCALE = 0.125f;
float MAX_MOTOR_TIMESCALE = 0.5f; float MAX_MOTOR_TIMESCALE = 0.5f;
float MIN_BRAKE_SPEED = 0.4f; float MIN_BRAKE_SPEED = 0.4f;
float timescale = MAX_MOTOR_TIMESCALE; float timescale = MAX_MOTOR_TIMESCALE;
float speed = glm::length(_velocity); bool isThrust = (glm::length2(_thrust) > EPSILON);
bool areThrusting = (glm::length2(_thrust) > EPSILON); if (_isPushing || isThrust) {
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;
} else {
if (_isPushing) {
timescale = _motorTimescale; timescale = _motorTimescale;
_isBraking = false;
} else {
float speed = glm::length(_velocity);
_isBraking = _wasPushing || (_isBraking && speed > MIN_BRAKE_SPEED);
if (_isBraking) {
timescale = MIN_MOTOR_TIMESCALE;
} }
_wasPushing = _isPushing || areThrusting;
} }
_wasPushing = _isPushing || isThrust;
_isPushing = false; _isPushing = false;
return timescale; return timescale;
} }
void MyAvatar::applyMotor(float deltaTime) { void MyAvatar::applyMotor(float deltaTime) {
// TODO: recover extra braking behavior when flying close to nearest avatar
if (!( _motionBehaviors & AVATAR_MOTION_MOTOR_ENABLED)) { if (!( _motionBehaviors & AVATAR_MOTION_MOTOR_ENABLED)) {
// nothing to do --> early exit // nothing to do --> early exit
return; return;
@ -1178,6 +1235,8 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
const float MIN_STEP_HEIGHT = 0.0f; const float MIN_STEP_HEIGHT = 0.0f;
glm::vec3 footBase = boundingShape.getPosition() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection; glm::vec3 footBase = boundingShape.getPosition() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection;
float highestStep = 0.0f; float highestStep = 0.0f;
float lowestStep = MAX_STEP_HEIGHT;
glm::vec3 floorPoint;
glm::vec3 stepPenetration(0.0f); glm::vec3 stepPenetration(0.0f);
glm::vec3 totalPenetration(0.0f); glm::vec3 totalPenetration(0.0f);
@ -1211,8 +1270,15 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
highestStep = stepHeight; highestStep = stepHeight;
stepPenetration = collision->_penetration; 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); float penetrationLength = glm::length(totalPenetration);
if (penetrationLength < EPSILON) { if (penetrationLength < EPSILON) {
@ -1251,8 +1317,9 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING); applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
} }
const float VOXEL_COLLISION_FREQUENCY = 0.5f; // Don't make a collision sound against voxlels by default -- too annoying when walking
updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY); //const float VOXEL_COLLISION_FREQUENCY = 0.5f;
//updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
} }
_trapDuration = isTrapped ? _trapDuration + deltaTime : 0.0f; _trapDuration = isTrapped ? _trapDuration + deltaTime : 0.0f;
} }
@ -1598,7 +1665,8 @@ void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) {
} }
void MyAvatar::updateMotionBehaviorsFromMenu() { void MyAvatar::updateMotionBehaviorsFromMenu() {
if (Menu::getInstance()->isOptionChecked(MenuOption::ObeyEnvironmentalGravity)) { Menu* menu = Menu::getInstance();
if (menu->isOptionChecked(MenuOption::ObeyEnvironmentalGravity)) {
_motionBehaviors |= AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY; _motionBehaviors |= AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY;
// Environmental and Local gravities are incompatible. Environmental setting trumps local. // Environmental and Local gravities are incompatible. Environmental setting trumps local.
_motionBehaviors &= ~AVATAR_MOTION_OBEY_LOCAL_GRAVITY; _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))) { if (! (_motionBehaviors & (AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY | AVATAR_MOTION_OBEY_LOCAL_GRAVITY))) {
setGravity(glm::vec3(0.0f)); 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) { 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; QString headJointName = (geometry.headJointIndex == -1) ? QString() : geometry.joints.at(geometry.headJointIndex).name;
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ? Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
Model::SHADOW_RENDER_MODE : Model::DEFAULT_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++) { for (int i = 0; i < _attachmentData.size(); i++) {
const QString& jointName = _attachmentData.at(i).jointName; const QString& jointName = _attachmentData.at(i).jointName;
if (jointName != headJointName && jointName != "Head") { 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::CollideWithAvatars, (bool)(_collisionGroups & COLLISION_GROUP_AVATARS));
menu->setIsOptionChecked(MenuOption::CollideWithVoxels, (bool)(_collisionGroups & COLLISION_GROUP_VOXELS)); menu->setIsOptionChecked(MenuOption::CollideWithVoxels, (bool)(_collisionGroups & COLLISION_GROUP_VOXELS));
menu->setIsOptionChecked(MenuOption::CollideWithParticles, (bool)(_collisionGroups & COLLISION_GROUP_PARTICLES)); 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) { void MyAvatar::setMotionBehaviorsByScript(quint32 flags) {

View file

@ -150,11 +150,11 @@ private:
bool _shouldJump; bool _shouldJump;
float _driveKeys[MAX_DRIVE_KEYS]; float _driveKeys[MAX_DRIVE_KEYS];
glm::vec3 _gravity; glm::vec3 _gravity;
glm::vec3 _environmentGravity;
float _distanceToNearestAvatar; // How close is the nearest avatar? float _distanceToNearestAvatar; // How close is the nearest avatar?
bool _wasPushing; bool _wasPushing;
bool _isPushing; bool _isPushing;
bool _isBraking;
float _trapDuration; // seconds that avatar has been trapped by collisions float _trapDuration; // seconds that avatar has been trapped by collisions
glm::vec3 _thrust; // final acceleration from outside sources for the current frame glm::vec3 _thrust; // final acceleration from outside sources for the current frame
@ -174,7 +174,9 @@ private:
QList<AnimationHandlePointer> _animationHandles; QList<AnimationHandlePointer> _animationHandles;
// private methods // private methods
float computeDistanceToFloor(const glm::vec3& startPoint);
void updateOrientation(float deltaTime); void updateOrientation(float deltaTime);
void updatePosition(float deltaTime);
void updateMotorFromKeyboard(float deltaTime, bool walking); void updateMotorFromKeyboard(float deltaTime, bool walking);
float computeMotorTimescale(); float computeMotorTimescale();
void applyMotor(float deltaTime); 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? // TODO: should we allow modelItems to have alpha on their models?
Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE
? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
if (modelItem.getGlowLevel() > 0.0f) {
Glower glower(modelItem.getGlowLevel());
model->render(alpha, modelRenderMode); model->render(alpha, modelRenderMode);
} else {
model->render(alpha, modelRenderMode);
}
if (!isShadowMode && displayModelBounds) { if (!isShadowMode && displayModelBounds) {

View file

@ -59,18 +59,39 @@ ProgramObject Model::_program;
ProgramObject Model::_normalMapProgram; ProgramObject Model::_normalMapProgram;
ProgramObject Model::_specularMapProgram; ProgramObject Model::_specularMapProgram;
ProgramObject Model::_normalSpecularMapProgram; ProgramObject Model::_normalSpecularMapProgram;
ProgramObject Model::_shadowMapProgram;
ProgramObject Model::_shadowNormalMapProgram;
ProgramObject Model::_shadowSpecularMapProgram;
ProgramObject Model::_shadowNormalSpecularMapProgram;
ProgramObject Model::_shadowProgram; ProgramObject Model::_shadowProgram;
ProgramObject Model::_skinProgram; ProgramObject Model::_skinProgram;
ProgramObject Model::_skinNormalMapProgram; ProgramObject Model::_skinNormalMapProgram;
ProgramObject Model::_skinSpecularMapProgram; ProgramObject Model::_skinSpecularMapProgram;
ProgramObject Model::_skinNormalSpecularMapProgram; ProgramObject Model::_skinNormalSpecularMapProgram;
ProgramObject Model::_skinShadowMapProgram;
ProgramObject Model::_skinShadowNormalMapProgram;
ProgramObject Model::_skinShadowSpecularMapProgram;
ProgramObject Model::_skinShadowNormalSpecularMapProgram;
ProgramObject Model::_skinShadowProgram; ProgramObject Model::_skinShadowProgram;
int Model::_normalMapTangentLocation; int Model::_normalMapTangentLocation;
int Model::_normalSpecularMapTangentLocation; int Model::_normalSpecularMapTangentLocation;
int Model::_shadowNormalMapTangentLocation;
int Model::_shadowNormalSpecularMapTangentLocation;
Model::SkinLocations Model::_skinLocations; Model::SkinLocations Model::_skinLocations;
Model::SkinLocations Model::_skinNormalMapLocations; Model::SkinLocations Model::_skinNormalMapLocations;
Model::SkinLocations Model::_skinSpecularMapLocations; Model::SkinLocations Model::_skinSpecularMapLocations;
Model::SkinLocations Model::_skinNormalSpecularMapLocations; Model::SkinLocations Model::_skinNormalSpecularMapLocations;
Model::SkinLocations Model::_skinShadowMapLocations;
Model::SkinLocations Model::_skinShadowNormalMapLocations;
Model::SkinLocations Model::_skinShadowSpecularMapLocations;
Model::SkinLocations Model::_skinShadowNormalSpecularMapLocations;
Model::SkinLocations Model::_skinShadowLocations; Model::SkinLocations Model::_skinShadowLocations;
void Model::setScale(const glm::vec3& scale) { 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(); program.bind();
locations.clusterMatrices = program.uniformLocation("clusterMatrices"); locations.clusterMatrices = program.uniformLocation("clusterMatrices");
locations.clusterIndices = program.attributeLocation("clusterIndices"); locations.clusterIndices = program.attributeLocation("clusterIndices");
@ -109,6 +131,7 @@ void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locati
program.setUniformValue("diffuseMap", 0); program.setUniformValue("diffuseMap", 0);
program.setUniformValue("normalMap", 1); program.setUniformValue("normalMap", 1);
program.setUniformValue("specularMap", specularTextureUnit); program.setUniformValue("specularMap", specularTextureUnit);
program.setUniformValue("shadowMap", shadowTextureUnit);
program.release(); program.release();
} }
@ -170,7 +193,7 @@ void Model::init() {
_program.link(); _program.link();
_program.bind(); _program.bind();
_program.setUniformValue("texture", 0); _program.setUniformValue("diffuseMap", 0);
_program.release(); _program.release();
_normalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, _normalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
@ -209,11 +232,63 @@ void Model::init() {
_normalSpecularMapTangentLocation = _normalMapProgram.attributeLocation("tangent"); _normalSpecularMapTangentLocation = _normalMapProgram.attributeLocation("tangent");
_normalSpecularMapProgram.release(); _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::Vertex, Application::resourcesPath() + "shaders/model_shadow.vert");
_shadowProgram.addShaderFromSourceFile(QGLShader::Fragment, _shadowProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_shadow.frag"); Application::resourcesPath() + "shaders/model_shadow.frag");
_shadowProgram.link(); _shadowProgram.link();
_skinProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/skin_model.vert"); _skinProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/skin_model.vert");
_skinProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/model.frag"); _skinProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/model.frag");
_skinProgram.link(); _skinProgram.link();
@ -244,6 +319,40 @@ void Model::init() {
initSkinProgram(_skinNormalSpecularMapProgram, _skinNormalSpecularMapLocations, 2); 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, _skinShadowProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/skin_model_shadow.vert"); Application::resourcesPath() + "shaders/skin_model_shadow.vert");
_skinShadowProgram.addShaderFromSourceFile(QGLShader::Fragment, _skinShadowProgram.addShaderFromSourceFile(QGLShader::Fragment,
@ -361,7 +470,7 @@ bool Model::updateGeometry() {
return needFullUpdate; return needFullUpdate;
} }
bool Model::render(float alpha, RenderMode mode) { bool Model::render(float alpha, RenderMode mode, bool receiveShadows) {
// render the attachments // render the attachments
foreach (Model* attachment, _attachments) { foreach (Model* attachment, _attachments) {
attachment->render(alpha, mode); attachment->render(alpha, mode);
@ -389,6 +498,9 @@ bool Model::render(float alpha, RenderMode mode) {
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
} else { } else {
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
if (mode == SHADOW_RENDER_MODE) {
glCullFace(GL_FRONT);
}
} }
// render opaque meshes with alpha testing // render opaque meshes with alpha testing
@ -396,16 +508,21 @@ bool Model::render(float alpha, RenderMode mode) {
glEnable(GL_ALPHA_TEST); glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.5f * alpha); 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); glDisable(GL_ALPHA_TEST);
// render translucent meshes afterwards // render translucent meshes afterwards
renderMeshes(alpha, mode, true); renderMeshes(alpha, mode, true, receiveShadows);
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
if (mode == SHADOW_RENDER_MODE) {
glCullFace(GL_BACK);
}
// deactivate vertex arrays after drawing // deactivate vertex arrays after drawing
glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_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 FBXGeometry& geometry = _geometry->getFBXGeometry();
const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes(); 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++) { for (int i = 0; i < networkMeshes.size(); i++) {
// exit early if the translucency doesn't match what we're drawing // exit early if the translucency doesn't match what we're drawing
const NetworkMesh& networkMesh = networkMeshes.at(i); const NetworkMesh& networkMesh = networkMeshes.at(i);
@ -1506,6 +1628,7 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
ProgramObject* skinProgram = &_skinProgram; ProgramObject* skinProgram = &_skinProgram;
SkinLocations* skinLocations = &_skinLocations; SkinLocations* skinLocations = &_skinLocations;
GLenum specularTextureUnit = 0; GLenum specularTextureUnit = 0;
GLenum shadowTextureUnit = 0;
if (mode == SHADOW_RENDER_MODE) { if (mode == SHADOW_RENDER_MODE) {
program = &_shadowProgram; program = &_shadowProgram;
skinProgram = &_skinShadowProgram; skinProgram = &_skinShadowProgram;
@ -1513,21 +1636,46 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
} else if (!mesh.tangents.isEmpty()) { } else if (!mesh.tangents.isEmpty()) {
if (mesh.hasSpecularTexture()) { if (mesh.hasSpecularTexture()) {
if (receiveShadows) {
program = &_shadowNormalSpecularMapProgram;
skinProgram = &_skinShadowNormalSpecularMapProgram;
skinLocations = &_skinShadowNormalSpecularMapLocations;
shadowTextureUnit = GL_TEXTURE3;
} else {
program = &_normalSpecularMapProgram; program = &_normalSpecularMapProgram;
skinProgram = &_skinNormalSpecularMapProgram; skinProgram = &_skinNormalSpecularMapProgram;
skinLocations = &_skinNormalSpecularMapLocations; skinLocations = &_skinNormalSpecularMapLocations;
}
specularTextureUnit = GL_TEXTURE2; specularTextureUnit = GL_TEXTURE2;
} else if (receiveShadows) {
program = &_shadowNormalMapProgram;
skinProgram = &_skinShadowNormalMapProgram;
skinLocations = &_skinShadowNormalMapLocations;
shadowTextureUnit = GL_TEXTURE2;
} else { } else {
program = &_normalMapProgram; program = &_normalMapProgram;
skinProgram = &_skinNormalMapProgram; skinProgram = &_skinNormalMapProgram;
skinLocations = &_skinNormalMapLocations; skinLocations = &_skinNormalMapLocations;
} }
} else if (mesh.hasSpecularTexture()) { } else if (mesh.hasSpecularTexture()) {
if (receiveShadows) {
program = &_shadowSpecularMapProgram;
skinProgram = &_skinShadowSpecularMapProgram;
skinLocations = &_skinShadowSpecularMapLocations;
shadowTextureUnit = GL_TEXTURE2;
} else {
program = &_specularMapProgram; program = &_specularMapProgram;
skinProgram = &_skinSpecularMapProgram; skinProgram = &_skinSpecularMapProgram;
skinLocations = &_skinSpecularMapLocations; skinLocations = &_skinSpecularMapLocations;
}
specularTextureUnit = GL_TEXTURE1; specularTextureUnit = GL_TEXTURE1;
} else if (receiveShadows) {
program = &_shadowMapProgram;
skinProgram = &_skinShadowMapProgram;
skinLocations = &_skinShadowMapLocations;
shadowTextureUnit = GL_TEXTURE1;
} }
const MeshState& state = _meshStates.at(i); const MeshState& state = _meshStates.at(i);
@ -1617,7 +1765,6 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
if (!mesh.tangents.isEmpty()) { if (!mesh.tangents.isEmpty()) {
specularTextureUnit = GL_TEXTURE2;
glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1);
Texture* normalMap = networkPart.normalTexture.data(); Texture* normalMap = networkPart.normalTexture.data();
glBindTexture(GL_TEXTURE_2D, !normalMap ? glBindTexture(GL_TEXTURE_2D, !normalMap ?
@ -1632,6 +1779,12 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
Application::getInstance()->getTextureCache()->getWhiteTextureID() : specularMap->getID()); Application::getInstance()->getTextureCache()->getWhiteTextureID() : specularMap->getID());
glActiveTexture(GL_TEXTURE0); 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); glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset);
offset += part.quadIndices.size() * sizeof(int); offset += part.quadIndices.size() * sizeof(int);
@ -1661,6 +1814,12 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
} }
if (shadowTextureUnit) {
glActiveTexture(shadowTextureUnit);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
}
if (state.clusterMatrices.size() > 1) { if (state.clusterMatrices.size() > 1) {
skinProgram->disableAttributeArray(skinLocations->clusterIndices); skinProgram->disableAttributeArray(skinLocations->clusterIndices);
skinProgram->disableAttributeArray(skinLocations->clusterWeights); 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 }; 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. /// 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 /// \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 applyNextGeometry();
void deleteGeometry(); 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); QVector<JointState> createJointStates(const FBXGeometry& geometry);
QSharedPointer<NetworkGeometry> _baseGeometry; ///< reference required to prevent collection of base QSharedPointer<NetworkGeometry> _baseGeometry; ///< reference required to prevent collection of base
@ -344,15 +344,30 @@ private:
static ProgramObject _normalMapProgram; static ProgramObject _normalMapProgram;
static ProgramObject _specularMapProgram; static ProgramObject _specularMapProgram;
static ProgramObject _normalSpecularMapProgram; static ProgramObject _normalSpecularMapProgram;
static ProgramObject _shadowMapProgram;
static ProgramObject _shadowNormalMapProgram;
static ProgramObject _shadowSpecularMapProgram;
static ProgramObject _shadowNormalSpecularMapProgram;
static ProgramObject _shadowProgram; static ProgramObject _shadowProgram;
static ProgramObject _skinProgram; static ProgramObject _skinProgram;
static ProgramObject _skinNormalMapProgram; static ProgramObject _skinNormalMapProgram;
static ProgramObject _skinSpecularMapProgram; static ProgramObject _skinSpecularMapProgram;
static ProgramObject _skinNormalSpecularMapProgram; static ProgramObject _skinNormalSpecularMapProgram;
static ProgramObject _skinShadowMapProgram;
static ProgramObject _skinShadowNormalMapProgram;
static ProgramObject _skinShadowSpecularMapProgram;
static ProgramObject _skinShadowNormalSpecularMapProgram;
static ProgramObject _skinShadowProgram; static ProgramObject _skinShadowProgram;
static int _normalMapTangentLocation; static int _normalMapTangentLocation;
static int _normalSpecularMapTangentLocation; static int _normalSpecularMapTangentLocation;
static int _shadowNormalMapTangentLocation;
static int _shadowNormalSpecularMapTangentLocation;
class SkinLocations { class SkinLocations {
public: public:
@ -366,9 +381,14 @@ private:
static SkinLocations _skinNormalMapLocations; static SkinLocations _skinNormalMapLocations;
static SkinLocations _skinSpecularMapLocations; static SkinLocations _skinSpecularMapLocations;
static SkinLocations _skinNormalSpecularMapLocations; static SkinLocations _skinNormalSpecularMapLocations;
static SkinLocations _skinShadowMapLocations;
static SkinLocations _skinShadowNormalMapLocations;
static SkinLocations _skinShadowSpecularMapLocations;
static SkinLocations _skinShadowNormalSpecularMapLocations;
static SkinLocations _skinShadowLocations; 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>) Q_DECLARE_METATYPE(QPointer<Model>)

View file

@ -80,7 +80,7 @@ ChatWindow::ChatWindow(QWidget* parent) :
} else { } else {
ui->numOnlineLabel->hide(); ui->numOnlineLabel->hide();
ui->closeButton->hide(); ui->closeButton->hide();
ui->usersWidget->hide(); ui->usersArea->hide();
ui->messagesScrollArea->hide(); ui->messagesScrollArea->hide();
ui->messagePlainTextEdit->hide(); ui->messagePlainTextEdit->hide();
connect(&XmppClient::getInstance(), SIGNAL(joinedPublicChatRoom()), this, SLOT(connected())); connect(&XmppClient::getInstance(), SIGNAL(joinedPublicChatRoom()), this, SLOT(connected()));
@ -208,7 +208,7 @@ void ChatWindow::connected() {
ui->connectingToXMPPLabel->hide(); ui->connectingToXMPPLabel->hide();
ui->numOnlineLabel->show(); ui->numOnlineLabel->show();
ui->closeButton->show(); ui->closeButton->show();
ui->usersWidget->show(); ui->usersArea->show();
ui->messagesScrollArea->show(); ui->messagesScrollArea->show();
ui->messagePlainTextEdit->show(); ui->messagePlainTextEdit->show();
ui->messagePlainTextEdit->setFocus(); ui->messagePlainTextEdit->setFocus();
@ -248,6 +248,7 @@ void ChatWindow::notificationClicked() {
return; return;
} }
} }
Application::processEvents();
scrollToBottom(); scrollToBottom();
} }
@ -262,6 +263,8 @@ void ChatWindow::error(QXmppClient::Error error) {
} }
void ChatWindow::participantsChanged() { void ChatWindow::participantsChanged() {
bool atBottom = isNearBottom();
QStringList participants = XmppClient::getInstance().getPublicChatRoom()->participants(); QStringList participants = XmppClient::getInstance().getPublicChatRoom()->participants();
ui->numOnlineLabel->setText(tr("%1 online now:").arg(participants.count())); ui->numOnlineLabel->setText(tr("%1 online now:").arg(participants.count()));
@ -288,6 +291,11 @@ void ChatWindow::participantsChanged() {
userLabel->installEventFilter(this); userLabel->installEventFilter(this);
ui->usersWidget->layout()->addWidget(userLabel); ui->usersWidget->layout()->addWidget(userLabel);
} }
Application::processEvents();
if (atBottom) {
scrollToBottom();
}
} }
void ChatWindow::messageReceived(const QXmppMessage& message) { void ChatWindow::messageReceived(const QXmppMessage& message) {
@ -306,7 +314,7 @@ void ChatWindow::messageReceived(const QXmppMessage& message) {
messageArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); messageArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
messageArea->setReadOnly(true); messageArea->setReadOnly(true);
messageArea->setStyleSheet("padding-bottom: 2px;" messageArea->setStyleSheet("QTextBrowser{ padding-bottom: 2px;"
"padding-left: 2px;" "padding-left: 2px;"
"padding-top: 2px;" "padding-top: 2px;"
"padding-right: 20px;" "padding-right: 20px;"
@ -314,7 +322,8 @@ void ChatWindow::messageReceived(const QXmppMessage& message) {
"color: #333333;" "color: #333333;"
"font-size: 14pt;" "font-size: 14pt;"
"background-color: rgba(0, 0, 0, 0%);" "background-color: rgba(0, 0, 0, 0%);"
"border: 0;"); "border: 0; }"
"QMenu{ border: 2px outset gray; }");
QString userLabel = getParticipantName(message.from()); QString userLabel = getParticipantName(message.from());
if (fromSelf) { 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 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 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 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) Q_DECLARE_METATYPE(QNetworkAccessManager::Operation)
@ -73,6 +77,10 @@ SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) :
} }
void SnapshotShareDialog::accept() { 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(); uploadSnapshot();
} }
@ -179,6 +187,8 @@ void SnapshotShareDialog::postRequestFinished() {
} }
} }
QMessageBox::warning(this, "", errorMessage); 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()); sendForumPost(responseObject["url"].toString());
} else { } else {
QMessageBox::warning(this, "", SHARE_DEFAULT_ERROR); QMessageBox::warning(this, "", SHARE_DEFAULT_ERROR);
_ui.shareButton->setEnabled(true);
_ui.shareButton->setStyleSheet(SHARE_BUTTON_STYLE + SHARE_BUTTON_ENABLED_STYLE);
} }
delete requestReply; delete requestReply;

View file

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

View file

@ -128,19 +128,43 @@ QPushButton:pressed {
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QWidget" name="usersWidget" native="true"> <widget class="QWidget" name="usersArea" native="true">
<property name="styleSheet"> <property name="sizePolicy">
<string notr="true">#usersWidget { <sizepolicy hsizetype="Expanding" vsizetype="Minimum">
margin-right: 20px; <horstretch>0</horstretch>
}</string> <verstretch>0</verstretch>
</sizepolicy>
</property> </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> </widget>
</item> </item>
<item> <item>
<widget class="QScrollArea" name="messagesScrollArea"> <widget class="QScrollArea" name="messagesScrollArea">
<property name="styleSheet">
<string notr="true">margin-top: 12px;</string>
</property>
<property name="horizontalScrollBarPolicy"> <property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum> <enum>Qt::ScrollBarAlwaysOff</enum>
</property> </property>

View file

@ -61,15 +61,19 @@ const quint32 AVATAR_MOTION_MOTOR_COLLISION_SURFACE_ONLY = 1U << 3;
const quint32 AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY = 1U << 4; const quint32 AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY = 1U << 4;
const quint32 AVATAR_MOTION_OBEY_LOCAL_GRAVITY = 1U << 5; const quint32 AVATAR_MOTION_OBEY_LOCAL_GRAVITY = 1U << 5;
const quint32 AVATAR_MOTION_STAND_ON_NEARBY_FLOORS = 1U << 6;
const quint32 AVATAR_MOTION_DEFAULTS = const quint32 AVATAR_MOTION_DEFAULTS =
AVATAR_MOTION_MOTOR_ENABLED | AVATAR_MOTION_MOTOR_ENABLED |
AVATAR_MOTION_MOTOR_KEYBOARD_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 // these bits will be expanded as features are exposed
const quint32 AVATAR_MOTION_SCRIPTABLE_BITS = const quint32 AVATAR_MOTION_SCRIPTABLE_BITS =
AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY | AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY |
AVATAR_MOTION_OBEY_LOCAL_GRAVITY; AVATAR_MOTION_OBEY_LOCAL_GRAVITY |
AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
// First bitset // First bitset

View file

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

View file

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

View file

@ -91,6 +91,7 @@ ModelItem::ModelItem(const ModelItemID& modelItemID, const ModelItemProperties&
_animationIsPlaying = false; _animationIsPlaying = false;
_animationFrameIndex = 0.0f; _animationFrameIndex = 0.0f;
_animationFPS = MODEL_DEFAULT_ANIMATION_FPS; _animationFPS = MODEL_DEFAULT_ANIMATION_FPS;
_glowLevel = 0.0f;
_jointMappingCompleted = false; _jointMappingCompleted = false;
_lastAnimated = now; _lastAnimated = now;
@ -125,6 +126,7 @@ void ModelItem::init(glm::vec3 position, float radius, rgbColor color, uint32_t
_animationIsPlaying = false; _animationIsPlaying = false;
_animationFrameIndex = 0.0f; _animationFrameIndex = 0.0f;
_animationFPS = MODEL_DEFAULT_ANIMATION_FPS; _animationFPS = MODEL_DEFAULT_ANIMATION_FPS;
_glowLevel = 0.0f;
_jointMappingCompleted = false; _jointMappingCompleted = false;
_lastAnimated = now; _lastAnimated = now;
} }
@ -802,6 +804,7 @@ ModelItemProperties::ModelItemProperties() :
_animationIsPlaying(false), _animationIsPlaying(false),
_animationFrameIndex(0.0), _animationFrameIndex(0.0),
_animationFPS(MODEL_DEFAULT_ANIMATION_FPS), _animationFPS(MODEL_DEFAULT_ANIMATION_FPS),
_glowLevel(0.0f),
_id(UNKNOWN_MODEL_ID), _id(UNKNOWN_MODEL_ID),
_idSet(false), _idSet(false),
@ -817,6 +820,7 @@ ModelItemProperties::ModelItemProperties() :
_animationIsPlayingChanged(false), _animationIsPlayingChanged(false),
_animationFrameIndexChanged(false), _animationFrameIndexChanged(false),
_animationFPSChanged(false), _animationFPSChanged(false),
_glowLevelChanged(false),
_defaultSettings(true) _defaultSettings(true)
{ {
} }
@ -890,6 +894,7 @@ QScriptValue ModelItemProperties::copyToScriptValue(QScriptEngine* engine) const
properties.setProperty("animationIsPlaying", _animationIsPlaying); properties.setProperty("animationIsPlaying", _animationIsPlaying);
properties.setProperty("animationFrameIndex", _animationFrameIndex); properties.setProperty("animationFrameIndex", _animationFrameIndex);
properties.setProperty("animationFPS", _animationFPS); properties.setProperty("animationFPS", _animationFPS);
properties.setProperty("glowLevel", _glowLevel);
if (_idSet) { if (_idSet) {
properties.setProperty("id", _id); properties.setProperty("id", _id);
@ -1026,6 +1031,16 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) {
} }
} }
QScriptValue glowLevel = object.property("glowLevel");
if (glowLevel.isValid()) {
float newGlowLevel;
newGlowLevel = glowLevel.toVariant().toFloat();
if (_defaultSettings || newGlowLevel != _glowLevel) {
_glowLevel = newGlowLevel;
_glowLevelChanged = true;
}
}
_lastEdited = usecTimestampNow(); _lastEdited = usecTimestampNow();
} }
@ -1081,6 +1096,11 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const {
somethingChanged = true; somethingChanged = true;
} }
if (_glowLevelChanged) {
modelItem.setGlowLevel(_glowLevel);
somethingChanged = true;
}
if (somethingChanged) { if (somethingChanged) {
bool wantDebug = false; bool wantDebug = false;
if (wantDebug) { if (wantDebug) {
@ -1104,6 +1124,7 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) {
_animationIsPlaying = modelItem.getAnimationIsPlaying(); _animationIsPlaying = modelItem.getAnimationIsPlaying();
_animationFrameIndex = modelItem.getAnimationFrameIndex(); _animationFrameIndex = modelItem.getAnimationFrameIndex();
_animationFPS = modelItem.getAnimationFPS(); _animationFPS = modelItem.getAnimationFPS();
_glowLevel = modelItem.getGlowLevel();
_id = modelItem.getID(); _id = modelItem.getID();
_idSet = true; _idSet = true;
@ -1119,6 +1140,7 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) {
_animationIsPlayingChanged = false; _animationIsPlayingChanged = false;
_animationFrameIndexChanged = false; _animationFrameIndexChanged = false;
_animationFPSChanged = false; _animationFPSChanged = false;
_glowLevelChanged = false;
_defaultSettings = false; _defaultSettings = false;
} }

View file

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

View file

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