mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:24:00 +02:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
01005dc792
27 changed files with 1508 additions and 352 deletions
|
@ -18,7 +18,8 @@ var toolWidth = 50;
|
|||
|
||||
var LASER_WIDTH = 4;
|
||||
var LASER_COLOR = { red: 255, green: 0, blue: 0 };
|
||||
var LASER_LENGTH_FACTOR = 5;
|
||||
var LASER_LENGTH_FACTOR = 500
|
||||
;
|
||||
|
||||
var LEFT = 0;
|
||||
var RIGHT = 1;
|
||||
|
@ -42,6 +43,8 @@ var toolBar;
|
|||
|
||||
var jointList = MyAvatar.getJointNames();
|
||||
|
||||
var mode = 0;
|
||||
|
||||
function isLocked(properties) {
|
||||
// special case to lock the ground plane model in hq.
|
||||
if (location.hostname == "hq.highfidelity.io" &&
|
||||
|
@ -57,6 +60,7 @@ function controller(wichSide) {
|
|||
this.palm = 2 * wichSide;
|
||||
this.tip = 2 * wichSide + 1;
|
||||
this.trigger = wichSide;
|
||||
this.bumper = 6 * wichSide + 5;
|
||||
|
||||
this.oldPalmPosition = Controller.getSpatialControlPosition(this.palm);
|
||||
this.palmPosition = Controller.getSpatialControlPosition(this.palm);
|
||||
|
@ -77,6 +81,7 @@ function controller(wichSide) {
|
|||
this.rotation = this.oldRotation;
|
||||
|
||||
this.triggerValue = Controller.getTriggerValue(this.trigger);
|
||||
this.bumperValue = Controller.isButtonPressed(this.bumper);
|
||||
|
||||
this.pressed = false; // is trigger pressed
|
||||
this.pressing = false; // is trigger being pressed (is pressed now but wasn't previously)
|
||||
|
@ -88,6 +93,11 @@ function controller(wichSide) {
|
|||
this.oldModelPosition;
|
||||
this.oldModelRadius;
|
||||
|
||||
this.positionAtGrab;
|
||||
this.rotationAtGrab;
|
||||
this.modelPositionAtGrab;
|
||||
this.modelRotationAtGrab;
|
||||
|
||||
this.jointsIntersectingFromStart = [];
|
||||
|
||||
this.laser = Overlays.addOverlay("line3d", {
|
||||
|
@ -145,6 +155,11 @@ function controller(wichSide) {
|
|||
this.oldModelRotation = properties.modelRotation;
|
||||
this.oldModelRadius = properties.radius;
|
||||
|
||||
this.positionAtGrab = this.palmPosition;
|
||||
this.rotationAtGrab = this.rotation;
|
||||
this.modelPositionAtGrab = properties.position;
|
||||
this.modelRotationAtGrab = properties.modelRotation;
|
||||
|
||||
this.jointsIntersectingFromStart = [];
|
||||
for (var i = 0; i < jointList.length; i++) {
|
||||
var distance = Vec3.distance(MyAvatar.getJointPosition(jointList[i]), this.oldModelPosition);
|
||||
|
@ -152,6 +167,7 @@ function controller(wichSide) {
|
|||
this.jointsIntersectingFromStart.push(i);
|
||||
}
|
||||
}
|
||||
this.showLaser(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,12 +185,16 @@ function controller(wichSide) {
|
|||
}
|
||||
}
|
||||
|
||||
print("closestJoint: " + jointList[closestJointIndex]);
|
||||
print("closestJointDistance (attach max distance): " + closestJointDistance + " (" + this.oldModelRadius + ")");
|
||||
if (closestJointIndex != -1) {
|
||||
print("closestJoint: " + jointList[closestJointIndex]);
|
||||
print("closestJointDistance (attach max distance): " + closestJointDistance + " (" + this.oldModelRadius + ")");
|
||||
}
|
||||
|
||||
if (closestJointDistance < this.oldModelRadius) {
|
||||
|
||||
if (this.jointsIntersectingFromStart.indexOf(closestJointIndex) != -1) {
|
||||
if (this.jointsIntersectingFromStart.indexOf(closestJointIndex) != -1 ||
|
||||
(leftController.grabbing && rightController.grabbing &&
|
||||
leftController.modelID.id == rightController.modelID.id)) {
|
||||
// Do nothing
|
||||
} else {
|
||||
print("Attaching to " + jointList[closestJointIndex]);
|
||||
|
@ -188,6 +208,7 @@ function controller(wichSide) {
|
|||
MyAvatar.attach(this.modelURL, jointList[closestJointIndex],
|
||||
attachmentOffset, attachmentRotation, 2.0 * this.oldModelRadius,
|
||||
true, false);
|
||||
|
||||
Models.deleteModel(this.modelID);
|
||||
}
|
||||
}
|
||||
|
@ -196,6 +217,7 @@ function controller(wichSide) {
|
|||
this.grabbing = false;
|
||||
this.modelID.isKnownID = false;
|
||||
this.jointsIntersectingFromStart = [];
|
||||
this.showLaser(true);
|
||||
}
|
||||
|
||||
this.checkTrigger = function () {
|
||||
|
@ -246,6 +268,7 @@ function controller(wichSide) {
|
|||
return { valid: false };
|
||||
}
|
||||
|
||||
this.glowedIntersectingModel = { isKnownID: false };
|
||||
this.moveLaser = function () {
|
||||
// the overlays here are anchored to the avatar, which means they are specified in the avatar's local frame
|
||||
|
||||
|
@ -258,46 +281,99 @@ function controller(wichSide) {
|
|||
|
||||
Overlays.editOverlay(this.laser, {
|
||||
position: startPosition,
|
||||
end: endPosition,
|
||||
visible: true
|
||||
end: endPosition
|
||||
});
|
||||
|
||||
|
||||
Overlays.editOverlay(this.ball, {
|
||||
position: endPosition,
|
||||
visible: true
|
||||
position: endPosition
|
||||
});
|
||||
Overlays.editOverlay(this.leftRight, {
|
||||
position: Vec3.sum(endPosition, Vec3.multiply(this.right, 2 * this.guideScale)),
|
||||
end: Vec3.sum(endPosition, Vec3.multiply(this.right, -2 * this.guideScale)),
|
||||
visible: true
|
||||
end: Vec3.sum(endPosition, Vec3.multiply(this.right, -2 * this.guideScale))
|
||||
});
|
||||
Overlays.editOverlay(this.topDown, {position: Vec3.sum(endPosition, Vec3.multiply(this.up, 2 * this.guideScale)),
|
||||
end: Vec3.sum(endPosition, Vec3.multiply(this.up, -2 * this.guideScale)),
|
||||
visible: true
|
||||
end: Vec3.sum(endPosition, Vec3.multiply(this.up, -2 * this.guideScale))
|
||||
});
|
||||
this.showLaser(!this.grabbing || mode == 0);
|
||||
|
||||
if (this.glowedIntersectingModel.isKnownID) {
|
||||
Models.editModel(this.glowedIntersectingModel, { glowLevel: 0.0 });
|
||||
this.glowedIntersectingModel.isKnownID = false;
|
||||
}
|
||||
if (!this.grabbing) {
|
||||
var intersection = Models.findRayIntersection({
|
||||
origin: this.palmPosition,
|
||||
direction: this.front
|
||||
});
|
||||
if (intersection.accurate && intersection.modelID.isKnownID) {
|
||||
this.glowedIntersectingModel = intersection.modelID;
|
||||
Models.editModel(this.glowedIntersectingModel, { glowLevel: 0.25 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.hideLaser = function() {
|
||||
Overlays.editOverlay(this.laser, { visible: false });
|
||||
Overlays.editOverlay(this.ball, { visible: false });
|
||||
Overlays.editOverlay(this.leftRight, { visible: false });
|
||||
Overlays.editOverlay(this.topDown, { visible: false });
|
||||
this.showLaser = function(show) {
|
||||
Overlays.editOverlay(this.laser, { visible: show });
|
||||
Overlays.editOverlay(this.ball, { visible: show });
|
||||
Overlays.editOverlay(this.leftRight, { visible: show });
|
||||
Overlays.editOverlay(this.topDown, { visible: show });
|
||||
}
|
||||
|
||||
this.moveModel = function () {
|
||||
if (this.grabbing) {
|
||||
var newPosition = Vec3.sum(this.palmPosition,
|
||||
Vec3.multiply(this.front, this.x));
|
||||
newPosition = Vec3.sum(newPosition,
|
||||
Vec3.multiply(this.up, this.y));
|
||||
newPosition = Vec3.sum(newPosition,
|
||||
Vec3.multiply(this.right, this.z));
|
||||
if (!this.modelID.isKnownID) {
|
||||
print("Unknown grabbed ID " + this.modelID.id + ", isKnown: " + this.modelID.isKnownID);
|
||||
this.modelID = Models.findRayIntersection({
|
||||
origin: this.palmPosition,
|
||||
direction: this.front
|
||||
}).modelID;
|
||||
print("Identified ID " + this.modelID.id + ", isKnown: " + this.modelID.isKnownID);
|
||||
}
|
||||
var newPosition;
|
||||
var newRotation;
|
||||
|
||||
var newRotation = Quat.multiply(this.rotation,
|
||||
Quat.inverse(this.oldRotation));
|
||||
newRotation = Quat.multiply(newRotation,
|
||||
this.oldModelRotation);
|
||||
switch (mode) {
|
||||
case 0:
|
||||
newPosition = Vec3.sum(this.palmPosition,
|
||||
Vec3.multiply(this.front, this.x));
|
||||
newPosition = Vec3.sum(newPosition,
|
||||
Vec3.multiply(this.up, this.y));
|
||||
newPosition = Vec3.sum(newPosition,
|
||||
Vec3.multiply(this.right, this.z));
|
||||
|
||||
|
||||
newRotation = Quat.multiply(this.rotation,
|
||||
Quat.inverse(this.oldRotation));
|
||||
newRotation = Quat.multiply(newRotation,
|
||||
this.oldModelRotation);
|
||||
break;
|
||||
case 1:
|
||||
var forward = Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1 });
|
||||
var d = Vec3.dot(forward, MyAvatar.position);
|
||||
|
||||
var factor1 = Vec3.dot(forward, this.positionAtGrab) - d;
|
||||
var factor2 = Vec3.dot(forward, this.modelPositionAtGrab) - d;
|
||||
var vector = Vec3.subtract(this.palmPosition, this.positionAtGrab);
|
||||
|
||||
if (factor2 < 0) {
|
||||
factor2 = 0;
|
||||
}
|
||||
if (factor1 <= 0) {
|
||||
factor1 = 1;
|
||||
factor2 = 1;
|
||||
}
|
||||
|
||||
newPosition = Vec3.sum(this.modelPositionAtGrab,
|
||||
Vec3.multiply(vector,
|
||||
factor2 / factor1));
|
||||
|
||||
newRotation = Quat.multiply(this.rotation,
|
||||
Quat.inverse(this.rotationAtGrab));
|
||||
newRotation = Quat.multiply(newRotation,
|
||||
this.modelRotationAtGrab);
|
||||
break;
|
||||
}
|
||||
|
||||
Models.editModel(this.modelID, {
|
||||
position: newPosition,
|
||||
|
@ -341,6 +417,21 @@ function controller(wichSide) {
|
|||
|
||||
this.triggerValue = Controller.getTriggerValue(this.trigger);
|
||||
|
||||
var bumperValue = Controller.isButtonPressed(this.bumper);
|
||||
if (bumperValue && !this.bumperValue) {
|
||||
if (mode == 0) {
|
||||
mode = 1;
|
||||
Overlays.editOverlay(leftController.laser, { color: { red: 0, green: 0, blue: 255 } });
|
||||
Overlays.editOverlay(rightController.laser, { color: { red: 0, green: 0, blue: 255 } });
|
||||
} else {
|
||||
mode = 0;
|
||||
Overlays.editOverlay(leftController.laser, { color: { red: 255, green: 0, blue: 0 } });
|
||||
Overlays.editOverlay(rightController.laser, { color: { red: 255, green: 0, blue: 0 } });
|
||||
}
|
||||
}
|
||||
this.bumperValue = bumperValue;
|
||||
|
||||
|
||||
this.checkTrigger();
|
||||
|
||||
this.moveLaser();
|
||||
|
@ -356,8 +447,12 @@ function controller(wichSide) {
|
|||
var attachmentIndex = -1;
|
||||
var attachmentX = LASER_LENGTH_FACTOR;
|
||||
|
||||
var newModel;
|
||||
var newProperties;
|
||||
|
||||
for (var i = 0; i < attachments.length; ++i) {
|
||||
var position = Vec3.sum(MyAvatar.getJointPosition(attachments[i].jointName), attachments[i].translation);
|
||||
var position = Vec3.sum(MyAvatar.getJointPosition(attachments[i].jointName),
|
||||
Vec3.multiplyQbyV(MyAvatar.getJointCombinedRotation(attachments[i].jointName), attachments[i].translation));
|
||||
var scale = attachments[i].scale;
|
||||
|
||||
var A = this.palmPosition;
|
||||
|
@ -375,53 +470,56 @@ function controller(wichSide) {
|
|||
}
|
||||
|
||||
if (attachmentIndex != -1) {
|
||||
print("Detaching: " + attachments[attachmentIndex].modelURL);
|
||||
MyAvatar.detachOne(attachments[attachmentIndex].modelURL, attachments[attachmentIndex].jointName);
|
||||
Models.addModel({
|
||||
position: Vec3.sum(MyAvatar.getJointPosition(attachments[attachmentIndex].jointName),
|
||||
attachments[attachmentIndex].translation),
|
||||
modelRotation: Quat.multiply(MyAvatar.getJointCombinedRotation(attachments[attachmentIndex].jointName),
|
||||
attachments[attachmentIndex].rotation),
|
||||
radius: attachments[attachmentIndex].scale / 2.0,
|
||||
modelURL: attachments[attachmentIndex].modelURL
|
||||
});
|
||||
}
|
||||
|
||||
// There is none so ...
|
||||
// Checking model tree
|
||||
Vec3.print("Looking at: ", this.palmPosition);
|
||||
var pickRay = { origin: this.palmPosition,
|
||||
direction: Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)) };
|
||||
var foundIntersection = Models.findRayIntersection(pickRay);
|
||||
|
||||
if(!foundIntersection.accurate) {
|
||||
return;
|
||||
}
|
||||
var foundModel = foundIntersection.modelID;
|
||||
|
||||
if (!foundModel.isKnownID) {
|
||||
var identify = Models.identifyModel(foundModel);
|
||||
if (!identify.isKnownID) {
|
||||
print("Unknown ID " + identify.id + " (update loop " + foundModel.id + ")");
|
||||
return;
|
||||
}
|
||||
foundModel = identify;
|
||||
}
|
||||
|
||||
var properties = Models.getModelProperties(foundModel);
|
||||
print("foundModel.modelURL=" + properties.modelURL);
|
||||
|
||||
if (isLocked(properties)) {
|
||||
print("Model locked " + properties.id);
|
||||
|
||||
newProperties = {
|
||||
position: Vec3.sum(MyAvatar.getJointPosition(attachments[attachmentIndex].jointName),
|
||||
Vec3.multiplyQbyV(MyAvatar.getJointCombinedRotation(attachments[attachmentIndex].jointName), attachments[attachmentIndex].translation)),
|
||||
modelRotation: Quat.multiply(MyAvatar.getJointCombinedRotation(attachments[attachmentIndex].jointName),
|
||||
attachments[attachmentIndex].rotation),
|
||||
radius: attachments[attachmentIndex].scale / 2.0,
|
||||
modelURL: attachments[attachmentIndex].modelURL
|
||||
};
|
||||
newModel = Models.addModel(newProperties);
|
||||
} else {
|
||||
print("Checking properties: " + properties.id + " " + properties.isKnownID);
|
||||
var check = this.checkModel(properties);
|
||||
if (check.valid) {
|
||||
this.grab(foundModel, properties);
|
||||
this.x = check.x;
|
||||
this.y = check.y;
|
||||
this.z = check.z;
|
||||
// There is none so ...
|
||||
// Checking model tree
|
||||
Vec3.print("Looking at: ", this.palmPosition);
|
||||
var pickRay = { origin: this.palmPosition,
|
||||
direction: Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)) };
|
||||
var foundIntersection = Models.findRayIntersection(pickRay);
|
||||
|
||||
if(!foundIntersection.accurate) {
|
||||
print("No accurate intersection");
|
||||
return;
|
||||
}
|
||||
newModel = foundIntersection.modelID;
|
||||
|
||||
if (!newModel.isKnownID) {
|
||||
var identify = Models.identifyModel(newModel);
|
||||
if (!identify.isKnownID) {
|
||||
print("Unknown ID " + identify.id + " (update loop " + newModel.id + ")");
|
||||
return;
|
||||
}
|
||||
newModel = identify;
|
||||
}
|
||||
newProperties = Models.getModelProperties(newModel);
|
||||
}
|
||||
|
||||
|
||||
print("foundModel.modelURL=" + newProperties.modelURL);
|
||||
|
||||
if (isLocked(newProperties)) {
|
||||
print("Model locked " + newProperties.id);
|
||||
} else {
|
||||
this.grab(newModel, newProperties);
|
||||
|
||||
var check = this.checkModel(newProperties);
|
||||
this.x = check.x;
|
||||
this.y = check.y;
|
||||
this.z = check.z;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -439,38 +537,74 @@ var rightController = new controller(RIGHT);
|
|||
|
||||
function moveModels() {
|
||||
if (leftController.grabbing && rightController.grabbing && rightController.modelID.id == leftController.modelID.id) {
|
||||
//print("Both controllers");
|
||||
var oldLeftPoint = Vec3.sum(leftController.oldPalmPosition, Vec3.multiply(leftController.oldFront, leftController.x));
|
||||
var oldRightPoint = Vec3.sum(rightController.oldPalmPosition, Vec3.multiply(rightController.oldFront, rightController.x));
|
||||
|
||||
var oldMiddle = Vec3.multiply(Vec3.sum(oldLeftPoint, oldRightPoint), 0.5);
|
||||
var oldLength = Vec3.length(Vec3.subtract(oldLeftPoint, oldRightPoint));
|
||||
var newPosition = leftController.oldModelPosition;
|
||||
var rotation = leftController.oldModelRotation;
|
||||
var ratio = 1;
|
||||
|
||||
|
||||
var leftPoint = Vec3.sum(leftController.palmPosition, Vec3.multiply(leftController.front, leftController.x));
|
||||
var rightPoint = Vec3.sum(rightController.palmPosition, Vec3.multiply(rightController.front, rightController.x));
|
||||
|
||||
var middle = Vec3.multiply(Vec3.sum(leftPoint, rightPoint), 0.5);
|
||||
var length = Vec3.length(Vec3.subtract(leftPoint, rightPoint));
|
||||
|
||||
var ratio = length / oldLength;
|
||||
|
||||
var newPosition = Vec3.sum(middle,
|
||||
Vec3.multiply(Vec3.subtract(leftController.oldModelPosition, oldMiddle), ratio));
|
||||
//Vec3.print("Ratio : " + ratio + " New position: ", newPosition);
|
||||
var rotation = Quat.multiply(leftController.rotation,
|
||||
Quat.inverse(leftController.oldRotation));
|
||||
rotation = Quat.multiply(rotation, leftController.oldModelRotation);
|
||||
switch (mode) {
|
||||
case 0:
|
||||
var oldLeftPoint = Vec3.sum(leftController.oldPalmPosition, Vec3.multiply(leftController.oldFront, leftController.x));
|
||||
var oldRightPoint = Vec3.sum(rightController.oldPalmPosition, Vec3.multiply(rightController.oldFront, rightController.x));
|
||||
|
||||
var oldMiddle = Vec3.multiply(Vec3.sum(oldLeftPoint, oldRightPoint), 0.5);
|
||||
var oldLength = Vec3.length(Vec3.subtract(oldLeftPoint, oldRightPoint));
|
||||
|
||||
|
||||
var leftPoint = Vec3.sum(leftController.palmPosition, Vec3.multiply(leftController.front, leftController.x));
|
||||
var rightPoint = Vec3.sum(rightController.palmPosition, Vec3.multiply(rightController.front, rightController.x));
|
||||
|
||||
var middle = Vec3.multiply(Vec3.sum(leftPoint, rightPoint), 0.5);
|
||||
var length = Vec3.length(Vec3.subtract(leftPoint, rightPoint));
|
||||
|
||||
|
||||
ratio = length / oldLength;
|
||||
newPosition = Vec3.sum(middle,
|
||||
Vec3.multiply(Vec3.subtract(leftController.oldModelPosition, oldMiddle), ratio));
|
||||
break;
|
||||
case 1:
|
||||
var u = Vec3.normalize(Vec3.subtract(rightController.oldPalmPosition, leftController.oldPalmPosition));
|
||||
var v = Vec3.normalize(Vec3.subtract(rightController.palmPosition, leftController.palmPosition));
|
||||
|
||||
var cos_theta = Vec3.dot(u, v);
|
||||
if (cos_theta > 1) {
|
||||
cos_theta = 1;
|
||||
}
|
||||
var angle = Math.acos(cos_theta) / Math.PI * 180;
|
||||
if (angle < 0.1) {
|
||||
return;
|
||||
|
||||
}
|
||||
var w = Vec3.normalize(Vec3.cross(u, v));
|
||||
|
||||
rotation = Quat.multiply(Quat.angleAxis(angle, w), leftController.oldModelRotation);
|
||||
|
||||
|
||||
leftController.positionAtGrab = leftController.palmPosition;
|
||||
leftController.rotationAtGrab = leftController.rotation;
|
||||
leftController.modelPositionAtGrab = leftController.oldModelPosition;
|
||||
leftController.modelRotationAtGrab = rotation;
|
||||
|
||||
rightController.positionAtGrab = rightController.palmPosition;
|
||||
rightController.rotationAtGrab = rightController.rotation;
|
||||
rightController.modelPositionAtGrab = rightController.oldModelPosition;
|
||||
rightController.modelRotationAtGrab = rotation;
|
||||
break;
|
||||
}
|
||||
|
||||
Models.editModel(leftController.modelID, {
|
||||
position: newPosition,
|
||||
//modelRotation: rotation,
|
||||
modelRotation: rotation,
|
||||
radius: leftController.oldModelRadius * ratio
|
||||
});
|
||||
|
||||
leftController.oldModelPosition = newPosition;
|
||||
leftController.oldModelRotation = rotation;
|
||||
leftController.oldModelRadius *= ratio;
|
||||
|
||||
rightController.oldModelPosition = newPosition;
|
||||
rightController.oldModelRotation = rotation;
|
||||
rightController.oldModelRadius *= ratio;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -498,8 +632,8 @@ function checkController(deltaTime) {
|
|||
if (hydraConnected) {
|
||||
hydraConnected = false;
|
||||
|
||||
leftController.hideLaser();
|
||||
rightController.hideLaser();
|
||||
leftController.showLaser(false);
|
||||
rightController.showLaser(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -510,7 +644,7 @@ function initToolBar() {
|
|||
toolBar = new ToolBar(0, 0, ToolBar.VERTICAL);
|
||||
// New Model
|
||||
newModel = toolBar.addTool({
|
||||
imageURL: toolIconUrl + "voxel-tool.svg",
|
||||
imageURL: toolIconUrl + "add-model-tool.svg",
|
||||
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
|
||||
width: toolWidth, height: toolHeight,
|
||||
visible: true,
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "Camera.h"
|
||||
#include "Menu.h"
|
||||
#include "Util.h"
|
||||
#include "devices/OculusManager.h"
|
||||
|
||||
const float CAMERA_FIRST_PERSON_MODE_UP_SHIFT = 0.0f;
|
||||
const float CAMERA_FIRST_PERSON_MODE_DISTANCE = 0.0f;
|
||||
|
@ -264,7 +265,12 @@ PickRay CameraScriptableObject::computePickRay(float x, float y) {
|
|||
float screenWidth = Application::getInstance()->getGLWidget()->width();
|
||||
float screenHeight = Application::getInstance()->getGLWidget()->height();
|
||||
PickRay result;
|
||||
_viewFrustum->computePickRay(x / screenWidth, y / screenHeight, result.origin, result.direction);
|
||||
if (OculusManager::isConnected()) {
|
||||
result.origin = _camera->getPosition();
|
||||
Application::getInstance()->getApplicationOverlay().computeOculusPickRay(x / screenWidth, y / screenHeight, result.direction);
|
||||
} else {
|
||||
_viewFrustum->computePickRay(x / screenWidth, y / screenHeight, result.origin, result.direction);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -353,6 +353,7 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderSkeletonCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderHeadCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderBoundingCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::CollideAsRagDoll);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu,
|
||||
|
|
|
@ -311,6 +311,7 @@ namespace MenuOption {
|
|||
const QString CascadedShadows = "Cascaded";
|
||||
const QString Chat = "Chat...";
|
||||
const QString ChatCircling = "Chat Circling";
|
||||
const QString CollideAsRagDoll = "Collide As RagDoll";
|
||||
const QString CollideWithAvatars = "Collide With Avatars";
|
||||
const QString CollideWithEnvironment = "Collide With World Boundaries";
|
||||
const QString CollideWithParticles = "Collide With Particles";
|
||||
|
|
|
@ -30,7 +30,7 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
}
|
||||
setTranslation(neckPosition);
|
||||
glm::quat neckParentRotation;
|
||||
if (!owningAvatar->getSkeletonModel().getNeckParentRotation(neckParentRotation)) {
|
||||
if (!owningAvatar->getSkeletonModel().getNeckParentRotationFromDefaultOrientation(neckParentRotation)) {
|
||||
neckParentRotation = owningAvatar->getOrientation();
|
||||
}
|
||||
setRotation(neckParentRotation);
|
||||
|
|
|
@ -23,7 +23,10 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar) :
|
|||
|
||||
void SkeletonModel::setJointStates(QVector<JointState> states) {
|
||||
Model::setJointStates(states);
|
||||
_ragDoll.init(_jointStates);
|
||||
|
||||
if (isActive() && _owningAvatar->isMyAvatar()) {
|
||||
_ragDoll.init(_jointStates);
|
||||
}
|
||||
}
|
||||
|
||||
const float PALM_PRIORITY = 3.0f;
|
||||
|
@ -88,7 +91,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
}
|
||||
|
||||
void SkeletonModel::simulateRagDoll(float deltaTime) {
|
||||
_ragDoll.slaveToSkeleton(_jointStates, 0.5f);
|
||||
_ragDoll.slaveToSkeleton(_jointStates, 0.1f); // fraction = 0.1f left intentionally low for demo purposes
|
||||
|
||||
float MIN_CONSTRAINT_ERROR = 0.005f; // 5mm
|
||||
int MAX_ITERATIONS = 4;
|
||||
|
@ -141,7 +144,9 @@ void SkeletonModel::getBodyShapes(QVector<const Shape*>& shapes) const {
|
|||
void SkeletonModel::renderIKConstraints() {
|
||||
renderJointConstraints(getRightHandJointIndex());
|
||||
renderJointConstraints(getLeftHandJointIndex());
|
||||
renderRagDoll();
|
||||
//if (isActive() && _owningAvatar->isMyAvatar()) {
|
||||
// renderRagDoll();
|
||||
//}
|
||||
}
|
||||
|
||||
class IndexValue {
|
||||
|
@ -188,45 +193,30 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
|||
if (parentJointIndex == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// rotate palm to align with its normal (normal points out of hand's palm)
|
||||
glm::quat palmRotation;
|
||||
glm::quat r0, r1;
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::AlternateIK) &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
|
||||
JointState parentState = _jointStates[parentJointIndex];
|
||||
palmRotation = parentState.getRotationFromBindToModelFrame();
|
||||
r0 = palmRotation;
|
||||
} else {
|
||||
JointState state = _jointStates[jointIndex];
|
||||
palmRotation = state.getRotationFromBindToModelFrame();
|
||||
}
|
||||
glm::quat inverseRotation = glm::inverse(_rotation);
|
||||
glm::vec3 palmNormal = inverseRotation * palm.getNormal();
|
||||
palmRotation = rotationBetween(palmRotation * geometry.palmDirection, palmNormal) * palmRotation;
|
||||
r1 = palmRotation;
|
||||
|
||||
// rotate palm to align with finger direction
|
||||
glm::vec3 direction = inverseRotation * palm.getFingerDirection();
|
||||
palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction) * palmRotation;
|
||||
|
||||
// set hand position, rotation
|
||||
glm::vec3 palmPosition = inverseRotation * (palm.getPosition() - _translation);
|
||||
glm::vec3 palmNormal = inverseRotation * palm.getNormal();
|
||||
glm::vec3 fingerDirection = inverseRotation * palm.getFingerDirection();
|
||||
|
||||
glm::quat palmRotation = rotationBetween(geometry.palmDirection, palmNormal);
|
||||
palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), fingerDirection) * palmRotation;
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::AlternateIK)) {
|
||||
setHandPosition(jointIndex, palmPosition, palmRotation);
|
||||
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
|
||||
glm::vec3 forearmVector = palmRotation * glm::vec3(sign, 0.0f, 0.0f);
|
||||
setJointPosition(parentJointIndex, palmPosition + forearmVector *
|
||||
geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale),
|
||||
float forearmLength = geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale);
|
||||
glm::vec3 forearm = palmRotation * glm::vec3(sign * forearmLength, 0.0f, 0.0f);
|
||||
setJointPosition(parentJointIndex, palmPosition + forearm,
|
||||
glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
|
||||
JointState& parentState = _jointStates[parentJointIndex];
|
||||
parentState.setRotationFromBindFrame(palmRotation, PALM_PRIORITY);
|
||||
// lock hand to forearm by slamming its rotation (in parent-frame) to identity
|
||||
_jointStates[jointIndex]._rotationInParentFrame = glm::quat();
|
||||
} else {
|
||||
setJointPosition(jointIndex, palmPosition, palmRotation,
|
||||
true, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
|
||||
setJointPosition(jointIndex, palmPosition, palmRotation,
|
||||
true, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,6 +244,15 @@ void SkeletonModel::updateJointState(int index) {
|
|||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::updateShapePositions() {
|
||||
if (isActive() && _owningAvatar->isMyAvatar() &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagDoll)) {
|
||||
_ragDoll.updateShapes(_jointShapes, _rotation, _translation);
|
||||
} else {
|
||||
Model::updateShapePositions();
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
if (!_owningAvatar->isMyAvatar() || Application::getInstance()->getPrioVR()->isActive()) {
|
||||
return;
|
||||
|
@ -435,7 +434,7 @@ bool SkeletonModel::getNeckPosition(glm::vec3& neckPosition) const {
|
|||
return isActive() && getJointPositionInWorldFrame(_geometry->getFBXGeometry().neckJointIndex, neckPosition);
|
||||
}
|
||||
|
||||
bool SkeletonModel::getNeckParentRotation(glm::quat& neckParentRotation) const {
|
||||
bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const {
|
||||
if (!isActive()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -443,7 +442,13 @@ bool SkeletonModel::getNeckParentRotation(glm::quat& neckParentRotation) const {
|
|||
if (geometry.neckJointIndex == -1) {
|
||||
return false;
|
||||
}
|
||||
return getJointRotationInWorldFrame(geometry.joints.at(geometry.neckJointIndex).parentIndex, neckParentRotation);
|
||||
int parentIndex = geometry.joints.at(geometry.neckJointIndex).parentIndex;
|
||||
glm::quat worldFrameRotation;
|
||||
if (getJointRotationInWorldFrame(parentIndex, worldFrameRotation)) {
|
||||
neckParentRotation = worldFrameRotation * _jointStates[parentIndex].getFBXJoint().inverseDefaultRotation;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
|
||||
|
|
|
@ -29,6 +29,7 @@ public:
|
|||
|
||||
void simulate(float deltaTime, bool fullUpdate = true);
|
||||
void simulateRagDoll(float deltaTime);
|
||||
void updateShapePositions();
|
||||
|
||||
/// \param jointIndex index of hand joint
|
||||
/// \param shapes[out] list in which is stored pointers to hand shapes
|
||||
|
@ -85,9 +86,9 @@ public:
|
|||
/// \return whether or not the neck was found
|
||||
bool getNeckPosition(glm::vec3& neckPosition) const;
|
||||
|
||||
/// Returns the rotation of the neck joint's parent.
|
||||
/// Returns the rotation of the neck joint's parent from default orientation
|
||||
/// \return whether or not the neck was found
|
||||
bool getNeckParentRotation(glm::quat& neckRotation) const;
|
||||
bool getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const;
|
||||
|
||||
/// Retrieve the positions of up to two eye meshes.
|
||||
/// \return whether or not both eye meshes were found
|
||||
|
|
|
@ -57,21 +57,29 @@ void ModelTreeRenderer::render(RenderMode renderMode) {
|
|||
|
||||
const FBXGeometry* ModelTreeRenderer::getGeometryForModel(const ModelItem& modelItem) {
|
||||
const FBXGeometry* result = NULL;
|
||||
|
||||
Model* model = getModel(modelItem);
|
||||
if (model) {
|
||||
result = &model->getGeometry()->getFBXGeometry();
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) {
|
||||
Model* model = NULL;
|
||||
|
||||
|
||||
if (modelItem.isKnownID()) {
|
||||
if (_knownModelsItemModels.find(modelItem.getID()) != _knownModelsItemModels.end()) {
|
||||
model = _knownModelsItemModels[modelItem.getID()];
|
||||
} else {
|
||||
|
||||
// Make sure we only create new models on the thread that owns the ModelTreeRenderer
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(Model*, model), Q_ARG(const ModelItem&, modelItem));
|
||||
return model;
|
||||
}
|
||||
|
||||
model = new Model();
|
||||
model->init();
|
||||
model->setURL(QUrl(modelItem.getModelURL()));
|
||||
|
@ -81,6 +89,13 @@ Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) {
|
|||
if (_unknownModelsItemModels.find(modelItem.getCreatorTokenID()) != _unknownModelsItemModels.end()) {
|
||||
model = _unknownModelsItemModels[modelItem.getCreatorTokenID()];
|
||||
} else {
|
||||
// Make sure we only create new models on the thread that owns the ModelTreeRenderer
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(Model*, model), Q_ARG(const ModelItem&, modelItem));
|
||||
return model;
|
||||
}
|
||||
|
||||
model = new Model();
|
||||
model->init();
|
||||
model->setURL(QUrl(modelItem.getModelURL()));
|
||||
|
@ -187,92 +202,122 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
|||
|
||||
if (drawAsModel) {
|
||||
glPushMatrix();
|
||||
{
|
||||
const float alpha = 1.0f;
|
||||
|
||||
Model* model = getModel(modelItem);
|
||||
|
||||
model->setScaleToFit(true, radius * 2.0f);
|
||||
model->setSnapModelToCenter(true);
|
||||
|
||||
// set the rotation
|
||||
glm::quat rotation = modelItem.getModelRotation();
|
||||
model->setRotation(rotation);
|
||||
|
||||
// set the position
|
||||
model->setTranslation(position);
|
||||
|
||||
// handle animations..
|
||||
if (modelItem.hasAnimation()) {
|
||||
if (!modelItem.jointsMapped()) {
|
||||
QStringList modelJointNames = model->getJointNames();
|
||||
modelItem.mapJoints(modelJointNames);
|
||||
if (model) {
|
||||
model->setScaleToFit(true, radius * 2.0f);
|
||||
model->setSnapModelToCenter(true);
|
||||
|
||||
// set the rotation
|
||||
glm::quat rotation = modelItem.getModelRotation();
|
||||
model->setRotation(rotation);
|
||||
|
||||
// set the position
|
||||
model->setTranslation(position);
|
||||
|
||||
// handle animations..
|
||||
if (modelItem.hasAnimation()) {
|
||||
if (!modelItem.jointsMapped()) {
|
||||
QStringList modelJointNames = model->getJointNames();
|
||||
modelItem.mapJoints(modelJointNames);
|
||||
}
|
||||
|
||||
QVector<glm::quat> frameData = modelItem.getAnimationFrame();
|
||||
for (int i = 0; i < frameData.size(); i++) {
|
||||
model->setJointState(i, true, frameData[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// make sure to simulate so everything gets set up correctly for rendering
|
||||
model->simulate(0.0f);
|
||||
|
||||
// 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;
|
||||
|
||||
if (modelItem.getGlowLevel() > 0.0f) {
|
||||
Glower glower(modelItem.getGlowLevel());
|
||||
|
||||
if (model->isActive()) {
|
||||
model->render(alpha, modelRenderMode);
|
||||
} else {
|
||||
// if we couldn't get a model, then just draw a sphere
|
||||
glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]);
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glutSolidSphere(radius, 15, 15);
|
||||
glPopMatrix();
|
||||
}
|
||||
} else {
|
||||
if (model->isActive()) {
|
||||
model->render(alpha, modelRenderMode);
|
||||
} else {
|
||||
// if we couldn't get a model, then just draw a sphere
|
||||
glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]);
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glutSolidSphere(radius, 15, 15);
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
QVector<glm::quat> frameData = modelItem.getAnimationFrame();
|
||||
for (int i = 0; i < frameData.size(); i++) {
|
||||
model->setJointState(i, true, frameData[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// make sure to simulate so everything gets set up correctly for rendering
|
||||
model->simulate(0.0f);
|
||||
if (!isShadowMode && displayModelBounds) {
|
||||
|
||||
// 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;
|
||||
|
||||
if (modelItem.getGlowLevel() > 0.0f) {
|
||||
Glower glower(modelItem.getGlowLevel());
|
||||
model->render(alpha, modelRenderMode);
|
||||
glm::vec3 unRotatedMinimum = model->getUnscaledMeshExtents().minimum;
|
||||
glm::vec3 unRotatedMaximum = model->getUnscaledMeshExtents().maximum;
|
||||
glm::vec3 unRotatedExtents = unRotatedMaximum - unRotatedMinimum;
|
||||
|
||||
float width = unRotatedExtents.x;
|
||||
float height = unRotatedExtents.y;
|
||||
float depth = unRotatedExtents.z;
|
||||
|
||||
Extents rotatedExtents = model->getUnscaledMeshExtents();
|
||||
calculateRotatedExtents(rotatedExtents, rotation);
|
||||
|
||||
glm::vec3 rotatedSize = rotatedExtents.maximum - rotatedExtents.minimum;
|
||||
|
||||
const glm::vec3& modelScale = model->getScale();
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
|
||||
// draw the orignal bounding cube
|
||||
glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
|
||||
glutWireCube(size);
|
||||
|
||||
// draw the rotated bounding cube
|
||||
glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
glPushMatrix();
|
||||
glScalef(rotatedSize.x * modelScale.x, rotatedSize.y * modelScale.y, rotatedSize.z * modelScale.z);
|
||||
glutWireCube(1.0);
|
||||
glPopMatrix();
|
||||
|
||||
// draw the model relative bounding box
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glScalef(width * modelScale.x, height * modelScale.y, depth * modelScale.z);
|
||||
glColor3f(0.0f, 1.0f, 0.0f);
|
||||
glutWireCube(1.0);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
}
|
||||
} else {
|
||||
model->render(alpha, modelRenderMode);
|
||||
}
|
||||
|
||||
if (!isShadowMode && displayModelBounds) {
|
||||
|
||||
glm::vec3 unRotatedMinimum = model->getUnscaledMeshExtents().minimum;
|
||||
glm::vec3 unRotatedMaximum = model->getUnscaledMeshExtents().maximum;
|
||||
glm::vec3 unRotatedExtents = unRotatedMaximum - unRotatedMinimum;
|
||||
|
||||
float width = unRotatedExtents.x;
|
||||
float height = unRotatedExtents.y;
|
||||
float depth = unRotatedExtents.z;
|
||||
|
||||
Extents rotatedExtents = model->getUnscaledMeshExtents();
|
||||
calculateRotatedExtents(rotatedExtents, rotation);
|
||||
|
||||
glm::vec3 rotatedSize = rotatedExtents.maximum - rotatedExtents.minimum;
|
||||
|
||||
const glm::vec3& modelScale = model->getScale();
|
||||
|
||||
// if we couldn't get a model, then just draw a sphere
|
||||
glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]);
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
|
||||
// draw the orignal bounding cube
|
||||
glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
|
||||
glutWireCube(size);
|
||||
|
||||
// draw the rotated bounding cube
|
||||
glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
glPushMatrix();
|
||||
glScalef(rotatedSize.x * modelScale.x, rotatedSize.y * modelScale.y, rotatedSize.z * modelScale.z);
|
||||
glutWireCube(1.0);
|
||||
glPopMatrix();
|
||||
|
||||
// draw the model relative bounding box
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glScalef(width * modelScale.x, height * modelScale.y, depth * modelScale.z);
|
||||
glColor3f(0.0f, 1.0f, 0.0f);
|
||||
glutWireCube(1.0);
|
||||
|
||||
glutSolidSphere(radius, 15, 15);
|
||||
glPopMatrix();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
glPopMatrix();
|
||||
} else {
|
||||
glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]);
|
||||
//glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]);
|
||||
glColor3f(1.0f, 0.0f, 0.0f);
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glutSolidSphere(radius, 15, 15);
|
||||
|
|
|
@ -1275,8 +1275,8 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const gl
|
|||
if (useRotation) {
|
||||
JointState& state = _jointStates[jointIndex];
|
||||
|
||||
state.setRotation(rotation, true, priority);
|
||||
endRotation = state.getRotation();
|
||||
state.setRotationFromBindFrame(rotation, priority);
|
||||
endRotation = state.getRotationFromBindToModelFrame();
|
||||
}
|
||||
|
||||
// then, we go from the joint upwards, rotating the end as close as possible to the target
|
||||
|
|
|
@ -140,7 +140,7 @@ public:
|
|||
void clearShapes();
|
||||
void rebuildShapes();
|
||||
void resetShapePositions();
|
||||
void updateShapePositions();
|
||||
virtual void updateShapePositions();
|
||||
void renderJointCollisionShapes(float alpha);
|
||||
void renderBoundingCollisionShapes(float alpha);
|
||||
|
||||
|
|
|
@ -15,13 +15,15 @@
|
|||
|
||||
#include <CollisionInfo.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <CapsuleShape.h>
|
||||
#include <SphereShape.h>
|
||||
|
||||
#include "RagDoll.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// FixedConstraint
|
||||
// ----------------------------------------------------------------------------
|
||||
FixedConstraint::FixedConstraint() : _point(NULL), _anchor(0.0f, 0.0f, 0.0f) {
|
||||
FixedConstraint::FixedConstraint(glm::vec3* point, const glm::vec3& anchor) : _point(point), _anchor(anchor) {
|
||||
}
|
||||
|
||||
float FixedConstraint::enforce() {
|
||||
|
@ -42,9 +44,9 @@ void FixedConstraint::setAnchor(const glm::vec3& anchor) {
|
|||
// ----------------------------------------------------------------------------
|
||||
// DistanceConstraint
|
||||
// ----------------------------------------------------------------------------
|
||||
DistanceConstraint::DistanceConstraint(glm::vec3* pointA, glm::vec3* pointB) : _distance(-1.0f) {
|
||||
_points[0] = pointA;
|
||||
_points[1] = pointB;
|
||||
DistanceConstraint::DistanceConstraint(glm::vec3* startPoint, glm::vec3* endPoint) : _distance(-1.0f) {
|
||||
_points[0] = startPoint;
|
||||
_points[1] = endPoint;
|
||||
_distance = glm::distance(*(_points[0]), *(_points[1]));
|
||||
}
|
||||
|
||||
|
@ -70,6 +72,28 @@ float DistanceConstraint::enforce() {
|
|||
return glm::abs(newDistance - _distance);
|
||||
}
|
||||
|
||||
void DistanceConstraint::updateProxyShape(Shape* shape, const glm::quat& rotation, const glm::vec3& translation) const {
|
||||
if (!shape) {
|
||||
return;
|
||||
}
|
||||
switch (shape->getType()) {
|
||||
case Shape::SPHERE_SHAPE: {
|
||||
// sphere collides at endPoint
|
||||
SphereShape* sphere = static_cast<SphereShape*>(shape);
|
||||
sphere->setPosition(translation + rotation * (*_points[1]));
|
||||
}
|
||||
break;
|
||||
case Shape::CAPSULE_SHAPE: {
|
||||
// capsule collides from startPoint to endPoint
|
||||
CapsuleShape* capsule = static_cast<CapsuleShape*>(shape);
|
||||
capsule->setEndPoints(translation + rotation * (*_points[0]), translation + rotation * (*_points[1]));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// RagDoll
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -90,12 +114,16 @@ void RagDoll::init(const QVector<JointState>& states) {
|
|||
_points.push_back(state.getPosition());
|
||||
int parentIndex = state.getFBXJoint().parentIndex;
|
||||
assert(parentIndex < i);
|
||||
if (parentIndex != -1) {
|
||||
if (parentIndex == -1) {
|
||||
FixedConstraint* anchor = new FixedConstraint(&(_points[i]), glm::vec3(0.0f));
|
||||
_constraints.push_back(anchor);
|
||||
} else {
|
||||
DistanceConstraint* stick = new DistanceConstraint(&(_points[i]), &(_points[parentIndex]));
|
||||
_constraints.push_back(stick);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete all data.
|
||||
void RagDoll::clear() {
|
||||
int numConstraints = _constraints.size();
|
||||
|
@ -129,3 +157,11 @@ float RagDoll::enforceConstraints() {
|
|||
}
|
||||
return maxDistance;
|
||||
}
|
||||
|
||||
void RagDoll::updateShapes(const QVector<Shape*>& shapes, const glm::quat& rotation, const glm::vec3& translation) const {
|
||||
int numShapes = shapes.size();
|
||||
int numConstraints = _constraints.size();
|
||||
for (int i = 0; i < numShapes && i < numConstraints; ++i) {
|
||||
_constraints[i]->updateProxyShape(shapes[i], rotation, translation);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include "renderer/Model.h"
|
||||
|
||||
class Shape;
|
||||
|
||||
class Constraint {
|
||||
public:
|
||||
Constraint() {}
|
||||
|
@ -22,11 +24,20 @@ public:
|
|||
/// Enforce contraint by moving relevant points.
|
||||
/// \return max distance of point movement
|
||||
virtual float enforce() = 0;
|
||||
|
||||
/// \param shape pointer to shape that will be this Constraint's collision proxy
|
||||
/// \param rotation rotation into shape's collision frame
|
||||
/// \param translation translation into shape's collision frame
|
||||
/// Moves the shape such that it will collide at this constraint's position
|
||||
virtual void updateProxyShape(Shape* shape, const glm::quat& rotation, const glm::vec3& translation) const {}
|
||||
|
||||
protected:
|
||||
int _type;
|
||||
};
|
||||
|
||||
class FixedConstraint : public Constraint {
|
||||
public:
|
||||
FixedConstraint();
|
||||
FixedConstraint(glm::vec3* point, const glm::vec3& anchor);
|
||||
float enforce();
|
||||
void setPoint(glm::vec3* point);
|
||||
void setAnchor(const glm::vec3& anchor);
|
||||
|
@ -37,10 +48,11 @@ private:
|
|||
|
||||
class DistanceConstraint : public Constraint {
|
||||
public:
|
||||
DistanceConstraint(glm::vec3* pointA, glm::vec3* pointB);
|
||||
DistanceConstraint(glm::vec3* startPoint, glm::vec3* endPoint);
|
||||
DistanceConstraint(const DistanceConstraint& other);
|
||||
float enforce();
|
||||
void setDistance(float distance);
|
||||
void updateProxyShape(Shape* shape, const glm::quat& rotation, const glm::vec3& translation) const;
|
||||
private:
|
||||
float _distance;
|
||||
glm::vec3* _points[2];
|
||||
|
@ -69,7 +81,12 @@ public:
|
|||
float enforceConstraints();
|
||||
|
||||
const QVector<glm::vec3>& getPoints() const { return _points; }
|
||||
|
||||
|
||||
/// \param shapes list of shapes to be updated with new positions
|
||||
/// \param rotation rotation into shapes' collision frame
|
||||
/// \param translation translation into shapes' collision frame
|
||||
void updateShapes(const QVector<Shape*>& shapes, const glm::quat& rotation, const glm::vec3& translation) const;
|
||||
|
||||
private:
|
||||
QVector<Constraint*> _constraints;
|
||||
QVector<glm::vec3> _points;
|
||||
|
|
|
@ -297,6 +297,25 @@ void ApplicationOverlay::displayOverlayTexture(Camera& whichCamera) {
|
|||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
const float textureFov = PI / 2.5f;
|
||||
|
||||
void ApplicationOverlay::computeOculusPickRay(float x, float y, glm::vec3& direction) const {
|
||||
glm::quat rot = Application::getInstance()->getAvatar()->getOrientation();
|
||||
|
||||
//invert y direction
|
||||
y = 1.0 - y;
|
||||
|
||||
//Get position on hemisphere UI
|
||||
x = sin((x - 0.5f) * textureFov);
|
||||
y = sin((y - 0.5f) * textureFov);
|
||||
|
||||
float dist = sqrt(x * x + y * y);
|
||||
float z = -sqrt(1.0f - dist * dist);
|
||||
|
||||
//Rotate the UI pick ray by the avatar orientation
|
||||
direction = glm::normalize(rot * glm::vec3(x, y, z));
|
||||
}
|
||||
|
||||
// Fast helper functions
|
||||
inline float max(float a, float b) {
|
||||
return (a > b) ? a : b;
|
||||
|
@ -306,8 +325,6 @@ inline float min(float a, float b) {
|
|||
return (a < b) ? a : b;
|
||||
}
|
||||
|
||||
const float textureFov = PI / 2.5f;
|
||||
|
||||
// Draws the FBO texture for Oculus rift. TODO: Draw a curved texture instead of plane.
|
||||
void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
class Overlays;
|
||||
class QOpenGLFramebufferObject;
|
||||
|
||||
// Handles the drawing of the overlays to the scree
|
||||
// Handles the drawing of the overlays to the screen
|
||||
class ApplicationOverlay {
|
||||
public:
|
||||
|
||||
|
@ -27,6 +27,7 @@ public:
|
|||
void renderOverlay(bool renderToTexture = false);
|
||||
void displayOverlayTexture(Camera& whichCamera);
|
||||
void displayOverlayTextureOculus(Camera& whichCamera);
|
||||
void computeOculusPickRay(float x, float y, glm::vec3& direction) const;
|
||||
|
||||
// Getters
|
||||
QOpenGLFramebufferObject* getFramebufferObject();
|
||||
|
|
|
@ -55,7 +55,9 @@ int PositionalAudioRingBuffer::parseData(const QByteArray& packet) {
|
|||
|
||||
readBytes += sizeof(int16_t);
|
||||
|
||||
addSilentFrame(numSilentSamples);
|
||||
if (numSilentSamples > 0) {
|
||||
addSilentFrame(numSilentSamples);
|
||||
}
|
||||
} else {
|
||||
// there is audio data to read
|
||||
readBytes += writeData(packet.data() + readBytes, packet.size() - readBytes);
|
||||
|
|
|
@ -894,14 +894,15 @@ FBXBlendshape extractBlendshape(const FBXNode& object) {
|
|||
}
|
||||
|
||||
void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) {
|
||||
glm::vec3 normal = glm::normalize(mesh.normals.at(firstIndex));
|
||||
const glm::vec3& normal = mesh.normals.at(firstIndex);
|
||||
glm::vec3 bitangent = glm::cross(normal, mesh.vertices.at(secondIndex) - mesh.vertices.at(firstIndex));
|
||||
if (glm::length(bitangent) < EPSILON) {
|
||||
return;
|
||||
}
|
||||
glm::vec2 texCoordDelta = mesh.texCoords.at(secondIndex) - mesh.texCoords.at(firstIndex);
|
||||
mesh.tangents[firstIndex] += glm::cross(glm::angleAxis(
|
||||
- atan2f(-texCoordDelta.t, texCoordDelta.s), normal) * glm::normalize(bitangent), normal);
|
||||
glm::vec3 normalizedNormal = glm::normalize(normal);
|
||||
mesh.tangents[firstIndex] += glm::cross(glm::angleAxis(-atan2f(-texCoordDelta.t, texCoordDelta.s), normalizedNormal) *
|
||||
glm::normalize(bitangent), normalizedNormal);
|
||||
}
|
||||
|
||||
QVector<int> getIndices(const QVector<QString> ids, QVector<QString> modelIDs) {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <QCryptographicHash>
|
||||
#include <QDataStream>
|
||||
#include <QMetaType>
|
||||
#include <QScriptValueIterator>
|
||||
#include <QUrl>
|
||||
#include <QtDebug>
|
||||
|
||||
|
@ -30,6 +31,7 @@ REGISTER_SIMPLE_TYPE_STREAMER(uint)
|
|||
REGISTER_SIMPLE_TYPE_STREAMER(float)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(QByteArray)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(QColor)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(QScriptValue)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(QString)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(QUrl)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(QVariantList)
|
||||
|
@ -79,10 +81,6 @@ IDStreamer& IDStreamer::operator>>(int& value) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
static QByteArray getEnumName(const QMetaEnum& metaEnum) {
|
||||
return QByteArray(metaEnum.scope()) + "::" + metaEnum.name();
|
||||
}
|
||||
|
||||
int Bitstream::registerMetaObject(const char* className, const QMetaObject* metaObject) {
|
||||
getMetaObjects().insert(className, metaObject);
|
||||
|
||||
|
@ -90,17 +88,6 @@ int Bitstream::registerMetaObject(const char* className, const QMetaObject* meta
|
|||
for (const QMetaObject* superClass = metaObject; superClass; superClass = superClass->superClass()) {
|
||||
getMetaObjectSubClasses().insert(superClass, metaObject);
|
||||
}
|
||||
|
||||
// register the streamers for all enumerators
|
||||
// temporarily disabled: crashes on Windows
|
||||
//for (int i = 0; i < metaObject->enumeratorCount(); i++) {
|
||||
// QMetaEnum metaEnum = metaObject->enumerator(i);
|
||||
// const TypeStreamer*& streamer = getEnumStreamers()[QPair<QByteArray, QByteArray>(metaEnum.scope(), metaEnum.name())];
|
||||
// if (!streamer) {
|
||||
// getEnumStreamersByName().insert(getEnumName(metaEnum), streamer = new EnumTypeStreamer(metaEnum));
|
||||
// }
|
||||
//}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -225,7 +212,8 @@ void Bitstream::persistWriteMappings(const WriteMappings& mappings) {
|
|||
}
|
||||
connect(it.key().data(), SIGNAL(destroyed(QObject*)), SLOT(clearSharedObject(QObject*)));
|
||||
QPointer<SharedObject>& reference = _sharedObjectReferences[it.key()->getOriginID()];
|
||||
if (reference) {
|
||||
if (reference && reference != it.key()) {
|
||||
// the object has been replaced by a successor, so we can forget about the original
|
||||
_sharedObjectStreamer.removePersistentID(reference);
|
||||
reference->disconnect(this);
|
||||
}
|
||||
|
@ -259,7 +247,8 @@ void Bitstream::persistReadMappings(const ReadMappings& mappings) {
|
|||
continue;
|
||||
}
|
||||
QPointer<SharedObject>& reference = _sharedObjectReferences[it.value()->getRemoteOriginID()];
|
||||
if (reference) {
|
||||
if (reference && reference != it.value()) {
|
||||
// the object has been replaced by a successor, so we can forget about the original
|
||||
_sharedObjectStreamer.removePersistentValue(reference.data());
|
||||
}
|
||||
reference = it.value();
|
||||
|
@ -298,6 +287,12 @@ void Bitstream::writeDelta(const QVariant& value, const QVariant& reference) {
|
|||
streamer->writeRawDelta(*this, value, reference);
|
||||
}
|
||||
|
||||
void Bitstream::writeRawDelta(const QVariant& value, const QVariant& reference) {
|
||||
const TypeStreamer* streamer = getTypeStreamers().value(value.userType());
|
||||
_typeStreamerStreamer << streamer;
|
||||
streamer->writeRawDelta(*this, value, reference);
|
||||
}
|
||||
|
||||
void Bitstream::readRawDelta(QVariant& value, const QVariant& reference) {
|
||||
TypeReader typeReader;
|
||||
_typeStreamerStreamer >> typeReader;
|
||||
|
@ -311,7 +306,7 @@ void Bitstream::writeRawDelta(const QObject* value, const QObject* reference) {
|
|||
}
|
||||
const QMetaObject* metaObject = value->metaObject();
|
||||
_metaObjectStreamer << metaObject;
|
||||
foreach (const PropertyWriter& propertyWriter, getPropertyWriters(metaObject)) {
|
||||
foreach (const PropertyWriter& propertyWriter, getPropertyWriters().value(metaObject)) {
|
||||
propertyWriter.writeDelta(*this, value, reference);
|
||||
}
|
||||
}
|
||||
|
@ -322,6 +317,272 @@ void Bitstream::readRawDelta(QObject*& value, const QObject* reference) {
|
|||
value = objectReader.readDelta(*this, reference);
|
||||
}
|
||||
|
||||
void Bitstream::writeRawDelta(const QScriptValue& value, const QScriptValue& reference) {
|
||||
if (reference.isUndefined() || reference.isNull()) {
|
||||
*this << value;
|
||||
|
||||
} else if (reference.isBool()) {
|
||||
if (value.isBool()) {
|
||||
*this << false;
|
||||
*this << value.toBool();
|
||||
|
||||
} else {
|
||||
*this << true;
|
||||
*this << value;
|
||||
}
|
||||
} else if (reference.isNumber()) {
|
||||
if (value.isNumber()) {
|
||||
*this << false;
|
||||
*this << value.toNumber();
|
||||
|
||||
} else {
|
||||
*this << true;
|
||||
*this << value;
|
||||
}
|
||||
} else if (reference.isString()) {
|
||||
if (value.isString()) {
|
||||
*this << false;
|
||||
*this << value.toString();
|
||||
|
||||
} else {
|
||||
*this << true;
|
||||
*this << value;
|
||||
}
|
||||
} else if (reference.isVariant()) {
|
||||
if (value.isVariant()) {
|
||||
*this << false;
|
||||
writeRawDelta(value.toVariant(), reference.toVariant());
|
||||
|
||||
} else {
|
||||
*this << true;
|
||||
*this << value;
|
||||
}
|
||||
} else if (reference.isQObject()) {
|
||||
if (value.isQObject()) {
|
||||
*this << false;
|
||||
writeRawDelta(value.toQObject(), reference.toQObject());
|
||||
|
||||
} else {
|
||||
*this << true;
|
||||
*this << value;
|
||||
}
|
||||
} else if (reference.isQMetaObject()) {
|
||||
if (value.isQMetaObject()) {
|
||||
*this << false;
|
||||
*this << value.toQMetaObject();
|
||||
|
||||
} else {
|
||||
*this << true;
|
||||
*this << value;
|
||||
}
|
||||
} else if (reference.isDate()) {
|
||||
if (value.isDate()) {
|
||||
*this << false;
|
||||
*this << value.toDateTime();
|
||||
|
||||
} else {
|
||||
*this << true;
|
||||
*this << value;
|
||||
}
|
||||
} else if (reference.isRegExp()) {
|
||||
if (value.isRegExp()) {
|
||||
*this << false;
|
||||
*this << value.toRegExp();
|
||||
|
||||
} else {
|
||||
*this << true;
|
||||
*this << value;
|
||||
}
|
||||
} else if (reference.isArray()) {
|
||||
if (value.isArray()) {
|
||||
*this << false;
|
||||
int length = value.property(ScriptCache::getInstance()->getLengthString()).toInt32();
|
||||
*this << length;
|
||||
int referenceLength = reference.property(ScriptCache::getInstance()->getLengthString()).toInt32();
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (i < referenceLength) {
|
||||
writeDelta(value.property(i), reference.property(i));
|
||||
} else {
|
||||
*this << value.property(i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
*this << true;
|
||||
*this << value;
|
||||
}
|
||||
} else if (reference.isObject()) {
|
||||
if (value.isObject() && !(value.isArray() || value.isRegExp() || value.isDate() ||
|
||||
value.isQMetaObject() || value.isQObject() || value.isVariant())) {
|
||||
*this << false;
|
||||
for (QScriptValueIterator it(value); it.hasNext(); ) {
|
||||
it.next();
|
||||
QScriptValue referenceValue = reference.property(it.scriptName());
|
||||
if (it.value() != referenceValue) {
|
||||
*this << it.scriptName();
|
||||
writeRawDelta(it.value(), referenceValue);
|
||||
}
|
||||
}
|
||||
for (QScriptValueIterator it(reference); it.hasNext(); ) {
|
||||
it.next();
|
||||
if (!value.property(it.scriptName()).isValid()) {
|
||||
*this << it.scriptName();
|
||||
writeRawDelta(QScriptValue(), it.value());
|
||||
}
|
||||
}
|
||||
*this << QScriptString();
|
||||
|
||||
} else {
|
||||
*this << true;
|
||||
*this << value;
|
||||
}
|
||||
} else {
|
||||
*this << value;
|
||||
}
|
||||
}
|
||||
|
||||
void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) {
|
||||
if (reference.isUndefined() || reference.isNull()) {
|
||||
*this >> value;
|
||||
|
||||
} else if (reference.isBool()) {
|
||||
bool typeChanged;
|
||||
*this >> typeChanged;
|
||||
if (typeChanged) {
|
||||
*this >> value;
|
||||
|
||||
} else {
|
||||
bool boolValue;
|
||||
*this >> boolValue;
|
||||
value = QScriptValue(boolValue);
|
||||
}
|
||||
} else if (reference.isNumber()) {
|
||||
bool typeChanged;
|
||||
*this >> typeChanged;
|
||||
if (typeChanged) {
|
||||
*this >> value;
|
||||
|
||||
} else {
|
||||
qsreal numberValue;
|
||||
*this >> numberValue;
|
||||
value = QScriptValue(numberValue);
|
||||
}
|
||||
} else if (reference.isString()) {
|
||||
bool typeChanged;
|
||||
*this >> typeChanged;
|
||||
if (typeChanged) {
|
||||
*this >> value;
|
||||
|
||||
} else {
|
||||
QString stringValue;
|
||||
*this >> stringValue;
|
||||
value = QScriptValue(stringValue);
|
||||
}
|
||||
} else if (reference.isVariant()) {
|
||||
bool typeChanged;
|
||||
*this >> typeChanged;
|
||||
if (typeChanged) {
|
||||
*this >> value;
|
||||
|
||||
} else {
|
||||
QVariant variant;
|
||||
readRawDelta(variant, reference.toVariant());
|
||||
value = ScriptCache::getInstance()->getEngine()->newVariant(variant);
|
||||
}
|
||||
} else if (reference.isQObject()) {
|
||||
bool typeChanged;
|
||||
*this >> typeChanged;
|
||||
if (typeChanged) {
|
||||
*this >> value;
|
||||
|
||||
} else {
|
||||
QObject* object;
|
||||
readRawDelta(object, reference.toQObject());
|
||||
value = ScriptCache::getInstance()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership);
|
||||
}
|
||||
} else if (reference.isQMetaObject()) {
|
||||
bool typeChanged;
|
||||
*this >> typeChanged;
|
||||
if (typeChanged) {
|
||||
*this >> value;
|
||||
|
||||
} else {
|
||||
const QMetaObject* metaObject;
|
||||
*this >> metaObject;
|
||||
value = ScriptCache::getInstance()->getEngine()->newQMetaObject(metaObject);
|
||||
}
|
||||
} else if (reference.isDate()) {
|
||||
bool typeChanged;
|
||||
*this >> typeChanged;
|
||||
if (typeChanged) {
|
||||
*this >> value;
|
||||
|
||||
} else {
|
||||
QDateTime dateTime;
|
||||
*this >> dateTime;
|
||||
value = ScriptCache::getInstance()->getEngine()->newDate(dateTime);
|
||||
}
|
||||
} else if (reference.isRegExp()) {
|
||||
bool typeChanged;
|
||||
*this >> typeChanged;
|
||||
if (typeChanged) {
|
||||
*this >> value;
|
||||
|
||||
} else {
|
||||
QRegExp regExp;
|
||||
*this >> regExp;
|
||||
value = ScriptCache::getInstance()->getEngine()->newRegExp(regExp);
|
||||
}
|
||||
} else if (reference.isArray()) {
|
||||
bool typeChanged;
|
||||
*this >> typeChanged;
|
||||
if (typeChanged) {
|
||||
*this >> value;
|
||||
|
||||
} else {
|
||||
int length;
|
||||
*this >> length;
|
||||
value = ScriptCache::getInstance()->getEngine()->newArray(length);
|
||||
int referenceLength = reference.property(ScriptCache::getInstance()->getLengthString()).toInt32();
|
||||
for (int i = 0; i < length; i++) {
|
||||
QScriptValue element;
|
||||
if (i < referenceLength) {
|
||||
readDelta(element, reference.property(i));
|
||||
} else {
|
||||
*this >> element;
|
||||
}
|
||||
value.setProperty(i, element);
|
||||
}
|
||||
}
|
||||
} else if (reference.isObject()) {
|
||||
bool typeChanged;
|
||||
*this >> typeChanged;
|
||||
if (typeChanged) {
|
||||
*this >> value;
|
||||
|
||||
} else {
|
||||
// start by shallow-copying the reference
|
||||
value = ScriptCache::getInstance()->getEngine()->newObject();
|
||||
for (QScriptValueIterator it(reference); it.hasNext(); ) {
|
||||
it.next();
|
||||
value.setProperty(it.scriptName(), it.value());
|
||||
}
|
||||
// then apply the requested changes
|
||||
forever {
|
||||
QScriptString name;
|
||||
*this >> name;
|
||||
if (!name.isValid()) {
|
||||
break;
|
||||
}
|
||||
QScriptValue scriptValue;
|
||||
readRawDelta(scriptValue, reference.property(name));
|
||||
value.setProperty(name, scriptValue);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
*this >> value;
|
||||
}
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(bool value) {
|
||||
if (value) {
|
||||
_byte |= (1 << _position);
|
||||
|
@ -363,6 +624,14 @@ Bitstream& Bitstream::operator>>(uint& value) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(qint64 value) {
|
||||
return write(&value, 64);
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>>(qint64& value) {
|
||||
return read(&value, 64);
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(float value) {
|
||||
return write(&value, 32);
|
||||
}
|
||||
|
@ -371,6 +640,14 @@ Bitstream& Bitstream::operator>>(float& value) {
|
|||
return read(&value, 32);
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(double value) {
|
||||
return write(&value, 64);
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>>(double& value) {
|
||||
return read(&value, 64);
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const glm::vec3& value) {
|
||||
return *this << value.x << value.y << value.z;
|
||||
}
|
||||
|
@ -433,6 +710,40 @@ Bitstream& Bitstream::operator>>(QUrl& url) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const QDateTime& dateTime) {
|
||||
return *this << dateTime.toMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>>(QDateTime& dateTime) {
|
||||
qint64 msecsSinceEpoch;
|
||||
*this >> msecsSinceEpoch;
|
||||
dateTime = QDateTime::fromMSecsSinceEpoch(msecsSinceEpoch);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const QRegExp& regExp) {
|
||||
*this << regExp.pattern();
|
||||
Qt::CaseSensitivity caseSensitivity = regExp.caseSensitivity();
|
||||
write(&caseSensitivity, 1);
|
||||
QRegExp::PatternSyntax syntax = regExp.patternSyntax();
|
||||
write(&syntax, 3);
|
||||
return *this << regExp.isMinimal();
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>>(QRegExp& regExp) {
|
||||
QString pattern;
|
||||
*this >> pattern;
|
||||
Qt::CaseSensitivity caseSensitivity = (Qt::CaseSensitivity)0;
|
||||
read(&caseSensitivity, 1);
|
||||
QRegExp::PatternSyntax syntax = (QRegExp::PatternSyntax)0;
|
||||
read(&syntax, 3);
|
||||
regExp = QRegExp(pattern, caseSensitivity, syntax);
|
||||
bool minimal;
|
||||
*this >> minimal;
|
||||
regExp.setMinimal(minimal);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const QVariant& value) {
|
||||
if (!value.isValid()) {
|
||||
_typeStreamerStreamer << NULL;
|
||||
|
@ -489,7 +800,7 @@ Bitstream& Bitstream::operator<<(const QObject* object) {
|
|||
}
|
||||
const QMetaObject* metaObject = object->metaObject();
|
||||
_metaObjectStreamer << metaObject;
|
||||
foreach (const PropertyWriter& propertyWriter, getPropertyWriters(metaObject)) {
|
||||
foreach (const PropertyWriter& propertyWriter, getPropertyWriters().value(metaObject)) {
|
||||
propertyWriter.write(*this, object);
|
||||
}
|
||||
return *this;
|
||||
|
@ -556,6 +867,185 @@ Bitstream& Bitstream::operator>>(QScriptString& string) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
enum ScriptValueType {
|
||||
INVALID_SCRIPT_VALUE,
|
||||
UNDEFINED_SCRIPT_VALUE,
|
||||
NULL_SCRIPT_VALUE,
|
||||
BOOL_SCRIPT_VALUE,
|
||||
NUMBER_SCRIPT_VALUE,
|
||||
STRING_SCRIPT_VALUE,
|
||||
VARIANT_SCRIPT_VALUE,
|
||||
QOBJECT_SCRIPT_VALUE,
|
||||
QMETAOBJECT_SCRIPT_VALUE,
|
||||
DATE_SCRIPT_VALUE,
|
||||
REGEXP_SCRIPT_VALUE,
|
||||
ARRAY_SCRIPT_VALUE,
|
||||
OBJECT_SCRIPT_VALUE
|
||||
};
|
||||
|
||||
const int SCRIPT_VALUE_BITS = 4;
|
||||
|
||||
static void writeScriptValueType(Bitstream& out, ScriptValueType type) {
|
||||
out.write(&type, SCRIPT_VALUE_BITS);
|
||||
}
|
||||
|
||||
static ScriptValueType readScriptValueType(Bitstream& in) {
|
||||
ScriptValueType type = (ScriptValueType)0;
|
||||
in.read(&type, SCRIPT_VALUE_BITS);
|
||||
return type;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const QScriptValue& value) {
|
||||
if (value.isUndefined()) {
|
||||
writeScriptValueType(*this, UNDEFINED_SCRIPT_VALUE);
|
||||
|
||||
} else if (value.isNull()) {
|
||||
writeScriptValueType(*this, NULL_SCRIPT_VALUE);
|
||||
|
||||
} else if (value.isBool()) {
|
||||
writeScriptValueType(*this, BOOL_SCRIPT_VALUE);
|
||||
*this << value.toBool();
|
||||
|
||||
} else if (value.isNumber()) {
|
||||
writeScriptValueType(*this, NUMBER_SCRIPT_VALUE);
|
||||
*this << value.toNumber();
|
||||
|
||||
} else if (value.isString()) {
|
||||
writeScriptValueType(*this, STRING_SCRIPT_VALUE);
|
||||
*this << value.toString();
|
||||
|
||||
} else if (value.isVariant()) {
|
||||
writeScriptValueType(*this, VARIANT_SCRIPT_VALUE);
|
||||
*this << value.toVariant();
|
||||
|
||||
} else if (value.isQObject()) {
|
||||
writeScriptValueType(*this, QOBJECT_SCRIPT_VALUE);
|
||||
*this << value.toQObject();
|
||||
|
||||
} else if (value.isQMetaObject()) {
|
||||
writeScriptValueType(*this, QMETAOBJECT_SCRIPT_VALUE);
|
||||
*this << value.toQMetaObject();
|
||||
|
||||
} else if (value.isDate()) {
|
||||
writeScriptValueType(*this, DATE_SCRIPT_VALUE);
|
||||
*this << value.toDateTime();
|
||||
|
||||
} else if (value.isRegExp()) {
|
||||
writeScriptValueType(*this, REGEXP_SCRIPT_VALUE);
|
||||
*this << value.toRegExp();
|
||||
|
||||
} else if (value.isArray()) {
|
||||
writeScriptValueType(*this, ARRAY_SCRIPT_VALUE);
|
||||
int length = value.property(ScriptCache::getInstance()->getLengthString()).toInt32();
|
||||
*this << length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
*this << value.property(i);
|
||||
}
|
||||
} else if (value.isObject()) {
|
||||
writeScriptValueType(*this, OBJECT_SCRIPT_VALUE);
|
||||
for (QScriptValueIterator it(value); it.hasNext(); ) {
|
||||
it.next();
|
||||
*this << it.scriptName();
|
||||
*this << it.value();
|
||||
}
|
||||
*this << QScriptString();
|
||||
|
||||
} else {
|
||||
writeScriptValueType(*this, INVALID_SCRIPT_VALUE);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>>(QScriptValue& value) {
|
||||
switch (readScriptValueType(*this)) {
|
||||
case UNDEFINED_SCRIPT_VALUE:
|
||||
value = QScriptValue(QScriptValue::UndefinedValue);
|
||||
break;
|
||||
|
||||
case NULL_SCRIPT_VALUE:
|
||||
value = QScriptValue(QScriptValue::NullValue);
|
||||
break;
|
||||
|
||||
case BOOL_SCRIPT_VALUE: {
|
||||
bool boolValue;
|
||||
*this >> boolValue;
|
||||
value = QScriptValue(boolValue);
|
||||
break;
|
||||
}
|
||||
case NUMBER_SCRIPT_VALUE: {
|
||||
qsreal numberValue;
|
||||
*this >> numberValue;
|
||||
value = QScriptValue(numberValue);
|
||||
break;
|
||||
}
|
||||
case STRING_SCRIPT_VALUE: {
|
||||
QString stringValue;
|
||||
*this >> stringValue;
|
||||
value = QScriptValue(stringValue);
|
||||
break;
|
||||
}
|
||||
case VARIANT_SCRIPT_VALUE: {
|
||||
QVariant variantValue;
|
||||
*this >> variantValue;
|
||||
value = ScriptCache::getInstance()->getEngine()->newVariant(variantValue);
|
||||
break;
|
||||
}
|
||||
case QOBJECT_SCRIPT_VALUE: {
|
||||
QObject* object;
|
||||
*this >> object;
|
||||
ScriptCache::getInstance()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership);
|
||||
break;
|
||||
}
|
||||
case QMETAOBJECT_SCRIPT_VALUE: {
|
||||
const QMetaObject* metaObject;
|
||||
*this >> metaObject;
|
||||
ScriptCache::getInstance()->getEngine()->newQMetaObject(metaObject);
|
||||
break;
|
||||
}
|
||||
case DATE_SCRIPT_VALUE: {
|
||||
QDateTime dateTime;
|
||||
*this >> dateTime;
|
||||
value = ScriptCache::getInstance()->getEngine()->newDate(dateTime);
|
||||
break;
|
||||
}
|
||||
case REGEXP_SCRIPT_VALUE: {
|
||||
QRegExp regExp;
|
||||
*this >> regExp;
|
||||
value = ScriptCache::getInstance()->getEngine()->newRegExp(regExp);
|
||||
break;
|
||||
}
|
||||
case ARRAY_SCRIPT_VALUE: {
|
||||
int length;
|
||||
*this >> length;
|
||||
value = ScriptCache::getInstance()->getEngine()->newArray(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
QScriptValue element;
|
||||
*this >> element;
|
||||
value.setProperty(i, element);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OBJECT_SCRIPT_VALUE: {
|
||||
value = ScriptCache::getInstance()->getEngine()->newObject();
|
||||
forever {
|
||||
QScriptString name;
|
||||
*this >> name;
|
||||
if (!name.isValid()) {
|
||||
break;
|
||||
}
|
||||
QScriptValue scriptValue;
|
||||
*this >> scriptValue;
|
||||
value.setProperty(name, scriptValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
value = QScriptValue();
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const SharedObjectPointer& object) {
|
||||
_sharedObjectStreamer << object;
|
||||
return *this;
|
||||
|
@ -574,7 +1064,7 @@ Bitstream& Bitstream::operator<(const QMetaObject* metaObject) {
|
|||
if (_metadataType == NO_METADATA) {
|
||||
return *this;
|
||||
}
|
||||
const QVector<PropertyWriter>& propertyWriters = getPropertyWriters(metaObject);
|
||||
const PropertyWriterVector& propertyWriters = getPropertyWriters().value(metaObject);
|
||||
*this << propertyWriters.size();
|
||||
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||
foreach (const PropertyWriter& propertyWriter, propertyWriters) {
|
||||
|
@ -608,12 +1098,12 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) {
|
|||
qWarning() << "Unknown class name: " << className << "\n";
|
||||
}
|
||||
if (_metadataType == NO_METADATA) {
|
||||
objectReader = ObjectReader(className, metaObject, getPropertyReaders(metaObject));
|
||||
objectReader = ObjectReader(className, metaObject, getPropertyReaders().value(metaObject));
|
||||
return *this;
|
||||
}
|
||||
int storedPropertyCount;
|
||||
*this >> storedPropertyCount;
|
||||
QVector<PropertyReader> properties(storedPropertyCount);
|
||||
PropertyReaderVector properties(storedPropertyCount);
|
||||
for (int i = 0; i < storedPropertyCount; i++) {
|
||||
TypeReader typeReader;
|
||||
*this >> typeReader;
|
||||
|
@ -632,7 +1122,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) {
|
|||
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||
bool matches = true;
|
||||
if (metaObject) {
|
||||
const QVector<PropertyWriter>& propertyWriters = getPropertyWriters(metaObject);
|
||||
const PropertyWriterVector& propertyWriters = getPropertyWriters().value(metaObject);
|
||||
if (propertyWriters.size() == properties.size()) {
|
||||
for (int i = 0; i < propertyWriters.size(); i++) {
|
||||
const PropertyWriter& propertyWriter = propertyWriters.at(i);
|
||||
|
@ -651,7 +1141,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) {
|
|||
QByteArray remoteHashResult(localHashResult.size(), 0);
|
||||
read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE);
|
||||
if (metaObject && matches && localHashResult == remoteHashResult) {
|
||||
objectReader = ObjectReader(className, metaObject, getPropertyReaders(metaObject));
|
||||
objectReader = ObjectReader(className, metaObject, getPropertyReaders().value(metaObject));
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
@ -912,14 +1402,17 @@ Bitstream& Bitstream::operator>(AttributePointer& attribute) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
const QString INVALID_STRING("%INVALID%");
|
||||
|
||||
Bitstream& Bitstream::operator<(const QScriptString& string) {
|
||||
return *this << string.toString();
|
||||
return *this << (string.isValid() ? string.toString() : INVALID_STRING);
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>(QScriptString& string) {
|
||||
QString rawString;
|
||||
*this >> rawString;
|
||||
string = ScriptCache::getInstance()->getEngine()->toStringHandle(rawString);
|
||||
string = (rawString == INVALID_STRING) ? QScriptString() :
|
||||
ScriptCache::getInstance()->getEngine()->toStringHandle(rawString);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -988,31 +1481,6 @@ void Bitstream::clearSharedObject(QObject* object) {
|
|||
}
|
||||
}
|
||||
|
||||
const QVector<PropertyWriter>& Bitstream::getPropertyWriters(const QMetaObject* metaObject) {
|
||||
QVector<PropertyWriter>& propertyWriters = _propertyWriters[metaObject];
|
||||
if (propertyWriters.isEmpty()) {
|
||||
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||
QMetaProperty property = metaObject->property(i);
|
||||
if (!property.isStored()) {
|
||||
continue;
|
||||
}
|
||||
const TypeStreamer* streamer;
|
||||
if (property.isEnumType()) {
|
||||
QMetaEnum metaEnum = property.enumerator();
|
||||
streamer = getEnumStreamers().value(QPair<QByteArray, QByteArray>(
|
||||
QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())),
|
||||
QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name()))));
|
||||
} else {
|
||||
streamer = getTypeStreamers().value(property.userType());
|
||||
}
|
||||
if (streamer) {
|
||||
propertyWriters.append(PropertyWriter(property, streamer));
|
||||
}
|
||||
}
|
||||
}
|
||||
return propertyWriters;
|
||||
}
|
||||
|
||||
QHash<QByteArray, const QMetaObject*>& Bitstream::getMetaObjects() {
|
||||
static QHash<QByteArray, const QMetaObject*> metaObjects;
|
||||
return metaObjects;
|
||||
|
@ -1028,42 +1496,100 @@ QHash<int, const TypeStreamer*>& Bitstream::getTypeStreamers() {
|
|||
return typeStreamers;
|
||||
}
|
||||
|
||||
QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*>& Bitstream::getEnumStreamers() {
|
||||
static QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*> enumStreamers;
|
||||
const QHash<ScopeNamePair, const TypeStreamer*>& Bitstream::getEnumStreamers() {
|
||||
static QHash<ScopeNamePair, const TypeStreamer*> enumStreamers = createEnumStreamers();
|
||||
return enumStreamers;
|
||||
}
|
||||
|
||||
QHash<QByteArray, const TypeStreamer*>& Bitstream::getEnumStreamersByName() {
|
||||
static QHash<QByteArray, const TypeStreamer*> enumStreamersByName;
|
||||
QHash<ScopeNamePair, const TypeStreamer*> Bitstream::createEnumStreamers() {
|
||||
QHash<ScopeNamePair, const TypeStreamer*> enumStreamers;
|
||||
foreach (const QMetaObject* metaObject, getMetaObjects()) {
|
||||
for (int i = 0; i < metaObject->enumeratorCount(); i++) {
|
||||
QMetaEnum metaEnum = metaObject->enumerator(i);
|
||||
const TypeStreamer*& streamer = enumStreamers[ScopeNamePair(metaEnum.scope(), metaEnum.name())];
|
||||
if (!streamer) {
|
||||
streamer = new EnumTypeStreamer(metaEnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
return enumStreamers;
|
||||
}
|
||||
|
||||
const QHash<QByteArray, const TypeStreamer*>& Bitstream::getEnumStreamersByName() {
|
||||
static QHash<QByteArray, const TypeStreamer*> enumStreamersByName = createEnumStreamersByName();
|
||||
return enumStreamersByName;
|
||||
}
|
||||
|
||||
QVector<PropertyReader> Bitstream::getPropertyReaders(const QMetaObject* metaObject) {
|
||||
QVector<PropertyReader> propertyReaders;
|
||||
if (!metaObject) {
|
||||
return propertyReaders;
|
||||
QHash<QByteArray, const TypeStreamer*> Bitstream::createEnumStreamersByName() {
|
||||
QHash<QByteArray, const TypeStreamer*> enumStreamersByName;
|
||||
foreach (const TypeStreamer* streamer, getEnumStreamers()) {
|
||||
enumStreamersByName.insert(streamer->getName(), streamer);
|
||||
}
|
||||
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||
QMetaProperty property = metaObject->property(i);
|
||||
if (!property.isStored()) {
|
||||
continue;
|
||||
}
|
||||
const TypeStreamer* streamer;
|
||||
if (property.isEnumType()) {
|
||||
QMetaEnum metaEnum = property.enumerator();
|
||||
streamer = getEnumStreamers().value(QPair<QByteArray, QByteArray>(
|
||||
QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())),
|
||||
QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name()))));
|
||||
} else {
|
||||
streamer = getTypeStreamers().value(property.userType());
|
||||
}
|
||||
if (streamer) {
|
||||
propertyReaders.append(PropertyReader(TypeReader(QByteArray(), streamer), property));
|
||||
return enumStreamersByName;
|
||||
}
|
||||
|
||||
const QHash<const QMetaObject*, PropertyReaderVector>& Bitstream::getPropertyReaders() {
|
||||
static QHash<const QMetaObject*, PropertyReaderVector> propertyReaders = createPropertyReaders();
|
||||
return propertyReaders;
|
||||
}
|
||||
|
||||
QHash<const QMetaObject*, PropertyReaderVector> Bitstream::createPropertyReaders() {
|
||||
QHash<const QMetaObject*, PropertyReaderVector> propertyReaders;
|
||||
foreach (const QMetaObject* metaObject, getMetaObjects()) {
|
||||
PropertyReaderVector& readers = propertyReaders[metaObject];
|
||||
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||
QMetaProperty property = metaObject->property(i);
|
||||
if (!property.isStored()) {
|
||||
continue;
|
||||
}
|
||||
const TypeStreamer* streamer;
|
||||
if (property.isEnumType()) {
|
||||
QMetaEnum metaEnum = property.enumerator();
|
||||
streamer = getEnumStreamers().value(ScopeNamePair(
|
||||
QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())),
|
||||
QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name()))));
|
||||
} else {
|
||||
streamer = getTypeStreamers().value(property.userType());
|
||||
}
|
||||
if (streamer) {
|
||||
readers.append(PropertyReader(TypeReader(QByteArray(), streamer), property));
|
||||
}
|
||||
}
|
||||
}
|
||||
return propertyReaders;
|
||||
}
|
||||
|
||||
const QHash<const QMetaObject*, PropertyWriterVector>& Bitstream::getPropertyWriters() {
|
||||
static QHash<const QMetaObject*, PropertyWriterVector> propertyWriters = createPropertyWriters();
|
||||
return propertyWriters;
|
||||
}
|
||||
|
||||
QHash<const QMetaObject*, PropertyWriterVector> Bitstream::createPropertyWriters() {
|
||||
QHash<const QMetaObject*, PropertyWriterVector> propertyWriters;
|
||||
foreach (const QMetaObject* metaObject, getMetaObjects()) {
|
||||
PropertyWriterVector& writers = propertyWriters[metaObject];
|
||||
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||
QMetaProperty property = metaObject->property(i);
|
||||
if (!property.isStored()) {
|
||||
continue;
|
||||
}
|
||||
const TypeStreamer* streamer;
|
||||
if (property.isEnumType()) {
|
||||
QMetaEnum metaEnum = property.enumerator();
|
||||
streamer = getEnumStreamers().value(ScopeNamePair(
|
||||
QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())),
|
||||
QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name()))));
|
||||
} else {
|
||||
streamer = getTypeStreamers().value(property.userType());
|
||||
}
|
||||
if (streamer) {
|
||||
writers.append(PropertyWriter(property, streamer));
|
||||
}
|
||||
}
|
||||
}
|
||||
return propertyWriters;
|
||||
}
|
||||
|
||||
TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer) :
|
||||
_typeName(typeName),
|
||||
_streamer(streamer),
|
||||
|
@ -1304,7 +1830,7 @@ void FieldReader::readDelta(Bitstream& in, const TypeStreamer* streamer, QVarian
|
|||
}
|
||||
|
||||
ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject,
|
||||
const QVector<PropertyReader>& properties) :
|
||||
const PropertyReaderVector& properties) :
|
||||
_className(className),
|
||||
_metaObject(metaObject),
|
||||
_properties(properties) {
|
||||
|
@ -1461,17 +1987,21 @@ QDebug& operator<<(QDebug& debug, const QMetaObject* metaObject) {
|
|||
return debug << (metaObject ? metaObject->className() : "null");
|
||||
}
|
||||
|
||||
EnumTypeStreamer::EnumTypeStreamer(const QMetaObject* metaObject, const char* name) :
|
||||
_metaObject(metaObject),
|
||||
_enumName(name),
|
||||
_name(QByteArray(metaObject->className()) + "::" + name),
|
||||
_bits(-1) {
|
||||
|
||||
setType(QMetaType::Int);
|
||||
}
|
||||
|
||||
EnumTypeStreamer::EnumTypeStreamer(const QMetaEnum& metaEnum) :
|
||||
_name(QByteArray(metaEnum.scope()) + "::" + metaEnum.name()),
|
||||
_metaEnum(metaEnum),
|
||||
_name(getEnumName(metaEnum)) {
|
||||
|
||||
setType(QMetaType::Int);
|
||||
|
||||
int highestValue = 0;
|
||||
for (int j = 0; j < metaEnum.keyCount(); j++) {
|
||||
highestValue = qMax(highestValue, metaEnum.value(j));
|
||||
}
|
||||
_bits = getBitsForHighestValue(highestValue);
|
||||
_bits(-1) {
|
||||
|
||||
setType(QMetaType::Int);
|
||||
}
|
||||
|
||||
const char* EnumTypeStreamer::getName() const {
|
||||
|
@ -1483,10 +2013,21 @@ TypeReader::Type EnumTypeStreamer::getReaderType() const {
|
|||
}
|
||||
|
||||
int EnumTypeStreamer::getBits() const {
|
||||
if (_bits == -1) {
|
||||
int highestValue = 0;
|
||||
QMetaEnum metaEnum = getMetaEnum();
|
||||
for (int j = 0; j < metaEnum.keyCount(); j++) {
|
||||
highestValue = qMax(highestValue, metaEnum.value(j));
|
||||
}
|
||||
const_cast<EnumTypeStreamer*>(this)->_bits = getBitsForHighestValue(highestValue);
|
||||
}
|
||||
return _bits;
|
||||
}
|
||||
|
||||
QMetaEnum EnumTypeStreamer::getMetaEnum() const {
|
||||
if (!_metaEnum.isValid()) {
|
||||
const_cast<EnumTypeStreamer*>(this)->_metaEnum = _metaObject->enumerator(_metaObject->indexOfEnumerator(_enumName));
|
||||
}
|
||||
return _metaEnum;
|
||||
}
|
||||
|
||||
|
@ -1496,12 +2037,12 @@ bool EnumTypeStreamer::equal(const QVariant& first, const QVariant& second) cons
|
|||
|
||||
void EnumTypeStreamer::write(Bitstream& out, const QVariant& value) const {
|
||||
int intValue = value.toInt();
|
||||
out.write(&intValue, _bits);
|
||||
out.write(&intValue, getBits());
|
||||
}
|
||||
|
||||
QVariant EnumTypeStreamer::read(Bitstream& in) const {
|
||||
int intValue = 0;
|
||||
in.read(&intValue, _bits);
|
||||
in.read(&intValue, getBits());
|
||||
return intValue;
|
||||
}
|
||||
|
||||
|
@ -1511,7 +2052,7 @@ void EnumTypeStreamer::writeDelta(Bitstream& out, const QVariant& value, const Q
|
|||
out << false;
|
||||
} else {
|
||||
out << true;
|
||||
out.write(&intValue, _bits);
|
||||
out.write(&intValue, getBits());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1520,7 +2061,7 @@ void EnumTypeStreamer::readDelta(Bitstream& in, QVariant& value, const QVariant&
|
|||
in >> changed;
|
||||
if (changed) {
|
||||
int intValue = 0;
|
||||
in.read(&intValue, _bits);
|
||||
in.read(&intValue, getBits());
|
||||
value = intValue;
|
||||
} else {
|
||||
value = reference;
|
||||
|
@ -1529,17 +2070,17 @@ void EnumTypeStreamer::readDelta(Bitstream& in, QVariant& value, const QVariant&
|
|||
|
||||
void EnumTypeStreamer::writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const {
|
||||
int intValue = value.toInt();
|
||||
out.write(&intValue, _bits);
|
||||
out.write(&intValue, getBits());
|
||||
}
|
||||
|
||||
void EnumTypeStreamer::readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const {
|
||||
int intValue = 0;
|
||||
in.read(&intValue, _bits);
|
||||
in.read(&intValue, getBits());
|
||||
value = intValue;
|
||||
}
|
||||
|
||||
void EnumTypeStreamer::setEnumValue(QVariant& object, int value, const QHash<int, int>& mappings) const {
|
||||
if (_metaEnum.isFlag()) {
|
||||
if (getMetaEnum().isFlag()) {
|
||||
int combined = 0;
|
||||
for (QHash<int, int>::const_iterator it = mappings.constBegin(); it != mappings.constEnd(); it++) {
|
||||
if (value & it.key()) {
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
class QByteArray;
|
||||
class QColor;
|
||||
class QDataStream;
|
||||
class QScriptValue;
|
||||
class QUrl;
|
||||
|
||||
class Attribute;
|
||||
|
@ -44,6 +45,10 @@ class TypeStreamer;
|
|||
|
||||
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
|
||||
|
||||
typedef QPair<QByteArray, QByteArray> ScopeNamePair;
|
||||
typedef QVector<PropertyReader> PropertyReaderVector;
|
||||
typedef QVector<PropertyWriter> PropertyWriterVector;
|
||||
|
||||
/// Streams integer identifiers that conform to the following pattern: each ID encountered in the stream is either one that
|
||||
/// has been sent (received) before, or is one more than the highest previously encountered ID (starting at zero). This allows
|
||||
/// us to use the minimum number of bits to encode the IDs.
|
||||
|
@ -287,11 +292,15 @@ public:
|
|||
template<class T> void writeDelta(const T& value, const T& reference);
|
||||
template<class T> void readDelta(T& value, const T& reference);
|
||||
|
||||
void writeRawDelta(const QVariant& value, const QVariant& reference);
|
||||
void readRawDelta(QVariant& value, const QVariant& reference);
|
||||
|
||||
void writeRawDelta(const QObject* value, const QObject* reference);
|
||||
void readRawDelta(QObject*& value, const QObject* reference);
|
||||
|
||||
void writeRawDelta(const QScriptValue& value, const QScriptValue& reference);
|
||||
void readRawDelta(QScriptValue& value, const QScriptValue& reference);
|
||||
|
||||
template<class T> void writeRawDelta(const T& value, const T& reference);
|
||||
template<class T> void readRawDelta(T& value, const T& reference);
|
||||
|
||||
|
@ -316,9 +325,15 @@ public:
|
|||
Bitstream& operator<<(uint value);
|
||||
Bitstream& operator>>(uint& value);
|
||||
|
||||
Bitstream& operator<<(qint64 value);
|
||||
Bitstream& operator>>(qint64& value);
|
||||
|
||||
Bitstream& operator<<(float value);
|
||||
Bitstream& operator>>(float& value);
|
||||
|
||||
Bitstream& operator<<(double value);
|
||||
Bitstream& operator>>(double& value);
|
||||
|
||||
Bitstream& operator<<(const glm::vec3& value);
|
||||
Bitstream& operator>>(glm::vec3& value);
|
||||
|
||||
|
@ -337,6 +352,12 @@ public:
|
|||
Bitstream& operator<<(const QUrl& url);
|
||||
Bitstream& operator>>(QUrl& url);
|
||||
|
||||
Bitstream& operator<<(const QDateTime& dateTime);
|
||||
Bitstream& operator>>(QDateTime& dateTime);
|
||||
|
||||
Bitstream& operator<<(const QRegExp& regExp);
|
||||
Bitstream& operator>>(QRegExp& regExp);
|
||||
|
||||
Bitstream& operator<<(const QVariant& value);
|
||||
Bitstream& operator>>(QVariant& value);
|
||||
|
||||
|
@ -372,6 +393,9 @@ public:
|
|||
Bitstream& operator<<(const QScriptString& string);
|
||||
Bitstream& operator>>(QScriptString& string);
|
||||
|
||||
Bitstream& operator<<(const QScriptValue& value);
|
||||
Bitstream& operator>>(QScriptValue& value);
|
||||
|
||||
Bitstream& operator<<(const SharedObjectPointer& object);
|
||||
Bitstream& operator>>(SharedObjectPointer& object);
|
||||
|
||||
|
@ -400,8 +424,6 @@ private slots:
|
|||
|
||||
private:
|
||||
|
||||
const QVector<PropertyWriter>& getPropertyWriters(const QMetaObject* metaObject);
|
||||
|
||||
QDataStream& _underlying;
|
||||
quint8 _byte;
|
||||
int _position;
|
||||
|
@ -421,14 +443,21 @@ private:
|
|||
QHash<QByteArray, const QMetaObject*> _metaObjectSubstitutions;
|
||||
QHash<QByteArray, const TypeStreamer*> _typeStreamerSubstitutions;
|
||||
|
||||
QHash<const QMetaObject*, QVector<PropertyWriter> > _propertyWriters;
|
||||
|
||||
static QHash<QByteArray, const QMetaObject*>& getMetaObjects();
|
||||
static QMultiHash<const QMetaObject*, const QMetaObject*>& getMetaObjectSubClasses();
|
||||
static QHash<int, const TypeStreamer*>& getTypeStreamers();
|
||||
static QHash<QPair<QByteArray, QByteArray>, const TypeStreamer*>& getEnumStreamers();
|
||||
static QHash<QByteArray, const TypeStreamer*>& getEnumStreamersByName();
|
||||
static QVector<PropertyReader> getPropertyReaders(const QMetaObject* metaObject);
|
||||
|
||||
static const QHash<ScopeNamePair, const TypeStreamer*>& getEnumStreamers();
|
||||
static QHash<ScopeNamePair, const TypeStreamer*> createEnumStreamers();
|
||||
|
||||
static const QHash<QByteArray, const TypeStreamer*>& getEnumStreamersByName();
|
||||
static QHash<QByteArray, const TypeStreamer*> createEnumStreamersByName();
|
||||
|
||||
static const QHash<const QMetaObject*, PropertyReaderVector>& getPropertyReaders();
|
||||
static QHash<const QMetaObject*, PropertyReaderVector> createPropertyReaders();
|
||||
|
||||
static const QHash<const QMetaObject*, PropertyWriterVector>& getPropertyWriters();
|
||||
static QHash<const QMetaObject*, PropertyWriterVector> createPropertyWriters();
|
||||
};
|
||||
|
||||
template<class T> inline void Bitstream::writeDelta(const T& value, const T& reference) {
|
||||
|
@ -938,8 +967,9 @@ public:
|
|||
class EnumTypeStreamer : public TypeStreamer {
|
||||
public:
|
||||
|
||||
EnumTypeStreamer(const QMetaObject* metaObject, const char* name);
|
||||
EnumTypeStreamer(const QMetaEnum& metaEnum);
|
||||
|
||||
|
||||
virtual const char* getName() const;
|
||||
virtual TypeReader::Type getReaderType() const;
|
||||
virtual int getBits() const;
|
||||
|
@ -955,8 +985,10 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
QMetaEnum _metaEnum;
|
||||
const QMetaObject* _metaObject;
|
||||
const char* _enumName;
|
||||
QByteArray _name;
|
||||
QMetaEnum _metaEnum;
|
||||
int _bits;
|
||||
};
|
||||
|
||||
|
@ -1037,12 +1069,12 @@ public:
|
|||
};
|
||||
|
||||
/// Macro for registering simple type streamers.
|
||||
#define REGISTER_SIMPLE_TYPE_STREAMER(x) static int x##Streamer = \
|
||||
Bitstream::registerTypeStreamer(qMetaTypeId<x>(), new SimpleTypeStreamer<x>());
|
||||
#define REGISTER_SIMPLE_TYPE_STREAMER(X) static int X##Streamer = \
|
||||
Bitstream::registerTypeStreamer(qMetaTypeId<X>(), new SimpleTypeStreamer<X>());
|
||||
|
||||
/// Macro for registering collection type streamers.
|
||||
#define REGISTER_COLLECTION_TYPE_STREAMER(x) static int x##Streamer = \
|
||||
Bitstream::registerTypeStreamer(qMetaTypeId<x>(), new CollectionTypeStreamer<x>());
|
||||
#define REGISTER_COLLECTION_TYPE_STREAMER(X) static int x##Streamer = \
|
||||
Bitstream::registerTypeStreamer(qMetaTypeId<X>(), new CollectionTypeStreamer<X>());
|
||||
|
||||
/// Declares the metatype and the streaming operators. The last lines
|
||||
/// ensure that the generated file will be included in the link phase.
|
||||
|
@ -1077,14 +1109,42 @@ public:
|
|||
_Pragma(STRINGIFY(unused(_TypePtr##X)))
|
||||
#endif
|
||||
|
||||
#define DECLARE_ENUM_METATYPE(S, N) Q_DECLARE_METATYPE(S::N) \
|
||||
Bitstream& operator<<(Bitstream& out, const S::N& obj); \
|
||||
Bitstream& operator>>(Bitstream& in, S::N& obj); \
|
||||
template<> inline void Bitstream::writeRawDelta(const S::N& value, const S::N& reference) { *this << value; } \
|
||||
template<> inline void Bitstream::readRawDelta(S::N& value, const S::N& reference) { *this >> value; }
|
||||
|
||||
#define IMPLEMENT_ENUM_METATYPE(S, N) \
|
||||
static int S##N##MetaTypeId = registerEnumMetaType<S::N>(&S::staticMetaObject, #N); \
|
||||
Bitstream& operator<<(Bitstream& out, const S::N& obj) { \
|
||||
static int bits = Bitstream::getTypeStreamer(qMetaTypeId<S::N>())->getBits(); \
|
||||
return out.write(&obj, bits); \
|
||||
} \
|
||||
Bitstream& operator>>(Bitstream& in, S::N& obj) { \
|
||||
static int bits = Bitstream::getTypeStreamer(qMetaTypeId<S::N>())->getBits(); \
|
||||
obj = (S::N)0; \
|
||||
return in.read(&obj, bits); \
|
||||
}
|
||||
|
||||
/// Registers a simple type and its streamer.
|
||||
/// \return the metatype id
|
||||
template<class T> int registerSimpleMetaType() {
|
||||
int type = qRegisterMetaType<T>();
|
||||
Bitstream::registerTypeStreamer(type, new SimpleTypeStreamer<T>());
|
||||
return type;
|
||||
}
|
||||
|
||||
/// Registers an enum type and its streamer.
|
||||
/// \return the metatype id
|
||||
template<class T> int registerEnumMetaType(const QMetaObject* metaObject, const char* name) {
|
||||
int type = qRegisterMetaType<T>();
|
||||
Bitstream::registerTypeStreamer(type, new EnumTypeStreamer(metaObject, name));
|
||||
return type;
|
||||
}
|
||||
|
||||
/// Registers a streamable type and its streamer.
|
||||
/// \return the metatype id
|
||||
template<class T> int registerStreamableMetaType() {
|
||||
int type = qRegisterMetaType<T>();
|
||||
Bitstream::registerTypeStreamer(type, new StreamableTypeStreamer<T>());
|
||||
|
@ -1092,6 +1152,7 @@ template<class T> int registerStreamableMetaType() {
|
|||
}
|
||||
|
||||
/// Registers a collection type and its streamer.
|
||||
/// \return the metatype id
|
||||
template<class T> int registerCollectionMetaType() {
|
||||
int type = qRegisterMetaType<T>();
|
||||
Bitstream::registerTypeStreamer(type, new CollectionTypeStreamer<T>());
|
||||
|
|
|
@ -13,11 +13,95 @@
|
|||
|
||||
#include <QNetworkReply>
|
||||
#include <QScriptEngine>
|
||||
#include <QScriptValueIterator>
|
||||
#include <QTextStream>
|
||||
|
||||
#include "AttributeRegistry.h"
|
||||
#include "ScriptCache.h"
|
||||
|
||||
static int scriptValueMetaTypeId = qRegisterMetaType<QScriptValue>();
|
||||
static bool scriptValueComparators = QMetaType::registerComparators<QScriptValue>();
|
||||
|
||||
bool operator==(const QScriptValue& first, const QScriptValue& second) {
|
||||
if (first.isUndefined()) {
|
||||
return second.isUndefined();
|
||||
|
||||
} else if (first.isNull()) {
|
||||
return second.isNull();
|
||||
|
||||
} else if (first.isBool()) {
|
||||
return second.isBool() && first.toBool() == second.toBool();
|
||||
|
||||
} else if (first.isNumber()) {
|
||||
return second.isNumber() && first.toNumber() == second.toNumber();
|
||||
|
||||
} else if (first.isString()) {
|
||||
return second.isString() && first.toString() == second.toString();
|
||||
|
||||
} else if (first.isVariant()) {
|
||||
return second.isVariant() && first.toVariant() == second.toVariant();
|
||||
|
||||
} else if (first.isQObject()) {
|
||||
return second.isQObject() && first.toQObject() == second.toQObject();
|
||||
|
||||
} else if (first.isQMetaObject()) {
|
||||
return second.isQMetaObject() && first.toQMetaObject() == second.toQMetaObject();
|
||||
|
||||
} else if (first.isDate()) {
|
||||
return second.isDate() && first.toDateTime() == second.toDateTime();
|
||||
|
||||
} else if (first.isRegExp()) {
|
||||
return second.isRegExp() && first.toRegExp() == second.toRegExp();
|
||||
|
||||
} else if (first.isArray()) {
|
||||
if (!second.isArray()) {
|
||||
return false;
|
||||
}
|
||||
int length = first.property(ScriptCache::getInstance()->getLengthString()).toInt32();
|
||||
if (second.property(ScriptCache::getInstance()->getLengthString()).toInt32() != length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (first.property(i) != second.property(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if (first.isObject()) {
|
||||
if (!second.isObject()) {
|
||||
return false;
|
||||
}
|
||||
int propertyCount = 0;
|
||||
for (QScriptValueIterator it(first); it.hasNext(); ) {
|
||||
it.next();
|
||||
if (second.property(it.scriptName()) != it.value()) {
|
||||
return false;
|
||||
}
|
||||
propertyCount++;
|
||||
}
|
||||
// make sure the second has exactly as many properties as the first
|
||||
for (QScriptValueIterator it(second); it.hasNext(); ) {
|
||||
it.next();
|
||||
if (--propertyCount < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return !second.isValid();
|
||||
}
|
||||
}
|
||||
|
||||
bool operator!=(const QScriptValue& first, const QScriptValue& second) {
|
||||
return !(first == second);
|
||||
}
|
||||
|
||||
bool operator<(const QScriptValue& first, const QScriptValue& second) {
|
||||
return first.lessThan(second);
|
||||
}
|
||||
|
||||
ScriptCache* ScriptCache::getInstance() {
|
||||
static ScriptCache cache;
|
||||
return &cache;
|
||||
|
|
|
@ -65,6 +65,12 @@ private:
|
|||
QScriptString _generatorString;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(QScriptValue)
|
||||
|
||||
bool operator==(const QScriptValue& first, const QScriptValue& second);
|
||||
bool operator!=(const QScriptValue& first, const QScriptValue& second);
|
||||
bool operator<(const QScriptValue& first, const QScriptValue& second);
|
||||
|
||||
/// A program loaded from the network.
|
||||
class NetworkProgram : public Resource {
|
||||
Q_OBJECT
|
||||
|
|
|
@ -185,7 +185,7 @@ void ModelTree::addModel(const ModelItemID& modelID, const ModelItemProperties&
|
|||
glm::vec3 position = model.getPosition();
|
||||
float size = std::max(MINIMUM_MODEL_ELEMENT_SIZE, model.getRadius());
|
||||
|
||||
ModelTreeElement* element = (ModelTreeElement*)getOrCreateChildElementAt(position.x, position.y, position.z, size);
|
||||
ModelTreeElement* element = static_cast<ModelTreeElement*>(getOrCreateChildElementAt(position.x, position.y, position.z, size));
|
||||
element->storeModel(model);
|
||||
|
||||
_isDirty = true;
|
||||
|
|
|
@ -186,6 +186,11 @@ bool ModelTreeElement::findDetailedRayIntersection(const glm::vec3& origin, cons
|
|||
if (fbxGeometry && fbxGeometry->meshExtents.isValid()) {
|
||||
Extents extents = fbxGeometry->meshExtents;
|
||||
|
||||
// NOTE: If the model has a bad mesh, then extents will be 0,0,0 & 0,0,0
|
||||
if (extents.minimum == extents.maximum && extents.minimum == glm::vec3(0,0,0)) {
|
||||
extents.maximum = glm::vec3(1.0f,1.0f,1.0f); // in this case we will simulate the unit cube
|
||||
}
|
||||
|
||||
// NOTE: these extents are model space, so we need to scale and center them accordingly
|
||||
// size is our "target size in world space"
|
||||
// we need to set our model scale so that the extents of the mesh, fit in a cube that size...
|
||||
|
|
|
@ -313,7 +313,7 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas
|
|||
QByteArray postData;
|
||||
postData.append("grant_type=password&");
|
||||
postData.append("username=" + login + "&");
|
||||
postData.append("password=" + password + "&");
|
||||
postData.append("password=" + QUrl::toPercentEncoding(password) + "&");
|
||||
postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE);
|
||||
|
||||
request.setUrl(grantURL);
|
||||
|
|
|
@ -33,20 +33,7 @@ CapsuleShape::CapsuleShape(float radius, float halfHeight, const glm::vec3& posi
|
|||
|
||||
CapsuleShape::CapsuleShape(float radius, const glm::vec3& startPoint, const glm::vec3& endPoint) :
|
||||
Shape(Shape::CAPSULE_SHAPE), _radius(radius), _halfHeight(0.0f) {
|
||||
glm::vec3 axis = endPoint - startPoint;
|
||||
_position = 0.5f * (endPoint + startPoint);
|
||||
float height = glm::length(axis);
|
||||
if (height > EPSILON) {
|
||||
_halfHeight = 0.5f * height;
|
||||
axis /= height;
|
||||
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
|
||||
float angle = glm::angle(axis, yAxis);
|
||||
if (angle > EPSILON) {
|
||||
axis = glm::normalize(glm::cross(yAxis, axis));
|
||||
_rotation = glm::angleAxis(angle, axis);
|
||||
}
|
||||
}
|
||||
updateBoundingRadius();
|
||||
setEndPoints(startPoint, endPoint);
|
||||
}
|
||||
|
||||
/// \param[out] startPoint is the center of start cap
|
||||
|
@ -80,3 +67,20 @@ void CapsuleShape::setRadiusAndHalfHeight(float radius, float halfHeight) {
|
|||
updateBoundingRadius();
|
||||
}
|
||||
|
||||
void CapsuleShape::setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint) {
|
||||
glm::vec3 axis = endPoint - startPoint;
|
||||
_position = 0.5f * (endPoint + startPoint);
|
||||
float height = glm::length(axis);
|
||||
if (height > EPSILON) {
|
||||
_halfHeight = 0.5f * height;
|
||||
axis /= height;
|
||||
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
|
||||
float angle = glm::angle(axis, yAxis);
|
||||
if (angle > EPSILON) {
|
||||
axis = glm::normalize(glm::cross(yAxis, axis));
|
||||
_rotation = glm::angleAxis(angle, axis);
|
||||
}
|
||||
}
|
||||
updateBoundingRadius();
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ public:
|
|||
void setRadius(float radius);
|
||||
void setHalfHeight(float height);
|
||||
void setRadiusAndHalfHeight(float radius, float height);
|
||||
void setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint);
|
||||
|
||||
protected:
|
||||
void updateBoundingRadius() { _boundingRadius = _radius + _halfHeight; }
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <QScriptValueIterator>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include <MetavoxelMessages.h>
|
||||
|
@ -20,12 +22,16 @@
|
|||
REGISTER_META_OBJECT(TestSharedObjectA)
|
||||
REGISTER_META_OBJECT(TestSharedObjectB)
|
||||
|
||||
IMPLEMENT_ENUM_METATYPE(TestSharedObjectA, TestEnum)
|
||||
|
||||
MetavoxelTests::MetavoxelTests(int& argc, char** argv) :
|
||||
QCoreApplication(argc, argv) {
|
||||
}
|
||||
|
||||
static int datagramsSent = 0;
|
||||
static int datagramsReceived = 0;
|
||||
static int bytesSent = 0;
|
||||
static int bytesReceived = 0;
|
||||
static int highPriorityMessagesSent = 0;
|
||||
static int highPriorityMessagesReceived = 0;
|
||||
static int unreliableMessagesSent = 0;
|
||||
|
@ -36,6 +42,9 @@ static int streamedBytesSent = 0;
|
|||
static int streamedBytesReceived = 0;
|
||||
static int sharedObjectsCreated = 0;
|
||||
static int sharedObjectsDestroyed = 0;
|
||||
static int objectMutationsPerformed = 0;
|
||||
static int scriptObjectsCreated = 0;
|
||||
static int scriptMutationsPerformed = 0;
|
||||
|
||||
static QByteArray createRandomBytes(int minimumSize, int maximumSize) {
|
||||
QByteArray bytes(randIntInRange(minimumSize, maximumSize), 0);
|
||||
|
@ -74,12 +83,56 @@ static TestSharedObjectA::TestFlags getRandomTestFlags() {
|
|||
return flags;
|
||||
}
|
||||
|
||||
static QScriptValue createRandomScriptValue(bool complex = false) {
|
||||
scriptObjectsCreated++;
|
||||
switch (randIntInRange(0, complex ? 5 : 3)) {
|
||||
case 0:
|
||||
return QScriptValue(QScriptValue::NullValue);
|
||||
|
||||
case 1:
|
||||
return QScriptValue(randomBoolean());
|
||||
|
||||
case 2:
|
||||
return QScriptValue(randFloat());
|
||||
|
||||
case 3:
|
||||
return QScriptValue(QString(createRandomBytes()));
|
||||
|
||||
case 4: {
|
||||
int length = randIntInRange(2, 6);
|
||||
QScriptValue value = ScriptCache::getInstance()->getEngine()->newArray(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
value.setProperty(i, createRandomScriptValue());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
default: {
|
||||
QScriptValue value = ScriptCache::getInstance()->getEngine()->newObject();
|
||||
if (randomBoolean()) {
|
||||
value.setProperty("foo", createRandomScriptValue());
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
value.setProperty("bar", createRandomScriptValue());
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
value.setProperty("baz", createRandomScriptValue());
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
value.setProperty("bong", createRandomScriptValue());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static TestMessageC createRandomMessageC() {
|
||||
TestMessageC message;
|
||||
message.foo = randomBoolean();
|
||||
message.bar = rand();
|
||||
message.baz = randFloat();
|
||||
message.bong.foo = createRandomBytes();
|
||||
message.bong.baz = getRandomTestEnum();
|
||||
message.bizzle = createRandomScriptValue(true);
|
||||
return message;
|
||||
}
|
||||
|
||||
|
@ -180,7 +233,7 @@ bool MetavoxelTests::run() {
|
|||
bob.setOther(&alice);
|
||||
|
||||
// perform a large number of simulation iterations
|
||||
const int SIMULATION_ITERATIONS = 100000;
|
||||
const int SIMULATION_ITERATIONS = 10000;
|
||||
for (int i = 0; i < SIMULATION_ITERATIONS; i++) {
|
||||
if (alice.simulate(i) || bob.simulate(i)) {
|
||||
return true;
|
||||
|
@ -191,8 +244,11 @@ bool MetavoxelTests::run() {
|
|||
qDebug() << "Sent" << unreliableMessagesSent << "unreliable messages, received" << unreliableMessagesReceived;
|
||||
qDebug() << "Sent" << reliableMessagesSent << "reliable messages, received" << reliableMessagesReceived;
|
||||
qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived;
|
||||
qDebug() << "Sent" << datagramsSent << "datagrams, received" << datagramsReceived;
|
||||
qDebug() << "Sent" << datagramsSent << "datagrams with" << bytesSent << "bytes, received" <<
|
||||
datagramsReceived << "with" << bytesReceived << "bytes";
|
||||
qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed;
|
||||
qDebug() << "Performed" << objectMutationsPerformed << "object mutations";
|
||||
qDebug() << "Created" << scriptObjectsCreated << "script objects, mutated" << scriptMutationsPerformed;
|
||||
qDebug();
|
||||
|
||||
qDebug() << "Running serialization tests...";
|
||||
|
@ -226,6 +282,20 @@ Endpoint::Endpoint(const QByteArray& datagramHeader) :
|
|||
connect(_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)),
|
||||
SLOT(handleHighPriorityMessage(const QVariant&)));
|
||||
|
||||
connect(_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(clearSendRecordsBefore(int)));
|
||||
connect(_sequencer, SIGNAL(receiveAcknowledged(int)), SLOT(clearReceiveRecordsBefore(int)));
|
||||
|
||||
// insert the baseline send record
|
||||
SendRecord sendRecord = { 0 };
|
||||
_sendRecords.append(sendRecord);
|
||||
|
||||
// insert the baseline receive record
|
||||
ReceiveRecord receiveRecord = { 0 };
|
||||
_receiveRecords.append(receiveRecord);
|
||||
|
||||
// create the object that represents out delta-encoded state
|
||||
_localState = new TestSharedObjectA();
|
||||
|
||||
connect(_sequencer->getReliableInputChannel(), SIGNAL(receivedMessage(const QVariant&)),
|
||||
SLOT(handleReliableMessage(const QVariant&)));
|
||||
|
||||
|
@ -252,16 +322,72 @@ static QVariant createRandomMessage() {
|
|||
return QVariant::fromValue(message);
|
||||
}
|
||||
case 1: {
|
||||
TestMessageB message = { createRandomBytes(), createRandomSharedObject() };
|
||||
TestMessageB message = { createRandomBytes(), createRandomSharedObject(), getRandomTestEnum() };
|
||||
return QVariant::fromValue(message);
|
||||
}
|
||||
case 2:
|
||||
default: {
|
||||
return QVariant::fromValue(createRandomMessageC());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static SharedObjectPointer mutate(const SharedObjectPointer& state) {
|
||||
switch (randIntInRange(0, 4)) {
|
||||
case 0: {
|
||||
SharedObjectPointer newState = state->clone(true);
|
||||
static_cast<TestSharedObjectA*>(newState.data())->setFoo(randFloat());
|
||||
objectMutationsPerformed++;
|
||||
return newState;
|
||||
}
|
||||
case 1: {
|
||||
SharedObjectPointer newState = state->clone(true);
|
||||
static_cast<TestSharedObjectA*>(newState.data())->setBaz(getRandomTestEnum());
|
||||
objectMutationsPerformed++;
|
||||
return newState;
|
||||
}
|
||||
case 2: {
|
||||
SharedObjectPointer newState = state->clone(true);
|
||||
static_cast<TestSharedObjectA*>(newState.data())->setBong(getRandomTestFlags());
|
||||
objectMutationsPerformed++;
|
||||
return newState;
|
||||
}
|
||||
case 3: {
|
||||
SharedObjectPointer newState = state->clone(true);
|
||||
QScriptValue oldValue = static_cast<TestSharedObjectA*>(newState.data())->getBizzle();
|
||||
QScriptValue newValue = ScriptCache::getInstance()->getEngine()->newObject();
|
||||
for (QScriptValueIterator it(oldValue); it.hasNext(); ) {
|
||||
it.next();
|
||||
newValue.setProperty(it.scriptName(), it.value());
|
||||
}
|
||||
switch (randIntInRange(0, 2)) {
|
||||
case 0: {
|
||||
QScriptValue oldArray = oldValue.property("foo");
|
||||
int oldLength = oldArray.property(ScriptCache::getInstance()->getLengthString()).toInt32();
|
||||
QScriptValue newArray = ScriptCache::getInstance()->getEngine()->newArray(oldLength);
|
||||
for (int i = 0; i < oldLength; i++) {
|
||||
newArray.setProperty(i, oldArray.property(i));
|
||||
}
|
||||
newArray.setProperty(randIntInRange(0, oldLength - 1), createRandomScriptValue(true));
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
newValue.setProperty("bar", QScriptValue(randFloat()));
|
||||
break;
|
||||
|
||||
default:
|
||||
newValue.setProperty("baz", createRandomScriptValue(true));
|
||||
break;
|
||||
}
|
||||
static_cast<TestSharedObjectA*>(newState.data())->setBizzle(newValue);
|
||||
scriptMutationsPerformed++;
|
||||
objectMutationsPerformed++;
|
||||
return newState;
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
static bool messagesEqual(const QVariant& firstMessage, const QVariant& secondMessage) {
|
||||
int type = firstMessage.userType();
|
||||
if (secondMessage.userType() != type) {
|
||||
|
@ -273,7 +399,7 @@ static bool messagesEqual(const QVariant& firstMessage, const QVariant& secondMe
|
|||
} else if (type == TestMessageB::Type) {
|
||||
TestMessageB first = firstMessage.value<TestMessageB>();
|
||||
TestMessageB second = secondMessage.value<TestMessageB>();
|
||||
return first.foo == second.foo && equals(first.bar, second.bar);
|
||||
return first.foo == second.foo && equals(first.bar, second.bar) && first.baz == second.baz;
|
||||
|
||||
} else if (type == TestMessageC::Type) {
|
||||
return firstMessage.value<TestMessageC>() == secondMessage.value<TestMessageC>();
|
||||
|
@ -320,10 +446,13 @@ bool Endpoint::simulate(int iterationNumber) {
|
|||
_reliableMessagesToSend -= 1.0f;
|
||||
}
|
||||
|
||||
// tweak the local state
|
||||
_localState = mutate(_localState);
|
||||
|
||||
// send a packet
|
||||
try {
|
||||
Bitstream& out = _sequencer->startPacket();
|
||||
SequencedTestMessage message = { iterationNumber, createRandomMessage() };
|
||||
SequencedTestMessage message = { iterationNumber, createRandomMessage(), _localState };
|
||||
_unreliableMessagesSent.append(message);
|
||||
unreliableMessagesSent++;
|
||||
out << message;
|
||||
|
@ -334,11 +463,16 @@ bool Endpoint::simulate(int iterationNumber) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// record the send
|
||||
SendRecord record = { _sequencer->getOutgoingPacketNumber(), _localState };
|
||||
_sendRecords.append(record);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Endpoint::sendDatagram(const QByteArray& datagram) {
|
||||
datagramsSent++;
|
||||
bytesSent += datagram.size();
|
||||
|
||||
// some datagrams are dropped
|
||||
const float DROP_PROBABILITY = 0.1f;
|
||||
|
@ -364,6 +498,7 @@ void Endpoint::sendDatagram(const QByteArray& datagram) {
|
|||
|
||||
_other->_sequencer->receivedDatagram(datagram);
|
||||
datagramsReceived++;
|
||||
bytesReceived += datagram.size();
|
||||
}
|
||||
|
||||
void Endpoint::handleHighPriorityMessage(const QVariant& message) {
|
||||
|
@ -384,12 +519,21 @@ void Endpoint::readMessage(Bitstream& in) {
|
|||
SequencedTestMessage message;
|
||||
in >> message;
|
||||
|
||||
_remoteState = message.state;
|
||||
|
||||
// record the receipt
|
||||
ReceiveRecord record = { _sequencer->getIncomingPacketNumber(), message.state };
|
||||
_receiveRecords.append(record);
|
||||
|
||||
for (QList<SequencedTestMessage>::iterator it = _other->_unreliableMessagesSent.begin();
|
||||
it != _other->_unreliableMessagesSent.end(); it++) {
|
||||
if (it->sequenceNumber == message.sequenceNumber) {
|
||||
if (!messagesEqual(it->submessage, message.submessage)) {
|
||||
throw QString("Sent/received unreliable message mismatch.");
|
||||
}
|
||||
if (!it->state->equals(message.state)) {
|
||||
throw QString("Delta-encoded object mismatch.");
|
||||
}
|
||||
_other->_unreliableMessagesSent.erase(_other->_unreliableMessagesSent.begin(), it + 1);
|
||||
unreliableMessagesReceived++;
|
||||
return;
|
||||
|
@ -427,11 +571,22 @@ void Endpoint::readReliableChannel() {
|
|||
streamedBytesReceived += bytes.size();
|
||||
}
|
||||
|
||||
void Endpoint::clearSendRecordsBefore(int index) {
|
||||
_sendRecords.erase(_sendRecords.begin(), _sendRecords.begin() + index + 1);
|
||||
}
|
||||
|
||||
void Endpoint::clearReceiveRecordsBefore(int index) {
|
||||
_receiveRecords.erase(_receiveRecords.begin(), _receiveRecords.begin() + index + 1);
|
||||
}
|
||||
|
||||
TestSharedObjectA::TestSharedObjectA(float foo, TestEnum baz, TestFlags bong) :
|
||||
_foo(foo),
|
||||
_baz(baz),
|
||||
_bong(bong) {
|
||||
sharedObjectsCreated++;
|
||||
sharedObjectsCreated++;
|
||||
|
||||
_bizzle = ScriptCache::getInstance()->getEngine()->newObject();
|
||||
_bizzle.setProperty("foo", ScriptCache::getInstance()->getEngine()->newArray(4));
|
||||
}
|
||||
|
||||
TestSharedObjectA::~TestSharedObjectA() {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <QVariantList>
|
||||
|
||||
#include <DatagramSequencer.h>
|
||||
#include <ScriptCache.h>
|
||||
|
||||
class SequencedTestMessage;
|
||||
|
||||
|
@ -54,9 +55,30 @@ private slots:
|
|||
void handleReliableMessage(const QVariant& message);
|
||||
void readReliableChannel();
|
||||
|
||||
void clearSendRecordsBefore(int index);
|
||||
void clearReceiveRecordsBefore(int index);
|
||||
|
||||
private:
|
||||
|
||||
class SendRecord {
|
||||
public:
|
||||
int packetNumber;
|
||||
SharedObjectPointer localState;
|
||||
};
|
||||
|
||||
class ReceiveRecord {
|
||||
public:
|
||||
int packetNumber;
|
||||
SharedObjectPointer remoteState;
|
||||
};
|
||||
|
||||
DatagramSequencer* _sequencer;
|
||||
QList<SendRecord> _sendRecords;
|
||||
QList<ReceiveRecord> _receiveRecords;
|
||||
|
||||
SharedObjectPointer _localState;
|
||||
SharedObjectPointer _remoteState;
|
||||
|
||||
Endpoint* _other;
|
||||
QList<QPair<QByteArray, int> > _delayedDatagrams;
|
||||
float _highPriorityMessagesToSend;
|
||||
|
@ -75,7 +97,8 @@ class TestSharedObjectA : public SharedObject {
|
|||
Q_PROPERTY(float foo READ getFoo WRITE setFoo NOTIFY fooChanged)
|
||||
Q_PROPERTY(TestEnum baz READ getBaz WRITE setBaz)
|
||||
Q_PROPERTY(TestFlags bong READ getBong WRITE setBong)
|
||||
|
||||
Q_PROPERTY(QScriptValue bizzle READ getBizzle WRITE setBizzle)
|
||||
|
||||
public:
|
||||
|
||||
enum TestEnum { FIRST_TEST_ENUM, SECOND_TEST_ENUM, THIRD_TEST_ENUM };
|
||||
|
@ -95,6 +118,9 @@ public:
|
|||
void setBong(TestFlags bong) { _bong = bong; }
|
||||
TestFlags getBong() const { return _bong; }
|
||||
|
||||
void setBizzle(const QScriptValue& bizzle) { _bizzle = bizzle; }
|
||||
const QScriptValue& getBizzle() const { return _bizzle; }
|
||||
|
||||
signals:
|
||||
|
||||
void fooChanged(float foo);
|
||||
|
@ -104,8 +130,11 @@ private:
|
|||
float _foo;
|
||||
TestEnum _baz;
|
||||
TestFlags _bong;
|
||||
QScriptValue _bizzle;
|
||||
};
|
||||
|
||||
DECLARE_ENUM_METATYPE(TestSharedObjectA, TestEnum)
|
||||
|
||||
/// Another simple shared object.
|
||||
class TestSharedObjectB : public SharedObject {
|
||||
Q_OBJECT
|
||||
|
@ -169,6 +198,7 @@ public:
|
|||
|
||||
STREAM QByteArray foo;
|
||||
STREAM SharedObjectPointer bar;
|
||||
STREAM TestSharedObjectA::TestEnum baz;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(TestMessageB)
|
||||
|
@ -180,6 +210,7 @@ class TestMessageC : STREAM public TestMessageA {
|
|||
public:
|
||||
|
||||
STREAM TestMessageB bong;
|
||||
STREAM QScriptValue bizzle;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(TestMessageC)
|
||||
|
@ -192,6 +223,7 @@ public:
|
|||
|
||||
STREAM int sequenceNumber;
|
||||
STREAM QVariant submessage;
|
||||
STREAM SharedObjectPointer state;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(SequencedTestMessage)
|
||||
|
|
Loading…
Reference in a new issue