mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 17:14:59 +02:00
Merge pull request #6079 from ericrius1/whiteboard
Entity whitelist for raypicking and whiteboard painting example
This commit is contained in:
commit
26f82e899e
23 changed files with 676 additions and 83 deletions
|
@ -584,7 +584,7 @@ function MyController(hand, triggerAction) {
|
|||
this.setState(STATE_RELEASE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabbingNonColliding");
|
||||
};
|
||||
|
||||
|
|
|
@ -253,3 +253,21 @@ orientationOf = function(vector) {
|
|||
return Quat.multiply(yaw, pitch);
|
||||
}
|
||||
|
||||
randFloat = function(low, high) {
|
||||
return low + Math.random() * (high - low);
|
||||
}
|
||||
|
||||
|
||||
randInt = function(low, high) {
|
||||
return Math.floor(randFloat(low, high));
|
||||
}
|
||||
|
||||
hexToRgb = function(hex) {
|
||||
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
return result ? {
|
||||
red: parseInt(result[1], 16),
|
||||
green: parseInt(result[2], 16),
|
||||
blue: parseInt(result[3], 16)
|
||||
} : null;
|
||||
}
|
||||
|
||||
|
|
23
examples/painting/whiteboard/blackInk.fs
Normal file
23
examples/painting/whiteboard/blackInk.fs
Normal file
|
@ -0,0 +1,23 @@
|
|||
vec2 iResolution = iWorldScale.xy;
|
||||
vec2 iMouse = vec2(0);
|
||||
|
||||
const float PI = 3.14159265;
|
||||
|
||||
float time = iGlobalTime;
|
||||
vec2 center = vec2(0.5, 0.5);
|
||||
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
|
||||
vec2 position = (fragCoord.xy/iResolution.xy) + 0.5;
|
||||
float dist = pow(distance(position.xy, center), 3.);
|
||||
dist = dist / 1.0 + sin(time * 10)/100.0;
|
||||
vec3 color = vec3(dist, 0.0, dist);
|
||||
fragColor = vec4(color, 1.0);
|
||||
}
|
||||
|
||||
vec4 getProceduralColor() {
|
||||
vec4 result;
|
||||
vec2 position = _position.xy;
|
||||
|
||||
mainImage(result, position * iWorldScale.xy);
|
||||
|
||||
return result;
|
||||
}
|
42
examples/painting/whiteboard/colorIndicatorEntityScript.js
Normal file
42
examples/painting/whiteboard/colorIndicatorEntityScript.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// colorIndicatorEntityScript.js
|
||||
// examples/painting/whiteboard
|
||||
//
|
||||
// Created by Eric Levin on 9/21/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
|
||||
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||
/*global ColorIndicator */
|
||||
|
||||
(function() {
|
||||
|
||||
var _this;
|
||||
ColorIndicator = function() {
|
||||
_this = this;
|
||||
};
|
||||
|
||||
ColorIndicator.prototype = {
|
||||
|
||||
changeColor: function() {
|
||||
var userData = JSON.parse(Entities.getEntityProperties(this.whiteboard, "userData").userData);
|
||||
var newColor = userData.color.currentColor;
|
||||
Entities.editEntity(this.entityID, {
|
||||
color: newColor
|
||||
});
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
var props = Entities.getEntityProperties(this.entityID, ["position", "userData"]);
|
||||
this.position = props.position;
|
||||
this.whiteboard = JSON.parse(props.userData).whiteboard;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new ColorIndicator();
|
||||
});
|
49
examples/painting/whiteboard/colorSelectorEntityScript.js
Normal file
49
examples/painting/whiteboard/colorSelectorEntityScript.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// colorSelectorEntityScript.js
|
||||
// examples/painting/whiteboard
|
||||
//
|
||||
// Created by Eric Levin on 9/21/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
|
||||
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||
/*global ColorSelector */
|
||||
|
||||
(function() {
|
||||
Script.include("../../libraries/utils.js");
|
||||
var _this;
|
||||
ColorSelector = function() {
|
||||
_this = this;
|
||||
};
|
||||
|
||||
ColorSelector.prototype = {
|
||||
|
||||
startFarGrabNonColliding: function() {
|
||||
this.selectColor();
|
||||
},
|
||||
|
||||
clickReleaseOnEntity: function() {
|
||||
this.selectColor();
|
||||
},
|
||||
|
||||
selectColor: function() {
|
||||
setEntityCustomData(this.colorKey, this.whiteboard, {currentColor: this.color});
|
||||
Entities.callEntityMethod(this.whiteboard, "changeColor");
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
var props = Entities.getEntityProperties(this.entityID, ["position, color, userData"]);
|
||||
this.position = props.position;
|
||||
this.color = props.color;
|
||||
this.colorKey = "color";
|
||||
this.whiteboard = JSON.parse(props.userData).whiteboard;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new ColorSelector();
|
||||
});
|
45
examples/painting/whiteboard/eraseBoardEntityScript.js
Normal file
45
examples/painting/whiteboard/eraseBoardEntityScript.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// eraseBoardEntityScript.js
|
||||
// examples/painting/whiteboard
|
||||
//
|
||||
// Created by Eric Levin on 9/21/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
|
||||
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||
/*global BoardEraser */
|
||||
|
||||
(function() {
|
||||
|
||||
var _this;
|
||||
BoardEraser = function() {
|
||||
_this = this;
|
||||
};
|
||||
|
||||
BoardEraser.prototype = {
|
||||
|
||||
startFarGrabNonColliding: function() {
|
||||
this.eraseBoard();
|
||||
},
|
||||
|
||||
clickReleaseOnEntity: function() {
|
||||
this.eraseBoard();
|
||||
},
|
||||
|
||||
eraseBoard: function() {
|
||||
Entities.callEntityMethod(this.whiteboard, "eraseBoard");
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
var props = Entities.getEntityProperties(this.entityID, ["userData"]);
|
||||
this.whiteboard = JSON.parse(props.userData).whiteboard;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new BoardEraser();
|
||||
});
|
250
examples/painting/whiteboard/whiteboardEntityScript.js
Normal file
250
examples/painting/whiteboard/whiteboardEntityScript.js
Normal file
|
@ -0,0 +1,250 @@
|
|||
//
|
||||
// whiteBoardEntityScript.js
|
||||
// examples/painting/whiteboard
|
||||
//
|
||||
// Created by Eric Levin on 10/12/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
|
||||
|
||||
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */
|
||||
|
||||
/*global Whiteboard */
|
||||
|
||||
|
||||
|
||||
(function() {
|
||||
Script.include("../../libraries/utils.js");
|
||||
var _this;
|
||||
var RIGHT_HAND = 1;
|
||||
var LEFT_HAND = 0;
|
||||
var MIN_POINT_DISTANCE = 0.01 ;
|
||||
var MAX_POINT_DISTANCE = 0.5;
|
||||
var MAX_POINTS_PER_LINE = 40;
|
||||
var MAX_DISTANCE = 5;
|
||||
|
||||
var PAINT_TRIGGER_THRESHOLD = 0.6;
|
||||
var MIN_STROKE_WIDTH = 0.0005;
|
||||
var MAX_STROKE_WIDTH = 0.03;
|
||||
|
||||
Whiteboard = function() {
|
||||
_this = this;
|
||||
};
|
||||
|
||||
Whiteboard.prototype = {
|
||||
|
||||
setRightHand: function() {
|
||||
this.hand = RIGHT_HAND;
|
||||
},
|
||||
|
||||
setLeftHand: function() {
|
||||
this.hand = LEFT_HAND;
|
||||
},
|
||||
|
||||
startFarGrabNonColliding: function() {
|
||||
if (this.painting) {
|
||||
return;
|
||||
}
|
||||
if (this.hand === RIGHT_HAND) {
|
||||
this.getHandPosition = MyAvatar.getRightPalmPosition;
|
||||
this.getHandRotation = MyAvatar.getRightPalmRotation;
|
||||
this.triggerAction = Controller.findAction("RIGHT_HAND_CLICK");
|
||||
} else if (this.hand === LEFT_HAND) {
|
||||
this.getHandPosition = MyAvatar.getLeftPalmPosition;
|
||||
this.getHandRotation = MyAvatar.getLeftPalmRotation;
|
||||
this.triggerAction = Controller.findAction("LEFT_HAND_CLICK");
|
||||
}
|
||||
Overlays.editOverlay(this.laserPointer, {
|
||||
visible: true
|
||||
});
|
||||
},
|
||||
|
||||
continueFarGrabbingNonColliding: function() {
|
||||
var handPosition = this.getHandPosition();
|
||||
var pickRay = {
|
||||
origin: handPosition,
|
||||
direction: Quat.getUp(this.getHandRotation())
|
||||
};
|
||||
|
||||
this.intersection = Entities.findRayIntersection(pickRay, true, this.whitelist);
|
||||
//Comment out above line and uncomment below line to see difference in performance between using a whitelist, and not using one
|
||||
// this.intersection = Entities.findRayIntersection(pickRay, true);
|
||||
|
||||
if (this.intersection.intersects) {
|
||||
var distance = Vec3.distance(handPosition, this.intersection.intersection);
|
||||
if (distance < MAX_DISTANCE) {
|
||||
this.triggerValue = Controller.getActionValue(this.triggerAction);
|
||||
this.currentStrokeWidth = map(this.triggerValue, 0, 1, MIN_STROKE_WIDTH, MAX_STROKE_WIDTH);
|
||||
var displayPoint = this.intersection.intersection;
|
||||
displayPoint = Vec3.sum(displayPoint, Vec3.multiply(this.normal, 0.01));
|
||||
Overlays.editOverlay(this.laserPointer, {
|
||||
position: displayPoint,
|
||||
size: {
|
||||
x: this.currentStrokeWidth,
|
||||
y: this.currentStrokeWidth
|
||||
}
|
||||
});
|
||||
if (this.triggerValue > PAINT_TRIGGER_THRESHOLD) {
|
||||
this.paint(this.intersection.intersection, this.intersection.surfaceNormal);
|
||||
} else {
|
||||
this.painting = false;
|
||||
this.oldPosition = null;
|
||||
}
|
||||
}
|
||||
} else if (this.intersection.properties.type !== "Unknown") {
|
||||
//Sometimes ray will pick against an invisible object with type unkown... so if type is unknown, ignore
|
||||
this.stopPainting();
|
||||
}
|
||||
},
|
||||
|
||||
stopPainting: function() {
|
||||
this.painting = false;
|
||||
Overlays.editOverlay(this.laserPointer, {
|
||||
visible: false
|
||||
});
|
||||
this.oldPosition = null;
|
||||
},
|
||||
|
||||
paint: function(position, normal) {
|
||||
if (this.painting === false) {
|
||||
if (this.oldPosition) {
|
||||
this.newStroke(this.oldPosition);
|
||||
} else {
|
||||
this.newStroke(position);
|
||||
}
|
||||
this.painting = true;
|
||||
}
|
||||
|
||||
|
||||
var localPoint = Vec3.subtract(position, this.strokeBasePosition);
|
||||
//Move stroke a bit forward along normal each point so it doesnt zfight with mesh its drawing on, or previous part of stroke(s)
|
||||
localPoint = Vec3.sum(localPoint, Vec3.multiply(this.normal, this.forwardOffset));
|
||||
this.forwardOffset += 0.00001;
|
||||
var distance = Vec3.distance(localPoint, this.strokePoints[this.strokePoints.length - 1]);
|
||||
if (this.strokePoints.length > 0 && distance < MIN_POINT_DISTANCE) {
|
||||
//need a minimum distance to avoid binormal NANs
|
||||
|
||||
return;
|
||||
}
|
||||
if (this.strokePoints.length > 0 && distance > MAX_POINT_DISTANCE) {
|
||||
//Prevents drawing lines accross models
|
||||
this.painting = false;
|
||||
return;
|
||||
}
|
||||
if (this.strokePoints.length === 0) {
|
||||
localPoint = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
}
|
||||
|
||||
this.strokePoints.push(localPoint);
|
||||
this.strokeNormals.push(this.normal);
|
||||
this.strokeWidths.push(this.currentStrokeWidth);
|
||||
Entities.editEntity(this.currentStroke, {
|
||||
linePoints: this.strokePoints,
|
||||
normals: this.strokeNormals,
|
||||
strokeWidths: this.strokeWidths
|
||||
});
|
||||
if (this.strokePoints.length === MAX_POINTS_PER_LINE) {
|
||||
this.painting = false;
|
||||
return;
|
||||
}
|
||||
this.oldPosition = position;
|
||||
},
|
||||
|
||||
|
||||
newStroke: function(position) {
|
||||
this.strokeBasePosition = position;
|
||||
this.currentStroke = Entities.addEntity({
|
||||
position: position,
|
||||
type: "PolyLine",
|
||||
name: "paintStroke",
|
||||
color: this.strokeColor,
|
||||
dimensions: {
|
||||
x: 50,
|
||||
y: 50,
|
||||
z: 50
|
||||
},
|
||||
lifetime: 200,
|
||||
userData: JSON.stringify({
|
||||
whiteboard: this.entityID
|
||||
})
|
||||
});
|
||||
this.strokePoints = [];
|
||||
this.strokeNormals = [];
|
||||
this.strokeWidths = [];
|
||||
|
||||
this.strokes.push(this.currentStroke);
|
||||
|
||||
},
|
||||
|
||||
releaseGrab: function() {
|
||||
this.stopPainting();
|
||||
|
||||
},
|
||||
|
||||
changeColor: function() {
|
||||
var userData = JSON.parse(Entities.getEntityProperties(this.entityID, ["userData"]).userData);
|
||||
this.strokeColor = userData.color.currentColor;
|
||||
this.colorIndicator = userData.colorIndicator;
|
||||
Overlays.editOverlay(this.laserPointer, {
|
||||
color: this.strokeColor
|
||||
});
|
||||
|
||||
|
||||
Entities.callEntityMethod(this.colorIndicator, "changeColor");
|
||||
},
|
||||
|
||||
eraseBoard: function() {
|
||||
var distance = Math.max(this.dimensions.x, this.dimensions.y);
|
||||
var entities = Entities.findEntities(this.position, distance);
|
||||
entities.forEach(function(entity) {
|
||||
var props = Entities.getEntityProperties(entity, ["name, userData"]);
|
||||
var name = props.name;
|
||||
if(!props.userData) {
|
||||
return;
|
||||
}
|
||||
var whiteboardID = JSON.parse(props.userData).whiteboard;
|
||||
if (name === "paintStroke" && JSON.stringify(whiteboardID) === JSON.stringify(_this.entityID)) {
|
||||
// This entity is a paintstroke and part of this whiteboard so delete it
|
||||
Entities.deleteEntity(entity);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
var props = Entities.getEntityProperties(this.entityID, ["position", "rotation", "userData", "dimensions"]);
|
||||
this.position = props.position;
|
||||
this.rotation = props.rotation;
|
||||
this.dimensions = props.dimensions;
|
||||
this.normal = Vec3.multiply(Quat.getFront(this.rotation), -1);
|
||||
this.painting = false;
|
||||
this.strokes = [];
|
||||
this.whitelist = [this.entityID];
|
||||
this.laserPointer = Overlays.addOverlay("circle3d", {
|
||||
color: this.strokeColor,
|
||||
solid: true,
|
||||
rotation: this.rotation
|
||||
});
|
||||
this.forwardOffset = 0.0005;
|
||||
|
||||
this.changeColor();
|
||||
},
|
||||
|
||||
unload: function() {
|
||||
|
||||
Overlays.deleteOverlay(this.laserPointer);
|
||||
// this.eraseBoard();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new Whiteboard();
|
||||
});
|
201
examples/painting/whiteboard/whiteboardSpawner.js
Normal file
201
examples/painting/whiteboard/whiteboardSpawner.js
Normal file
|
@ -0,0 +1,201 @@
|
|||
//
|
||||
// whiteBoardSpawner.js
|
||||
// examples/painting/whiteboard
|
||||
//
|
||||
// Created by Eric Levina on 10/12/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Run this script to spawn a whiteboard that one can paint on
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */
|
||||
// Script specific
|
||||
/*global hexToRgb */
|
||||
|
||||
Script.include("../../libraries/utils.js");
|
||||
var scriptURL = Script.resolvePath("whiteboardEntityScript.js");
|
||||
var rotation = Quat.safeEulerAngles(Camera.getOrientation());
|
||||
rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0);
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(rotation)));
|
||||
center.y += 0.4;
|
||||
|
||||
var colors = [
|
||||
hexToRgb("#2F8E84"),
|
||||
hexToRgb("#66CCB3"),
|
||||
hexToRgb("#A43C37"),
|
||||
hexToRgb("#491849"),
|
||||
hexToRgb("#6AB03B"),
|
||||
hexToRgb("#993369"),
|
||||
hexToRgb("#9B47C2")
|
||||
];
|
||||
|
||||
//WHITEBOARD
|
||||
var whiteboardDimensions = {
|
||||
x: 2,
|
||||
y: 1.5,
|
||||
z: 0.08
|
||||
};
|
||||
var whiteboard = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: "https://hifi-public.s3.amazonaws.com/ozan/support/for_eric/whiteboard/whiteboard.fbx",
|
||||
name: "whiteboard",
|
||||
position: center,
|
||||
rotation: rotation,
|
||||
script: scriptURL,
|
||||
dimensions: whiteboardDimensions,
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// COLOR INDICATOR BOX
|
||||
var colorIndicatorDimensions = {
|
||||
x: whiteboardDimensions.x,
|
||||
y: 0.05,
|
||||
z: 0.02
|
||||
};
|
||||
scriptURL = Script.resolvePath("colorIndicatorEntityScript.js");
|
||||
var colorIndicatorPosition = Vec3.sum(center, {
|
||||
x: 0,
|
||||
y: whiteboardDimensions.y / 2 + colorIndicatorDimensions.y / 2,
|
||||
z: 0
|
||||
});
|
||||
var colorIndicatorBox = Entities.addEntity({
|
||||
type: "Box",
|
||||
name: "Color Indicator",
|
||||
color: colors[0],
|
||||
rotation: rotation,
|
||||
position: colorIndicatorPosition,
|
||||
dimensions: colorIndicatorDimensions,
|
||||
script: scriptURL,
|
||||
userData: JSON.stringify({
|
||||
whiteboard: whiteboard
|
||||
})
|
||||
});
|
||||
|
||||
Entities.editEntity(whiteboard, {
|
||||
userData: JSON.stringify({
|
||||
color: {
|
||||
currentColor: colors[0]
|
||||
},
|
||||
colorIndicator: colorIndicatorBox
|
||||
})
|
||||
});
|
||||
|
||||
//COLOR BOXES
|
||||
var direction = Quat.getRight(rotation);
|
||||
var colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2));
|
||||
var colorBoxes = [];
|
||||
var colorSquareDimensions = {
|
||||
x: (whiteboardDimensions.x / 2) / (colors.length - 1),
|
||||
y: 0.1,
|
||||
z: 0.05
|
||||
};
|
||||
colorBoxPosition.y += whiteboardDimensions.y / 2 + colorIndicatorDimensions.y + colorSquareDimensions.y / 2;
|
||||
var spaceBetweenColorBoxes = Vec3.multiply(direction, colorSquareDimensions.x * 2);
|
||||
var scriptURL = Script.resolvePath("colorSelectorEntityScript.js");
|
||||
for (var i = 0; i < colors.length; i++) {
|
||||
var colorBox = Entities.addEntity({
|
||||
type: "Box",
|
||||
name: "Color Selector",
|
||||
position: colorBoxPosition,
|
||||
dimensions: colorSquareDimensions,
|
||||
rotation: rotation,
|
||||
color: colors[i],
|
||||
script: scriptURL,
|
||||
userData: JSON.stringify({
|
||||
whiteboard: whiteboard,
|
||||
colorIndicator: colorIndicatorBox
|
||||
})
|
||||
});
|
||||
colorBoxes.push(colorBox);
|
||||
colorBoxPosition = Vec3.sum(colorBoxPosition, spaceBetweenColorBoxes);
|
||||
}
|
||||
|
||||
|
||||
// BLACK BOX
|
||||
var blackBoxDimensions = {
|
||||
x: 0.3,
|
||||
y: 0.3,
|
||||
z: 0.01
|
||||
};
|
||||
|
||||
colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + blackBoxDimensions.x / 2 - 0.01));
|
||||
colorBoxPosition.y += 0.3;
|
||||
var fragShaderURL = Script.resolvePath('blackInk.fs?v1' + Math.random());
|
||||
var blackBox = Entities.addEntity({
|
||||
type: 'Box',
|
||||
name: "Black Color",
|
||||
position: colorBoxPosition,
|
||||
dimensions: blackBoxDimensions,
|
||||
rotation: rotation,
|
||||
color: {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 0
|
||||
},
|
||||
script: scriptURL,
|
||||
userData: JSON.stringify({
|
||||
whiteboard: whiteboard,
|
||||
version: 2,
|
||||
ProceduralEntity: {
|
||||
shaderUrl: fragShaderURL
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
var eraseBoxDimensions = {
|
||||
x: 0.5,
|
||||
y: 0.1,
|
||||
z: 0.01
|
||||
};
|
||||
|
||||
|
||||
var eraseBoxPosition = Vec3.sum(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + eraseBoxDimensions.x / 2 + 0.01));
|
||||
eraseBoxPosition.y += 0.3;
|
||||
scriptURL = Script.resolvePath("eraseBoardEntityScript.js");
|
||||
var eraseAllText = Entities.addEntity({
|
||||
type: "Text",
|
||||
position: eraseBoxPosition,
|
||||
name: "Eraser",
|
||||
script: scriptURL,
|
||||
rotation: rotation,
|
||||
dimensions: eraseBoxDimensions,
|
||||
backgroundColor: {
|
||||
red: 0,
|
||||
green: 60,
|
||||
blue: 0
|
||||
},
|
||||
textColor: {
|
||||
red: 255,
|
||||
green: 10,
|
||||
blue: 10
|
||||
},
|
||||
text: "ERASE BOARD",
|
||||
lineHeight: 0.07,
|
||||
userData: JSON.stringify({
|
||||
whiteboard: whiteboard
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(whiteboard);
|
||||
Entities.deleteEntity(eraseAllText);
|
||||
Entities.deleteEntity(blackBox);
|
||||
Entities.deleteEntity(colorIndicatorBox);
|
||||
colorBoxes.forEach(function(colorBox) {
|
||||
Entities.deleteEntity(colorBox);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Uncomment this line to delete whiteboard and all associated entity on script close
|
||||
Script.scriptEnding.connect(cleanup);
|
|
@ -1,60 +0,0 @@
|
|||
// utilities.js
|
||||
// examples
|
||||
//
|
||||
// Created by Eric Levin on June 8
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Common utilities
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
|
||||
|
||||
map = function(value, min1, max1, min2, max2) {
|
||||
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
|
||||
}
|
||||
|
||||
hslToRgb = function(hslColor) {
|
||||
var h = hslColor.hue;
|
||||
var s = hslColor.sat;
|
||||
var l = hslColor.light;
|
||||
var r, g, b;
|
||||
|
||||
if (s == 0) {
|
||||
r = g = b = l; // achromatic
|
||||
} else {
|
||||
var hue2rgb = function hue2rgb(p, q, t) {
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
||||
if (t < 1 / 2) return q;
|
||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
||||
return p;
|
||||
}
|
||||
|
||||
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
var p = 2 * l - q;
|
||||
r = hue2rgb(p, q, h + 1 / 3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1 / 3);
|
||||
}
|
||||
|
||||
return {
|
||||
red: Math.round(r * 255),
|
||||
green: Math.round(g * 255),
|
||||
blue: Math.round(b * 255)
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
randFloat = function(low, high) {
|
||||
return low + Math.random() * (high - low);
|
||||
}
|
||||
|
||||
|
||||
randInt = function(low, high) {
|
||||
return Math.floor(randFloat(low, high));
|
||||
}
|
|
@ -482,7 +482,7 @@ void EntityTreeRenderer::deleteReleasedModels() {
|
|||
}
|
||||
|
||||
RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
|
||||
bool precisionPicking) {
|
||||
bool precisionPicking, const QVector<QUuid>& entityIdsToInclude) {
|
||||
RayToEntityIntersectionResult result;
|
||||
if (_tree) {
|
||||
EntityTreePointer entityTree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
|
@ -490,7 +490,7 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons
|
|||
OctreeElementPointer element;
|
||||
EntityItemPointer intersectedEntity = NULL;
|
||||
result.intersects = entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance,
|
||||
result.face, result.surfaceNormal,
|
||||
result.face, result.surfaceNormal, entityIdsToInclude,
|
||||
(void**)&intersectedEntity, lockType, &result.accurate,
|
||||
precisionPicking);
|
||||
if (result.intersects && intersectedEntity) {
|
||||
|
|
|
@ -130,7 +130,7 @@ private:
|
|||
|
||||
QList<Model*> _releasedModels;
|
||||
RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
|
||||
bool precisionPicking);
|
||||
bool precisionPicking, const QVector<QUuid>& entityIdsToInclude = QVector<QUuid>());
|
||||
|
||||
EntityItemID _currentHoverOverEntityID;
|
||||
EntityItemID _currentClickingOnEntityID;
|
||||
|
|
|
@ -279,17 +279,19 @@ QVector<QUuid> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn
|
|||
return result;
|
||||
}
|
||||
|
||||
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking) {
|
||||
return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking);
|
||||
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude) {
|
||||
QVector<QUuid> entities = qVectorQUuidFromScriptValue(entityIdsToInclude);
|
||||
return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking, entities);
|
||||
}
|
||||
|
||||
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking) {
|
||||
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
|
||||
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude) {
|
||||
const QVector<QUuid>& entities = qVectorQUuidFromScriptValue(entityIdsToInclude);
|
||||
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entities);
|
||||
}
|
||||
|
||||
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray,
|
||||
Octree::lockType lockType,
|
||||
bool precisionPicking) {
|
||||
bool precisionPicking, const QVector<QUuid>& entityIdsToInclude) {
|
||||
|
||||
|
||||
RayToEntityIntersectionResult result;
|
||||
|
@ -297,7 +299,7 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke
|
|||
OctreeElementPointer element;
|
||||
EntityItemPointer intersectedEntity = NULL;
|
||||
result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
|
||||
result.surfaceNormal, (void**)&intersectedEntity, lockType, &result.accurate,
|
||||
result.surfaceNormal, entityIdsToInclude, (void**)&intersectedEntity, lockType, &result.accurate,
|
||||
precisionPicking);
|
||||
if (result.intersects && intersectedEntity) {
|
||||
result.entityID = intersectedEntity->getEntityItemID();
|
||||
|
|
|
@ -111,11 +111,11 @@ public slots:
|
|||
/// If the scripting context has visible entities, this will determine a ray intersection, the results
|
||||
/// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate
|
||||
/// will be false.
|
||||
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false);
|
||||
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue());
|
||||
|
||||
/// If the scripting context has visible entities, this will determine a ray intersection, and will block in
|
||||
/// order to return an accurate result
|
||||
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false);
|
||||
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue());
|
||||
|
||||
Q_INVOKABLE void setLightsArePickable(bool value);
|
||||
Q_INVOKABLE bool getLightsArePickable() const;
|
||||
|
@ -186,7 +186,7 @@ private:
|
|||
|
||||
/// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode
|
||||
RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
|
||||
bool precisionPicking);
|
||||
bool precisionPicking, const QVector<QUuid>& entityIdsToInclude);
|
||||
|
||||
EntityTreePointer _entityTree;
|
||||
EntitiesScriptEngineProvider* _entitiesScriptEngine = nullptr;
|
||||
|
|
|
@ -495,12 +495,16 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3
|
|||
|
||||
bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching,
|
||||
OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal,
|
||||
void** intersectedObject, bool precisionPicking, float distanceToElementCube) {
|
||||
const QVector<QUuid>& entityIdsToInclude, void** intersectedObject, bool precisionPicking, float distanceToElementCube) {
|
||||
|
||||
// only called if we do intersect our bounding cube, but find if we actually intersect with entities...
|
||||
int entityNumber = 0;
|
||||
bool somethingIntersected = false;
|
||||
forEachEntity([&](EntityItemPointer entity) {
|
||||
if (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) {
|
||||
return;
|
||||
}
|
||||
|
||||
AABox entityBox = entity->getAABox();
|
||||
float localDistance;
|
||||
BoxFace localFace;
|
||||
|
|
|
@ -144,7 +144,7 @@ public:
|
|||
virtual bool canRayIntersect() const { return hasEntities(); }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
BoxFace& face, glm::vec3& surfaceNormal, const QVector<QUuid>& entityIdsToInclude,
|
||||
void** intersectedObject, bool precisionPicking, float distanceToElementCube);
|
||||
|
||||
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
|
||||
|
|
|
@ -703,6 +703,7 @@ public:
|
|||
float& distance;
|
||||
BoxFace& face;
|
||||
glm::vec3& surfaceNormal;
|
||||
const QVector<QUuid>& entityIdsToInclude;
|
||||
void** intersectedObject;
|
||||
bool found;
|
||||
bool precisionPicking;
|
||||
|
@ -712,7 +713,7 @@ bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) {
|
|||
RayArgs* args = static_cast<RayArgs*>(extraData);
|
||||
bool keepSearching = true;
|
||||
if (element->findRayIntersection(args->origin, args->direction, keepSearching,
|
||||
args->element, args->distance, args->face, args->surfaceNormal,
|
||||
args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude,
|
||||
args->intersectedObject, args->precisionPicking)) {
|
||||
args->found = true;
|
||||
}
|
||||
|
@ -721,9 +722,9 @@ bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) {
|
|||
|
||||
bool Octree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal, void** intersectedObject,
|
||||
BoxFace& face, glm::vec3& surfaceNormal, const QVector<QUuid>& entityIdsToInclude, void** intersectedObject,
|
||||
Octree::lockType lockType, bool* accurateResult, bool precisionPicking) {
|
||||
RayArgs args = { origin, direction, element, distance, face, surfaceNormal, intersectedObject, false, precisionPicking};
|
||||
RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, intersectedObject, false, precisionPicking};
|
||||
distance = FLT_MAX;
|
||||
|
||||
bool requireLock = lockType == Octree::Lock;
|
||||
|
|
|
@ -300,6 +300,7 @@ public:
|
|||
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& node, float& distance, BoxFace& face, glm::vec3& surfaceNormal,
|
||||
const QVector<QUuid>& entityIdsToInclude = QVector<QUuid>(),
|
||||
void** intersectedObject = NULL,
|
||||
Octree::lockType lockType = Octree::TryLock,
|
||||
bool* accurateResult = NULL,
|
||||
|
|
|
@ -575,7 +575,7 @@ void OctreeElement::notifyUpdateHooks() {
|
|||
|
||||
bool OctreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
BoxFace& face, glm::vec3& surfaceNormal, const QVector<QUuid>& entityIdsToInclude,
|
||||
void** intersectedObject, bool precisionPicking) {
|
||||
|
||||
keepSearching = true; // assume that we will continue searching after this.
|
||||
|
@ -601,7 +601,7 @@ bool OctreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3
|
|||
if (_cube.contains(origin) || distanceToElementCube < distance) {
|
||||
|
||||
if (findDetailedRayIntersection(origin, direction, keepSearching, element, distanceToElementDetails,
|
||||
face, localSurfaceNormal, intersectedObject, precisionPicking, distanceToElementCube)) {
|
||||
face, localSurfaceNormal, entityIdsToInclude, intersectedObject, precisionPicking, distanceToElementCube)) {
|
||||
|
||||
if (distanceToElementDetails < distance) {
|
||||
distance = distanceToElementDetails;
|
||||
|
@ -616,7 +616,7 @@ bool OctreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3
|
|||
|
||||
bool OctreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
BoxFace& face, glm::vec3& surfaceNormal, const QVector<QUuid>& entityIdsToInclude,
|
||||
void** intersectedObject, bool precisionPicking, float distanceToElementCube) {
|
||||
|
||||
// we did hit this element, so calculate appropriate distances
|
||||
|
|
|
@ -120,12 +120,12 @@ public:
|
|||
virtual bool canRayIntersect() const { return isLeaf(); }
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& node, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
BoxFace& face, glm::vec3& surfaceNormal, const QVector<QUuid>& entityIdsToInclude,
|
||||
void** intersectedObject = NULL, bool precisionPicking = false);
|
||||
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
BoxFace& face, glm::vec3& surfaceNormal, const QVector<QUuid>& entityIdsToInclude,
|
||||
void** intersectedObject, bool precisionPicking, float distanceToElementCube);
|
||||
|
||||
/// \param center center of sphere in meters
|
||||
|
|
|
@ -104,6 +104,21 @@ QVector<float> qVectorFloatFromScriptValue(const QScriptValue& array) {
|
|||
return newVector;
|
||||
}
|
||||
|
||||
QVector<QUuid> qVectorQUuidFromScriptValue(const QScriptValue& array) {
|
||||
if (!array.isArray()) {
|
||||
return QVector<QUuid>();
|
||||
}
|
||||
QVector<QUuid> newVector;
|
||||
int length = array.property("length").toInteger();
|
||||
newVector.reserve(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
QString uuidAsString = array.property(i).toString();
|
||||
QUuid fromString(uuidAsString);
|
||||
newVector << fromString;
|
||||
}
|
||||
return newVector;
|
||||
}
|
||||
|
||||
QScriptValue qVectorFloatToScriptValue(QScriptEngine* engine, const QVector<float>& vector) {
|
||||
QScriptValue array = engine->newArray();
|
||||
for (int i = 0; i < vector.size(); i++) {
|
||||
|
|
|
@ -65,6 +65,8 @@ QScriptValue qVectorFloatToScriptValue(QScriptEngine* engine, const QVector<floa
|
|||
void qVectorFloatFromScriptValue(const QScriptValue& array, QVector<float>& vector);
|
||||
QVector<float> qVectorFloatFromScriptValue(const QScriptValue& array);
|
||||
|
||||
QVector<QUuid> qVectorQUuidFromScriptValue(const QScriptValue& array);
|
||||
|
||||
class PickRay {
|
||||
public:
|
||||
PickRay() : origin(0.0f), direction(0.0f) { }
|
||||
|
|
Loading…
Reference in a new issue