mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-16 12:17:31 +02:00
merge from upstream
This commit is contained in:
commit
bfa8cc1969
107 changed files with 8981 additions and 1451 deletions
56
cmake/externals/neuron/CMakeLists.txt
vendored
Normal file
56
cmake/externals/neuron/CMakeLists.txt
vendored
Normal 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()
|
||||
|
17
cmake/macros/TargetNeuron.cmake
Normal file
17
cmake/macros/TargetNeuron.cmake
Normal 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()
|
28
cmake/modules/FindNeuron.cmake
Normal file
28
cmake/modules/FindNeuron.cmake
Normal 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)
|
||||
|
72
examples/acScripts/triggeredRecordingOnAC.js
Normal file
72
examples/acScripts/triggeredRecordingOnAC.js
Normal 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();
|
||||
}
|
||||
});
|
54
examples/controllers/getHUDLookAtPositionTest.js
Normal file
54
examples/controllers/getHUDLookAtPositionTest.js
Normal 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
239
examples/controllers/neuron/neuronAvatar.js
Normal file
239
examples/controllers/neuron/neuronAvatar.js
Normal 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);
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
145
examples/flowArts/arcBall/arcBall.js
Normal file
145
examples/flowArts/arcBall/arcBall.js
Normal 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;
|
||||
}
|
155
examples/flowArts/arcBall/arcBallEntityScript.js
Normal file
155
examples/flowArts/arcBall/arcBallEntityScript.js
Normal 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();
|
||||
});
|
91
examples/flowArts/flowArtsHutSpawner.js
Normal file
91
examples/flowArts/flowArtsHutSpawner.js
Normal 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);
|
145
examples/flowArts/lightBall/lightBall.js
Normal file
145
examples/flowArts/lightBall/lightBall.js
Normal 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;
|
||||
}
|
67
examples/flowArts/lightSaber/lightSaber.js
Normal file
67
examples/flowArts/lightSaber/lightSaber.js
Normal 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;
|
||||
}
|
116
examples/flowArts/lightSaber/lightSaberEntityScript.js
Normal file
116
examples/flowArts/lightSaber/lightSaberEntityScript.js
Normal 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();
|
||||
});
|
191
examples/flowArts/lightTrails.js
Normal file
191
examples/flowArts/lightTrails.js
Normal 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));
|
||||
}
|
95
examples/flowArts/raveStick/raveStick.js
Normal file
95
examples/flowArts/raveStick/raveStick.js
Normal 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;
|
||||
}
|
141
examples/flowArts/raveStick/raveStickEntityScript.js
Normal file
141
examples/flowArts/raveStick/raveStickEntityScript.js
Normal 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
|
@ -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]);
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
29
examples/light_modifier/README.md
Normal file
29
examples/light_modifier/README.md
Normal 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
|
||||
|
||||

|
36
examples/light_modifier/closeButton.js
Normal file
36
examples/light_modifier/closeButton.js
Normal 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();
|
||||
});
|
20
examples/light_modifier/lightLoader.js
Normal file
20
examples/light_modifier/lightLoader.js
Normal 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)
|
876
examples/light_modifier/lightModifier.js
Normal file
876
examples/light_modifier/lightModifier.js
Normal 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
|
73
examples/light_modifier/lightModifierTestScene.js
Normal file
73
examples/light_modifier/lightModifierTestScene.js
Normal 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();
|
40
examples/light_modifier/lightParent.js
Normal file
40
examples/light_modifier/lightParent.js
Normal 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();
|
||||
});
|
105
examples/light_modifier/slider.js
Normal file
105
examples/light_modifier/slider.js
Normal 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();
|
||||
});
|
40
examples/light_modifier/visiblePanel.js
Normal file
40
examples/light_modifier/visiblePanel.js
Normal 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();
|
||||
});
|
|
@ -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 = [];
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
73
examples/rayPickingFilterExample.js
Normal file
73
examples/rayPickingFilterExample.js
Normal 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);
|
|
@ -241,9 +241,9 @@
|
|||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
},
|
||||
creatorSessionUUID: MyAvatar.sessionUUID
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
var makeArrowStick = function(entityA, entityB, collision) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
})
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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"
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
73
examples/winterSmashUp/targetPractice/shooterPlatform.js
Normal file
73
examples/winterSmashUp/targetPractice/shooterPlatform.js
Normal 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;
|
||||
}
|
||||
};
|
||||
});
|
93
examples/winterSmashUp/targetPractice/startTargetPractice.js
Normal file
93
examples/winterSmashUp/targetPractice/startTargetPractice.js
Normal 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);
|
||||
});
|
227
examples/winterSmashUp/targetPractice/targetPracticeGame.js
Normal file
227
examples/winterSmashUp/targetPractice/targetPracticeGame.js
Normal 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();
|
||||
});
|
|
@ -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
|
||||
|
|
7
interface/resources/controllers/neuron.json
Normal file
7
interface/resources/controllers/neuron.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "Neuron to Standard",
|
||||
"channels": [
|
||||
{ "from": "Hydra.LeftHand", "to": "Standard.LeftHand" },
|
||||
{ "from": "Hydra.RightHand", "to": "Standard.RightHand" }
|
||||
]
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
|
676
libraries/animation/src/AnimExpression.cpp
Normal file
676
libraries/animation/src/AnimExpression.cpp
Normal 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
|
158
libraries/animation/src/AnimExpression.h
Normal file
158
libraries/animation/src/AnimExpression.h
Normal 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
|
||||
|
|
@ -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();
|
||||
|
|
|
@ -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.)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ void main(void) {
|
|||
// pass along the diffuse color
|
||||
varColor = colorToLinearRGBA(inColor);
|
||||
|
||||
|
||||
// standard transform
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ void main(void) {
|
|||
|
||||
packDeferredFragment(
|
||||
normalize(_normal.xyz),
|
||||
glowIntensity * texel.a,
|
||||
texel.a,
|
||||
_color.rgb * texel.rgb,
|
||||
DEFAULT_SPECULAR, DEFAULT_SHININESS);
|
||||
}
|
|
@ -27,7 +27,7 @@ void main(void) {
|
|||
|
||||
packDeferredFragmentLightmap(
|
||||
normalize(_normal),
|
||||
glowIntensity * texel.a,
|
||||
texel.a,
|
||||
_color.rgb,
|
||||
DEFAULT_SPECULAR, DEFAULT_SHININESS,
|
||||
texel.rgb);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
13
plugins/hifiNeuron/CMakeLists.txt
Normal file
13
plugins/hifiNeuron/CMakeLists.txt
Normal 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()
|
||||
|
557
plugins/hifiNeuron/src/NeuronPlugin.cpp
Normal file
557
plugins/hifiNeuron/src/NeuronPlugin.cpp
Normal 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());
|
||||
}
|
85
plugins/hifiNeuron/src/NeuronPlugin.h
Normal file
85
plugins/hifiNeuron/src/NeuronPlugin.h
Normal 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
|
||||
|
45
plugins/hifiNeuron/src/NeuronProvider.cpp
Normal file
45
plugins/hifiNeuron/src/NeuronProvider.cpp
Normal 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"
|
1
plugins/hifiNeuron/src/plugin.json
Normal file
1
plugins/hifiNeuron/src/plugin.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -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 };
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue