mirror of
https://github.com/overte-org/overte.git
synced 2025-04-23 00:54:08 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into animationGroupSettings
This commit is contained in:
commit
be79101a93
42 changed files with 964 additions and 319 deletions
assignment-client/src/entities
domain-server/src
examples
interface/src
libraries
animation/src
entities-renderer/src
entities/src
gpu/src/gpu
input-plugins/src/input-plugins
model-networking
octree/src
plugins/src/plugins
procedural/src/procedural
shared/src
tests/ui/src
unpublishedScripts
|
@ -26,28 +26,28 @@ public:
|
|||
~EntityServer();
|
||||
|
||||
// Subclasses must implement these methods
|
||||
virtual OctreeQueryNode* createOctreeQueryNode();
|
||||
virtual char getMyNodeType() const { return NodeType::EntityServer; }
|
||||
virtual PacketType getMyQueryMessageType() const { return PacketType::EntityQuery; }
|
||||
virtual const char* getMyServerName() const { return MODEL_SERVER_NAME; }
|
||||
virtual const char* getMyLoggingServerTargetName() const { return MODEL_SERVER_LOGGING_TARGET_NAME; }
|
||||
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_MODELS_PERSIST_FILE; }
|
||||
virtual PacketType getMyEditNackType() const { return PacketType::EntityEditNack; }
|
||||
virtual QString getMyDomainSettingsKey() const { return QString("entity_server_settings"); }
|
||||
virtual OctreeQueryNode* createOctreeQueryNode() override ;
|
||||
virtual char getMyNodeType() const override { return NodeType::EntityServer; }
|
||||
virtual PacketType getMyQueryMessageType() const override { return PacketType::EntityQuery; }
|
||||
virtual const char* getMyServerName() const override { return MODEL_SERVER_NAME; }
|
||||
virtual const char* getMyLoggingServerTargetName() const override { return MODEL_SERVER_LOGGING_TARGET_NAME; }
|
||||
virtual const char* getMyDefaultPersistFilename() const override { return LOCAL_MODELS_PERSIST_FILE; }
|
||||
virtual PacketType getMyEditNackType() const override { return PacketType::EntityEditNack; }
|
||||
virtual QString getMyDomainSettingsKey() const override { return QString("entity_server_settings"); }
|
||||
|
||||
// subclass may implement these method
|
||||
virtual void beforeRun();
|
||||
virtual bool hasSpecialPacketsToSend(const SharedNodePointer& node);
|
||||
virtual int sendSpecialPackets(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent);
|
||||
virtual void beforeRun() override;
|
||||
virtual bool hasSpecialPacketsToSend(const SharedNodePointer& node) override;
|
||||
virtual int sendSpecialPackets(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) override;
|
||||
|
||||
virtual void entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode);
|
||||
virtual void entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) override;
|
||||
virtual bool readAdditionalConfiguration(const QJsonObject& settingsSectionObject) override;
|
||||
|
||||
public slots:
|
||||
void pruneDeletedEntities();
|
||||
|
||||
protected:
|
||||
virtual OctreePointer createTree();
|
||||
virtual OctreePointer createTree() override;
|
||||
|
||||
private slots:
|
||||
void handleEntityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
|
|
|
@ -133,33 +133,14 @@ bool DomainServer::optionallyReadX509KeyAndCertificate() {
|
|||
QString keyPath = _settingsManager.getSettingsMap().value(X509_PRIVATE_KEY_OPTION).toString();
|
||||
|
||||
if (!certPath.isEmpty() && !keyPath.isEmpty()) {
|
||||
// the user wants to use DTLS to encrypt communication with nodes
|
||||
// the user wants to use the following cert and key for HTTPS
|
||||
// this is used for Oauth callbacks when authorizing users against a data server
|
||||
// let's make sure we can load the key and certificate
|
||||
// _x509Credentials = new gnutls_certificate_credentials_t;
|
||||
// gnutls_certificate_allocate_credentials(_x509Credentials);
|
||||
|
||||
QString keyPassphraseString = QProcessEnvironment::systemEnvironment().value(X509_KEY_PASSPHRASE_ENV);
|
||||
|
||||
qDebug() << "Reading certificate file at" << certPath << "for DTLS.";
|
||||
qDebug() << "Reading key file at" << keyPath << "for DTLS.";
|
||||
|
||||
// int gnutlsReturn = gnutls_certificate_set_x509_key_file2(*_x509Credentials,
|
||||
// certPath.toLocal8Bit().constData(),
|
||||
// keyPath.toLocal8Bit().constData(),
|
||||
// GNUTLS_X509_FMT_PEM,
|
||||
// keyPassphraseString.toLocal8Bit().constData(),
|
||||
// 0);
|
||||
//
|
||||
// if (gnutlsReturn < 0) {
|
||||
// qDebug() << "Unable to load certificate or key file." << "Error" << gnutlsReturn << "- domain-server will now quit.";
|
||||
// QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// qDebug() << "Successfully read certificate and private key.";
|
||||
|
||||
// we need to also pass this certificate and private key to the HTTPS manager
|
||||
// this is used for Oauth callbacks when authorizing users against a data server
|
||||
qDebug() << "Reading certificate file at" << certPath << "for HTTPS.";
|
||||
qDebug() << "Reading key file at" << keyPath << "for HTTPS.";
|
||||
|
||||
QFile certFile(certPath);
|
||||
certFile.open(QIODevice::ReadOnly);
|
||||
|
|
|
@ -79,23 +79,26 @@ var STATE_NEAR_GRABBING_NON_COLLIDING = 6;
|
|||
var STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING = 7;
|
||||
var STATE_RELEASE = 8;
|
||||
|
||||
var GRAB_USER_DATA_KEY = "grabKey";
|
||||
var GRABBABLE_DATA_KEY = "grabbableKey";
|
||||
var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js
|
||||
var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js
|
||||
|
||||
function getTag() {
|
||||
return "grab-" + MyAvatar.sessionUUID;
|
||||
}
|
||||
|
||||
function entityIsGrabbedByOther(entityID) {
|
||||
// by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*.
|
||||
var actionIDs = Entities.getActionIDs(entityID);
|
||||
for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) {
|
||||
var actionID = actionIDs[actionIndex];
|
||||
var actionArguments = Entities.getActionArguments(entityID, actionID);
|
||||
var tag = actionArguments["tag"];
|
||||
if (tag == getTag()) {
|
||||
// we see a grab-*uuid* shaped tag, but it's our tag, so that's okay.
|
||||
continue;
|
||||
}
|
||||
if (tag.slice(0, 5) == "grab-") {
|
||||
// we see a grab-*uuid* shaped tag and it's not ours, so someone else is grabbing it.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -164,6 +167,12 @@ function MyController(hand, triggerAction) {
|
|||
}
|
||||
};
|
||||
|
||||
this.setState = function(newState) {
|
||||
// print("STATE: " + this.state + " --> " + newState);
|
||||
this.state = newState;
|
||||
}
|
||||
|
||||
|
||||
this.lineOn = function(closePoint, farPoint, color) {
|
||||
// draw a line
|
||||
if (this.pointer === null) {
|
||||
|
@ -217,14 +226,14 @@ function MyController(hand, triggerAction) {
|
|||
|
||||
this.off = function() {
|
||||
if (this.triggerSmoothedSqueezed()) {
|
||||
this.state = STATE_SEARCHING;
|
||||
this.setState(STATE_SEARCHING);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.search = function() {
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
this.state = STATE_RELEASE;
|
||||
this.setState(STATE_RELEASE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -235,6 +244,8 @@ function MyController(hand, triggerAction) {
|
|||
direction: Quat.getUp(this.getHandRotation())
|
||||
};
|
||||
|
||||
this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
|
||||
|
||||
var defaultGrabbableData = {
|
||||
grabbable: true
|
||||
};
|
||||
|
@ -250,20 +261,20 @@ function MyController(hand, triggerAction) {
|
|||
|
||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, defaultGrabbableData);
|
||||
if (grabbableData.grabbable === false) {
|
||||
this.grabbedEntity = null;
|
||||
return;
|
||||
}
|
||||
if (intersectionDistance < NEAR_PICK_MAX_DISTANCE) {
|
||||
// the hand is very close to the intersected object. go into close-grabbing mode.
|
||||
this.state = STATE_NEAR_GRABBING;
|
||||
|
||||
this.setState(STATE_NEAR_GRABBING);
|
||||
} else {
|
||||
// don't allow two people to distance grab the same object
|
||||
if (entityIsGrabbedByOther(intersection.entityID)) {
|
||||
// don't allow two people to distance grab the same object
|
||||
return;
|
||||
this.grabbedEntity = null;
|
||||
} else {
|
||||
// the hand is far from the intersected object. go into distance-holding mode
|
||||
this.setState(STATE_DISTANCE_HOLDING);
|
||||
}
|
||||
// the hand is far from the intersected object. go into distance-holding mode
|
||||
this.state = STATE_DISTANCE_HOLDING;
|
||||
this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
|
||||
}
|
||||
} else {
|
||||
// forward ray test failed, try sphere test.
|
||||
|
@ -287,22 +298,22 @@ function MyController(hand, triggerAction) {
|
|||
}
|
||||
}
|
||||
if (this.grabbedEntity === null) {
|
||||
this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
|
||||
// this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
|
||||
} else if (props.locked === 0 && props.collisionsWillMove === 1) {
|
||||
this.state = STATE_NEAR_GRABBING;
|
||||
this.setState(STATE_NEAR_GRABBING);
|
||||
} else if (props.collisionsWillMove === 0) {
|
||||
// We have grabbed a non-physical object, so we want to trigger a non-colliding event as opposed to a grab event
|
||||
this.state = STATE_NEAR_GRABBING_NON_COLLIDING;
|
||||
this.setState(STATE_NEAR_GRABBING_NON_COLLIDING);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.distanceHolding = function() {
|
||||
|
||||
var handControllerPosition = Controller.getSpatialControlPosition(this.palm);
|
||||
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]);
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation",
|
||||
"gravity", "ignoreForCollisions"]);
|
||||
|
||||
// add the action and initialize some variables
|
||||
this.currentObjectPosition = grabbedProperties.position;
|
||||
|
@ -325,8 +336,8 @@ function MyController(hand, triggerAction) {
|
|||
}
|
||||
|
||||
if (this.actionID !== null) {
|
||||
this.state = STATE_CONTINUE_DISTANCE_HOLDING;
|
||||
this.activateEntity(this.grabbedEntity);
|
||||
this.setState(STATE_CONTINUE_DISTANCE_HOLDING);
|
||||
this.activateEntity(this.grabbedEntity, grabbedProperties);
|
||||
if (this.hand === RIGHT_HAND) {
|
||||
Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
|
||||
} else {
|
||||
|
@ -342,7 +353,7 @@ function MyController(hand, triggerAction) {
|
|||
|
||||
this.continueDistanceHolding = function() {
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
this.state = STATE_RELEASE;
|
||||
this.setState(STATE_RELEASE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -400,7 +411,7 @@ function MyController(hand, triggerAction) {
|
|||
var now = Date.now();
|
||||
var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds
|
||||
this.computeReleaseVelocity(deltaPosition, deltaTime, false);
|
||||
|
||||
|
||||
this.currentObjectPosition = newObjectPosition;
|
||||
this.currentObjectTime = now;
|
||||
|
||||
|
@ -423,15 +434,15 @@ function MyController(hand, triggerAction) {
|
|||
this.nearGrabbing = function() {
|
||||
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
this.state = STATE_RELEASE;
|
||||
this.setState(STATE_RELEASE);
|
||||
return;
|
||||
}
|
||||
|
||||
this.lineOff();
|
||||
|
||||
this.activateEntity(this.grabbedEntity);
|
||||
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]);
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity,
|
||||
["position", "rotation", "gravity", "ignoreForCollisions"]);
|
||||
this.activateEntity(this.grabbedEntity, grabbedProperties);
|
||||
|
||||
var handRotation = this.getHandRotation();
|
||||
var handPosition = this.getHandPosition();
|
||||
|
@ -444,7 +455,7 @@ function MyController(hand, triggerAction) {
|
|||
var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset);
|
||||
|
||||
this.actionID = NULL_ACTION_ID;
|
||||
this.actionID = Entities.addAction("hold", this.grabbedEntity, {
|
||||
this.actionID = Entities.addAction("kinematic-hold", this.grabbedEntity, {
|
||||
hand: this.hand === RIGHT_HAND ? "right" : "left",
|
||||
timeScale: NEAR_GRABBING_ACTION_TIMEFRAME,
|
||||
relativePosition: offsetPosition,
|
||||
|
@ -454,7 +465,7 @@ function MyController(hand, triggerAction) {
|
|||
if (this.actionID === NULL_ACTION_ID) {
|
||||
this.actionID = null;
|
||||
} else {
|
||||
this.state = STATE_CONTINUE_NEAR_GRABBING;
|
||||
this.setState(STATE_CONTINUE_NEAR_GRABBING);
|
||||
if (this.hand === RIGHT_HAND) {
|
||||
Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
|
||||
} else {
|
||||
|
@ -471,7 +482,7 @@ function MyController(hand, triggerAction) {
|
|||
|
||||
this.continueNearGrabbing = function() {
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
this.state = STATE_RELEASE;
|
||||
this.setState(STATE_RELEASE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -498,7 +509,7 @@ function MyController(hand, triggerAction) {
|
|||
|
||||
this.nearGrabbingNonColliding = function() {
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
this.state = STATE_RELEASE;
|
||||
this.setState(STATE_RELEASE);
|
||||
return;
|
||||
}
|
||||
if (this.hand === RIGHT_HAND) {
|
||||
|
@ -507,12 +518,12 @@ function MyController(hand, triggerAction) {
|
|||
Entities.callEntityMethod(this.grabbedEntity, "setLeftHand");
|
||||
}
|
||||
Entities.callEntityMethod(this.grabbedEntity, "startNearGrabNonColliding");
|
||||
this.state = STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING;
|
||||
this.setState(STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING);
|
||||
};
|
||||
|
||||
this.continueNearGrabbingNonColliding = function() {
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
this.state = STATE_RELEASE;
|
||||
this.setState(STATE_RELEASE);
|
||||
return;
|
||||
}
|
||||
Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabbingNonColliding");
|
||||
|
@ -623,27 +634,42 @@ function MyController(hand, triggerAction) {
|
|||
this.grabbedVelocity = ZERO_VEC;
|
||||
this.grabbedEntity = null;
|
||||
this.actionID = null;
|
||||
this.state = STATE_OFF;
|
||||
this.setState(STATE_OFF);
|
||||
};
|
||||
|
||||
this.cleanup = function() {
|
||||
this.release();
|
||||
};
|
||||
|
||||
this.activateEntity = function() {
|
||||
var data = {
|
||||
activated: true,
|
||||
avatarId: MyAvatar.sessionUUID
|
||||
};
|
||||
setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data);
|
||||
this.activateEntity = function(entityID, grabbedProperties) {
|
||||
var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {});
|
||||
data["activated"] = true;
|
||||
data["avatarId"] = MyAvatar.sessionUUID;
|
||||
data["refCount"] = data["refCount"] ? data["refCount"] + 1 : 1;
|
||||
// zero gravity and set ignoreForCollisions to true, but in a way that lets us put them back, after all grabs are done
|
||||
if (data["refCount"] == 1) {
|
||||
data["gravity"] = grabbedProperties.gravity;
|
||||
data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions;
|
||||
Entities.editEntity(entityID, {gravity: {x:0, y:0, z:0}, ignoreForCollisions: true});
|
||||
}
|
||||
setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data);
|
||||
};
|
||||
|
||||
this.deactivateEntity = function() {
|
||||
var data = {
|
||||
activated: false,
|
||||
avatarId: null
|
||||
};
|
||||
setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data);
|
||||
this.deactivateEntity = function(entityID) {
|
||||
var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {});
|
||||
if (data && data["refCount"]) {
|
||||
data["refCount"] = data["refCount"] - 1;
|
||||
if (data["refCount"] < 1) {
|
||||
Entities.editEntity(entityID, {
|
||||
gravity: data["gravity"],
|
||||
ignoreForCollisions: data["ignoreForCollisions"]
|
||||
});
|
||||
data = null;
|
||||
}
|
||||
} else {
|
||||
data = null;
|
||||
}
|
||||
setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,8 @@ var IDENTITY_QUAT = {
|
|||
z: 0,
|
||||
w: 0
|
||||
};
|
||||
var GRABBABLE_DATA_KEY = "grabbableKey";
|
||||
var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with handControllerGrab.js
|
||||
var GRAB_USER_DATA_KEY = "grabKey"; // shared with handControllerGrab.js
|
||||
|
||||
var defaultGrabbableData = {
|
||||
grabbable: true
|
||||
|
@ -45,26 +46,25 @@ var IDENTITY_QUAT = {
|
|||
z: 0,
|
||||
w: 0
|
||||
};
|
||||
var ACTION_LIFETIME = 120; // 2 minutes
|
||||
var ACTION_LIFETIME = 10; // seconds
|
||||
|
||||
function getTag() {
|
||||
return "grab-" + MyAvatar.sessionUUID;
|
||||
}
|
||||
|
||||
function entityIsGrabbedByOther(entityID) {
|
||||
// by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*.
|
||||
var actionIDs = Entities.getActionIDs(entityID);
|
||||
var actionIndex;
|
||||
var actionID;
|
||||
var actionArguments;
|
||||
var tag;
|
||||
for (actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) {
|
||||
actionID = actionIDs[actionIndex];
|
||||
actionArguments = Entities.getActionArguments(entityID, actionID);
|
||||
tag = actionArguments.tag;
|
||||
if (tag === getTag()) {
|
||||
for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) {
|
||||
var actionID = actionIDs[actionIndex];
|
||||
var actionArguments = Entities.getActionArguments(entityID, actionID);
|
||||
var tag = actionArguments["tag"];
|
||||
if (tag == getTag()) {
|
||||
// we see a grab-*uuid* shaped tag, but it's our tag, so that's okay.
|
||||
continue;
|
||||
}
|
||||
if (tag.slice(0, 5) === "grab-") {
|
||||
if (tag.slice(0, 5) == "grab-") {
|
||||
// we see a grab-*uuid* shaped tag and it's not ours, so someone else is grabbing it.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -248,7 +248,6 @@ Grabber = function() {
|
|||
this.currentPosition = ZERO_VEC3;
|
||||
this.planeNormal = ZERO_VEC3;
|
||||
|
||||
this.originalGravity = ZERO_VEC3;
|
||||
// maxDistance is a function of the size of the object.
|
||||
this.maxDistance;
|
||||
|
||||
|
@ -347,14 +346,11 @@ Grabber.prototype.pressEvent = function(event) {
|
|||
return;
|
||||
}
|
||||
|
||||
Entities.editEntity(clickedEntity, {
|
||||
gravity: ZERO_VEC3
|
||||
});
|
||||
this.activateEntity(clickedEntity, entityProperties);
|
||||
this.isGrabbing = true;
|
||||
|
||||
this.entityID = clickedEntity;
|
||||
this.currentPosition = entityProperties.position;
|
||||
this.originalGravity = entityProperties.gravity;
|
||||
this.targetPosition = {
|
||||
x: this.startPosition.x,
|
||||
y: this.startPosition.y,
|
||||
|
@ -380,12 +376,7 @@ Grabber.prototype.pressEvent = function(event) {
|
|||
|
||||
Grabber.prototype.releaseEvent = function() {
|
||||
if (this.isGrabbing) {
|
||||
if (Vec3.length(this.originalGravity) != 0) {
|
||||
Entities.editEntity(this.entityID, {
|
||||
gravity: this.originalGravity
|
||||
});
|
||||
}
|
||||
|
||||
this.deactivateEntity(this.entityID);
|
||||
this.isGrabbing = false
|
||||
Entities.deleteAction(this.entityID, this.actionID);
|
||||
this.actionID = null;
|
||||
|
@ -504,6 +495,39 @@ Grabber.prototype.keyPressEvent = function(event) {
|
|||
this.computeNewGrabPlane();
|
||||
}
|
||||
|
||||
Grabber.prototype.activateEntity = function(entityID, grabbedProperties) {
|
||||
var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {});
|
||||
data["activated"] = true;
|
||||
data["avatarId"] = MyAvatar.sessionUUID;
|
||||
data["refCount"] = data["refCount"] ? data["refCount"] + 1 : 1;
|
||||
// zero gravity and set ignoreForCollisions to true, but in a way that lets us put them back, after all grabs are done
|
||||
if (data["refCount"] == 1) {
|
||||
data["gravity"] = grabbedProperties.gravity;
|
||||
data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions;
|
||||
Entities.editEntity(entityID, {gravity: {x:0, y:0, z:0}, ignoreForCollisions: true});
|
||||
}
|
||||
setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data);
|
||||
};
|
||||
|
||||
Grabber.prototype.deactivateEntity = function(entityID) {
|
||||
var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {});
|
||||
if (data && data["refCount"]) {
|
||||
data["refCount"] = data["refCount"] - 1;
|
||||
if (data["refCount"] < 1) {
|
||||
Entities.editEntity(entityID, {
|
||||
gravity: data["gravity"],
|
||||
ignoreForCollisions: data["ignoreForCollisions"]
|
||||
});
|
||||
data = null;
|
||||
}
|
||||
} else {
|
||||
data = null;
|
||||
}
|
||||
setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data);
|
||||
};
|
||||
|
||||
|
||||
|
||||
var grabber = new Grabber();
|
||||
|
||||
function pressEvent(event) {
|
||||
|
@ -530,4 +554,4 @@ Controller.mousePressEvent.connect(pressEvent);
|
|||
Controller.mouseMoveEvent.connect(moveEvent);
|
||||
Controller.mouseReleaseEvent.connect(releaseEvent);
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
|
|
|
@ -79,13 +79,21 @@ getEntityUserData = function(id) {
|
|||
// Non-destructively modify the user data of an entity.
|
||||
setEntityCustomData = function(customKey, id, data) {
|
||||
var userData = getEntityUserData(id);
|
||||
userData[customKey] = data;
|
||||
if (data == null) {
|
||||
delete userData[customKey];
|
||||
} else {
|
||||
userData[customKey] = data;
|
||||
}
|
||||
setEntityUserData(id, userData);
|
||||
}
|
||||
|
||||
getEntityCustomData = function(customKey, id, defaultValue) {
|
||||
var userData = getEntityUserData(id);
|
||||
return userData[customKey] ? userData[customKey] : defaultValue;
|
||||
if (undefined != userData[customKey]) {
|
||||
return userData[customKey];
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
mergeObjects = function(proto, custom) {
|
||||
|
|
|
@ -13,8 +13,8 @@ Script.include("../../utilities.js");
|
|||
|
||||
var scriptURL = Script.resolvePath('pingPongGun.js');
|
||||
|
||||
var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'
|
||||
var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj';
|
||||
var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx?123'
|
||||
var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj?123';
|
||||
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {
|
||||
x: 0,
|
||||
|
@ -25,14 +25,15 @@ var center = Vec3.sum(Vec3.sum(MyAvatar.position, {
|
|||
var pingPongGun = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: MODEL_URL,
|
||||
shapeType: 'compound',
|
||||
compoundShapeURL: COLLISION_HULL_URL,
|
||||
shapeType:'box',
|
||||
// shapeType: 'compound',
|
||||
// compoundShapeURL: COLLISION_HULL_URL,
|
||||
script: scriptURL,
|
||||
position: center,
|
||||
dimensions: {
|
||||
x:0.67,
|
||||
y: 0.14,
|
||||
z: 0.09
|
||||
x: 0.08,
|
||||
y: 0.21,
|
||||
z: 0.47
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
});
|
||||
|
@ -40,4 +41,4 @@ var pingPongGun = Entities.addEntity({
|
|||
function cleanUp() {
|
||||
Entities.deleteEntity(pingPongGun);
|
||||
}
|
||||
Script.scriptEnding.connect(cleanUp);
|
||||
Script.scriptEnding.connect(cleanUp);
|
|
@ -21,14 +21,14 @@
|
|||
|
||||
//if the trigger value goes below this value, reload the gun.
|
||||
var RELOAD_THRESHOLD = 0.95;
|
||||
var GUN_TIP_FWD_OFFSET = 0.45;
|
||||
var GUN_TIP_FWD_OFFSET =-0.35;
|
||||
var GUN_TIP_UP_OFFSET = 0.040;
|
||||
var GUN_FORCE = 15;
|
||||
var GUN_FORCE = 9;
|
||||
var BALL_RESTITUTION = 0.6;
|
||||
var BALL_LINEAR_DAMPING = 0.4;
|
||||
var BALL_GRAVITY = {
|
||||
x: 0,
|
||||
y: -9.8,
|
||||
y: -4.8,
|
||||
z: 0
|
||||
};
|
||||
|
||||
|
@ -36,14 +36,14 @@
|
|||
x: 0.04,
|
||||
y: 0.04,
|
||||
z: 0.04
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var BALL_COLOR = {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
}
|
||||
};
|
||||
|
||||
PingPongGun.prototype = {
|
||||
hand: null,
|
||||
|
@ -68,7 +68,6 @@
|
|||
},
|
||||
|
||||
continueNearGrab: function() {
|
||||
|
||||
if (this.whichHand === null) {
|
||||
//only set the active hand once -- if we always read the current hand, our 'holding' hand will get overwritten
|
||||
this.setWhichHand();
|
||||
|
@ -81,10 +80,14 @@
|
|||
},
|
||||
|
||||
releaseGrab: function() {
|
||||
var _t = this;
|
||||
this.canShootTimeout = Script.setTimeout(function() {
|
||||
_t.canShoot = false;
|
||||
}, 250)
|
||||
var _this = this;
|
||||
|
||||
if (this.whichHand === this.hand) {
|
||||
this.whichHand = null;
|
||||
this.canShootTimeout = Script.setTimeout(function() {
|
||||
_this.canShoot = false;
|
||||
}, 250);
|
||||
}
|
||||
},
|
||||
|
||||
checkTriggerPressure: function(gunHand) {
|
||||
|
@ -97,18 +100,20 @@
|
|||
if (this.triggerValue < RELOAD_THRESHOLD) {
|
||||
// print('RELOAD');
|
||||
this.canShoot = true;
|
||||
} else if (this.triggerValue >= RELOAD_THRESHOLD && this.canShoot === true) {
|
||||
} else if (this.triggerValue >= RELOAD_THRESHOLD && this.canShoot === true && this.hand === this.whichHand) {
|
||||
var gunProperties = Entities.getEntityProperties(this.entityID, ["position", "rotation"]);
|
||||
this.shootBall(gunProperties);
|
||||
this.canShoot = false;
|
||||
}
|
||||
|
||||
return;
|
||||
},
|
||||
|
||||
shootBall: function(gunProperties) {
|
||||
var forwardVec = Quat.getFront(Quat.multiply(gunProperties.rotation, Quat.fromPitchYawRollDegrees(0, -90, 0)));
|
||||
var forwardVec = Quat.getFront(Quat.multiply(gunProperties.rotation, Quat.fromPitchYawRollDegrees(0, 180, 0)));
|
||||
forwardVec = Vec3.normalize(forwardVec);
|
||||
forwardVec = Vec3.multiply(forwardVec, GUN_FORCE);
|
||||
|
||||
var properties = {
|
||||
type: 'Sphere',
|
||||
color: BALL_COLOR,
|
||||
|
@ -130,7 +135,7 @@
|
|||
|
||||
playSoundAtCurrentPosition: function(position) {
|
||||
var audioProperties = {
|
||||
volume: 0.1,
|
||||
volume: 0.2,
|
||||
position: position
|
||||
};
|
||||
|
||||
|
@ -139,12 +144,14 @@
|
|||
|
||||
getGunTipPosition: function(properties) {
|
||||
//the tip of the gun is going to be in a different place than the center, so we move in space relative to the model to find that position
|
||||
var frontVector = Quat.getRight(properties.rotation);
|
||||
var frontVector = Quat.getFront(properties.rotation);
|
||||
var frontOffset = Vec3.multiply(frontVector, GUN_TIP_FWD_OFFSET);
|
||||
var upVector = Quat.getRight(properties.rotation);
|
||||
var upVector = Quat.getUp(properties.rotation);
|
||||
var upOffset = Vec3.multiply(upVector, GUN_TIP_UP_OFFSET);
|
||||
|
||||
var gunTipPosition = Vec3.sum(properties.position, frontOffset);
|
||||
gunTipPosition = Vec3.sum(gunTipPosition, upOffset);
|
||||
|
||||
return gunTipPosition;
|
||||
},
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
//
|
||||
|
||||
|
||||
(function() {
|
||||
(function () {
|
||||
// Script.include("../libraries/utils.js");
|
||||
//Need absolute path for now, for testing before PR merge and s3 cloning. Will change post-merge
|
||||
|
||||
|
@ -33,19 +33,19 @@
|
|||
var MIN_POINT_DISTANCE = 0.01;
|
||||
var STROKE_WIDTH = 0.02;
|
||||
|
||||
this.setRightHand = function() {
|
||||
this.setRightHand = function () {
|
||||
this.hand = 'RIGHT';
|
||||
}
|
||||
|
||||
this.setLeftHand = function() {
|
||||
this.setLeftHand = function () {
|
||||
this.hand = 'LEFT';
|
||||
}
|
||||
|
||||
this.startNearGrab = function() {
|
||||
this.startNearGrab = function () {
|
||||
this.whichHand = this.hand;
|
||||
}
|
||||
|
||||
this.toggleWithTriggerPressure = function() {
|
||||
this.toggleWithTriggerPressure = function () {
|
||||
var handClickString = this.whichHand + "_HAND_CLICK";
|
||||
|
||||
var handClick = Controller.findAction(handClickString);
|
||||
|
@ -60,7 +60,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
this.enableStream = function() {
|
||||
this.enableStream = function () {
|
||||
var position = Entities.getEntityProperties(this.entityId, "position").position;
|
||||
var PI = 3.141593;
|
||||
var DEG_TO_RAD = PI / 180.0;
|
||||
|
@ -78,6 +78,11 @@
|
|||
particleRadius: 0.01,
|
||||
radiusSpread: 0.005,
|
||||
polarFinish: 0.05,
|
||||
colorStart: {
|
||||
red: 50,
|
||||
green: 10,
|
||||
blue: 150
|
||||
},
|
||||
color: {
|
||||
red: 170,
|
||||
green: 20,
|
||||
|
@ -98,11 +103,11 @@
|
|||
|
||||
}
|
||||
|
||||
this.releaseGrab = function() {
|
||||
this.releaseGrab = function () {
|
||||
this.disableStream();
|
||||
}
|
||||
|
||||
this.disableStream = function() {
|
||||
this.disableStream = function () {
|
||||
Entities.deleteEntity(this.paintStream);
|
||||
this.paintStream = null;
|
||||
this.spraying = false;
|
||||
|
@ -110,7 +115,7 @@
|
|||
}
|
||||
|
||||
|
||||
this.continueNearGrab = function() {
|
||||
this.continueNearGrab = function () {
|
||||
|
||||
this.toggleWithTriggerPressure();
|
||||
|
||||
|
@ -136,7 +141,7 @@
|
|||
});
|
||||
}
|
||||
|
||||
this.preload = function(entityId) {
|
||||
this.preload = function (entityId) {
|
||||
this.sprayVolume = 0.1;
|
||||
this.spraying = false;
|
||||
this.entityId = entityId;
|
||||
|
@ -144,12 +149,9 @@
|
|||
}
|
||||
|
||||
|
||||
this.unload = function() {
|
||||
this.unload = function () {
|
||||
if (this.paintStream) {
|
||||
Entities.deleteEntity(this.paintStream);
|
||||
}
|
||||
this.strokes.forEach(function(stroke) {
|
||||
Entities.deleteEntity(stroke);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
68
examples/utilities/tools/MonoHMD.js
Normal file
68
examples/utilities/tools/MonoHMD.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// MonoHMD.js
|
||||
//
|
||||
// Created by Chris Collins on 10/5/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This script allows you to switch between mono and stereo mode within the HMD.
|
||||
// It will add adition menu to Tools called "IPD".
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
|
||||
|
||||
|
||||
function setupipdMenu() {
|
||||
if (!Menu.menuExists("Tools > IPD")) {
|
||||
Menu.addMenu("Tools > IPD");
|
||||
}
|
||||
if (!Menu.menuItemExists("Tools > IPD", "Stereo")) {
|
||||
Menu.addMenuItem({
|
||||
menuName: "Tools > IPD",
|
||||
menuItemName: "Stereo",
|
||||
isCheckable: true,
|
||||
isChecked: true
|
||||
});
|
||||
}
|
||||
if (!Menu.menuItemExists("Tools > IPD", "Mono")) {
|
||||
Menu.addMenuItem({
|
||||
menuName: "Tools > IPD",
|
||||
menuItemName: "Mono",
|
||||
isCheckable: true,
|
||||
isChecked: false
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function menuItemEvent(menuItem) {
|
||||
if (menuItem == "Stereo") {
|
||||
Menu.setIsOptionChecked("Mono", false);
|
||||
HMD.ipdScale = 1.0;
|
||||
|
||||
}
|
||||
if (menuItem == "Mono") {
|
||||
Menu.setIsOptionChecked("Stereo", false);
|
||||
HMD.ipdScale = 0.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function scriptEnding() {
|
||||
|
||||
Menu.removeMenuItem("Tools > IPD", "Stereo");
|
||||
Menu.removeMenuItem("Tools > IPD", "Mono");
|
||||
Menu.removeMenu("Tools > IPD");
|
||||
//reset the HMD to stereo mode
|
||||
HMD.setIPDScale(1.0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
setupipdMenu();
|
||||
Menu.menuItemEvent.connect(menuItemEvent);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
|
||||
#include <avatar/AvatarActionHold.h>
|
||||
#include <avatar/AvatarActionKinematicHold.h>
|
||||
#include <ObjectActionOffset.h>
|
||||
#include <ObjectActionSpring.h>
|
||||
|
||||
|
@ -28,6 +29,8 @@ EntityActionPointer interfaceActionFactory(EntityActionType type, const QUuid& i
|
|||
return (EntityActionPointer) new ObjectActionSpring(id, ownerEntity);
|
||||
case ACTION_TYPE_HOLD:
|
||||
return (EntityActionPointer) new AvatarActionHold(id, ownerEntity);
|
||||
case ACTION_TYPE_KINEMATIC_HOLD:
|
||||
return (EntityActionPointer) new AvatarActionKinematicHold(id, ownerEntity);
|
||||
}
|
||||
|
||||
assert(false);
|
||||
|
@ -65,9 +68,9 @@ EntityActionPointer InterfaceActionFactory::factoryBA(EntityItemPointer ownerEnt
|
|||
|
||||
if (action) {
|
||||
action->deserialize(data);
|
||||
}
|
||||
if (action->lifetimeIsOver()) {
|
||||
return nullptr;
|
||||
if (action->lifetimeIsOver()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return action;
|
||||
|
|
|
@ -139,6 +139,10 @@ void PluginContainerProxy::unsetFullscreen(const QScreen* avoid) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void PluginContainerProxy::requestReset() {
|
||||
// We could signal qApp to sequence this, but it turns out that requestReset is only used from within the main thread anyway.
|
||||
qApp->resetSensors();
|
||||
}
|
||||
|
||||
void PluginContainerProxy::showDisplayPluginsTools() {
|
||||
DependencyManager::get<DialogsManager>()->hmdTools(true);
|
||||
|
|
|
@ -16,10 +16,11 @@ class PluginContainerProxy : public QObject, PluginContainer {
|
|||
virtual QAction* addMenuItem(const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override;
|
||||
virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override;
|
||||
virtual bool isOptionChecked(const QString& name) override;
|
||||
virtual void setIsOptionChecked(const QString& path, bool checked);
|
||||
virtual void setIsOptionChecked(const QString& path, bool checked) override;
|
||||
virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = true) override;
|
||||
virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override;
|
||||
virtual void showDisplayPluginsTools() override;
|
||||
virtual void requestReset() override;
|
||||
virtual QGLWidget* getPrimarySurface() override;
|
||||
virtual bool isForeground() override;
|
||||
QRect _savedGeometry{ 10, 120, 800, 600 };
|
||||
|
|
188
interface/src/avatar/AvatarActionKinematicHold.cpp
Normal file
188
interface/src/avatar/AvatarActionKinematicHold.cpp
Normal file
|
@ -0,0 +1,188 @@
|
|||
//
|
||||
// AvatarActionKinematicHold.cpp
|
||||
// interface/src/avatar/
|
||||
//
|
||||
// Created by Seth Alves 2015-6-9
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "QVariantGLM.h"
|
||||
#include "avatar/MyAvatar.h"
|
||||
#include "avatar/AvatarManager.h"
|
||||
|
||||
#include "AvatarActionKinematicHold.h"
|
||||
|
||||
const uint16_t AvatarActionKinematicHold::holdVersion = 1;
|
||||
|
||||
AvatarActionKinematicHold::AvatarActionKinematicHold(const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectActionSpring(id, ownerEntity),
|
||||
_relativePosition(glm::vec3(0.0f)),
|
||||
_relativeRotation(glm::quat()),
|
||||
_hand("right"),
|
||||
_mine(false),
|
||||
_previousPositionalTarget(Vectors::ZERO),
|
||||
_previousRotationalTarget(Quaternions::IDENTITY)
|
||||
{
|
||||
_type = ACTION_TYPE_KINEMATIC_HOLD;
|
||||
#if WANT_DEBUG
|
||||
qDebug() << "AvatarActionKinematicHold::AvatarActionKinematicHold";
|
||||
#endif
|
||||
}
|
||||
|
||||
AvatarActionKinematicHold::~AvatarActionKinematicHold() {
|
||||
#if WANT_DEBUG
|
||||
qDebug() << "AvatarActionKinematicHold::~AvatarActionKinematicHold";
|
||||
#endif
|
||||
}
|
||||
|
||||
void AvatarActionKinematicHold::updateActionWorker(float deltaTimeStep) {
|
||||
if (!_mine) {
|
||||
// if a local script isn't updating this, then we are just getting spring-action data over the wire.
|
||||
// let the super-class handle it.
|
||||
ObjectActionSpring::updateActionWorker(deltaTimeStep);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(deltaTimeStep > 0.0f);
|
||||
|
||||
glm::quat rotation;
|
||||
glm::vec3 position;
|
||||
glm::vec3 offset;
|
||||
bool gotLock = withTryReadLock([&]{
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
glm::vec3 palmPosition;
|
||||
glm::quat palmRotation;
|
||||
if (_hand == "right") {
|
||||
palmPosition = myAvatar->getRightPalmPosition();
|
||||
palmRotation = myAvatar->getRightPalmRotation();
|
||||
} else {
|
||||
palmPosition = myAvatar->getLeftPalmPosition();
|
||||
palmRotation = myAvatar->getLeftPalmRotation();
|
||||
}
|
||||
|
||||
rotation = palmRotation * _relativeRotation;
|
||||
offset = rotation * _relativePosition;
|
||||
position = palmPosition + offset;
|
||||
});
|
||||
|
||||
if (gotLock) {
|
||||
gotLock = withTryWriteLock([&]{
|
||||
if (_positionalTarget != position || _rotationalTarget != rotation) {
|
||||
_positionalTarget = position;
|
||||
_rotationalTarget = rotation;
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setActionDataDirty(true);
|
||||
void* physicsInfo = ownerEntity->getPhysicsInfo();
|
||||
if (physicsInfo) {
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
|
||||
btRigidBody* rigidBody = motionState ? motionState->getRigidBody() : nullptr;
|
||||
if (!rigidBody) {
|
||||
qDebug() << "ObjectActionSpring::updateActionWorker no rigidBody";
|
||||
return;
|
||||
}
|
||||
|
||||
if (_setVelocity) {
|
||||
if (_previousSet) {
|
||||
glm::vec3 positionalVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep;
|
||||
rigidBody->setLinearVelocity(glmToBullet(positionalVelocity));
|
||||
// back up along velocity a bit in order to smooth out a "vibrating" appearance
|
||||
_positionalTarget -= positionalVelocity * deltaTimeStep / 2.0f;
|
||||
}
|
||||
}
|
||||
|
||||
btTransform worldTrans = rigidBody->getWorldTransform();
|
||||
worldTrans.setOrigin(glmToBullet(_positionalTarget));
|
||||
worldTrans.setRotation(glmToBullet(_rotationalTarget));
|
||||
rigidBody->setWorldTransform(worldTrans);
|
||||
|
||||
_previousPositionalTarget = _positionalTarget;
|
||||
_previousRotationalTarget = _rotationalTarget;
|
||||
_previousSet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (gotLock) {
|
||||
ObjectActionSpring::updateActionWorker(deltaTimeStep);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool AvatarActionKinematicHold::updateArguments(QVariantMap arguments) {
|
||||
if (!ObjectAction::updateArguments(arguments)) {
|
||||
return false;
|
||||
}
|
||||
bool ok = true;
|
||||
glm::vec3 relativePosition =
|
||||
EntityActionInterface::extractVec3Argument("kinematic-hold", arguments, "relativePosition", ok, false);
|
||||
if (!ok) {
|
||||
relativePosition = _relativePosition;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
glm::quat relativeRotation =
|
||||
EntityActionInterface::extractQuatArgument("kinematic-hold", arguments, "relativeRotation", ok, false);
|
||||
if (!ok) {
|
||||
relativeRotation = _relativeRotation;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
QString hand =
|
||||
EntityActionInterface::extractStringArgument("kinematic-hold", arguments, "hand", ok, false);
|
||||
if (!ok || !(hand == "left" || hand == "right")) {
|
||||
hand = _hand;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
int setVelocity =
|
||||
EntityActionInterface::extractIntegerArgument("kinematic-hold", arguments, "setVelocity", ok, false);
|
||||
if (!ok) {
|
||||
setVelocity = false;
|
||||
}
|
||||
|
||||
if (relativePosition != _relativePosition
|
||||
|| relativeRotation != _relativeRotation
|
||||
|| hand != _hand) {
|
||||
withWriteLock([&] {
|
||||
_relativePosition = relativePosition;
|
||||
_relativeRotation = relativeRotation;
|
||||
_hand = hand;
|
||||
_setVelocity = setVelocity;
|
||||
|
||||
_mine = true;
|
||||
_active = true;
|
||||
activateBody();
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
QVariantMap AvatarActionKinematicHold::getArguments() {
|
||||
QVariantMap arguments = ObjectAction::getArguments();
|
||||
withReadLock([&]{
|
||||
if (!_mine) {
|
||||
arguments = ObjectActionSpring::getArguments();
|
||||
return;
|
||||
}
|
||||
|
||||
arguments["relativePosition"] = glmToQMap(_relativePosition);
|
||||
arguments["relativeRotation"] = glmToQMap(_relativeRotation);
|
||||
arguments["setVelocity"] = _setVelocity;
|
||||
arguments["hand"] = _hand;
|
||||
});
|
||||
return arguments;
|
||||
}
|
||||
|
||||
|
||||
void AvatarActionKinematicHold::deserialize(QByteArray serializedArguments) {
|
||||
if (!_mine) {
|
||||
ObjectActionSpring::deserialize(serializedArguments);
|
||||
}
|
||||
}
|
47
interface/src/avatar/AvatarActionKinematicHold.h
Normal file
47
interface/src/avatar/AvatarActionKinematicHold.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// AvatarActionKinematicHold.h
|
||||
// interface/src/avatar/
|
||||
//
|
||||
// Created by Seth Alves 2015-10-2
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AvatarActionKinematicHold_h
|
||||
#define hifi_AvatarActionKinematicHold_h
|
||||
|
||||
#include <QUuid>
|
||||
|
||||
#include <EntityItem.h>
|
||||
#include <ObjectActionSpring.h>
|
||||
|
||||
class AvatarActionKinematicHold : public ObjectActionSpring {
|
||||
public:
|
||||
AvatarActionKinematicHold(const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~AvatarActionKinematicHold();
|
||||
|
||||
virtual bool updateArguments(QVariantMap arguments);
|
||||
virtual QVariantMap getArguments();
|
||||
|
||||
virtual void updateActionWorker(float deltaTimeStep);
|
||||
|
||||
virtual void deserialize(QByteArray serializedArguments);
|
||||
|
||||
private:
|
||||
static const uint16_t holdVersion;
|
||||
|
||||
glm::vec3 _relativePosition;
|
||||
glm::quat _relativeRotation;
|
||||
QString _hand;
|
||||
bool _mine = false;
|
||||
|
||||
bool _previousSet = false;
|
||||
glm::vec3 _previousPositionalTarget;
|
||||
glm::quat _previousRotationalTarget;
|
||||
|
||||
bool _setVelocity = false;
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarActionKinematicHold_h
|
|
@ -139,19 +139,19 @@ public:
|
|||
void updateLookAtTargetAvatar();
|
||||
void clearLookAtTargetAvatar();
|
||||
|
||||
virtual void setJointRotations(QVector<glm::quat> jointRotations);
|
||||
virtual void setJointTranslations(QVector<glm::vec3> jointTranslations);
|
||||
virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation);
|
||||
virtual void setJointRotation(int index, const glm::quat& rotation);
|
||||
virtual void setJointTranslation(int index, const glm::vec3& translation);
|
||||
virtual void clearJointData(int index);
|
||||
virtual void clearJointsData();
|
||||
virtual void setJointRotations(QVector<glm::quat> jointRotations) override;
|
||||
virtual void setJointTranslations(QVector<glm::vec3> jointTranslations) override;
|
||||
virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) override;
|
||||
virtual void setJointRotation(int index, const glm::quat& rotation) override;
|
||||
virtual void setJointTranslation(int index, const glm::vec3& translation) override;
|
||||
virtual void clearJointData(int index) override;
|
||||
virtual void clearJointsData() override;
|
||||
|
||||
Q_INVOKABLE void useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName = QString());
|
||||
Q_INVOKABLE const QUrl& getFullAvatarURLFromPreferences() const { return _fullAvatarURLFromPreferences; }
|
||||
Q_INVOKABLE const QString& getFullAvatarModelName() const { return _fullAvatarModelName; }
|
||||
|
||||
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
|
||||
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData) override;
|
||||
|
||||
DynamicCharacterController* getCharacterController() { return &_characterController; }
|
||||
|
||||
|
@ -218,7 +218,7 @@ public slots:
|
|||
void saveRecording(QString filename);
|
||||
void loadLastRecording();
|
||||
|
||||
virtual void rebuildSkeletonBody();
|
||||
virtual void rebuildSkeletonBody() override;
|
||||
|
||||
bool getEnableRigAnimations() const { return _rig->getEnableRig(); }
|
||||
void setEnableRigAnimations(bool isEnabled);
|
||||
|
@ -243,7 +243,7 @@ private:
|
|||
|
||||
glm::vec3 getWorldBodyPosition() const;
|
||||
glm::quat getWorldBodyOrientation() const;
|
||||
QByteArray toByteArray(bool cullSmallChanges, bool sendAll);
|
||||
QByteArray toByteArray(bool cullSmallChanges, bool sendAll) override;
|
||||
void simulate(float deltaTime);
|
||||
void updateFromTrackers(float deltaTime);
|
||||
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override;
|
||||
|
@ -252,9 +252,9 @@ private:
|
|||
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; }
|
||||
bool getShouldRenderLocally() const { return _shouldRender; }
|
||||
bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; };
|
||||
bool isMyAvatar() const { return true; }
|
||||
virtual int parseDataFromBuffer(const QByteArray& buffer);
|
||||
virtual glm::vec3 getSkeletonPosition() const;
|
||||
bool isMyAvatar() const override { return true; }
|
||||
virtual int parseDataFromBuffer(const QByteArray& buffer) override;
|
||||
virtual glm::vec3 getSkeletonPosition() const override;
|
||||
|
||||
glm::vec3 getScriptedMotorVelocity() const { return _scriptedMotorVelocity; }
|
||||
float getScriptedMotorTimescale() const { return _scriptedMotorTimescale; }
|
||||
|
@ -264,7 +264,7 @@ private:
|
|||
void setScriptedMotorFrame(QString frame);
|
||||
virtual void attach(const QString& modelURL, const QString& jointName = QString(),
|
||||
const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f,
|
||||
bool allowDuplicates = false, bool useSaved = true);
|
||||
bool allowDuplicates = false, bool useSaved = true) override;
|
||||
|
||||
void renderLaserPointers(gpu::Batch& batch);
|
||||
const RecorderPointer getRecorder() const { return _recorder; }
|
||||
|
@ -273,8 +273,8 @@ private:
|
|||
bool cameraInsideHead() const;
|
||||
|
||||
// These are made private for MyAvatar so that you will use the "use" methods instead
|
||||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
virtual void setFaceModelURL(const QUrl& faceModelURL) override;
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
|
||||
|
||||
void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity);
|
||||
|
||||
|
|
|
@ -27,10 +27,10 @@ public:
|
|||
SkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr, RigPointer rig = nullptr);
|
||||
~SkeletonModel();
|
||||
|
||||
virtual void initJointStates(QVector<JointState> states);
|
||||
virtual void initJointStates(QVector<JointState> states) override;
|
||||
|
||||
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
||||
virtual void updateRig(float deltaTime, glm::mat4 parentTransform);
|
||||
virtual void simulate(float deltaTime, bool fullUpdate = true) override;
|
||||
virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override;
|
||||
void updateAttitude();
|
||||
|
||||
void renderIKConstraints(gpu::Batch& batch);
|
||||
|
|
|
@ -159,7 +159,7 @@ void AnimClip::copyFromNetworkAnim() {
|
|||
|
||||
// used to adjust translation offsets, so large translation animatons on the reference skeleton
|
||||
// will be adjusted when played on a skeleton with short limbs.
|
||||
float limbLengthScale = fabs(glm::length(fbxZeroTrans)) <= 0.0001f ? 1.0f : (glm::length(relBindPose.trans) / glm::length(fbxZeroTrans));
|
||||
float limbLengthScale = fabsf(glm::length(fbxZeroTrans)) <= 0.0001f ? 1.0f : (glm::length(relBindPose.trans) / glm::length(fbxZeroTrans));
|
||||
|
||||
AnimPose& pose = _anim[frame][skeletonJoint];
|
||||
const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame];
|
||||
|
|
|
@ -52,7 +52,7 @@ protected:
|
|||
|
||||
void computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses);
|
||||
void solveWithCyclicCoordinateDescent(const std::vector<IKTarget>& targets);
|
||||
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton);
|
||||
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override;
|
||||
|
||||
// for AnimDebugDraw rendering
|
||||
virtual const AnimPoseVec& getPosesInternal() const override { return _relativePoses; }
|
||||
|
|
|
@ -56,7 +56,7 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
|
|||
|
||||
if (_procedural->ready()) {
|
||||
batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well
|
||||
_procedural->prepare(batch, this->getDimensions());
|
||||
_procedural->prepare(batch, getPosition(), getDimensions());
|
||||
auto color = _procedural->getColor(cubeColor);
|
||||
batch._glColor4f(color.r, color.g, color.b, color.a);
|
||||
DependencyManager::get<GeometryCache>()->renderCube(batch);
|
||||
|
|
|
@ -25,8 +25,8 @@ public:
|
|||
|
||||
void updateRenderItem();
|
||||
|
||||
virtual bool addToScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges);
|
||||
virtual void removeFromScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges);
|
||||
virtual bool addToScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges) override;
|
||||
virtual void removeFromScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges) override;
|
||||
|
||||
protected:
|
||||
render::ItemID _renderItemId;
|
||||
|
|
|
@ -62,7 +62,7 @@ void RenderableSphereEntityItem::render(RenderArgs* args) {
|
|||
modelTransform.postScale(SPHERE_ENTITY_SCALE);
|
||||
if (_procedural->ready()) {
|
||||
batch.setModelTransform(modelTransform); // use a transform with scale, rotation, registration point and translation
|
||||
_procedural->prepare(batch, getDimensions());
|
||||
_procedural->prepare(batch, getPosition(), getDimensions());
|
||||
auto color = _procedural->getColor(sphereColor);
|
||||
batch._glColor4f(color.r, color.g, color.b, color.a);
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(batch);
|
||||
|
|
|
@ -100,6 +100,9 @@ EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeS
|
|||
if (normalizedActionTypeString == "hold") {
|
||||
return ACTION_TYPE_HOLD;
|
||||
}
|
||||
if (normalizedActionTypeString == "kinematichold") {
|
||||
return ACTION_TYPE_KINEMATIC_HOLD;
|
||||
}
|
||||
|
||||
qDebug() << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString;
|
||||
return ACTION_TYPE_NONE;
|
||||
|
@ -115,6 +118,8 @@ QString EntityActionInterface::actionTypeToString(EntityActionType actionType) {
|
|||
return "spring";
|
||||
case ACTION_TYPE_HOLD:
|
||||
return "hold";
|
||||
case ACTION_TYPE_KINEMATIC_HOLD:
|
||||
return "kinematic-hold";
|
||||
}
|
||||
assert(false);
|
||||
return "none";
|
||||
|
@ -244,6 +249,28 @@ float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMa
|
|||
return v;
|
||||
}
|
||||
|
||||
int EntityActionInterface::extractIntegerArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qDebug() << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
QVariant vV = arguments[argumentName];
|
||||
bool vOk = true;
|
||||
int v = vV.toInt(&vOk);
|
||||
|
||||
if (!vOk || v != v) {
|
||||
ok = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
QString EntityActionInterface::extractStringArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
|
|
|
@ -23,7 +23,8 @@ enum EntityActionType {
|
|||
ACTION_TYPE_NONE = 0,
|
||||
ACTION_TYPE_OFFSET = 1000,
|
||||
ACTION_TYPE_SPRING = 2000,
|
||||
ACTION_TYPE_HOLD = 3000
|
||||
ACTION_TYPE_HOLD = 3000,
|
||||
ACTION_TYPE_KINEMATIC_HOLD = 4000
|
||||
};
|
||||
|
||||
|
||||
|
@ -48,6 +49,8 @@ public:
|
|||
|
||||
virtual bool lifetimeIsOver() { return false; }
|
||||
|
||||
bool locallyAddedButNotYetReceived = false;
|
||||
|
||||
protected:
|
||||
virtual glm::vec3 getPosition() = 0;
|
||||
virtual void setPosition(glm::vec3 position) = 0;
|
||||
|
@ -67,6 +70,8 @@ protected:
|
|||
QString argumentName, bool& ok, bool required = true);
|
||||
static float extractFloatArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required = true);
|
||||
static int extractIntegerArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required = true);
|
||||
static QString extractStringArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required = true);
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
bool EntityItem::_sendPhysicsUpdates = true;
|
||||
int EntityItem::_maxActionsDataSize = 800;
|
||||
quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND;
|
||||
|
||||
EntityItem::EntityItem(const EntityItemID& entityItemID) :
|
||||
_type(EntityTypes::Unknown),
|
||||
|
@ -1505,6 +1506,8 @@ bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer act
|
|||
result = addActionInternal(simulation, action);
|
||||
if (!result) {
|
||||
removeActionInternal(action->getID());
|
||||
} else {
|
||||
action->locallyAddedButNotYetReceived = true;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1525,7 +1528,8 @@ bool EntityItem::addActionInternal(EntitySimulation* simulation, EntityActionPoi
|
|||
simulation->addAction(action);
|
||||
|
||||
bool success;
|
||||
QByteArray newDataCache = serializeActions(success);
|
||||
QByteArray newDataCache;
|
||||
serializeActions(success, newDataCache);
|
||||
if (success) {
|
||||
_allActionsDataCache = newDataCache;
|
||||
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
|
||||
|
@ -1546,7 +1550,7 @@ bool EntityItem::updateAction(EntitySimulation* simulation, const QUuid& actionI
|
|||
|
||||
success = action->updateArguments(arguments);
|
||||
if (success) {
|
||||
_allActionsDataCache = serializeActions(success);
|
||||
serializeActions(success, _allActionsDataCache);
|
||||
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
|
||||
} else {
|
||||
qDebug() << "EntityItem::updateAction failed";
|
||||
|
@ -1566,6 +1570,7 @@ bool EntityItem::removeAction(EntitySimulation* simulation, const QUuid& actionI
|
|||
|
||||
bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* simulation) {
|
||||
assertWriteLocked();
|
||||
_previouslyDeletedActions.insert(actionID, usecTimestampNow());
|
||||
if (_objectActions.contains(actionID)) {
|
||||
if (!simulation) {
|
||||
EntityTreePointer entityTree = _element ? _element->getTree() : nullptr;
|
||||
|
@ -1581,7 +1586,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* s
|
|||
}
|
||||
|
||||
bool success = true;
|
||||
_allActionsDataCache = serializeActions(success);
|
||||
serializeActions(success, _allActionsDataCache);
|
||||
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
|
||||
return success;
|
||||
}
|
||||
|
@ -1618,6 +1623,8 @@ void EntityItem::deserializeActions() {
|
|||
void EntityItem::deserializeActionsInternal() {
|
||||
assertWriteLocked();
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
|
||||
if (!_element) {
|
||||
return;
|
||||
}
|
||||
|
@ -1643,6 +1650,10 @@ void EntityItem::deserializeActionsInternal() {
|
|||
QUuid actionID;
|
||||
serializedActionStream >> actionType;
|
||||
serializedActionStream >> actionID;
|
||||
if (_previouslyDeletedActions.contains(actionID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
updated << actionID;
|
||||
|
||||
if (_objectActions.contains(actionID)) {
|
||||
|
@ -1650,14 +1661,14 @@ void EntityItem::deserializeActionsInternal() {
|
|||
// TODO: make sure types match? there isn't currently a way to
|
||||
// change the type of an existing action.
|
||||
action->deserialize(serializedAction);
|
||||
action->locallyAddedButNotYetReceived = false;
|
||||
} else {
|
||||
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
|
||||
|
||||
// EntityItemPointer entity = entityTree->findEntityByEntityItemID(_id, false);
|
||||
EntityItemPointer entity = shared_from_this();
|
||||
EntityActionPointer action = actionFactory->factoryBA(entity, serializedAction);
|
||||
if (action) {
|
||||
entity->addActionInternal(simulation, action);
|
||||
action->locallyAddedButNotYetReceived = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1667,11 +1678,27 @@ void EntityItem::deserializeActionsInternal() {
|
|||
while (i != _objectActions.end()) {
|
||||
QUuid id = i.key();
|
||||
if (!updated.contains(id)) {
|
||||
_actionsToRemove << id;
|
||||
EntityActionPointer action = i.value();
|
||||
// if we've just added this action, don't remove it due to lack of mention in an incoming packet.
|
||||
if (! action->locallyAddedButNotYetReceived) {
|
||||
_actionsToRemove << id;
|
||||
_previouslyDeletedActions.insert(id, now);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// trim down _previouslyDeletedActions
|
||||
QMutableHashIterator<QUuid, quint64> _previouslyDeletedIter(_previouslyDeletedActions);
|
||||
while (_previouslyDeletedIter.hasNext()) {
|
||||
_previouslyDeletedIter.next();
|
||||
if (now - _previouslyDeletedIter.value() > _rememberDeletedActionTime) {
|
||||
_previouslyDeletedActions.remove(_previouslyDeletedIter.key());
|
||||
}
|
||||
}
|
||||
|
||||
_actionDataDirty = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1697,13 +1724,13 @@ void EntityItem::setActionDataInternal(QByteArray actionData) {
|
|||
deserializeActionsInternal();
|
||||
}
|
||||
|
||||
QByteArray EntityItem::serializeActions(bool& success) const {
|
||||
void EntityItem::serializeActions(bool& success, QByteArray& result) const {
|
||||
assertLocked();
|
||||
QByteArray result;
|
||||
|
||||
if (_objectActions.size() == 0) {
|
||||
success = true;
|
||||
return QByteArray();
|
||||
result.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
QVector<QByteArray> serializedActions;
|
||||
|
@ -1721,21 +1748,20 @@ QByteArray EntityItem::serializeActions(bool& success) const {
|
|||
|
||||
if (result.size() >= _maxActionsDataSize) {
|
||||
success = false;
|
||||
return result;
|
||||
return;
|
||||
}
|
||||
|
||||
success = true;
|
||||
return result;
|
||||
return;
|
||||
}
|
||||
|
||||
const QByteArray EntityItem::getActionDataInternal() const {
|
||||
if (_actionDataDirty) {
|
||||
bool success;
|
||||
QByteArray newDataCache = serializeActions(success);
|
||||
serializeActions(success, _allActionsDataCache);
|
||||
if (success) {
|
||||
_allActionsDataCache = newDataCache;
|
||||
_actionDataDirty = false;
|
||||
}
|
||||
_actionDataDirty = false;
|
||||
}
|
||||
return _allActionsDataCache;
|
||||
}
|
||||
|
|
|
@ -508,17 +508,21 @@ protected:
|
|||
bool addActionInternal(EntitySimulation* simulation, EntityActionPointer action);
|
||||
bool removeActionInternal(const QUuid& actionID, EntitySimulation* simulation = nullptr);
|
||||
void deserializeActionsInternal();
|
||||
QByteArray serializeActions(bool& success) const;
|
||||
void serializeActions(bool& success, QByteArray& result) const;
|
||||
QHash<QUuid, EntityActionPointer> _objectActions;
|
||||
|
||||
static int _maxActionsDataSize;
|
||||
mutable QByteArray _allActionsDataCache;
|
||||
|
||||
// when an entity-server starts up, EntityItem::setActionData is called before the entity-tree is
|
||||
// ready. This means we can't find our EntityItemPointer or add the action to the simulation. These
|
||||
// are used to keep track of and work around this situation.
|
||||
void checkWaitingToRemove(EntitySimulation* simulation = nullptr);
|
||||
mutable QSet<QUuid> _actionsToRemove;
|
||||
mutable bool _actionDataDirty = false;
|
||||
// _previouslyDeletedActions is used to avoid an action being re-added due to server round-trip lag
|
||||
static quint64 _rememberDeletedActionTime;
|
||||
mutable QHash<QUuid, quint64> _previouslyDeletedActions;
|
||||
};
|
||||
|
||||
#endif // hifi_EntityItem_h
|
||||
|
|
|
@ -156,24 +156,24 @@ public:
|
|||
// Indirect buffer is used by the multiDrawXXXIndirect calls
|
||||
// The indirect buffer contains the command descriptions to execute multiple drawcalls in a single call
|
||||
void setIndirectBuffer(const BufferPointer& buffer, Offset offset = 0, Offset stride = 0);
|
||||
|
||||
// multi command desctription for multiDrawIndexedIndirect
|
||||
class DrawIndirectCommand {
|
||||
public:
|
||||
uint _count{ 0 };
|
||||
uint _instanceCount{ 0 };
|
||||
uint _firstIndex{ 0 };
|
||||
uint _baseInstance{ 0 };
|
||||
|
||||
// multi command desctription for multiDrawIndexedIndirect
|
||||
class DrawIndirectCommand {
|
||||
public:
|
||||
uint _count{ 0 };
|
||||
uint _instanceCount{ 0 };
|
||||
uint _firstIndex{ 0 };
|
||||
uint _baseInstance{ 0 };
|
||||
};
|
||||
|
||||
// multi command desctription for multiDrawIndexedIndirect
|
||||
class DrawIndexedIndirectCommand {
|
||||
public:
|
||||
uint _count{ 0 };
|
||||
uint _instanceCount{ 0 };
|
||||
uint _firstIndex{ 0 };
|
||||
uint _baseVertex{ 0 };
|
||||
uint _baseInstance{ 0 };
|
||||
|
||||
// multi command desctription for multiDrawIndexedIndirect
|
||||
class DrawIndexedIndirectCommand {
|
||||
public:
|
||||
uint _count{ 0 };
|
||||
uint _instanceCount{ 0 };
|
||||
uint _firstIndex{ 0 };
|
||||
uint _baseVertex{ 0 };
|
||||
uint _baseInstance{ 0 };
|
||||
};
|
||||
|
||||
// Transform Stage
|
||||
|
@ -246,6 +246,26 @@ public:
|
|||
void _glUniform4iv(int location, int count, const int* value);
|
||||
void _glUniformMatrix4fv(int location, int count, unsigned char transpose, const float* value);
|
||||
|
||||
void _glUniform(int location, int v0) {
|
||||
_glUniform1i(location, v0);
|
||||
}
|
||||
|
||||
void _glUniform(int location, float v0) {
|
||||
_glUniform1f(location, v0);
|
||||
}
|
||||
|
||||
void _glUniform(int location, const glm::vec2& v) {
|
||||
_glUniform2f(location, v.x, v.y);
|
||||
}
|
||||
|
||||
void _glUniform(int location, const glm::vec3& v) {
|
||||
_glUniform3f(location, v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
void _glUniform(int location, const glm::vec4& v) {
|
||||
_glUniform4f(location, v.x, v.y, v.z, v.w);
|
||||
}
|
||||
|
||||
void _glColor4f(float red, float green, float blue, float alpha);
|
||||
|
||||
enum Command {
|
||||
|
|
|
@ -343,9 +343,22 @@ bool Texture::assignStoredMipFace(uint16 level, const Element& format, Size size
|
|||
}
|
||||
|
||||
uint16 Texture::autoGenerateMips(uint16 maxMip) {
|
||||
_autoGenerateMips = true;
|
||||
_maxMip = std::min((uint16) (evalNumMips() - 1), maxMip);
|
||||
_stamp++;
|
||||
bool changed = false;
|
||||
if (!_autoGenerateMips) {
|
||||
changed = true;
|
||||
_autoGenerateMips = true;
|
||||
}
|
||||
|
||||
auto newMaxMip = std::min((uint16)(evalNumMips() - 1), maxMip);
|
||||
if (newMaxMip != _maxMip) {
|
||||
changed = true;
|
||||
_maxMip = newMaxMip;;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
_stamp++;
|
||||
}
|
||||
|
||||
return _maxMip;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ public:
|
|||
// Plugin functions
|
||||
virtual bool isSupported() const override { return true; }
|
||||
virtual bool isJointController() const override { return false; }
|
||||
const QString& getName() const { return NAME; }
|
||||
const QString& getName() const override { return NAME; }
|
||||
|
||||
virtual void activate() override {};
|
||||
virtual void deactivate() override {};
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
// Plugin functions
|
||||
virtual bool isSupported() const override;
|
||||
virtual bool isJointController() const override { return false; }
|
||||
const QString& getName() const { return NAME; }
|
||||
const QString& getName() const override { return NAME; }
|
||||
|
||||
virtual void init() override;
|
||||
virtual void deinit() override;
|
||||
|
|
|
@ -285,6 +285,7 @@ void SixenseManager::updateCalibration(void* controllersX) {
|
|||
_avatarRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, Vectors::UNIT_Y, zAxis)));
|
||||
const float Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR = -0.3f;
|
||||
_avatarPosition.y += Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR;
|
||||
CONTAINER->requestReset();
|
||||
qCDebug(inputplugins, "succeess: sixense calibration");
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -63,7 +63,7 @@ public:
|
|||
// Plugin functions
|
||||
virtual bool isSupported() const override;
|
||||
virtual bool isJointController() const override { return true; }
|
||||
const QString& getName() const { return NAME; }
|
||||
const QString& getName() const override { return NAME; }
|
||||
|
||||
virtual void activate() override;
|
||||
virtual void deactivate() override;
|
||||
|
|
|
@ -53,7 +53,7 @@ public:
|
|||
// Plugin functions
|
||||
virtual bool isSupported() const override;
|
||||
virtual bool isJointController() const override { return true; }
|
||||
const QString& getName() const { return NAME; }
|
||||
const QString& getName() const override { return NAME; }
|
||||
|
||||
virtual void activate() override;
|
||||
virtual void deactivate() override;
|
||||
|
|
|
@ -7,5 +7,5 @@ add_dependency_external_projects(glm)
|
|||
find_package(GLM REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
||||
|
||||
link_hifi_libraries(shared networking gpu model)
|
||||
link_hifi_libraries(shared networking gpu model fbx)
|
||||
|
||||
|
|
|
@ -30,9 +30,9 @@ class OctreeHeadlessViewer : public OctreeRenderer {
|
|||
public:
|
||||
OctreeHeadlessViewer();
|
||||
virtual ~OctreeHeadlessViewer() {};
|
||||
virtual void renderElement(OctreeElementPointer element, RenderArgs* args) { /* swallow these */ }
|
||||
virtual void renderElement(OctreeElementPointer element, RenderArgs* args) override { /* swallow these */ }
|
||||
|
||||
virtual void init();
|
||||
virtual void init() override ;
|
||||
virtual void render(RenderArgs* renderArgs) override { /* swallow these */ }
|
||||
|
||||
void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; }
|
||||
|
@ -58,7 +58,7 @@ public slots:
|
|||
|
||||
// getters for LOD and PPS
|
||||
float getVoxelSizeScale() const { return _voxelSizeScale; }
|
||||
int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
|
||||
int getBoundaryLevelAdjust() const override { return _boundaryLevelAdjust; }
|
||||
int getMaxPacketsPerSecond() const { return _maxPacketsPerSecond; }
|
||||
|
||||
unsigned getOctreeElementsCount() const { return _tree->getOctreeElementsCount(); }
|
||||
|
|
|
@ -26,6 +26,7 @@ public:
|
|||
virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = false) = 0;
|
||||
virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) = 0;
|
||||
virtual void showDisplayPluginsTools() = 0;
|
||||
virtual void requestReset() = 0;
|
||||
virtual QGLWidget* getPrimarySurface() = 0;
|
||||
virtual bool isForeground() = 0;
|
||||
};
|
||||
|
|
|
@ -17,20 +17,30 @@
|
|||
#include <gpu/Batch.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
#include "ProceduralShaders.h"
|
||||
|
||||
static const char* const UNIFORM_TIME_NAME= "iGlobalTime";
|
||||
static const char* const UNIFORM_SCALE_NAME = "iWorldScale";
|
||||
// Userdata parsing constants
|
||||
static const QString PROCEDURAL_USER_DATA_KEY = "ProceduralEntity";
|
||||
|
||||
static const QString URL_KEY = "shaderUrl";
|
||||
static const QString VERSION_KEY = "version";
|
||||
static const QString UNIFORMS_KEY = "uniforms";
|
||||
static const QString CHANNELS_KEY = "channels";
|
||||
|
||||
// Shader replace strings
|
||||
static const std::string PROCEDURAL_BLOCK = "//PROCEDURAL_BLOCK";
|
||||
static const std::string PROCEDURAL_COMMON_BLOCK = "//PROCEDURAL_COMMON_BLOCK";
|
||||
static const std::string PROCEDURAL_VERSION = "//PROCEDURAL_VERSION";
|
||||
|
||||
static const std::string STANDARD_UNIFORM_NAMES[Procedural::NUM_STANDARD_UNIFORMS] = {
|
||||
"iDate",
|
||||
"iGlobalTime",
|
||||
"iFrameCount",
|
||||
"iWorldScale",
|
||||
"iWorldPosition",
|
||||
"iChannelResolution"
|
||||
};
|
||||
|
||||
// Example
|
||||
//{
|
||||
|
@ -100,7 +110,21 @@ void Procedural::parse(const QJsonObject& proceduralData) {
|
|||
{
|
||||
auto uniforms = proceduralData[UNIFORMS_KEY];
|
||||
if (uniforms.isObject()) {
|
||||
_uniforms = uniforms.toObject();;
|
||||
_parsedUniforms = uniforms.toObject();
|
||||
}
|
||||
}
|
||||
|
||||
// Grab any textures
|
||||
{
|
||||
auto channels = proceduralData[CHANNELS_KEY];
|
||||
if (channels.isArray()) {
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
_parsedChannels = channels.toArray();
|
||||
size_t channelCount = std::min(MAX_PROCEDURAL_TEXTURE_CHANNELS, (size_t)_parsedChannels.size());
|
||||
for (size_t i = 0; i < channelCount; ++i) {
|
||||
QString url = _parsedChannels.at(i).toString();
|
||||
_channels[i] = textureCache->getTexture(QUrl(url));
|
||||
}
|
||||
}
|
||||
}
|
||||
_enabled = true;
|
||||
|
@ -111,20 +135,26 @@ bool Procedural::ready() {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!_shaderPath.isEmpty()) {
|
||||
return true;
|
||||
// Do we have a network or local shader
|
||||
if (_shaderPath.isEmpty() && (!_networkShader || !_networkShader->isLoaded())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_networkShader) {
|
||||
return _networkShader->isLoaded();
|
||||
// Do we have textures, and if so, are they loaded?
|
||||
for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) {
|
||||
if (_channels[i] && !_channels[i]->isLoaded()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Procedural::prepare(gpu::Batch& batch, const glm::vec3& size) {
|
||||
void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size) {
|
||||
_entityDimensions = size;
|
||||
_entityPosition = position;
|
||||
if (_shaderUrl.isLocalFile()) {
|
||||
auto lastModified = (quint64) QFileInfo(_shaderPath).lastModified().toMSecsSinceEpoch();
|
||||
auto lastModified = (quint64)QFileInfo(_shaderPath).lastModified().toMSecsSinceEpoch();
|
||||
if (lastModified > _shaderModified) {
|
||||
QFile file(_shaderPath);
|
||||
file.open(QIODevice::ReadOnly);
|
||||
|
@ -164,69 +194,183 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& size) {
|
|||
//qDebug() << "FragmentShader:\n" << fragmentShaderSource.c_str();
|
||||
_fragmentShader = gpu::ShaderPointer(gpu::Shader::createPixel(fragmentShaderSource));
|
||||
_shader = gpu::ShaderPointer(gpu::Shader::createProgram(_vertexShader, _fragmentShader));
|
||||
gpu::Shader::makeProgram(*_shader);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("iChannel0"), 0));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("iChannel1"), 1));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("iChannel2"), 2));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("iChannel3"), 3));
|
||||
gpu::Shader::makeProgram(*_shader, slotBindings);
|
||||
|
||||
_pipeline = gpu::PipelinePointer(gpu::Pipeline::create(_shader, _state));
|
||||
_timeSlot = _shader->getUniforms().findLocation(UNIFORM_TIME_NAME);
|
||||
_scaleSlot = _shader->getUniforms().findLocation(UNIFORM_SCALE_NAME);
|
||||
for (size_t i = 0; i < NUM_STANDARD_UNIFORMS; ++i) {
|
||||
const std::string& name = STANDARD_UNIFORM_NAMES[i];
|
||||
_standardUniformSlots[i] = _shader->getUniforms().findLocation(name);
|
||||
}
|
||||
_start = usecTimestampNow();
|
||||
_frameCount = 0;
|
||||
}
|
||||
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
if (_pipelineDirty) {
|
||||
_pipelineDirty = false;
|
||||
// Set any userdata specified uniforms
|
||||
foreach(QString key, _uniforms.keys()) {
|
||||
std::string uniformName = key.toLocal8Bit().data();
|
||||
int32_t slot = _shader->getUniforms().findLocation(uniformName);
|
||||
if (gpu::Shader::INVALID_LOCATION == slot) {
|
||||
continue;
|
||||
setupUniforms();
|
||||
}
|
||||
|
||||
|
||||
for (auto lambda : _uniforms) {
|
||||
lambda(batch);
|
||||
}
|
||||
|
||||
static gpu::Sampler sampler;
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
gpu::Sampler::Desc desc;
|
||||
desc._filter = gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR;
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) {
|
||||
if (_channels[i] && _channels[i]->isLoaded()) {
|
||||
auto gpuTexture = _channels[i]->getGPUTexture();
|
||||
if (gpuTexture) {
|
||||
gpuTexture->setSampler(sampler);
|
||||
gpuTexture->autoGenerateMips(-1);
|
||||
}
|
||||
QJsonValue value = _uniforms[key];
|
||||
if (value.isDouble()) {
|
||||
batch._glUniform1f(slot, value.toDouble());
|
||||
} else if (value.isArray()) {
|
||||
auto valueArray = value.toArray();
|
||||
switch (valueArray.size()) {
|
||||
case 0:
|
||||
break;
|
||||
batch.setResourceTexture(i, gpuTexture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case 1:
|
||||
batch._glUniform1f(slot, valueArray[0].toDouble());
|
||||
break;
|
||||
case 2:
|
||||
batch._glUniform2f(slot,
|
||||
valueArray[0].toDouble(),
|
||||
valueArray[1].toDouble());
|
||||
break;
|
||||
case 3:
|
||||
batch._glUniform3f(slot,
|
||||
valueArray[0].toDouble(),
|
||||
valueArray[1].toDouble(),
|
||||
valueArray[2].toDouble());
|
||||
break;
|
||||
case 4:
|
||||
default:
|
||||
batch._glUniform4f(slot,
|
||||
valueArray[0].toDouble(),
|
||||
valueArray[1].toDouble(),
|
||||
valueArray[2].toDouble(),
|
||||
valueArray[3].toDouble());
|
||||
break;
|
||||
void Procedural::setupUniforms() {
|
||||
_uniforms.clear();
|
||||
// Set any userdata specified uniforms
|
||||
foreach(QString key, _parsedUniforms.keys()) {
|
||||
std::string uniformName = key.toLocal8Bit().data();
|
||||
int32_t slot = _shader->getUniforms().findLocation(uniformName);
|
||||
if (gpu::Shader::INVALID_LOCATION == slot) {
|
||||
continue;
|
||||
}
|
||||
QJsonValue value = _parsedUniforms[key];
|
||||
if (value.isDouble()) {
|
||||
float v = value.toDouble();
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform1f(slot, v);
|
||||
});
|
||||
} else if (value.isArray()) {
|
||||
auto valueArray = value.toArray();
|
||||
switch (valueArray.size()) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 1: {
|
||||
float v = valueArray[0].toDouble();
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform1f(slot, v);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
glm::vec2 v{ valueArray[0].toDouble(), valueArray[1].toDouble() };
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform2f(slot, v.x, v.y);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: {
|
||||
glm::vec3 v{
|
||||
valueArray[0].toDouble(),
|
||||
valueArray[1].toDouble(),
|
||||
valueArray[2].toDouble(),
|
||||
};
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform3f(slot, v.x, v.y, v.z);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
case 4: {
|
||||
glm::vec4 v{
|
||||
valueArray[0].toDouble(),
|
||||
valueArray[1].toDouble(),
|
||||
valueArray[2].toDouble(),
|
||||
valueArray[3].toDouble(),
|
||||
};
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform4f(slot, v.x, v.y, v.z, v.w);
|
||||
});
|
||||
break;
|
||||
}
|
||||
valueArray.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Minimize floating point error by doing an integer division to milliseconds, before the floating point division to seconds
|
||||
float time = (float)((usecTimestampNow() - _start) / USECS_PER_MSEC) / MSECS_PER_SECOND;
|
||||
batch._glUniform1f(_timeSlot, time);
|
||||
// FIXME move into the 'set once' section, since this doesn't change over time
|
||||
batch._glUniform3f(_scaleSlot, size.x, size.y, size.z);
|
||||
}
|
||||
if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[TIME]) {
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
// Minimize floating point error by doing an integer division to milliseconds, before the floating point division to seconds
|
||||
float time = (float)((usecTimestampNow() - _start) / USECS_PER_MSEC) / MSECS_PER_SECOND;
|
||||
batch._glUniform(_standardUniformSlots[TIME], time);
|
||||
});
|
||||
}
|
||||
|
||||
if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[DATE]) {
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
QDateTime now = QDateTime::currentDateTimeUtc();
|
||||
QDate date = now.date();
|
||||
QTime time = now.time();
|
||||
vec4 v;
|
||||
v.x = date.year();
|
||||
// Shadertoy month is 0 based
|
||||
v.y = date.month() - 1;
|
||||
// But not the day... go figure
|
||||
v.z = date.day();
|
||||
v.w = (time.hour() * 3600) + (time.minute() * 60) + time.second();
|
||||
batch._glUniform(_standardUniformSlots[DATE], v);
|
||||
});
|
||||
}
|
||||
|
||||
if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[FRAME_COUNT]) {
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform(_standardUniformSlots[FRAME_COUNT], ++_frameCount);
|
||||
});
|
||||
}
|
||||
|
||||
if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[SCALE]) {
|
||||
// FIXME move into the 'set once' section, since this doesn't change over time
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform(_standardUniformSlots[SCALE], _entityDimensions);
|
||||
});
|
||||
}
|
||||
|
||||
if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[SCALE]) {
|
||||
// FIXME move into the 'set once' section, since this doesn't change over time
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform(_standardUniformSlots[SCALE], _entityDimensions);
|
||||
});
|
||||
}
|
||||
|
||||
if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[POSITION]) {
|
||||
// FIXME move into the 'set once' section, since this doesn't change over time
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
batch._glUniform(_standardUniformSlots[POSITION], _entityPosition);
|
||||
});
|
||||
}
|
||||
|
||||
if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[CHANNEL_RESOLUTION]) {
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
vec3 channelSizes[MAX_PROCEDURAL_TEXTURE_CHANNELS];
|
||||
for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) {
|
||||
if (_channels[i]) {
|
||||
channelSizes[i] = vec3(_channels[i]->getWidth(), _channels[i]->getHeight(), 1.0);
|
||||
}
|
||||
}
|
||||
batch._glUniform3fv(_standardUniformSlots[CHANNEL_RESOLUTION], MAX_PROCEDURAL_TEXTURE_CHANNELS, &channelSizes[0].x);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec4 Procedural::getColor(const glm::vec4& entityColor) {
|
||||
if (_version == 1) {
|
||||
|
|
|
@ -14,11 +14,16 @@
|
|||
#include <QtCore/QString>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonArray>
|
||||
|
||||
#include <gpu/Shader.h>
|
||||
#include <gpu/Pipeline.h>
|
||||
#include <gpu/Batch.h>
|
||||
#include <model-networking/ShaderCache.h>
|
||||
#include <model-networking/TextureCache.h>
|
||||
|
||||
using UniformLambdas = std::list<std::function<void(gpu::Batch& batch)>>;
|
||||
const size_t MAX_PROCEDURAL_TEXTURE_CHANNELS{ 4 };
|
||||
|
||||
// FIXME better encapsulation
|
||||
// FIXME better mechanism for extending to things rendered using shaders other than simple.slv
|
||||
|
@ -29,7 +34,8 @@ struct Procedural {
|
|||
void parse(const QString& userDataJson);
|
||||
void parse(const QJsonObject&);
|
||||
bool ready();
|
||||
void prepare(gpu::Batch& batch, const glm::vec3& size);
|
||||
void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size);
|
||||
void setupUniforms();
|
||||
glm::vec4 getColor(const glm::vec4& entityColor);
|
||||
|
||||
bool _enabled{ false };
|
||||
|
@ -43,17 +49,34 @@ struct Procedural {
|
|||
QUrl _shaderUrl;
|
||||
quint64 _shaderModified{ 0 };
|
||||
bool _pipelineDirty{ true };
|
||||
int32_t _timeSlot{ gpu::Shader::INVALID_LOCATION };
|
||||
int32_t _scaleSlot{ gpu::Shader::INVALID_LOCATION };
|
||||
uint64_t _start{ 0 };
|
||||
NetworkShaderPointer _networkShader;
|
||||
QJsonObject _uniforms;
|
||||
|
||||
enum StandardUniforms {
|
||||
DATE,
|
||||
TIME,
|
||||
FRAME_COUNT,
|
||||
SCALE,
|
||||
POSITION,
|
||||
CHANNEL_RESOLUTION,
|
||||
NUM_STANDARD_UNIFORMS
|
||||
};
|
||||
|
||||
int32_t _standardUniformSlots[NUM_STANDARD_UNIFORMS];
|
||||
|
||||
uint64_t _start{ 0 };
|
||||
int32_t _frameCount{ 0 };
|
||||
NetworkShaderPointer _networkShader;
|
||||
QJsonObject _parsedUniforms;
|
||||
QJsonArray _parsedChannels;
|
||||
|
||||
UniformLambdas _uniforms;
|
||||
NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS];
|
||||
gpu::PipelinePointer _pipeline;
|
||||
gpu::ShaderPointer _vertexShader;
|
||||
gpu::ShaderPointer _fragmentShader;
|
||||
gpu::ShaderPointer _shader;
|
||||
gpu::StatePointer _state;
|
||||
glm::vec3 _entityDimensions;
|
||||
glm::vec3 _entityPosition;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -262,15 +262,39 @@ float snoise(vec2 v) {
|
|||
return 130.0 * dot(m, g);
|
||||
}
|
||||
|
||||
// TODO add more uniforms
|
||||
uniform float iGlobalTime; // shader playback time (in seconds)
|
||||
uniform vec3 iWorldScale; // the dimensions of the object being rendered
|
||||
|
||||
// TODO add support for textures
|
||||
// TODO document available inputs other than the uniforms
|
||||
// TODO provide world scale in addition to the untransformed position
|
||||
// shader playback time (in seconds)
|
||||
uniform float iGlobalTime;
|
||||
// the dimensions of the object being rendered
|
||||
uniform vec3 iWorldScale;
|
||||
|
||||
#define PROCEDURAL 1
|
||||
|
||||
//PROCEDURAL_VERSION
|
||||
|
||||
#ifdef PROCEDURAL_V1
|
||||
|
||||
#else
|
||||
|
||||
// Unimplemented uniforms
|
||||
// Resolution doesn't make sense in the VR context
|
||||
const vec3 iResolution = vec3(1.0);
|
||||
// Mouse functions not enabled currently
|
||||
const vec4 iMouse = vec4(0.0);
|
||||
// No support for audio input
|
||||
const float iSampleRate = 1.0;
|
||||
// No support for video input
|
||||
const vec4 iChannelTime = vec4(0.0);
|
||||
|
||||
|
||||
uniform vec4 iDate;
|
||||
uniform int iFrameCount;
|
||||
uniform vec3 iWorldPosition;
|
||||
uniform vec3 iChannelResolution[4];
|
||||
uniform sampler2D iChannel0;
|
||||
uniform sampler2D iChannel1;
|
||||
uniform sampler2D iChannel2;
|
||||
uniform sampler2D iChannel3;
|
||||
|
||||
#endif
|
||||
|
||||
)SHADER";
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
//
|
||||
// ProceduralSkybox.cpp
|
||||
// libraries/procedural/src/procedural
|
||||
//
|
||||
// Created by Sam Gateau on 9/21/2015.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "ProceduralSkybox.h"
|
||||
|
||||
|
||||
#include <gpu/Batch.h>
|
||||
#include <gpu/Context.h>
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
//
|
||||
// ProceduralSkybox.cpp
|
||||
// libraries/procedural/src/procedural
|
||||
//
|
||||
// Created by Sam Gateau on 9/21/2015.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "ProceduralSkybox.h"
|
||||
|
||||
|
||||
#include <gpu/Batch.h>
|
||||
#include <gpu/Context.h>
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
#include "ProceduralSkybox_vert.h"
|
||||
#include "ProceduralSkybox_frag.h"
|
||||
|
||||
|
@ -74,7 +74,7 @@ void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum,
|
|||
batch.setResourceTexture(0, skybox.getCubemap());
|
||||
}
|
||||
|
||||
skybox._procedural->prepare(batch, glm::vec3(1));
|
||||
skybox._procedural->prepare(batch, glm::vec3(0), glm::vec3(1));
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,11 +120,11 @@ QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& cont
|
|||
}
|
||||
|
||||
// log prefix is in the following format
|
||||
// [DEBUG] [TIMESTAMP] [PID] [TARGET] logged string
|
||||
// [TIMESTAMP] [DEBUG] [PID] [TARGET] logged string
|
||||
|
||||
QString prefixString = QString("[%1]").arg(stringForLogType(type));
|
||||
QString prefixString = QString("[%1]").arg(QDateTime::currentDateTime().toString(DATE_STRING_FORMAT));
|
||||
|
||||
prefixString.append(QString(" [%1]").arg(QDateTime::currentDateTime().toString(DATE_STRING_FORMAT)));
|
||||
prefixString.append(QString(" [%1]").arg(stringForLogType(type)));
|
||||
|
||||
if (_shouldOutputPID) {
|
||||
prefixString.append(QString(" [%1]").arg(QCoreApplication::instance()->applicationPid()));
|
||||
|
|
|
@ -260,7 +260,7 @@ protected:
|
|||
}
|
||||
|
||||
|
||||
void keyPressEvent(QKeyEvent* event) {
|
||||
void keyPressEvent(QKeyEvent* event) override {
|
||||
_altPressed = Qt::Key_Alt == event->key();
|
||||
switch (event->key()) {
|
||||
case Qt::Key_B:
|
||||
|
@ -292,13 +292,13 @@ protected:
|
|||
QWindow::keyPressEvent(event);
|
||||
}
|
||||
QQmlContext* menuContext{ nullptr };
|
||||
void keyReleaseEvent(QKeyEvent *event) {
|
||||
void keyReleaseEvent(QKeyEvent *event) override {
|
||||
if (_altPressed && Qt::Key_Alt == event->key()) {
|
||||
VrMenu::toggle();
|
||||
}
|
||||
}
|
||||
|
||||
void moveEvent(QMoveEvent* event) {
|
||||
void moveEvent(QMoveEvent* event) override {
|
||||
static qreal oldPixelRatio = 0.0;
|
||||
if (devicePixelRatio() != oldPixelRatio) {
|
||||
oldPixelRatio = devicePixelRatio();
|
||||
|
|
|
@ -71,13 +71,13 @@ function createAllToys() {
|
|||
|
||||
createCombinedArmChair({
|
||||
x: 549.29,
|
||||
y: 495.05,
|
||||
y: 494.9,
|
||||
z: 508.22
|
||||
});
|
||||
|
||||
createPottedPlant({
|
||||
x: 554.26,
|
||||
y: 495.23,
|
||||
y: 495.2,
|
||||
z: 504.53
|
||||
});
|
||||
|
||||
|
@ -98,7 +98,7 @@ function createAllToys() {
|
|||
function deleteAllToys() {
|
||||
var entities = Entities.findEntities(MyAvatar.position, 100);
|
||||
|
||||
entities.forEach(function (entity) {
|
||||
entities.forEach(function(entity) {
|
||||
//params: customKey, id, defaultValue
|
||||
var shouldReset = getEntityCustomData(resetKey, entity, {}).resetMe;
|
||||
if (shouldReset === true) {
|
||||
|
@ -447,8 +447,7 @@ function createPingPongBallGun() {
|
|||
var pingPongGun = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: MODEL_URL,
|
||||
shapeType: 'compound',
|
||||
compoundShapeURL: COLLISION_HULL_URL,
|
||||
shapeType: 'box',
|
||||
script: scriptURL,
|
||||
position: position,
|
||||
rotation: rotation,
|
||||
|
@ -458,9 +457,9 @@ function createPingPongBallGun() {
|
|||
z: 0
|
||||
},
|
||||
dimensions: {
|
||||
x: 0.67,
|
||||
y: 0.14,
|
||||
z: 0.09
|
||||
x: 0.08,
|
||||
y: 0.21,
|
||||
z: 0.47
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
});
|
||||
|
@ -468,8 +467,6 @@ function createPingPongBallGun() {
|
|||
setEntityCustomData(resetKey, pingPongGun, {
|
||||
resetMe: true
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
function createBasketballHoop() {
|
||||
|
|
Loading…
Reference in a new issue