mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 09:33:49 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into polyvox-again
This commit is contained in:
commit
72322c0270
35 changed files with 1065 additions and 249 deletions
|
@ -1,5 +1,4 @@
|
|||
High Fidelity (hifi) is an early-stage technology
|
||||
lab experimenting with Virtual Worlds and VR.
|
||||
High Fidelity (hifi) is an early-stage technology lab experimenting with Virtual Worlds and VR.
|
||||
|
||||
In this repository you'll find the source to many of the components in our
|
||||
alpha-stage virtual world. The project embraces distributed development
|
||||
|
|
|
@ -17,3 +17,4 @@ Script.load("users.js");
|
|||
Script.load("grab.js");
|
||||
Script.load("directory.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 };
|
||||
|
||||
IDENTITY_QUATERNION = { w: 1, x: 0, y: 0, z: 0 };
|
||||
|
||||
COLORS = {
|
||||
WHITE: {
|
||||
red: 255,
|
||||
|
|
|
@ -29,7 +29,7 @@ var SELECTION_OVERLAY = {
|
|||
|
||||
Highlighter = function() {
|
||||
this.highlightCube = Overlays.addOverlay("cube", this.SELECTION_OVERLAY);
|
||||
this.hightlighted = null;
|
||||
this.highlighted = null;
|
||||
var _this = this;
|
||||
Script.scriptEnding.connect(function() {
|
||||
_this.onCleanup();
|
||||
|
@ -40,9 +40,9 @@ Highlighter.prototype.onCleanup = function() {
|
|||
Overlays.deleteOverlay(this.highlightCube);
|
||||
}
|
||||
|
||||
Highlighter.prototype.highlight = function(entityId) {
|
||||
if (entityId != this.hightlighted) {
|
||||
this.hightlighted = entityId;
|
||||
Highlighter.prototype.highlight = function(entityIdOrPosition) {
|
||||
if (entityIdOrPosition != this.highlighted) {
|
||||
this.highlighted = entityIdOrPosition;
|
||||
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) {
|
||||
Overlays.editOverlay(this.highlightCube, {
|
||||
rotation: newRotation
|
||||
|
@ -60,11 +67,15 @@ Highlighter.prototype.setRotation = function(newRotation) {
|
|||
}
|
||||
|
||||
Highlighter.prototype.updateHighlight = function() {
|
||||
if (this.hightlighted) {
|
||||
var properties = Entities.getEntityProperties(this.hightlighted);
|
||||
if (this.highlighted) {
|
||||
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));
|
||||
Overlays.editOverlay(this.highlightCube, {
|
||||
position: properties.position,
|
||||
position: position,
|
||||
visible: true
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -11,13 +11,14 @@ Script.include("utils.js");
|
|||
Script.include("highlighter.js");
|
||||
Script.include("omniTool/models/modelBase.js");
|
||||
Script.include("omniTool/models/wand.js");
|
||||
Script.include("omniTool/models/invisibleWand.js");
|
||||
|
||||
OmniToolModules = {};
|
||||
OmniToolModuleType = null;
|
||||
|
||||
OmniTool = function(side) {
|
||||
this.OMNI_KEY = "OmniTool";
|
||||
this.MAX_FRAMERATE = 30;
|
||||
this.MAX_FRAMERATE = 60;
|
||||
this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE
|
||||
this.SIDE = side;
|
||||
this.PALM = 2 * side;
|
||||
|
@ -28,6 +29,7 @@ OmniTool = function(side) {
|
|||
this.ignoreEntities = {};
|
||||
this.nearestOmniEntity = {
|
||||
id: null,
|
||||
|
||||
inside: false,
|
||||
position: null,
|
||||
distance: Infinity,
|
||||
|
@ -38,13 +40,11 @@ OmniTool = function(side) {
|
|||
|
||||
this.activeOmniEntityId = null;
|
||||
this.lastUpdateInterval = 0;
|
||||
this.tipLength = 0.4;
|
||||
this.active = false;
|
||||
this.module = null;
|
||||
this.moduleEntityId = null;
|
||||
this.lastScanPosition = ZERO_VECTOR;
|
||||
this.model = new Wand();
|
||||
this.model.setLength(this.tipLength);
|
||||
this.showWand(false);
|
||||
|
||||
// Connect to desired events
|
||||
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) {
|
||||
this.unloadModule();
|
||||
}
|
||||
|
@ -110,10 +127,9 @@ OmniTool.prototype.setActive = function(active) {
|
|||
if (active === this.active) {
|
||||
return;
|
||||
}
|
||||
logDebug("omnitool changing active state: " + active);
|
||||
logDebug("OmniTool changing active state: " + active);
|
||||
this.active = active;
|
||||
this.model.setVisible(this.active);
|
||||
|
||||
if (this.module && this.module.onActiveChanged) {
|
||||
this.module.onActiveChanged(this.side);
|
||||
}
|
||||
|
@ -125,14 +141,25 @@ OmniTool.prototype.onUpdate = function(deltaTime) {
|
|||
this.position = Controller.getSpatialControlPosition(this.PALM);
|
||||
// When on the base, hydras report a position of 0
|
||||
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();
|
||||
|
||||
|
@ -144,6 +171,19 @@ OmniTool.prototype.onUpdate = function(deltaTime) {
|
|||
OmniTool.prototype.onClick = function() {
|
||||
// First check to see if the user is switching to a new omni module
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
@ -157,12 +197,10 @@ OmniTool.prototype.onClick = function() {
|
|||
}
|
||||
|
||||
OmniTool.prototype.onRelease = function() {
|
||||
// FIXME how to I switch to a new module?
|
||||
if (this.module && this.module.onRelease) {
|
||||
this.module.onRelease();
|
||||
return;
|
||||
}
|
||||
logDebug("Base omnitool does nothing on release");
|
||||
}
|
||||
|
||||
// FIXME resturn a structure of all nearby entities to distances
|
||||
|
@ -210,6 +248,11 @@ OmniTool.prototype.getPosition = function() {
|
|||
OmniTool.prototype.onEnterNearestOmniEntity = function() {
|
||||
this.nearestOmniEntity.inside = true;
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
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.tipPosition = Vec3.sum(this.position, this.tipVector);
|
||||
}
|
||||
|
||||
ModelBase.prototype.setTipColors = function(color1, color2) {
|
||||
}
|
||||
|
||||
ModelBase.prototype.onCleanup = function() {
|
||||
}
|
||||
|
|
|
@ -1,22 +1,18 @@
|
|||
|
||||
Wand = function() {
|
||||
// Max updates fps
|
||||
this.MAX_FRAMERATE = 30
|
||||
this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE
|
||||
this.DEFAULT_TIP_COLORS = [ {
|
||||
red: 128,
|
||||
green: 128,
|
||||
blue: 128,
|
||||
}, {
|
||||
red: 64,
|
||||
green: 64,
|
||||
blue: 64,
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 0,
|
||||
}];
|
||||
this.POINTER_ROTATION = Quat.fromPitchYawRollDegrees(45, 0, 45);
|
||||
|
||||
// FIXME does this need to be a member of this?
|
||||
this.lastUpdateInterval = 0;
|
||||
|
||||
|
||||
this.pointers = [
|
||||
Overlays.addOverlay("cube", {
|
||||
position: ZERO_VECTOR,
|
||||
|
@ -45,17 +41,7 @@ Wand = function() {
|
|||
|
||||
var _this = this;
|
||||
Script.scriptEnding.connect(function() {
|
||||
Overlays.deleteOverlay(_this.pointers[0]);
|
||||
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;
|
||||
}
|
||||
_this.onCleanup();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -76,7 +62,6 @@ Wand.prototype.setVisible = function(visible) {
|
|||
|
||||
Wand.prototype.setTransform = function(transform) {
|
||||
ModelBase.prototype.setTransform.call(this, transform);
|
||||
|
||||
var wandPosition = Vec3.sum(this.position, Vec3.multiply(0.5, this.tipVector));
|
||||
Overlays.editOverlay(this.pointers[0], {
|
||||
position: this.tipPosition,
|
||||
|
@ -106,7 +91,10 @@ Wand.prototype.setTipColors = function(color1, color2) {
|
|||
}
|
||||
|
||||
Wand.prototype.onUpdate = function(deltaTime) {
|
||||
logDebug("Z4");
|
||||
|
||||
if (this.visible) {
|
||||
logDebug("5");
|
||||
var time = new Date().getTime() / 250;
|
||||
var scale1 = Math.abs(Math.sin(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() {
|
||||
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"
|
|
@ -11,6 +11,15 @@ vec3toStr = function (v, 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) {
|
||||
var v = Vec3.subtract(end, start);
|
||||
var length = Vec3.length(v);
|
||||
|
@ -127,3 +136,13 @@ findSpherePointHit = function(sphereCenter, sphereRadius, point) {
|
|||
findSphereSphereHit = function(firstCenter, firstRadius, secondCenter, secondRadius) {
|
||||
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/edgeSpring.js");
|
||||
Script.include("../toys/magBalls/magBalls.js");
|
||||
Script.include("avatarRelativeOverlays.js");
|
||||
|
||||
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) {
|
||||
return getEntityCustomData(MAG_BALLS_DATA_NAME, id, {});
|
||||
}
|
||||
|
@ -34,54 +23,177 @@ setMagBallsData = function(id, value) {
|
|||
setEntityCustomData(MAG_BALLS_DATA_NAME, id, value);
|
||||
}
|
||||
|
||||
//var magBalls = new MagBalls();
|
||||
// DEBUGGING ONLY - Clear any previous balls
|
||||
// magBalls.clear();
|
||||
var UI_BALL_RADIUS = 0.01;
|
||||
var MODE_INFO = { };
|
||||
|
||||
OmniToolModules.MagBallsController.prototype.onClick = function() {
|
||||
logDebug("MagBallsController onClick: " + vec3toStr(this.tipPosition));
|
||||
MODE_INFO[BALL_EDIT_MODE_ADD] = {
|
||||
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);
|
||||
if (!this.selected) {
|
||||
this.selected = this.magBalls.createBall(this.tipPosition);
|
||||
}
|
||||
this.magBalls.selectBall(this.selected);
|
||||
this.highlighter.highlight(null);
|
||||
logDebug("Selected " + this.selected);
|
||||
|
||||
// create the overlay relative to the avatar
|
||||
this.uiOverlays.addOverlay("sphere", mergeObjects(UI_BALL_PROTOTYPE, {
|
||||
color: MODE_INFO[BALL_EDIT_MODE_ADD].colors[0],
|
||||
position: MODE_INFO[BALL_EDIT_MODE_ADD].uiPosition,
|
||||
}));
|
||||
this.uiOverlays.addOverlay("sphere", mergeObjects(UI_BALL_PROTOTYPE, {
|
||||
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() {
|
||||
logDebug("MagBallsController onRelease: " + vec3toStr(this.tipPosition));
|
||||
OmniToolModules.MagBallsController.prototype.onUnload = function() {
|
||||
this.clearGhostEdges();
|
||||
if (this.selected) {
|
||||
this.magBalls.releaseBall(this.selected);
|
||||
this.selected = null;
|
||||
}
|
||||
this.uiOverlays.deleteAll();
|
||||
}
|
||||
|
||||
OmniToolModules.MagBallsController.prototype.onUpdate = function(deltaTime) {
|
||||
this.tipPosition = this.omniTool.getPosition();
|
||||
if (!this.selected) {
|
||||
// Find the highlight target and set it.
|
||||
var target = this.magBalls.findNearestNode(this.tipPosition, BALL_SELECTION_RADIUS);
|
||||
this.highlighter.highlight(target);
|
||||
if (!target) {
|
||||
this.magBalls.onUpdate(deltaTime);
|
||||
|
||||
OmniToolModules.MagBallsController.prototype.setMode = function(mode) {
|
||||
if (mode === this.mode) {
|
||||
return;
|
||||
}
|
||||
|
||||
logDebug("Changing mode to '" + mode + "'");
|
||||
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;
|
||||
}
|
||||
|
||||
this.highlighter.highlight(null);
|
||||
Entities.editEntity(this.selected, { position: this.tipPosition });
|
||||
var targetBalls = this.magBalls.findPotentialEdges(this.selected);
|
||||
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]) {
|
||||
// create the ovleray
|
||||
this.ghostEdges[ballId] = Overlays.addOverlay("line3d", {
|
||||
start: this.magBalls.getNodePosition(ballId),
|
||||
end: this.tipPosition,
|
||||
color: COLORS.RED,
|
||||
color: color,
|
||||
alpha: 1,
|
||||
lineWidth: 5,
|
||||
visible: true,
|
||||
|
@ -89,6 +201,7 @@ OmniToolModules.MagBallsController.prototype.onUpdate = function(deltaTime) {
|
|||
} else {
|
||||
Overlays.editOverlay(this.ghostEdges[ballId], {
|
||||
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() {
|
||||
for(var ballId in this.ghostEdges) {
|
||||
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
|
||||
SCALE = 0.5;
|
||||
BALL_SIZE = 0.08 * SCALE;
|
||||
STICK_LENGTH = 0.24 * SCALE;
|
||||
MAG_BALLS_SCALE = 0.5;
|
||||
BALL_SIZE = 0.08 * MAG_BALLS_SCALE;
|
||||
STICK_LENGTH = 0.24 * MAG_BALLS_SCALE;
|
||||
|
||||
DEBUG_MAGSTICKS = true;
|
||||
|
||||
|
@ -11,8 +13,6 @@ EDGE_NAME = "MagStick";
|
|||
|
||||
BALL_RADIUS = BALL_SIZE / 2.0;
|
||||
|
||||
BALL_SELECTION_RADIUS = BALL_RADIUS * 1.5;
|
||||
|
||||
BALL_DIMENSIONS = {
|
||||
x: BALL_SIZE,
|
||||
y: BALL_SIZE,
|
||||
|
@ -45,10 +45,13 @@ BALL_PROTOTYPE = {
|
|||
// 2 millimeters
|
||||
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 = {
|
||||
x: 5,
|
||||
y: 5,
|
||||
z: 5
|
||||
x: LINE_DIAGONAL,
|
||||
y: LINE_DIAGONAL,
|
||||
z: LINE_DIAGONAL
|
||||
}
|
||||
|
||||
LINE_PROTOTYPE = {
|
||||
|
@ -75,3 +78,9 @@ EDGE_PROTOTYPE = LINE_PROTOTYPE;
|
|||
// ignoreCollisions: true,
|
||||
// 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;
|
||||
}
|
||||
|
||||
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 endPos = this.getAdjustedPosition(this.end, results);
|
||||
var vector = Vec3.subtract(endPos, startPos);
|
||||
|
|
|
@ -25,10 +25,6 @@ MagBalls = function() {
|
|||
this.refresh();
|
||||
|
||||
var _this = this;
|
||||
//Script.update.connect(function(deltaTime) {
|
||||
// _this.onUpdate(deltaTime);
|
||||
//});
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
_this.onCleanup();
|
||||
});
|
||||
|
@ -61,8 +57,13 @@ MagBalls.prototype.onUpdate = function(deltaTime) {
|
|||
if (!this.unstableEdges[edgeId]) {
|
||||
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) {
|
||||
var curPos = this.getNodePosition(nodeId);
|
||||
var newPos = nodeAdjustResults[nodeId];
|
||||
|
@ -71,18 +72,24 @@ MagBalls.prototype.onUpdate = function(deltaTime) {
|
|||
fixupEdges[edgeId] = true;
|
||||
}
|
||||
// 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) {
|
||||
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) {
|
||||
|
@ -357,4 +364,96 @@ MagBalls.prototype.onEntityAdded = function(entityId) {
|
|||
if (properties.name == BALL_NAME || properties.name == EDGE_NAME) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3553,6 +3553,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
renderContext._drawHitEffect = sceneInterface->doEngineDisplayHitEffect();
|
||||
|
||||
renderContext._occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion);
|
||||
renderContext._fxaaStatus = Menu::getInstance()->isOptionChecked(MenuOption::Antialiasing);
|
||||
|
||||
renderArgs->_shouldRender = LODManager::shouldRender;
|
||||
|
||||
|
|
|
@ -317,6 +317,7 @@ Menu::Menu() {
|
|||
0, // QML Qt::SHIFT | Qt::Key_A,
|
||||
true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DebugAmbientOcclusion);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Antialiasing);
|
||||
|
||||
MenuWrapper* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight);
|
||||
QActionGroup* ambientLightGroup = new QActionGroup(ambientLightMenu);
|
||||
|
|
|
@ -132,6 +132,7 @@ namespace MenuOption {
|
|||
const QString AddRemoveFriends = "Add/Remove Friends...";
|
||||
const QString AddressBar = "Show Address Bar";
|
||||
const QString Animations = "Animations...";
|
||||
const QString Antialiasing = "Antialiasing";
|
||||
const QString Atmosphere = "Atmosphere";
|
||||
const QString Attachments = "Attachments...";
|
||||
const QString AudioNoiseReduction = "Audio Noise Reduction";
|
||||
|
|
|
@ -68,7 +68,7 @@ float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f;
|
|||
const int SCRIPTED_MOTOR_CAMERA_FRAME = 0;
|
||||
const int SCRIPTED_MOTOR_AVATAR_FRAME = 1;
|
||||
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_MAX = 25.0f;
|
||||
|
|
|
@ -147,7 +147,12 @@ QScriptValue Web3DOverlay::getProperty(const QString& property) {
|
|||
|
||||
void Web3DOverlay::setURL(const QString& 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) {
|
||||
|
|
|
@ -310,8 +310,6 @@ void OculusDisplayPlugin::activate() {
|
|||
// not needed since the structure was zeroed on init, but explicit
|
||||
sceneLayer.ColorTexture[1] = nullptr;
|
||||
|
||||
PerformanceTimer::setActive(true);
|
||||
|
||||
if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd,
|
||||
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
|
||||
qFatal("Could not attach to sensor device");
|
||||
|
@ -322,15 +320,12 @@ void OculusDisplayPlugin::activate() {
|
|||
void OculusDisplayPlugin::customizeContext() {
|
||||
WindowOpenGLDisplayPlugin::customizeContext();
|
||||
#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->Init(getRecommendedRenderSize());
|
||||
#endif
|
||||
enableVsync(false);
|
||||
isVsyncEnabled();
|
||||
// Only enable mirroring if we know vsync is disabled
|
||||
_enableMirror = !isVsyncEnabled();
|
||||
}
|
||||
|
||||
void OculusDisplayPlugin::deactivate() {
|
||||
|
@ -338,7 +333,6 @@ void OculusDisplayPlugin::deactivate() {
|
|||
makeCurrent();
|
||||
_sceneFbo.reset();
|
||||
doneCurrent();
|
||||
PerformanceTimer::setActive(false);
|
||||
|
||||
WindowOpenGLDisplayPlugin::deactivate();
|
||||
|
||||
|
@ -355,6 +349,16 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
|
|||
// controlling vsync
|
||||
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([&] {
|
||||
auto size = _sceneFbo->size;
|
||||
Context::Viewport(size.x, size.y);
|
||||
|
@ -369,25 +373,7 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
|
|||
});
|
||||
|
||||
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;
|
||||
viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f;
|
||||
viewScaleDesc.HmdToEyeViewOffset[0] = _eyeOffsets[0];
|
||||
|
@ -401,20 +387,6 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
|
|||
}
|
||||
_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;
|
||||
#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 "AmbientOcclusionEffect.h"
|
||||
#include "AntialiasingEffect.h"
|
||||
|
||||
#include "overlay3D_vert.h"
|
||||
#include "overlay3D_frag.h"
|
||||
|
@ -88,6 +89,11 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
|
|||
_jobs.back().setEnabled(false);
|
||||
_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",
|
||||
FetchItems(
|
||||
ItemFilter::Builder::transparentShape().withoutLayered(),
|
||||
|
@ -146,6 +152,8 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend
|
|||
// TODO: turn on/off AO through menu item
|
||||
setOcclusionStatus(renderContext->_occlusionStatus);
|
||||
|
||||
setAntialiasingStatus(renderContext->_fxaaStatus);
|
||||
|
||||
renderContext->args->_context->syncCache();
|
||||
|
||||
for (auto job : _jobs) {
|
||||
|
|
|
@ -91,6 +91,11 @@ public:
|
|||
void setOcclusionStatus(bool draw) { if (_occlusionJobIndex >= 0) { _jobs[_occlusionJobIndex].setEnabled(draw); } }
|
||||
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);
|
||||
|
||||
|
||||
|
|
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 _color;
|
||||
out vec2 _texCoord0;
|
||||
out vec4 _position;
|
||||
|
||||
void main(void) {
|
||||
_color = inColor.rgb;
|
||||
_texCoord0 = inTexCoord0.st;
|
||||
_position = inPosition;
|
||||
|
||||
// standard transform
|
||||
TransformCamera cam = getTransformCamera();
|
||||
|
|
|
@ -53,6 +53,7 @@ public:
|
|||
bool _drawHitEffect = false;
|
||||
|
||||
bool _occlusionStatus = false;
|
||||
bool _fxaaStatus = false;
|
||||
|
||||
RenderContext() {}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue