mirror of
https://github.com/overte-org/overte.git
synced 2025-04-22 03:04:33 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into aacube
This commit is contained in:
commit
c63c0c0ce3
37 changed files with 905 additions and 216 deletions
|
@ -213,6 +213,8 @@ void Agent::run() {
|
|||
|
||||
loop.exec();
|
||||
|
||||
|
||||
|
||||
// let the AvatarData and ResourceCache classes use our QNetworkAccessManager
|
||||
AvatarData::setNetworkAccessManager(networkManager);
|
||||
ResourceCache::setNetworkAccessManager(networkManager);
|
||||
|
|
|
@ -735,7 +735,7 @@ void DomainServer::setupPendingAssignmentCredits() {
|
|||
|
||||
const float CREDITS_PER_HOUR = 0.10f;
|
||||
const float CREDITS_PER_MSEC = CREDITS_PER_HOUR / (60 * 60 * 1000);
|
||||
const int SATOSHIS_PER_MSEC = CREDITS_PER_MSEC * powf(10.0f, 8.0f);
|
||||
const int SATOSHIS_PER_MSEC = CREDITS_PER_MSEC * SATOSHIS_PER_CREDIT;
|
||||
|
||||
float pendingCredits = elapsedMsecsSinceLastPayment * SATOSHIS_PER_MSEC;
|
||||
|
||||
|
@ -884,10 +884,10 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) {
|
|||
|
||||
if (!nodeData->getWalletUUID().isNull()) {
|
||||
TransactionHash::iterator i = _pendingAssignmentCredits.find(nodeData->getWalletUUID());
|
||||
double pendingCreditAmount = 0;
|
||||
float pendingCreditAmount = 0;
|
||||
|
||||
while (i != _pendingAssignmentCredits.end() && i.key() == nodeData->getWalletUUID()) {
|
||||
pendingCreditAmount += i.value()->getAmount();
|
||||
pendingCreditAmount += i.value()->getAmount() / SATOSHIS_PER_CREDIT;
|
||||
++i;
|
||||
}
|
||||
|
||||
|
@ -1004,6 +1004,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
|
||||
return true;
|
||||
} else {
|
||||
// check if this is for json stats for a node
|
||||
const QString NODE_JSON_REGEX_STRING = QString("\\%1\\/(%2).json\\/?$").arg(URI_NODES).arg(UUID_REGEX_STRING);
|
||||
QRegExp nodeShowRegex(NODE_JSON_REGEX_STRING);
|
||||
|
||||
|
@ -1028,6 +1029,40 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
// tell the caller we processed the request
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if this is a request for a scripted assignment (with a temp unique UUID)
|
||||
const QString ASSIGNMENT_REGEX_STRING = QString("\\%1\\/(%2)\\/?$").arg(URI_ASSIGNMENT).arg(UUID_REGEX_STRING);
|
||||
QRegExp assignmentRegex(ASSIGNMENT_REGEX_STRING);
|
||||
|
||||
if (assignmentRegex.indexIn(url.path()) != -1) {
|
||||
QUuid matchingUUID = QUuid(assignmentRegex.cap(1));
|
||||
|
||||
SharedAssignmentPointer matchingAssignment = _allAssignments.value(matchingUUID);
|
||||
if (!matchingAssignment) {
|
||||
// check if we have a pending assignment that matches this temp UUID, and it is a scripted assignment
|
||||
PendingAssignedNodeData* pendingData = _pendingAssignedNodes.value(matchingUUID);
|
||||
if (pendingData) {
|
||||
matchingAssignment = _allAssignments.value(pendingData->getAssignmentUUID());
|
||||
|
||||
if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) {
|
||||
// we have a matching assignment and it is for the right type, have the HTTP manager handle it
|
||||
// via correct URL for the script so the client can download
|
||||
|
||||
QUrl scriptURL = url;
|
||||
scriptURL.setPath(URI_ASSIGNMENT + "/"
|
||||
+ uuidStringWithoutCurlyBraces(pendingData->getAssignmentUUID()));
|
||||
|
||||
// have the HTTPManager serve the appropriate script file
|
||||
return _httpManager.handleHTTPRequest(connection, scriptURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// request not handled
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (connection->requestOperation() == QNetworkAccessManager::PostOperation) {
|
||||
|
|
|
@ -18,13 +18,13 @@
|
|||
WalletTransaction::WalletTransaction() :
|
||||
_uuid(),
|
||||
_destinationUUID(),
|
||||
_amount(),
|
||||
_amount(0),
|
||||
_isFinalized(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
WalletTransaction::WalletTransaction(const QUuid& destinationUUID, double amount) :
|
||||
WalletTransaction::WalletTransaction(const QUuid& destinationUUID, qint64 amount) :
|
||||
_uuid(QUuid::createUuid()),
|
||||
_destinationUUID(destinationUUID),
|
||||
_amount(amount),
|
||||
|
@ -63,5 +63,5 @@ void WalletTransaction::loadFromJson(const QJsonObject& jsonObject) {
|
|||
|
||||
_uuid = QUuid(transactionObject.value(TRANSACTION_ID_KEY).toString());
|
||||
_destinationUUID = QUuid(transactionObject.value(TRANSACTION_DESTINATION_WALLET_ID_KEY).toString());
|
||||
_amount = transactionObject.value(TRANSACTION_AMOUNT_KEY).toDouble();
|
||||
_amount = transactionObject.value(TRANSACTION_AMOUNT_KEY).toInt();
|
||||
}
|
|
@ -19,16 +19,16 @@
|
|||
class WalletTransaction : public QObject {
|
||||
public:
|
||||
WalletTransaction();
|
||||
WalletTransaction(const QUuid& destinationUUID, double amount);
|
||||
WalletTransaction(const QUuid& destinationUUID, qint64 amount);
|
||||
|
||||
const QUuid& getUUID() const { return _uuid; }
|
||||
|
||||
void setDestinationUUID(const QUuid& destinationUUID) { _destinationUUID = destinationUUID; }
|
||||
const QUuid& getDestinationUUID() const { return _destinationUUID; }
|
||||
|
||||
double getAmount() const { return _amount; }
|
||||
void setAmount(double amount) { _amount = amount; }
|
||||
void incrementAmount(double increment) { _amount += increment; }
|
||||
qint64 getAmount() const { return _amount; }
|
||||
void setAmount(qint64 amount) { _amount = amount; }
|
||||
void incrementAmount(qint64 increment) { _amount += increment; }
|
||||
|
||||
bool isFinalized() const { return _isFinalized; }
|
||||
void setIsFinalized(bool isFinalized) { _isFinalized = isFinalized; }
|
||||
|
@ -39,7 +39,7 @@ public:
|
|||
private:
|
||||
QUuid _uuid;
|
||||
QUuid _destinationUUID;
|
||||
double _amount;
|
||||
qint64 _amount;
|
||||
bool _isFinalized;
|
||||
};
|
||||
|
||||
|
|
|
@ -479,6 +479,7 @@ function mousePressEvent(event) {
|
|||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
Vec3.print("[Mouse] Looking at: ", pickRay.origin);
|
||||
var foundModels = Models.findModels(pickRay.origin, LASER_LENGTH_FACTOR);
|
||||
var closest = -1.0;
|
||||
for (var i = 0; i < foundModels.length; i++) {
|
||||
if (!foundModels[i].isKnownID) {
|
||||
var identify = Models.identifyModel(foundModels[i]);
|
||||
|
@ -514,35 +515,51 @@ function mousePressEvent(event) {
|
|||
var d = Vec3.length(Vec3.subtract(P, X));
|
||||
|
||||
if (d < properties.radius && 0 < x && x < LASER_LENGTH_FACTOR) {
|
||||
modelSelected = true;
|
||||
selectedModelID = foundModels[i];
|
||||
selectedModelProperties = properties;
|
||||
|
||||
selectedModelProperties.oldRadius = selectedModelProperties.radius;
|
||||
selectedModelProperties.oldPosition = {
|
||||
x: selectedModelProperties.position.x,
|
||||
y: selectedModelProperties.position.y,
|
||||
z: selectedModelProperties.position.z,
|
||||
};
|
||||
selectedModelProperties.oldRotation = {
|
||||
x: selectedModelProperties.modelRotation.x,
|
||||
y: selectedModelProperties.modelRotation.y,
|
||||
z: selectedModelProperties.modelRotation.z,
|
||||
w: selectedModelProperties.modelRotation.w,
|
||||
};
|
||||
|
||||
|
||||
orientation = MyAvatar.orientation;
|
||||
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
|
||||
|
||||
print("Clicked on " + selectedModelID.id + " " + modelSelected);
|
||||
return;
|
||||
if (closest < 0.0) {
|
||||
closest = x;
|
||||
}
|
||||
|
||||
if (x <= closest) {
|
||||
modelSelected = true;
|
||||
selectedModelID = foundModels[i];
|
||||
selectedModelProperties = properties;
|
||||
|
||||
orientation = MyAvatar.orientation;
|
||||
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (modelSelected) {
|
||||
selectedModelProperties.oldRadius = selectedModelProperties.radius;
|
||||
selectedModelProperties.oldPosition = {
|
||||
x: selectedModelProperties.position.x,
|
||||
y: selectedModelProperties.position.y,
|
||||
z: selectedModelProperties.position.z,
|
||||
};
|
||||
selectedModelProperties.oldRotation = {
|
||||
x: selectedModelProperties.modelRotation.x,
|
||||
y: selectedModelProperties.modelRotation.y,
|
||||
z: selectedModelProperties.modelRotation.z,
|
||||
w: selectedModelProperties.modelRotation.w,
|
||||
};
|
||||
|
||||
selectedModelProperties.glowLevel = 0.1;
|
||||
Models.editModel(selectedModelID, { glowLevel: selectedModelProperties.glowLevel});
|
||||
|
||||
print("Clicked on " + selectedModelID.id + " " + modelSelected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Controller.mouseReleaseEvent.connect(function() {
|
||||
if (modelSelected) {
|
||||
Models.editModel(selectedModelID, { glowLevel: 0.0 });
|
||||
modelSelected = false;
|
||||
}
|
||||
});
|
||||
|
||||
var oldModifier = 0;
|
||||
var modifier = 0;
|
||||
var wasShifted = false;
|
||||
|
|
|
@ -78,7 +78,7 @@ function createDebugOverlay() {
|
|||
position: defaultPosition,
|
||||
size: RADIUS,
|
||||
color: GRAY_COLOR,
|
||||
alpha: 1,
|
||||
alpha: 0.75,
|
||||
visible: true,
|
||||
solid: true,
|
||||
anchor: "MyAvatar"
|
||||
|
@ -87,7 +87,7 @@ function createDebugOverlay() {
|
|||
position: defaultPosition,
|
||||
size: RADIUS,
|
||||
color: RED_COLOR,
|
||||
alpha: 1,
|
||||
alpha: 0.5,
|
||||
visible: true,
|
||||
solid: true,
|
||||
anchor: "MyAvatar"
|
||||
|
@ -111,7 +111,7 @@ function displayDebug() {
|
|||
}
|
||||
} else {
|
||||
// update debug indicator
|
||||
if (greenSphere == -1) {
|
||||
if (greenSphere == -1) {
|
||||
createDebugOverlay();
|
||||
}
|
||||
|
||||
|
@ -149,8 +149,8 @@ function getGrabRotation() {
|
|||
// When move button is pressed, process results
|
||||
function handleGrabBehavior(deltaTime) {
|
||||
// check for and handle grab behaviors
|
||||
grabbingWithRightHand = Controller.isButtonPressed(RIGHT_BUTTON_FWD) || Controller.isButtonPressed(RIGHT_BUTTON_4);
|
||||
grabbingWithLeftHand = Controller.isButtonPressed(LEFT_BUTTON_FWD) || Controller.isButtonPressed(LEFT_BUTTON_4);
|
||||
grabbingWithRightHand = Controller.isButtonPressed(RIGHT_BUTTON_4);
|
||||
grabbingWithLeftHand = Controller.isButtonPressed(LEFT_BUTTON_4);
|
||||
stoppedGrabbingWithLeftHand = false;
|
||||
stoppedGrabbingWithRightHand = false;
|
||||
|
||||
|
@ -201,20 +201,22 @@ function handleGrabBehavior(deltaTime) {
|
|||
printVector("grabDelta: ", grabDelta, 3);
|
||||
}
|
||||
|
||||
var THRUST_GRAB_SCALING = 300000.0;
|
||||
var thrust = Vec3.multiply(grabDelta, Math.abs(Vec3.length(grabDelta)));
|
||||
|
||||
var THRUST_GRAB_SCALING = 100000.0;
|
||||
|
||||
var thrustFront = Vec3.multiply(front, MyAvatar.scale * -grabDelta.z * THRUST_GRAB_SCALING * deltaTime);
|
||||
var thrustFront = Vec3.multiply(front, MyAvatar.scale * -thrust.z * THRUST_GRAB_SCALING * deltaTime);
|
||||
MyAvatar.addThrust(thrustFront);
|
||||
var thrustRight = Vec3.multiply(right, MyAvatar.scale * grabDelta.x * THRUST_GRAB_SCALING * deltaTime);
|
||||
var thrustRight = Vec3.multiply(right, MyAvatar.scale * thrust.x * THRUST_GRAB_SCALING * deltaTime);
|
||||
MyAvatar.addThrust(thrustRight);
|
||||
var thrustUp = Vec3.multiply(up, MyAvatar.scale * grabDelta.y * THRUST_GRAB_SCALING * deltaTime);
|
||||
var thrustUp = Vec3.multiply(up, MyAvatar.scale * thrust.y * THRUST_GRAB_SCALING * deltaTime);
|
||||
MyAvatar.addThrust(thrustUp);
|
||||
|
||||
// add some rotation...
|
||||
var deltaRotation = getGrabRotation();
|
||||
var PITCH_SCALING = 2.0;
|
||||
var PITCH_SCALING = 2.5;
|
||||
var PITCH_DEAD_ZONE = 2.0;
|
||||
var YAW_SCALING = 2.0;
|
||||
var YAW_SCALING = 2.5;
|
||||
var ROLL_SCALING = 2.0;
|
||||
|
||||
var euler = Quat.safeEulerAngles(deltaRotation);
|
||||
|
|
|
@ -3,22 +3,22 @@
|
|||
// examples
|
||||
//
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
// This sample script loads a sound file and plays it at the 'fingertip' of the
|
||||
// Plays a sample audio file at the avatar's current location
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
// First, load the clap sound from a URL
|
||||
var clap = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw");
|
||||
// First, load a sample sound from a URL
|
||||
var bird = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw");
|
||||
|
||||
function maybePlaySound(deltaTime) {
|
||||
if (Math.random() < 0.01) {
|
||||
// Set the location and other info for the sound to play
|
||||
var options = new AudioInjectionOptions();
|
||||
var palmPosition = Controller.getSpatialControlPosition(0);
|
||||
options.position = palmPosition;
|
||||
var position = MyAvatar.position;
|
||||
options.position = position;
|
||||
options.volume = 0.5;
|
||||
Audio.playSound(clap, options);
|
||||
Audio.playSound(bird, options);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,9 @@ void main(void) {
|
|||
// and the texture coordinates
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
|
||||
// and the shadow texture coordinates
|
||||
gl_TexCoord[1] = vec4(dot(gl_EyePlaneS[0], position), dot(gl_EyePlaneT[0], position), dot(gl_EyePlaneR[0], position), 1.0);
|
||||
|
||||
// use standard pipeline transform
|
||||
gl_Position = ftransform();
|
||||
}
|
||||
|
|
|
@ -36,6 +36,10 @@ void main(void) {
|
|||
// and the texture coordinates
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
|
||||
// and the shadow texture coordinates
|
||||
gl_TexCoord[1] = vec4(dot(gl_EyePlaneS[0], interpolatedPosition), dot(gl_EyePlaneT[0], interpolatedPosition),
|
||||
dot(gl_EyePlaneR[0], interpolatedPosition), 1.0);
|
||||
|
||||
// use standard pipeline transform
|
||||
gl_Position = ftransform();
|
||||
}
|
||||
|
|
48
interface/resources/shaders/model_shadow_map.frag
Normal file
48
interface/resources/shaders/model_shadow_map.frag
Normal 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);
|
||||
}
|
60
interface/resources/shaders/model_shadow_normal_map.frag
Normal file
60
interface/resources/shaders/model_shadow_normal_map.frag
Normal 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);
|
||||
}
|
|
@ -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);
|
||||
}
|
51
interface/resources/shaders/model_shadow_specular_map.frag
Normal file
51
interface/resources/shaders/model_shadow_specular_map.frag
Normal 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);
|
||||
}
|
|
@ -43,5 +43,8 @@ void main(void) {
|
|||
// and the texture coordinates
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
|
||||
// and the shadow texture coordinates
|
||||
gl_TexCoord[1] = vec4(dot(gl_EyePlaneS[0], position), dot(gl_EyePlaneT[0], position), dot(gl_EyePlaneR[0], position), 1.0);
|
||||
|
||||
gl_Position = gl_ProjectionMatrix * position;
|
||||
}
|
||||
|
|
|
@ -52,5 +52,9 @@ void main(void) {
|
|||
// and the texture coordinates
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
|
||||
// and the shadow texture coordinates
|
||||
gl_TexCoord[1] = vec4(dot(gl_EyePlaneS[0], interpolatedPosition), dot(gl_EyePlaneT[0], interpolatedPosition),
|
||||
dot(gl_EyePlaneR[0], interpolatedPosition), 1.0);
|
||||
|
||||
gl_Position = gl_ProjectionMatrix * interpolatedPosition;
|
||||
}
|
||||
|
|
|
@ -172,7 +172,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
_nodeBoundsDisplay(this),
|
||||
_previousScriptLocation(),
|
||||
_runningScriptsWidget(new RunningScriptsWidget(_window)),
|
||||
_runningScriptsWidgetWasVisible(false)
|
||||
_runningScriptsWidgetWasVisible(false),
|
||||
_trayIcon(new QSystemTrayIcon(_window))
|
||||
{
|
||||
// read the ApplicationInfo.ini file for Name/Version/Domain information
|
||||
QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat);
|
||||
|
@ -242,7 +243,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
QTimer* locationUpdateTimer = new QTimer(this);
|
||||
connect(locationUpdateTimer, &QTimer::timeout, this, &Application::updateLocationInServer);
|
||||
locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS);
|
||||
|
||||
|
||||
connect(nodeList, &NodeList::nodeAdded, this, &Application::nodeAdded);
|
||||
connect(nodeList, &NodeList::nodeKilled, this, &Application::nodeKilled);
|
||||
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer)));
|
||||
|
@ -251,16 +252,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle);
|
||||
connect(nodeList, SIGNAL(uuidChanged(const QUuid&)), _myAvatar, SLOT(setSessionUUID(const QUuid&)));
|
||||
connect(nodeList, &NodeList::limitOfSilentDomainCheckInsReached, nodeList, &NodeList::reset);
|
||||
|
||||
|
||||
// connect to appropriate slots on AccountManager
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
|
||||
const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * 1000;
|
||||
|
||||
|
||||
QTimer* balanceUpdateTimer = new QTimer(this);
|
||||
connect(balanceUpdateTimer, &QTimer::timeout, &accountManager, &AccountManager::updateBalance);
|
||||
balanceUpdateTimer->start(BALANCE_UPDATE_INTERVAL_MSECS);
|
||||
|
||||
|
||||
connect(&accountManager, &AccountManager::balanceChanged, this, &Application::updateWindowTitle);
|
||||
|
||||
connect(&accountManager, &AccountManager::authRequired, Menu::getInstance(), &Menu::loginForCurrentDomain);
|
||||
|
@ -391,6 +392,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
OAuthWebViewHandler::getInstance();
|
||||
// make sure the High Fidelity root CA is in our list of trusted certs
|
||||
OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig();
|
||||
|
||||
_trayIcon->show();
|
||||
}
|
||||
|
||||
Application::~Application() {
|
||||
|
@ -562,7 +565,7 @@ void Application::paintGL() {
|
|||
PerformanceWarning warn(showWarnings, "Application::paintGL()");
|
||||
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
|
||||
|
||||
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
|
||||
_myCamera.setTightness(0.0f); // In first person, camera follows (untweaked) head exactly without delay
|
||||
_myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
|
||||
|
@ -2325,10 +2328,15 @@ void Application::updateShadowMap() {
|
|||
// store view matrix without translation, which we'll use for precision-sensitive objects
|
||||
updateUntranslatedViewMatrix();
|
||||
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(1.1f, 4.0f); // magic numbers courtesy http://www.eecs.berkeley.edu/~ravir/6160/papers/shadowmaps.ppt
|
||||
|
||||
_avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE);
|
||||
_particles.render(OctreeRenderer::SHADOW_RENDER_MODE);
|
||||
_models.render(OctreeRenderer::SHADOW_RENDER_MODE);
|
||||
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
|
@ -2534,6 +2542,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
if (_voxelFades.size() > 0) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... voxel fades...");
|
||||
_voxelFadesLock.lockForWrite();
|
||||
for(std::vector<VoxelFade>::iterator fade = _voxelFades.begin(); fade != _voxelFades.end();) {
|
||||
fade->render();
|
||||
if(fade->isDone()) {
|
||||
|
@ -2542,6 +2551,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
++fade;
|
||||
}
|
||||
}
|
||||
_voxelFadesLock.unlock();
|
||||
}
|
||||
|
||||
// give external parties a change to hook in
|
||||
|
@ -3103,18 +3113,18 @@ void Application::updateWindowTitle(){
|
|||
QString username = AccountManager::getInstance().getAccountInfo().getUsername();
|
||||
QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())
|
||||
+ nodeList->getDomainHandler().getHostname() + buildVersion;
|
||||
|
||||
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
if (accountManager.getAccountInfo().hasBalance()) {
|
||||
float creditBalance = accountManager.getAccountInfo().getBalance() * pow(10.0f, -8.0f);
|
||||
|
||||
float creditBalance = accountManager.getAccountInfo().getBalance() / SATOSHIS_PER_CREDIT;
|
||||
|
||||
QString creditBalanceString;
|
||||
creditBalanceString.sprintf("%.8f", creditBalance);
|
||||
|
||||
|
||||
title += " - ₵" + creditBalanceString;
|
||||
}
|
||||
|
||||
qDebug("Application title set to: %s", title.toStdString().c_str());
|
||||
|
||||
qDebug("Application title set to: %s", title.toStdString().c_str());
|
||||
_window->setWindowTitle(title);
|
||||
}
|
||||
|
||||
|
@ -3208,7 +3218,9 @@ void Application::nodeKilled(SharedNodePointer node) {
|
|||
fade.voxelDetails = rootDetails;
|
||||
const float slightly_smaller = 0.99f;
|
||||
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
|
||||
_voxelFadesLock.lockForWrite();
|
||||
_voxelFades.push_back(fade);
|
||||
_voxelFadesLock.unlock();
|
||||
}
|
||||
|
||||
// If the voxel server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server
|
||||
|
@ -3239,7 +3251,9 @@ void Application::nodeKilled(SharedNodePointer node) {
|
|||
fade.voxelDetails = rootDetails;
|
||||
const float slightly_smaller = 0.99f;
|
||||
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
|
||||
_voxelFadesLock.lockForWrite();
|
||||
_voxelFades.push_back(fade);
|
||||
_voxelFadesLock.unlock();
|
||||
}
|
||||
|
||||
// If the particle server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server
|
||||
|
@ -3271,7 +3285,9 @@ void Application::nodeKilled(SharedNodePointer node) {
|
|||
fade.voxelDetails = rootDetails;
|
||||
const float slightly_smaller = 0.99f;
|
||||
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
|
||||
_voxelFadesLock.lockForWrite();
|
||||
_voxelFades.push_back(fade);
|
||||
_voxelFadesLock.unlock();
|
||||
}
|
||||
|
||||
// If the model server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server
|
||||
|
@ -3356,7 +3372,9 @@ int Application::parseOctreeStats(const QByteArray& packet, const SharedNodePoin
|
|||
fade.voxelDetails = rootDetails;
|
||||
const float slightly_smaller = 0.99f;
|
||||
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
|
||||
_voxelFadesLock.lockForWrite();
|
||||
_voxelFades.push_back(fade);
|
||||
_voxelFadesLock.unlock();
|
||||
}
|
||||
}
|
||||
// store jurisdiction details for later use
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <QHash>
|
||||
#include <QTouchEvent>
|
||||
#include <QUndoStack>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
#include <ModelEditPacketSender.h>
|
||||
#include <NetworkPacket.h>
|
||||
|
@ -203,6 +204,7 @@ public:
|
|||
JoystickManager* getJoystickManager() { return &_joystickManager; }
|
||||
BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; }
|
||||
QUndoStack* getUndoStack() { return &_undoStack; }
|
||||
QSystemTrayIcon* getTrayIcon() { return _trayIcon; }
|
||||
|
||||
/// if you need to access the application settings, use lockSettings()/unlockSettings()
|
||||
QSettings* lockSettings() { _settingsMutex.lock(); return _settings; }
|
||||
|
@ -467,7 +469,7 @@ private:
|
|||
glm::mat4 _untranslatedViewMatrix;
|
||||
glm::vec3 _viewMatrixTranslation;
|
||||
glm::mat4 _projectionMatrix;
|
||||
|
||||
|
||||
float _scaleMirror;
|
||||
float _rotateMirror;
|
||||
float _raiseMirror;
|
||||
|
@ -533,6 +535,7 @@ private:
|
|||
NodeBounds _nodeBoundsDisplay;
|
||||
|
||||
std::vector<VoxelFade> _voxelFades;
|
||||
QReadWriteLock _voxelFadesLock;
|
||||
ControllerScriptingInterface _controllerScriptingInterface;
|
||||
QPointer<LogDialog> _logDialog;
|
||||
QPointer<SnapshotShareDialog> _snapshotShareDialog;
|
||||
|
@ -554,6 +557,8 @@ private:
|
|||
RunningScriptsWidget* _runningScriptsWidget;
|
||||
QHash<QString, ScriptEngine*> _scriptEnginesHash;
|
||||
bool _runningScriptsWidgetWasVisible;
|
||||
|
||||
QSystemTrayIcon* _trayIcon;
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <UUID.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "AccountManager.h"
|
||||
#include "Menu.h"
|
||||
#include "scripting/MenuScriptingInterface.h"
|
||||
#include "Util.h"
|
||||
|
@ -195,12 +196,13 @@ Menu::Menu() :
|
|||
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::Attachments, 0, this, SLOT(editAttachments()));
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::Animations, 0, this, SLOT(editAnimations()));
|
||||
|
||||
|
||||
addDisabledActionAndSeparator(editMenu, "Physics");
|
||||
QObject* avatar = appInstance->getAvatar();
|
||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, false,
|
||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, false,
|
||||
avatar, SLOT(updateMotionBehaviorsFromMenu()));
|
||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::StandOnNearbyFloors, 0, true,
|
||||
avatar, SLOT(updateMotionBehaviorsFromMenu()));
|
||||
|
||||
|
||||
addAvatarCollisionSubMenu(editMenu);
|
||||
|
||||
|
@ -331,6 +333,7 @@ Menu::Menu() :
|
|||
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::AllowOculusCameraModeChange, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::AvatarsReceiveShadows, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderSkeletonCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderHeadCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderBoundingCollisionShapes);
|
||||
|
@ -1025,24 +1028,24 @@ void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJson
|
|||
void Menu::muteEnvironment() {
|
||||
int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment);
|
||||
int packetSize = headerSize + sizeof(glm::vec3) + sizeof(float);
|
||||
|
||||
|
||||
glm::vec3 position = Application::getInstance()->getAvatar()->getPosition();
|
||||
|
||||
|
||||
char* packet = (char*)malloc(packetSize);
|
||||
populatePacketHeader(packet, PacketTypeMuteEnvironment);
|
||||
memcpy(packet + headerSize, &position, sizeof(glm::vec3));
|
||||
memcpy(packet + headerSize + sizeof(glm::vec3), &MUTE_RADIUS, sizeof(float));
|
||||
|
||||
|
||||
QByteArray mutePacket(packet, packetSize);
|
||||
|
||||
|
||||
// grab our audio mixer from the NodeList, if it exists
|
||||
SharedNodePointer audioMixer = NodeList::getInstance()->soloNodeOfType(NodeType::AudioMixer);
|
||||
|
||||
|
||||
if (audioMixer) {
|
||||
// send off this mute packet
|
||||
NodeList::getInstance()->writeDatagram(mutePacket, audioMixer);
|
||||
}
|
||||
|
||||
|
||||
free(packet);
|
||||
}
|
||||
|
||||
|
@ -1200,19 +1203,24 @@ void Menu::showScriptEditor() {
|
|||
}
|
||||
|
||||
void Menu::showChat() {
|
||||
QMainWindow* mainWindow = Application::getInstance()->getWindow();
|
||||
if (!_chatWindow) {
|
||||
_chatWindow = new ChatWindow(mainWindow);
|
||||
}
|
||||
if (_chatWindow->isHidden()) {
|
||||
_chatWindow->show();
|
||||
if (AccountManager::getInstance().isLoggedIn()) {
|
||||
QMainWindow* mainWindow = Application::getInstance()->getWindow();
|
||||
if (!_chatWindow) {
|
||||
_chatWindow = new ChatWindow(mainWindow);
|
||||
}
|
||||
|
||||
if (_chatWindow->isHidden()) {
|
||||
_chatWindow->show();
|
||||
}
|
||||
} else {
|
||||
Application::getInstance()->getTrayIcon()->showMessage("Interface", "You need to login to be able to chat with others on this domain.");
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::toggleChat() {
|
||||
#ifdef HAVE_QXMPP
|
||||
_chatAction->setEnabled(XmppClient::getInstance().getXMPPClient().isConnected());
|
||||
if (!_chatAction->isEnabled() && _chatWindow) {
|
||||
if (!_chatAction->isEnabled() && _chatWindow && AccountManager::getInstance().isLoggedIn()) {
|
||||
if (_chatWindow->isHidden()) {
|
||||
_chatWindow->show();
|
||||
} else {
|
||||
|
|
|
@ -293,6 +293,7 @@ namespace MenuOption {
|
|||
const QString AudioSpatialProcessingDontDistanceAttenuate = "Don't calculate distance attenuation";
|
||||
const QString AudioSpatialProcessingAlternateDistanceAttenuate = "Alternate distance attenuation";
|
||||
const QString Avatars = "Avatars";
|
||||
const QString AvatarsReceiveShadows = "Avatars Receive Shadows";
|
||||
const QString Bandwidth = "Bandwidth Display";
|
||||
const QString BandwidthDetails = "Bandwidth Details";
|
||||
const QString BuckyBalls = "Bucky Balls";
|
||||
|
@ -376,6 +377,7 @@ namespace MenuOption {
|
|||
const QString ShowBordersModelNodes = "Show Model Nodes";
|
||||
const QString ShowBordersParticleNodes = "Show Particle Nodes";
|
||||
const QString ShowIKConstraints = "Show IK Constraints";
|
||||
const QString StandOnNearbyFloors = "Stand on nearby floors";
|
||||
const QString Stars = "Stars";
|
||||
const QString Stats = "Stats";
|
||||
const QString StopAllScripts = "Stop All Scripts";
|
||||
|
|
|
@ -347,7 +347,6 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
|
|||
void Avatar::renderBody(RenderMode renderMode, float glowLevel) {
|
||||
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
|
||||
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
|
||||
|
||||
{
|
||||
Glower glower(glowLevel);
|
||||
|
||||
|
@ -356,7 +355,8 @@ void Avatar::renderBody(RenderMode renderMode, float glowLevel) {
|
|||
renderBillboard();
|
||||
return;
|
||||
}
|
||||
_skeletonModel.render(1.0f, modelRenderMode);
|
||||
|
||||
_skeletonModel.render(1.0f, modelRenderMode, Menu::getInstance()->isOptionChecked(MenuOption::AvatarsReceiveShadows));
|
||||
renderAttachments(renderMode);
|
||||
getHand()->render(false, modelRenderMode);
|
||||
}
|
||||
|
@ -390,8 +390,9 @@ void Avatar::simulateAttachments(float deltaTime) {
|
|||
void Avatar::renderAttachments(RenderMode renderMode) {
|
||||
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
|
||||
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
|
||||
bool receiveShadows = Menu::getInstance()->isOptionChecked(MenuOption::AvatarsReceiveShadows);
|
||||
foreach (Model* model, _attachmentModels) {
|
||||
model->render(1.0f, modelRenderMode);
|
||||
model->render(1.0f, modelRenderMode, receiveShadows);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -176,7 +176,8 @@ void Head::relaxLean(float deltaTime) {
|
|||
}
|
||||
|
||||
void Head::render(float alpha, Model::RenderMode mode) {
|
||||
if (_faceModel.render(alpha, mode) && _renderLookatVectors && mode != Model::SHADOW_RENDER_MODE) {
|
||||
if (_faceModel.render(alpha, mode, Menu::getInstance()->isOptionChecked(MenuOption::AvatarsReceiveShadows)) &&
|
||||
_renderLookatVectors && mode != Model::SHADOW_RENDER_MODE) {
|
||||
renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,6 +65,7 @@ MyAvatar::MyAvatar() :
|
|||
_distanceToNearestAvatar(std::numeric_limits<float>::max()),
|
||||
_wasPushing(false),
|
||||
_isPushing(false),
|
||||
_isBraking(false),
|
||||
_trapDuration(0.0f),
|
||||
_thrust(0.0f),
|
||||
_motorVelocity(0.0f),
|
||||
|
@ -134,45 +135,7 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
_handState = HAND_STATE_NULL;
|
||||
|
||||
updateOrientation(deltaTime);
|
||||
|
||||
float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) +
|
||||
fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT]) +
|
||||
fabsf(_driveKeys[UP] - _driveKeys[DOWN]);
|
||||
|
||||
bool walkingOnFloor = false;
|
||||
float gravityLength = glm::length(_gravity);
|
||||
if (gravityLength > EPSILON) {
|
||||
const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape();
|
||||
glm::vec3 startCap;
|
||||
boundingShape.getStartPoint(startCap);
|
||||
glm::vec3 bottomOfBoundingCapsule = startCap + (boundingShape.getRadius() / gravityLength) * _gravity;
|
||||
|
||||
float fallThreshold = 2.0f * deltaTime * gravityLength;
|
||||
walkingOnFloor = (glm::distance(bottomOfBoundingCapsule, _lastFloorContactPoint) < fallThreshold);
|
||||
}
|
||||
|
||||
if (keyboardInput > 0.0f || glm::length2(_velocity) > 0.0f || glm::length2(_thrust) > 0.0f ||
|
||||
! walkingOnFloor) {
|
||||
// apply gravity
|
||||
_velocity += _scale * _gravity * (GRAVITY_EARTH * deltaTime);
|
||||
|
||||
// update motor and thrust
|
||||
updateMotorFromKeyboard(deltaTime, walkingOnFloor);
|
||||
applyMotor(deltaTime);
|
||||
applyThrust(deltaTime);
|
||||
|
||||
// update position
|
||||
if (glm::length2(_velocity) < EPSILON) {
|
||||
_velocity = glm::vec3(0.0f);
|
||||
} else {
|
||||
_position += _velocity * deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
// update moving flag based on speed
|
||||
const float MOVING_SPEED_THRESHOLD = 0.01f;
|
||||
_moving = glm::length(_velocity) > MOVING_SPEED_THRESHOLD;
|
||||
updateChatCircle(deltaTime);
|
||||
updatePosition(deltaTime);
|
||||
|
||||
// update avatar skeleton and simulate hand and head
|
||||
getHand()->collideAgainstOurself();
|
||||
|
@ -206,19 +169,17 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.0f));
|
||||
radius *= COLLISION_RADIUS_SCALAR;
|
||||
}
|
||||
if (_collisionGroups) {
|
||||
updateShapePositions();
|
||||
if (_collisionGroups & COLLISION_GROUP_ENVIRONMENT) {
|
||||
updateCollisionWithEnvironment(deltaTime, radius);
|
||||
}
|
||||
if (_collisionGroups & COLLISION_GROUP_VOXELS) {
|
||||
updateCollisionWithVoxels(deltaTime, radius);
|
||||
} else {
|
||||
_trapDuration = 0.0f;
|
||||
}
|
||||
if (_collisionGroups & COLLISION_GROUP_AVATARS) {
|
||||
updateCollisionWithAvatars(deltaTime);
|
||||
}
|
||||
updateShapePositions();
|
||||
if (_collisionGroups & COLLISION_GROUP_ENVIRONMENT) {
|
||||
updateCollisionWithEnvironment(deltaTime, radius);
|
||||
}
|
||||
if (_collisionGroups & COLLISION_GROUP_VOXELS) {
|
||||
updateCollisionWithVoxels(deltaTime, radius);
|
||||
} else {
|
||||
_trapDuration = 0.0f;
|
||||
}
|
||||
if (_collisionGroups & COLLISION_GROUP_AVATARS) {
|
||||
updateCollisionWithAvatars(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -423,9 +384,9 @@ void MyAvatar::setGravity(const glm::vec3& gravity) {
|
|||
float gravityLength = glm::length(gravity);
|
||||
if (gravityLength > EPSILON) {
|
||||
_worldUpDirection = _gravity / -gravityLength;
|
||||
} else {
|
||||
_worldUpDirection = DEFAULT_UP_DIRECTION;
|
||||
}
|
||||
// NOTE: the else case here it to leave _worldUpDirection unchanged
|
||||
// so it continues to point opposite to the previous gravity setting.
|
||||
}
|
||||
|
||||
AnimationHandlePointer MyAvatar::addAnimationHandle() {
|
||||
|
@ -812,7 +773,7 @@ void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) {
|
|||
// Render the body's voxels and head
|
||||
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
|
||||
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
|
||||
_skeletonModel.render(1.0f, modelRenderMode);
|
||||
_skeletonModel.render(1.0f, modelRenderMode, Menu::getInstance()->isOptionChecked(MenuOption::AvatarsReceiveShadows));
|
||||
renderAttachments(renderMode);
|
||||
|
||||
// Render head so long as the camera isn't inside it
|
||||
|
@ -830,6 +791,15 @@ bool MyAvatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode rend
|
|||
(glm::length(cameraPosition - head->calculateAverageEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE * _scale);
|
||||
}
|
||||
|
||||
float MyAvatar::computeDistanceToFloor(const glm::vec3& startPoint) {
|
||||
glm::vec3 direction = -_worldUpDirection;
|
||||
OctreeElement* elementHit; // output from findRayIntersection
|
||||
float distance = FLT_MAX; // output from findRayIntersection
|
||||
BoxFace face; // output from findRayIntersection
|
||||
Application::getInstance()->getVoxelTree()->findRayIntersection(startPoint, direction, elementHit, distance, face);
|
||||
return distance;
|
||||
}
|
||||
|
||||
void MyAvatar::updateOrientation(float deltaTime) {
|
||||
// Gather rotation information from keyboard
|
||||
_bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime;
|
||||
|
@ -890,8 +860,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
// We must adjust the body orientation using a delta rotation (rather than
|
||||
// doing yaw math) because the body's yaw ranges are not the same
|
||||
// as what the Oculus API provides.
|
||||
glm::vec3 UP_AXIS = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
glm::quat bodyCorrection = glm::angleAxis(glm::radians(delta), UP_AXIS);
|
||||
glm::quat bodyCorrection = glm::angleAxis(glm::radians(delta), _worldUpDirection);
|
||||
orientation = orientation * bodyCorrection;
|
||||
}
|
||||
Head* head = getHead();
|
||||
|
@ -905,6 +874,96 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
setOrientation(orientation);
|
||||
}
|
||||
|
||||
const float NEARBY_FLOOR_THRESHOLD = 5.0f;
|
||||
|
||||
void MyAvatar::updatePosition(float deltaTime) {
|
||||
float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) +
|
||||
fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT]) +
|
||||
fabsf(_driveKeys[UP] - _driveKeys[DOWN]);
|
||||
|
||||
bool walkingOnFloor = false;
|
||||
float gravityLength = glm::length(_gravity) * GRAVITY_EARTH;
|
||||
|
||||
const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape();
|
||||
glm::vec3 startCap;
|
||||
boundingShape.getStartPoint(startCap);
|
||||
glm::vec3 bottom = startCap - boundingShape.getRadius() * _worldUpDirection;
|
||||
|
||||
if (gravityLength > EPSILON) {
|
||||
float speedFromGravity = _scale * deltaTime * gravityLength;
|
||||
float distanceToFall = glm::distance(bottom, _lastFloorContactPoint);
|
||||
walkingOnFloor = (distanceToFall < 2.0f * deltaTime * speedFromGravity);
|
||||
|
||||
if (walkingOnFloor) {
|
||||
// BEGIN HACK: to prevent the avatar from bouncing on a floor surface
|
||||
if (distanceToFall < deltaTime * speedFromGravity) {
|
||||
float verticalSpeed = glm::dot(_velocity, _worldUpDirection);
|
||||
if (fabs(verticalSpeed) < speedFromGravity) {
|
||||
// we're standing on a floor, and nearly at rest so we zero the vertical velocity component
|
||||
_velocity -= verticalSpeed * _worldUpDirection;
|
||||
}
|
||||
} else {
|
||||
// fall with gravity against floor
|
||||
_velocity -= speedFromGravity * _worldUpDirection;
|
||||
}
|
||||
// END HACK
|
||||
} else {
|
||||
if (!_isBraking) {
|
||||
// fall with gravity toward floor
|
||||
_velocity -= speedFromGravity * _worldUpDirection;
|
||||
}
|
||||
|
||||
if (_motionBehaviors & AVATAR_MOTION_STAND_ON_NEARBY_FLOORS) {
|
||||
const float MAX_VERTICAL_FLOOR_DETECTION_SPEED = _scale * MAX_WALKING_SPEED;
|
||||
if (keyboardInput && glm::dot(_motorVelocity, _worldUpDirection) > 0.0f &&
|
||||
glm::dot(_velocity, _worldUpDirection) > MAX_VERTICAL_FLOOR_DETECTION_SPEED) {
|
||||
// disable local gravity when flying up
|
||||
setLocalGravity(glm::vec3(0.0f));
|
||||
} else {
|
||||
const float maxFloorDistance = _scale * NEARBY_FLOOR_THRESHOLD;
|
||||
if (computeDistanceToFloor(bottom) > maxFloorDistance) {
|
||||
// disable local gravity when floor is too far
|
||||
setLocalGravity(glm::vec3(0.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((_collisionGroups & COLLISION_GROUP_VOXELS) &&
|
||||
_motionBehaviors & AVATAR_MOTION_STAND_ON_NEARBY_FLOORS) {
|
||||
const float MIN_FLOOR_DETECTION_SPEED = _scale * 1.0f;
|
||||
if (glm::length(_velocity) < MIN_FLOOR_DETECTION_SPEED ) {
|
||||
// scan for floor under avatar
|
||||
const float maxFloorDistance = _scale * NEARBY_FLOOR_THRESHOLD;
|
||||
if (computeDistanceToFloor(bottom) < maxFloorDistance) {
|
||||
// enable local gravity
|
||||
setLocalGravity(-_worldUpDirection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (keyboardInput > 0.0f || glm::length2(_velocity) > 0.0f || glm::length2(_thrust) > 0.0f || ! walkingOnFloor) {
|
||||
// update motor and thrust
|
||||
updateMotorFromKeyboard(deltaTime, walkingOnFloor);
|
||||
applyMotor(deltaTime);
|
||||
applyThrust(deltaTime);
|
||||
|
||||
// update position
|
||||
if (glm::length2(_velocity) < EPSILON) {
|
||||
_velocity = glm::vec3(0.0f);
|
||||
} else {
|
||||
_position += _velocity * deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
// update moving flag based on speed
|
||||
const float MOVING_SPEED_THRESHOLD = 0.01f;
|
||||
_moving = glm::length(_velocity) > MOVING_SPEED_THRESHOLD;
|
||||
|
||||
updateChatCircle(deltaTime);
|
||||
}
|
||||
|
||||
void MyAvatar::updateMotorFromKeyboard(float deltaTime, bool walking) {
|
||||
// Increase motor velocity until its length is equal to _maxMotorSpeed.
|
||||
if (!(_motionBehaviors & AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED)) {
|
||||
|
@ -930,12 +989,12 @@ void MyAvatar::updateMotorFromKeyboard(float deltaTime, bool walking) {
|
|||
if (directionLength > EPSILON) {
|
||||
direction /= directionLength;
|
||||
// the finalMotorSpeed depends on whether we are walking or not
|
||||
float finalMaxMotorSpeed = walking ? MAX_WALKING_SPEED : _maxMotorSpeed;
|
||||
float finalMaxMotorSpeed = walking ? _scale * MAX_WALKING_SPEED : _scale * _maxMotorSpeed;
|
||||
|
||||
float motorLength = glm::length(_motorVelocity);
|
||||
if (motorLength < MIN_KEYBOARD_CONTROL_SPEED) {
|
||||
if (motorLength < _scale * MIN_KEYBOARD_CONTROL_SPEED) {
|
||||
// an active keyboard motor should never be slower than this
|
||||
_motorVelocity = MIN_KEYBOARD_CONTROL_SPEED * direction;
|
||||
_motorVelocity = _scale * MIN_KEYBOARD_CONTROL_SPEED * direction;
|
||||
} else {
|
||||
float MOTOR_LENGTH_TIMESCALE = 1.5f;
|
||||
float tau = glm::clamp(deltaTime / MOTOR_LENGTH_TIMESCALE, 0.0f, 1.0f);
|
||||
|
@ -963,32 +1022,30 @@ float MyAvatar::computeMotorTimescale() {
|
|||
// (1) braking --> short timescale (aggressive motor assertion)
|
||||
// (2) pushing --> medium timescale (mild motor assertion)
|
||||
// (3) inactive --> long timescale (gentle friction for low speeds)
|
||||
//
|
||||
// TODO: recover extra braking behavior when flying close to nearest avatar
|
||||
|
||||
float MIN_MOTOR_TIMESCALE = 0.125f;
|
||||
float MAX_MOTOR_TIMESCALE = 0.5f;
|
||||
float MIN_BRAKE_SPEED = 0.4f;
|
||||
|
||||
float timescale = MAX_MOTOR_TIMESCALE;
|
||||
float speed = glm::length(_velocity);
|
||||
bool areThrusting = (glm::length2(_thrust) > EPSILON);
|
||||
|
||||
if (_wasPushing && !(_isPushing || areThrusting) && speed > MIN_BRAKE_SPEED) {
|
||||
// we don't change _wasPushing for this case -->
|
||||
// keeps the brakes on until we go below MIN_BRAKE_SPEED
|
||||
timescale = MIN_MOTOR_TIMESCALE;
|
||||
bool isThrust = (glm::length2(_thrust) > EPSILON);
|
||||
if (_isPushing || isThrust) {
|
||||
timescale = _motorTimescale;
|
||||
_isBraking = false;
|
||||
} else {
|
||||
if (_isPushing) {
|
||||
timescale = _motorTimescale;
|
||||
}
|
||||
_wasPushing = _isPushing || areThrusting;
|
||||
float speed = glm::length(_velocity);
|
||||
_isBraking = _wasPushing || (_isBraking && speed > MIN_BRAKE_SPEED);
|
||||
if (_isBraking) {
|
||||
timescale = MIN_MOTOR_TIMESCALE;
|
||||
}
|
||||
}
|
||||
_wasPushing = _isPushing || isThrust;
|
||||
_isPushing = false;
|
||||
return timescale;
|
||||
}
|
||||
|
||||
void MyAvatar::applyMotor(float deltaTime) {
|
||||
// TODO: recover extra braking behavior when flying close to nearest avatar
|
||||
if (!( _motionBehaviors & AVATAR_MOTION_MOTOR_ENABLED)) {
|
||||
// nothing to do --> early exit
|
||||
return;
|
||||
|
@ -1178,6 +1235,8 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
|
|||
const float MIN_STEP_HEIGHT = 0.0f;
|
||||
glm::vec3 footBase = boundingShape.getPosition() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection;
|
||||
float highestStep = 0.0f;
|
||||
float lowestStep = MAX_STEP_HEIGHT;
|
||||
glm::vec3 floorPoint;
|
||||
glm::vec3 stepPenetration(0.0f);
|
||||
glm::vec3 totalPenetration(0.0f);
|
||||
|
||||
|
@ -1211,8 +1270,15 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
|
|||
highestStep = stepHeight;
|
||||
stepPenetration = collision->_penetration;
|
||||
}
|
||||
if (stepHeight < lowestStep) {
|
||||
lowestStep = stepHeight;
|
||||
floorPoint = collision->_contactPoint - collision->_penetration;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lowestStep < MAX_STEP_HEIGHT) {
|
||||
_lastFloorContactPoint = floorPoint;
|
||||
}
|
||||
|
||||
float penetrationLength = glm::length(totalPenetration);
|
||||
if (penetrationLength < EPSILON) {
|
||||
|
@ -1251,8 +1317,9 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
|
|||
applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
|
||||
}
|
||||
|
||||
const float VOXEL_COLLISION_FREQUENCY = 0.5f;
|
||||
updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
|
||||
// Don't make a collision sound against voxlels by default -- too annoying when walking
|
||||
//const float VOXEL_COLLISION_FREQUENCY = 0.5f;
|
||||
//updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
|
||||
}
|
||||
_trapDuration = isTrapped ? _trapDuration + deltaTime : 0.0f;
|
||||
}
|
||||
|
@ -1598,7 +1665,8 @@ void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) {
|
|||
}
|
||||
|
||||
void MyAvatar::updateMotionBehaviorsFromMenu() {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::ObeyEnvironmentalGravity)) {
|
||||
Menu* menu = Menu::getInstance();
|
||||
if (menu->isOptionChecked(MenuOption::ObeyEnvironmentalGravity)) {
|
||||
_motionBehaviors |= AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY;
|
||||
// Environmental and Local gravities are incompatible. Environmental setting trumps local.
|
||||
_motionBehaviors &= ~AVATAR_MOTION_OBEY_LOCAL_GRAVITY;
|
||||
|
@ -1608,6 +1676,14 @@ void MyAvatar::updateMotionBehaviorsFromMenu() {
|
|||
if (! (_motionBehaviors & (AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY | AVATAR_MOTION_OBEY_LOCAL_GRAVITY))) {
|
||||
setGravity(glm::vec3(0.0f));
|
||||
}
|
||||
if (menu->isOptionChecked(MenuOption::StandOnNearbyFloors)) {
|
||||
_motionBehaviors |= AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
|
||||
// standing on floors requires collision with voxels
|
||||
_collisionGroups |= COLLISION_GROUP_VOXELS;
|
||||
menu->setIsOptionChecked(MenuOption::CollideWithVoxels, true);
|
||||
} else {
|
||||
_motionBehaviors &= ~AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::renderAttachments(RenderMode renderMode) {
|
||||
|
@ -1619,10 +1695,11 @@ void MyAvatar::renderAttachments(RenderMode renderMode) {
|
|||
QString headJointName = (geometry.headJointIndex == -1) ? QString() : geometry.joints.at(geometry.headJointIndex).name;
|
||||
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
|
||||
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
|
||||
bool receiveShadows = Menu::getInstance()->isOptionChecked(MenuOption::AvatarsReceiveShadows);
|
||||
for (int i = 0; i < _attachmentData.size(); i++) {
|
||||
const QString& jointName = _attachmentData.at(i).jointName;
|
||||
if (jointName != headJointName && jointName != "Head") {
|
||||
_attachmentModels.at(i)->render(1.0f, modelRenderMode);
|
||||
_attachmentModels.at(i)->render(1.0f, modelRenderMode, receiveShadows);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1634,6 +1711,11 @@ void MyAvatar::setCollisionGroups(quint32 collisionGroups) {
|
|||
menu->setIsOptionChecked(MenuOption::CollideWithAvatars, (bool)(_collisionGroups & COLLISION_GROUP_AVATARS));
|
||||
menu->setIsOptionChecked(MenuOption::CollideWithVoxels, (bool)(_collisionGroups & COLLISION_GROUP_VOXELS));
|
||||
menu->setIsOptionChecked(MenuOption::CollideWithParticles, (bool)(_collisionGroups & COLLISION_GROUP_PARTICLES));
|
||||
if (! (_collisionGroups & COLLISION_GROUP_VOXELS)) {
|
||||
// no collision with voxels --> disable standing on floors
|
||||
_motionBehaviors &= ~AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
|
||||
menu->setIsOptionChecked(MenuOption::StandOnNearbyFloors, false);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setMotionBehaviorsByScript(quint32 flags) {
|
||||
|
|
|
@ -150,11 +150,11 @@ private:
|
|||
bool _shouldJump;
|
||||
float _driveKeys[MAX_DRIVE_KEYS];
|
||||
glm::vec3 _gravity;
|
||||
glm::vec3 _environmentGravity;
|
||||
float _distanceToNearestAvatar; // How close is the nearest avatar?
|
||||
|
||||
bool _wasPushing;
|
||||
bool _isPushing;
|
||||
bool _isBraking;
|
||||
float _trapDuration; // seconds that avatar has been trapped by collisions
|
||||
glm::vec3 _thrust; // final acceleration from outside sources for the current frame
|
||||
|
||||
|
@ -174,7 +174,9 @@ private:
|
|||
QList<AnimationHandlePointer> _animationHandles;
|
||||
|
||||
// private methods
|
||||
float computeDistanceToFloor(const glm::vec3& startPoint);
|
||||
void updateOrientation(float deltaTime);
|
||||
void updatePosition(float deltaTime);
|
||||
void updateMotorFromKeyboard(float deltaTime, bool walking);
|
||||
float computeMotorTimescale();
|
||||
void applyMotor(float deltaTime);
|
||||
|
|
|
@ -220,7 +220,13 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
|||
// TODO: should we allow modelItems to have alpha on their models?
|
||||
Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE
|
||||
? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
|
||||
model->render(alpha, modelRenderMode);
|
||||
|
||||
if (modelItem.getGlowLevel() > 0.0f) {
|
||||
Glower glower(modelItem.getGlowLevel());
|
||||
model->render(alpha, modelRenderMode);
|
||||
} else {
|
||||
model->render(alpha, modelRenderMode);
|
||||
}
|
||||
|
||||
if (!isShadowMode && displayModelBounds) {
|
||||
|
||||
|
|
|
@ -59,18 +59,39 @@ ProgramObject Model::_program;
|
|||
ProgramObject Model::_normalMapProgram;
|
||||
ProgramObject Model::_specularMapProgram;
|
||||
ProgramObject Model::_normalSpecularMapProgram;
|
||||
|
||||
ProgramObject Model::_shadowMapProgram;
|
||||
ProgramObject Model::_shadowNormalMapProgram;
|
||||
ProgramObject Model::_shadowSpecularMapProgram;
|
||||
ProgramObject Model::_shadowNormalSpecularMapProgram;
|
||||
|
||||
ProgramObject Model::_shadowProgram;
|
||||
|
||||
ProgramObject Model::_skinProgram;
|
||||
ProgramObject Model::_skinNormalMapProgram;
|
||||
ProgramObject Model::_skinSpecularMapProgram;
|
||||
ProgramObject Model::_skinNormalSpecularMapProgram;
|
||||
|
||||
ProgramObject Model::_skinShadowMapProgram;
|
||||
ProgramObject Model::_skinShadowNormalMapProgram;
|
||||
ProgramObject Model::_skinShadowSpecularMapProgram;
|
||||
ProgramObject Model::_skinShadowNormalSpecularMapProgram;
|
||||
|
||||
ProgramObject Model::_skinShadowProgram;
|
||||
|
||||
int Model::_normalMapTangentLocation;
|
||||
int Model::_normalSpecularMapTangentLocation;
|
||||
int Model::_shadowNormalMapTangentLocation;
|
||||
int Model::_shadowNormalSpecularMapTangentLocation;
|
||||
|
||||
Model::SkinLocations Model::_skinLocations;
|
||||
Model::SkinLocations Model::_skinNormalMapLocations;
|
||||
Model::SkinLocations Model::_skinSpecularMapLocations;
|
||||
Model::SkinLocations Model::_skinNormalSpecularMapLocations;
|
||||
Model::SkinLocations Model::_skinShadowMapLocations;
|
||||
Model::SkinLocations Model::_skinShadowNormalMapLocations;
|
||||
Model::SkinLocations Model::_skinShadowSpecularMapLocations;
|
||||
Model::SkinLocations Model::_skinShadowNormalSpecularMapLocations;
|
||||
Model::SkinLocations Model::_skinShadowLocations;
|
||||
|
||||
void Model::setScale(const glm::vec3& scale) {
|
||||
|
@ -100,7 +121,8 @@ void Model::setOffset(const glm::vec3& offset) {
|
|||
}
|
||||
|
||||
|
||||
void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations, int specularTextureUnit) {
|
||||
void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations,
|
||||
int specularTextureUnit, int shadowTextureUnit) {
|
||||
program.bind();
|
||||
locations.clusterMatrices = program.uniformLocation("clusterMatrices");
|
||||
locations.clusterIndices = program.attributeLocation("clusterIndices");
|
||||
|
@ -109,6 +131,7 @@ void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locati
|
|||
program.setUniformValue("diffuseMap", 0);
|
||||
program.setUniformValue("normalMap", 1);
|
||||
program.setUniformValue("specularMap", specularTextureUnit);
|
||||
program.setUniformValue("shadowMap", shadowTextureUnit);
|
||||
program.release();
|
||||
}
|
||||
|
||||
|
@ -170,7 +193,7 @@ void Model::init() {
|
|||
_program.link();
|
||||
|
||||
_program.bind();
|
||||
_program.setUniformValue("texture", 0);
|
||||
_program.setUniformValue("diffuseMap", 0);
|
||||
_program.release();
|
||||
|
||||
_normalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
|
@ -209,11 +232,63 @@ void Model::init() {
|
|||
_normalSpecularMapTangentLocation = _normalMapProgram.attributeLocation("tangent");
|
||||
_normalSpecularMapProgram.release();
|
||||
|
||||
|
||||
_shadowMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model.vert");
|
||||
_shadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
|
||||
"shaders/model_shadow_map.frag");
|
||||
_shadowMapProgram.link();
|
||||
|
||||
_shadowMapProgram.bind();
|
||||
_shadowMapProgram.setUniformValue("diffuseMap", 0);
|
||||
_shadowMapProgram.setUniformValue("shadowMap", 1);
|
||||
_shadowMapProgram.release();
|
||||
|
||||
_shadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/model_normal_map.vert");
|
||||
_shadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_shadow_normal_map.frag");
|
||||
_shadowNormalMapProgram.link();
|
||||
|
||||
_shadowNormalMapProgram.bind();
|
||||
_shadowNormalMapProgram.setUniformValue("diffuseMap", 0);
|
||||
_shadowNormalMapProgram.setUniformValue("normalMap", 1);
|
||||
_shadowNormalMapProgram.setUniformValue("shadowMap", 2);
|
||||
_shadowNormalMapTangentLocation = _shadowNormalMapProgram.attributeLocation("tangent");
|
||||
_shadowNormalMapProgram.release();
|
||||
|
||||
_shadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/model.vert");
|
||||
_shadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_shadow_specular_map.frag");
|
||||
_shadowSpecularMapProgram.link();
|
||||
|
||||
_shadowSpecularMapProgram.bind();
|
||||
_shadowSpecularMapProgram.setUniformValue("diffuseMap", 0);
|
||||
_shadowSpecularMapProgram.setUniformValue("specularMap", 1);
|
||||
_shadowSpecularMapProgram.setUniformValue("shadowMap", 2);
|
||||
_shadowSpecularMapProgram.release();
|
||||
|
||||
_shadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/model_normal_map.vert");
|
||||
_shadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_shadow_normal_specular_map.frag");
|
||||
_shadowNormalSpecularMapProgram.link();
|
||||
|
||||
_shadowNormalSpecularMapProgram.bind();
|
||||
_shadowNormalSpecularMapProgram.setUniformValue("diffuseMap", 0);
|
||||
_shadowNormalSpecularMapProgram.setUniformValue("normalMap", 1);
|
||||
_shadowNormalSpecularMapProgram.setUniformValue("specularMap", 2);
|
||||
_shadowNormalSpecularMapProgram.setUniformValue("shadowMap", 3);
|
||||
_shadowNormalSpecularMapTangentLocation = _normalMapProgram.attributeLocation("tangent");
|
||||
_shadowNormalSpecularMapProgram.release();
|
||||
|
||||
|
||||
_shadowProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_shadow.vert");
|
||||
_shadowProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_shadow.frag");
|
||||
_shadowProgram.link();
|
||||
|
||||
|
||||
_skinProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/skin_model.vert");
|
||||
_skinProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/model.frag");
|
||||
_skinProgram.link();
|
||||
|
@ -244,6 +319,40 @@ void Model::init() {
|
|||
|
||||
initSkinProgram(_skinNormalSpecularMapProgram, _skinNormalSpecularMapLocations, 2);
|
||||
|
||||
|
||||
_skinShadowMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
||||
"shaders/skin_model.vert");
|
||||
_skinShadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
|
||||
"shaders/model_shadow_map.frag");
|
||||
_skinShadowMapProgram.link();
|
||||
|
||||
initSkinProgram(_skinShadowMapProgram, _skinShadowMapLocations);
|
||||
|
||||
_skinShadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/skin_model_normal_map.vert");
|
||||
_skinShadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_shadow_normal_map.frag");
|
||||
_skinShadowNormalMapProgram.link();
|
||||
|
||||
initSkinProgram(_skinShadowNormalMapProgram, _skinShadowNormalMapLocations, 1, 2);
|
||||
|
||||
_skinShadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/skin_model.vert");
|
||||
_skinShadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_shadow_specular_map.frag");
|
||||
_skinShadowSpecularMapProgram.link();
|
||||
|
||||
initSkinProgram(_skinShadowSpecularMapProgram, _skinShadowSpecularMapLocations, 1, 2);
|
||||
|
||||
_skinShadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/skin_model_normal_map.vert");
|
||||
_skinShadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_shadow_normal_specular_map.frag");
|
||||
_skinShadowNormalSpecularMapProgram.link();
|
||||
|
||||
initSkinProgram(_skinShadowNormalSpecularMapProgram, _skinShadowNormalSpecularMapLocations, 2, 3);
|
||||
|
||||
|
||||
_skinShadowProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/skin_model_shadow.vert");
|
||||
_skinShadowProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
|
@ -361,7 +470,7 @@ bool Model::updateGeometry() {
|
|||
return needFullUpdate;
|
||||
}
|
||||
|
||||
bool Model::render(float alpha, RenderMode mode) {
|
||||
bool Model::render(float alpha, RenderMode mode, bool receiveShadows) {
|
||||
// render the attachments
|
||||
foreach (Model* attachment, _attachments) {
|
||||
attachment->render(alpha, mode);
|
||||
|
@ -389,6 +498,9 @@ bool Model::render(float alpha, RenderMode mode) {
|
|||
glDisable(GL_CULL_FACE);
|
||||
} else {
|
||||
glEnable(GL_CULL_FACE);
|
||||
if (mode == SHADOW_RENDER_MODE) {
|
||||
glCullFace(GL_FRONT);
|
||||
}
|
||||
}
|
||||
|
||||
// render opaque meshes with alpha testing
|
||||
|
@ -396,16 +508,21 @@ bool Model::render(float alpha, RenderMode mode) {
|
|||
glEnable(GL_ALPHA_TEST);
|
||||
glAlphaFunc(GL_GREATER, 0.5f * alpha);
|
||||
|
||||
renderMeshes(alpha, mode, false);
|
||||
receiveShadows &= Menu::getInstance()->isOptionChecked(MenuOption::Shadows);
|
||||
renderMeshes(alpha, mode, false, receiveShadows);
|
||||
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
|
||||
// render translucent meshes afterwards
|
||||
|
||||
renderMeshes(alpha, mode, true);
|
||||
renderMeshes(alpha, mode, true, receiveShadows);
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
if (mode == SHADOW_RENDER_MODE) {
|
||||
glCullFace(GL_BACK);
|
||||
}
|
||||
|
||||
// deactivate vertex arrays after drawing
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
@ -1480,10 +1597,15 @@ void Model::deleteGeometry() {
|
|||
}
|
||||
}
|
||||
|
||||
void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
|
||||
void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool receiveShadows) {
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes();
|
||||
|
||||
if (receiveShadows) {
|
||||
glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[0]);
|
||||
glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[1]);
|
||||
glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[2]);
|
||||
}
|
||||
for (int i = 0; i < networkMeshes.size(); i++) {
|
||||
// exit early if the translucency doesn't match what we're drawing
|
||||
const NetworkMesh& networkMesh = networkMeshes.at(i);
|
||||
|
@ -1506,6 +1628,7 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
|
|||
ProgramObject* skinProgram = &_skinProgram;
|
||||
SkinLocations* skinLocations = &_skinLocations;
|
||||
GLenum specularTextureUnit = 0;
|
||||
GLenum shadowTextureUnit = 0;
|
||||
if (mode == SHADOW_RENDER_MODE) {
|
||||
program = &_shadowProgram;
|
||||
skinProgram = &_skinShadowProgram;
|
||||
|
@ -1513,21 +1636,46 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
|
|||
|
||||
} else if (!mesh.tangents.isEmpty()) {
|
||||
if (mesh.hasSpecularTexture()) {
|
||||
program = &_normalSpecularMapProgram;
|
||||
skinProgram = &_skinNormalSpecularMapProgram;
|
||||
skinLocations = &_skinNormalSpecularMapLocations;
|
||||
if (receiveShadows) {
|
||||
program = &_shadowNormalSpecularMapProgram;
|
||||
skinProgram = &_skinShadowNormalSpecularMapProgram;
|
||||
skinLocations = &_skinShadowNormalSpecularMapLocations;
|
||||
shadowTextureUnit = GL_TEXTURE3;
|
||||
} else {
|
||||
program = &_normalSpecularMapProgram;
|
||||
skinProgram = &_skinNormalSpecularMapProgram;
|
||||
skinLocations = &_skinNormalSpecularMapLocations;
|
||||
}
|
||||
specularTextureUnit = GL_TEXTURE2;
|
||||
|
||||
} else if (receiveShadows) {
|
||||
program = &_shadowNormalMapProgram;
|
||||
skinProgram = &_skinShadowNormalMapProgram;
|
||||
skinLocations = &_skinShadowNormalMapLocations;
|
||||
shadowTextureUnit = GL_TEXTURE2;
|
||||
} else {
|
||||
program = &_normalMapProgram;
|
||||
skinProgram = &_skinNormalMapProgram;
|
||||
skinLocations = &_skinNormalMapLocations;
|
||||
}
|
||||
} else if (mesh.hasSpecularTexture()) {
|
||||
program = &_specularMapProgram;
|
||||
skinProgram = &_skinSpecularMapProgram;
|
||||
skinLocations = &_skinSpecularMapLocations;
|
||||
if (receiveShadows) {
|
||||
program = &_shadowSpecularMapProgram;
|
||||
skinProgram = &_skinShadowSpecularMapProgram;
|
||||
skinLocations = &_skinShadowSpecularMapLocations;
|
||||
shadowTextureUnit = GL_TEXTURE2;
|
||||
} else {
|
||||
program = &_specularMapProgram;
|
||||
skinProgram = &_skinSpecularMapProgram;
|
||||
skinLocations = &_skinSpecularMapLocations;
|
||||
}
|
||||
specularTextureUnit = GL_TEXTURE1;
|
||||
|
||||
} else if (receiveShadows) {
|
||||
program = &_shadowMapProgram;
|
||||
skinProgram = &_skinShadowMapProgram;
|
||||
skinLocations = &_skinShadowMapLocations;
|
||||
shadowTextureUnit = GL_TEXTURE1;
|
||||
}
|
||||
|
||||
const MeshState& state = _meshStates.at(i);
|
||||
|
@ -1616,8 +1764,7 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
|
|||
Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID());
|
||||
|
||||
|
||||
if (!mesh.tangents.isEmpty()) {
|
||||
specularTextureUnit = GL_TEXTURE2;
|
||||
if (!mesh.tangents.isEmpty()) {
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
Texture* normalMap = networkPart.normalTexture.data();
|
||||
glBindTexture(GL_TEXTURE_2D, !normalMap ?
|
||||
|
@ -1632,6 +1779,12 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
|
|||
Application::getInstance()->getTextureCache()->getWhiteTextureID() : specularMap->getID());
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
if (shadowTextureUnit) {
|
||||
glActiveTexture(shadowTextureUnit);
|
||||
glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getShadowDepthTextureID());
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
}
|
||||
glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset);
|
||||
offset += part.quadIndices.size() * sizeof(int);
|
||||
|
@ -1661,6 +1814,12 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
|
|||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
if (shadowTextureUnit) {
|
||||
glActiveTexture(shadowTextureUnit);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
if (state.clusterMatrices.size() > 1) {
|
||||
skinProgram->disableAttributeArray(skinLocations->clusterIndices);
|
||||
skinProgram->disableAttributeArray(skinLocations->clusterWeights);
|
||||
|
|
|
@ -79,7 +79,7 @@ public:
|
|||
|
||||
enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE };
|
||||
|
||||
bool render(float alpha = 1.0f, RenderMode mode = DEFAULT_RENDER_MODE);
|
||||
bool render(float alpha = 1.0f, RenderMode mode = DEFAULT_RENDER_MODE, bool receiveShadows = true);
|
||||
|
||||
/// Sets the URL of the model to render.
|
||||
/// \param fallback the URL of a fallback model to render if the requested model fails to load
|
||||
|
@ -315,7 +315,7 @@ private:
|
|||
|
||||
void applyNextGeometry();
|
||||
void deleteGeometry();
|
||||
void renderMeshes(float alpha, RenderMode mode, bool translucent);
|
||||
void renderMeshes(float alpha, RenderMode mode, bool translucent, bool receiveShadows);
|
||||
QVector<JointState> createJointStates(const FBXGeometry& geometry);
|
||||
|
||||
QSharedPointer<NetworkGeometry> _baseGeometry; ///< reference required to prevent collection of base
|
||||
|
@ -344,15 +344,30 @@ private:
|
|||
static ProgramObject _normalMapProgram;
|
||||
static ProgramObject _specularMapProgram;
|
||||
static ProgramObject _normalSpecularMapProgram;
|
||||
|
||||
static ProgramObject _shadowMapProgram;
|
||||
static ProgramObject _shadowNormalMapProgram;
|
||||
static ProgramObject _shadowSpecularMapProgram;
|
||||
static ProgramObject _shadowNormalSpecularMapProgram;
|
||||
|
||||
static ProgramObject _shadowProgram;
|
||||
|
||||
static ProgramObject _skinProgram;
|
||||
static ProgramObject _skinNormalMapProgram;
|
||||
static ProgramObject _skinSpecularMapProgram;
|
||||
static ProgramObject _skinNormalSpecularMapProgram;
|
||||
|
||||
static ProgramObject _skinShadowMapProgram;
|
||||
static ProgramObject _skinShadowNormalMapProgram;
|
||||
static ProgramObject _skinShadowSpecularMapProgram;
|
||||
static ProgramObject _skinShadowNormalSpecularMapProgram;
|
||||
|
||||
static ProgramObject _skinShadowProgram;
|
||||
|
||||
static int _normalMapTangentLocation;
|
||||
static int _normalSpecularMapTangentLocation;
|
||||
static int _shadowNormalMapTangentLocation;
|
||||
static int _shadowNormalSpecularMapTangentLocation;
|
||||
|
||||
class SkinLocations {
|
||||
public:
|
||||
|
@ -366,9 +381,14 @@ private:
|
|||
static SkinLocations _skinNormalMapLocations;
|
||||
static SkinLocations _skinSpecularMapLocations;
|
||||
static SkinLocations _skinNormalSpecularMapLocations;
|
||||
static SkinLocations _skinShadowMapLocations;
|
||||
static SkinLocations _skinShadowNormalMapLocations;
|
||||
static SkinLocations _skinShadowSpecularMapLocations;
|
||||
static SkinLocations _skinShadowNormalSpecularMapLocations;
|
||||
static SkinLocations _skinShadowLocations;
|
||||
|
||||
static void initSkinProgram(ProgramObject& program, SkinLocations& locations, int specularTextureUnit = 1);
|
||||
static void initSkinProgram(ProgramObject& program, SkinLocations& locations,
|
||||
int specularTextureUnit = 1, int shadowTextureUnit = 1);
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(QPointer<Model>)
|
||||
|
|
|
@ -80,7 +80,7 @@ ChatWindow::ChatWindow(QWidget* parent) :
|
|||
} else {
|
||||
ui->numOnlineLabel->hide();
|
||||
ui->closeButton->hide();
|
||||
ui->usersWidget->hide();
|
||||
ui->usersArea->hide();
|
||||
ui->messagesScrollArea->hide();
|
||||
ui->messagePlainTextEdit->hide();
|
||||
connect(&XmppClient::getInstance(), SIGNAL(joinedPublicChatRoom()), this, SLOT(connected()));
|
||||
|
@ -208,7 +208,7 @@ void ChatWindow::connected() {
|
|||
ui->connectingToXMPPLabel->hide();
|
||||
ui->numOnlineLabel->show();
|
||||
ui->closeButton->show();
|
||||
ui->usersWidget->show();
|
||||
ui->usersArea->show();
|
||||
ui->messagesScrollArea->show();
|
||||
ui->messagePlainTextEdit->show();
|
||||
ui->messagePlainTextEdit->setFocus();
|
||||
|
@ -248,6 +248,7 @@ void ChatWindow::notificationClicked() {
|
|||
return;
|
||||
}
|
||||
}
|
||||
Application::processEvents();
|
||||
|
||||
scrollToBottom();
|
||||
}
|
||||
|
@ -262,6 +263,8 @@ void ChatWindow::error(QXmppClient::Error error) {
|
|||
}
|
||||
|
||||
void ChatWindow::participantsChanged() {
|
||||
bool atBottom = isNearBottom();
|
||||
|
||||
QStringList participants = XmppClient::getInstance().getPublicChatRoom()->participants();
|
||||
ui->numOnlineLabel->setText(tr("%1 online now:").arg(participants.count()));
|
||||
|
||||
|
@ -288,6 +291,11 @@ void ChatWindow::participantsChanged() {
|
|||
userLabel->installEventFilter(this);
|
||||
ui->usersWidget->layout()->addWidget(userLabel);
|
||||
}
|
||||
Application::processEvents();
|
||||
|
||||
if (atBottom) {
|
||||
scrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatWindow::messageReceived(const QXmppMessage& message) {
|
||||
|
@ -306,7 +314,7 @@ void ChatWindow::messageReceived(const QXmppMessage& message) {
|
|||
messageArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
messageArea->setReadOnly(true);
|
||||
|
||||
messageArea->setStyleSheet("padding-bottom: 2px;"
|
||||
messageArea->setStyleSheet("QTextBrowser{ padding-bottom: 2px;"
|
||||
"padding-left: 2px;"
|
||||
"padding-top: 2px;"
|
||||
"padding-right: 20px;"
|
||||
|
@ -314,7 +322,8 @@ void ChatWindow::messageReceived(const QXmppMessage& message) {
|
|||
"color: #333333;"
|
||||
"font-size: 14pt;"
|
||||
"background-color: rgba(0, 0, 0, 0%);"
|
||||
"border: 0;");
|
||||
"border: 0; }"
|
||||
"QMenu{ border: 2px outset gray; }");
|
||||
|
||||
QString userLabel = getParticipantName(message.from());
|
||||
if (fromSelf) {
|
||||
|
|
|
@ -31,6 +31,10 @@ const QString FORUM_REPLY_TO_TOPIC = "244";
|
|||
const QString FORUM_POST_TEMPLATE = "<img src='%1'/><p>%2</p>";
|
||||
const QString SHARE_DEFAULT_ERROR = "The server isn't responding. Please try again in a few minutes.";
|
||||
const QString SUCCESS_LABEL_TEMPLATE = "Success!!! Go check out your image ...<br/><a style='color:#333;text-decoration:none' href='%1'>%1</a>";
|
||||
const QString SHARE_BUTTON_STYLE = "border-width:0;border-radius:9px;border-radius:9px;font-family:Arial;font-size:18px;"
|
||||
"font-weight:100;color:#FFFFFF;width: 120px;height: 50px;";
|
||||
const QString SHARE_BUTTON_ENABLED_STYLE = "background-color: #333;";
|
||||
const QString SHARE_BUTTON_DISABLED_STYLE = "background-color: #999;";
|
||||
|
||||
Q_DECLARE_METATYPE(QNetworkAccessManager::Operation)
|
||||
|
||||
|
@ -73,6 +77,10 @@ SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) :
|
|||
}
|
||||
|
||||
void SnapshotShareDialog::accept() {
|
||||
// prevent multiple clicks on share button
|
||||
_ui.shareButton->setEnabled(false);
|
||||
// gray out share button
|
||||
_ui.shareButton->setStyleSheet(SHARE_BUTTON_STYLE + SHARE_BUTTON_DISABLED_STYLE);
|
||||
uploadSnapshot();
|
||||
}
|
||||
|
||||
|
@ -179,6 +187,8 @@ void SnapshotShareDialog::postRequestFinished() {
|
|||
}
|
||||
}
|
||||
QMessageBox::warning(this, "", errorMessage);
|
||||
_ui.shareButton->setEnabled(true);
|
||||
_ui.shareButton->setStyleSheet(SHARE_BUTTON_STYLE + SHARE_BUTTON_ENABLED_STYLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,6 +202,8 @@ void SnapshotShareDialog::uploadRequestFinished() {
|
|||
sendForumPost(responseObject["url"].toString());
|
||||
} else {
|
||||
QMessageBox::warning(this, "", SHARE_DEFAULT_ERROR);
|
||||
_ui.shareButton->setEnabled(true);
|
||||
_ui.shareButton->setStyleSheet(SHARE_BUTTON_STYLE + SHARE_BUTTON_ENABLED_STYLE);
|
||||
}
|
||||
|
||||
delete requestReply;
|
||||
|
|
|
@ -22,16 +22,26 @@ Overlays::Overlays() : _nextOverlayID(1) {
|
|||
}
|
||||
|
||||
Overlays::~Overlays() {
|
||||
QMap<unsigned int, Overlay*>::iterator it;
|
||||
for (it = _overlays2D.begin(); it != _overlays2D.end(); ++it) {
|
||||
delete _overlays2D.take(it.key());
|
||||
|
||||
{
|
||||
QWriteLocker lock(&_lock);
|
||||
foreach(Overlay* thisOverlay, _overlays2D) {
|
||||
delete thisOverlay;
|
||||
}
|
||||
_overlays2D.clear();
|
||||
foreach(Overlay* thisOverlay, _overlays3D) {
|
||||
delete thisOverlay;
|
||||
}
|
||||
_overlays3D.clear();
|
||||
}
|
||||
for (it = _overlays3D.begin(); it != _overlays3D.end(); ++it) {
|
||||
delete _overlays3D.take(it.key());
|
||||
}
|
||||
while (!_overlaysToDelete.isEmpty()) {
|
||||
delete _overlaysToDelete.takeLast();
|
||||
|
||||
if (!_overlaysToDelete.isEmpty()) {
|
||||
QWriteLocker lock(&_deleteLock);
|
||||
do {
|
||||
delete _overlaysToDelete.takeLast();
|
||||
} while (!_overlaysToDelete.isEmpty());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Overlays::init(QGLWidget* parent) {
|
||||
|
|
|
@ -128,19 +128,43 @@ QPushButton:pressed {
|
|||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="usersWidget" native="true">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">#usersWidget {
|
||||
margin-right: 20px;
|
||||
}</string>
|
||||
<widget class="QWidget" name="usersArea" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="usersWidget" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="messagesScrollArea">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">margin-top: 12px;</string>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
|
|
|
@ -53,23 +53,27 @@ typedef unsigned long long quint64;
|
|||
#include "HandData.h"
|
||||
|
||||
// avatar motion behaviors
|
||||
const quint32 AVATAR_MOTION_MOTOR_ENABLED = 1U << 0;
|
||||
const quint32 AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED = 1U << 1;
|
||||
const quint32 AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME = 1U << 2;
|
||||
const quint32 AVATAR_MOTION_MOTOR_COLLISION_SURFACE_ONLY = 1U << 3;
|
||||
const quint32 AVATAR_MOTION_MOTOR_ENABLED = 1U << 0;
|
||||
const quint32 AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED = 1U << 1;
|
||||
const quint32 AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME = 1U << 2;
|
||||
const quint32 AVATAR_MOTION_MOTOR_COLLISION_SURFACE_ONLY = 1U << 3;
|
||||
|
||||
const quint32 AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY = 1U << 4;
|
||||
const quint32 AVATAR_MOTION_OBEY_LOCAL_GRAVITY = 1U << 5;
|
||||
const quint32 AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY = 1U << 4;
|
||||
const quint32 AVATAR_MOTION_OBEY_LOCAL_GRAVITY = 1U << 5;
|
||||
|
||||
const quint32 AVATAR_MOTION_STAND_ON_NEARBY_FLOORS = 1U << 6;
|
||||
|
||||
const quint32 AVATAR_MOTION_DEFAULTS =
|
||||
AVATAR_MOTION_MOTOR_ENABLED |
|
||||
AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED |
|
||||
AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME;
|
||||
AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME |
|
||||
AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
|
||||
|
||||
// these bits will be expanded as features are exposed
|
||||
const quint32 AVATAR_MOTION_SCRIPTABLE_BITS =
|
||||
AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY |
|
||||
AVATAR_MOTION_OBEY_LOCAL_GRAVITY;
|
||||
AVATAR_MOTION_OBEY_LOCAL_GRAVITY |
|
||||
AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
|
||||
|
||||
|
||||
// First bitset
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
const char* HTTPConnection::StatusCode200 = "200 OK";
|
||||
const char* HTTPConnection::StatusCode301 = "301 Moved Permanently";
|
||||
const char* HTTPConnection::StatusCode302 = "302 Found";
|
||||
const char* HTTPConnection::StatusCode400 = "400 Bad Request";
|
||||
const char* HTTPConnection::StatusCode404 = "404 Not Found";
|
||||
const char* HTTPConnection::DefaultContentType = "text/plain; charset=ISO-8859-1";
|
||||
|
|
|
@ -44,6 +44,7 @@ class HTTPConnection : public QObject {
|
|||
public:
|
||||
static const char* StatusCode200;
|
||||
static const char* StatusCode301;
|
||||
static const char* StatusCode302;
|
||||
static const char* StatusCode400;
|
||||
static const char* StatusCode404;
|
||||
static const char* DefaultContentType;
|
||||
|
|
|
@ -85,12 +85,13 @@ ModelItem::ModelItem(const ModelItemID& modelItemID, const ModelItemProperties&
|
|||
_shouldDie = false;
|
||||
_modelURL = MODEL_DEFAULT_MODEL_URL;
|
||||
_modelRotation = MODEL_DEFAULT_MODEL_ROTATION;
|
||||
|
||||
|
||||
// animation related
|
||||
_animationURL = MODEL_DEFAULT_ANIMATION_URL;
|
||||
_animationIsPlaying = false;
|
||||
_animationFrameIndex = 0.0f;
|
||||
_animationFPS = MODEL_DEFAULT_ANIMATION_FPS;
|
||||
_glowLevel = 0.0f;
|
||||
|
||||
_jointMappingCompleted = false;
|
||||
_lastAnimated = now;
|
||||
|
@ -125,6 +126,7 @@ void ModelItem::init(glm::vec3 position, float radius, rgbColor color, uint32_t
|
|||
_animationIsPlaying = false;
|
||||
_animationFrameIndex = 0.0f;
|
||||
_animationFPS = MODEL_DEFAULT_ANIMATION_FPS;
|
||||
_glowLevel = 0.0f;
|
||||
_jointMappingCompleted = false;
|
||||
_lastAnimated = now;
|
||||
}
|
||||
|
@ -802,6 +804,7 @@ ModelItemProperties::ModelItemProperties() :
|
|||
_animationIsPlaying(false),
|
||||
_animationFrameIndex(0.0),
|
||||
_animationFPS(MODEL_DEFAULT_ANIMATION_FPS),
|
||||
_glowLevel(0.0f),
|
||||
|
||||
_id(UNKNOWN_MODEL_ID),
|
||||
_idSet(false),
|
||||
|
@ -817,6 +820,7 @@ ModelItemProperties::ModelItemProperties() :
|
|||
_animationIsPlayingChanged(false),
|
||||
_animationFrameIndexChanged(false),
|
||||
_animationFPSChanged(false),
|
||||
_glowLevelChanged(false),
|
||||
_defaultSettings(true)
|
||||
{
|
||||
}
|
||||
|
@ -890,6 +894,7 @@ QScriptValue ModelItemProperties::copyToScriptValue(QScriptEngine* engine) const
|
|||
properties.setProperty("animationIsPlaying", _animationIsPlaying);
|
||||
properties.setProperty("animationFrameIndex", _animationFrameIndex);
|
||||
properties.setProperty("animationFPS", _animationFPS);
|
||||
properties.setProperty("glowLevel", _glowLevel);
|
||||
|
||||
if (_idSet) {
|
||||
properties.setProperty("id", _id);
|
||||
|
@ -1015,7 +1020,7 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) {
|
|||
_animationFrameIndexChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QScriptValue animationFPS = object.property("animationFPS");
|
||||
if (animationFPS.isValid()) {
|
||||
float newFPS;
|
||||
|
@ -1025,6 +1030,16 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) {
|
|||
_animationFPSChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue glowLevel = object.property("glowLevel");
|
||||
if (glowLevel.isValid()) {
|
||||
float newGlowLevel;
|
||||
newGlowLevel = glowLevel.toVariant().toFloat();
|
||||
if (_defaultSettings || newGlowLevel != _glowLevel) {
|
||||
_glowLevel = newGlowLevel;
|
||||
_glowLevelChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
_lastEdited = usecTimestampNow();
|
||||
}
|
||||
|
@ -1075,11 +1090,16 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const {
|
|||
modelItem.setAnimationFrameIndex(_animationFrameIndex);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
|
||||
if (_animationFPSChanged) {
|
||||
modelItem.setAnimationFPS(_animationFPS);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (_glowLevelChanged) {
|
||||
modelItem.setGlowLevel(_glowLevel);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (somethingChanged) {
|
||||
bool wantDebug = false;
|
||||
|
@ -1104,6 +1124,7 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) {
|
|||
_animationIsPlaying = modelItem.getAnimationIsPlaying();
|
||||
_animationFrameIndex = modelItem.getAnimationFrameIndex();
|
||||
_animationFPS = modelItem.getAnimationFPS();
|
||||
_glowLevel = modelItem.getGlowLevel();
|
||||
|
||||
_id = modelItem.getID();
|
||||
_idSet = true;
|
||||
|
@ -1119,6 +1140,7 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) {
|
|||
_animationIsPlayingChanged = false;
|
||||
_animationFrameIndexChanged = false;
|
||||
_animationFPSChanged = false;
|
||||
_glowLevelChanged = false;
|
||||
_defaultSettings = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -83,6 +83,7 @@ public:
|
|||
float getAnimationFrameIndex() const { return _animationFrameIndex; }
|
||||
bool getAnimationIsPlaying() const { return _animationIsPlaying; }
|
||||
float getAnimationFPS() const { return _animationFPS; }
|
||||
float getGlowLevel() const { return _glowLevel; }
|
||||
|
||||
quint64 getLastEdited() const { return _lastEdited; }
|
||||
uint16_t getChangedBits() const;
|
||||
|
@ -100,6 +101,7 @@ public:
|
|||
void setAnimationFrameIndex(float value) { _animationFrameIndex = value; _animationFrameIndexChanged = true; }
|
||||
void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; _animationIsPlayingChanged = true; }
|
||||
void setAnimationFPS(float value) { _animationFPS = value; _animationFPSChanged = true; }
|
||||
void setGlowLevel(float value) { _glowLevel = value; _glowLevelChanged = true; }
|
||||
|
||||
/// used by ModelScriptingInterface to return ModelItemProperties for unknown models
|
||||
void setIsUnknownID() { _id = UNKNOWN_MODEL_ID; _idSet = true; }
|
||||
|
@ -119,6 +121,7 @@ private:
|
|||
bool _animationIsPlaying;
|
||||
float _animationFrameIndex;
|
||||
float _animationFPS;
|
||||
float _glowLevel;
|
||||
|
||||
uint32_t _id;
|
||||
bool _idSet;
|
||||
|
@ -135,6 +138,7 @@ private:
|
|||
bool _animationIsPlayingChanged;
|
||||
bool _animationFrameIndexChanged;
|
||||
bool _animationFPSChanged;
|
||||
bool _glowLevelChanged;
|
||||
bool _defaultSettings;
|
||||
};
|
||||
Q_DECLARE_METATYPE(ModelItemProperties);
|
||||
|
@ -206,6 +210,7 @@ public:
|
|||
const glm::quat& getModelRotation() const { return _modelRotation; }
|
||||
bool hasAnimation() const { return !_animationURL.isEmpty(); }
|
||||
const QString& getAnimationURL() const { return _animationURL; }
|
||||
float getGlowLevel() const { return _glowLevel; }
|
||||
|
||||
ModelItemID getModelItemID() const { return ModelItemID(getID(), getCreatorTokenID(), getID() != UNKNOWN_MODEL_ID); }
|
||||
ModelItemProperties getProperties() const;
|
||||
|
@ -248,6 +253,7 @@ public:
|
|||
void setAnimationFrameIndex(float value) { _animationFrameIndex = value; }
|
||||
void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; }
|
||||
void setAnimationFPS(float value) { _animationFPS = value; }
|
||||
void setGlowLevel(float glowLevel) { _glowLevel = glowLevel; }
|
||||
|
||||
void setProperties(const ModelItemProperties& properties);
|
||||
|
||||
|
@ -293,6 +299,8 @@ protected:
|
|||
// model related items
|
||||
QString _modelURL;
|
||||
glm::quat _modelRotation;
|
||||
|
||||
float _glowLevel;
|
||||
|
||||
uint32_t _creatorTokenID;
|
||||
bool _newlyCreated;
|
||||
|
|
|
@ -83,7 +83,7 @@ void DataServerAccountInfo::setDiscourseApiKey(const QString& discourseApiKey) {
|
|||
}
|
||||
}
|
||||
|
||||
void DataServerAccountInfo::setBalance(quint64 balance) {
|
||||
void DataServerAccountInfo::setBalance(qint64 balance) {
|
||||
if (!_hasBalance || _balance != balance) {
|
||||
_balance = balance;
|
||||
_hasBalance = true;
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include "OAuthAccessToken.h"
|
||||
|
||||
const float SATOSHIS_PER_CREDIT = 100000000.0f;
|
||||
|
||||
class DataServerAccountInfo : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -35,8 +37,8 @@ public:
|
|||
const QString& getDiscourseApiKey() const { return _discourseApiKey; }
|
||||
void setDiscourseApiKey(const QString& discourseApiKey);
|
||||
|
||||
quint64 getBalance() const { return _balance; }
|
||||
void setBalance(quint64 balance);
|
||||
qint64 getBalance() const { return _balance; }
|
||||
void setBalance(qint64 balance);
|
||||
bool hasBalance() const { return _hasBalance; }
|
||||
void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; }
|
||||
Q_INVOKABLE void setBalanceFromJSON(const QJsonObject& jsonObject);
|
||||
|
@ -52,7 +54,7 @@ private:
|
|||
QString _username;
|
||||
QString _xmppPassword;
|
||||
QString _discourseApiKey;
|
||||
quint64 _balance;
|
||||
qint64 _balance;
|
||||
bool _hasBalance;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue