mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 14:58:03 +02:00
Merge branch 'master' into ajt/new-anim-system
This commit is contained in:
commit
df28c7bf1a
37 changed files with 1072 additions and 255 deletions
|
@ -1,5 +1,4 @@
|
||||||
High Fidelity (hifi) is an early-stage technology
|
High Fidelity (hifi) is an early-stage technology lab experimenting with Virtual Worlds and VR.
|
||||||
lab experimenting with Virtual Worlds and VR.
|
|
||||||
|
|
||||||
In this repository you'll find the source to many of the components in our
|
In this repository you'll find the source to many of the components in our
|
||||||
alpha-stage virtual world. The project embraces distributed development
|
alpha-stage virtual world. The project embraces distributed development
|
||||||
|
|
|
@ -17,3 +17,4 @@ Script.load("users.js");
|
||||||
Script.load("grab.js");
|
Script.load("grab.js");
|
||||||
Script.load("directory.js");
|
Script.load("directory.js");
|
||||||
Script.load("dialTone.js");
|
Script.load("dialTone.js");
|
||||||
|
Script.load("libraries/omniTool.js");
|
||||||
|
|
25
examples/html/magBalls/addMode.html
Normal file
25
examples/html/magBalls/addMode.html
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" href="../style.css">
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
display:table;
|
||||||
|
position:absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 25vw;
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
line-height: normal;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container"><span>Add</span></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
25
examples/html/magBalls/deleteMode.html
Normal file
25
examples/html/magBalls/deleteMode.html
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" href="../style.css">
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
display:table;
|
||||||
|
position:absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 25vw;
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
line-height: normal;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container"><span>Delete</span></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
14
examples/html/magBalls/magBalls.css
Normal file
14
examples/html/magBalls/magBalls.css
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
.container {
|
||||||
|
display:table;
|
||||||
|
position:absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 25vw;
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
line-height: normal;
|
||||||
|
text-align: center;
|
||||||
|
}
|
25
examples/html/magBalls/moveMode.html
Normal file
25
examples/html/magBalls/moveMode.html
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" href="../style.css">
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
display:table;
|
||||||
|
position:absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 25vw;
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
line-height: normal;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container"><span>Move</span></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
55
examples/libraries/avatarRelativeOverlays.js
Normal file
55
examples/libraries/avatarRelativeOverlays.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
|
||||||
|
AvatarRelativeOverlays = function() {
|
||||||
|
// id -> position & rotation
|
||||||
|
this.overlays = {};
|
||||||
|
this.lastAvatarTransform = {
|
||||||
|
position: ZERO_VECTOR,
|
||||||
|
rotation: IDENTITY_QUATERNION,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME judder in movement is annoying.... add an option to
|
||||||
|
// automatically hide all overlays when the position or orientation change and then
|
||||||
|
// restore the ones that were previously visible once the movement stops.
|
||||||
|
AvatarRelativeOverlays.prototype.onUpdate = function(deltaTime) {
|
||||||
|
// cache avatar position and orientation and only update on change
|
||||||
|
if (Vec3.equal(this.lastAvatarTransform.position, MyAvatar.position) &&
|
||||||
|
Quat.equal(this.lastAvatarTransform.rotation, MyAvatar.orientation)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastAvatarTransform.position = MyAvatar.position;
|
||||||
|
this.lastAvatarTransform.rotation = MyAvatar.orientation;
|
||||||
|
for (var overlayId in this.overlays) {
|
||||||
|
this.updateOverlayTransform(overlayId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AvatarRelativeOverlays.prototype.updateOverlayTransform = function(overlayId) {
|
||||||
|
Overlays.editOverlay(overlayId, {
|
||||||
|
position: getEyeRelativePosition(this.overlays[overlayId].position),
|
||||||
|
rotation: getAvatarRelativeRotation(this.overlays[overlayId].rotation),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
AvatarRelativeOverlays.prototype.addOverlay = function(type, overlayDefinition) {
|
||||||
|
var overlayId = Overlays.addOverlay(type, overlayDefinition);
|
||||||
|
if (!overlayId) {
|
||||||
|
logDebug("Failed to create overlay of type " + type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.overlays[overlayId] = {
|
||||||
|
position: overlayDefinition.position || ZERO_VECTOR,
|
||||||
|
rotation: overlayDefinition.rotation || IDENTITY_QUATERNION,
|
||||||
|
};
|
||||||
|
this.updateOverlayTransform(overlayId);
|
||||||
|
return overlayId;
|
||||||
|
}
|
||||||
|
|
||||||
|
AvatarRelativeOverlays.prototype.deleteAll = function() {
|
||||||
|
for (var overlayId in this.overlays) {
|
||||||
|
Overlays.deleteOverlay(overlayId);
|
||||||
|
}
|
||||||
|
this.overlays = {};
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ STICK_URL = HIFI_PUBLIC_BUCKET + "models/props/geo_stick.fbx";
|
||||||
|
|
||||||
ZERO_VECTOR = { x: 0, y: 0, z: 0 };
|
ZERO_VECTOR = { x: 0, y: 0, z: 0 };
|
||||||
|
|
||||||
|
IDENTITY_QUATERNION = { w: 1, x: 0, y: 0, z: 0 };
|
||||||
|
|
||||||
COLORS = {
|
COLORS = {
|
||||||
WHITE: {
|
WHITE: {
|
||||||
red: 255,
|
red: 255,
|
||||||
|
|
|
@ -29,7 +29,7 @@ var SELECTION_OVERLAY = {
|
||||||
|
|
||||||
Highlighter = function() {
|
Highlighter = function() {
|
||||||
this.highlightCube = Overlays.addOverlay("cube", this.SELECTION_OVERLAY);
|
this.highlightCube = Overlays.addOverlay("cube", this.SELECTION_OVERLAY);
|
||||||
this.hightlighted = null;
|
this.highlighted = null;
|
||||||
var _this = this;
|
var _this = this;
|
||||||
Script.scriptEnding.connect(function() {
|
Script.scriptEnding.connect(function() {
|
||||||
_this.onCleanup();
|
_this.onCleanup();
|
||||||
|
@ -40,9 +40,9 @@ Highlighter.prototype.onCleanup = function() {
|
||||||
Overlays.deleteOverlay(this.highlightCube);
|
Overlays.deleteOverlay(this.highlightCube);
|
||||||
}
|
}
|
||||||
|
|
||||||
Highlighter.prototype.highlight = function(entityId) {
|
Highlighter.prototype.highlight = function(entityIdOrPosition) {
|
||||||
if (entityId != this.hightlighted) {
|
if (entityIdOrPosition != this.highlighted) {
|
||||||
this.hightlighted = entityId;
|
this.highlighted = entityIdOrPosition;
|
||||||
this.updateHighlight();
|
this.updateHighlight();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,13 @@ Highlighter.prototype.setSize = function(newSize) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Highlighter.prototype.setColor = function(color) {
|
||||||
|
Overlays.editOverlay(this.highlightCube, {
|
||||||
|
color: color
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Highlighter.prototype.setRotation = function(newRotation) {
|
Highlighter.prototype.setRotation = function(newRotation) {
|
||||||
Overlays.editOverlay(this.highlightCube, {
|
Overlays.editOverlay(this.highlightCube, {
|
||||||
rotation: newRotation
|
rotation: newRotation
|
||||||
|
@ -60,11 +67,15 @@ Highlighter.prototype.setRotation = function(newRotation) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Highlighter.prototype.updateHighlight = function() {
|
Highlighter.prototype.updateHighlight = function() {
|
||||||
if (this.hightlighted) {
|
if (this.highlighted) {
|
||||||
var properties = Entities.getEntityProperties(this.hightlighted);
|
var position = this.highlighted;
|
||||||
|
if (typeof this.highlighted === "string") {
|
||||||
|
var properties = Entities.getEntityProperties(this.highlighted);
|
||||||
|
position = properties.position;
|
||||||
|
}
|
||||||
// logDebug("Making highlight " + this.highlightCube + " visible @ " + vec3toStr(properties.position));
|
// logDebug("Making highlight " + this.highlightCube + " visible @ " + vec3toStr(properties.position));
|
||||||
Overlays.editOverlay(this.highlightCube, {
|
Overlays.editOverlay(this.highlightCube, {
|
||||||
position: properties.position,
|
position: position,
|
||||||
visible: true
|
visible: true
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -11,13 +11,14 @@ Script.include("utils.js");
|
||||||
Script.include("highlighter.js");
|
Script.include("highlighter.js");
|
||||||
Script.include("omniTool/models/modelBase.js");
|
Script.include("omniTool/models/modelBase.js");
|
||||||
Script.include("omniTool/models/wand.js");
|
Script.include("omniTool/models/wand.js");
|
||||||
|
Script.include("omniTool/models/invisibleWand.js");
|
||||||
|
|
||||||
OmniToolModules = {};
|
OmniToolModules = {};
|
||||||
OmniToolModuleType = null;
|
OmniToolModuleType = null;
|
||||||
|
|
||||||
OmniTool = function(side) {
|
OmniTool = function(side) {
|
||||||
this.OMNI_KEY = "OmniTool";
|
this.OMNI_KEY = "OmniTool";
|
||||||
this.MAX_FRAMERATE = 30;
|
this.MAX_FRAMERATE = 60;
|
||||||
this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE
|
this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE
|
||||||
this.SIDE = side;
|
this.SIDE = side;
|
||||||
this.PALM = 2 * side;
|
this.PALM = 2 * side;
|
||||||
|
@ -28,6 +29,7 @@ OmniTool = function(side) {
|
||||||
this.ignoreEntities = {};
|
this.ignoreEntities = {};
|
||||||
this.nearestOmniEntity = {
|
this.nearestOmniEntity = {
|
||||||
id: null,
|
id: null,
|
||||||
|
|
||||||
inside: false,
|
inside: false,
|
||||||
position: null,
|
position: null,
|
||||||
distance: Infinity,
|
distance: Infinity,
|
||||||
|
@ -38,13 +40,11 @@ OmniTool = function(side) {
|
||||||
|
|
||||||
this.activeOmniEntityId = null;
|
this.activeOmniEntityId = null;
|
||||||
this.lastUpdateInterval = 0;
|
this.lastUpdateInterval = 0;
|
||||||
this.tipLength = 0.4;
|
|
||||||
this.active = false;
|
this.active = false;
|
||||||
this.module = null;
|
this.module = null;
|
||||||
this.moduleEntityId = null;
|
this.moduleEntityId = null;
|
||||||
this.lastScanPosition = ZERO_VECTOR;
|
this.lastScanPosition = ZERO_VECTOR;
|
||||||
this.model = new Wand();
|
this.showWand(false);
|
||||||
this.model.setLength(this.tipLength);
|
|
||||||
|
|
||||||
// Connect to desired events
|
// Connect to desired events
|
||||||
var _this = this;
|
var _this = this;
|
||||||
|
@ -65,6 +65,23 @@ OmniTool = function(side) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OmniTool.prototype.showWand = function(show) {
|
||||||
|
if (this.model && this.model.onCleanup) {
|
||||||
|
this.model.onCleanup();
|
||||||
|
}
|
||||||
|
logDebug("Showing wand: " + show);
|
||||||
|
if (show) {
|
||||||
|
this.model = new Wand();
|
||||||
|
this.model.setLength(0.4);
|
||||||
|
this.model.setVisible(true);
|
||||||
|
} else {
|
||||||
|
this.model = new InvisibleWand();
|
||||||
|
this.model.setLength(0.1);
|
||||||
|
this.model.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
OmniTool.prototype.onCleanup = function(action) {
|
OmniTool.prototype.onCleanup = function(action) {
|
||||||
this.unloadModule();
|
this.unloadModule();
|
||||||
}
|
}
|
||||||
|
@ -110,10 +127,9 @@ OmniTool.prototype.setActive = function(active) {
|
||||||
if (active === this.active) {
|
if (active === this.active) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logDebug("omnitool changing active state: " + active);
|
logDebug("OmniTool changing active state: " + active);
|
||||||
this.active = active;
|
this.active = active;
|
||||||
this.model.setVisible(this.active);
|
this.model.setVisible(this.active);
|
||||||
|
|
||||||
if (this.module && this.module.onActiveChanged) {
|
if (this.module && this.module.onActiveChanged) {
|
||||||
this.module.onActiveChanged(this.side);
|
this.module.onActiveChanged(this.side);
|
||||||
}
|
}
|
||||||
|
@ -125,14 +141,25 @@ OmniTool.prototype.onUpdate = function(deltaTime) {
|
||||||
this.position = Controller.getSpatialControlPosition(this.PALM);
|
this.position = Controller.getSpatialControlPosition(this.PALM);
|
||||||
// When on the base, hydras report a position of 0
|
// When on the base, hydras report a position of 0
|
||||||
this.setActive(Vec3.length(this.position) > 0.001);
|
this.setActive(Vec3.length(this.position) > 0.001);
|
||||||
|
if (!this.active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (this.model) {
|
||||||
|
// Update the wand
|
||||||
|
var rawRotation = Controller.getSpatialControlRawRotation(this.PALM);
|
||||||
|
this.rotation = Quat.multiply(MyAvatar.orientation, rawRotation);
|
||||||
|
this.model.setTransform({
|
||||||
|
rotation: this.rotation,
|
||||||
|
position: this.position,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.model.onUpdate) {
|
||||||
|
this.model.onUpdate(deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var rawRotation = Controller.getSpatialControlRawRotation(this.PALM);
|
|
||||||
this.rotation = Quat.multiply(MyAvatar.orientation, rawRotation);
|
|
||||||
|
|
||||||
this.model.setTransform({
|
|
||||||
rotation: this.rotation,
|
|
||||||
position: this.position,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.scan();
|
this.scan();
|
||||||
|
|
||||||
|
@ -144,6 +171,19 @@ OmniTool.prototype.onUpdate = function(deltaTime) {
|
||||||
OmniTool.prototype.onClick = function() {
|
OmniTool.prototype.onClick = function() {
|
||||||
// First check to see if the user is switching to a new omni module
|
// First check to see if the user is switching to a new omni module
|
||||||
if (this.nearestOmniEntity.inside && this.nearestOmniEntity.omniProperties.script) {
|
if (this.nearestOmniEntity.inside && this.nearestOmniEntity.omniProperties.script) {
|
||||||
|
|
||||||
|
// If this is already the active entity, turn it off
|
||||||
|
// FIXME add a flag to allow omni modules to cause this entity to be
|
||||||
|
// ignored in order to support items that will be picked up.
|
||||||
|
if (this.moduleEntityId && this.moduleEntityId == this.nearestOmniEntity.id) {
|
||||||
|
this.showWand(false);
|
||||||
|
this.unloadModule();
|
||||||
|
this.highlighter.setColor("White");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showWand(true);
|
||||||
|
this.highlighter.setColor("Red");
|
||||||
this.activateNewOmniModule();
|
this.activateNewOmniModule();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -157,12 +197,10 @@ OmniTool.prototype.onClick = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
OmniTool.prototype.onRelease = function() {
|
OmniTool.prototype.onRelease = function() {
|
||||||
// FIXME how to I switch to a new module?
|
|
||||||
if (this.module && this.module.onRelease) {
|
if (this.module && this.module.onRelease) {
|
||||||
this.module.onRelease();
|
this.module.onRelease();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logDebug("Base omnitool does nothing on release");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME resturn a structure of all nearby entities to distances
|
// FIXME resturn a structure of all nearby entities to distances
|
||||||
|
@ -210,6 +248,11 @@ OmniTool.prototype.getPosition = function() {
|
||||||
OmniTool.prototype.onEnterNearestOmniEntity = function() {
|
OmniTool.prototype.onEnterNearestOmniEntity = function() {
|
||||||
this.nearestOmniEntity.inside = true;
|
this.nearestOmniEntity.inside = true;
|
||||||
this.highlighter.highlight(this.nearestOmniEntity.id);
|
this.highlighter.highlight(this.nearestOmniEntity.id);
|
||||||
|
if (this.moduleEntityId && this.moduleEntityId == this.nearestOmniEntity.id) {
|
||||||
|
this.highlighter.setColor("Red");
|
||||||
|
} else {
|
||||||
|
this.highlighter.setColor("White");
|
||||||
|
}
|
||||||
logDebug("On enter omniEntity " + this.nearestOmniEntity.id);
|
logDebug("On enter omniEntity " + this.nearestOmniEntity.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
6
examples/libraries/omniTool/models/invisibleWand.js
Normal file
6
examples/libraries/omniTool/models/invisibleWand.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
InvisibleWand = function() {
|
||||||
|
}
|
||||||
|
|
||||||
|
InvisibleWand.prototype = Object.create( ModelBase.prototype );
|
||||||
|
|
|
@ -17,3 +17,9 @@ ModelBase.prototype.setTransform = function(transform) {
|
||||||
this.tipVector = Vec3.multiplyQbyV(this.rotation, { x: 0, y: this.length, z: 0 });
|
this.tipVector = Vec3.multiplyQbyV(this.rotation, { x: 0, y: this.length, z: 0 });
|
||||||
this.tipPosition = Vec3.sum(this.position, this.tipVector);
|
this.tipPosition = Vec3.sum(this.position, this.tipVector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModelBase.prototype.setTipColors = function(color1, color2) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelBase.prototype.onCleanup = function() {
|
||||||
|
}
|
||||||
|
|
|
@ -1,22 +1,18 @@
|
||||||
|
|
||||||
Wand = function() {
|
Wand = function() {
|
||||||
// Max updates fps
|
// Max updates fps
|
||||||
this.MAX_FRAMERATE = 30
|
|
||||||
this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE
|
|
||||||
this.DEFAULT_TIP_COLORS = [ {
|
this.DEFAULT_TIP_COLORS = [ {
|
||||||
red: 128,
|
red: 128,
|
||||||
green: 128,
|
green: 128,
|
||||||
blue: 128,
|
blue: 128,
|
||||||
}, {
|
}, {
|
||||||
red: 64,
|
red: 0,
|
||||||
green: 64,
|
green: 0,
|
||||||
blue: 64,
|
blue: 0,
|
||||||
}];
|
}];
|
||||||
this.POINTER_ROTATION = Quat.fromPitchYawRollDegrees(45, 0, 45);
|
this.POINTER_ROTATION = Quat.fromPitchYawRollDegrees(45, 0, 45);
|
||||||
|
|
||||||
// FIXME does this need to be a member of this?
|
|
||||||
this.lastUpdateInterval = 0;
|
|
||||||
|
|
||||||
this.pointers = [
|
this.pointers = [
|
||||||
Overlays.addOverlay("cube", {
|
Overlays.addOverlay("cube", {
|
||||||
position: ZERO_VECTOR,
|
position: ZERO_VECTOR,
|
||||||
|
@ -45,17 +41,7 @@ Wand = function() {
|
||||||
|
|
||||||
var _this = this;
|
var _this = this;
|
||||||
Script.scriptEnding.connect(function() {
|
Script.scriptEnding.connect(function() {
|
||||||
Overlays.deleteOverlay(_this.pointers[0]);
|
_this.onCleanup();
|
||||||
Overlays.deleteOverlay(_this.pointers[1]);
|
|
||||||
Overlays.deleteOverlay(_this.wand);
|
|
||||||
});
|
|
||||||
|
|
||||||
Script.update.connect(function(deltaTime) {
|
|
||||||
_this.lastUpdateInterval += deltaTime;
|
|
||||||
if (_this.lastUpdateInterval >= _this.UPDATE_INTERVAL) {
|
|
||||||
_this.onUpdate(_this.lastUpdateInterval);
|
|
||||||
_this.lastUpdateInterval = 0;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +62,6 @@ Wand.prototype.setVisible = function(visible) {
|
||||||
|
|
||||||
Wand.prototype.setTransform = function(transform) {
|
Wand.prototype.setTransform = function(transform) {
|
||||||
ModelBase.prototype.setTransform.call(this, transform);
|
ModelBase.prototype.setTransform.call(this, transform);
|
||||||
|
|
||||||
var wandPosition = Vec3.sum(this.position, Vec3.multiply(0.5, this.tipVector));
|
var wandPosition = Vec3.sum(this.position, Vec3.multiply(0.5, this.tipVector));
|
||||||
Overlays.editOverlay(this.pointers[0], {
|
Overlays.editOverlay(this.pointers[0], {
|
||||||
position: this.tipPosition,
|
position: this.tipPosition,
|
||||||
|
@ -106,7 +91,10 @@ Wand.prototype.setTipColors = function(color1, color2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Wand.prototype.onUpdate = function(deltaTime) {
|
Wand.prototype.onUpdate = function(deltaTime) {
|
||||||
|
logDebug("Z4");
|
||||||
|
|
||||||
if (this.visible) {
|
if (this.visible) {
|
||||||
|
logDebug("5");
|
||||||
var time = new Date().getTime() / 250;
|
var time = new Date().getTime() / 250;
|
||||||
var scale1 = Math.abs(Math.sin(time));
|
var scale1 = Math.abs(Math.sin(time));
|
||||||
var scale2 = Math.abs(Math.cos(time));
|
var scale2 = Math.abs(Math.cos(time));
|
||||||
|
@ -118,3 +106,9 @@ Wand.prototype.onUpdate = function(deltaTime) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Wand.prototype.onCleanup = function() {
|
||||||
|
Overlays.deleteOverlay(this.pointers[0]);
|
||||||
|
Overlays.deleteOverlay(this.pointers[1]);
|
||||||
|
Overlays.deleteOverlay(this.wand);
|
||||||
|
}
|
|
@ -1,9 +1,36 @@
|
||||||
|
|
||||||
OmniToolModules.Test = function() {
|
Script.include("avatarRelativeOverlays.js");
|
||||||
|
|
||||||
|
OmniToolModules.Test = function(omniTool, activeEntityId) {
|
||||||
|
this.omniTool = omniTool;
|
||||||
|
this.activeEntityId = activeEntityId;
|
||||||
|
this.avatarOverlays = new AvatarRelativeOverlays();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OmniToolModules.Test.prototype.onUnload = function() {
|
||||||
|
if (this.testOverlay) {
|
||||||
|
Overlays.deleteOverlay(this.testOverlay);
|
||||||
|
this.testOverlay = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var CUBE_POSITION = {
|
||||||
|
x: 0.1,
|
||||||
|
y: -0.1,
|
||||||
|
z: -0.4
|
||||||
|
};
|
||||||
|
|
||||||
OmniToolModules.Test.prototype.onClick = function() {
|
OmniToolModules.Test.prototype.onClick = function() {
|
||||||
logDebug("Test module onClick");
|
if (this.testOverlay) {
|
||||||
|
Overlays.deleteOverlay(this.testOverlay);
|
||||||
|
this.testOverlay = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
OmniToolModules.Test.prototype.onUpdate = function(deltaTime) {
|
||||||
|
this.avatarOverlays.onUpdate(deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
OmniToolModuleType = "Test"
|
OmniToolModuleType = "Test"
|
|
@ -11,6 +11,15 @@ vec3toStr = function (v, digits) {
|
||||||
return "{ " + v.x.toFixed(digits) + ", " + v.y.toFixed(digits) + ", " + v.z.toFixed(digits)+ " }";
|
return "{ " + v.x.toFixed(digits) + ", " + v.y.toFixed(digits) + ", " + v.z.toFixed(digits)+ " }";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
colorMix = function(colorA, colorB, mix) {
|
||||||
|
var result = {};
|
||||||
|
for (var key in colorA) {
|
||||||
|
result[key] = (colorA[key] * (1 - mix)) + (colorB[key] * mix);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
scaleLine = function (start, end, scale) {
|
scaleLine = function (start, end, scale) {
|
||||||
var v = Vec3.subtract(end, start);
|
var v = Vec3.subtract(end, start);
|
||||||
var length = Vec3.length(v);
|
var length = Vec3.length(v);
|
||||||
|
@ -127,3 +136,13 @@ findSpherePointHit = function(sphereCenter, sphereRadius, point) {
|
||||||
findSphereSphereHit = function(firstCenter, firstRadius, secondCenter, secondRadius) {
|
findSphereSphereHit = function(firstCenter, firstRadius, secondCenter, secondRadius) {
|
||||||
return findSpherePointHit(firstCenter, firstRadius + secondRadius, secondCenter);
|
return findSpherePointHit(firstCenter, firstRadius + secondRadius, secondCenter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Given a vec3 v, return a vec3 that is the same vector relative to the avatars
|
||||||
|
// DEFAULT eye position, rotated into the avatars reference frame.
|
||||||
|
getEyeRelativePosition = function(v) {
|
||||||
|
return Vec3.sum(MyAvatar.getDefaultEyePosition(), Vec3.multiplyQbyV(MyAvatar.orientation, v));
|
||||||
|
}
|
||||||
|
|
||||||
|
getAvatarRelativeRotation = function(q) {
|
||||||
|
return Quat.multiply(MyAvatar.orientation, q);
|
||||||
|
}
|
||||||
|
|
|
@ -11,21 +11,10 @@ Script.include("../toys/magBalls/constants.js");
|
||||||
Script.include("../toys/magBalls/graph.js");
|
Script.include("../toys/magBalls/graph.js");
|
||||||
Script.include("../toys/magBalls/edgeSpring.js");
|
Script.include("../toys/magBalls/edgeSpring.js");
|
||||||
Script.include("../toys/magBalls/magBalls.js");
|
Script.include("../toys/magBalls/magBalls.js");
|
||||||
|
Script.include("avatarRelativeOverlays.js");
|
||||||
|
|
||||||
OmniToolModuleType = "MagBallsController"
|
OmniToolModuleType = "MagBallsController"
|
||||||
|
|
||||||
|
|
||||||
OmniToolModules.MagBallsController = function(omniTool, entityId) {
|
|
||||||
this.omniTool = omniTool;
|
|
||||||
this.entityId = entityId;
|
|
||||||
this.highlighter = new Highlighter();
|
|
||||||
this.magBalls = new MagBalls();
|
|
||||||
this.highlighter.setSize(BALL_SIZE);
|
|
||||||
this.ghostEdges = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
var MAG_BALLS_DATA_NAME = "magBalls";
|
|
||||||
|
|
||||||
getMagBallsData = function(id) {
|
getMagBallsData = function(id) {
|
||||||
return getEntityCustomData(MAG_BALLS_DATA_NAME, id, {});
|
return getEntityCustomData(MAG_BALLS_DATA_NAME, id, {});
|
||||||
}
|
}
|
||||||
|
@ -34,54 +23,177 @@ setMagBallsData = function(id, value) {
|
||||||
setEntityCustomData(MAG_BALLS_DATA_NAME, id, value);
|
setEntityCustomData(MAG_BALLS_DATA_NAME, id, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
//var magBalls = new MagBalls();
|
var UI_BALL_RADIUS = 0.01;
|
||||||
// DEBUGGING ONLY - Clear any previous balls
|
var MODE_INFO = { };
|
||||||
// magBalls.clear();
|
|
||||||
|
|
||||||
OmniToolModules.MagBallsController.prototype.onClick = function() {
|
MODE_INFO[BALL_EDIT_MODE_ADD] = {
|
||||||
logDebug("MagBallsController onClick: " + vec3toStr(this.tipPosition));
|
uiPosition: {
|
||||||
|
x: 0.15,
|
||||||
|
y: -0.08,
|
||||||
|
z: -0.35,
|
||||||
|
},
|
||||||
|
colors: [ COLORS.GREEN, COLORS.BLUE ],
|
||||||
|
// FIXME use an http path or find a way to get the relative path to the file
|
||||||
|
url: "file:///" + Script.resolvePath('../html/magBalls/addMode.html').replace("c:", "C:"),
|
||||||
|
};
|
||||||
|
|
||||||
|
MODE_INFO[BALL_EDIT_MODE_DELETE] = {
|
||||||
|
uiPosition: {
|
||||||
|
x: 0.20,
|
||||||
|
y: -0.08,
|
||||||
|
z: -0.32,
|
||||||
|
},
|
||||||
|
colors: [ COLORS.RED, COLORS.BLUE ],
|
||||||
|
// FIXME use an http path or find a way to get the relative path to the file
|
||||||
|
url: "file:///" + Script.resolvePath('../html/magBalls/deleteMode.html').replace("c:", "C:"),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var UI_POSITION_MODE_LABEL = Vec3.multiply(0.5,
|
||||||
|
Vec3.sum(MODE_INFO[BALL_EDIT_MODE_ADD].uiPosition,
|
||||||
|
MODE_INFO[BALL_EDIT_MODE_DELETE].uiPosition));
|
||||||
|
|
||||||
|
UI_POSITION_MODE_LABEL.y = -0.02;
|
||||||
|
|
||||||
|
var UI_BALL_PROTOTYPE = {
|
||||||
|
size: UI_BALL_RADIUS * 2.0,
|
||||||
|
alpha: 1.0,
|
||||||
|
solid: true,
|
||||||
|
visible: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
OmniToolModules.MagBallsController = function(omniTool, entityId) {
|
||||||
|
this.omniTool = omniTool;
|
||||||
|
this.entityId = entityId;
|
||||||
|
|
||||||
|
// In hold mode, holding a ball requires that you keep the action
|
||||||
|
// button pressed, while if this is false, clicking on a ball selects
|
||||||
|
// it and clicking again will drop it.
|
||||||
|
this.holdMode = true;
|
||||||
|
|
||||||
|
this.highlighter = new Highlighter();
|
||||||
|
this.magBalls = new MagBalls();
|
||||||
|
this.highlighter.setSize(BALL_SIZE);
|
||||||
|
this.ghostEdges = {};
|
||||||
|
this.selectionRadiusMultipler = 1.5;
|
||||||
|
this.uiOverlays = new AvatarRelativeOverlays();
|
||||||
|
|
||||||
this.selected = this.highlighter.hightlighted;
|
|
||||||
logDebug("This selected: " + this.selected);
|
// create the overlay relative to the avatar
|
||||||
if (!this.selected) {
|
this.uiOverlays.addOverlay("sphere", mergeObjects(UI_BALL_PROTOTYPE, {
|
||||||
this.selected = this.magBalls.createBall(this.tipPosition);
|
color: MODE_INFO[BALL_EDIT_MODE_ADD].colors[0],
|
||||||
}
|
position: MODE_INFO[BALL_EDIT_MODE_ADD].uiPosition,
|
||||||
this.magBalls.selectBall(this.selected);
|
}));
|
||||||
this.highlighter.highlight(null);
|
this.uiOverlays.addOverlay("sphere", mergeObjects(UI_BALL_PROTOTYPE, {
|
||||||
logDebug("Selected " + this.selected);
|
color: MODE_INFO[BALL_EDIT_MODE_DELETE].colors[0],
|
||||||
|
position: MODE_INFO[BALL_EDIT_MODE_DELETE].uiPosition,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// FIXME find the proper URLs to use
|
||||||
|
this.modeLabel = this.uiOverlays.addOverlay("web3d", {
|
||||||
|
isFacingAvatar: true,
|
||||||
|
alpha: 1.0,
|
||||||
|
dimensions: { x: 0.16, y: 0.12, z: 0.001},
|
||||||
|
color: "White",
|
||||||
|
position: UI_POSITION_MODE_LABEL,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setMode(BALL_EDIT_MODE_ADD);
|
||||||
|
|
||||||
|
// DEBUGGING ONLY - Fix old, bad edge bounding boxes
|
||||||
|
//for (var edgeId in this.magBalls.edges) {
|
||||||
|
// Entities.editEntity(edgeId, {
|
||||||
|
// dimensions: LINE_DIMENSIONS,
|
||||||
|
// });
|
||||||
|
//}
|
||||||
|
// DEBUGGING ONLY - Clear any previous balls
|
||||||
|
// this.magBalls.clear();
|
||||||
|
// DEBUGGING ONLY - Attempt to fix connections between balls
|
||||||
|
// and delete bad connections. Warning... if you haven't looked around
|
||||||
|
// and caused the domain server to send you all the nearby balls as well as the connections,
|
||||||
|
// this can break your structures
|
||||||
|
// this.magBalls.repair();
|
||||||
}
|
}
|
||||||
|
|
||||||
OmniToolModules.MagBallsController.prototype.onRelease = function() {
|
OmniToolModules.MagBallsController.prototype.onUnload = function() {
|
||||||
logDebug("MagBallsController onRelease: " + vec3toStr(this.tipPosition));
|
|
||||||
this.clearGhostEdges();
|
this.clearGhostEdges();
|
||||||
if (this.selected) {
|
this.uiOverlays.deleteAll();
|
||||||
this.magBalls.releaseBall(this.selected);
|
|
||||||
this.selected = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OmniToolModules.MagBallsController.prototype.onUpdate = function(deltaTime) {
|
|
||||||
this.tipPosition = this.omniTool.getPosition();
|
OmniToolModules.MagBallsController.prototype.setMode = function(mode) {
|
||||||
if (!this.selected) {
|
if (mode === this.mode) {
|
||||||
// Find the highlight target and set it.
|
return;
|
||||||
var target = this.magBalls.findNearestNode(this.tipPosition, BALL_SELECTION_RADIUS);
|
}
|
||||||
this.highlighter.highlight(target);
|
|
||||||
if (!target) {
|
logDebug("Changing mode to '" + mode + "'");
|
||||||
this.magBalls.onUpdate(deltaTime);
|
Overlays.editOverlay(this.modeLabel, {
|
||||||
|
url: MODE_INFO[mode].url
|
||||||
|
});
|
||||||
|
|
||||||
|
this.mode = mode;
|
||||||
|
var color1;
|
||||||
|
var color2;
|
||||||
|
switch (this.mode) {
|
||||||
|
case BALL_EDIT_MODE_ADD:
|
||||||
|
color1 = COLORS.BLUE;
|
||||||
|
color2 = COLORS.GREEN;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BALL_EDIT_MODE_MOVE:
|
||||||
|
color1 = COLORS.GREEN;
|
||||||
|
color2 = COLORS.LIGHT_GREEN;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BALL_EDIT_MODE_DELETE:
|
||||||
|
color1 = COLORS.RED;
|
||||||
|
color2 = COLORS.BLUE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BALL_EDIT_MODE_DELETE_SHAPE:
|
||||||
|
color1 = COLORS.RED;
|
||||||
|
color2 = COLORS.YELLOW;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.omniTool.model.setTipColors(color1, color2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
OmniToolModules.MagBallsController.prototype.findUiBallHit = function() {
|
||||||
|
var result = null;
|
||||||
|
for (var mode in MODE_INFO) {
|
||||||
|
var modeInfo = MODE_INFO[mode];
|
||||||
|
var spherePoint = getEyeRelativePosition(modeInfo.uiPosition);
|
||||||
|
if (findSpherePointHit(spherePoint, UI_BALL_RADIUS * 2, this.tipPosition)) {
|
||||||
|
this.highlighter.highlight(spherePoint);
|
||||||
|
this.highlighter.setColor("White");
|
||||||
|
// FIXME why doesn't this work?
|
||||||
|
this.highlighter.setSize(UI_BALL_RADIUS * 4);
|
||||||
|
return mode;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OmniToolModules.MagBallsController.prototype.onUpdateSelected = function(deltaTime) {
|
||||||
|
if (!this.selected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.highlighter.highlight(null);
|
|
||||||
Entities.editEntity(this.selected, { position: this.tipPosition });
|
Entities.editEntity(this.selected, { position: this.tipPosition });
|
||||||
var targetBalls = this.magBalls.findPotentialEdges(this.selected);
|
var targetBalls = this.magBalls.findPotentialEdges(this.selected);
|
||||||
for (var ballId in targetBalls) {
|
for (var ballId in targetBalls) {
|
||||||
|
var targetPosition = this.magBalls.getNodePosition(ballId);
|
||||||
|
var distance = Vec3.distance(targetPosition, this.tipPosition);
|
||||||
|
var variance = this.magBalls.getVariance(distance);
|
||||||
|
var mix = Math.abs(variance) / this.magBalls.MAX_VARIANCE;
|
||||||
|
var color = colorMix(COLORS.YELLOW, COLORS.RED, mix);
|
||||||
if (!this.ghostEdges[ballId]) {
|
if (!this.ghostEdges[ballId]) {
|
||||||
// create the ovleray
|
// create the ovleray
|
||||||
this.ghostEdges[ballId] = Overlays.addOverlay("line3d", {
|
this.ghostEdges[ballId] = Overlays.addOverlay("line3d", {
|
||||||
start: this.magBalls.getNodePosition(ballId),
|
start: this.magBalls.getNodePosition(ballId),
|
||||||
end: this.tipPosition,
|
end: this.tipPosition,
|
||||||
color: COLORS.RED,
|
color: color,
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
lineWidth: 5,
|
lineWidth: 5,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -89,6 +201,7 @@ OmniToolModules.MagBallsController.prototype.onUpdate = function(deltaTime) {
|
||||||
} else {
|
} else {
|
||||||
Overlays.editOverlay(this.ghostEdges[ballId], {
|
Overlays.editOverlay(this.ghostEdges[ballId], {
|
||||||
end: this.tipPosition,
|
end: this.tipPosition,
|
||||||
|
color: color,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,6 +213,95 @@ OmniToolModules.MagBallsController.prototype.onUpdate = function(deltaTime) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OmniToolModules.MagBallsController.prototype.onUpdate = function(deltaTime) {
|
||||||
|
this.tipPosition = this.omniTool.getPosition();
|
||||||
|
this.uiOverlays.onUpdate(deltaTime);
|
||||||
|
|
||||||
|
this.onUpdateSelected();
|
||||||
|
|
||||||
|
if (this.findUiBallHit()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.selected) {
|
||||||
|
// Find the highlight target and set it.
|
||||||
|
var target = this.magBalls.findNearestNode(this.tipPosition, BALL_RADIUS * this.selectionRadiusMultipler);
|
||||||
|
this.highlighter.highlight(target);
|
||||||
|
this.highlighter.setColor(MODE_INFO[this.mode].colors[0]);
|
||||||
|
if (!target) {
|
||||||
|
this.magBalls.onUpdate(deltaTime);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OmniToolModules.MagBallsController.prototype.deselect = function() {
|
||||||
|
if (!this.selected) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
this.clearGhostEdges();
|
||||||
|
this.magBalls.releaseBall(this.selected);
|
||||||
|
this.selected = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
OmniToolModules.MagBallsController.prototype.onClick = function() {
|
||||||
|
var newMode = this.findUiBallHit();
|
||||||
|
if (newMode) {
|
||||||
|
if (this.selected) {
|
||||||
|
this.magBalls.destroyNode(highlighted);
|
||||||
|
this.selected = null;
|
||||||
|
}
|
||||||
|
this.setMode(newMode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.deselect()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logDebug("MagBallsController onClick: " + vec3toStr(this.tipPosition));
|
||||||
|
|
||||||
|
// TODO add checking against UI shapes for adding or deleting balls.
|
||||||
|
var highlighted = this.highlighter.highlighted;
|
||||||
|
if (this.mode == BALL_EDIT_MODE_ADD && !highlighted) {
|
||||||
|
highlighted = this.magBalls.createBall(this.tipPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing to select or create means we're done here.
|
||||||
|
if (!highlighted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this.mode) {
|
||||||
|
case BALL_EDIT_MODE_ADD:
|
||||||
|
case BALL_EDIT_MODE_MOVE:
|
||||||
|
this.magBalls.selectBall(highlighted);
|
||||||
|
this.selected = highlighted;
|
||||||
|
logDebug("Selected " + this.selected);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BALL_EDIT_MODE_DELETE:
|
||||||
|
this.magBalls.destroyNode(highlighted);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BALL_EDIT_MODE_DELETE_SHAPE:
|
||||||
|
logDebug("Not implemented yet");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.selected) {
|
||||||
|
this.highlighter.highlight(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OmniToolModules.MagBallsController.prototype.onRelease = function() {
|
||||||
|
if (this.holdMode) {
|
||||||
|
this.deselect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
OmniToolModules.MagBallsController.prototype.clearGhostEdges = function() {
|
OmniToolModules.MagBallsController.prototype.clearGhostEdges = function() {
|
||||||
for(var ballId in this.ghostEdges) {
|
for(var ballId in this.ghostEdges) {
|
||||||
Overlays.deleteOverlay(this.ghostEdges[ballId]);
|
Overlays.deleteOverlay(this.ghostEdges[ballId]);
|
||||||
|
@ -108,6 +310,3 @@ OmniToolModules.MagBallsController.prototype.clearGhostEdges = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BallController.prototype.onUnload = function() {
|
|
||||||
this.clearGhostEdges();
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
|
||||||
|
MAG_BALLS_DATA_NAME = "magBalls";
|
||||||
|
|
||||||
// FIXME make this editable through some script UI, so the user can customize the size of the structure built
|
// FIXME make this editable through some script UI, so the user can customize the size of the structure built
|
||||||
SCALE = 0.5;
|
MAG_BALLS_SCALE = 0.5;
|
||||||
BALL_SIZE = 0.08 * SCALE;
|
BALL_SIZE = 0.08 * MAG_BALLS_SCALE;
|
||||||
STICK_LENGTH = 0.24 * SCALE;
|
STICK_LENGTH = 0.24 * MAG_BALLS_SCALE;
|
||||||
|
|
||||||
DEBUG_MAGSTICKS = true;
|
DEBUG_MAGSTICKS = true;
|
||||||
|
|
||||||
|
@ -11,8 +13,6 @@ EDGE_NAME = "MagStick";
|
||||||
|
|
||||||
BALL_RADIUS = BALL_SIZE / 2.0;
|
BALL_RADIUS = BALL_SIZE / 2.0;
|
||||||
|
|
||||||
BALL_SELECTION_RADIUS = BALL_RADIUS * 1.5;
|
|
||||||
|
|
||||||
BALL_DIMENSIONS = {
|
BALL_DIMENSIONS = {
|
||||||
x: BALL_SIZE,
|
x: BALL_SIZE,
|
||||||
y: BALL_SIZE,
|
y: BALL_SIZE,
|
||||||
|
@ -45,10 +45,13 @@ BALL_PROTOTYPE = {
|
||||||
// 2 millimeters
|
// 2 millimeters
|
||||||
BALL_EPSILON = (.002) / BALL_DISTANCE;
|
BALL_EPSILON = (.002) / BALL_DISTANCE;
|
||||||
|
|
||||||
|
// FIXME better handling of the line bounding box would require putting the
|
||||||
|
// origin in the middle of the line, not at one end
|
||||||
|
LINE_DIAGONAL = Math.sqrt((STICK_LENGTH * STICK_LENGTH * 2) * 3) * 1.1;
|
||||||
LINE_DIMENSIONS = {
|
LINE_DIMENSIONS = {
|
||||||
x: 5,
|
x: LINE_DIAGONAL,
|
||||||
y: 5,
|
y: LINE_DIAGONAL,
|
||||||
z: 5
|
z: LINE_DIAGONAL
|
||||||
}
|
}
|
||||||
|
|
||||||
LINE_PROTOTYPE = {
|
LINE_PROTOTYPE = {
|
||||||
|
@ -75,3 +78,9 @@ EDGE_PROTOTYPE = LINE_PROTOTYPE;
|
||||||
// ignoreCollisions: true,
|
// ignoreCollisions: true,
|
||||||
// collisionsWillMove: false
|
// collisionsWillMove: false
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
BALL_EDIT_MODE_ADD = "add";
|
||||||
|
BALL_EDIT_MODE_MOVE = "move";
|
||||||
|
BALL_EDIT_MODE_DELETE = "delete";
|
||||||
|
BALL_EDIT_MODE_DELETE_SHAPE = "deleteShape";
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
findMatchingNode = function(position, nodePositions) {
|
|
||||||
for (var nodeId in nodePositions) {
|
|
||||||
var nodePos = nodePositions[nodeId];
|
|
||||||
var distance = Vec3.distance(position, nodePos);
|
|
||||||
if (distance < 0.03) {
|
|
||||||
return nodeId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repairConnections = function() {
|
|
||||||
var ids = Entities.findEntities(MyAvatar.position, 50);
|
|
||||||
|
|
||||||
// Find all the balls and record their positions
|
|
||||||
var nodePositions = {};
|
|
||||||
for (var i in ids) {
|
|
||||||
var id = ids[i];
|
|
||||||
var properties = Entities.getEntityProperties(id);
|
|
||||||
if (properties.name == BALL_NAME) {
|
|
||||||
nodePositions[id] = properties.position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now check all the edges to see if they're valid (point to balls)
|
|
||||||
// and ensure that the balls point back to them
|
|
||||||
var ballsToEdges = {};
|
|
||||||
for (var i in ids) {
|
|
||||||
var id = ids[i];
|
|
||||||
var properties = Entities.getEntityProperties(id);
|
|
||||||
if (properties.name == EDGE_NAME) {
|
|
||||||
var startPos = properties.position;
|
|
||||||
var endPos = Vec3.sum(startPos, properties.linePoints[1]);
|
|
||||||
var magBallData = getMagBallsData(id);
|
|
||||||
var update = false;
|
|
||||||
if (!magBallData.start) {
|
|
||||||
var startNode = findMatchingNode(startPos, nodePositions);
|
|
||||||
if (startNode) {
|
|
||||||
logDebug("Found start node " + startNode)
|
|
||||||
magBallData.start = startNode;
|
|
||||||
update = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!magBallData.end) {
|
|
||||||
var endNode = findMatchingNode(endPos, nodePositions);
|
|
||||||
if (endNode) {
|
|
||||||
logDebug("Found end node " + endNode)
|
|
||||||
magBallData.end = endNode;
|
|
||||||
update = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!magBallData.start || !magBallData.end) {
|
|
||||||
logDebug("Didn't find both ends");
|
|
||||||
Entities.deleteEntity(id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!ballsToEdges[magBallData.start]) {
|
|
||||||
ballsToEdges[magBallData.start] = [ id ];
|
|
||||||
} else {
|
|
||||||
ballsToEdges[magBallData.start].push(id);
|
|
||||||
}
|
|
||||||
if (!ballsToEdges[magBallData.end]) {
|
|
||||||
ballsToEdges[magBallData.end] = [ id ];
|
|
||||||
} else {
|
|
||||||
ballsToEdges[magBallData.end].push(id);
|
|
||||||
}
|
|
||||||
if (update) {
|
|
||||||
logDebug("Updating incomplete edge " + id);
|
|
||||||
magBallData.length = BALL_DISTANCE;
|
|
||||||
setMagBallsData(id, magBallData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var nodeId in ballsToEdges) {
|
|
||||||
var magBallData = getMagBallsData(nodeId);
|
|
||||||
var edges = magBallData.edges || [];
|
|
||||||
var edgeHash = {};
|
|
||||||
for (var i in edges) {
|
|
||||||
edgeHash[edges[i]] = true;
|
|
||||||
}
|
|
||||||
var update = false;
|
|
||||||
for (var i in ballsToEdges[nodeId]) {
|
|
||||||
var edgeId = ballsToEdges[nodeId][i];
|
|
||||||
if (!edgeHash[edgeId]) {
|
|
||||||
update = true;
|
|
||||||
edgeHash[edgeId] = true;
|
|
||||||
edges.push(edgeId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (update) {
|
|
||||||
logDebug("Fixing node with missing edge data");
|
|
||||||
magBallData.edges = edges;
|
|
||||||
setMagBallsData(nodeId, magBallData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,7 +9,10 @@ EdgeSpring = function(edgeId, graph) {
|
||||||
this.desiredLength = magBallsData.length || BALL_DISTANCE;
|
this.desiredLength = magBallsData.length || BALL_DISTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
EdgeSpring.prototype.adjust = function(results) {
|
// FIXME as iterations increase, start introducing some randomness
|
||||||
|
// to the adjustment so that we avoid false equilibriums
|
||||||
|
// Alternatively, larger iterations could increase the acceptable variance
|
||||||
|
EdgeSpring.prototype.adjust = function(results, iterations) {
|
||||||
var startPos = this.getAdjustedPosition(this.start, results);
|
var startPos = this.getAdjustedPosition(this.start, results);
|
||||||
var endPos = this.getAdjustedPosition(this.end, results);
|
var endPos = this.getAdjustedPosition(this.end, results);
|
||||||
var vector = Vec3.subtract(endPos, startPos);
|
var vector = Vec3.subtract(endPos, startPos);
|
||||||
|
|
|
@ -25,10 +25,6 @@ MagBalls = function() {
|
||||||
this.refresh();
|
this.refresh();
|
||||||
|
|
||||||
var _this = this;
|
var _this = this;
|
||||||
//Script.update.connect(function(deltaTime) {
|
|
||||||
// _this.onUpdate(deltaTime);
|
|
||||||
//});
|
|
||||||
|
|
||||||
Script.scriptEnding.connect(function() {
|
Script.scriptEnding.connect(function() {
|
||||||
_this.onCleanup();
|
_this.onCleanup();
|
||||||
});
|
});
|
||||||
|
@ -61,8 +57,13 @@ MagBalls.prototype.onUpdate = function(deltaTime) {
|
||||||
if (!this.unstableEdges[edgeId]) {
|
if (!this.unstableEdges[edgeId]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
adjusted |= this.edgeObjects[edgeId].adjust(nodeAdjustResults);
|
// FIXME need to add some randomness to this so that objects don't hit a
|
||||||
|
// false equilibrium
|
||||||
|
// FIXME should this be done node-wise, to more easily account for the number of edge
|
||||||
|
// connections for a node?
|
||||||
|
adjusted |= this.edgeObjects[edgeId].adjust(nodeAdjustResults, this.adjustIterations);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var nodeId in nodeAdjustResults) {
|
for (var nodeId in nodeAdjustResults) {
|
||||||
var curPos = this.getNodePosition(nodeId);
|
var curPos = this.getNodePosition(nodeId);
|
||||||
var newPos = nodeAdjustResults[nodeId];
|
var newPos = nodeAdjustResults[nodeId];
|
||||||
|
@ -71,18 +72,24 @@ MagBalls.prototype.onUpdate = function(deltaTime) {
|
||||||
fixupEdges[edgeId] = true;
|
fixupEdges[edgeId] = true;
|
||||||
}
|
}
|
||||||
// logDebug("Moving node Id " + nodeId + " " + (distance * 1000).toFixed(3) + " mm");
|
// logDebug("Moving node Id " + nodeId + " " + (distance * 1000).toFixed(3) + " mm");
|
||||||
Entities.editEntity(nodeId, { position: newPos, color: COLORS.RED });
|
Entities.editEntity(nodeId, {
|
||||||
|
position: newPos,
|
||||||
|
// DEBUGGING, flashes moved balls
|
||||||
|
// color: COLORS.RED
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEBUGGING, flashes moved balls
|
||||||
|
//Script.setTimeout(function(){
|
||||||
|
// for (var nodeId in nodeAdjustResults) {
|
||||||
|
// Entities.editEntity(nodeId, { color: BALL_COLOR });
|
||||||
|
// }
|
||||||
|
//}, ((UPDATE_INTERVAL * 1000) / 2));
|
||||||
|
|
||||||
for (var edgeId in fixupEdges) {
|
for (var edgeId in fixupEdges) {
|
||||||
this.fixupEdge(edgeId);
|
this.fixupEdge(edgeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Script.setTimeout(function(){
|
|
||||||
for (var nodeId in nodeAdjustResults) {
|
|
||||||
Entities.editEntity(nodeId, { color: BALL_COLOR });
|
|
||||||
}
|
|
||||||
}, ((UPDATE_INTERVAL * 1000) / 2));
|
|
||||||
|
|
||||||
if (!adjusted || this.adjustIterations > this.MAX_ADJUST_ITERATIONS) {
|
if (!adjusted || this.adjustIterations > this.MAX_ADJUST_ITERATIONS) {
|
||||||
if (adjusted) {
|
if (adjusted) {
|
||||||
|
@ -357,4 +364,96 @@ MagBalls.prototype.onEntityAdded = function(entityId) {
|
||||||
if (properties.name == BALL_NAME || properties.name == EDGE_NAME) {
|
if (properties.name == BALL_NAME || properties.name == EDGE_NAME) {
|
||||||
this.refreshNeeded = 1;
|
this.refreshNeeded = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findMatchingNode = function(position, nodePositions) {
|
||||||
|
for (var nodeId in nodePositions) {
|
||||||
|
var nodePos = nodePositions[nodeId];
|
||||||
|
var distance = Vec3.distance(position, nodePos);
|
||||||
|
if (distance < 0.03) {
|
||||||
|
return nodeId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MagBalls.prototype.repair = function() {
|
||||||
|
// Find all the balls and record their positions
|
||||||
|
var nodePositions = {};
|
||||||
|
for (var nodeId in this.nodes) {
|
||||||
|
nodePositions[nodeId] = this.getNodePosition(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now check all the edges to see if they're valid (point to balls)
|
||||||
|
// and ensure that the balls point back to them
|
||||||
|
var ballsToEdges = {};
|
||||||
|
|
||||||
|
// WARNING O(n^2) algorithm, every edge that is broken does
|
||||||
|
// an O(N) search against the nodes
|
||||||
|
for (var edgeId in this.edges) {
|
||||||
|
var properties = Entities.getEntityProperties(edgeId);
|
||||||
|
var startPos = properties.position;
|
||||||
|
var endPos = Vec3.sum(startPos, properties.linePoints[1]);
|
||||||
|
var magBallData = getMagBallsData(edgeId);
|
||||||
|
var update = false;
|
||||||
|
if (!magBallData.start) {
|
||||||
|
var startNode = findMatchingNode(startPos, nodePositions);
|
||||||
|
if (startNode) {
|
||||||
|
logDebug("Found start node " + startNode)
|
||||||
|
magBallData.start = startNode;
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!magBallData.end) {
|
||||||
|
var endNode = findMatchingNode(endPos, nodePositions);
|
||||||
|
if (endNode) {
|
||||||
|
logDebug("Found end node " + endNode)
|
||||||
|
magBallData.end = endNode;
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!magBallData.start || !magBallData.end) {
|
||||||
|
logDebug("Didn't find both ends");
|
||||||
|
this.destroyEdge(edgeId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!ballsToEdges[magBallData.start]) {
|
||||||
|
ballsToEdges[magBallData.start] = [ edgeId ];
|
||||||
|
} else {
|
||||||
|
ballsToEdges[magBallData.start].push(edgeId);
|
||||||
|
}
|
||||||
|
if (!ballsToEdges[magBallData.end]) {
|
||||||
|
ballsToEdges[magBallData.end] = [ edgeId ];
|
||||||
|
} else {
|
||||||
|
ballsToEdges[magBallData.end].push(edgeId);
|
||||||
|
}
|
||||||
|
if (update) {
|
||||||
|
logDebug("Updating incomplete edge " + edgeId);
|
||||||
|
magBallData.length = BALL_DISTANCE;
|
||||||
|
setMagBallsData(edgeId, magBallData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var nodeId in ballsToEdges) {
|
||||||
|
var magBallData = getMagBallsData(nodeId);
|
||||||
|
var edges = magBallData.edges || [];
|
||||||
|
var edgeHash = {};
|
||||||
|
for (var i in edges) {
|
||||||
|
edgeHash[edges[i]] = true;
|
||||||
|
}
|
||||||
|
var update = false;
|
||||||
|
for (var i in ballsToEdges[nodeId]) {
|
||||||
|
var edgeId = ballsToEdges[nodeId][i];
|
||||||
|
if (!edgeHash[edgeId]) {
|
||||||
|
update = true;
|
||||||
|
edgeHash[edgeId] = true;
|
||||||
|
edges.push(edgeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (update) {
|
||||||
|
logDebug("Fixing node with missing edge data");
|
||||||
|
magBallData.edges = edges;
|
||||||
|
setMagBallsData(nodeId, magBallData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -3558,6 +3558,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
||||||
renderContext._drawHitEffect = sceneInterface->doEngineDisplayHitEffect();
|
renderContext._drawHitEffect = sceneInterface->doEngineDisplayHitEffect();
|
||||||
|
|
||||||
renderContext._occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion);
|
renderContext._occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion);
|
||||||
|
renderContext._fxaaStatus = Menu::getInstance()->isOptionChecked(MenuOption::Antialiasing);
|
||||||
|
|
||||||
renderArgs->_shouldRender = LODManager::shouldRender;
|
renderArgs->_shouldRender = LODManager::shouldRender;
|
||||||
|
|
||||||
|
|
|
@ -317,6 +317,7 @@ Menu::Menu() {
|
||||||
0, // QML Qt::SHIFT | Qt::Key_A,
|
0, // QML Qt::SHIFT | Qt::Key_A,
|
||||||
true);
|
true);
|
||||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DebugAmbientOcclusion);
|
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DebugAmbientOcclusion);
|
||||||
|
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Antialiasing);
|
||||||
|
|
||||||
MenuWrapper* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight);
|
MenuWrapper* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight);
|
||||||
QActionGroup* ambientLightGroup = new QActionGroup(ambientLightMenu);
|
QActionGroup* ambientLightGroup = new QActionGroup(ambientLightMenu);
|
||||||
|
|
|
@ -134,6 +134,7 @@ namespace MenuOption {
|
||||||
const QString Animations = "Animations...";
|
const QString Animations = "Animations...";
|
||||||
const QString AnimDebugDrawAnimPose = "Debug Draw Animation";
|
const QString AnimDebugDrawAnimPose = "Debug Draw Animation";
|
||||||
const QString AnimDebugDrawBindPose = "Debug Draw Bind Pose";
|
const QString AnimDebugDrawBindPose = "Debug Draw Bind Pose";
|
||||||
|
const QString Antialiasing = "Antialiasing";
|
||||||
const QString Atmosphere = "Atmosphere";
|
const QString Atmosphere = "Atmosphere";
|
||||||
const QString Attachments = "Attachments...";
|
const QString Attachments = "Attachments...";
|
||||||
const QString AudioNoiseReduction = "Audio Noise Reduction";
|
const QString AudioNoiseReduction = "Audio Noise Reduction";
|
||||||
|
|
|
@ -69,7 +69,7 @@ float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f;
|
||||||
const int SCRIPTED_MOTOR_CAMERA_FRAME = 0;
|
const int SCRIPTED_MOTOR_CAMERA_FRAME = 0;
|
||||||
const int SCRIPTED_MOTOR_AVATAR_FRAME = 1;
|
const int SCRIPTED_MOTOR_AVATAR_FRAME = 1;
|
||||||
const int SCRIPTED_MOTOR_WORLD_FRAME = 2;
|
const int SCRIPTED_MOTOR_WORLD_FRAME = 2;
|
||||||
const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit1.wav";
|
const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/Body_Hits_Impact.wav";
|
||||||
|
|
||||||
const float MyAvatar::ZOOM_MIN = 0.5f;
|
const float MyAvatar::ZOOM_MIN = 0.5f;
|
||||||
const float MyAvatar::ZOOM_MAX = 25.0f;
|
const float MyAvatar::ZOOM_MAX = 25.0f;
|
||||||
|
|
|
@ -147,7 +147,12 @@ QScriptValue Web3DOverlay::getProperty(const QString& property) {
|
||||||
|
|
||||||
void Web3DOverlay::setURL(const QString& url) {
|
void Web3DOverlay::setURL(const QString& url) {
|
||||||
_url = url;
|
_url = url;
|
||||||
_isLoaded = false;
|
if (_webSurface) {
|
||||||
|
AbstractViewStateInterface::instance()->postLambdaEvent([this, url] {
|
||||||
|
_webSurface->getRootItem()->setProperty("url", url);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) {
|
bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) {
|
||||||
|
|
|
@ -310,8 +310,6 @@ void OculusDisplayPlugin::activate() {
|
||||||
// not needed since the structure was zeroed on init, but explicit
|
// not needed since the structure was zeroed on init, but explicit
|
||||||
sceneLayer.ColorTexture[1] = nullptr;
|
sceneLayer.ColorTexture[1] = nullptr;
|
||||||
|
|
||||||
PerformanceTimer::setActive(true);
|
|
||||||
|
|
||||||
if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd,
|
if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd,
|
||||||
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
|
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
|
||||||
qFatal("Could not attach to sensor device");
|
qFatal("Could not attach to sensor device");
|
||||||
|
@ -322,15 +320,12 @@ void OculusDisplayPlugin::activate() {
|
||||||
void OculusDisplayPlugin::customizeContext() {
|
void OculusDisplayPlugin::customizeContext() {
|
||||||
WindowOpenGLDisplayPlugin::customizeContext();
|
WindowOpenGLDisplayPlugin::customizeContext();
|
||||||
#if (OVR_MAJOR_VERSION >= 6)
|
#if (OVR_MAJOR_VERSION >= 6)
|
||||||
//_texture = DependencyManager::get<TextureCache>()->
|
|
||||||
// getImageTexture(PathUtils::resourcesPath() + "/images/cube_texture.png");
|
|
||||||
uvec2 mirrorSize = toGlm(_window->geometry().size());
|
|
||||||
|
|
||||||
_sceneFbo = SwapFboPtr(new SwapFramebufferWrapper(_hmd));
|
_sceneFbo = SwapFboPtr(new SwapFramebufferWrapper(_hmd));
|
||||||
_sceneFbo->Init(getRecommendedRenderSize());
|
_sceneFbo->Init(getRecommendedRenderSize());
|
||||||
#endif
|
#endif
|
||||||
enableVsync(false);
|
enableVsync(false);
|
||||||
isVsyncEnabled();
|
// Only enable mirroring if we know vsync is disabled
|
||||||
|
_enableMirror = !isVsyncEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OculusDisplayPlugin::deactivate() {
|
void OculusDisplayPlugin::deactivate() {
|
||||||
|
@ -338,7 +333,6 @@ void OculusDisplayPlugin::deactivate() {
|
||||||
makeCurrent();
|
makeCurrent();
|
||||||
_sceneFbo.reset();
|
_sceneFbo.reset();
|
||||||
doneCurrent();
|
doneCurrent();
|
||||||
PerformanceTimer::setActive(false);
|
|
||||||
|
|
||||||
WindowOpenGLDisplayPlugin::deactivate();
|
WindowOpenGLDisplayPlugin::deactivate();
|
||||||
|
|
||||||
|
@ -355,6 +349,16 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
|
||||||
// controlling vsync
|
// controlling vsync
|
||||||
wglSwapIntervalEXT(0);
|
wglSwapIntervalEXT(0);
|
||||||
|
|
||||||
|
// screen mirroring
|
||||||
|
if (_enableMirror) {
|
||||||
|
auto windowSize = toGlm(_window->size());
|
||||||
|
Context::Viewport(windowSize.x, windowSize.y);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, finalTexture);
|
||||||
|
GLenum err = glGetError();
|
||||||
|
Q_ASSERT(0 == err);
|
||||||
|
drawUnitQuad();
|
||||||
|
}
|
||||||
|
|
||||||
_sceneFbo->Bound([&] {
|
_sceneFbo->Bound([&] {
|
||||||
auto size = _sceneFbo->size;
|
auto size = _sceneFbo->size;
|
||||||
Context::Viewport(size.x, size.y);
|
Context::Viewport(size.x, size.y);
|
||||||
|
@ -369,25 +373,7 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
|
||||||
});
|
});
|
||||||
|
|
||||||
auto windowSize = toGlm(_window->size());
|
auto windowSize = toGlm(_window->size());
|
||||||
|
|
||||||
/*
|
|
||||||
Two alternatives for mirroring to the screen, the first is to copy our own composited
|
|
||||||
scene to the window framebuffer, before distortion. Note this only works if we're doing
|
|
||||||
ui compositing ourselves, and not relying on the Oculus SDK compositor (or we don't want
|
|
||||||
the UI visible in the output window (unlikely). This should be done before
|
|
||||||
_sceneFbo->Increment or we're be using the wrong texture
|
|
||||||
*/
|
|
||||||
if (_enableMirror) {
|
|
||||||
_sceneFbo->Bound(Framebuffer::Target::Read, [&] {
|
|
||||||
glBlitFramebuffer(
|
|
||||||
0, 0, _sceneFbo->size.x, _sceneFbo->size.y,
|
|
||||||
0, 0, windowSize.x, windowSize.y,
|
|
||||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
PerformanceTimer("OculusSubmit");
|
|
||||||
ovrViewScaleDesc viewScaleDesc;
|
ovrViewScaleDesc viewScaleDesc;
|
||||||
viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f;
|
viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f;
|
||||||
viewScaleDesc.HmdToEyeViewOffset[0] = _eyeOffsets[0];
|
viewScaleDesc.HmdToEyeViewOffset[0] = _eyeOffsets[0];
|
||||||
|
@ -401,20 +387,6 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
|
||||||
}
|
}
|
||||||
_sceneFbo->Increment();
|
_sceneFbo->Increment();
|
||||||
|
|
||||||
/*
|
|
||||||
The other alternative for mirroring is to use the Oculus mirror texture support, which
|
|
||||||
will contain the post-distorted and fully composited scene regardless of how many layers
|
|
||||||
we send.
|
|
||||||
Currently generates an error.
|
|
||||||
*/
|
|
||||||
//auto mirrorSize = _mirrorFbo->size;
|
|
||||||
//_mirrorFbo->Bound(Framebuffer::Target::Read, [&] {
|
|
||||||
// Context::BlitFramebuffer(
|
|
||||||
// 0, mirrorSize.y, mirrorSize.x, 0,
|
|
||||||
// 0, 0, windowSize.x, windowSize.y,
|
|
||||||
// BufferSelectBit::ColorBuffer, BlitFilter::Nearest);
|
|
||||||
//});
|
|
||||||
|
|
||||||
++_frameIndex;
|
++_frameIndex;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
165
libraries/render-utils/src/AntialiasingEffect.cpp
Normal file
165
libraries/render-utils/src/AntialiasingEffect.cpp
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
//
|
||||||
|
// AntialiasingEffect.cpp
|
||||||
|
// libraries/render-utils/src/
|
||||||
|
//
|
||||||
|
// Created by Raffi Bedikian on 8/30/15
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include <glm/gtc/random.hpp>
|
||||||
|
|
||||||
|
#include <PathUtils.h>
|
||||||
|
#include <SharedUtil.h>
|
||||||
|
#include <gpu/Context.h>
|
||||||
|
|
||||||
|
#include "gpu/StandardShaderLib.h"
|
||||||
|
#include "AntialiasingEffect.h"
|
||||||
|
#include "TextureCache.h"
|
||||||
|
#include "FramebufferCache.h"
|
||||||
|
#include "DependencyManager.h"
|
||||||
|
#include "ViewFrustum.h"
|
||||||
|
#include "GeometryCache.h"
|
||||||
|
|
||||||
|
#include "fxaa_vert.h"
|
||||||
|
#include "fxaa_frag.h"
|
||||||
|
#include "fxaa_blend_frag.h"
|
||||||
|
|
||||||
|
|
||||||
|
Antialiasing::Antialiasing() {
|
||||||
|
}
|
||||||
|
|
||||||
|
const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() {
|
||||||
|
if (!_antialiasingPipeline) {
|
||||||
|
auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(fxaa_vert)));
|
||||||
|
auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(fxaa_frag)));
|
||||||
|
gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
|
||||||
|
|
||||||
|
gpu::Shader::BindingSet slotBindings;
|
||||||
|
slotBindings.insert(gpu::Shader::Binding(std::string("colorTexture"), 0));
|
||||||
|
|
||||||
|
gpu::Shader::makeProgram(*program, slotBindings);
|
||||||
|
|
||||||
|
_texcoordOffsetLoc = program->getUniforms().findLocation("texcoordOffset");
|
||||||
|
|
||||||
|
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||||
|
|
||||||
|
state->setDepthTest(false, false, gpu::LESS_EQUAL);
|
||||||
|
|
||||||
|
// Link the antialiasing FBO to texture
|
||||||
|
_antialiasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32,
|
||||||
|
DependencyManager::get<FramebufferCache>()->getFrameBufferSize().width(), DependencyManager::get<FramebufferCache>()->getFrameBufferSize().height()));
|
||||||
|
auto format = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
|
||||||
|
auto width = _antialiasingBuffer->getWidth();
|
||||||
|
auto height = _antialiasingBuffer->getHeight();
|
||||||
|
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
|
||||||
|
_antialiasingTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler));
|
||||||
|
|
||||||
|
// Good to go add the brand new pipeline
|
||||||
|
_antialiasingPipeline.reset(gpu::Pipeline::create(program, state));
|
||||||
|
}
|
||||||
|
|
||||||
|
int w = DependencyManager::get<FramebufferCache>()->getFrameBufferSize().width();
|
||||||
|
int h = DependencyManager::get<FramebufferCache>()->getFrameBufferSize().height();
|
||||||
|
if (w != _antialiasingBuffer->getWidth() || h != _antialiasingBuffer->getHeight()) {
|
||||||
|
_antialiasingBuffer->resize(w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _antialiasingPipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gpu::PipelinePointer& Antialiasing::getBlendPipeline() {
|
||||||
|
if (!_blendPipeline) {
|
||||||
|
auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(fxaa_vert)));
|
||||||
|
auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(fxaa_blend_frag)));
|
||||||
|
gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
|
||||||
|
|
||||||
|
gpu::Shader::BindingSet slotBindings;
|
||||||
|
slotBindings.insert(gpu::Shader::Binding(std::string("colorTexture"), 0));
|
||||||
|
|
||||||
|
gpu::Shader::makeProgram(*program, slotBindings);
|
||||||
|
|
||||||
|
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||||
|
|
||||||
|
state->setDepthTest(false, false, gpu::LESS_EQUAL);
|
||||||
|
|
||||||
|
// Good to go add the brand new pipeline
|
||||||
|
_blendPipeline.reset(gpu::Pipeline::create(program, state));
|
||||||
|
}
|
||||||
|
return _blendPipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Antialiasing::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) {
|
||||||
|
assert(renderContext->args);
|
||||||
|
assert(renderContext->args->_viewFrustum);
|
||||||
|
|
||||||
|
if (renderContext->args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpu::Batch batch;
|
||||||
|
|
||||||
|
batch.enableStereo(false);
|
||||||
|
|
||||||
|
RenderArgs* args = renderContext->args;
|
||||||
|
|
||||||
|
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||||
|
QSize framebufferSize = framebufferCache->getFrameBufferSize();
|
||||||
|
float fbWidth = framebufferSize.width();
|
||||||
|
float fbHeight = framebufferSize.height();
|
||||||
|
float sMin = args->_viewport.x / fbWidth;
|
||||||
|
float sWidth = args->_viewport.z / fbWidth;
|
||||||
|
float tMin = args->_viewport.y / fbHeight;
|
||||||
|
float tHeight = args->_viewport.w / fbHeight;
|
||||||
|
|
||||||
|
glm::mat4 projMat;
|
||||||
|
Transform viewMat;
|
||||||
|
args->_viewFrustum->evalProjectionMatrix(projMat);
|
||||||
|
args->_viewFrustum->evalViewTransform(viewMat);
|
||||||
|
batch.setProjectionTransform(projMat);
|
||||||
|
batch.setViewTransform(viewMat);
|
||||||
|
batch.setModelTransform(Transform());
|
||||||
|
|
||||||
|
// FXAA step
|
||||||
|
getAntialiasingPipeline();
|
||||||
|
batch.setResourceTexture(0, framebufferCache->getPrimaryColorTexture());
|
||||||
|
_antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture);
|
||||||
|
batch.setFramebuffer(_antialiasingBuffer);
|
||||||
|
batch.setPipeline(getAntialiasingPipeline());
|
||||||
|
|
||||||
|
// initialize the view-space unpacking uniforms using frustum data
|
||||||
|
float left, right, bottom, top, nearVal, farVal;
|
||||||
|
glm::vec4 nearClipPlane, farClipPlane;
|
||||||
|
|
||||||
|
args->_viewFrustum->computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
|
||||||
|
|
||||||
|
float depthScale = (farVal - nearVal) / farVal;
|
||||||
|
float nearScale = -1.0f / nearVal;
|
||||||
|
float depthTexCoordScaleS = (right - left) * nearScale / sWidth;
|
||||||
|
float depthTexCoordScaleT = (top - bottom) * nearScale / tHeight;
|
||||||
|
float depthTexCoordOffsetS = left * nearScale - sMin * depthTexCoordScaleS;
|
||||||
|
float depthTexCoordOffsetT = bottom * nearScale - tMin * depthTexCoordScaleT;
|
||||||
|
|
||||||
|
batch._glUniform2f(_texcoordOffsetLoc, 1.0 / fbWidth, 1.0 / fbHeight);
|
||||||
|
|
||||||
|
glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
glm::vec2 bottomLeft(-1.0f, -1.0f);
|
||||||
|
glm::vec2 topRight(1.0f, 1.0f);
|
||||||
|
glm::vec2 texCoordTopLeft(0.0f, 0.0f);
|
||||||
|
glm::vec2 texCoordBottomRight(1.0f, 1.0f);
|
||||||
|
DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color);
|
||||||
|
|
||||||
|
// Blend step
|
||||||
|
getBlendPipeline();
|
||||||
|
batch.setResourceTexture(0, _antialiasingTexture);
|
||||||
|
batch.setFramebuffer(framebufferCache->getPrimaryFramebuffer());
|
||||||
|
batch.setPipeline(getBlendPipeline());
|
||||||
|
|
||||||
|
DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color);
|
||||||
|
|
||||||
|
// Ready to render
|
||||||
|
args->_context->render((batch));
|
||||||
|
}
|
44
libraries/render-utils/src/AntialiasingEffect.h
Normal file
44
libraries/render-utils/src/AntialiasingEffect.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
//
|
||||||
|
// AntialiasingEffect.h
|
||||||
|
// libraries/render-utils/src/
|
||||||
|
//
|
||||||
|
// Created by Raffi Bedikian on 8/30/15
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_AntialiasingEffect_h
|
||||||
|
#define hifi_AntialiasingEffect_h
|
||||||
|
|
||||||
|
#include <DependencyManager.h>
|
||||||
|
|
||||||
|
#include "render/DrawTask.h"
|
||||||
|
|
||||||
|
class Antialiasing {
|
||||||
|
public:
|
||||||
|
|
||||||
|
Antialiasing();
|
||||||
|
|
||||||
|
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
|
||||||
|
typedef render::Job::Model<Antialiasing> JobModel;
|
||||||
|
|
||||||
|
const gpu::PipelinePointer& getAntialiasingPipeline();
|
||||||
|
const gpu::PipelinePointer& getBlendPipeline();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Uniforms for AA
|
||||||
|
gpu::int32 _texcoordOffsetLoc;
|
||||||
|
|
||||||
|
gpu::FramebufferPointer _antialiasingBuffer;
|
||||||
|
|
||||||
|
gpu::TexturePointer _antialiasingTexture;
|
||||||
|
|
||||||
|
gpu::PipelinePointer _antialiasingPipeline;
|
||||||
|
gpu::PipelinePointer _blendPipeline;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_AntialiasingEffect_h
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include "render/DrawStatus.h"
|
#include "render/DrawStatus.h"
|
||||||
#include "AmbientOcclusionEffect.h"
|
#include "AmbientOcclusionEffect.h"
|
||||||
|
#include "AntialiasingEffect.h"
|
||||||
|
|
||||||
#include "overlay3D_vert.h"
|
#include "overlay3D_vert.h"
|
||||||
#include "overlay3D_frag.h"
|
#include "overlay3D_frag.h"
|
||||||
|
@ -88,6 +89,11 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
|
||||||
_jobs.back().setEnabled(false);
|
_jobs.back().setEnabled(false);
|
||||||
_occlusionJobIndex = _jobs.size() - 1;
|
_occlusionJobIndex = _jobs.size() - 1;
|
||||||
|
|
||||||
|
_jobs.push_back(Job(new Antialiasing::JobModel("Antialiasing")));
|
||||||
|
|
||||||
|
_jobs.back().setEnabled(false);
|
||||||
|
_antialiasingJobIndex = _jobs.size() - 1;
|
||||||
|
|
||||||
_jobs.push_back(Job(new FetchItems::JobModel("FetchTransparent",
|
_jobs.push_back(Job(new FetchItems::JobModel("FetchTransparent",
|
||||||
FetchItems(
|
FetchItems(
|
||||||
ItemFilter::Builder::transparentShape().withoutLayered(),
|
ItemFilter::Builder::transparentShape().withoutLayered(),
|
||||||
|
@ -146,6 +152,8 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend
|
||||||
// TODO: turn on/off AO through menu item
|
// TODO: turn on/off AO through menu item
|
||||||
setOcclusionStatus(renderContext->_occlusionStatus);
|
setOcclusionStatus(renderContext->_occlusionStatus);
|
||||||
|
|
||||||
|
setAntialiasingStatus(renderContext->_fxaaStatus);
|
||||||
|
|
||||||
renderContext->args->_context->syncCache();
|
renderContext->args->_context->syncCache();
|
||||||
|
|
||||||
for (auto job : _jobs) {
|
for (auto job : _jobs) {
|
||||||
|
|
|
@ -91,6 +91,11 @@ public:
|
||||||
void setOcclusionStatus(bool draw) { if (_occlusionJobIndex >= 0) { _jobs[_occlusionJobIndex].setEnabled(draw); } }
|
void setOcclusionStatus(bool draw) { if (_occlusionJobIndex >= 0) { _jobs[_occlusionJobIndex].setEnabled(draw); } }
|
||||||
bool doOcclusionStatus() const { if (_occlusionJobIndex >= 0) { return _jobs[_occlusionJobIndex].isEnabled(); } else { return false; } }
|
bool doOcclusionStatus() const { if (_occlusionJobIndex >= 0) { return _jobs[_occlusionJobIndex].isEnabled(); } else { return false; } }
|
||||||
|
|
||||||
|
int _antialiasingJobIndex = -1;
|
||||||
|
|
||||||
|
void setAntialiasingStatus(bool draw) { if (_antialiasingJobIndex >= 0) { _jobs[_antialiasingJobIndex].setEnabled(draw); } }
|
||||||
|
bool doAntialiasingStatus() const { if (_antialiasingJobIndex >= 0) { return _jobs[_antialiasingJobIndex].isEnabled(); } else { return false; } }
|
||||||
|
|
||||||
virtual void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
|
virtual void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
|
||||||
|
|
||||||
|
|
||||||
|
|
94
libraries/render-utils/src/fxaa.slf
Normal file
94
libraries/render-utils/src/fxaa.slf
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
<@include gpu/Config.slh@>
|
||||||
|
<$VERSION_HEADER$>
|
||||||
|
// Generated on <$_SCRIBE_DATE$>
|
||||||
|
//
|
||||||
|
// fxaa.frag
|
||||||
|
// fragment shader
|
||||||
|
//
|
||||||
|
// Created by Raffi Bedikian on 8/30/15
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
// FXAA shader, GLSL code adapted from:
|
||||||
|
// http://horde3d.org/wiki/index.php5?title=Shading_Technique_-_FXAA
|
||||||
|
// Whitepaper describing the technique:
|
||||||
|
// http://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf
|
||||||
|
|
||||||
|
#ifdef GL_ES
|
||||||
|
precision mediump float;
|
||||||
|
precision mediump int;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uniform sampler2D colorTexture;
|
||||||
|
uniform vec2 texcoordOffset;
|
||||||
|
|
||||||
|
in vec2 varTexcoord;
|
||||||
|
out vec4 outFragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// filter width limit for dependent "two-tap" texture samples
|
||||||
|
float FXAA_SPAN_MAX = 8.0;
|
||||||
|
|
||||||
|
// local contrast multiplier for performing AA
|
||||||
|
// higher = sharper, but setting this value too high will cause near-vertical and near-horizontal edges to fail
|
||||||
|
// see "fxaaQualityEdgeThreshold"
|
||||||
|
float FXAA_REDUCE_MUL = 1.0 / 8.0;
|
||||||
|
|
||||||
|
// luminance threshold for processing dark colors
|
||||||
|
// see "fxaaQualityEdgeThresholdMin"
|
||||||
|
float FXAA_REDUCE_MIN = 1.0 / 128.0;
|
||||||
|
|
||||||
|
// fetch raw RGB values for nearby locations
|
||||||
|
// sampling pattern is "five on a die" (each diagonal direction and the center)
|
||||||
|
// computing the coordinates for these texture reads could be moved to the vertex shader for speed if needed
|
||||||
|
vec3 rgbNW = texture2D(colorTexture, varTexcoord + (vec2(-1.0, -1.0) * texcoordOffset)).xyz;
|
||||||
|
vec3 rgbNE = texture2D(colorTexture, varTexcoord + (vec2(+1.0, -1.0) * texcoordOffset)).xyz;
|
||||||
|
vec3 rgbSW = texture2D(colorTexture, varTexcoord + (vec2(-1.0, +1.0) * texcoordOffset)).xyz;
|
||||||
|
vec3 rgbSE = texture2D(colorTexture, varTexcoord + (vec2(+1.0, +1.0) * texcoordOffset)).xyz;
|
||||||
|
vec3 rgbM = texture2D(colorTexture, varTexcoord).xyz;
|
||||||
|
|
||||||
|
// convert RGB values to luminance
|
||||||
|
vec3 luma = vec3(0.299, 0.587, 0.114);
|
||||||
|
float lumaNW = dot(rgbNW, luma);
|
||||||
|
float lumaNE = dot(rgbNE, luma);
|
||||||
|
float lumaSW = dot(rgbSW, luma);
|
||||||
|
float lumaSE = dot(rgbSE, luma);
|
||||||
|
float lumaM = dot( rgbM, luma);
|
||||||
|
|
||||||
|
// luma range of local neighborhood
|
||||||
|
float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
|
||||||
|
float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
|
||||||
|
|
||||||
|
// direction perpendicular to local luma gradient
|
||||||
|
vec2 dir;
|
||||||
|
dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
|
||||||
|
dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));
|
||||||
|
|
||||||
|
// compute clamped direction offset for additional "two-tap" samples
|
||||||
|
// longer vector = blurry, shorter vector = sharp
|
||||||
|
float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);
|
||||||
|
float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);
|
||||||
|
dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX),
|
||||||
|
max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * texcoordOffset;
|
||||||
|
|
||||||
|
// perform additional texture sampling perpendicular to gradient
|
||||||
|
vec3 rgbA = (1.0 / 2.0) * (
|
||||||
|
texture2D(colorTexture, varTexcoord + dir * (1.0 / 3.0 - 0.5)).xyz +
|
||||||
|
texture2D(colorTexture, varTexcoord + dir * (2.0 / 3.0 - 0.5)).xyz);
|
||||||
|
vec3 rgbB = rgbA * (1.0 / 2.0) + (1.0 / 4.0) * (
|
||||||
|
texture2D(colorTexture, varTexcoord + dir * (0.0 / 3.0 - 0.5)).xyz +
|
||||||
|
texture2D(colorTexture, varTexcoord + dir * (3.0 / 3.0 - 0.5)).xyz);
|
||||||
|
float lumaB = dot(rgbB, luma);
|
||||||
|
|
||||||
|
// compare luma of new samples to the luma range of the original neighborhood
|
||||||
|
// if the new samples exceed this range, just use the first two samples instead of all four
|
||||||
|
if (lumaB < lumaMin || lumaB > lumaMax) {
|
||||||
|
outFragColor.xyz=rgbA;
|
||||||
|
} else {
|
||||||
|
outFragColor.xyz=rgbB;
|
||||||
|
}
|
||||||
|
outFragColor.a = 1.0;
|
||||||
|
}
|
26
libraries/render-utils/src/fxaa.slv
Normal file
26
libraries/render-utils/src/fxaa.slv
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<@include gpu/Config.slh@>
|
||||||
|
<$VERSION_HEADER$>
|
||||||
|
// Generated on <$_SCRIBE_DATE$>
|
||||||
|
//
|
||||||
|
// fxaa.vert
|
||||||
|
// vertex shader
|
||||||
|
//
|
||||||
|
// Created by Raffi Bedikian on 8/30/15
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
<@include gpu/Inputs.slh@>
|
||||||
|
|
||||||
|
<@include gpu/Transform.slh@>
|
||||||
|
|
||||||
|
<$declareStandardTransform()$>
|
||||||
|
|
||||||
|
out vec2 varTexcoord;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
varTexcoord = inTexCoord0.xy;
|
||||||
|
gl_Position = inPosition;
|
||||||
|
}
|
24
libraries/render-utils/src/fxaa_blend.slf
Normal file
24
libraries/render-utils/src/fxaa_blend.slf
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<@include gpu/Config.slh@>
|
||||||
|
<$VERSION_HEADER$>
|
||||||
|
// Generated on <$_SCRIBE_DATE$>
|
||||||
|
//
|
||||||
|
// fxaa_blend.frag
|
||||||
|
// fragment shader
|
||||||
|
//
|
||||||
|
// Created by Raffi Bedikian on 8/30/15
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
<@include DeferredBufferWrite.slh@>
|
||||||
|
|
||||||
|
in vec2 varTexcoord;
|
||||||
|
out vec4 outFragColor;
|
||||||
|
|
||||||
|
uniform sampler2D colorTexture;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
outFragColor = texture(colorTexture, varTexcoord);
|
||||||
|
}
|
|
@ -23,10 +23,12 @@
|
||||||
out vec3 _normal;
|
out vec3 _normal;
|
||||||
out vec3 _color;
|
out vec3 _color;
|
||||||
out vec2 _texCoord0;
|
out vec2 _texCoord0;
|
||||||
|
out vec4 _position;
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
_color = inColor.rgb;
|
_color = inColor.rgb;
|
||||||
_texCoord0 = inTexCoord0.st;
|
_texCoord0 = inTexCoord0.st;
|
||||||
|
_position = inPosition;
|
||||||
|
|
||||||
// standard transform
|
// standard transform
|
||||||
TransformCamera cam = getTransformCamera();
|
TransformCamera cam = getTransformCamera();
|
||||||
|
|
|
@ -53,6 +53,7 @@ public:
|
||||||
bool _drawHitEffect = false;
|
bool _drawHitEffect = false;
|
||||||
|
|
||||||
bool _occlusionStatus = false;
|
bool _occlusionStatus = false;
|
||||||
|
bool _fxaaStatus = false;
|
||||||
|
|
||||||
RenderContext() {}
|
RenderContext() {}
|
||||||
};
|
};
|
||||||
|
|
|
@ -58,11 +58,13 @@ void BatchLoader::start() {
|
||||||
connect(this, &QObject::destroyed, reply, &QObject::deleteLater);
|
connect(this, &QObject::destroyed, reply, &QObject::deleteLater);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
#ifdef _WIN32
|
|
||||||
QString fileName = url.toString();
|
|
||||||
#else
|
|
||||||
QString fileName = url.toLocalFile();
|
QString fileName = url.toLocalFile();
|
||||||
#endif
|
|
||||||
|
// sometimes on windows, we see the toLocalFile() return null,
|
||||||
|
// in this case we will attempt to simply use the url as a string
|
||||||
|
if (fileName.isEmpty()) {
|
||||||
|
fileName = url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
qCDebug(scriptengine) << "Reading file at " << fileName;
|
qCDebug(scriptengine) << "Reading file at " << fileName;
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,7 @@ static int quatMetaTypeId = qRegisterMetaType<glm::quat>();
|
||||||
static int xColorMetaTypeId = qRegisterMetaType<xColor>();
|
static int xColorMetaTypeId = qRegisterMetaType<xColor>();
|
||||||
static int pickRayMetaTypeId = qRegisterMetaType<PickRay>();
|
static int pickRayMetaTypeId = qRegisterMetaType<PickRay>();
|
||||||
static int collisionMetaTypeId = qRegisterMetaType<Collision>();
|
static int collisionMetaTypeId = qRegisterMetaType<Collision>();
|
||||||
|
static int qMapURLStringMetaTypeId = qRegisterMetaType<QMap<QUrl,QString>>();
|
||||||
|
|
||||||
|
|
||||||
void registerMetaTypes(QScriptEngine* engine) {
|
void registerMetaTypes(QScriptEngine* engine) {
|
||||||
qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue);
|
qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue);
|
||||||
|
|
Loading…
Reference in a new issue