merge from upstream

This commit is contained in:
Seth Alves 2016-01-03 06:47:25 -08:00
commit bfa8cc1969
107 changed files with 8981 additions and 1451 deletions

56
cmake/externals/neuron/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,56 @@
include(ExternalProject)
set(EXTERNAL_NAME neuron)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
set(NEURON_URL "https://s3.amazonaws.com/hifi-public/dependencies/neuron_datareader_b.12.zip")
set(NEURON_URL_MD5 "0ab54ca04c9cc8094e0fa046c226e574")
ExternalProject_Add(${EXTERNAL_NAME}
URL ${NEURON_URL}
URL_MD5 ${NEURON_URL_MD5}
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD 1)
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
# set include dir
if(WIN32)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS "${SOURCE_DIR}/NeuronDataReader_Windows/include" CACHE TYPE INTERNAL)
elseif(APPLE)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS "${SOURCE_DIR}/NeuronDataReader_Mac/include" CACHE TYPE INTERNAL)
else()
# Unsupported
endif()
if(WIN32)
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(ARCH_DIR "x64")
else()
set(ARCH_DIR "x86")
endif()
set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/NeuronDataReader_Windows/lib/${ARCH_DIR}")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.lib" CACHE TYPE INTERNAL)
set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.lib" CACHE TYPE INTERNAL)
add_paths_to_fixup_libs("${${EXTERNAL_NAME_UPPER}_LIB_PATH}")
elseif(APPLE)
set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/NeuronDataReader_Mac/dylib")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.dylib" CACHE TYPE INTERNAL)
set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.dylib" CACHE TYPE INTERNAL)
add_paths_to_fixup_libs("${${EXTERNAL_NAME_UPPER}_LIB_PATH}")
else()
# UNSUPPORTED
endif()

View file

@ -0,0 +1,17 @@
#
# Copyright 2015 High Fidelity, Inc.
# Created by Anthony J. Thibault on 2015/12/21
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_NEURON)
# Neuron data reader is only available on these platforms
if (WIN32 OR APPLE)
add_dependency_external_projects(neuron)
find_package(Neuron REQUIRED)
target_include_directories(${TARGET_NAME} PRIVATE ${NEURON_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${NEURON_LIBRARIES})
add_definitions(-DHAVE_NEURON)
endif(WIN32 OR APPLE)
endmacro()

View file

@ -0,0 +1,28 @@
#
# FindNeuron.cmake
#
# Try to find the Perception Neuron SDK
#
# You must provide a NEURON_ROOT_DIR which contains lib and include directories
#
# Once done this will define
#
# NEURON_FOUND - system found Neuron SDK
# NEURON_INCLUDE_DIRS - the Neuron SDK include directory
# NEURON_LIBRARIES - Link this to use Neuron
#
# Created on 12/21/2015 by Anthony J. Thibault
# 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(SelectLibraryConfigurations)
select_library_configurations(NEURON)
set(NEURON_REQUIREMENTS NEURON_INCLUDE_DIRS NEURON_LIBRARIES)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Neuron DEFAULT_MSG NEURON_INCLUDE_DIRS NEURON_LIBRARIES)
mark_as_advanced(NEURON_LIBRARIES NEURON_INCLUDE_DIRS NEURON_SEARCH_DIRS)

View file

@ -0,0 +1,72 @@
//
// triggeredRecordingOnAC.js
// examples/acScripts
//
// Created by Thijs Wenker on 12/21/15.
// Copyright 2015 High Fidelity, Inc.
//
// This is the triggered rocording script used in the winterSmashUp target practice game.
// Change the CLIP_URL to your asset,
// the RECORDING_CHANNEL and RECORDING_CHANNEL_MESSAGE are used to trigger it i.e.:
// Messages.sendMessage("PlayBackOnAssignment", "BowShootingGameWelcome");
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
const CLIP_URL = "atp:3fbe82f2153c443f12f9a2b14ce2d7fa2fff81977263746d9e0885ea5aabed62.hfr";
const RECORDING_CHANNEL = 'PlayBackOnAssignment';
const RECORDING_CHANNEL_MESSAGE = 'BowShootingGameWelcome'; // For each different assignment add a different message here.
const PLAY_FROM_CURRENT_LOCATION = true;
const USE_DISPLAY_NAME = true;
const USE_ATTACHMENTS = true;
const USE_AVATAR_MODEL = true;
const AUDIO_OFFSET = 0.0;
const STARTING_TIME = 0.0;
const COOLDOWN_PERIOD = 0; // The period in ms that no animations can be played after one has been played already
var isPlaying = false;
var isPlayable = true;
var playRecording = function() {
if (!isPlayable || isPlaying) {
return;
}
Agent.isAvatar = true;
Recording.setPlayFromCurrentLocation(PLAY_FROM_CURRENT_LOCATION);
Recording.setPlayerUseDisplayName(USE_DISPLAY_NAME);
Recording.setPlayerUseAttachments(USE_ATTACHMENTS);
Recording.setPlayerUseHeadModel(false);
Recording.setPlayerUseSkeletonModel(USE_AVATAR_MODEL);
Recording.setPlayerLoop(false);
Recording.setPlayerTime(STARTING_TIME);
Recording.setPlayerAudioOffset(AUDIO_OFFSET);
Recording.loadRecording(CLIP_URL);
Recording.startPlaying();
isPlaying = true;
isPlayable = false; // Set this true again after the cooldown period
};
Script.update.connect(function(deltaTime) {
if (isPlaying && !Recording.isPlaying()) {
print('Reached the end of the recording. Resetting.');
isPlaying = false;
Agent.isAvatar = false;
if (COOLDOWN_PERIOD === 0) {
isPlayable = true;
return;
}
Script.setTimeout(function () {
isPlayable;
}, COOLDOWN_PERIOD);
}
});
Messages.subscribe(RECORDING_CHANNEL);
Messages.messageReceived.connect(function (channel, message, senderID) {
if (channel === RECORDING_CHANNEL && message === RECORDING_CHANNEL_MESSAGE) {
playRecording();
}
});

View file

@ -0,0 +1,54 @@
//
// getHUDLookAtPositionTest.js
// examples/controllers
//
// Created by Brad Hefta-Gaub on 2015/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
//
// This script demonstrates the testing of the HMD.getHUDLookAtPosition--() functions.
// If these functions are working correctly, we'd expect to see a 3D cube and a 2D square
// follow around the center of the HMD view.
var cubePosition = { x: 0, y: 0, z: 0 };
var cubeSize = 0.03;
var cube = Overlays.addOverlay("cube", {
position: cubePosition,
size: cubeSize,
color: { red: 255, green: 0, blue: 0},
alpha: 1,
solid: false
});
var square = Overlays.addOverlay("text", {
x: 0,
y: 0,
width: 20,
height: 20,
color: { red: 255, green: 255, blue: 0},
backgroundColor: { red: 255, green: 255, blue: 0},
alpha: 1
});
Script.update.connect(function(deltaTime) {
if (!HMD.active) {
return;
}
var lookAt3D = HMD.getHUDLookAtPosition3D();
Overlays.editOverlay(cube, { position: lookAt3D });
var lookAt2D = HMD.getHUDLookAtPosition2D();
Overlays.editOverlay(square, { x: lookAt2D.x, y: lookAt2D.y });
});
Script.scriptEnding.connect(function(){
Overlays.deleteOverlay(cube);
Overlays.deleteOverlay(square);
});

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,239 @@
var JOINT_PARENT_MAP = {
Hips: "",
RightUpLeg: "Hips",
RightLeg: "RightUpLeg",
RightFoot: "RightLeg",
LeftUpLeg: "Hips",
LeftLeg: "LeftUpLeg",
LeftFoot: "LeftLeg",
Spine: "Hips",
Spine1: "Spine",
Spine2: "Spine1",
Spine3: "Spine2",
Neck: "Spine3",
Head: "Neck",
RightShoulder: "Spine3",
RightArm: "RightShoulder",
RightForeArm: "RightArm",
RightHand: "RightForeArm",
RightHandThumb1: "RightHand",
RightHandThumb2: "RightHandThumb1",
RightHandThumb3: "RightHandThumb2",
RightHandThumb4: "RightHandThumb3",
RightHandIndex1: "RightHand",
RightHandIndex2: "RightHandIndex1",
RightHandIndex3: "RightHandIndex2",
RightHandIndex4: "RightHandIndex3",
RightHandMiddle1: "RightHand",
RightHandMiddle2: "RightHandMiddle1",
RightHandMiddle3: "RightHandMiddle2",
RightHandMiddle4: "RightHandMiddle3",
RightHandRing1: "RightHand",
RightHandRing2: "RightHandRing1",
RightHandRing3: "RightHandRing2",
RightHandRing4: "RightHandRing3",
RightHandPinky1: "RightHand",
RightHandPinky2: "RightHandPinky1",
RightHandPinky3: "RightHandPinky2",
RightHandPinky4: "RightHandPinky3",
LeftShoulder: "Spine3",
LeftArm: "LeftShoulder",
LeftForeArm: "LeftArm",
LeftHand: "LeftForeArm",
LeftHandThumb1: "LeftHand",
LeftHandThumb2: "LeftHandThumb1",
LeftHandThumb3: "LeftHandThumb2",
LeftHandThumb4: "LeftHandThumb3",
LeftHandIndex1: "LeftHand",
LeftHandIndex2: "LeftHandIndex1",
LeftHandIndex3: "LeftHandIndex2",
LeftHandIndex4: "LeftHandIndex3",
LeftHandMiddle1: "LeftHand",
LeftHandMiddle2: "LeftHandMiddle1",
LeftHandMiddle3: "LeftHandMiddle2",
LeftHandMiddle4: "LeftHandMiddle3",
LeftHandRing1: "LeftHand",
LeftHandRing2: "LeftHandRing1",
LeftHandRing3: "LeftHandRing2",
LeftHandRing4: "LeftHandRing3",
LeftHandPinky1: "LeftHand",
LeftHandPinky2: "LeftHandPinky1",
LeftHandPinky3: "LeftHandPinky2",
LeftHandPinky: "LeftHandPinky3",
};
var USE_TRANSLATIONS = false;
// ctor
function Xform(rot, pos) {
this.rot = rot;
this.pos = pos;
};
Xform.mul = function (lhs, rhs) {
var rot = Quat.multiply(lhs.rot, rhs.rot);
var pos = Vec3.sum(lhs.pos, Vec3.multiplyQbyV(lhs.rot, rhs.pos));
return new Xform(rot, pos);
};
Xform.prototype.inv = function () {
var invRot = Quat.inverse(this.rot);
var invPos = Vec3.multiply(-1, this.pos);
return new Xform(invRot, Vec3.multiplyQbyV(invRot, invPos));
};
Xform.prototype.toString = function () {
var rot = this.rot;
var pos = this.pos;
return "Xform rot = (" + rot.x + ", " + rot.y + ", " + rot.z + ", " + rot.w + "), pos = (" + pos.x + ", " + pos.y + ", " + pos.z + ")";
};
function dumpHardwareMapping() {
Object.keys(Controller.Hardware).forEach(function (deviceName) {
Object.keys(Controller.Hardware[deviceName]).forEach(function (input) {
print("Controller.Hardware." + deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]);
});
});
}
// ctor
function NeuronAvatar() {
var self = this;
Script.scriptEnding.connect(function () {
self.shutdown();
});
Controller.hardwareChanged.connect(function () {
self.hardwareChanged();
});
if (Controller.Hardware.Neuron) {
this.activate();
} else {
this.deactivate();
}
}
NeuronAvatar.prototype.shutdown = function () {
this.deactivate();
};
NeuronAvatar.prototype.hardwareChanged = function () {
if (Controller.Hardware.Neuron) {
this.activate();
} else {
this.deactivate();
}
};
NeuronAvatar.prototype.activate = function () {
if (!this._active) {
Script.update.connect(updateCallback);
}
this._active = true;
// build absDefaultPoseMap
this._defaultAbsRotMap = {};
this._defaultAbsPosMap = {};
this._defaultAbsRotMap[""] = {x: 0, y: 0, z: 0, w: 1};
this._defaultAbsPosMap[""] = {x: 0, y: 0, z: 0};
var keys = Object.keys(JOINT_PARENT_MAP);
var i, l = keys.length;
for (i = 0; i < l; i++) {
var jointName = keys[i];
var j = MyAvatar.getJointIndex(jointName);
var parentJointName = JOINT_PARENT_MAP[jointName];
this._defaultAbsRotMap[jointName] = Quat.multiply(this._defaultAbsRotMap[parentJointName], MyAvatar.getDefaultJointRotation(j));
this._defaultAbsPosMap[jointName] = Vec3.sum(this._defaultAbsPosMap[parentJointName],
Quat.multiply(this._defaultAbsRotMap[parentJointName], MyAvatar.getDefaultJointTranslation(j)));
}
};
NeuronAvatar.prototype.deactivate = function () {
if (this._active) {
var self = this;
Script.update.disconnect(updateCallback);
}
this._active = false;
MyAvatar.clearJointsData();
};
NeuronAvatar.prototype.update = function (deltaTime) {
var hmdActive = HMD.active;
var keys = Object.keys(JOINT_PARENT_MAP);
var i, l = keys.length;
var absDefaultRot = {};
var jointName, channel, pose, parentJointName, j, parentDefaultAbsRot;
var localRotations = {};
var localTranslations = {};
for (i = 0; i < l; i++) {
var jointName = keys[i];
var channel = Controller.Hardware.Neuron[jointName];
if (channel) {
pose = Controller.getPoseValue(channel);
parentJointName = JOINT_PARENT_MAP[jointName];
j = MyAvatar.getJointIndex(jointName);
defaultAbsRot = this._defaultAbsRotMap[jointName];
parentDefaultAbsRot = this._defaultAbsRotMap[parentJointName];
// Rotations from the neuron controller are in world orientation but are delta's from the default pose.
// So first we build the absolute rotation of the default pose (local into world).
// Then apply the rotation from the controller, in world space.
// Then we transform back into joint local by multiplying by the inverse of the parents absolute rotation.
var localRotation = Quat.multiply(Quat.inverse(parentDefaultAbsRot), Quat.multiply(pose.rotation, defaultAbsRot));
if (!hmdActive || jointName !== "Hips") {
MyAvatar.setJointRotation(j, localRotation);
}
localRotations[jointName] = localRotation;
// translation proportions might be different from the neuron avatar and the user avatar skeleton.
// so this is disabled by default
if (USE_TRANSLATIONS) {
var localTranslation = Vec3.multiplyQbyV(Quat.inverse(parentDefaultAbsRot), pose.translation);
MyAvatar.setJointTranslation(j, localTranslation);
localTranslations[jointName] = localTranslation;
} else {
localTranslations[jointName] = MyAvatar.getDefaultJointTranslation(j);
}
}
}
// it attempts to adjust the hips so that the avatar's head is at the same location & oreintation as the HMD.
// however it's fighting with the internal c++ code that also attempts to adjust the hips.
if (hmdActive) {
var UNIT_SCALE = 1 / 100;
var hmdXform = new Xform(HMD.orientation, Vec3.multiply(1 / UNIT_SCALE, HMD.position)); // convert to cm
var y180Xform = new Xform({x: 0, y: 1, z: 0, w: 0}, {x: 0, y: 0, z: 0});
var avatarXform = new Xform(MyAvatar.orientation, Vec3.multiply(1 / UNIT_SCALE, MyAvatar.position)); // convert to cm
var hipsJointIndex = MyAvatar.getJointIndex("Hips");
var modelOffsetInvXform = new Xform({x: 0, y: 0, z: 0, w: 1}, MyAvatar.getDefaultJointTranslation(hipsJointIndex));
var defaultHipsXform = new Xform(MyAvatar.getDefaultJointRotation(hipsJointIndex), MyAvatar.getDefaultJointTranslation(hipsJointIndex));
var headXform = new Xform(localRotations["Head"], localTranslations["Head"]);
// transform eyes down the heirarchy chain into avatar space.
var hierarchy = ["Neck", "Spine3", "Spine2", "Spine1", "Spine"];
var i, l = hierarchy.length;
for (i = 0; i < l; i++) {
var xform = new Xform(localRotations[hierarchy[i]], localTranslations[hierarchy[i]]);
headXform = Xform.mul(xform, headXform);
}
headXform = Xform.mul(defaultHipsXform, headXform);
var preXform = Xform.mul(headXform, y180Xform);
var postXform = Xform.mul(avatarXform, Xform.mul(y180Xform, modelOffsetInvXform.inv()));
// solve for the offset that will put the eyes at the hmd position & orientation.
var hipsOffsetXform = Xform.mul(postXform.inv(), Xform.mul(hmdXform, preXform.inv()));
// now combine it with the default hips transform
var hipsXform = Xform.mul(hipsOffsetXform, defaultHipsXform);
MyAvatar.setJointRotation("Hips", hipsXform.rot);
MyAvatar.setJointTranslation("Hips", hipsXform.pos);
}
};
var neuronAvatar = new NeuronAvatar();
function updateCallback(deltaTime) {
neuronAvatar.update(deltaTime);
}

View file

@ -9,8 +9,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var whichHand = Controller.Standard.RightHand;
var whichTrigger = Controller.Standard.RT;
var DEBUGGING = false;
Math.clamp=function(a,b,c) {
@ -24,80 +22,86 @@ function length(posA, posB) {
return length;
}
var EXPECTED_CHANGE = 50;
var lastPos = Controller.getReticlePosition();
function moveReticleAbsolute(x, y) {
var globalPos = Controller.getReticlePosition();
var dX = x - globalPos.x;
var dY = y - globalPos.y;
// some debugging to see if position is jumping around on us...
var distanceSinceLastMove = length(lastPos, globalPos);
if (distanceSinceLastMove > EXPECTED_CHANGE) {
print("------------------ distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------");
}
if (Math.abs(dX) > EXPECTED_CHANGE) {
print("surpressing unexpectedly large change dX:" + dX + "----------------------------");
}
if (Math.abs(dY) > EXPECTED_CHANGE) {
print("surpressing unexpectedly large change dY:" + dY + "----------------------------");
}
globalPos.x = x;
globalPos.y = y;
Controller.setReticlePosition(globalPos);
lastPos = globalPos;
}
var MAPPING_NAME = "com.highfidelity.testing.reticleWithHandRotation";
var mapping = Controller.newMapping(MAPPING_NAME);
mapping.from(whichTrigger).peek().constrainToInteger().to(Controller.Actions.ReticleClick);
mapping.from(whichHand).peek().to(function(pose) {
mapping.from(Controller.Standard.LT).peek().constrainToInteger().to(Controller.Actions.ReticleClick);
mapping.from(Controller.Standard.RT).peek().constrainToInteger().to(Controller.Actions.ReticleClick);
mapping.enable();
function debugPrint(message) {
if (DEBUGGING) {
print(message);
}
}
var leftRightBias = 0.0;
var filteredRotatedLeft = Vec3.UNIT_NEG_Y;
var filteredRotatedRight = Vec3.UNIT_NEG_Y;
Script.update.connect(function(deltaTime) {
var poseRight = Controller.getPoseValue(Controller.Standard.RightHand);
var poseLeft = Controller.getPoseValue(Controller.Standard.LeftHand);
// NOTE: hack for now
var screenSizeX = 1920;
var screenSizeY = 1080;
var rotated = Vec3.multiplyQbyV(pose.rotation, Vec3.UNIT_NEG_Y); //
var rotatedRight = Vec3.multiplyQbyV(poseRight.rotation, Vec3.UNIT_NEG_Y);
var rotatedLeft = Vec3.multiplyQbyV(poseLeft.rotation, Vec3.UNIT_NEG_Y);
lastRotatedRight = rotatedRight;
// Decide which hand should be controlling the pointer
// by comparing which one is moving more, and by
// tending to stay with the one moving more.
var BIAS_ADJUST_RATE = 0.5;
var BIAS_ADJUST_DEADZONE = 0.05;
leftRightBias += (Vec3.length(poseRight.angularVelocity) - Vec3.length(poseLeft.angularVelocity)) * BIAS_ADJUST_RATE;
if (leftRightBias < BIAS_ADJUST_DEADZONE) {
leftRightBias = 0.0;
} else if (leftRightBias > (1.0 - BIAS_ADJUST_DEADZONE)) {
leftRightBias = 1.0;
}
// Velocity filter the hand rotation used to position reticle so that it is easier to target small things with the hand controllers
var VELOCITY_FILTER_GAIN = 1.0;
filteredRotatedLeft = Vec3.mix(filteredRotatedLeft, rotatedLeft, Math.clamp(Vec3.length(poseLeft.angularVelocity) * VELOCITY_FILTER_GAIN, 0.0, 1.0));
filteredRotatedRight = Vec3.mix(filteredRotatedRight, rotatedRight, Math.clamp(Vec3.length(poseRight.angularVelocity) * VELOCITY_FILTER_GAIN, 0.0, 1.0));
var rotated = Vec3.mix(filteredRotatedLeft, filteredRotatedRight, leftRightBias);
var absolutePitch = rotated.y; // from 1 down to -1 up ... but note: if you rotate down "too far" it starts to go up again...
var absoluteYaw = -rotated.x; // from -1 left to 1 right
if (DEBUGGING) {
print("absolutePitch:" + absolutePitch);
print("absoluteYaw:" + absoluteYaw);
Vec3.print("rotated:", rotated);
}
var ROTATION_BOUND = 0.6;
var clampYaw = Math.clamp(absoluteYaw, -ROTATION_BOUND, ROTATION_BOUND);
var clampPitch = Math.clamp(absolutePitch, -ROTATION_BOUND, ROTATION_BOUND);
if (DEBUGGING) {
print("clampYaw:" + clampYaw);
print("clampPitch:" + clampPitch);
}
// using only from -ROTATION_BOUND to ROTATION_BOUND
var xRatio = (clampYaw + ROTATION_BOUND) / (2 * ROTATION_BOUND);
var yRatio = (clampPitch + ROTATION_BOUND) / (2 * ROTATION_BOUND);
if (DEBUGGING) {
print("xRatio:" + xRatio);
print("yRatio:" + yRatio);
}
var x = screenSizeX * xRatio;
var y = screenSizeY * yRatio;
if (DEBUGGING) {
print("position x:" + x + " y:" + y);
}
if (!(xRatio == 0.5 && yRatio == 0)) {
// don't move the reticle with the hand controllers unless the controllers are actually being moved
var MINIMUM_CONTROLLER_ANGULAR_VELOCITY = 0.0001;
var angularVelocityMagnitude = Vec3.length(poseLeft.angularVelocity) * (1.0 - leftRightBias) + Vec3.length(poseRight.angularVelocity) * leftRightBias;
if (!(xRatio == 0.5 && yRatio == 0) && (angularVelocityMagnitude > MINIMUM_CONTROLLER_ANGULAR_VELOCITY)) {
moveReticleAbsolute(x, y);
}
});
mapping.enable();
Script.scriptEnding.connect(function(){
mapping.disable();

View file

@ -0,0 +1,145 @@
//
// arcBall.js
// examples/arcBall
//
// Created by Eric Levin on 12/17/15.
// Copyright 2015 High Fidelity, Inc.
//
// This script creats a particle light ball which makes particle trails as you move it.
//
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/utils.js");
var scriptURL = Script.resolvePath("arcBallEntityScript.js?v1" + Math.random());
ArcBall = function(spawnPosition) {
var colorPalette = [{
red: 25,
green: 20,
blue: 162
}];
var containerBall = Entities.addEntity({
type: "Sphere",
name: "Arc Ball",
script: scriptURL,
position: Vec3.sum(spawnPosition, {
x: 0,
y: .7,
z: 0
}),
dimensions: {
x: .05,
y: .05,
z: .05
},
color: {
red: 100,
green: 10,
blue: 150
},
ignoreForCollisions: true,
damping: 0.8,
collisionsWillMove: true,
userData: JSON.stringify({
grabbableKey: {
spatialKey: {
// relativePosition: {
// x: 0,
// y: -0.5,
// z: 0.0
// },
},
// invertSolidWhileHeld: true
}
})
});
var light = Entities.addEntity({
type: 'Light',
name: "ballLight",
parentID: containerBall,
dimensions: {
x: 30,
y: 30,
z: 30
},
color: colorPalette[randInt(0, colorPalette.length)],
intensity: 5
});
var arcBall = Entities.addEntity({
type: "ParticleEffect",
parentID: containerBall,
isEmitting: true,
name: "Arc Ball Particle Effect",
colorStart: {
red: 200,
green: 20,
blue: 40
},
color: {
red: 200,
green: 200,
blue: 255
},
colorFinish: {
red: 25,
green: 20,
blue: 255
},
maxParticles: 100000,
lifespan: 2,
emitRate: 400,
emitSpeed: .1,
lifetime: -1,
speedSpread: 0.0,
emitDimensions: {
x: 0,
y: 0,
z: 0
},
polarStart: 0,
polarFinish: Math.PI,
azimuthStart: -Math.PI,
azimuthFinish: Math.PI,
emitAcceleration: {
x: 0,
y: 0,
z: 0
},
accelerationSpread: {
x: .00,
y: .00,
z: .00
},
particleRadius: 0.02,
radiusSpread: 0,
radiusStart: 0.03,
radiusFinish: 0.0003,
alpha: 0,
alphaSpread: .5,
alphaStart: 0,
alphaFinish: 0.5,
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
emitterShouldTrail: true
})
function cleanup() {
Entities.deleteEntity(arcBall);
Entities.deleteEntity(containerBall);
Entities.deleteEntity(light);
}
this.cleanup = cleanup;
}

View file

@ -0,0 +1,155 @@
// arcBallEntityScript.js
//
// Script Type: Entity
// Created by Eric Levin on 12/17/15.
// Copyright 2015 High Fidelity, Inc.
//
// This entity script handles the logic for the arcBall rave toy
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
Script.include("../../libraries/utils.js");
var _this;
var ArcBall = function() {
_this = this;
this.colorPalette = [{
red: 25,
green: 20,
blue: 162
}, {
red: 200,
green: 10,
blue: 10
}];
this.searchRadius = 10;
};
ArcBall.prototype = {
isGrabbed: false,
startDistantGrab: function() {
this.searchForNearbyArcBalls();
},
startNearGrab: function() {
this.searchForNearbyArcBalls();
},
searchForNearbyArcBalls: function() {
//Search for nearby balls and create an arc to it if one is found
var position = Entities.getEntityProperties(this.entityID, "position").position
var entities = Entities.findEntities(position, this.searchRadius);
entities.forEach(function(entity) {
var props = Entities.getEntityProperties(entity, ["position", "name"]);
if (props.name === "Arc Ball" && JSON.stringify(_this.entityID) !== JSON.stringify(entity)) {
_this.target = entity;
_this.createBeam(position, props.position);
}
});
},
createBeam: function(startPosition, endPosition) {
// Creates particle arc from start position to end position
var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation;
var sourceToTargetVec = Vec3.subtract(endPosition, startPosition);
var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec);
emitOrientation = Quat.multiply(Quat.inverse(rotation), emitOrientation);
var color = this.colorPalette[randInt(0, this.colorPalette.length)];
var props = {
type: "ParticleEffect",
name: "Particle Arc",
parentID: this.entityID,
parentJointIndex: -1,
// position: startPosition,
isEmitting: true,
colorStart: color,
color: {
red: 200,
green: 200,
blue: 255
},
colorFinish: color,
maxParticles: 100000,
lifespan: 1,
emitRate: 1000,
emitOrientation: emitOrientation,
emitSpeed: 1,
speedSpread: 0.02,
emitDimensions: {
x: .01,
y: .01,
z: .01
},
polarStart: 0,
polarFinish: 0,
azimuthStart: 0.02,
azimuthFinish: .01,
emitAcceleration: {
x: 0,
y: 0,
z: 0
},
accelerationSpread: {
x: 0,
y: 0,
z: 0
},
radiusStart: 0.01,
radiusFinish: 0.005,
radiusSpread: 0.005,
alpha: 0.5,
alphaSpread: 0.1,
alphaStart: 0.5,
alphaFinish: 0.5,
textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png",
emitterShouldTrail: true
}
this.particleArc = Entities.addEntity(props);
},
updateBeam: function() {
if(!this.target) {
return;
}
var startPosition = Entities.getEntityProperties(this.entityID, "position").position;
var targetPosition = Entities.getEntityProperties(this.target, "position").position;
var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation;
var sourceToTargetVec = Vec3.subtract(targetPosition, startPosition);
var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec);
Entities.editEntity(this.particleArc, {
emitOrientation: emitOrientation
});
},
continueNearGrab: function() {
this.updateBeam();
},
continueDistantGrab: function() {
this.updateBeam();
},
releaseGrab: function() {
Entities.editEntity(this.particleArc, {
isEmitting: false
});
this.target = null;
},
unload: function() {
if (this.particleArc) {
Entities.deleteEntity(this.particleArc);
}
},
preload: function(entityID) {
this.entityID = entityID;
},
};
return new ArcBall();
});

View file

@ -0,0 +1,91 @@
//
// flowArtsHutSpawner.js
// examples/flowArts
//
// Created by Eric Levin on 12/17/15.
// Copyright 2015 High Fidelity, Inc.
//
// This script creates a special flow arts hut with a bunch of flow art toys people can go in and play with
//
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/utils.js");
Script.include("lightBall/lightBall.js");
Script.include("raveStick/raveStick.js");
Script.include("lightSaber/lightSaber.js");
Script.include("arcBall/arcBall.js");
var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Camera.getOrientation())));
basePosition.y = MyAvatar.position.y + 1;
// RAVE ITEMS
// var lightBall = new LightBall(basePosition);
var arcBall = new ArcBall(basePosition);
var arcBall2 = new ArcBall(Vec3.sum(basePosition, {x: -1, y: 0, z: 0}));
var raveStick = new RaveStick(Vec3.sum(basePosition, {x: 1, y: 0.5, z: 1}));
var lightSaber = new LightSaber(Vec3.sum(basePosition, {x: 3, y: 0.5, z: 1}));
var modelURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/RaveRoom.fbx";
var roomDimensions = {x: 30.58, y: 15.29, z: 30.58};
var raveRoom = Entities.addEntity({
type: "Model",
name: "Rave Hut Room",
modelURL: modelURL,
position: basePosition,
dimensions:roomDimensions,
visible: true
});
var floor = Entities.addEntity({
type: "Box",
name: "Rave Floor",
position: Vec3.sum(basePosition, {x: 0, y: -1.2, z: 0}),
dimensions: {x: roomDimensions.x, y: 0.6, z: roomDimensions.z},
color: {red: 50, green: 10, blue: 100},
shapeType: 'box'
});
var lightZone = Entities.addEntity({
type: "Zone",
name: "Rave Hut Zone",
shapeType: 'box',
keyLightIntensity: 0.4,
keyLightColor: {
red: 50,
green: 0,
blue: 50
},
keyLightAmbientIntensity: .2,
position: MyAvatar.position,
dimensions: {
x: 100,
y: 100,
z: 100
}
});
function cleanup() {
Entities.deleteEntity(raveRoom);
Entities.deleteEntity(lightZone);
Entities.deleteEntity(floor);
// lightBall.cleanup();
arcBall.cleanup();
arcBall2.cleanup();
raveStick.cleanup();
lightSaber.cleanup();
}
Script.scriptEnding.connect(cleanup);

View file

@ -0,0 +1,145 @@
//
// LightBall.js
// examples/lightBall
//
// Created by Eric Levin on 12/17/15.
// Copyright 2014 High Fidelity, Inc.
//
// This script creats a particle light ball which makes particle trails as you move it.
//
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/utils.js");
LightBall = function(spawnPosition) {
var colorPalette = [{
red: 25,
green: 20,
blue: 162
}];
var containerBall = Entities.addEntity({
type: "Sphere",
name: "containerBall",
position: Vec3.sum(spawnPosition, {
x: 0,
y: 0.5,
z: 0
}),
dimensions: {
x: 0.1,
y: 0.1,
z: 0.1
},
color: {
red: 15,
green: 10,
blue: 150
},
collisionsWillMove: true,
// gravity: {
// x: 0,
// y: -0.5,
// z: 0
// },
userData: JSON.stringify({
grabbableKey: {
spatialKey: {
relativePosition: {
x: 0,
y: 0.1,
z: 0
}
},
invertSolidWhileHeld: true
}
})
});
var light = Entities.addEntity({
type: 'Light',
name: "ballLight",
parentID: containerBall,
dimensions: {
x: 30,
y: 30,
z: 30
},
color: colorPalette[randInt(0, colorPalette.length)],
intensity: 5
});
var lightBall = Entities.addEntity({
type: "ParticleEffect",
parentID: containerBall,
isEmitting: true,
name: "particleBall",
colorStart: {
red: 200,
green: 20,
blue: 40
},
color: {
red: 200,
green: 200,
blue: 255
},
colorFinish: {
red: 25,
green: 20,
blue: 255
},
maxParticles: 100000,
lifespan: 2,
emitRate: 10000,
emitSpeed: 0.1,
lifetime: -1,
speedSpread: 0.0,
emitDimensions: {
x: 0,
y: 0,
z: 0
},
polarStart: 0,
polarFinish: Math.PI,
azimuthStart: -Math.PI,
azimuthFinish: Math.PI,
emitAcceleration: {
x: 0,
y: 0,
z: 0
},
accelerationSpread: {
x: 0.00,
y: 0.00,
z: 0.00
},
particleRadius: 0.02,
radiusSpread: 0,
radiusStart: 0.03,
radiusFinish: 0.0003,
alpha: 0,
alphaSpread: 0.5,
alphaStart: 0,
alphaFinish: 0.5,
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
emitterShouldTrail: true
})
function cleanup() {
Entities.deleteEntity(lightBall);
Entities.deleteEntity(containerBall);
Entities.deleteEntity(light);
}
this.cleanup = cleanup;
}

View file

@ -0,0 +1,67 @@
//
// LightSaber.js
// examples
//
// Created by Eric Levin on 12/17/15.
// Copyright 2015 High Fidelity, Inc.
//
// This script creates a lightsaber which activates on grab
//
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/utils.js");
var modelURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/lightSaber.fbx";
var scriptURL = Script.resolvePath("lightSaberEntityScript.js");
LightSaber = function(spawnPosition) {
var saberHandle = Entities.addEntity({
type: "Model",
name: "LightSaber Handle",
modelURL: modelURL,
position: spawnPosition,
shapeType: 'box',
collisionsWillMove: true,
script: scriptURL,
dimensions: {
x: 0.06,
y: 0.06,
z: 0.31
},
userData: JSON.stringify({
grabbableKey: {
spatialKey: {
relativePosition: {
x: 0,
y: 0,
z: -0.1
},
relativeRotation: Quat.fromPitchYawRollDegrees(180, 90, 0)
},
invertSolidWhileHeld: true
}
})
});
var light = Entities.addEntity({
type: 'Light',
name: "raveLight",
parentID: saberHandle,
dimensions: {
x: 30,
y: 30,
z: 30
},
color: {red: 200, green: 10, blue: 200},
intensity: 5
});
function cleanup() {
Entities.deleteEntity(saberHandle);
}
this.cleanup = cleanup;
}

View file

@ -0,0 +1,116 @@
// lightSaberEntityScript.js
//
// Script Type: Entity
// Created by Eric Levin on 12/17/15.
// Copyright 2015 High Fidelity, Inc.
//
// This entity script creates the logic for displaying the lightsaber beam.
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
Script.include("../../libraries/utils.js");
var _this;
// this is the "constructor" for the entity as a JS object we don't do much here
var LightSaber = function() {
_this = this;
this.colorPalette = [{
red: 0,
green: 200,
blue: 40
}, {
red: 200,
green: 10,
blue: 40
}];
};
LightSaber.prototype = {
isGrabbed: false,
startNearGrab: function() {
Entities.editEntity(this.beam, {
isEmitting: true,
visible: true
});
},
releaseGrab: function() {
Entities.editEntity(this.beam, {
visible: false,
isEmitting: false
});
},
preload: function(entityID) {
this.entityID = entityID;
this.createBeam();
},
unload: function() {
Entities.deleteEntity(this.beam);
},
createBeam: function() {
this.props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]);
var forwardVec = Quat.getFront(Quat.multiply(this.props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0)));
var forwardQuat = Quat.rotationBetween(Vec3.UNIT_Z, forwardVec);
var position = this.props.position;
var color = this.colorPalette[randInt(0, this.colorPalette.length)];
var props = {
type: "ParticleEffect",
name: "LightSaber Beam",
position: position,
parentID: this.entityID,
isEmitting: false,
colorStart: color,
color: {
red: 200,
green: 200,
blue: 255
},
colorFinish: color,
maxParticles: 100000,
lifespan: 2,
emitRate: 1000,
emitOrientation: forwardQuat,
emitSpeed: 0.7,
speedSpread: 0.0,
emitDimensions: {
x: 0,
y: 0,
z: 0
},
polarStart: 0,
polarFinish: 0,
azimuthStart: 0.1,
azimuthFinish: 0.01,
emitAcceleration: {
x: 0,
y: 0,
z: 0
},
accelerationSpread: {
x: .00,
y: .00,
z: .00
},
radiusStart: 0.03,
adiusFinish: 0.025,
alpha: 0.7,
alphaSpread: 0.1,
alphaStart: 0.5,
alphaFinish: 0.5,
textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png",
emitterShouldTrail: false
}
this.beam = Entities.addEntity(props);
}
};
// entity scripts always need to return a newly constructed object of our type
return new LightSaber();
});

View file

@ -0,0 +1,191 @@
//
// lightTrails.js
// examples
//
// Created by Eric Levin on 5/14/15.
// Copyright 2015 High Fidelity, Inc.
//
// This script creates light trails as you move your hydra hands
//
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../libraries/utils.js");
var eraseTrail = true;
var ugLSD = 25;
// var eraseTrail = false;
var LEFT = 0;
var RIGHT = 1;
var MAX_POINTS_PER_LINE = 50;
var LIFETIME = 6000;
var DRAWING_DEPTH = 0.8;
var LINE_DIMENSIONS = 100;
var MIN_POINT_DISTANCE = 0.02;
var colorPalette = [{
red: 250,
green: 137,
blue: 162
}, {
red: 204,
green: 244,
blue: 249
}, {
red: 146,
green: 206,
blue: 116
}, {
red: 240,
green: 87,
blue: 129
}];
var STROKE_WIDTH = 0.04;
function controller(side, triggerAction) {
this.triggerHeld = false;
this.triggerThreshold = 0.9;
this.side = side;
this.triggerAction = triggerAction;
var texture = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/trails.png";
this.light = Entities.addEntity({
type: 'Light',
position: MyAvatar.position,
dimensions: {
x: 30,
y: 30,
z: 30
},
color: colorPalette[randInt(0, colorPalette.length)],
intensity: 5
});
this.trail = Entities.addEntity({
type: "PolyLine",
dimensions: {
x: LINE_DIMENSIONS,
y: LINE_DIMENSIONS,
z: LINE_DIMENSIONS
},
color: {red: 255, green: 255, blue: 255},
textures: texture,
lifetime: LIFETIME
});
this.points = [];
this.normals = [];
this.strokeWidths = [];
var self = this;
this.trailEraseInterval = Script.setInterval(function() {
if (self.points.length > 0 && eraseTrail) {
self.points.shift();
self.normals.shift();
self.strokeWidths.shift();
Entities.editEntity(self.trail, {
linePoints: self.points,
strokeWidths: self.strokeWidths,
normals: self.normals
});
}
}, ugLSD);
this.setTrailPosition = function(position) {
this.trailPosition = position;
Entities.editEntity(this.trail, {
position: this.trailPosition
});
}
this.update = function(deltaTime) {
this.updateControllerState();
var newTrailPosOffset = Vec3.multiply(Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)), DRAWING_DEPTH);
var newTrailPos = Vec3.sum(this.palmPosition, newTrailPosOffset);
Entities.editEntity(this.light, {
position: newTrailPos
});
if (!this.drawing) {
this.setTrailPosition(newTrailPos);
this.drawing = true;
}
if (this.drawing) {
var localPoint = Vec3.subtract(newTrailPos, this.trailPosition);
if (Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) {
//Need a minimum distance to avoid binormal NANs
return;
}
if (this.points.length === MAX_POINTS_PER_LINE) {
this.points.shift();
this.normals.shift();
this.strokeWidths.shift();
}
this.points.push(localPoint);
var normal = computeNormal(newTrailPos, Camera.getPosition());
this.normals.push(normal);
this.strokeWidths.push(STROKE_WIDTH + Math.random() * 0.01);
Entities.editEntity(this.trail, {
linePoints: this.points,
normals: this.normals,
strokeWidths: this.strokeWidths,
});
}
}
this.updateControllerState = function() {
this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation;
this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation;
this.triggerValue = Controller.getActionValue(this.triggerAction);
}
this.cleanup = function() {
Entities.deleteEntity(this.trail);
Entities.deleteEntity(this.light);
Script.clearInterval(this.trailEraseInterval);
}
}
function computeNormal(p1, p2) {
return Vec3.normalize(Vec3.subtract(p2, p1));
}
function update(deltaTime) {
leftController.update(deltaTime);
rightController.update(deltaTime);
}
function scriptEnding() {
leftController.cleanup();
rightController.cleanup();
}
function vectorIsZero(v) {
return v.x === 0 && v.y === 0 && v.z === 0;
}
var rightController = new controller(RIGHT, Controller.findAction("RIGHT_HAND_CLICK"));
var leftController = new controller(LEFT, Controller.findAction("LEFT_HAND_CLICK"));
Script.update.connect(update);
Script.scriptEnding.connect(scriptEnding);
function map(value, min1, max1, min2, max2) {
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
}

View file

@ -0,0 +1,95 @@
//
// RaveStick.js
// examples/flowArats/raveStick
//
// Created by Eric Levin on 12/17/15.
// Copyright 2015 High Fidelity, Inc.
//
// This script creates a rave stick which makes pretty light trails as you paint
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/utils.js");
var modelURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/raveStick.fbx";
var scriptURL = Script.resolvePath("raveStickEntityScript.js");
RaveStick = function(spawnPosition) {
var colorPalette = [{
red: 0,
green: 200,
blue: 40
}, {
red: 200,
green: 10,
blue: 40
}];
var stick = Entities.addEntity({
type: "Model",
name: "raveStick",
modelURL: modelURL,
position: spawnPosition,
shapeType: 'box',
collisionsWillMove: true,
script: scriptURL,
dimensions: {
x: 0.06,
y: 0.06,
z: 0.31
},
userData: JSON.stringify({
grabbableKey: {
spatialKey: {
rightRelativePosition: {
x: 0.02,
y: 0,
z: 0
},
leftRelativePosition: {
x: -0.02,
y: 0,
z: 0
},
relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0)
},
invertSolidWhileHeld: true
}
})
});
var light = Entities.addEntity({
type: 'Light',
name: "raveLight",
parentID: stick,
dimensions: {
x: 30,
y: 30,
z: 30
},
color: colorPalette[randInt(0, colorPalette.length)],
intensity: 5
});
var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0)
var forwardVec = Quat.getFront(Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0)));
forwardVec = Vec3.normalize(forwardVec);
var forwardQuat = orientationOf(forwardVec);
var position = Vec3.sum(spawnPosition, Vec3.multiply(Quat.getFront(rotation), 0.1));
position.z += 0.1;
position.x += -0.035;
var color = {
red: 0,
green: 200,
blue: 40
};
function cleanup() {
Entities.deleteEntity(stick);
Entities.deleteEntity(light);
}
this.cleanup = cleanup;
}

View file

@ -0,0 +1,141 @@
// raveStickEntityScript.js
//
// Script Type: Entity
// Created by Eric Levin on 12/16/15.
// Copyright 2015 High Fidelity, Inc.
//
// This entity script create light trails on a given object as it moves.
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
Script.include("../../libraries/utils.js");
var _this;
var LIFETIME = 6000;
var DRAWING_DEPTH = 0.8;
var LINE_DIMENSIONS = 100;
var MAX_POINTS_PER_LINE = 50;
var MIN_POINT_DISTANCE = 0.02;
var STROKE_WIDTH = 0.05
var ugLSD = 35;
var RaveStick = function() {
_this = this;
this.colorPalette = [{
red: 0,
green: 200,
blue: 40
}, {
red: 200,
green: 10,
blue: 40
}];
var texture = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/trails.png";
this.trail = Entities.addEntity({
type: "PolyLine",
dimensions: {
x: LINE_DIMENSIONS,
y: LINE_DIMENSIONS,
z: LINE_DIMENSIONS
},
color: {
red: 255,
green: 255,
blue: 255
},
textures: texture,
lifetime: LIFETIME
});
this.points = [];
this.normals = [];
this.strokeWidths = [];
};
RaveStick.prototype = {
isGrabbed: false,
startNearGrab: function() {
this.trailBasePosition = Entities.getEntityProperties(this.entityID, "position").position;
Entities.editEntity(this.trail, {
position: this.trailBasePosition
});
this.points = [];
this.normals = [];
this.strokeWidths = [];
this.setupEraseInterval();
},
continueNearGrab: function() {
var props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]);
var forwardVec = Quat.getFront(Quat.multiply(props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0)));
forwardVec = Vec3.normalize(forwardVec);
var forwardQuat = orientationOf(forwardVec);
var position = Vec3.sum(props.position, Vec3.multiply(Quat.getFront(props.rotation), 0.04));
var localPoint = Vec3.subtract(position, this.trailBasePosition);
if (this.points.length >= 1 && Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) {
//Need a minimum distance to avoid binormal NANs
return;
}
if (this.points.length === MAX_POINTS_PER_LINE) {
this.points.shift();
this.normals.shift();
this.strokeWidths.shift();
}
this.points.push(localPoint);
var normal = Quat.getUp(props.rotation);
this.normals.push(normal);
this.strokeWidths.push(STROKE_WIDTH);
Entities.editEntity(this.trail, {
linePoints: this.points,
normals: this.normals,
strokeWidths: this.strokeWidths
});
},
setupEraseInterval: function() {
this.trailEraseInterval = Script.setInterval(function() {
if (_this.points.length > 0) {
_this.points.shift();
_this.normals.shift();
_this.strokeWidths.shift();
Entities.editEntity(_this.trail, {
linePoints: _this.points,
strokeWidths: _this.strokeWidths,
normals: _this.normals
});
}
}, ugLSD);
},
releaseGrab: function() {
if(!this.trailEraseInterval) {
return;
}
Script.setTimeout(function() {
Script.clearInterval(_this.trailEraseInterval);
_this.trailEraseInterval = null;
}, 3000);
},
preload: function(entityID) {
this.entityID = entityID;
},
unload: function() {
Entities.deleteEntity(this.beam);
Entities.deleteEntity(this.trail);
if (this.trailEraseInterval) {
Script.clearInterval(this.trailEraseInterval);
}
}
};
return new RaveStick();
function computeNormal(p1, p2) {
return Vec3.normalize(Vec3.subtract(p2, p1));
}
});

File diff suppressed because it is too large Load diff

View file

@ -53,7 +53,9 @@ LightOverlayManager = function() {
if (visible != isVisible) {
visible = isVisible;
for (var id in entityOverlays) {
Overlays.editOverlay(entityOverlays[id], { visible: visible });
Overlays.editOverlay(entityOverlays[id], {
visible: visible
});
}
}
};
@ -61,8 +63,7 @@ LightOverlayManager = function() {
// Allocate or get an unused overlay
function getOverlay() {
if (unusedOverlays.length == 0) {
var overlay = Overlays.addOverlay("image3d", {
});
var overlay = Overlays.addOverlay("image3d", {});
allOverlays.push(overlay);
} else {
var overlay = unusedOverlays.pop();
@ -72,7 +73,9 @@ LightOverlayManager = function() {
function releaseOverlay(overlay) {
unusedOverlays.push(overlay);
Overlays.editOverlay(overlay, { visible: false });
Overlays.editOverlay(overlay, {
visible: false
});
}
function addEntity(entityID) {
@ -88,7 +91,11 @@ LightOverlayManager = function() {
visible: visible,
alpha: 0.9,
scale: 0.5,
color: { red: 255, green: 255, blue: 255 }
color: {
red: 255,
green: 255,
blue: 255
}
});
}
}
@ -123,4 +130,4 @@ LightOverlayManager = function() {
Overlays.deleteOverlay(allOverlays[i]);
}
});
};
};

View file

@ -271,3 +271,29 @@ hexToRgb = function(hex) {
} : null;
}
calculateHandSizeRatio = function() {
// Get the ratio of the current avatar's hand to Owen's hand
var standardCenterHandPoint = 0.11288;
var jointNames = MyAvatar.getJointNames();
//get distance from handJoint up to leftHandIndex3 as a proxy for center of hand
var wristToFingertipDistance = 0;;
for (var i = 0; i < jointNames.length; i++) {
var jointName = jointNames[i];
print(jointName)
if (jointName.indexOf("LeftHandIndex") !== -1) {
// translations are relative to parent joint, so simply add them together
// joints face down the y-axis
var translation = MyAvatar.getDefaultJointTranslation(i).y;
wristToFingertipDistance += translation;
}
}
// Right now units are in cm, so convert to meters
wristToFingertipDistance /= 100;
var centerHandPoint = wristToFingertipDistance/2;
// Compare against standard hand (Owen)
var handSizeRatio = centerHandPoint/standardCenterHandPoint;
return handSizeRatio;
}

View file

@ -0,0 +1,29 @@
This PR demonstrates one way in-world editing of objects might work.
Running this script will show light overlay icons in-world. Enter edit mode by running your distance beam through a light overlay. Exit using the red X.
When you distant grab the sliders, you can move them along their axis to change their values. You may also rotate / move the block to which the spotlight is attached.
To test: https://rawgit.com/imgntn/hifi/light_mod/examples/lights/lightLoader.js
To reset, I recommend stopping all scripts then re-loading lightLoader.js
When you run the lightLoader.js script, several scripts will be loaded:
- handControllerGrab.js (will not impart velocity when you move the parent or a slider, will not move sliders with head movement,will constrain movement for a slider to a given axis start and end, will support blacklisting of entities for raypicking during search for objects)
- lightModifier.js (listens for message to create sliders for a given light. will start with slider set to the light's initial properties)
- lightModifierTestScene.js (creates a light)
- slider.js (attached to each slider entity)
- lightParent.js (attached to a 3d model of a light, to which a light is parented, so you can move it around. or keep the current parent if a light already has a parent)
- visiblePanel.js (the transparent panel)
- closeButton.js (for closing the ui)
- ../libraries/lightOverlayManager.js (shows 2d overlays for lights in the world)
- ../libraries/entitySelectionTool.js (visualizes volume of the lights)
Current sliders are (top to bottom):
red
green
blue
intensity
cutoff
exponent
![capture](https://cloud.githubusercontent.com/assets/843228/11910139/afaaf1ae-a5a5-11e5-8b66-0eb3fc6976df.PNG)

View file

@ -0,0 +1,36 @@
//
// closeButton.js
//
// Created by James Pollack @imgntn on 12/15/2015
// Copyright 2015 High Fidelity, Inc.
//
// Entity script that closes sliders when interacted with.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
function CloseButton() {
return this;
}
CloseButton.prototype = {
preload: function(entityID) {
this.entityID = entityID;
var entityProperties = Entities.getEntityProperties(this.entityID, "userData");
this.initialProperties = entityProperties
this.userData = JSON.parse(entityProperties.userData);
},
startNearGrab: function() {
},
startFarTrigger: function() {
Messages.sendMessage('Hifi-Light-Modifier-Cleanup', 'callCleanup')
}
};
return new CloseButton();
});

View file

@ -0,0 +1,20 @@
//
// lightLoader.js
//
// Created by James Pollack @imgntn on 12/15/2015
// Copyright 2015 High Fidelity, Inc.
//
// Loads a test scene showing sliders that you can grab and move to change entity properties.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var grabScript = Script.resolvePath('../controllers/handControllerGrab.js?' + Math.random(0 - 100));
Script.load(grabScript);
var lightModifier = Script.resolvePath('lightModifier.js?' + Math.random(0 - 100));
Script.load(lightModifier);
Script.setTimeout(function() {
var lightModifierTestScene = Script.resolvePath('lightModifierTestScene.js?' + Math.random(0 - 100));
Script.load(lightModifierTestScene);
}, 750)

View file

@ -0,0 +1,876 @@
//
// lightModifier.js
//
// Created by James Pollack @imgntn on 12/15/2015
// Copyright 2015 High Fidelity, Inc.
//
// Given a selected light, instantiate some entities that represent various values you can dynamically adjust by grabbing and moving.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
//some experimental options
var ONLY_I_CAN_EDIT = false;
var SLIDERS_SHOULD_STAY_WITH_AVATAR = false;
var VERTICAL_SLIDERS = false;
var SHOW_OVERLAYS = true;
var SHOW_LIGHT_VOLUME = true;
var USE_PARENTED_PANEL = true;
var VISIBLE_PANEL = true;
var USE_LABELS = true;
var LEFT_LABELS = false;
var RIGHT_LABELS = true;
var ROTATE_CLOSE_BUTTON = false;
//variables for managing overlays
var selectionDisplay;
var selectionManager;
var lightOverlayManager;
//for when we make a 3d model of a light a parent for the light
var PARENT_SCRIPT_URL = Script.resolvePath('lightParent.js?' + Math.random(0 - 100));
if (SHOW_OVERLAYS === true) {
Script.include('../libraries/gridTool.js');
Script.include('../libraries/entitySelectionTool.js?' + Math.random(0 - 100));
Script.include('../libraries/lightOverlayManager.js');
var grid = Grid();
gridTool = GridTool({
horizontalGrid: grid
});
gridTool.setVisible(false);
selectionDisplay = SelectionDisplay;
selectionManager = SelectionManager;
lightOverlayManager = new LightOverlayManager();
selectionManager.addEventListener(function() {
selectionDisplay.updateHandles();
lightOverlayManager.updatePositions();
});
lightOverlayManager.setVisible(true);
}
var DEFAULT_PARENT_ID = '{00000000-0000-0000-0000-000000000000}'
var AXIS_SCALE = 1;
var COLOR_MAX = 255;
var INTENSITY_MAX = 0.05;
var CUTOFF_MAX = 360;
var EXPONENT_MAX = 1;
var SLIDER_SCRIPT_URL = Script.resolvePath('slider.js?' + Math.random(0, 100));
var LIGHT_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/source4_very_good.fbx';
var CLOSE_BUTTON_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/red_x.fbx';
var CLOSE_BUTTON_SCRIPT_URL = Script.resolvePath('closeButton.js?' + Math.random(0, 100));
var TRANSPARENT_PANEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/transparent_box_alpha_15.fbx';
var VISIBLE_PANEL_SCRIPT_URL = Script.resolvePath('visiblePanel.js?' + Math.random(0, 100));
var RED = {
red: 255,
green: 0,
blue: 0
};
var GREEN = {
red: 0,
green: 255,
blue: 0
};
var BLUE = {
red: 0,
green: 0,
blue: 255
};
var PURPLE = {
red: 255,
green: 0,
blue: 255
};
var WHITE = {
red: 255,
green: 255,
blue: 255
};
var ORANGE = {
red: 255,
green: 165,
blue: 0
}
var SLIDER_DIMENSIONS = {
x: 0.075,
y: 0.075,
z: 0.075
};
var CLOSE_BUTTON_DIMENSIONS = {
x: 0.1,
y: 0.025,
z: 0.1
}
var LIGHT_MODEL_DIMENSIONS = {
x: 0.58,
y: 1.21,
z: 0.57
}
var PER_ROW_OFFSET = {
x: 0,
y: -0.2,
z: 0
};
var sliders = [];
var slidersRef = {
'color_red': null,
'color_green': null,
'color_blue': null,
intensity: null,
cutoff: null,
exponent: null
};
var light = null;
var basePosition;
var avatarRotation;
function entitySlider(light, color, sliderType, displayText, row) {
this.light = light;
this.lightID = light.id.replace(/[{}]/g, "");
this.initialProperties = light.initialProperties;
this.color = color;
this.sliderType = sliderType;
this.displayText = displayText;
this.verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET);
this.avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0);
this.basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(this.avatarRot)));
this.basePosition.y += 1;
basePosition = this.basePosition;
avatarRot = this.avatarRot;
var message = {
lightID: this.lightID,
sliderType: this.sliderType,
sliderValue: null
};
if (this.sliderType === 'color_red') {
message.sliderValue = this.initialProperties.color.red
this.setValueFromMessage(message);
}
if (this.sliderType === 'color_green') {
message.sliderValue = this.initialProperties.color.green
this.setValueFromMessage(message);
}
if (this.sliderType === 'color_blue') {
message.sliderValue = this.initialProperties.color.blue
this.setValueFromMessage(message);
}
if (this.sliderType === 'intensity') {
message.sliderValue = this.initialProperties.intensity
this.setValueFromMessage(message);
}
if (this.sliderType === 'exponent') {
message.sliderValue = this.initialProperties.exponent
this.setValueFromMessage(message);
}
if (this.sliderType === 'cutoff') {
message.sliderValue = this.initialProperties.cutoff
this.setValueFromMessage(message);
}
this.setInitialSliderPositions();
this.createAxis();
this.createSliderIndicator();
if (USE_LABELS === true) {
this.createLabel()
}
return this;
}
//what's the ux for adjusting values? start with simple entities, try image overlays etc
entitySlider.prototype = {
createAxis: function() {
//start of line
var position;
var extension;
if (VERTICAL_SLIDERS == true) {
position = Vec3.sum(this.basePosition, Vec3.multiply(row, (Vec3.multiply(0.2, Quat.getRight(this.avatarRot)))));
//line starts on bottom and goes up
var upVector = Quat.getUp(this.avatarRot);
extension = Vec3.multiply(AXIS_SCALE, upVector);
} else {
position = Vec3.sum(this.basePosition, this.verticalOffset);
//line starts on left and goes to right
//set the end of the line to the right
var rightVector = Quat.getRight(this.avatarRot);
extension = Vec3.multiply(AXIS_SCALE, rightVector);
}
this.axisStart = position;
this.endOfAxis = Vec3.sum(position, extension);
this.createEndOfAxisEntity();
var properties = {
type: 'Line',
name: 'Hifi-Slider-Axis::' + this.sliderType,
color: this.color,
collisionsWillMove: false,
ignoreForCollisions: true,
dimensions: {
x: 3,
y: 3,
z: 3
},
position: position,
linePoints: [{
x: 0,
y: 0,
z: 0
}, extension],
lineWidth: 5,
};
this.axis = Entities.addEntity(properties);
},
createEndOfAxisEntity: function() {
//we use this to track the end of the axis while parented to a panel
var properties = {
name: 'Hifi-End-Of-Axis',
type: 'Box',
collisionsWillMove: false,
ignoreForCollisions: true,
dimensions: {
x: 0.01,
y: 0.01,
z: 0.01
},
color: {
red: 255,
green: 255,
blue: 255
},
position: this.endOfAxis,
parentID: this.axis,
visible: false
}
this.endOfAxisEntity = Entities.addEntity(this.endOfAxis);
},
createLabel: function() {
var LABEL_WIDTH = 0.25
var PER_LETTER_SPACING = 0.1;
var textWidth = this.displayText.length * PER_LETTER_SPACING;
var position;
if (LEFT_LABELS === true) {
var leftVector = Vec3.multiply(-1, Quat.getRight(this.avatarRot));
var extension = Vec3.multiply(textWidth, leftVector);
position = Vec3.sum(this.axisStart, extension);
}
if (RIGHT_LABELS === true) {
var rightVector = Quat.getRight(this.avatarRot);
var extension = Vec3.multiply(textWidth / 1.75, rightVector);
position = Vec3.sum(this.endOfAxis, extension);
}
var labelProperties = {
name: 'Hifi-Slider-Label-' + this.sliderType,
type: 'Text',
dimensions: {
x: textWidth,
y: 0.2,
z: 0.1
},
textColor: {
red: 255,
green: 255,
blue: 255
},
text: this.displayText,
lineHeight: 0.14,
backgroundColor: {
red: 0,
green: 0,
blue: 0
},
position: position,
rotation: this.avatarRot,
}
print('BEFORE CREATE LABEL' + JSON.stringify(labelProperties))
this.label = Entities.addEntity(labelProperties);
print('AFTER CREATE LABEL')
},
createSliderIndicator: function() {
var extensionVector;
var position;
if (VERTICAL_SLIDERS == true) {
position = Vec3.sum(this.basePosition, Vec3.multiply(row, (Vec3.multiply(0.2, Quat.getRight(this.avatarRot)))));
extensionVector = Quat.getUp(this.avatarRot);
} else {
position = Vec3.sum(this.basePosition, this.verticalOffset);
extensionVector = Quat.getRight(this.avatarRot);
}
var initialDistance;
if (this.sliderType === 'color_red') {
initialDistance = this.distanceRed;
}
if (this.sliderType === 'color_green') {
initialDistance = this.distanceGreen;
}
if (this.sliderType === 'color_blue') {
initialDistance = this.distanceBlue;
}
if (this.sliderType === 'intensity') {
initialDistance = this.distanceIntensity;
}
if (this.sliderType === 'cutoff') {
initialDistance = this.distanceCutoff;
}
if (this.sliderType === 'exponent') {
initialDistance = this.distanceExponent;
}
var extension = Vec3.multiply(initialDistance, extensionVector);
var sliderPosition = Vec3.sum(position, extension);
var properties = {
type: 'Sphere',
name: 'Hifi-Slider-' + this.sliderType,
dimensions: SLIDER_DIMENSIONS,
collisionsWillMove: true,
color: this.color,
position: sliderPosition,
script: SLIDER_SCRIPT_URL,
ignoreForCollisions: true,
userData: JSON.stringify({
lightModifierKey: {
lightID: this.lightID,
sliderType: this.sliderType,
axisStart: position,
axisEnd: this.endOfAxis,
},
handControllerKey: {
disableReleaseVelocity: true,
disableMoveWithHead: true,
disableNearGrab:true
}
}),
};
this.sliderIndicator = Entities.addEntity(properties);
},
setValueFromMessage: function(message) {
//message is not for our light
if (message.lightID !== this.lightID) {
// print('not our light')
return;
}
//message is not our type
if (message.sliderType !== this.sliderType) {
// print('not our slider type')
return
}
var lightProperties = Entities.getEntityProperties(this.lightID);
if (this.sliderType === 'color_red') {
Entities.editEntity(this.lightID, {
color: {
red: message.sliderValue,
green: lightProperties.color.green,
blue: lightProperties.color.blue
}
});
}
if (this.sliderType === 'color_green') {
Entities.editEntity(this.lightID, {
color: {
red: lightProperties.color.red,
green: message.sliderValue,
blue: lightProperties.color.blue
}
});
}
if (this.sliderType === 'color_blue') {
Entities.editEntity(this.lightID, {
color: {
red: lightProperties.color.red,
green: lightProperties.color.green,
blue: message.sliderValue,
}
});
}
if (this.sliderType === 'intensity') {
Entities.editEntity(this.lightID, {
intensity: message.sliderValue
});
}
if (this.sliderType === 'cutoff') {
Entities.editEntity(this.lightID, {
cutoff: message.sliderValue
});
}
if (this.sliderType === 'exponent') {
Entities.editEntity(this.lightID, {
exponent: message.sliderValue
});
}
},
setInitialSliderPositions: function() {
this.distanceRed = (this.initialProperties.color.red / COLOR_MAX) * AXIS_SCALE;
this.distanceGreen = (this.initialProperties.color.green / COLOR_MAX) * AXIS_SCALE;
this.distanceBlue = (this.initialProperties.color.blue / COLOR_MAX) * AXIS_SCALE;
this.distanceIntensity = (this.initialProperties.intensity / INTENSITY_MAX) * AXIS_SCALE;
this.distanceCutoff = (this.initialProperties.cutoff / CUTOFF_MAX) * AXIS_SCALE;
this.distanceExponent = (this.initialProperties.exponent / EXPONENT_MAX) * AXIS_SCALE;
}
};
var panel;
var visiblePanel;
function makeSliders(light) {
if (USE_PARENTED_PANEL === true) {
panel = createPanelEntity(MyAvatar.position);
}
if (light.type === 'spotlight') {
var USE_COLOR_SLIDER = true;
var USE_INTENSITY_SLIDER = true;
var USE_CUTOFF_SLIDER = true;
var USE_EXPONENT_SLIDER = true;
}
if (light.type === 'pointlight') {
var USE_COLOR_SLIDER = true;
var USE_INTENSITY_SLIDER = true;
var USE_CUTOFF_SLIDER = false;
var USE_EXPONENT_SLIDER = false;
}
if (USE_COLOR_SLIDER === true) {
slidersRef.color_red = new entitySlider(light, RED, 'color_red', 'Red', 1);
slidersRef.color_green = new entitySlider(light, GREEN, 'color_green', 'Green', 2);
slidersRef.color_blue = new entitySlider(light, BLUE, 'color_blue', 'Blue', 3);
sliders.push(slidersRef.color_red);
sliders.push(slidersRef.color_green);
sliders.push(slidersRef.color_blue);
}
if (USE_INTENSITY_SLIDER === true) {
slidersRef.intensity = new entitySlider(light, WHITE, 'intensity', 'Intensity', 4);
sliders.push(slidersRef.intensity);
}
if (USE_CUTOFF_SLIDER === true) {
slidersRef.cutoff = new entitySlider(light, PURPLE, 'cutoff', 'Cutoff', 5);
sliders.push(slidersRef.cutoff);
}
if (USE_EXPONENT_SLIDER === true) {
slidersRef.exponent = new entitySlider(light, ORANGE, 'exponent', 'Exponent', 6);
sliders.push(slidersRef.exponent);
}
createCloseButton(slidersRef.color_red.axisStart);
subscribeToSliderMessages();
if (USE_PARENTED_PANEL === true) {
parentEntitiesToPanel(panel);
}
if (SLIDERS_SHOULD_STAY_WITH_AVATAR === true) {
parentPanelToAvatar(panel);
}
if (VISIBLE_PANEL === true) {
visiblePanel = createVisiblePanel();
}
};
function parentPanelToAvatar(panel) {
//this is going to need some more work re: the sliders actually being grabbable. probably something to do with updating axis movement
Entities.editEntity(panel, {
parentID: MyAvatar.sessionUUID,
//actually figure out which one to parent it to -- probably a spine or something.
parentJointIndex: 1,
})
}
function parentEntitiesToPanel(panel) {
sliders.forEach(function(slider) {
Entities.editEntity(slider.axis, {
parentID: panel
})
Entities.editEntity(slider.sliderIndicator, {
parentID: panel
})
})
closeButtons.forEach(function(button) {
Entities.editEntity(button, {
parentID: panel
})
})
}
function createPanelEntity(position) {
print('CREATING PANEL at ' + JSON.stringify(position));
var panelProperties = {
name: 'Hifi-Slider-Panel',
type: 'Box',
dimensions: {
x: 0.1,
y: 0.1,
z: 0.1
},
visible: false,
collisionsWillMove: false,
ignoreForCollisions: true
}
var panel = Entities.addEntity(panelProperties);
return panel
}
function createVisiblePanel() {
var totalOffset = -PER_ROW_OFFSET.y * sliders.length;
var moveRight = Vec3.sum(basePosition, Vec3.multiply(AXIS_SCALE / 2, Quat.getRight(avatarRot)));
var moveDown = Vec3.sum(moveRight, Vec3.multiply((sliders.length + 1) / 2, PER_ROW_OFFSET))
var panelProperties = {
name: 'Hifi-Visible-Transparent-Panel',
type: 'Model',
modelURL: TRANSPARENT_PANEL_URL,
dimensions: {
x: AXIS_SCALE + 0.1,
y: totalOffset,
z: SLIDER_DIMENSIONS.z / 4
},
visible: true,
collisionsWillMove: false,
ignoreForCollisions: true,
position: moveDown,
rotation: avatarRot,
script: VISIBLE_PANEL_SCRIPT_URL
}
var panel = Entities.addEntity(panelProperties);
return panel
}
function createLightModel(position, rotation) {
var blockProperties = {
name: 'Hifi-Spotlight-Model',
type: 'Model',
shapeType: 'box',
modelURL: LIGHT_MODEL_URL,
dimensions: LIGHT_MODEL_DIMENSIONS,
collisionsWillMove: true,
position: position,
rotation: rotation,
script: PARENT_SCRIPT_URL,
userData: JSON.stringify({
handControllerKey: {
disableReleaseVelocity: true
}
})
};
var block = Entities.addEntity(blockProperties);
return block
}
var closeButtons = [];
function createCloseButton(axisStart) {
var MARGIN = 0.10;
var VERTICAL_OFFFSET = {
x: 0,
y: 0.15,
z: 0
};
var leftVector = Vec3.multiply(-1, Quat.getRight(avatarRot));
var extension = Vec3.multiply(MARGIN, leftVector);
var position = Vec3.sum(axisStart, extension);
var buttonProperties = {
name: 'Hifi-Close-Button',
type: 'Model',
modelURL: CLOSE_BUTTON_MODEL_URL,
dimensions: CLOSE_BUTTON_DIMENSIONS,
position: Vec3.sum(position, VERTICAL_OFFFSET),
rotation: Quat.multiply(avatarRot, Quat.fromPitchYawRollDegrees(90, 0, 45)),
//rotation: Quat.fromPitchYawRollDegrees(0, 0, 90),
collisionsWillMove: false,
ignoreForCollisions: true,
script: CLOSE_BUTTON_SCRIPT_URL,
userData: JSON.stringify({
grabbableKey: {
wantsTrigger: true
}
})
}
var button = Entities.addEntity(buttonProperties);
closeButtons.push(button);
if (ROTATE_CLOSE_BUTTON === true) {
Script.update.connect(rotateCloseButtons);
}
}
function rotateCloseButtons() {
closeButtons.forEach(function(button) {
Entities.editEntity(button, {
angularVelocity: {
x: 0,
y: 0.5,
z: 0
}
})
})
}
function subScribeToNewLights() {
Messages.subscribe('Hifi-Light-Mod-Receiver');
Messages.messageReceived.connect(handleLightModMessages);
}
function subscribeToSliderMessages() {
Messages.subscribe('Hifi-Slider-Value-Reciever');
Messages.messageReceived.connect(handleValueMessages);
}
function subscribeToLightOverlayRayCheckMessages() {
Messages.subscribe('Hifi-Light-Overlay-Ray-Check');
Messages.messageReceived.connect(handleLightOverlayRayCheckMessages);
}
function subscribeToCleanupMessages() {
Messages.subscribe('Hifi-Light-Modifier-Cleanup');
Messages.messageReceived.connect(handleCleanupMessages);
}
function handleLightModMessages(channel, message, sender) {
if (channel !== 'Hifi-Light-Mod-Receiver') {
return;
}
if (sender !== MyAvatar.sessionUUID) {
return;
}
var parsedMessage = JSON.parse(message);
makeSliders(parsedMessage.light);
light = parsedMessage.light.id
if (SHOW_LIGHT_VOLUME === true) {
selectionManager.setSelections([parsedMessage.light.id]);
}
}
function handleValueMessages(channel, message, sender) {
if (channel !== 'Hifi-Slider-Value-Reciever') {
return;
}
if (ONLY_I_CAN_EDIT === true && sender !== MyAvatar.sessionUUID) {
return;
}
var parsedMessage = JSON.parse(message);
slidersRef[parsedMessage.sliderType].setValueFromMessage(parsedMessage);
}
var currentLight;
var block;
var oldParent = null;
var hasParent = false;
function handleLightOverlayRayCheckMessages(channel, message, sender) {
if (channel !== 'Hifi-Light-Overlay-Ray-Check') {
return;
}
if (ONLY_I_CAN_EDIT === true && sender !== MyAvatar.sessionUUID) {
return;
}
var pickRay = JSON.parse(message);
var doesIntersect = lightOverlayManager.findRayIntersection(pickRay);
// print('DOES INTERSECT A LIGHT WE HAVE???' + doesIntersect.intersects);
if (doesIntersect.intersects === true) {
// print('FULL MESSAGE:::' + JSON.stringify(doesIntersect))
var lightID = doesIntersect.entityID;
if (currentLight === lightID) {
// print('ALREADY HAVE A BLOCK, EXIT')
return;
}
currentLight = lightID;
var lightProperties = Entities.getEntityProperties(lightID);
if (lightProperties.parentID !== DEFAULT_PARENT_ID) {
//this light has a parent already. so lets call our block the parent and then make sure not to delete it at the end;
oldParent = lightProperties.parentID;
hasParent = true;
block = lightProperties.parentID;
if (lightProperties.parentJointIndex !== -1) {
//should make sure to retain the parent too. but i don't actually know what the
}
} else {
block = createLightModel(lightProperties.position, lightProperties.rotation);
}
var light = {
id: lightID,
type: 'spotlight',
initialProperties: lightProperties
}
makeSliders(light);
if (SHOW_LIGHT_VOLUME === true) {
selectionManager.setSelections([lightID]);
}
Entities.editEntity(lightID, {
parentID: block,
parentJointIndex: -1
});
}
}
function handleCleanupMessages(channel, message, sender) {
if (channel !== 'Hifi-Light-Modifier-Cleanup') {
return;
}
if (ONLY_I_CAN_EDIT === true && sender !== MyAvatar.sessionUUID) {
return;
}
if (message === 'callCleanup') {
cleanup(true);
}
}
function updateSliderAxis() {
sliders.forEach(function(slider) {
})
}
function cleanup(fromMessage) {
var i;
for (i = 0; i < sliders.length; i++) {
Entities.deleteEntity(sliders[i].axis);
Entities.deleteEntity(sliders[i].sliderIndicator);
Entities.deleteEntity(sliders[i].label);
}
while (closeButtons.length > 0) {
Entities.deleteEntity(closeButtons.pop());
}
//if the light was already parented to something we will want to restore that. or come up with groups or something clever.
if (oldParent !== null) {
Entities.editEntity(currentLight, {
parentID: oldParent,
});
} else {
Entities.editEntity(currentLight, {
parentID: null,
});
}
if (fromMessage !== true) {
Messages.messageReceived.disconnect(handleLightModMessages);
Messages.messageReceived.disconnect(handleValueMessages);
Messages.messageReceived.disconnect(handleLightOverlayRayCheckMessages);
lightOverlayManager.setVisible(false);
}
Entities.deleteEntity(panel);
Entities.deleteEntity(visiblePanel);
selectionManager.clearSelections();
if (ROTATE_CLOSE_BUTTON === true) {
Script.update.disconnect(rotateCloseButtons);
}
if (hasParent === false) {
Entities.deleteEntity(block);
}
oldParent = null;
hasParent = false;
currentLight = null;
sliders = [];
}
Script.scriptEnding.connect(cleanup);
Script.scriptEnding.connect(function() {
lightOverlayManager.setVisible(false);
})
subscribeToLightOverlayRayCheckMessages();
subScribeToNewLights();
subscribeToCleanupMessages();
//other light properties
// diffuseColor: { red: 255, green: 255, blue: 255 },
// ambientColor: { red: 255, green: 255, blue: 255 },
// specularColor: { red: 255, green: 255, blue: 255 },
// constantAttenuation: 1,
// linearAttenuation: 0,
// quadraticAttenuation: 0,
// exponent: 0,
// cutoff: 180, // in degrees

View file

@ -0,0 +1,73 @@
//
// lightModifierTestScene.js
//
// Created by James Pollack @imgntn on 12/15/2015
// Copyright 2015 High Fidelity, Inc.
//
// Given a selected light, instantiate some entities that represent various values you can dynamically adjust.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var PARENT_SCRIPT_URL = Script.resolvePath('lightParent.js?' + Math.random(0 - 100));
var basePosition, avatarRot;
avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0);
basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(0, Quat.getUp(avatarRot)));
var light;
function createLight() {
var position = basePosition;
position.y += 2;
var lightTransform = evalLightWorldTransform(position, avatarRot);
var lightProperties = {
name: 'Hifi-Spotlight',
type: "Light",
isSpotlight: true,
dimensions: {
x: 2,
y: 2,
z: 8
},
color: {
red: 255,
green: 0,
blue: 255
},
intensity: 0.035,
exponent: 1,
cutoff: 30,
lifetime: -1,
position: lightTransform.p,
rotation: lightTransform.q
};
light = Entities.addEntity(lightProperties);
}
function evalLightWorldTransform(modelPos, modelRot) {
var MODEL_LIGHT_POSITION = {
x: 0,
y: -0.3,
z: 0
};
var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, {
x: 1,
y: 0,
z: 0
});
return {
p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)),
q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION)
};
}
function cleanup() {
Entities.deleteEntity(light);
}
Script.scriptEnding.connect(cleanup);
createLight();

View file

@ -0,0 +1,40 @@
//
// lightParent.js
//
// Created by James Pollack @imgntn on 12/15/2015
// Copyright 2015 High Fidelity, Inc.
//
// Entity script that tells the light parent to update the selection tool when we move it.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
function LightParent() {
return this;
}
LightParent.prototype = {
preload: function(entityID) {
this.entityID = entityID;
var entityProperties = Entities.getEntityProperties(this.entityID, "userData");
this.initialProperties = entityProperties
this.userData = JSON.parse(entityProperties.userData);
},
startNearGrab: function() {},
startDistantGrab: function() {
},
continueNearGrab: function() {
this.continueDistantGrab();
},
continueDistantGrab: function() {
Messages.sendMessage('entityToolUpdates', 'callUpdate');
},
};
return new LightParent();
});

View file

@ -0,0 +1,105 @@
//
// slider.js
//
// Created by James Pollack @imgntn on 12/15/2015
// Copyright 2015 High Fidelity, Inc.
//
// Entity script that sends a scaled value to a light based on its distance from the start of its constraint axis.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
var AXIS_SCALE = 1;
var COLOR_MAX = 255;
var INTENSITY_MAX = 0.05;
var CUTOFF_MAX = 360;
var EXPONENT_MAX = 1;
function Slider() {
return this;
}
Slider.prototype = {
preload: function(entityID) {
this.entityID = entityID;
var entityProperties = Entities.getEntityProperties(this.entityID, "userData");
var parsedUserData = JSON.parse(entityProperties.userData);
this.userData = parsedUserData.lightModifierKey;
},
startNearGrab: function() {
this.setInitialProperties();
},
startDistantGrab: function() {
this.setInitialProperties();
},
setInitialProperties: function() {
this.initialProperties = Entities.getEntityProperties(this.entityID);
},
continueNearGrab: function() {
// this.continueDistantGrab();
},
continueDistantGrab: function() {
this.setSliderValueBasedOnDistance();
},
setSliderValueBasedOnDistance: function() {
var currentPosition = Entities.getEntityProperties(this.entityID, "position").position;
var distance = Vec3.distance(this.userData.axisStart, currentPosition);
if (this.userData.sliderType === 'color_red' || this.userData.sliderType === 'color_green' || this.userData.sliderType === 'color_blue') {
this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, COLOR_MAX);
}
if (this.userData.sliderType === 'intensity') {
this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, INTENSITY_MAX);
}
if (this.userData.sliderType === 'cutoff') {
this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, CUTOFF_MAX);
}
if (this.userData.sliderType === 'exponent') {
this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, EXPONENT_MAX);
};
this.sendValueToSlider();
},
releaseGrab: function() {
Entities.editEntity(this.entityID, {
velocity: {
x: 0,
y: 0,
z: 0
},
angularVelocity: {
x: 0,
y: 0,
z: 0
}
})
this.sendValueToSlider();
},
scaleValueBasedOnDistanceFromStart: function(value, min2, max2) {
var min1 = 0;
var max1 = AXIS_SCALE;
var min2 = min2;
var max2 = max2;
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
},
sendValueToSlider: function() {
var _t = this;
var message = {
lightID: _t.userData.lightID,
sliderType: _t.userData.sliderType,
sliderValue: _t.sliderValue
}
Messages.sendMessage('Hifi-Slider-Value-Reciever', JSON.stringify(message));
if (_t.userData.sliderType === 'cutoff') {
Messages.sendMessage('entityToolUpdates', 'callUpdate');
}
}
};
return new Slider();
});

View file

@ -0,0 +1,40 @@
//
// visiblePanel.js
//
// Created by James Pollack @imgntn on 12/15/2015
// Copyright 2015 High Fidelity, Inc.
//
// Entity script that disables picking on this panel.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
function VisiblePanel() {
return this;
}
VisiblePanel.prototype = {
preload: function(entityID) {
this.entityID = entityID;
var data = {
action: 'add',
id: this.entityID
};
Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data))
},
unload: function() {
var data = {
action: 'remove',
id: this.entityID
};
Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data))
}
};
return new VisiblePanel();
});

View file

@ -43,6 +43,8 @@ var MAX_STROKE_WIDTH = 0.04;
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(Camera.getOrientation())));
var textureURL = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/paintStroke.png";
function MyController(hand, triggerAction) {
@ -148,7 +150,8 @@ function MyController(hand, triggerAction) {
y: 50,
z: 50
},
lifetime: 200
lifetime: 200,
textures: textureURL
});
this.strokePoints = [];
this.strokeNormals = [];

View file

@ -29,6 +29,8 @@
var MIN_STROKE_WIDTH = 0.0005;
var MAX_STROKE_WIDTH = 0.03;
var textureURL = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/paintStroke.png";
var TRIGGER_CONTROLS = [
Controller.Standard.LT,
Controller.Standard.RT,
@ -168,6 +170,7 @@
type: "PolyLine",
name: "paintStroke",
color: this.strokeColor,
textures: "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/paintStroke.png",
dimensions: {
x: 50,
y: 50,

View file

@ -247,4 +247,4 @@ function cleanup() {
// Uncomment this line to delete whiteboard and all associated entity on script close
//Script.scriptEnding.connect(cleanup);
// Script.scriptEnding.connect(cleanup);

View file

@ -0,0 +1,73 @@
//
// rayPickingFilterExample.js
// examples
//
// Created by Eric Levin on 12/24/2015
// Copyright 2015 High Fidelity, Inc.
//
// This is an example script that demonstrates the use of filtering entities for ray picking
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));
var whiteListBox = Entities.addEntity({
type: "Box",
color: {
red: 10,
green: 200,
blue: 10
},
dimensions: {
x: 0.2,
y: 0.2,
z: 0.2
},
position: center
});
var blackListBox = Entities.addEntity({
type: "Box",
color: {
red: 100,
green: 10,
blue: 10
},
dimensions: {
x: 0.2,
y: 0.2,
z: 0.2
},
position: Vec3.sum(center, {
x: 0,
y: 0.3,
z: 0
})
});
function castRay(event) {
var pickRay = Camera.computePickRay(event.x, event.y);
// In this example every entity will be pickable except the entities in the blacklist array
// the third argument is the whitelist array,and the fourth and final is the blacklist array
var pickResults = Entities.findRayIntersection(pickRay, true, [], [blackListBox]);
// With below example, only entities added to whitelist will be pickable
// var pickResults = Entities.findRayIntersection(pickRay, true, [whiteListBox], []);
if (pickResults.intersects) {
print("INTERSECTION!");
}
}
function cleanup() {
Entities.deleteEntity(whiteListBox);
Entities.deleteEntity(blackListBox);
}
Script.scriptEnding.connect(cleanup);
Controller.mousePressEvent.connect(castRay);

View file

@ -241,9 +241,9 @@
userData: JSON.stringify({
grabbableKey: {
grabbable: false
}
},
creatorSessionUUID: MyAvatar.sessionUUID
})
});
var makeArrowStick = function(entityA, entityB, collision) {

View file

@ -38,6 +38,14 @@ var pingPongGun = Entities.addEntity({
collisionSoundURL: COLLISION_SOUND_URL,
userData: JSON.stringify({
grabbableKey: {
spatialKey: {
relativePosition: {
x: -0.05,
y: 0,
z: 0.0
},
relativeRotation: Quat.fromPitchYawRollDegrees(0, -90, -90)
},
invertSolidWhileHeld: true
}
})

View file

@ -29,21 +29,14 @@
this.equipped = false;
this.forceMultiplier = 1;
this.laserLength = 100;
this.laserOffsets = {
y: .095
};
this.firingOffsets = {
z: 0.16
}
this.fireSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Guns/GUN-SHOT2.raw");
this.ricochetSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Guns/Ricochet.L.wav");
this.playRichochetSoundChance = 0.1;
this.fireVolume = 0.2;
this.bulletForce = 10;
this.showLaser = false;
};
Pistol.prototype = {
@ -58,20 +51,36 @@
if (!this.equipped) {
return;
}
this.toggleWithTriggerPressure();
this.updateProps();
if (this.showLaser) {
this.updateLaser();
}
this.toggleWithTriggerPressure();
},
updateProps: function() {
var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
this.position = gunProps.position;
this.rotation = gunProps.rotation;
this.firingDirection = Quat.getFront(this.rotation);
var upVec = Quat.getUp(this.rotation);
this.barrelPoint = Vec3.sum(this.position, Vec3.multiply(upVec, this.laserOffsets.y));
this.laserTip = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.laserLength));
this.barrelPoint = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.firingOffsets.z))
var pickRay = {
origin: this.barrelPoint,
direction: this.firingDirection
};
},
toggleWithTriggerPressure: function() {
this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[this.hand]);
if (this.triggerValue < RELOAD_THRESHOLD) {
// print('RELOAD');
this.canShoot = true;
}
if (this.canShoot === true && this.triggerValue === 1) {
// print('SHOOT');
this.fire();
this.canShoot = false;
}
@ -91,17 +100,10 @@
},
updateLaser: function() {
var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
var position = gunProps.position;
var rotation = gunProps.rotation;
this.firingDirection = Quat.getFront(rotation);
var upVec = Quat.getUp(rotation);
this.barrelPoint = Vec3.sum(position, Vec3.multiply(upVec, this.laserOffsets.y));
var laserTip = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.laserLength));
this.barrelPoint = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.firingOffsets.z))
Overlays.editOverlay(this.laser, {
start: this.barrelPoint,
end: laserTip,
end: this.laserTip,
alpha: 1
});
},
@ -114,19 +116,6 @@
});
},
preload: function(entityID) {
this.entityID = entityID;
// this.initControllerMapping();
this.laser = Overlays.addOverlay("line3d", {
start: ZERO_VECTOR,
end: ZERO_VECTOR,
color: COLORS.RED,
alpha: 1,
visible: true,
lineWidth: 2
});
},
triggerPress: function(hand, value) {
if (this.hand === hand && value === 1) {
//We are pulling trigger on the hand we have the gun in, so fire
@ -135,15 +124,16 @@
},
fire: function() {
var pickRay = {
origin: this.barrelPoint,
direction: this.firingDirection
};
Audio.playSound(this.fireSound, {
position: this.barrelPoint,
volume: this.fireVolume
});
var pickRay = {
origin: this.barrelPoint,
direction: this.firingDirection
};
this.createGunFireEffect(this.barrelPoint)
var intersection = Entities.findRayIntersectionBlocking(pickRay, true);
if (intersection.intersects) {
@ -170,11 +160,11 @@
},
createEntityHitEffect: function(position) {
var flash = Entities.addEntity({
var sparks = Entities.addEntity({
type: "ParticleEffect",
position: position,
lifetime: 4,
"name": "Flash Emitter",
"name": "Sparks Emitter",
"color": {
red: 228,
green: 128,
@ -228,7 +218,7 @@
});
Script.setTimeout(function() {
Entities.editEntity(flash, {
Entities.editEntity(sparks, {
isEmitting: false
});
}, 100);
@ -261,11 +251,11 @@
"z": 0
},
"accelerationSpread": {
"x": .2,
"x": 0.2,
"y": 0,
"z": .2
"z": 0.2
},
"radiusSpread": .04,
"radiusSpread": 0.04,
"particleRadius": 0.07,
"radiusStart": 0.07,
"radiusFinish": 0.07,
@ -282,11 +272,46 @@
});
}, 100);
var flash = Entities.addEntity({
Entities.editEntity(this.flash, {
isEmitting: true
});
Script.setTimeout(function() {
Entities.editEntity(_this.flash, {
isEmitting: false
});
}, 100)
},
preload: function(entityID) {
this.entityID = entityID;
this.laser = Overlays.addOverlay("line3d", {
start: ZERO_VECTOR,
end: ZERO_VECTOR,
color: COLORS.RED,
alpha: 1,
visible: true,
lineWidth: 2
});
this.laserOffsets = {
y: 0.095
};
this.firingOffsets = {
z: 0.16
}
var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
var position = gunProps.position;
var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0);
this.firingDirection = Quat.getFront(rotation);
var upVec = Quat.getUp(rotation);
this.barrelPoint = Vec3.sum(position, Vec3.multiply(upVec, this.laserOffsets.y));
this.barrelPoint = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.firingOffsets.z))
this.flash = Entities.addEntity({
type: "ParticleEffect",
position: position,
lifetime: 4,
position: this.barrelPoint,
"name": "Muzzle Flash",
isEmitting: false,
"color": {
red: 228,
green: 128,
@ -339,16 +364,13 @@
"textures": "http://ericrius1.github.io/PartiArt/assets/star.png"
});
Script.setTimeout(function() {
Entities.editEntity(flash, {
isEmitting: false
});
}, 100)
}
Script.setTimeout(function() {
Entities.editEntity(_this.flash, {parentID: _this.entityID});
}, 500)
},
};
// entity scripts always need to return a newly constructed object of our type
return new Pistol();
});
});

View file

@ -814,12 +814,14 @@ var CHECK_MARK_COLOR = {
});
};
CollapsablePanelItem.prototype.destroy = function() {
Overlays.deleteOverlay(this.title);
Overlays.deleteOverlay(this.thumb);
};
CollapsablePanelItem.prototype.editTitle = function(opts) {
Overlays.editOverlay(this.title, opts);
};
CollapsablePanelItem.prototype.hide = function() {
Overlays.editOverlay(this.title, {
@ -1531,4 +1533,4 @@ Controller.captureKeyEvents({
});
Controller.captureKeyEvents({
text: "right"
});
});

View file

@ -21,56 +21,34 @@ Number.prototype.clamp = function(min, max) {
var panel = new Panel(10, 100);
function CounterWidget(parentPanel, name, feedGetter, drawGetter, capSetter, capGetter) {
this.subPanel = parentPanel.newSubPanel(name);
function CounterWidget(parentPanel, name, counter) {
var subPanel = parentPanel.newSubPanel(name);
var widget = parentPanel.items[name];
widget.editTitle({ width: 270 });
this.subPanel.newSlider("Num Feed", 0, 1,
function(value) { },
feedGetter,
function(value) { return (value); });
this.subPanel.newSlider("Num Drawn", 0, 1,
function(value) { },
drawGetter,
function(value) { return (value); });
this.subPanel.newSlider("Max Drawn", -1, 1,
capSetter,
capGetter,
function(value) { return (value); });
subPanel.newSlider('Max Drawn', -1, 1,
function(value) { counter.maxDrawn = value; }, // setter
function() { return counter.maxDrawn; }, // getter
function(value) { return value; });
var slider = subPanel.getWidget('Max Drawn');
this.update = function () {
var numFeed = this.subPanel.get("Num Feed");
var numDrawn = this.subPanel.get("Num Drawn");
var numMax = Math.max(numFeed, 1);
var numDrawn = counter.numDrawn; // avoid double polling
var numMax = Math.max(numDrawn, 1);
var title = [
' ' + name,
numDrawn + ' / ' + counter.numFeed
].join('\t');
this.subPanel.set("Num Feed", numFeed);
this.subPanel.set("Num Drawn", numDrawn);
this.subPanel.getWidget("Num Feed").setMaxValue(numMax);
this.subPanel.getWidget("Num Drawn").setMaxValue(numMax);
this.subPanel.getWidget("Max Drawn").setMaxValue(numMax);
widget.editTitle({ text: title });
slider.setMaxValue(numMax);
};
};
var opaquesCounter = new CounterWidget(panel, "Opaques",
function () { return Render.opaque.numFeed; },
function () { return Render.opaque.numDrawn; },
function(value) { Render.opaque.maxDrawn = value; },
function () { return Render.opaque.maxDrawn; }
);
var transparentsCounter = new CounterWidget(panel, "Transparents",
function () { return Render.transparent.numFeed; },
function () { return Render.transparent.numDrawn; },
function(value) { Render.transparent.maxDrawn = value; },
function () { return Render.transparent.maxDrawn; }
);
var overlaysCounter = new CounterWidget(panel, "Overlays",
function () { return Render.overlay3D.numFeed; },
function () { return Render.overlay3D.numDrawn; },
function(value) { Render.overlay3D.maxDrawn = value; },
function () { return Render.overlay3D.maxDrawn; }
);
var opaquesCounter = new CounterWidget(panel, "Opaques", Render.opaque);
var transparentsCounter = new CounterWidget(panel, "Transparents", Render.transparent);
var overlaysCounter = new CounterWidget(panel, "Overlays", Render.overlay3D);
var resizing = false;
var previousMode = Settings.getValue(SETTINGS_KEY, -1);
@ -162,9 +140,13 @@ Menu.menuItemEvent.connect(menuItemEvent);
function scriptEnding() {
panel.destroy();
Menu.removeActionGroup(MENU);
// Reset
Settings.setValue(SETTINGS_KEY, Render.deferredDebugMode);
Render.deferredDebugMode = -1;
Render.deferredDebugSize = { x: 0.0, y: -1.0, z: 1.0, w: 1.0 }; // Reset to default size
Render.deferredDebugSize = { x: 0.0, y: -1.0, z: 1.0, w: 1.0 };
Render.opaque.maxDrawn = -1;
Render.transparent.maxDrawn = -1;
Render.overlay3D.maxDrawn = -1;
}
Script.scriptEnding.connect(scriptEnding);

View file

@ -0,0 +1,73 @@
//
// shooterPlatform.js
// examples/winterSmashUp/targetPractice
//
// Created by Thijs Wenker on 12/21/15.
// Copyright 2015 High Fidelity, Inc.
//
// The Winter Smash Up Target Practice Game using a bow.
// This is the platform that spawns the bow and attaches it to the avatars hand,
// then de-rez the bow on leaving to prevent walking up to the targets with the bow.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
var _this = this;
const GAME_CHANNEL = 'winterSmashUpGame';
const SCRIPT_URL = Script.resolvePath('../../toybox/bow/bow.js');
const MODEL_URL = "https://hifi-public.s3.amazonaws.com/models/bow/new/bow-deadly.fbx";
const COLLISION_HULL_URL = "https://hifi-public.s3.amazonaws.com/models/bow/new/bow_collision_hull.obj";
const RIGHT_HAND = 1;
const LEFT_HAND = 0;
var bowEntity = undefined;
_this.enterEntity = function(entityID) {
print('entered bow game entity');
// Triggers a recording on an assignment client:
Messages.sendMessage('PlayBackOnAssignment', 'BowShootingGameExplaination');
bowEntity = Entities.addEntity({
name: 'Hifi-Bow-For-Game',
type: 'Model',
modelURL: MODEL_URL,
position: MyAvatar.position,
dimensions: {x: 0.04, y: 1.3, z: 0.21},
collisionsWillMove: true,
gravity: {x: 0, y: 0, z: 0},
shapeType: 'compound',
compoundShapeURL: COLLISION_HULL_URL,
script: SCRIPT_URL,
userData: JSON.stringify({
grabbableKey: {
invertSolidWhileHeld: true,
spatialKey: {
leftRelativePosition: {
x: 0.05,
y: 0.06,
z: -0.05
},
rightRelativePosition: {
x: -0.05,
y: 0.06,
z: -0.05
},
relativeRotation: Quat.fromPitchYawRollDegrees(0, 90, -90)
}
}
})
});
Messages.sendMessage('Hifi-Hand-Grab', JSON.stringify({hand: 'left', entityID: bowEntity}));
};
_this.leaveEntity = function(entityID) {
if (bowEntity !== undefined) {
Entities.deleteEntity(bowEntity);
bowEntity = undefined;
}
};
});

View file

@ -0,0 +1,93 @@
//
// startTargetPractice.js
// examples/winterSmashUp/targetPractice
//
// Created by Thijs Wenker on 12/21/15.
// Copyright 2015 High Fidelity, Inc.
//
// The Winter Smash Up Target Practice Game using a bow.
// This script starts the game, when the entity that contains the script gets shot.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
var _this = this;
var waitForScriptLoad = false;
const MAX_GAME_TIME = 60; //seconds
const SCRIPT_URL = 'http://s3.amazonaws.com/hifi-public/scripts/winterSmashUp/targetPractice/targetPracticeGame.js';
const GAME_CHANNEL = 'winterSmashUpGame';
var isScriptRunning = function(script) {
script = script.toLowerCase().trim();
var runningScripts = ScriptDiscoveryService.getRunning();
for (i in runningScripts) {
if (runningScripts[i].url.toLowerCase().trim() == script) {
return true;
}
}
return false;
};
var sendStartSignal = function() {
Messages.sendMessage(GAME_CHANNEL, JSON.stringify({
action: 'start',
gameEntityID: _this.entityID,
playerSessionUUID: MyAvatar.sessionUUID
}));
}
var startGame = function() {
//TODO: check here if someone is already playing this game instance by verifying the userData for playerSessionID
// and startTime with a maximum timeout of X seconds (30?)
if (!isScriptRunning(SCRIPT_URL)) {
// Loads the script for the player if this isn't yet loaded
Script.load(SCRIPT_URL);
waitForScriptLoad = true;
return;
}
sendStartSignal();
};
Messages.messageReceived.connect(function (channel, message, senderID) {
if (channel == GAME_CHANNEL) {
var data = JSON.parse(message);
switch (data.action) {
case 'scriptLoaded':
if (waitForPing) {
sendStartSignal();
waitForPing = false;
}
break;
}
}
});
_this.preload = function(entityID) {
_this.entityID = entityID;
};
_this.collisionWithEntity = function(entityA, entityB, collisionInfo) {
if (entityA == _this.entityID) {
try {
var data = JSON.parse(Entities.getEntityProperties(entityB).userData);
if (data.creatorSessionUUID === MyAvatar.sessionUUID) {
print('attempting to startGame by collisionWithEntity.');
startGame();
}
} catch(e) {
}
}
};
_this.onShot = function(forceDirection) {
print('attempting to startGame by onShot.');
startGame();
};
Messages.subscribe(GAME_CHANNEL);
});

View file

@ -0,0 +1,227 @@
//
// targetPracticeGame.js
// examples/winterSmashUp/targetPractice
//
// Created by Thijs Wenker on 12/21/15.
// Copyright 2015 High Fidelity, Inc.
//
// The Winter Smash Up Target Practice Game using a bow.
// This script runs on the client side (it is loaded through the platform trigger entity)
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
const GAME_CHANNEL = 'winterSmashUpGame';
const SCORE_POST_URL = 'https://script.google.com/macros/s/AKfycbwZAMx6cMBx6-8NGEhR8ELUA-dldtpa_4P55z38Q4vYHW6kneg/exec';
const MODEL_URL = 'http://cdn.highfidelity.com/chris/production/winter/game/';
const MAX_GAME_TIME = 120; //seconds
const TARGET_CLOSE_OFFSET = 0.5;
const MILLISECS_IN_SEC = 1000;
const HIT_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Clay_Pigeon_02.L.wav';
const GRAVITY = -9.8;
const MESSAGE_WIDTH = 100;
const MESSAGE_HEIGHT = 50;
const TARGETS = [
{pitch: -1, yaw: -20, maxDistance: 17},
{pitch: -1, yaw: -15, maxDistance: 17},
{pitch: -1, yaw: -10, maxDistance: 17},
{pitch: -2, yaw: -5, maxDistance: 17},
{pitch: -1, yaw: 0, maxDistance: 17},
{pitch: 3, yaw: 10, maxDistance: 17},
{pitch: -1, yaw: 15, maxDistance: 17},
{pitch: -1, yaw: 20, maxDistance: 17},
{pitch: 2, yaw: 25, maxDistance: 17},
{pitch: 0, yaw: 30, maxDistance: 17}
];
var gameRunning = false;
var gameStartTime;
var gameEntityID;
var gameTimeoutTimer;
var targetEntities = [];
var hitSound = SoundCache.getSound(HIT_SOUND_URL);
var messageOverlay = undefined;
var messageExpire = 0;
var clearMessage = function() {
if (messageOverlay !== undefined) {
Overlays.deleteOverlay(messageOverlay);
messageOverlay = undefined;
}
};
var displayMessage = function(message, timeout) {
clearMessage();
messageExpire = Date.now() + timeout;
messageOverlay = Overlays.addOverlay('text', {
text: message,
x: (Window.innerWidth / 2) - (MESSAGE_WIDTH / 2),
y: (Window.innerHeight / 2) - (MESSAGE_HEIGHT / 2),
width: MESSAGE_WIDTH,
height: MESSAGE_HEIGHT,
alpha: 1,
backgroundAlpha: 0.0,
font: {size: 20}
});
};
var getStatsText = function() {
var timeLeft = MAX_GAME_TIME - ((Date.now() - gameStartTime) / MILLISECS_IN_SEC);
return 'Time remaining: ' + timeLeft.toFixed(1) + 's\nTargets hit: ' + (TARGETS.length - targetEntities.length) + '/' + TARGETS.length;
};
const timerOverlayWidth = 50;
var timerOverlay = Overlays.addOverlay('text', {
text: '',
x: Window.innerWidth / 2 - (timerOverlayWidth / 2),
y: 100,
width: timerOverlayWidth,
alpha: 1,
backgroundAlpha: 0.0,
visible: false,
font: {size: 20}
});
var cleanRemainingTargets = function() {
while (targetEntities.length > 0) {
Entities.deleteEntity(targetEntities.pop());
}
};
var createTarget = function(position, rotation, scale) {
var initialDimensions = {x: 1.8437, y: 0.1614, z: 1.8438};
var dimensions = Vec3.multiply(initialDimensions, scale);
return Entities.addEntity({
type: 'Model',
rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(90, 0, 0)),
lifetime: MAX_GAME_TIME,
modelURL: MODEL_URL + 'target.fbx',
shapeType: 'compound',
compoundShapeURL: MODEL_URL + 'targetCollision.obj',
dimensions: dimensions,
position: position
});
};
var createTargetInDirection = function(startPosition, startRotation, pitch, yaw, maxDistance, scale) {
var directionQuat = Quat.multiply(startRotation, Quat.fromPitchYawRollDegrees(pitch, yaw, 0.0));
var directionVec = Vec3.multiplyQbyV(directionQuat, Vec3.FRONT);
var intersection = Entities.findRayIntersection({direction: directionVec, origin: startPosition}, true);
var distance = maxDistance;
if (intersection.intersects && intersection.distance <= maxDistance) {
distance = intersection.distance - TARGET_CLOSE_OFFSET;
}
return createTarget(Vec3.sum(startPosition, Vec3.multiplyQbyV(directionQuat, Vec3.multiply(Vec3.FRONT, distance))), startRotation, scale);
};
var killTimer = function() {
if (gameTimeoutTimer !== undefined) {
Script.clearTimeout(gameTimeoutTimer);
gameTimeoutTimer = undefined;
}
};
var submitScore = function() {
gameRunning = false;
killTimer();
Overlays.editOverlay(timerOverlay, {visible: false});
if (!GlobalServices.username) {
displayMessage('Could not submit score, you are not logged in.', 5000);
return;
}
var timeItTook = Date.now() - gameStartTime;
var req = new XMLHttpRequest();
req.open('POST', SCORE_POST_URL, false);
req.send(JSON.stringify({
username: GlobalServices.username,
time: timeItTook / MILLISECS_IN_SEC
}));
displayMessage('Your score has been submitted!', 5000);
};
var onTargetHit = function(targetEntity, projectileEntity, collision) {
var targetIndex = targetEntities.indexOf(targetEntity);
if (targetIndex !== -1) {
try {
var data = JSON.parse(Entities.getEntityProperties(projectileEntity).userData);
if (data.creatorSessionUUID === MyAvatar.sessionUUID) {
this.audioInjector = Audio.playSound(hitSound, {
position: collision.contactPoint,
volume: 0.5
});
// Attach arrow to target for the nice effect
Entities.editEntity(projectileEntity, {
ignoreForCollisions: true,
parentID: targetEntity
});
Entities.editEntity(targetEntity, {
collisionsWillMove: true,
gravity: {x: 0, y: GRAVITY, z: 0},
velocity: {x: 0, y: -0.01, z: 0}
});
targetEntities.splice(targetIndex, 1);
if (targetEntities.length === 0) {
submitScore();
}
}
} catch(e) {
}
}
};
var startGame = function(entityID) {
cleanRemainingTargets();
killTimer();
gameEntityID = entityID;
targetEntities = [];
var parentEntity = Entities.getEntityProperties(gameEntityID);
for (var i in TARGETS) {
var target = TARGETS[i];
var targetEntity = createTargetInDirection(parentEntity.position, parentEntity.rotation, target.pitch, target.yaw, target.maxDistance, 0.67);
Script.addEventHandler(targetEntity, 'collisionWithEntity', onTargetHit);
targetEntities.push(targetEntity);
}
gameStartTime = Date.now();
gameTimeoutTimer = Script.setTimeout(function() {
cleanRemainingTargets();
Overlays.editOverlay(timerOverlay, {visible: false});
gameRunning = false;
displayMessage('Game timed out.', 5000);
}, MAX_GAME_TIME * MILLISECS_IN_SEC);
gameRunning = true;
Overlays.editOverlay(timerOverlay, {visible: true, text: getStatsText()});
displayMessage('Game started! GO GO GO!', 3000);
};
Messages.messageReceived.connect(function (channel, message, senderID) {
if (channel == GAME_CHANNEL) {
var data = JSON.parse(message);
switch (data.action) {
case 'start':
if (data.playerSessionUUID === MyAvatar.sessionUUID) {
startGame(data.gameEntityID);
}
break;
}
}
});
Messages.subscribe(GAME_CHANNEL);
Messages.sendMessage(GAME_CHANNEL, JSON.stringify({action: 'scriptLoaded'}));
Script.update.connect(function(deltaTime) {
if (gameRunning) {
Overlays.editOverlay(timerOverlay, {text: getStatsText()});
}
if (messageOverlay !== undefined && Date.now() > messageExpire) {
clearMessage();
}
});
Script.scriptEnding.connect(function() {
Overlays.deleteOverlay(timerOverlay);
cleanRemainingTargets();
clearMessage();
});

View file

@ -109,7 +109,9 @@ add_dependency_external_projects(sdl2)
if (WIN32)
add_dependency_external_projects(OpenVR)
endif()
if(WIN32 OR APPLE)
add_dependency_external_projects(neuron)
endif()
# disable /OPT:REF and /OPT:ICF for the Debug builds
# This will prevent the following linker warnings

View file

@ -0,0 +1,7 @@
{
"name": "Neuron to Standard",
"channels": [
{ "from": "Hydra.LeftHand", "to": "Standard.LeftHand" },
{ "from": "Hydra.RightHand", "to": "Standard.RightHand" }
]
}

View file

@ -1207,23 +1207,12 @@ void Application::paintGL() {
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebuffer();
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
renderArgs._blitFramebuffer = DependencyManager::get<FramebufferCache>()->getSelfieFramebuffer();
renderRearViewMirror(&renderArgs, _mirrorViewRect);
renderArgs._blitFramebuffer.reset();
renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE;
{
float ratio = ((float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale());
// Flip the src and destination rect horizontally to do the mirror
auto mirrorRect = glm::ivec4(0, 0, _mirrorViewRect.width() * ratio, _mirrorViewRect.height() * ratio);
auto mirrorRectDest = glm::ivec4(mirrorRect.z, mirrorRect.y, mirrorRect.x, mirrorRect.w);
auto selfieFbo = DependencyManager::get<FramebufferCache>()->getSelfieFramebuffer();
gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
batch.setFramebuffer(selfieFbo);
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f));
batch.blit(primaryFbo, mirrorRect, selfieFbo, mirrorRectDest);
batch.setFramebuffer(nullptr);
});
}
}
{
@ -1394,65 +1383,11 @@ void Application::paintGL() {
renderArgs._context->setStereoProjections(eyeProjections);
renderArgs._context->setStereoViews(eyeOffsets);
}
renderArgs._blitFramebuffer = finalFramebuffer;
displaySide(&renderArgs, _myCamera);
renderArgs._blitFramebuffer.reset();
renderArgs._context->enableStereo(false);
// Blit primary to final FBO
auto primaryFbo = framebufferCache->getPrimaryFramebuffer();
if (renderArgs._renderMode == RenderArgs::MIRROR_RENDER_MODE) {
if (displayPlugin->isStereo()) {
gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
gpu::Vec4i srcRectLeft;
srcRectLeft.z = size.width() / 2;
srcRectLeft.w = size.height();
gpu::Vec4i srcRectRight;
srcRectRight.x = size.width() / 2;
srcRectRight.z = size.width();
srcRectRight.w = size.height();
gpu::Vec4i destRectLeft;
destRectLeft.x = srcRectLeft.z;
destRectLeft.z = srcRectLeft.x;
destRectLeft.y = srcRectLeft.y;
destRectLeft.w = srcRectLeft.w;
gpu::Vec4i destRectRight;
destRectRight.x = srcRectRight.z;
destRectRight.z = srcRectRight.x;
destRectRight.y = srcRectRight.y;
destRectRight.w = srcRectRight.w;
batch.setFramebuffer(finalFramebuffer);
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 1.0f, 0.0f));
// BLit left to right and right to left in stereo
batch.blit(primaryFbo, srcRectRight, finalFramebuffer, destRectLeft);
batch.blit(primaryFbo, srcRectLeft, finalFramebuffer, destRectRight);
});
} else {
gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
gpu::Vec4i srcRect;
srcRect.z = size.width();
srcRect.w = size.height();
gpu::Vec4i destRect;
destRect.x = size.width();
destRect.y = 0;
destRect.z = 0;
destRect.w = size.height();
batch.setFramebuffer(finalFramebuffer);
batch.blit(primaryFbo, srcRect, finalFramebuffer, destRect);
});
}
} else {
gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
gpu::Vec4i rect;
rect.z = size.width();
rect.w = size.height();
batch.setFramebuffer(finalFramebuffer);
batch.blit(primaryFbo, rect, finalFramebuffer, rect);
});
}
}
// Overlay Composition, needs to occur after screen space effects have completed
@ -3551,78 +3486,86 @@ namespace render {
// Background rendering decision
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
if (skyStage->getBackgroundMode() == model::SunSkyStage::NO_BACKGROUND) {
auto backgroundMode = skyStage->getBackgroundMode();
if (backgroundMode == model::SunSkyStage::NO_BACKGROUND) {
// this line intentionally left blank
} else if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_DOME) {
if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
PerformanceTimer perfTimer("stars");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::payloadRender<BackgroundRenderData>() ... stars...");
// should be the first rendering pass - w/o depth buffer / lighting
// compute starfield alpha based on distance from atmosphere
float alpha = 1.0f;
bool hasStars = true;
if (Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) {
// TODO: handle this correctly for zones
const EnvironmentData& closestData = background->_environment->getClosestData(args->_viewFrustum->getPosition()); // was theCamera instead of _viewFrustum
if (closestData.getHasStars()) {
const float APPROXIMATE_DISTANCE_FROM_HORIZON = 0.1f;
const float DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON = 0.2f;
glm::vec3 sunDirection = (args->_viewFrustum->getPosition()/*getAvatarPosition()*/ - closestData.getSunLocation())
/ closestData.getAtmosphereOuterRadius();
float height = glm::distance(args->_viewFrustum->getPosition()/*theCamera.getPosition()*/, closestData.getAtmosphereCenter());
if (height < closestData.getAtmosphereInnerRadius()) {
// If we're inside the atmosphere, then determine if our keyLight is below the horizon
alpha = 0.0f;
if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) {
float directionY = glm::clamp(sunDirection.y,
-APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
+ APPROXIMATE_DISTANCE_FROM_HORIZON;
alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON);
}
} else if (height < closestData.getAtmosphereOuterRadius()) {
alpha = (height - closestData.getAtmosphereInnerRadius()) /
(closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius());
if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) {
float directionY = glm::clamp(sunDirection.y,
-APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
+ APPROXIMATE_DISTANCE_FROM_HORIZON;
alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON);
}
}
} else {
hasStars = false;
}
} else {
if (backgroundMode == model::SunSkyStage::SKY_BOX) {
auto skybox = skyStage->getSkybox();
if (skybox && skybox->getCubemap() && skybox->getCubemap()->isDefined()) {
PerformanceTimer perfTimer("skybox");
skybox->render(batch, *(args->_viewFrustum));
} else {
// If no skybox texture is available, render the SKY_DOME while it loads
backgroundMode = model::SunSkyStage::SKY_DOME;
}
// finally render the starfield
if (hasStars) {
background->_stars.render(args, alpha);
}
// draw the sky dome
if (/*!selfAvatarOnly &&*/ Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) {
PerformanceTimer perfTimer("atmosphere");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... atmosphere...");
background->_environment->renderAtmospheres(batch, *(args->_viewFrustum));
}
}
} else if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_BOX) {
PerformanceTimer perfTimer("skybox");
auto skybox = skyStage->getSkybox();
if (skybox) {
skybox->render(batch, *(args->_viewFrustum));
if (backgroundMode == model::SunSkyStage::SKY_DOME) {
if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
PerformanceTimer perfTimer("stars");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::payloadRender<BackgroundRenderData>() ... stars...");
// should be the first rendering pass - w/o depth buffer / lighting
// compute starfield alpha based on distance from atmosphere
float alpha = 1.0f;
bool hasStars = true;
if (Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) {
// TODO: handle this correctly for zones
const EnvironmentData& closestData = background->_environment->getClosestData(args->_viewFrustum->getPosition()); // was theCamera instead of _viewFrustum
if (closestData.getHasStars()) {
const float APPROXIMATE_DISTANCE_FROM_HORIZON = 0.1f;
const float DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON = 0.2f;
glm::vec3 sunDirection = (args->_viewFrustum->getPosition()/*getAvatarPosition()*/ - closestData.getSunLocation())
/ closestData.getAtmosphereOuterRadius();
float height = glm::distance(args->_viewFrustum->getPosition()/*theCamera.getPosition()*/, closestData.getAtmosphereCenter());
if (height < closestData.getAtmosphereInnerRadius()) {
// If we're inside the atmosphere, then determine if our keyLight is below the horizon
alpha = 0.0f;
if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) {
float directionY = glm::clamp(sunDirection.y,
-APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
+ APPROXIMATE_DISTANCE_FROM_HORIZON;
alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON);
}
} else if (height < closestData.getAtmosphereOuterRadius()) {
alpha = (height - closestData.getAtmosphereInnerRadius()) /
(closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius());
if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) {
float directionY = glm::clamp(sunDirection.y,
-APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
+ APPROXIMATE_DISTANCE_FROM_HORIZON;
alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON);
}
}
} else {
hasStars = false;
}
}
// finally render the starfield
if (hasStars) {
background->_stars.render(args, alpha);
}
// draw the sky dome
if (/*!selfAvatarOnly &&*/ Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) {
PerformanceTimer perfTimer("atmosphere");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... atmosphere...");
background->_environment->renderAtmospheres(batch, *(args->_viewFrustum));
}
}
}
}
}

View file

@ -310,8 +310,8 @@ QVector<QUuid> AvatarManager::getAvatarIdentifiers() {
}
AvatarData* AvatarManager::getAvatar(QUuid avatarID) {
QReadLocker locker(&_hashLock);
return _avatarHash[avatarID].get(); // Non-obvious: A bogus avatarID answers your own avatar.
// Null/Default-constructed QUuids will return MyAvatar
return getAvatarBySessionID(avatarID).get();
}

View file

@ -0,0 +1,676 @@
//
// AnimExpression.cpp
//
// Created by Anthony J. Thibault on 11/1/15.
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <StreamUtils.h>
#include <QRegExp>
#include "AnimExpression.h"
#include "AnimationLogging.h"
AnimExpression::AnimExpression(const QString& str) :
_expression(str) {
auto iter = str.begin();
parseExpr(_expression, iter);
while(!_tokenStack.empty()) {
_tokenStack.pop();
}
}
//
// Tokenizer
//
void AnimExpression::unconsumeToken(const Token& token) {
_tokenStack.push(token);
}
AnimExpression::Token AnimExpression::consumeToken(const QString& str, QString::const_iterator& iter) const {
if (!_tokenStack.empty()) {
Token top = _tokenStack.top();
_tokenStack.pop();
return top;
} else {
while (iter != str.end()) {
if (iter->isSpace()) {
++iter;
} else if (iter->isLetter()) {
return consumeIdentifier(str, iter);
} else if (iter->isDigit()) {
return consumeNumber(str, iter);
} else {
switch (iter->unicode()) {
case '&': return consumeAnd(str, iter);
case '|': return consumeOr(str, iter);
case '>': return consumeGreaterThan(str, iter);
case '<': return consumeLessThan(str, iter);
case '(': ++iter; return Token(Token::LeftParen);
case ')': ++iter; return Token(Token::RightParen);
case '!': return consumeNot(str, iter);
case '-': ++iter; return Token(Token::Minus);
case '+': ++iter; return Token(Token::Plus);
case '*': ++iter; return Token(Token::Multiply);
case '/': ++iter; return Token(Token::Divide);
case '%': ++iter; return Token(Token::Modulus);
case ',': ++iter; return Token(Token::Comma);
default:
qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin());
return Token(Token::Error);
}
}
}
return Token(Token::End);
}
}
AnimExpression::Token AnimExpression::consumeIdentifier(const QString& str, QString::const_iterator& iter) const {
assert(iter != str.end());
assert(iter->isLetter());
auto begin = iter;
while ((iter->isLetter() || iter->isDigit()) && iter != str.end()) {
++iter;
}
int pos = (int)(begin - str.begin());
int len = (int)(iter - begin);
QStringRef stringRef(const_cast<const QString*>(&str), pos, len);
if (stringRef == "true") {
return Token(true);
} else if (stringRef == "false") {
return Token(false);
} else {
return Token(stringRef);
}
}
// TODO: not very efficient or accruate, but it's close enough for now.
static float computeFractionalPart(int fractionalPart)
{
float frac = (float)fractionalPart;
while (fractionalPart) {
fractionalPart /= 10;
frac /= 10.0f;
}
return frac;
}
static float computeFloat(int whole, int fraction) {
return (float)whole + computeFractionalPart(fraction);
}
AnimExpression::Token AnimExpression::consumeNumber(const QString& str, QString::const_iterator& iter) const {
assert(iter != str.end());
assert(iter->isDigit());
auto begin = iter;
while (iter->isDigit() && iter != str.end()) {
++iter;
}
// parse whole integer part
int pos = (int)(begin - str.begin());
int len = (int)(iter - begin);
QString sub = QStringRef(const_cast<const QString*>(&str), pos, len).toString();
int whole = sub.toInt();
// parse optional fractional part
if (iter->unicode() == '.') {
iter++;
auto begin = iter;
while (iter->isDigit() && iter != str.end()) {
++iter;
}
int pos = (int)(begin - str.begin());
int len = (int)(iter - begin);
QString sub = QStringRef(const_cast<const QString*>(&str), pos, len).toString();
int fraction = sub.toInt();
return Token(computeFloat(whole, fraction));
} else {
return Token(whole);
}
}
AnimExpression::Token AnimExpression::consumeAnd(const QString& str, QString::const_iterator& iter) const {
assert(iter != str.end());
assert(iter->unicode() == '&');
iter++;
if (iter->unicode() == '&') {
iter++;
return Token(Token::And);
} else {
qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin());
return Token(Token::Error);
}
}
AnimExpression::Token AnimExpression::consumeOr(const QString& str, QString::const_iterator& iter) const {
assert(iter != str.end());
assert(iter->unicode() == '|');
iter++;
if (iter->unicode() == '|') {
iter++;
return Token(Token::Or);
} else {
qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin());
return Token(Token::Error);
}
}
AnimExpression::Token AnimExpression::consumeGreaterThan(const QString& str, QString::const_iterator& iter) const {
assert(iter != str.end());
assert(iter->unicode() == '>');
iter++;
if (iter->unicode() == '=') {
iter++;
return Token(Token::GreaterThanEqual);
} else {
return Token(Token::GreaterThan);
}
}
AnimExpression::Token AnimExpression::consumeLessThan(const QString& str, QString::const_iterator& iter) const {
assert(iter != str.end());
assert(iter->unicode() == '<');
iter++;
if (iter->unicode() == '=') {
iter++;
return Token(Token::LessThanEqual);
} else {
return Token(Token::LessThan);
}
}
AnimExpression::Token AnimExpression::consumeNot(const QString& str, QString::const_iterator& iter) const {
assert(iter != str.end());
assert(iter->unicode() == '!');
iter++;
if (iter->unicode() == '=') {
iter++;
return Token(Token::NotEqual);
} else {
return Token(Token::Not);
}
}
//
// Parser
//
/*
Expr Term Expr'
Expr' '||' Term Expr'
| ε
Term Unary Term'
Term' '&&' Unary Term'
| ε
Unary '!' Unary
| Factor
Factor INT
| BOOL
| FLOAT
| IDENTIFIER
| '(' Expr ')'
*/
// Expr → Term Expr'
bool AnimExpression::parseExpr(const QString& str, QString::const_iterator& iter) {
if (!parseTerm(str, iter)) {
return false;
}
if (!parseExprPrime(str, iter)) {
return false;
}
return true;
}
// Expr' → '||' Term Expr' | ε
bool AnimExpression::parseExprPrime(const QString& str, QString::const_iterator& iter) {
auto token = consumeToken(str, iter);
if (token.type == Token::Or) {
if (!parseTerm(str, iter)) {
unconsumeToken(token);
return false;
}
if (!parseExprPrime(str, iter)) {
unconsumeToken(token);
return false;
}
_opCodes.push_back(OpCode {OpCode::Or});
return true;
} else {
unconsumeToken(token);
return true;
}
}
// Term → Unary Term'
bool AnimExpression::parseTerm(const QString& str, QString::const_iterator& iter) {
if (!parseUnary(str, iter)) {
return false;
}
if (!parseTermPrime(str, iter)) {
return false;
}
return true;
}
// Term' → '&&' Unary Term' | ε
bool AnimExpression::parseTermPrime(const QString& str, QString::const_iterator& iter) {
auto token = consumeToken(str, iter);
if (token.type == Token::And) {
if (!parseUnary(str, iter)) {
unconsumeToken(token);
return false;
}
if (!parseTermPrime(str, iter)) {
unconsumeToken(token);
return false;
}
_opCodes.push_back(OpCode {OpCode::And});
return true;
} else {
unconsumeToken(token);
return true;
}
}
// Unary → '!' Unary | Factor
bool AnimExpression::parseUnary(const QString& str, QString::const_iterator& iter) {
auto token = consumeToken(str, iter);
if (token.type == Token::Not) {
if (!parseUnary(str, iter)) {
unconsumeToken(token);
return false;
}
_opCodes.push_back(OpCode {OpCode::Not});
return true;
}
unconsumeToken(token);
return parseFactor(str, iter);
}
// Factor → INT | BOOL | FLOAT | IDENTIFIER | '(' Expr ')'
bool AnimExpression::parseFactor(const QString& str, QString::const_iterator& iter) {
auto token = consumeToken(str, iter);
if (token.type == Token::Int) {
_opCodes.push_back(OpCode {token.intVal});
return true;
} else if (token.type == Token::Bool) {
_opCodes.push_back(OpCode {(bool)token.intVal});
return true;
} else if (token.type == Token::Float) {
_opCodes.push_back(OpCode {token.floatVal});
return true;
} else if (token.type == Token::Identifier) {
_opCodes.push_back(OpCode {token.strVal});
return true;
} else if (token.type == Token::LeftParen) {
if (!parseExpr(str, iter)) {
unconsumeToken(token);
return false;
}
auto nextToken = consumeToken(str, iter);
if (nextToken.type != Token::RightParen) {
unconsumeToken(nextToken);
unconsumeToken(token);
return false;
}
return true;
} else {
unconsumeToken(token);
return false;
}
}
//
// Evaluator
//
AnimExpression::OpCode AnimExpression::evaluate(const AnimVariantMap& map) const {
std::stack<OpCode> stack;
for (auto& opCode : _opCodes) {
switch (opCode.type) {
case OpCode::Identifier:
case OpCode::Int:
case OpCode::Float:
case OpCode::Bool:
stack.push(opCode);
break;
case OpCode::And: evalAnd(map, stack); break;
case OpCode::Or: evalOr(map, stack); break;
case OpCode::GreaterThan: evalGreaterThan(map, stack); break;
case OpCode::GreaterThanEqual: evalGreaterThanEqual(map, stack); break;
case OpCode::LessThan: evalLessThan(map, stack); break;
case OpCode::LessThanEqual: evalLessThanEqual(map, stack); break;
case OpCode::Equal: evalEqual(map, stack); break;
case OpCode::NotEqual: evalNotEqual(map, stack); break;
case OpCode::Not: evalNot(map, stack); break;
case OpCode::Subtract: evalSubtract(map, stack); break;
case OpCode::Add: evalAdd(map, stack); break;
case OpCode::Multiply: evalMultiply(map, stack); break;
case OpCode::Divide: evalDivide(map, stack); break;
case OpCode::Modulus: evalModulus(map, stack); break;
case OpCode::UnaryMinus: evalUnaryMinus(map, stack); break;
}
}
return coerseToValue(map, stack.top());
}
#define POP_BOOL(NAME) \
const OpCode& NAME##_temp = stack.top(); \
bool NAME = NAME##_temp.coerceBool(map); \
stack.pop()
#define PUSH(EXPR) \
stack.push(OpCode {(EXPR)})
void AnimExpression::evalAnd(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
POP_BOOL(lhs);
POP_BOOL(rhs);
PUSH(lhs && rhs);
}
void AnimExpression::evalOr(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
POP_BOOL(lhs);
POP_BOOL(rhs);
PUSH(lhs || rhs);
}
void AnimExpression::evalGreaterThan(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
OpCode lhs = stack.top(); stack.pop();
OpCode rhs = stack.top(); stack.pop();
// TODO:
PUSH(false);
}
void AnimExpression::evalGreaterThanEqual(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
OpCode lhs = stack.top(); stack.pop();
OpCode rhs = stack.top(); stack.pop();
// TODO:
PUSH(false);
}
void AnimExpression::evalLessThan(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
OpCode lhs = stack.top(); stack.pop();
OpCode rhs = stack.top(); stack.pop();
// TODO:
PUSH(false);
}
void AnimExpression::evalLessThanEqual(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
OpCode lhs = stack.top(); stack.pop();
OpCode rhs = stack.top(); stack.pop();
// TODO:
PUSH(false);
}
void AnimExpression::evalEqual(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
OpCode lhs = stack.top(); stack.pop();
OpCode rhs = stack.top(); stack.pop();
// TODO:
PUSH(false);
}
void AnimExpression::evalNotEqual(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
OpCode lhs = stack.top(); stack.pop();
OpCode rhs = stack.top(); stack.pop();
// TODO:
PUSH(false);
}
void AnimExpression::evalNot(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
POP_BOOL(rhs);
PUSH(!rhs);
}
void AnimExpression::evalSubtract(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
OpCode lhs = stack.top(); stack.pop();
OpCode rhs = stack.top(); stack.pop();
// TODO:
PUSH(0.0f);
}
void AnimExpression::add(int lhs, const OpCode& rhs, std::stack<OpCode>& stack) const {
switch (rhs.type) {
case OpCode::Bool:
case OpCode::Int:
PUSH(lhs + rhs.intVal);
break;
case OpCode::Float:
PUSH((float)lhs + rhs.floatVal);
break;
default:
PUSH(lhs);
}
}
void AnimExpression::add(float lhs, const OpCode& rhs, std::stack<OpCode>& stack) const {
switch (rhs.type) {
case OpCode::Bool:
case OpCode::Int:
PUSH(lhs + (float)rhs.intVal);
break;
case OpCode::Float:
PUSH(lhs + rhs.floatVal);
break;
default:
PUSH(lhs);
}
}
void AnimExpression::evalAdd(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
OpCode lhs = coerseToValue(map, stack.top());
stack.pop();
OpCode rhs = coerseToValue(map, stack.top());
stack.pop();
switch (lhs.type) {
case OpCode::Bool:
add(lhs.intVal, rhs, stack);
break;
case OpCode::Int:
add(lhs.intVal, rhs, stack);
break;
case OpCode::Float:
add(lhs.floatVal, rhs, stack);
break;
default:
add(0, rhs, stack);
break;
}
}
void AnimExpression::evalMultiply(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
OpCode lhs = coerseToValue(map, stack.top());
stack.pop();
OpCode rhs = coerseToValue(map, stack.top());
stack.pop();
switch(lhs.type) {
case OpCode::Bool:
mul(lhs.intVal, rhs, stack);
break;
case OpCode::Int:
mul(lhs.intVal, rhs, stack);
break;
case OpCode::Float:
mul(lhs.floatVal, rhs, stack);
break;
default:
mul(0, rhs, stack);
break;
}
}
void AnimExpression::mul(int lhs, const OpCode& rhs, std::stack<OpCode>& stack) const {
switch (rhs.type) {
case OpCode::Bool:
case OpCode::Int:
PUSH(lhs * rhs.intVal);
break;
case OpCode::Float:
PUSH((float)lhs * rhs.floatVal);
break;
default:
PUSH(lhs);
}
}
void AnimExpression::mul(float lhs, const OpCode& rhs, std::stack<OpCode>& stack) const {
switch (rhs.type) {
case OpCode::Bool:
case OpCode::Int:
PUSH(lhs * (float)rhs.intVal);
break;
case OpCode::Float:
PUSH(lhs * rhs.floatVal);
break;
default:
PUSH(lhs);
}
}
void AnimExpression::evalDivide(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
OpCode lhs = stack.top(); stack.pop();
OpCode rhs = stack.top(); stack.pop();
// TODO:
PUSH(0.0f);
}
void AnimExpression::evalModulus(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
OpCode lhs = stack.top(); stack.pop();
OpCode rhs = stack.top(); stack.pop();
// TODO:
PUSH((int)0);
}
void AnimExpression::evalUnaryMinus(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
OpCode rhs = stack.top(); stack.pop();
switch (rhs.type) {
case OpCode::Identifier: {
const AnimVariant& var = map.get(rhs.strVal);
switch (var.getType()) {
case AnimVariant::Type::Bool:
qCWarning(animation) << "AnimExpression: type missmatch for unary minus, expected a number not a bool";
// interpret this as boolean not.
PUSH(!var.getBool());
break;
case AnimVariant::Type::Int:
PUSH(-var.getInt());
break;
case AnimVariant::Type::Float:
PUSH(-var.getFloat());
break;
default:
// TODO: Vec3, Quat are unsupported
assert(false);
PUSH(false);
break;
}
}
case OpCode::Int:
PUSH(-rhs.intVal);
break;
case OpCode::Float:
PUSH(-rhs.floatVal);
break;
case OpCode::Bool:
qCWarning(animation) << "AnimExpression: type missmatch for unary minus, expected a number not a bool";
// interpret this as boolean not.
PUSH(!rhs.coerceBool(map));
break;
default:
qCCritical(animation) << "AnimExpression: ERRROR for unary minus, expected a number, type = " << rhs.type;
assert(false);
PUSH(false);
break;
}
}
AnimExpression::OpCode AnimExpression::coerseToValue(const AnimVariantMap& map, const OpCode& opCode) const {
switch (opCode.type) {
case OpCode::Identifier:
{
const AnimVariant& var = map.get(opCode.strVal);
switch (var.getType()) {
case AnimVariant::Type::Bool:
return OpCode((bool)var.getBool());
break;
case AnimVariant::Type::Int:
return OpCode(var.getInt());
break;
case AnimVariant::Type::Float:
return OpCode(var.getFloat());
break;
default:
// TODO: Vec3, Quat are unsupported
assert(false);
return OpCode(0);
break;
}
}
break;
case OpCode::Bool:
case OpCode::Int:
case OpCode::Float:
return opCode;
default:
qCCritical(animation) << "AnimExpression: ERROR expected a number, type = " << opCode.type;
assert(false);
return OpCode(0);
break;
}
}
#ifndef NDEBUG
void AnimExpression::dumpOpCodes() const {
QString tmp;
for (auto& op : _opCodes) {
switch (op.type) {
case OpCode::Identifier: tmp += QString(" %1").arg(op.strVal); break;
case OpCode::Bool: tmp += QString(" %1").arg(op.intVal ? "true" : "false"); break;
case OpCode::Int: tmp += QString(" %1").arg(op.intVal); break;
case OpCode::Float: tmp += QString(" %1").arg(op.floatVal); break;
case OpCode::And: tmp += " &&"; break;
case OpCode::Or: tmp += " ||"; break;
case OpCode::GreaterThan: tmp += " >"; break;
case OpCode::GreaterThanEqual: tmp += " >="; break;
case OpCode::LessThan: tmp += " <"; break;
case OpCode::LessThanEqual: tmp += " <="; break;
case OpCode::Equal: tmp += " =="; break;
case OpCode::NotEqual: tmp += " !="; break;
case OpCode::Not: tmp += " !"; break;
case OpCode::Subtract: tmp += " -"; break;
case OpCode::Add: tmp += " +"; break;
case OpCode::Multiply: tmp += " *"; break;
case OpCode::Divide: tmp += " /"; break;
case OpCode::Modulus: tmp += " %"; break;
case OpCode::UnaryMinus: tmp += " unary-"; break;
default: tmp += " ???"; break;
}
}
qCDebug(animation).nospace().noquote() << "opCodes =" << tmp;
qCDebug(animation).resetFormat();
}
#endif

View file

@ -0,0 +1,158 @@
//
// AnimExpression.h
//
// Created by Anthony J. Thibault on 11/1/15.
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
//
// 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_AnimExpression
#define hifi_AnimExpression
#include <QString>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <stack>
#include <vector>
#include "AnimVariant.h"
class AnimExpression {
public:
friend class AnimTests;
AnimExpression(const QString& str);
protected:
struct Token {
enum Type {
End = 0,
Identifier,
Bool,
Int,
Float,
And,
Or,
GreaterThan,
GreaterThanEqual,
LessThan,
LessThanEqual,
Equal,
NotEqual,
LeftParen,
RightParen,
Not,
Minus,
Plus,
Multiply,
Divide,
Modulus,
Comma,
Error
};
Token(Type type) : type {type} {}
Token(const QStringRef& strRef) : type {Type::Identifier}, strVal {strRef.toString()} {}
explicit Token(int val) : type {Type::Int}, intVal {val} {}
explicit Token(bool val) : type {Type::Bool}, intVal {val} {}
explicit Token(float val) : type {Type::Float}, floatVal {val} {}
Type type {End};
QString strVal;
int intVal {0};
float floatVal {0.0f};
};
struct OpCode {
enum Type {
Identifier,
Bool,
Int,
Float,
And,
Or,
GreaterThan,
GreaterThanEqual,
LessThan,
LessThanEqual,
Equal,
NotEqual,
Not,
Subtract,
Add,
Multiply,
Divide,
Modulus,
UnaryMinus
};
OpCode(Type type) : type {type} {}
explicit OpCode(const QStringRef& strRef) : type {Type::Identifier}, strVal {strRef.toString()} {}
explicit OpCode(const QString& str) : type {Type::Identifier}, strVal {str} {}
explicit OpCode(int val) : type {Type::Int}, intVal {val} {}
explicit OpCode(bool val) : type {Type::Bool}, intVal {(int)val} {}
explicit OpCode(float val) : type {Type::Float}, floatVal {val} {}
bool coerceBool(const AnimVariantMap& map) const {
if (type == Int || type == Bool) {
return intVal != 0;
} else if (type == Identifier) {
return map.lookup(strVal, false);
} else {
return true;
}
}
Type type {Int};
QString strVal;
int intVal {0};
float floatVal {0.0f};
};
void unconsumeToken(const Token& token);
Token consumeToken(const QString& str, QString::const_iterator& iter) const;
Token consumeIdentifier(const QString& str, QString::const_iterator& iter) const;
Token consumeNumber(const QString& str, QString::const_iterator& iter) const;
Token consumeAnd(const QString& str, QString::const_iterator& iter) const;
Token consumeOr(const QString& str, QString::const_iterator& iter) const;
Token consumeGreaterThan(const QString& str, QString::const_iterator& iter) const;
Token consumeLessThan(const QString& str, QString::const_iterator& iter) const;
Token consumeNot(const QString& str, QString::const_iterator& iter) const;
bool parseExpr(const QString& str, QString::const_iterator& iter);
bool parseExprPrime(const QString& str, QString::const_iterator& iter);
bool parseTerm(const QString& str, QString::const_iterator& iter);
bool parseTermPrime(const QString& str, QString::const_iterator& iter);
bool parseUnary(const QString& str, QString::const_iterator& iter);
bool parseFactor(const QString& str, QString::const_iterator& iter);
OpCode evaluate(const AnimVariantMap& map) const;
void evalAnd(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
void evalOr(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
void evalGreaterThan(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
void evalGreaterThanEqual(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
void evalLessThan(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
void evalLessThanEqual(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
void evalEqual(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
void evalNotEqual(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
void evalNot(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
void evalSubtract(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
void evalAdd(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
void add(int lhs, const OpCode& rhs, std::stack<OpCode>& stack) const;
void add(float lhs, const OpCode& rhs, std::stack<OpCode>& stack) const;
void evalMultiply(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
void mul(int lhs, const OpCode& rhs, std::stack<OpCode>& stack) const;
void mul(float lhs, const OpCode& rhs, std::stack<OpCode>& stack) const;
void evalDivide(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
void evalModulus(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
void evalUnaryMinus(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
OpCode coerseToValue(const AnimVariantMap& map, const OpCode& opCode) const;
QString _expression;
mutable std::stack<Token> _tokenStack; // TODO: remove, only needed during parsing
std::vector<OpCode> _opCodes;
#ifndef NDEBUG
void dumpOpCodes() const;
#endif
};
#endif

View file

@ -15,6 +15,8 @@
#include <RegisteredMetaTypes.h>
#include "AnimVariant.h" // which has AnimVariant/AnimVariantMap
const AnimVariant AnimVariant::False = AnimVariant();
QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const {
if (QThread::currentThread() != engine->thread()) {
qCWarning(animation) << "Cannot create Javacript object from non-script thread" << QThread::currentThread();

View file

@ -34,6 +34,8 @@ public:
NumTypes
};
static const AnimVariant False;
AnimVariant() : _type(Type::Bool) { memset(&_val, 0, sizeof(_val)); }
AnimVariant(bool value) : _type(Type::Bool) { _val.boolVal = value; }
AnimVariant(int value) : _type(Type::Int) { _val.intVal = value; }
@ -57,13 +59,50 @@ public:
void setQuat(const glm::quat& value) { assert(_type == Type::Quat); *reinterpret_cast<glm::quat*>(&_val) = value; }
void setString(const QString& value) { assert(_type == Type::String); _stringVal = value; }
bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; }
int getInt() const { assert(_type == Type::Int || _type == Type::Float); return _type == Type::Float ? (int)_val.floats[0] : _val.intVal; }
float getFloat() const { assert(_type == Type::Float || _type == Type::Int); return _type == Type::Int ? (float)_val.intVal : _val.floats[0]; }
const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast<const glm::vec3*>(&_val); }
const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast<const glm::quat*>(&_val); }
const QString& getString() const { assert(_type == Type::String); return _stringVal; }
bool getBool() const {
if (_type == Type::Bool) {
return _val.boolVal;
} else if (_type == Type::Int) {
return _val.intVal != 0;
} else {
return false;
}
}
int getInt() const {
if (_type == Type::Int) {
return _val.intVal;
} else if (_type == Type::Float) {
return (int)_val.floats[0];
} else {
return 0;
}
}
float getFloat() const {
if (_type == Type::Float) {
return _val.floats[0];
} else if (_type == Type::Int) {
return (float)_val.intVal;
} else {
return 0.0f;
}
}
const glm::vec3& getVec3() const {
if (_type == Type::Vec3) {
return *reinterpret_cast<const glm::vec3*>(&_val);
} else {
return Vectors::ZERO;
}
}
const glm::quat& getQuat() const {
if (_type == Type::Quat) {
return *reinterpret_cast<const glm::quat*>(&_val);
} else {
return Quaternions::IDENTITY;
}
}
const QString& getString() const {
return _stringVal;
}
protected:
Type _type;
@ -71,7 +110,7 @@ protected:
union {
bool boolVal;
int intVal;
float floats[16];
float floats[4];
} _val;
};
@ -172,6 +211,15 @@ public:
void clearMap() { _map.clear(); }
bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); }
const AnimVariant& get(const QString& key) const {
auto iter = _map.find(key);
if (iter != _map.end()) {
return iter->second;
} else {
return AnimVariant::False;
}
}
// Answer a Plain Old Javascript Object (for the given engine) all of our values set as properties.
QScriptValue animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const;
// Side-effect us with the value of object's own properties. (No inherited properties.)

View file

@ -289,8 +289,10 @@ void Rig::clearJointState(int index) {
void Rig::clearJointStates() {
_internalPoseSet._overrideFlags.clear();
_internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints());
_internalPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses();
if (_animSkeleton) {
_internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints());
_internalPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses();
}
}
void Rig::clearJointAnimationPriority(int index) {

View file

@ -88,15 +88,73 @@ namespace controller {
// No correlation to SDL
enum StandardPoseChannel {
LEFT_HAND = 0,
RIGHT_HAND,
HIPS = 0,
RIGHT_UP_LEG,
RIGHT_LEG,
RIGHT_FOOT,
LEFT_UP_LEG,
LEFT_LEG,
LEFT_FOOT,
SPINE,
SPINE1,
SPINE2,
SPINE3,
NECK,
HEAD,
RIGHT_SHOULDER,
RIGHT_ARM,
RIGHT_FORE_ARM,
RIGHT_HAND,
RIGHT_HAND_THUMB1,
RIGHT_HAND_THUMB2,
RIGHT_HAND_THUMB3,
RIGHT_HAND_THUMB4,
RIGHT_HAND_INDEX1,
RIGHT_HAND_INDEX2,
RIGHT_HAND_INDEX3,
RIGHT_HAND_INDEX4,
RIGHT_HAND_MIDDLE1,
RIGHT_HAND_MIDDLE2,
RIGHT_HAND_MIDDLE3,
RIGHT_HAND_MIDDLE4,
RIGHT_HAND_RING1,
RIGHT_HAND_RING2,
RIGHT_HAND_RING3,
RIGHT_HAND_RING4,
RIGHT_HAND_PINKY1,
RIGHT_HAND_PINKY2,
RIGHT_HAND_PINKY3,
RIGHT_HAND_PINKY4,
LEFT_SHOULDER,
LEFT_ARM,
LEFT_FORE_ARM,
LEFT_HAND,
LEFT_HAND_THUMB1,
LEFT_HAND_THUMB2,
LEFT_HAND_THUMB3,
LEFT_HAND_THUMB4,
LEFT_HAND_INDEX1,
LEFT_HAND_INDEX2,
LEFT_HAND_INDEX3,
LEFT_HAND_INDEX4,
LEFT_HAND_MIDDLE1,
LEFT_HAND_MIDDLE2,
LEFT_HAND_MIDDLE3,
LEFT_HAND_MIDDLE4,
LEFT_HAND_RING1,
LEFT_HAND_RING2,
LEFT_HAND_RING3,
LEFT_HAND_RING4,
LEFT_HAND_PINKY1,
LEFT_HAND_PINKY2,
LEFT_HAND_PINKY3,
LEFT_HAND_PINKY4,
NUM_STANDARD_POSES
};
enum StandardCounts {
TRIGGERS = 2,
ANALOG_STICKS = 2,
POSES = 2, // FIXME 3? if we want to expose the head?
POSES = NUM_STANDARD_POSES
};
}

View file

@ -487,7 +487,8 @@ void EntityTreeRenderer::deleteReleasedModels() {
}
RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude) {
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard) {
RayToEntityIntersectionResult result;
if (_tree) {
EntityTreePointer entityTree = std::static_pointer_cast<EntityTree>(_tree);
@ -495,7 +496,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, entityIdsToInclude,
result.face, result.surfaceNormal, entityIdsToInclude, entityIdsToDiscard,
(void**)&intersectedEntity, lockType, &result.accurate,
precisionPicking);
if (result.intersects && intersectedEntity) {

View file

@ -130,7 +130,8 @@ private:
QList<Model*> _releasedModels;
RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude = QVector<EntityItemID>());
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude = QVector<EntityItemID>(),
const QVector<EntityItemID>& entityIdsToDiscard = QVector<EntityItemID>());
EntityItemID _currentHoverOverEntityID;
EntityItemID _currentClickingOnEntityID;

View file

@ -245,8 +245,11 @@ void RenderableParticleEffectEntityItem::updateRenderItem() {
return;
}
Transform transform;
transform.setTranslation(position);
transform.setRotation(rotation);
if (!getEmitterShouldTrail()) {
transform.setTranslation(position);
transform.setRotation(rotation);
}
render::PendingChanges pendingChanges;
pendingChanges.updateItem<ParticlePayloadData>(_renderItemId, [=](ParticlePayloadData& payload) {

View file

@ -23,6 +23,13 @@
#include "paintStroke_frag.h"
struct PolyLineUniforms {
glm::vec3 color;
};
EntityItemPointer RenderablePolyLineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
EntityItemPointer entity{ new RenderablePolyLineEntityItem(entityID) };
entity->setProperties(properties);
@ -30,9 +37,12 @@ EntityItemPointer RenderablePolyLineEntityItem::factory(const EntityItemID& enti
}
RenderablePolyLineEntityItem::RenderablePolyLineEntityItem(const EntityItemID& entityItemID) :
PolyLineEntityItem(entityItemID) {
_numVertices = 0;
PolyLineEntityItem(entityItemID),
_numVertices(0)
{
_vertices = QVector<glm::vec3>(0.0f);
PolyLineUniforms uniforms;
_uniformBuffer = std::make_shared<gpu::Buffer>(sizeof(PolyLineUniforms), (const gpu::Byte*) &uniforms);
}
gpu::PipelinePointer RenderablePolyLineEntityItem::_pipeline;
@ -41,13 +51,11 @@ int32_t RenderablePolyLineEntityItem::PAINTSTROKE_GPU_SLOT;
void RenderablePolyLineEntityItem::createPipeline() {
static const int NORMAL_OFFSET = 12;
static const int COLOR_OFFSET = 24;
static const int TEXTURE_OFFSET = 28;
static const int TEXTURE_OFFSET = 24;
_format.reset(new gpu::Stream::Format());
_format->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
_format->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), NORMAL_OFFSET);
_format->setAttribute(gpu::Stream::COLOR, 0, gpu::Element::COLOR_RGBA_32, COLOR_OFFSET);
_format->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), TEXTURE_OFFSET);
auto VS = gpu::Shader::createVertex(std::string(paintStroke_vert));
@ -72,47 +80,28 @@ void RenderablePolyLineEntityItem::updateGeometry() {
_verticesBuffer.reset(new gpu::Buffer());
int vertexIndex = 0;
vec2 uv;
float tailStart = 0.0f;
float tailEnd = 0.25f;
float tailLength = tailEnd - tailStart;
float headStart = 0.76f;
float headEnd = 1.0f;
float headLength = headEnd - headStart;
float uCoord, vCoord;
int numTailStrips = 5;
int numHeadStrips = 10;
int startHeadIndex = _vertices.size() / 2 - numHeadStrips;
uCoord = 0.0f;
float uCoordInc = 1.0 / (_vertices.size() / 2);
for (int i = 0; i < _vertices.size() / 2; i++) {
uCoord = 0.26f;
vCoord = 0.0f;
//tail
if (i < numTailStrips) {
uCoord = float(i) / numTailStrips * tailLength + tailStart;
}
//head
if (i > startHeadIndex) {
uCoord = float((i + 1) - startHeadIndex) / numHeadStrips * headLength + headStart;
}
uv = vec2(uCoord, vCoord);
_verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_vertices.at(vertexIndex));
_verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_normals.at(i));
_verticesBuffer->append(sizeof(int), (gpu::Byte*)&_color);
_verticesBuffer->append(sizeof(glm::vec2), (gpu::Byte*)&uv);
vertexIndex++;
uv.y = 1.0f;
_verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_vertices.at(vertexIndex));
_verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_normals.at(i));
_verticesBuffer->append(sizeof(int), (gpu::Byte*)_color);
_verticesBuffer->append(sizeof(glm::vec2), (const gpu::Byte*)&uv);
vertexIndex++;
_numVertices += 2;
uCoord += uCoordInc;
}
_pointsChanged = false;
_normalsChanged = false;
@ -152,6 +141,11 @@ void RenderablePolyLineEntityItem::updateVertices() {
_vertices << v1 << v2;
}
// Guard against an empty polyline
if (finalIndex < 0) {
return;
}
// For last point we can assume binormals are the same since it represents the last two vertices of quad
point = _points.at(finalIndex);
v1 = point + binormal;
@ -161,6 +155,16 @@ void RenderablePolyLineEntityItem::updateVertices() {
}
void RenderablePolyLineEntityItem::update(const quint64& now) {
PolyLineUniforms uniforms;
uniforms.color = toGlm(getXColor());
memcpy(&_uniformBuffer.edit<PolyLineUniforms>(), &uniforms, sizeof(PolyLineUniforms));
if (_pointsChanged || _strokeWidthsChanged || _normalsChanged) {
updateVertices();
updateGeometry();
}
}
void RenderablePolyLineEntityItem::render(RenderArgs* args) {
QWriteLocker lock(&_quadReadWriteLock);
@ -181,17 +185,13 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderablePolyLineEntityItem::render");
Q_ASSERT(getType() == EntityTypes::PolyLine);
Q_ASSERT(args->_batch);
if (_pointsChanged || _strokeWidthsChanged || _normalsChanged) {
updateVertices();
updateGeometry();
}
gpu::Batch& batch = *args->_batch;
Transform transform = Transform();
transform.setTranslation(getPosition());
transform.setRotation(getRotation());
batch.setUniformBuffer(0, _uniformBuffer);
batch.setModelTransform(transform);
batch.setPipeline(_pipeline);
@ -200,7 +200,7 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) {
} else {
batch.setResourceTexture(PAINTSTROKE_GPU_SLOT, args->_whiteTexture);
}
batch.setInputFormat(_format);
batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride);

View file

@ -29,6 +29,8 @@ public:
RenderablePolyLineEntityItem(const EntityItemID& entityItemID);
virtual void render(RenderArgs* args);
virtual void update(const quint64& now) override;
virtual bool needsToCallUpdate() const { return true; };
SIMPLE_RENDERABLE();
@ -42,6 +44,7 @@ protected:
void updateGeometry();
void updateVertices();
gpu::BufferPointer _verticesBuffer;
gpu::BufferView _uniformBuffer;
unsigned int _numVertices;
QVector<glm::vec3> _vertices;

View file

@ -23,8 +23,6 @@ layout(location = 0) out vec4 _fragColor0;
layout(location = 1) out vec4 _fragColor1;
layout(location = 2) out vec4 _fragColor2;
// the glow intensity
uniform float glowIntensity;
// the alpha threshold
uniform float alphaThreshold;
uniform sampler2D normalFittingMap;
@ -318,8 +316,8 @@ const QString SHADER_TEMPLATE_V1 = SHADER_COMMON + R"SCRIBE(
void main(void) {
vec4 emissive = getProceduralColor();
float alpha = glowIntensity * emissive.a;
if (alpha != glowIntensity) {
float alpha = emissive.a;
if (alpha != 1.0) {
discard;
}

View file

@ -23,23 +23,24 @@ in vec3 interpolatedNormal;
in vec2 varTexcoord;
in vec4 varColor;
float rand(vec2 point){
return fract(sin(dot(point.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
struct PolyLineUniforms {
vec3 color;
};
uniform polyLineBuffer {
PolyLineUniforms polyline;
};
void main(void) {
vec4 texel = texture(originalTexture, varTexcoord);
int frontCondition = 1 -int(gl_FrontFacing) * 2;
vec3 color = varColor.rgb;
//vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess
vec3 color = varColor.rgb;
packDeferredFragmentTranslucent(
interpolatedNormal * frontCondition,
texel.a,
color *texel.rgb,
polyline.color * texel.rgb,
vec3(0.01, 0.01, 0.01),
10.0);
}

View file

@ -32,6 +32,7 @@ void main(void) {
// pass along the diffuse color
varColor = colorToLinearRGBA(inColor);
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();

View file

@ -195,7 +195,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_ALPHA_SPREAD, alphaSpread);
CHECK_PROPERTY_CHANGE(PROP_ALPHA_START, alphaStart);
CHECK_PROPERTY_CHANGE(PROP_ALPHA_FINISH, alphaFinish);
CHECK_PROPERTY_CHANGE(PROP_ADDITIVE_BLENDING, additiveBlending);
CHECK_PROPERTY_CHANGE(PROP_EMITTER_SHOULD_TRAIL, emitterShouldTrail);
CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL);
CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL);
CHECK_PROPERTY_CHANGE(PROP_VISIBLE, visible);
@ -355,7 +355,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_SPREAD, alphaSpread);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_START, alphaStart);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_FINISH, alphaFinish);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ADDITIVE_BLENDING, additiveBlending);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMITTER_SHOULD_TRAIL, emitterShouldTrail);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_POSITION, localPosition);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ROTATION, localRotation);
}
@ -519,7 +519,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaSpread, float, setAlphaSpread);
COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaStart, float, setAlphaStart);
COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaFinish, float, setAlphaFinish);
COPY_PROPERTY_FROM_QSCRIPTVALUE(additiveBlending, bool, setAdditiveBlending);
COPY_PROPERTY_FROM_QSCRIPTVALUE(emitterShouldTrail , bool, setEmitterShouldTrail);
COPY_PROPERTY_FROM_QSCRIPTVALUE(modelURL, QString, setModelURL);
COPY_PROPERTY_FROM_QSCRIPTVALUE(compoundShapeURL, QString, setCompoundShapeURL);
COPY_PROPERTY_FROM_QSCRIPTVALUE(glowLevel, float, setGlowLevel);
@ -675,7 +675,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
ADD_PROPERTY_TO_MAP(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float);
ADD_PROPERTY_TO_MAP(PROP_ALPHA_START, AlphaStart, alphaStart, float);
ADD_PROPERTY_TO_MAP(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, float);
ADD_PROPERTY_TO_MAP(PROP_ADDITIVE_BLENDING, AdditiveBlending, additiveBlending, bool);
ADD_PROPERTY_TO_MAP(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool);
ADD_PROPERTY_TO_MAP(PROP_MODEL_URL, ModelURL, modelURL, QString);
ADD_PROPERTY_TO_MAP(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString);
ADD_PROPERTY_TO_MAP(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, glm::vec3);
@ -986,7 +986,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
APPEND_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, properties.getAlphaSpread());
APPEND_ENTITY_PROPERTY(PROP_ALPHA_START, properties.getAlphaStart());
APPEND_ENTITY_PROPERTY(PROP_ALPHA_FINISH, properties.getAlphaFinish());
APPEND_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, properties.getAdditiveBlending());
APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, properties.getEmitterShouldTrail());
}
if (properties.getType() == EntityTypes::Zone) {
@ -1272,7 +1272,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_SPREAD, float, setAlphaSpread);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_START, float, setAlphaStart);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_FINISH, float, setAlphaFinish);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ADDITIVE_BLENDING, bool, setAdditiveBlending);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail);
}
if (properties.getType() == EntityTypes::Zone) {
@ -1591,8 +1591,8 @@ QList<QString> EntityItemProperties::listChangedProperties() {
if (alphaFinishChanged()) {
out += "alphaFinish";
}
if (additiveBlendingChanged()) {
out += "additiveBlending";
if (emitterShouldTrailChanged()) {
out += "emitterShouldTrail";
}
if (modelURLChanged()) {
out += "modelURL";

View file

@ -161,7 +161,7 @@ public:
DEFINE_PROPERTY(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float, ParticleEffectEntityItem::DEFAULT_RADIUS_SPREAD);
DEFINE_PROPERTY(PROP_RADIUS_START, RadiusStart, radiusStart, float, ParticleEffectEntityItem::DEFAULT_RADIUS_START);
DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH);
DEFINE_PROPERTY(PROP_ADDITIVE_BLENDING, AdditiveBlending, additiveBlending, bool, ParticleEffectEntityItem::DEFAULT_ADDITIVE_BLENDING);
DEFINE_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool, ParticleEffectEntityItem::DEFAULT_EMITTER_SHOULD_TRAIL);
DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID);
DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup);
DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE);

View file

@ -149,7 +149,7 @@ enum EntityPropertyList {
PROP_ANIMATION_HOLD,
PROP_ANIMATION_START_AUTOMATICALLY,
PROP_ADDITIVE_BLENDING,
PROP_EMITTER_SHOULD_TRAIL,
PROP_PARENT_ID,
PROP_PARENT_JOINT_INDEX,

View file

@ -383,19 +383,21 @@ QVector<QUuid> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn
return result;
}
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude) {
QVector<EntityItemID> entities = qVectorEntityItemIDFromScriptValue(entityIdsToInclude);
return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking, entities);
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) {
QVector<EntityItemID> entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude);
QVector<EntityItemID> entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard);
return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking, entitiesToInclude, entitiesToDiscard);
}
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude) {
const QVector<EntityItemID>& entities = qVectorEntityItemIDFromScriptValue(entityIdsToInclude);
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entities);
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) {
const QVector<EntityItemID>& entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude);
const QVector<EntityItemID> entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard);
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entitiesToInclude, entitiesToDiscard);
}
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray,
Octree::lockType lockType,
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude) {
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard) {
RayToEntityIntersectionResult result;
@ -403,7 +405,7 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke
OctreeElementPointer element;
EntityItemPointer intersectedEntity = NULL;
result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
result.surfaceNormal, entityIdsToInclude, (void**)&intersectedEntity, lockType, &result.accurate,
result.surfaceNormal, entityIdsToInclude, entityIdsToDiscard, (void**)&intersectedEntity, lockType, &result.accurate,
precisionPicking);
if (result.intersects && intersectedEntity) {
result.entityID = intersectedEntity->getEntityItemID();

View file

@ -112,11 +112,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, const QScriptValue& entityIdsToInclude = QScriptValue());
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = 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, const QScriptValue& entityIdsToInclude = QScriptValue());
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue());
Q_INVOKABLE void setLightsArePickable(bool value);
Q_INVOKABLE bool getLightsArePickable() const;
@ -189,7 +189,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, const QVector<EntityItemID>& entityIdsToInclude);
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard);
EntityTreePointer _entityTree;
EntitiesScriptEngineProvider* _entitiesScriptEngine = nullptr;

View file

@ -514,6 +514,7 @@ public:
BoxFace& face;
glm::vec3& surfaceNormal;
const QVector<EntityItemID>& entityIdsToInclude;
const QVector<EntityItemID>& entityIdsToDiscard;
void** intersectedObject;
bool found;
bool precisionPicking;
@ -526,7 +527,7 @@ bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) {
EntityTreeElementPointer entityTreeElementPointer = std::dynamic_pointer_cast<EntityTreeElement>(element);
if (entityTreeElementPointer ->findRayIntersection(args->origin, args->direction, keepSearching,
args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude,
args->intersectedObject, args->precisionPicking)) {
args->entityIdsToDiscard, args->intersectedObject, args->precisionPicking)) {
args->found = true;
}
return keepSearching;
@ -534,9 +535,9 @@ bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) {
bool EntityTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude, void** intersectedObject,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard, void** intersectedObject,
Octree::lockType lockType, bool* accurateResult, bool precisionPicking) {
RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, intersectedObject, false, precisionPicking };
RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, entityIdsToDiscard, intersectedObject, false, precisionPicking };
distance = FLT_MAX;
bool requireLock = lockType == Octree::Lock;

View file

@ -84,6 +84,7 @@ public:
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElementPointer& node, float& distance, BoxFace& face, glm::vec3& surfaceNormal,
const QVector<EntityItemID>& entityIdsToInclude = QVector<EntityItemID>(),
const QVector<EntityItemID>& entityIdsToDiscard = QVector<EntityItemID>(),
void** intersectedObject = NULL,
Octree::lockType lockType = Octree::TryLock,
bool* accurateResult = NULL,

View file

@ -486,8 +486,8 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3
bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude,
void** intersectedObject, bool precisionPicking) {
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard, void** intersectedObject, bool precisionPicking) {
keepSearching = true; // assume that we will continue searching after this.
@ -512,7 +512,7 @@ bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm::
if (_cube.contains(origin) || distanceToElementCube < distance) {
if (findDetailedRayIntersection(origin, direction, keepSearching, element, distanceToElementDetails,
face, localSurfaceNormal, entityIdsToInclude, intersectedObject, precisionPicking, distanceToElementCube)) {
face, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, intersectedObject, precisionPicking, distanceToElementCube)) {
if (distanceToElementDetails < distance) {
distance = distanceToElementDetails;
@ -527,14 +527,13 @@ bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm::
bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching,
OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal,
const QVector<EntityItemID>& entityIdsToInclude, void** intersectedObject,
bool precisionPicking, float distanceToElementCube) {
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIDsToDiscard, 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())) {
if ( (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) || (entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID())) ) {
return;
}

View file

@ -144,11 +144,13 @@ public:
virtual bool canRayIntersect() const { return hasEntities(); }
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElementPointer& node, float& distance,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard,
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, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard,
void** intersectedObject, bool precisionPicking, float distanceToElementCube);
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
glm::vec3& penetration, void** penetratedObject) const;

View file

@ -252,9 +252,11 @@ void ModelEntityItem::getAnimationFrame(bool& newFrame,
if (index < translations.size()) {
translationMat = glm::translate(translations[index]);
}
glm::mat4 rotationMat;
glm::mat4 rotationMat(glm::mat4::_null);
if (index < rotations.size()) {
rotationMat = glm::mat4_cast(rotations[index]);
rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * rotations[index] * fbxJoints[index].postRotation);
} else {
rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * fbxJoints[index].postRotation);
}
glm::mat4 finalMat = (translationMat * fbxJoints[index].preTransform *
rotationMat * fbxJoints[index].postTransform);

View file

@ -52,13 +52,13 @@ const float ParticleEffectEntityItem::MINIMUM_ALPHA = 0.0f;
const float ParticleEffectEntityItem::MAXIMUM_ALPHA = 1.0f;
const quint32 ParticleEffectEntityItem::DEFAULT_MAX_PARTICLES = 1000;
const quint32 ParticleEffectEntityItem::MINIMUM_MAX_PARTICLES = 1;
const quint32 ParticleEffectEntityItem::MAXIMUM_MAX_PARTICLES = 10000;
const quint32 ParticleEffectEntityItem::MAXIMUM_MAX_PARTICLES = 100000;
const float ParticleEffectEntityItem::DEFAULT_LIFESPAN = 3.0f;
const float ParticleEffectEntityItem::MINIMUM_LIFESPAN = 0.0f;
const float ParticleEffectEntityItem::MAXIMUM_LIFESPAN = 86400.0f; // 1 day
const float ParticleEffectEntityItem::DEFAULT_EMIT_RATE = 15.0f;
const float ParticleEffectEntityItem::MINIMUM_EMIT_RATE = 0.0f;
const float ParticleEffectEntityItem::MAXIMUM_EMIT_RATE = 1000.0f;
const float ParticleEffectEntityItem::MAXIMUM_EMIT_RATE = 100000.0f;
const float ParticleEffectEntityItem::DEFAULT_EMIT_SPEED = 5.0f;
const float ParticleEffectEntityItem::MINIMUM_EMIT_SPEED = 0.0f;
const float ParticleEffectEntityItem::MAXIMUM_EMIT_SPEED = 1000.0f; // Approx mach 3
@ -91,7 +91,7 @@ const float ParticleEffectEntityItem::DEFAULT_RADIUS_SPREAD = 0.0f;
const float ParticleEffectEntityItem::DEFAULT_RADIUS_START = DEFAULT_PARTICLE_RADIUS;
const float ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH = DEFAULT_PARTICLE_RADIUS;
const QString ParticleEffectEntityItem::DEFAULT_TEXTURES = "";
const bool ParticleEffectEntityItem::DEFAULT_ADDITIVE_BLENDING = false;
const bool ParticleEffectEntityItem::DEFAULT_EMITTER_SHOULD_TRAIL = false;
EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
@ -331,7 +331,7 @@ EntityItemProperties ParticleEffectEntityItem::getProperties(EntityPropertyFlags
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaStart, getAlphaStart);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaFinish, getAlphaFinish);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(additiveBlending, getAdditiveBlending);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitterShouldTrail, getEmitterShouldTrail);
return properties;
@ -370,7 +370,7 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaStart, setAlphaStart);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaFinish, setAlphaFinish);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(additiveBlending, setAdditiveBlending);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitterShouldTrail, setEmitterShouldTrail);
if (somethingChanged) {
bool wantDebug = false;
@ -463,7 +463,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
}
if (args.bitstreamVersion >= VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING) {
READ_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, bool, setAdditiveBlending);
READ_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail);
}
return bytesRead;
@ -503,7 +503,7 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea
requestedProperties += PROP_POLAR_FINISH;
requestedProperties += PROP_AZIMUTH_START;
requestedProperties += PROP_AZIMUTH_FINISH;
requestedProperties += PROP_ADDITIVE_BLENDING;
requestedProperties += PROP_EMITTER_SHOULD_TRAIL;
return requestedProperties;
}
@ -546,7 +546,7 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData,
APPEND_ENTITY_PROPERTY(PROP_POLAR_FINISH, getPolarFinish());
APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_START, getAzimuthStart());
APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, getAzimuthFinish());
APPEND_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, getAdditiveBlending());
APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, getEmitterShouldTrail());
}
bool ParticleEffectEntityItem::isEmittingParticles() const {
@ -641,13 +641,17 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) {
ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() {
Particle particle;
particle.seed = randFloatInRange(0.0f, 1.0f);
particle.seed = randFloatInRange(-1.0f, 1.0f);
if (getEmitterShouldTrail()) {
particle.position = getPosition();
}
// Position, velocity, and acceleration
if (_polarStart == 0.0f && _polarFinish == 0.0f && _emitDimensions.z == 0.0f) {
// Emit along z-axis from position
particle.velocity = (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * Vectors::UNIT_Z);
particle.velocity = (_emitSpeed + 0.2f * _speedSpread) * (_emitOrientation * Vectors::UNIT_Z);
particle.acceleration = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread;
} else {
@ -658,11 +662,12 @@ ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() {
float elevationMinZ = sin(PI_OVER_TWO - _polarFinish);
float elevationMaxZ = sin(PI_OVER_TWO - _polarStart);
float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat());
// float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat());
float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) *randFloat());
float azimuth;
if (_azimuthFinish >= _azimuthStart) {
azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * randFloat();
azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * randFloat();
} else {
azimuth = _azimuthStart + (TWO_PI + _azimuthFinish - _azimuthStart) * randFloat();
}
@ -693,7 +698,12 @@ ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() {
radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f
));
particle.position = _emitOrientation * emitPosition;
if (getEmitterShouldTrail()) {
particle.position += _emitOrientation * emitPosition;
}
else {
particle.position = _emitOrientation * emitPosition;
}
}
particle.velocity = (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * emitDirection);

View file

@ -213,10 +213,10 @@ public:
}
}
static const bool DEFAULT_ADDITIVE_BLENDING;
bool getAdditiveBlending() const { return _additiveBlending; }
void setAdditiveBlending(bool additiveBlending) {
_additiveBlending = additiveBlending;
static const bool DEFAULT_EMITTER_SHOULD_TRAIL;
bool getEmitterShouldTrail() const { return _emitterShouldTrail; }
void setEmitterShouldTrail(bool emitterShouldTrail) {
_emitterShouldTrail = emitterShouldTrail;
}
virtual bool supportsDetailedRayIntersection() const { return false; }
@ -286,7 +286,7 @@ protected:
float _timeUntilNextEmit { 0.0f };
bool _additiveBlending { DEFAULT_ADDITIVE_BLENDING };
bool _emitterShouldTrail { DEFAULT_EMITTER_SHOULD_TRAIL };
};
#endif // hifi_ParticleEffectEntityItem_h

View file

@ -89,6 +89,7 @@ bool PolyLineEntityItem::setProperties(const EntityItemProperties& properties) {
return somethingChanged;
}
bool PolyLineEntityItem::appendPoint(const glm::vec3& point) {
if (_points.size() > MAX_POINTS_PER_LINE - 1) {
qDebug() << "MAX POINTS REACHED!";
@ -104,6 +105,7 @@ bool PolyLineEntityItem::appendPoint(const glm::vec3& point) {
return true;
}
bool PolyLineEntityItem::setStrokeWidths(const QVector<float>& strokeWidths) {
_strokeWidths = strokeWidths;
_strokeWidthsChanged = true;

View file

@ -49,7 +49,7 @@ class PolyLineEntityItem : public EntityItem {
memcpy(_color, value, sizeof(_color));
}
void setColor(const xColor& value) {
_color[RED_INDEX] = value.red;
_color[GREEN_INDEX] = value.green;
_color[BLUE_INDEX] = value.blue;
@ -75,7 +75,9 @@ class PolyLineEntityItem : public EntityItem {
_texturesChangedFlag = true;
}
}
virtual bool needsToCallUpdate() const { return true; }
virtual ShapeType getShapeType() const { return SHAPE_TYPE_LINE; }
// never have a ray intersection pick a PolyLineEntityItem.

View file

@ -96,7 +96,12 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
}
});
// Render
gpu::TexturePointer skymap = skybox.getCubemap();
// FIXME: skymap->isDefined may not be threadsafe
assert(skymap && skymap->isDefined());
glm::mat4 projMat;
viewFrustum.evalProjectionMatrix(projMat);
@ -106,11 +111,6 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
batch.setViewTransform(viewTransform);
batch.setModelTransform(Transform()); // only for Mac
gpu::TexturePointer skymap;
if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) {
skymap = skybox.getCubemap();
}
batch.setPipeline(thePipeline);
batch.setUniformBuffer(SKYBOX_CONSTANTS_SLOT, skybox._dataBuffer);
batch.setResourceTexture(SKYBOX_SKYMAP_SLOT, skymap);
@ -118,6 +118,5 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
batch.draw(gpu::TRIANGLE_STRIP, 4);
batch.setResourceTexture(SKYBOX_SKYMAP_SLOT, nullptr);
}

View file

@ -234,6 +234,7 @@ void NodeList::addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes)
void NodeList::sendDomainServerCheckIn() {
if (_isShuttingDown) {
qCDebug(networking) << "Refusing to send a domain-server check in while shutting down.";
return;
}
if (_publicSockAddr.isNull()) {

View file

@ -48,6 +48,10 @@ void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum,
}
if (skybox._procedural && skybox._procedural->_enabled && skybox._procedural->ready()) {
gpu::TexturePointer skymap = skybox.getCubemap();
// FIXME: skymap->isDefined may not be threadsafe
assert(skymap && skymap->isDefined());
glm::mat4 projMat;
viewFrustum.evalProjectionMatrix(projMat);
@ -56,10 +60,7 @@ void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum,
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewTransform);
batch.setModelTransform(Transform()); // only for Mac
if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) {
batch.setResourceTexture(0, skybox.getCubemap());
}
batch.setResourceTexture(0, skybox.getCubemap());
skybox._procedural->prepare(batch, glm::vec3(0), glm::vec3(1));
batch.draw(gpu::TRIANGLE_STRIP, 4);

View file

@ -56,7 +56,7 @@ static const std::string DEEFAULT_ROUGHNESS_SHADER {
};
static const std::string DEEFAULT_NORMAL_SHADER {
"vec4 getFragmentColor() {"
" return vec4(texture(normalMap, uv).xyz, 1.0);"
" return vec4(normalize(texture(normalMap, uv).xyz), 1.0);"
" }"
};
static const std::string DEEFAULT_DEPTH_SHADER {

View file

@ -15,12 +15,6 @@ layout(location = 0) out vec4 _fragColor0;
layout(location = 1) out vec4 _fragColor1;
layout(location = 2) out vec4 _fragColor2;
// the glow intensity
uniform float glowIntensity;
// the alpha threshold
uniform float alphaThreshold;
uniform sampler2D normalFittingMap;
vec3 bestFitNormal(vec3 normal) {
@ -39,15 +33,18 @@ vec3 bestFitNormal(vec3 normal) {
return (cN * 0.5 + 0.5);
}
// the alpha threshold
const float alphaThreshold = 0.5;
float evalOpaqueFinalAlpha(float alpha, float mapAlpha) {
return mix(alpha * glowIntensity, 1.0 - alpha * glowIntensity, step(mapAlpha, alphaThreshold));
return mix(alpha, 1.0 - alpha, step(mapAlpha, alphaThreshold));
}
const vec3 DEFAULT_SPECULAR = vec3(0.1);
const float DEFAULT_SHININESS = 10;
void packDeferredFragment(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess) {
if (alpha != glowIntensity) {
if (alpha != 1.0) {
discard;
}
@ -57,7 +54,7 @@ void packDeferredFragment(vec3 normal, float alpha, vec3 diffuse, vec3 specular,
}
void packDeferredFragmentLightmap(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess, vec3 emissive) {
if (alpha != glowIntensity) {
if (alpha != 1.0) {
discard;
}
@ -67,7 +64,7 @@ void packDeferredFragmentLightmap(vec3 normal, float alpha, vec3 diffuse, vec3 s
}
void packDeferredFragmentTranslucent(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess) {
if (alpha <= alphaThreshold) {
if (alpha <= 0.0) {
discard;
}

View file

@ -40,8 +40,6 @@
#include "point_light_frag.h"
#include "spot_light_frag.h"
static const std::string glowIntensityShaderHandle = "glowIntensity";
struct LightLocations {
int radius;
int ambientSphere;
@ -134,8 +132,6 @@ gpu::PipelinePointer DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch
batch.setPipeline(pipeline);
gpu::ShaderPointer program = (config.isEmissive()) ? _emissiveShader : _simpleShader;
int glowIntensity = program->getUniforms().findLocation("glowIntensity");
batch._glUniform1f(glowIntensity, 1.0f);
if (!config.isTextured()) {
// If it is not textured, bind white texture and keep using textured pipeline

View file

@ -199,8 +199,6 @@ void MeshPartPayload::render(RenderArgs* args) const {
gpu::Batch& batch = *(args->_batch);
auto mode = args->_renderMode;
auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME
model::MaterialKey drawMaterialKey;
if (_drawMaterial) {
drawMaterialKey = _drawMaterial->getKey();
@ -217,7 +215,7 @@ void MeshPartPayload::render(RenderArgs* args) const {
}
ModelRender::Locations* locations = nullptr;
ModelRender::pickPrograms(batch, mode, translucentMesh, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe,
ModelRender::pickPrograms(batch, mode, translucentMesh, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe,
args, locations);
@ -395,9 +393,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const {
gpu::Batch& batch = *(args->_batch);
auto mode = args->_renderMode;
auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME
const FBXGeometry& geometry = _model->_geometry->getFBXGeometry();
const std::vector<std::unique_ptr<NetworkMesh>>& networkMeshes = _model->_geometry->getMeshes();
@ -467,7 +463,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const {
}
ModelRender::Locations* locations = nullptr;
ModelRender::pickPrograms(batch, mode, translucentMesh, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe,
ModelRender::pickPrograms(batch, mode, translucentMesh, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe,
args, locations);
if (!locations) { // the pipeline could not be found

View file

@ -228,10 +228,8 @@ void ModelRender::RenderPipelineLib::addRenderPipeline(ModelRender::RenderKey ke
void ModelRender::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, ModelRender::Locations& locations) {
locations.alphaThreshold = program->getUniforms().findLocation("alphaThreshold");
locations.texcoordMatrices = program->getUniforms().findLocation("texcoordMatrices");
locations.emissiveParams = program->getUniforms().findLocation("emissiveParams");
locations.glowIntensity = program->getUniforms().findLocation("glowIntensity");
locations.normalFittingMapUnit = program->getTextures().findLocation("normalFittingMap");
locations.diffuseTextureUnit = program->getTextures().findLocation("diffuseMap");
locations.normalTextureUnit = program->getTextures().findLocation("normalMap");
@ -244,14 +242,14 @@ void ModelRender::RenderPipelineLib::initLocations(gpu::ShaderPointer& program,
}
void ModelRender::pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold,
void ModelRender::pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent,
bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args,
Locations*& locations) {
PerformanceTimer perfTimer("Model::pickPrograms");
getRenderPipelineLib();
RenderKey key(mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe);
RenderKey key(mode, translucent, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe);
auto pipeline = _renderPipelineLib.find(key.getRaw());
if (pipeline == _renderPipelineLib.end()) {
qDebug() << "No good, couldn't find a pipeline from the key ?" << key.getRaw();
@ -266,15 +264,6 @@ void ModelRender::pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, b
// Setup the One pipeline
batch.setPipeline((*pipeline).second._pipeline);
if ((locations->alphaThreshold > -1) && (mode != RenderArgs::SHADOW_RENDER_MODE)) {
batch._glUniform1f(locations->alphaThreshold, alphaThreshold);
}
if ((locations->glowIntensity > -1) && (mode != RenderArgs::SHADOW_RENDER_MODE)) {
const float DEFAULT_GLOW_INTENSITY = 1.0f; // FIXME - glow is removed
batch._glUniform1f(locations->glowIntensity, DEFAULT_GLOW_INTENSITY);
}
if ((locations->normalFittingMapUnit > -1)) {
batch.setResourceTexture(locations->normalFittingMapUnit,
DependencyManager::get<TextureCache>()->getNormalFittingTexture());

View file

@ -29,21 +29,19 @@ public:
class Locations {
public:
int alphaThreshold;
int texcoordMatrices;
int diffuseTextureUnit;
int normalTextureUnit;
int specularTextureUnit;
int emissiveTextureUnit;
int emissiveParams;
int glowIntensity;
int normalFittingMapUnit;
int skinClusterBufferUnit;
int materialBufferUnit;
int lightBufferUnit;
};
static void pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold,
static void pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent,
bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args,
Locations*& locations);
@ -111,9 +109,9 @@ public:
) {}
RenderKey(RenderArgs::RenderMode mode,
bool translucent, float alphaThreshold, bool hasLightmap,
bool translucent, bool hasLightmap,
bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe) :
RenderKey(((translucent && (alphaThreshold == 0.0f) && (mode != RenderArgs::SHADOW_RENDER_MODE)) ? IS_TRANSLUCENT : 0)
RenderKey(((translucent && (mode != RenderArgs::SHADOW_RENDER_MODE)) ? IS_TRANSLUCENT : 0)
| (hasLightmap && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_LIGHTMAP : 0) // Lightmap, tangents and specular don't matter for depthOnly
| (hasTangents && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_TANGENTS : 0)
| (hasSpecular && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_SPECULAR : 0)

View file

@ -127,11 +127,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
_jobs.back().setEnabled(false);
_drawHitEffectJobIndex = (int)_jobs.size() -1;
// Give ourselves 3 frmaes of timer queries
_timerQueries.push_back(std::make_shared<gpu::Query>());
_timerQueries.push_back(std::make_shared<gpu::Query>());
_timerQueries.push_back(std::make_shared<gpu::Query>());
_currentTimerQueryIndex = 0;
_jobs.push_back(Job(new Blit::JobModel("Blit")));
}
RenderDeferredTask::~RenderDeferredTask() {
@ -197,10 +193,6 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
{
const float OPAQUE_ALPHA_THRESHOLD = 0.5f;
args->_alphaThreshold = OPAQUE_ALPHA_THRESHOLD;
}
renderItems(sceneContext, renderContext, inItems, opaque.maxDrawn);
args->_batch = nullptr;
});
@ -226,10 +218,7 @@ void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f;
args->_alphaThreshold = TRANSPARENT_ALPHA_THRESHOLD;
renderItems(sceneContext, renderContext, inItems, transparent.maxDrawn);
args->_batch = nullptr;
});
@ -398,6 +387,76 @@ void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const
args->_batch = nullptr;
}
void Blit::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
assert(renderContext->getArgs());
assert(renderContext->getArgs()->_context);
RenderArgs* renderArgs = renderContext->getArgs();
auto blitFbo = renderArgs->_blitFramebuffer;
if (!blitFbo) {
return;
}
// Determine size from viewport
int width = renderArgs->_viewport.z;
int height = renderArgs->_viewport.w;
// Blit primary to blit FBO
auto framebufferCache = DependencyManager::get<FramebufferCache>();
auto primaryFbo = framebufferCache->getPrimaryFramebuffer();
gpu::doInBatch(renderArgs->_context, [=](gpu::Batch& batch) {
batch.setFramebuffer(blitFbo);
if (renderArgs->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
if (renderArgs->_context->isStereo()) {
gpu::Vec4i srcRectLeft;
srcRectLeft.z = width / 2;
srcRectLeft.w = height;
gpu::Vec4i srcRectRight;
srcRectRight.x = width / 2;
srcRectRight.z = width;
srcRectRight.w = height;
gpu::Vec4i destRectLeft;
destRectLeft.x = srcRectLeft.z;
destRectLeft.z = srcRectLeft.x;
destRectLeft.y = srcRectLeft.y;
destRectLeft.w = srcRectLeft.w;
gpu::Vec4i destRectRight;
destRectRight.x = srcRectRight.z;
destRectRight.z = srcRectRight.x;
destRectRight.y = srcRectRight.y;
destRectRight.w = srcRectRight.w;
// Blit left to right and right to left in stereo
batch.blit(primaryFbo, srcRectRight, blitFbo, destRectLeft);
batch.blit(primaryFbo, srcRectLeft, blitFbo, destRectRight);
} else {
gpu::Vec4i srcRect;
srcRect.z = width;
srcRect.w = height;
gpu::Vec4i destRect;
destRect.x = width;
destRect.y = 0;
destRect.z = 0;
destRect.w = height;
batch.blit(primaryFbo, srcRect, blitFbo, destRect);
}
} else {
gpu::Vec4i rect;
rect.z = width;
rect.w = height;
batch.blit(primaryFbo, rect, blitFbo, rect);
}
});
}
void RenderDeferredTask::setToneMappingExposure(float exposure) {
if (_toneMappingJobIndex >= 0) {

View file

@ -90,6 +90,13 @@ public:
typedef render::Job::Model<DrawOverlay3D> JobModel;
};
class Blit {
public:
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
typedef render::Job::Model<Blit> JobModel;
};
class RenderDeferredTask : public render::Task {
public:

View file

@ -114,5 +114,6 @@ void main (void)
vec3 finalColor = color + fMiePhase * secondaryColor;
outFragColor.a = finalColor.b;
outFragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2));
// outFragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2));
outFragColor.rgb = finalColor.rgb;
}

View file

@ -51,9 +51,9 @@ void main(void) {
if (emissiveAmount > 0.0) {
packDeferredFragmentLightmap(
normal, glowIntensity, diffuse, specular, shininess, specular);
normal, 1.0, diffuse, specular, shininess, specular);
} else {
packDeferredFragment(
normal, glowIntensity, diffuse, specular, shininess);
normal, 1.0, diffuse, specular, shininess);
}
}

View file

@ -29,7 +29,7 @@ void main(void) {
packDeferredFragment(
normalize(_normal.xyz),
glowIntensity * texel.a,
texel.a,
_color.rgb * texel.rgb,
DEFAULT_SPECULAR, DEFAULT_SHININESS);
}

View file

@ -27,7 +27,7 @@ void main(void) {
packDeferredFragmentLightmap(
normalize(_normal),
glowIntensity * texel.a,
texel.a,
_color.rgb,
DEFAULT_SPECULAR, DEFAULT_SHININESS,
texel.rgb);

View file

@ -14,9 +14,10 @@
using namespace render;
RenderContext::RenderContext(ItemsConfig items, Tone tone, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode)
: _args{ nullptr }, _items{ items }, _tone{ tone },
_drawStatus{ drawStatus }, _drawHitEffect{ drawHitEffect },
_deferredDebugSize{ deferredDebugSize }, _deferredDebugMode{ deferredDebugMode } {};
: _deferredDebugMode{ deferredDebugMode }, _deferredDebugSize{ deferredDebugSize },
_args{ nullptr },
_drawStatus{ drawStatus }, _drawHitEffect{ drawHitEffect },
_items{ items }, _tone{ tone } {}
void RenderContext::setOptions(bool occlusion, bool fxaa, bool showOwned) {
_occlusionStatus = occlusion;

View file

@ -91,15 +91,15 @@ public:
void setOptions(bool occlusion, bool fxaa, bool showOwned);
// Debugging
int _deferredDebugMode = -1;
glm::vec4 _deferredDebugSize { 0.0f, -1.0f, 1.0f, 1.0f };
int _deferredDebugMode;
glm::vec4 _deferredDebugSize;
protected:
RenderArgs* _args;
// Options
int _drawStatus = 0; // bitflag
bool _drawHitEffect = false;
int _drawStatus; // bitflag
bool _drawHitEffect;
bool _occlusionStatus = false;
bool _fxaaStatus = false;

View file

@ -82,6 +82,7 @@ public:
static const vec3& RIGHT;
static const vec3& UP;
static const vec3& FRONT;
static const vec3 ZERO4;
};
// These pack/unpack functions are designed to start specific known types in as efficient a manner

View file

@ -24,6 +24,7 @@ namespace gpu {
class Batch;
class Context;
class Texture;
class Framebuffer;
}
class RenderDetails {
@ -101,6 +102,7 @@ public:
}
std::shared_ptr<gpu::Context> _context = nullptr;
std::shared_ptr<gpu::Framebuffer> _blitFramebuffer = nullptr;
OctreeRenderer* _renderer = nullptr;
ViewFrustum* _viewFrustum = nullptr;
glm::ivec4 _viewport{ 0, 0, 1, 1 };
@ -115,8 +117,6 @@ public:
std::shared_ptr<gpu::Texture> _whiteTexture;
RenderDetails _details;
float _alphaThreshold = 0.5f;
};
#endif // hifi_RenderArgs_h

View file

@ -0,0 +1,13 @@
#
# Created by Anthony Thibault on 2015/12/18
# 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
#
set(TARGET_NAME hifiNeuron)
setup_hifi_plugin(Script Qml Widgets)
link_hifi_libraries(shared controllers plugins input-plugins)
target_neuron()

View file

@ -0,0 +1,557 @@
//
// NeuronPlugin.cpp
// input-plugins/src/input-plugins
//
// Created by Anthony Thibault on 12/18/2015.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "NeuronPlugin.h"
#include <controllers/UserInputMapper.h>
#include <QLoggingCategory>
#include <PathUtils.h>
#include <DebugDraw.h>
#include <cassert>
#include <NumericalConstants.h>
#include <StreamUtils.h>
Q_DECLARE_LOGGING_CATEGORY(inputplugins)
Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins")
#define __OS_XUN__ 1
#define BOOL int
#ifdef HAVE_NEURON
#include <NeuronDataReader.h>
#endif
const QString NeuronPlugin::NAME = "Neuron";
const QString NeuronPlugin::NEURON_ID_STRING = "Perception Neuron";
// indices of joints of the Neuron standard skeleton.
// This is 'almost' the same as the High Fidelity standard skeleton.
// It is missing a thumb joint.
enum NeuronJointIndex {
Hips = 0,
RightUpLeg,
RightLeg,
RightFoot,
LeftUpLeg,
LeftLeg,
LeftFoot,
Spine,
Spine1,
Spine2,
Spine3,
Neck,
Head,
RightShoulder,
RightArm,
RightForeArm,
RightHand,
RightHandThumb1,
RightHandThumb2,
RightHandThumb3,
RightInHandIndex,
RightHandIndex1,
RightHandIndex2,
RightHandIndex3,
RightInHandMiddle,
RightHandMiddle1,
RightHandMiddle2,
RightHandMiddle3,
RightInHandRing,
RightHandRing1,
RightHandRing2,
RightHandRing3,
RightInHandPinky,
RightHandPinky1,
RightHandPinky2,
RightHandPinky3,
LeftShoulder,
LeftArm,
LeftForeArm,
LeftHand,
LeftHandThumb1,
LeftHandThumb2,
LeftHandThumb3,
LeftInHandIndex,
LeftHandIndex1,
LeftHandIndex2,
LeftHandIndex3,
LeftInHandMiddle,
LeftHandMiddle1,
LeftHandMiddle2,
LeftHandMiddle3,
LeftInHandRing,
LeftHandRing1,
LeftHandRing2,
LeftHandRing3,
LeftInHandPinky,
LeftHandPinky1,
LeftHandPinky2,
LeftHandPinky3,
Size
};
// Almost a direct mapping except for LEFT_HAND_THUMB1 and RIGHT_HAND_THUMB1,
// which are not present in the Neuron standard skeleton.
static controller::StandardPoseChannel neuronJointIndexToPoseIndexMap[NeuronJointIndex::Size] = {
controller::HIPS,
controller::RIGHT_UP_LEG,
controller::RIGHT_LEG,
controller::RIGHT_FOOT,
controller::LEFT_UP_LEG,
controller::LEFT_LEG,
controller::LEFT_FOOT,
controller::SPINE,
controller::SPINE1,
controller::SPINE2,
controller::SPINE3,
controller::NECK,
controller::HEAD,
controller::RIGHT_SHOULDER,
controller::RIGHT_ARM,
controller::RIGHT_FORE_ARM,
controller::RIGHT_HAND,
controller::RIGHT_HAND_THUMB2,
controller::RIGHT_HAND_THUMB3,
controller::RIGHT_HAND_THUMB4,
controller::RIGHT_HAND_INDEX1,
controller::RIGHT_HAND_INDEX2,
controller::RIGHT_HAND_INDEX3,
controller::RIGHT_HAND_INDEX4,
controller::RIGHT_HAND_MIDDLE1,
controller::RIGHT_HAND_MIDDLE2,
controller::RIGHT_HAND_MIDDLE3,
controller::RIGHT_HAND_MIDDLE4,
controller::RIGHT_HAND_RING1,
controller::RIGHT_HAND_RING2,
controller::RIGHT_HAND_RING3,
controller::RIGHT_HAND_RING4,
controller::RIGHT_HAND_PINKY1,
controller::RIGHT_HAND_PINKY2,
controller::RIGHT_HAND_PINKY3,
controller::RIGHT_HAND_PINKY4,
controller::LEFT_SHOULDER,
controller::LEFT_ARM,
controller::LEFT_FORE_ARM,
controller::LEFT_HAND,
controller::LEFT_HAND_THUMB2,
controller::LEFT_HAND_THUMB3,
controller::LEFT_HAND_THUMB4,
controller::LEFT_HAND_INDEX1,
controller::LEFT_HAND_INDEX2,
controller::LEFT_HAND_INDEX3,
controller::LEFT_HAND_INDEX4,
controller::LEFT_HAND_MIDDLE1,
controller::LEFT_HAND_MIDDLE2,
controller::LEFT_HAND_MIDDLE3,
controller::LEFT_HAND_MIDDLE4,
controller::LEFT_HAND_RING1,
controller::LEFT_HAND_RING2,
controller::LEFT_HAND_RING3,
controller::LEFT_HAND_RING4,
controller::LEFT_HAND_PINKY1,
controller::LEFT_HAND_PINKY2,
controller::LEFT_HAND_PINKY3,
controller::LEFT_HAND_PINKY4
};
// in rig frame
static glm::vec3 rightHandThumb1DefaultAbsTranslation(-2.155500650405884, -0.7610001564025879, 2.685631036758423);
static glm::vec3 leftHandThumb1DefaultAbsTranslation(2.1555817127227783, -0.7603635787963867, 2.6856393814086914);
// default translations (cm)
static glm::vec3 neuronJointTranslations[NeuronJointIndex::Size] = {
{131.901, 95.6602, -27.9815},
{-9.55907, -1.58772, 0.0760284},
{0.0144232, -41.4683, -0.105322},
{1.59348, -41.5875, -0.557237},
{9.72077, -1.68926, -0.280643},
{0.0886684, -43.1586, -0.0111596},
{-2.98473, -44.0517, 0.0694456},
{0.110967, 16.3959, 0.140463},
{0.0500451, 10.0238, 0.0731921},
{0.061568, 10.4352, 0.0583075},
{0.0500606, 10.0217, 0.0711083},
{0.0317731, 10.7176, 0.0779325},
{-0.0204253, 9.71067, 0.131734},
{-3.24245, 7.13584, 0.185638},
{-13.0885, -0.0877601, 0.176065},
{-27.2674, 0.0688724, 0.0272146},
{-26.7673, 0.0301916, 0.0102847},
{-2.56017, 0.195537, 3.20968},
{-3.78796, 0, 0},
{-2.63141, 0, 0},
{-3.31579, 0.522947, 2.03495},
{-5.36589, -0.0939789, 1.02771},
{-3.72278, 0, 0},
{-2.11074, 0, 0},
{-3.47874, 0.532042, 0.778358},
{-5.32194, -0.0864, 0.322863},
{-4.06232, 0, 0},
{-2.54653, 0, 0},
{-3.46131, 0.553263, -0.132632},
{-4.76716, -0.0227368, -0.492632},
{-3.54073, 0, 0},
{-2.45634, 0, 0},
{-3.25137, 0.482779, -1.23613},
{-4.25937, -0.0227368, -1.12168},
{-2.83528, 0, 0},
{-1.79166, 0, 0},
{3.25624, 7.13148, -0.131575},
{13.149, -0.052598, -0.125076},
{27.2903, 0.00282644, -0.0181535},
{26.6602, 0.000969969, -0.0487599},
{2.56017, 0.195537, 3.20968},
{3.78796, 0, 0},
{2.63141, 0, 0},
{3.31579, 0.522947, 2.03495},
{5.36589, -0.0939789, 1.02771},
{3.72278, 0, 0},
{2.11074, 0, 0},
{3.47874, 0.532042, 0.778358},
{5.32194, -0.0864, 0.322863},
{4.06232, 0, 0},
{2.54653, 0, 0},
{3.46131, 0.553263, -0.132632},
{4.76716, -0.0227368, -0.492632},
{3.54073, 0, 0},
{2.45634, 0, 0},
{3.25137, 0.482779, -1.23613},
{4.25937, -0.0227368, -1.12168},
{2.83528, 0, 0},
{1.79166, 0, 0}
};
static controller::StandardPoseChannel neuronJointIndexToPoseIndex(NeuronJointIndex i) {
assert(i >= 0 && i < NeuronJointIndex::Size);
if (i >= 0 && i < NeuronJointIndex::Size) {
return neuronJointIndexToPoseIndexMap[i];
} else {
return (controller::StandardPoseChannel)0; // not sure what to do here, but don't crash!
}
}
static const char* controllerJointName(controller::StandardPoseChannel i) {
switch (i) {
case controller::HIPS: return "Hips";
case controller::RIGHT_UP_LEG: return "RightUpLeg";
case controller::RIGHT_LEG: return "RightLeg";
case controller::RIGHT_FOOT: return "RightFoot";
case controller::LEFT_UP_LEG: return "LeftUpLeg";
case controller::LEFT_LEG: return "LeftLeg";
case controller::LEFT_FOOT: return "LeftFoot";
case controller::SPINE: return "Spine";
case controller::SPINE1: return "Spine1";
case controller::SPINE2: return "Spine2";
case controller::SPINE3: return "Spine3";
case controller::NECK: return "Neck";
case controller::HEAD: return "Head";
case controller::RIGHT_SHOULDER: return "RightShoulder";
case controller::RIGHT_ARM: return "RightArm";
case controller::RIGHT_FORE_ARM: return "RightForeArm";
case controller::RIGHT_HAND: return "RightHand";
case controller::RIGHT_HAND_THUMB1: return "RightHandThumb1";
case controller::RIGHT_HAND_THUMB2: return "RightHandThumb2";
case controller::RIGHT_HAND_THUMB3: return "RightHandThumb3";
case controller::RIGHT_HAND_THUMB4: return "RightHandThumb4";
case controller::RIGHT_HAND_INDEX1: return "RightHandIndex1";
case controller::RIGHT_HAND_INDEX2: return "RightHandIndex2";
case controller::RIGHT_HAND_INDEX3: return "RightHandIndex3";
case controller::RIGHT_HAND_INDEX4: return "RightHandIndex4";
case controller::RIGHT_HAND_MIDDLE1: return "RightHandMiddle1";
case controller::RIGHT_HAND_MIDDLE2: return "RightHandMiddle2";
case controller::RIGHT_HAND_MIDDLE3: return "RightHandMiddle3";
case controller::RIGHT_HAND_MIDDLE4: return "RightHandMiddle4";
case controller::RIGHT_HAND_RING1: return "RightHandRing1";
case controller::RIGHT_HAND_RING2: return "RightHandRing2";
case controller::RIGHT_HAND_RING3: return "RightHandRing3";
case controller::RIGHT_HAND_RING4: return "RightHandRing4";
case controller::RIGHT_HAND_PINKY1: return "RightHandPinky1";
case controller::RIGHT_HAND_PINKY2: return "RightHandPinky2";
case controller::RIGHT_HAND_PINKY3: return "RightHandPinky3";
case controller::RIGHT_HAND_PINKY4: return "RightHandPinky4";
case controller::LEFT_SHOULDER: return "LeftShoulder";
case controller::LEFT_ARM: return "LeftArm";
case controller::LEFT_FORE_ARM: return "LeftForeArm";
case controller::LEFT_HAND: return "LeftHand";
case controller::LEFT_HAND_THUMB1: return "LeftHandThumb1";
case controller::LEFT_HAND_THUMB2: return "LeftHandThumb2";
case controller::LEFT_HAND_THUMB3: return "LeftHandThumb3";
case controller::LEFT_HAND_THUMB4: return "LeftHandThumb4";
case controller::LEFT_HAND_INDEX1: return "LeftHandIndex1";
case controller::LEFT_HAND_INDEX2: return "LeftHandIndex2";
case controller::LEFT_HAND_INDEX3: return "LeftHandIndex3";
case controller::LEFT_HAND_INDEX4: return "LeftHandIndex4";
case controller::LEFT_HAND_MIDDLE1: return "LeftHandMiddle1";
case controller::LEFT_HAND_MIDDLE2: return "LeftHandMiddle2";
case controller::LEFT_HAND_MIDDLE3: return "LeftHandMiddle3";
case controller::LEFT_HAND_MIDDLE4: return "LeftHandMiddle4";
case controller::LEFT_HAND_RING1: return "LeftHandRing1";
case controller::LEFT_HAND_RING2: return "LeftHandRing2";
case controller::LEFT_HAND_RING3: return "LeftHandRing3";
case controller::LEFT_HAND_RING4: return "LeftHandRing4";
case controller::LEFT_HAND_PINKY1: return "LeftHandPinky1";
case controller::LEFT_HAND_PINKY2: return "LeftHandPinky2";
case controller::LEFT_HAND_PINKY3: return "LeftHandPinky3";
case controller::LEFT_HAND_PINKY4: return "LeftHandPinky4";
default: return "???";
}
}
// convert between YXZ neuron euler angles in degrees to quaternion
// this is the default setting in the Axis Neuron server.
static quat eulerToQuat(vec3 euler) {
// euler.x and euler.y are swaped, WTF.
glm::vec3 e = glm::vec3(euler.y, euler.x, euler.z) * RADIANS_PER_DEGREE;
return (glm::angleAxis(e.y, Vectors::UNIT_Y) *
glm::angleAxis(e.x, Vectors::UNIT_X) *
glm::angleAxis(e.z, Vectors::UNIT_Z));
}
#ifdef HAVE_NEURON
//
// neuronDataReader SDK callback functions
//
// NOTE: must be thread-safe
void FrameDataReceivedCallback(void* context, SOCKET_REF sender, BvhDataHeaderEx* header, float* data) {
auto neuronPlugin = reinterpret_cast<NeuronPlugin*>(context);
// version 1.0
if (header->DataVersion.Major == 1 && header->DataVersion.Minor == 0) {
// skip reference joint if present
if (header->WithReference && header->WithDisp) {
data += 6;
} else if (header->WithReference && !header->WithDisp) {
data += 3;
}
if (header->WithDisp) {
// enter mutex
std::lock_guard<std::mutex> guard(neuronPlugin->_jointsMutex);
//
// Data is 6 floats per joint: 3 position values, 3 rotation euler angles (degrees)
//
// resize vector if necessary
const size_t NUM_FLOATS_PER_JOINT = 6;
const size_t NUM_JOINTS = header->DataCount / NUM_FLOATS_PER_JOINT;
if (neuronPlugin->_joints.size() != NUM_JOINTS) {
neuronPlugin->_joints.resize(NUM_JOINTS, { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } });
}
assert(sizeof(NeuronPlugin::NeuronJoint) == (NUM_FLOATS_PER_JOINT * sizeof(float)));
// copy the data
memcpy(&(neuronPlugin->_joints[0]), data, sizeof(NeuronPlugin::NeuronJoint) * NUM_JOINTS);
} else {
qCWarning(inputplugins) << "NeuronPlugin: unsuported binary format, please enable displacements";
// enter mutex
std::lock_guard<std::mutex> guard(neuronPlugin->_jointsMutex);
if (neuronPlugin->_joints.size() != NeuronJointIndex::Size) {
neuronPlugin->_joints.resize(NeuronJointIndex::Size, { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } });
}
for (int i = 0; i < NeuronJointIndex::Size; i++) {
neuronPlugin->_joints[i].euler = glm::vec3();
neuronPlugin->_joints[i].pos = neuronJointTranslations[i];
}
}
} else {
static bool ONCE = false;
if (!ONCE) {
qCCritical(inputplugins) << "NeuronPlugin: bad frame version number, expected 1.0";
ONCE = true;
}
}
}
// I can't even get the SDK to send me a callback.
// BRCommandFetchAvatarDataFromServer & BRRegisterAutoSyncParmeter [sic] don't seem to work.
// So this is totally untested.
// NOTE: must be thread-safe
static void CommandDataReceivedCallback(void* context, SOCKET_REF sender, CommandPack* pack, void* data) {
DATA_VER version;
version._VersionMask = pack->DataVersion;
if (version.Major == 1 && version.Minor == 0) {
const char* str = "Unknown";
switch (pack->CommandId) {
case Cmd_BoneSize: // Id can be used to request bone size from server or register avatar name command.
str = "BoneSize";
break;
case Cmd_AvatarName: // Id can be used to request avatar name from server or register avatar name command.
str = "AvatarName";
break;
case Cmd_FaceDirection: // Id used to request face direction from server
str = "FaceDirection";
break;
case Cmd_DataFrequency: // Id can be used to request data frequency from server or register data frequency command.
str = "DataFrequency";
break;
case Cmd_BvhInheritanceTxt: // Id can be used to request bvh header txt from server or register bvh header txt command.
str = "BvhInheritanceTxt";
break;
case Cmd_AvatarCount: // Id can be used to request avatar count from server or register avatar count command.
str = "AvatarCount";
break;
case Cmd_CombinationMode: // Id can be used to request combination mode from server or register combination mode command.
str = "CombinationMode";
break;
case Cmd_RegisterEvent: // Id can be used to register event.
str = "RegisterEvent";
break;
case Cmd_UnRegisterEvent: // Id can be used to unregister event.
str = "UnRegisterEvent";
break;
}
qCDebug(inputplugins) << "NeuronPlugin: command data received CommandID = " << str;
} else {
static bool ONCE = false;
if (!ONCE) {
qCCritical(inputplugins) << "NeuronPlugin: bad command version number, expected 1.0";
ONCE = true;
}
}
}
// NOTE: must be thread-safe
static void SocketStatusChangedCallback(void* context, SOCKET_REF sender, SocketStatus status, char* message) {
// just dump to log, later we might want to pop up a connection lost dialog or attempt to reconnect.
qCDebug(inputplugins) << "NeuronPlugin: socket status = " << message;
}
#endif // #ifdef HAVE_NEURON
//
// NeuronPlugin
//
bool NeuronPlugin::isSupported() const {
#ifdef HAVE_NEURON
// Because it's a client/server network architecture, we can't tell
// if the neuron is actually connected until we connect to the server.
return true;
#else
return false;
#endif
}
void NeuronPlugin::activate() {
#ifdef HAVE_NEURON
InputPlugin::activate();
// register with userInputMapper
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
userInputMapper->registerDevice(_inputDevice);
// register c-style callbacks
BRRegisterFrameDataCallback((void*)this, FrameDataReceivedCallback);
BRRegisterCommandDataCallback((void*)this, CommandDataReceivedCallback);
BRRegisterSocketStatusCallback((void*)this, SocketStatusChangedCallback);
// TODO: Pull these from prefs dialog?
// localhost is fine for now.
_serverAddress = "localhost";
_serverPort = 7001; // default port for TCP Axis Neuron server.
_socketRef = BRConnectTo((char*)_serverAddress.c_str(), _serverPort);
if (!_socketRef) {
// error
qCCritical(inputplugins) << "NeuronPlugin: error connecting to " << _serverAddress.c_str() << ":" << _serverPort << ", error = " << BRGetLastErrorMessage();
} else {
qCDebug(inputplugins) << "NeuronPlugin: success connecting to " << _serverAddress.c_str() << ":" << _serverPort;
BRRegisterAutoSyncParmeter(_socketRef, Cmd_CombinationMode);
}
#endif
}
void NeuronPlugin::deactivate() {
#ifdef HAVE_NEURON
// unregister from userInputMapper
if (_inputDevice->_deviceID != controller::Input::INVALID_DEVICE) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
userInputMapper->removeDevice(_inputDevice->_deviceID);
}
if (_socketRef) {
BRUnregisterAutoSyncParmeter(_socketRef, Cmd_CombinationMode);
BRCloseSocket(_socketRef);
}
InputPlugin::deactivate();
#endif
}
void NeuronPlugin::pluginUpdate(float deltaTime, bool jointsCaptured) {
std::vector<NeuronJoint> joints;
{
// lock and copy
std::lock_guard<std::mutex> guard(_jointsMutex);
joints = _joints;
}
_inputDevice->update(deltaTime, joints, _prevJoints);
_prevJoints = joints;
}
void NeuronPlugin::saveSettings() const {
InputPlugin::saveSettings();
}
void NeuronPlugin::loadSettings() {
InputPlugin::loadSettings();
}
//
// InputDevice
//
controller::Input::NamedVector NeuronPlugin::InputDevice::getAvailableInputs() const {
static controller::Input::NamedVector availableInputs;
if (availableInputs.size() == 0) {
for (int i = 0; i < controller::NUM_STANDARD_POSES; i++) {
auto channel = static_cast<controller::StandardPoseChannel>(i);
availableInputs.push_back(makePair(channel, controllerJointName(channel)));
}
};
return availableInputs;
}
QString NeuronPlugin::InputDevice::getDefaultMappingConfig() const {
static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/neuron.json";
return MAPPING_JSON;
}
void NeuronPlugin::InputDevice::update(float deltaTime, const std::vector<NeuronPlugin::NeuronJoint>& joints, const std::vector<NeuronPlugin::NeuronJoint>& prevJoints) {
for (size_t i = 0; i < joints.size(); i++) {
glm::vec3 linearVel, angularVel;
glm::vec3 pos = joints[i].pos;
glm::quat rot = eulerToQuat(joints[i].euler);
if (i < prevJoints.size()) {
linearVel = (pos - (prevJoints[i].pos * METERS_PER_CENTIMETER)) / deltaTime; // m/s
// quat log imaginary part points along the axis of rotation, with length of one half the angle of rotation.
glm::quat d = glm::log(rot * glm::inverse(eulerToQuat(prevJoints[i].euler)));
angularVel = glm::vec3(d.x, d.y, d.z) / (0.5f * deltaTime); // radians/s
}
int poseIndex = neuronJointIndexToPoseIndex((NeuronJointIndex)i);
_poseStateMap[poseIndex] = controller::Pose(pos, rot, linearVel, angularVel);
}
_poseStateMap[controller::RIGHT_HAND_THUMB1] = controller::Pose(rightHandThumb1DefaultAbsTranslation, glm::quat(), glm::vec3(), glm::vec3());
_poseStateMap[controller::LEFT_HAND_THUMB1] = controller::Pose(leftHandThumb1DefaultAbsTranslation, glm::quat(), glm::vec3(), glm::vec3());
}

View file

@ -0,0 +1,85 @@
//
// NeuronPlugin.h
// input-plugins/src/input-plugins
//
// Created by Anthony Thibault on 12/18/2015.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_NeuronPlugin_h
#define hifi_NeuronPlugin_h
#include <controllers/InputDevice.h>
#include <controllers/StandardControls.h>
#include <plugins/InputPlugin.h>
struct _BvhDataHeaderEx;
void FrameDataReceivedCallback(void* context, void* sender, _BvhDataHeaderEx* header, float* data);
// Handles interaction with the Neuron SDK
class NeuronPlugin : public InputPlugin {
Q_OBJECT
public:
friend void FrameDataReceivedCallback(void* context, void* sender, _BvhDataHeaderEx* header, float* data);
// Plugin functions
virtual bool isSupported() const override;
virtual bool isJointController() const override { return true; }
const QString& getName() const override { return NAME; }
const QString& getID() const override { return NEURON_ID_STRING; }
virtual void activate() override;
virtual void deactivate() override;
virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }
virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override;
virtual void saveSettings() const override;
virtual void loadSettings() override;
protected:
struct NeuronJoint {
glm::vec3 pos;
glm::vec3 euler;
};
class InputDevice : public controller::InputDevice {
public:
friend class NeuronPlugin;
InputDevice() : controller::InputDevice("Neuron") {}
// Device functions
virtual controller::Input::NamedVector getAvailableInputs() const override;
virtual QString getDefaultMappingConfig() const override;
virtual void update(float deltaTime, bool jointsCaptured) override {};
virtual void focusOutEvent() override {};
void update(float deltaTime, const std::vector<NeuronPlugin::NeuronJoint>& joints, const std::vector<NeuronPlugin::NeuronJoint>& prevJoints);
};
std::shared_ptr<InputDevice> _inputDevice { std::make_shared<InputDevice>() };
static const QString NAME;
static const QString NEURON_ID_STRING;
std::string _serverAddress;
int _serverPort;
void* _socketRef;
// used to guard multi-threaded access to _joints
std::mutex _jointsMutex;
// copy of data directly from the NeuronDataReader SDK
std::vector<NeuronJoint> _joints;
// one frame old copy of _joints, used to caluclate angular and linear velocity.
std::vector<NeuronJoint> _prevJoints;
};
#endif // hifi_NeuronPlugin_h

View file

@ -0,0 +1,45 @@
//
// Created by Anthony Thibault on 2015/12/18
// 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 <mutex>
#include <QtCore/QObject>
#include <QtCore/QtPlugin>
#include <QtCore/QStringList>
#include <plugins/RuntimePlugin.h>
#include <plugins/InputPlugin.h>
#include "NeuronPlugin.h"
class NeuronProvider : public QObject, public InputProvider
{
Q_OBJECT
Q_PLUGIN_METADATA(IID InputProvider_iid FILE "plugin.json")
Q_INTERFACES(InputProvider)
public:
NeuronProvider(QObject* parent = nullptr) : QObject(parent) {}
virtual ~NeuronProvider() {}
virtual InputPluginList getInputPlugins() override {
static std::once_flag once;
std::call_once(once, [&] {
InputPluginPointer plugin(new NeuronPlugin());
if (plugin->isSupported()) {
_inputPlugins.push_back(plugin);
}
});
return _inputPlugins;
}
private:
InputPluginList _inputPlugins;
};
#include "NeuronProvider.moc"

View file

@ -0,0 +1 @@
{}

View file

@ -56,9 +56,9 @@ private:
mat4 _eyeProjections[3];
mat4 _compositeEyeProjections[2];
uvec2 _desiredFramebufferSize;
ovrTexture _eyeTextures[2];
mutable int _hmdScreen{ -1 };
bool _hswDismissed{ false };
//ovrTexture _eyeTextures[2]; // FIXME - not currently in use
mutable int _hmdScreen { -1 };
bool _hswDismissed { false };
};

View file

@ -274,7 +274,7 @@ struct ByteData {
QTextStream & operator << (QTextStream& stream, const ByteData & wrapper) {
// Print bytes as hex
stream << QByteArray::fromRawData(wrapper.data, wrapper.length).toHex();
stream << QByteArray::fromRawData(wrapper.data, (int)wrapper.length).toHex();
return stream;
}

View file

@ -8,12 +8,13 @@
//
#include "AnimTests.h"
#include "AnimNodeLoader.h"
#include "AnimClip.h"
#include "AnimBlendLinear.h"
#include "AnimationLogging.h"
#include "AnimVariant.h"
#include "AnimUtil.h"
#include <AnimNodeLoader.h>
#include <AnimClip.h>
#include <AnimBlendLinear.h>
#include <AnimationLogging.h>
#include <AnimVariant.h>
#include <AnimExpression.h>
#include <AnimUtil.h>
#include <../QTestExtensions.h>
@ -315,7 +316,6 @@ void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFram
triggers.clear();
}
void AnimTests::testAnimPose() {
const float PI = (float)M_PI;
const glm::quat ROT_X_90 = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f));
@ -394,3 +394,234 @@ void AnimTests::testAnimPose() {
}
}
}
void AnimTests::testExpressionTokenizer() {
QString str = "(10 + x) >= 20.1 && (y != !z)";
AnimExpression e("x");
auto iter = str.cbegin();
AnimExpression::Token token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::LeftParen);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::Int);
QVERIFY(token.intVal == 10);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::Plus);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::Identifier);
QVERIFY(token.strVal == "x");
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::RightParen);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::GreaterThanEqual);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::Float);
QVERIFY(fabsf(token.floatVal - 20.1f) < 0.0001f);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::And);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::LeftParen);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::Identifier);
QVERIFY(token.strVal == "y");
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::NotEqual);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::Not);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::Identifier);
QVERIFY(token.strVal == "z");
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::RightParen);
token = e.consumeToken(str, iter);
str = "true";
iter = str.cbegin();
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::Bool);
QVERIFY(token.intVal == (int)true);
}
void AnimTests::testExpressionParser() {
auto vars = AnimVariantMap();
vars.set("f", false);
vars.set("t", true);
vars.set("ten", (int)10);
vars.set("twenty", (int)20);
vars.set("five", (float)5.0f);
vars.set("forty", (float)40.0f);
AnimExpression e("10");
QVERIFY(e._opCodes.size() == 1);
if (e._opCodes.size() == 1) {
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int);
QVERIFY(e._opCodes[0].intVal == 10);
}
e = AnimExpression("(10)");
QVERIFY(e._opCodes.size() == 1);
if (e._opCodes.size() == 1) {
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int);
QVERIFY(e._opCodes[0].intVal == 10);
}
e = AnimExpression("((10))");
QVERIFY(e._opCodes.size() == 1);
if (e._opCodes.size() == 1) {
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int);
QVERIFY(e._opCodes[0].intVal == 10);
}
e = AnimExpression("12.5");
QVERIFY(e._opCodes.size() == 1);
if (e._opCodes.size() == 1) {
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Float);
QVERIFY(e._opCodes[0].floatVal == 12.5f);
}
e = AnimExpression("twenty");
QVERIFY(e._opCodes.size() == 1);
if (e._opCodes.size() == 1) {
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier);
QVERIFY(e._opCodes[0].strVal == "twenty");
}
e = AnimExpression("true || false");
QVERIFY(e._opCodes.size() == 3);
if (e._opCodes.size() == 3) {
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Bool);
QVERIFY(e._opCodes[0].intVal == (int)true);
QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Bool);
QVERIFY(e._opCodes[1].intVal == (int)false);
QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Or);
}
e = AnimExpression("true || false && true");
QVERIFY(e._opCodes.size() == 5);
if (e._opCodes.size() == 5) {
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Bool);
QVERIFY(e._opCodes[0].intVal == (int)true);
QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Bool);
QVERIFY(e._opCodes[1].intVal == (int)false);
QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Bool);
QVERIFY(e._opCodes[2].intVal == (int)true);
QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::And);
QVERIFY(e._opCodes[4].type == AnimExpression::OpCode::Or);
}
e = AnimExpression("(true || false) && true");
QVERIFY(e._opCodes.size() == 5);
if (e._opCodes.size() == 5) {
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Bool);
QVERIFY(e._opCodes[0].intVal == (int)true);
QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Bool);
QVERIFY(e._opCodes[1].intVal == (int)false);
QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Or);
QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::Bool);
QVERIFY(e._opCodes[3].intVal == (int)true);
QVERIFY(e._opCodes[4].type == AnimExpression::OpCode::And);
}
e = AnimExpression("!(true || false) && true");
QVERIFY(e._opCodes.size() == 6);
if (e._opCodes.size() == 6) {
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Bool);
QVERIFY(e._opCodes[0].intVal == (int)true);
QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Bool);
QVERIFY(e._opCodes[1].intVal == (int)false);
QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Or);
QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::Not);
QVERIFY(e._opCodes[4].type == AnimExpression::OpCode::Bool);
QVERIFY(e._opCodes[4].intVal == (int)true);
QVERIFY(e._opCodes[5].type == AnimExpression::OpCode::And);
}
}
#define TEST_BOOL_EXPR(EXPR) \
result = AnimExpression( #EXPR ).evaluate(vars); \
QVERIFY(result.type == AnimExpression::OpCode::Bool); \
QVERIFY(result.intVal == (int)(EXPR))
void AnimTests::testExpressionEvaluator() {
auto vars = AnimVariantMap();
bool f = false;
bool t = true;
int ten = 10;
int twenty = 20;
float five = 5.0f;
float fourty = 40.0f;
vars.set("f", f);
vars.set("t", t);
vars.set("ten", ten);
vars.set("twenty", twenty);
vars.set("five", five);
vars.set("forty", fourty);
AnimExpression::OpCode result(AnimExpression::OpCode::Int);
result = AnimExpression("10").evaluate(vars);
QVERIFY(result.type == AnimExpression::OpCode::Int);
QVERIFY(result.intVal == 10);
result = AnimExpression("(10)").evaluate(vars);
QVERIFY(result.type == AnimExpression::OpCode::Int);
QVERIFY(result.intVal == 10);
TEST_BOOL_EXPR(true);
TEST_BOOL_EXPR(false);
TEST_BOOL_EXPR(t);
TEST_BOOL_EXPR(f);
TEST_BOOL_EXPR(true || false);
TEST_BOOL_EXPR(true || true);
TEST_BOOL_EXPR(false || false);
TEST_BOOL_EXPR(false || true);
TEST_BOOL_EXPR(true && false);
TEST_BOOL_EXPR(true && true);
TEST_BOOL_EXPR(false && false);
TEST_BOOL_EXPR(false && true);
TEST_BOOL_EXPR(true || false && true);
TEST_BOOL_EXPR(true || false && false);
TEST_BOOL_EXPR(true || true && true);
TEST_BOOL_EXPR(true || true && false);
TEST_BOOL_EXPR(false || false && true);
TEST_BOOL_EXPR(false || false && false);
TEST_BOOL_EXPR(false || true && true);
TEST_BOOL_EXPR(false || true && false);
TEST_BOOL_EXPR(true && false || true);
TEST_BOOL_EXPR(true && false || false);
TEST_BOOL_EXPR(true && true || true);
TEST_BOOL_EXPR(true && true || false);
TEST_BOOL_EXPR(false && false || true);
TEST_BOOL_EXPR(false && false || false);
TEST_BOOL_EXPR(false && true || true);
TEST_BOOL_EXPR(false && true || false);
TEST_BOOL_EXPR(t || false);
TEST_BOOL_EXPR(t || true);
TEST_BOOL_EXPR(f || false);
TEST_BOOL_EXPR(f || true);
TEST_BOOL_EXPR(!true);
TEST_BOOL_EXPR(!false);
TEST_BOOL_EXPR(!true || true);
TEST_BOOL_EXPR(!true && !false || !true);
TEST_BOOL_EXPR(!true && !false || true);
TEST_BOOL_EXPR(!true && false || !true);
TEST_BOOL_EXPR(!true && false || true);
TEST_BOOL_EXPR(true && !false || !true);
TEST_BOOL_EXPR(true && !false || true);
TEST_BOOL_EXPR(true && false || !true);
TEST_BOOL_EXPR(true && false || true);
TEST_BOOL_EXPR(!(true && f) || !t);
TEST_BOOL_EXPR(!!!(t) && (!!f || true));
TEST_BOOL_EXPR(!(true && f) && true);
}

View file

@ -27,6 +27,9 @@ private slots:
void testVariant();
void testAccumulateTime();
void testAnimPose();
void testExpressionTokenizer();
void testExpressionParser();
void testExpressionEvaluator();
};
#endif // hifi_AnimTests_h

Some files were not shown because too many files have changed in this diff Show more