mirror of
https://github.com/overte-org/overte.git
synced 2025-04-22 23:53:48 +02:00
Merge remote-tracking branch 'upstream/master' into render_cursor
This commit is contained in:
commit
b7cd1ea72a
46 changed files with 1449 additions and 268 deletions
CMakeLists.txt
cmake/macros
examples
interface/src
Application.cppApplication.hInterfaceActionFactory.cppInterfaceActionFactory.h
avatar
devices
scripting
ui
libraries
entities-renderer/src
EntityTreeRenderer.cppRenderableBoxEntityItem.cppRenderableDebugableEntityItem.cppRenderableSphereEntityItem.cppRenderableZoneEntityItem.cppRenderableZoneEntityItem.h
entities/src
EntityActionFactoryInterface.hEntityActionInterface.cppEntityActionInterface.hEntityItem.hEntityScriptingInterface.cppEntitySimulation.h
physics/src
ObjectAction.cppObjectAction.hObjectActionPullToPoint.cppObjectActionPullToPoint.hObjectActionSpring.cppObjectActionSpring.hPhysicalEntitySimulation.cppPhysicalEntitySimulation.h
render-utils/src
render/src/render
script-engine/src
|
@ -53,7 +53,7 @@ else ()
|
|||
endif ()
|
||||
endif(WIN32)
|
||||
|
||||
if (NOT MSVC12)
|
||||
if ((NOT MSVC12) AND (NOT MSVC14))
|
||||
include(CheckCXXCompilerFlag)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
|
||||
|
|
|
@ -14,12 +14,12 @@ macro(SETUP_EXTERNALS_BINARY_DIR)
|
|||
# get a short name for the generator to use in the path
|
||||
STRING(REGEX REPLACE " " "-" CMAKE_GENERATOR_FOLDER_NAME ${CMAKE_GENERATOR})
|
||||
|
||||
if (MSVC12)
|
||||
if (MSVC12)
|
||||
set(CMAKE_GENERATOR_FOLDER_NAME "vc12")
|
||||
else ()
|
||||
if (CMAKE_GENERATOR_FOLDER_NAME STREQUAL "Unix-Makefiles")
|
||||
set(CMAKE_GENERATOR_FOLDER_NAME "makefiles")
|
||||
endif ()
|
||||
elseif (MSVC14)
|
||||
set(CMAKE_GENERATOR_FOLDER_NAME "vc14")
|
||||
elseif(CMAKE_GENERATOR_FOLDER_NAME STREQUAL "Unix-Makefiles")
|
||||
set(CMAKE_GENERATOR_FOLDER_NAME "makefiles")
|
||||
endif ()
|
||||
|
||||
set(EXTERNALS_BINARY_ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/ext")
|
||||
|
|
96
examples/example/scripts/controllerScriptingExamples.js
Normal file
96
examples/example/scripts/controllerScriptingExamples.js
Normal file
|
@ -0,0 +1,96 @@
|
|||
//
|
||||
// controllerScriptingExamples.js
|
||||
// examples
|
||||
//
|
||||
// Created by Sam Gondelman on 6/2/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
|
||||
//
|
||||
|
||||
// Assumes you only have the default keyboard connected
|
||||
|
||||
// Resets every device to its default key bindings:
|
||||
Controller.resetAllDeviceBindings();
|
||||
|
||||
// Query all actions
|
||||
print("All Actions: \n" + Controller.getAllActions());
|
||||
|
||||
// Each action stores:
|
||||
// action: int representation of enum
|
||||
print("Action 5 int: \n" + Controller.getAllActions()[5].action);
|
||||
|
||||
// actionName: string representation of enum
|
||||
print("Action 5 name: \n" + Controller.getAllActions()[5].actionName);
|
||||
|
||||
// inputChannels: list of all inputchannels that control that action
|
||||
print("Action 5 input channels: \n" + Controller.getAllActions()[5].inputChannels + "\n");
|
||||
|
||||
|
||||
// Each input channel stores:
|
||||
// action: Action that this InputChannel maps to
|
||||
print("Input channel action: \n" + Controller.getAllActions()[5].inputChannels[0].action);
|
||||
|
||||
// scale: sensitivity of input
|
||||
print("Input channel scale: \n" + Controller.getAllActions()[5].inputChannels[0].scale);
|
||||
|
||||
// input and modifier: Inputs
|
||||
print("Input channel input and modifier: \n" + Controller.getAllActions()[5].inputChannels[0].input + "\n" + Controller.getAllActions()[5].inputChannels[0].modifier + "\n");
|
||||
|
||||
|
||||
// Each Input stores:
|
||||
// device: device of input
|
||||
print("Input device: \n" + Controller.getAllActions()[5].inputChannels[0].input.device);
|
||||
|
||||
// channel: channel of input
|
||||
print("Input channel: \n" + Controller.getAllActions()[5].inputChannels[0].input.channel);
|
||||
|
||||
// type: type of input (Unknown, Button, Axis, Joint)
|
||||
print("Input type: \n" + Controller.getAllActions()[5].inputChannels[0].input.type);
|
||||
|
||||
// id: id of input
|
||||
print("Input id: \n" + Controller.getAllActions()[5].inputChannels[0].input.id + "\n");
|
||||
|
||||
|
||||
// You can get the name of a device from its id
|
||||
print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[5].inputChannels[0].input.id));
|
||||
|
||||
// You can also get all of a devices input channels
|
||||
print("Device 1's input channels: \n" + Controller.getAllInputsForDevice(1) + "\n");
|
||||
|
||||
|
||||
// Modifying properties:
|
||||
// The following code will switch the "w" and "s" key functionality and adjust their scales
|
||||
var s = Controller.getAllActions()[0].inputChannels[0];
|
||||
var w = Controller.getAllActions()[1].inputChannels[0];
|
||||
|
||||
// You must remove an input controller before modifying it so the old input controller isn't registered anymore
|
||||
// removeInputChannel and addInputChannel return true if successful, false otherwise
|
||||
Controller.removeInputChannel(s);
|
||||
Controller.removeInputChannel(w);
|
||||
print(s.scale);
|
||||
s.action = 1;
|
||||
s.scale = .01;
|
||||
|
||||
w.action = 0;
|
||||
w.scale = 10000;
|
||||
Controller.addInputChannel(s);
|
||||
Controller.addInputChannel(w);
|
||||
print(s.scale);
|
||||
|
||||
// You can get all the available inputs for any device
|
||||
// Each AvailableInput has:
|
||||
// input: the Input itself
|
||||
// inputName: string representing the input
|
||||
var availableInputs = Controller.getAvailableInputs(1);
|
||||
for (i = 0; i < availableInputs.length; i++) {
|
||||
print(availableInputs[i].inputName);
|
||||
}
|
||||
|
||||
// You can modify key bindings by using these avaiable inputs
|
||||
// This will replace e (up) with 6
|
||||
var e = Controller.getAllActions()[5].inputChannels[0];
|
||||
Controller.removeInputChannel(e);
|
||||
e.input = availableInputs[6].input;
|
||||
Controller.addInputChannel(e);
|
100
examples/grab.js
100
examples/grab.js
|
@ -4,7 +4,7 @@
|
|||
// Created by Eric Levin on May 1, 2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Grab's physically moveable entities with the mouse, by applying a spring force.
|
||||
// Grab's physically moveable entities with the mouse, by applying a spring force.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -20,7 +20,7 @@ var ANGULAR_DAMPING_RATE = 0.40;
|
|||
// NOTE: to improve readability global variable names start with 'g'
|
||||
var gIsGrabbing = false;
|
||||
var gGrabbedEntity = null;
|
||||
var gPrevMouse = {x: 0, y: 0};
|
||||
var gActionID = null;
|
||||
var gEntityProperties;
|
||||
var gStartPosition;
|
||||
var gStartRotation;
|
||||
|
@ -31,20 +31,20 @@ var gPlaneNormal = ZERO_VEC3;
|
|||
// gMaxGrabDistance is a function of the size of the object.
|
||||
var gMaxGrabDistance;
|
||||
|
||||
// gGrabMode defines the degrees of freedom of the grab target positions
|
||||
// relative to gGrabStartPosition options include:
|
||||
// gGrabMode defines the degrees of freedom of the grab target positions
|
||||
// relative to gGrabStartPosition options include:
|
||||
// xzPlane (default)
|
||||
// verticalCylinder (SHIFT)
|
||||
// rotate (CONTROL)
|
||||
// Modes to eventually support?:
|
||||
// xyPlane
|
||||
// yzPlane
|
||||
// xyPlane
|
||||
// yzPlane
|
||||
// polar
|
||||
// elevationAzimuth
|
||||
var gGrabMode = "xzplane";
|
||||
var gGrabMode = "xzplane";
|
||||
|
||||
// gGrabOffset allows the user to grab an object off-center. It points from ray's intersection
|
||||
// with the move-plane to object center (at the moment the grab is initiated). Future target positions
|
||||
// gGrabOffset allows the user to grab an object off-center. It points from ray's intersection
|
||||
// with the move-plane to object center (at the moment the grab is initiated). Future target positions
|
||||
// are relative to the ray's intersection by the same offset.
|
||||
var gGrabOffset = { x: 0, y: 0, z: 0 };
|
||||
|
||||
|
@ -53,13 +53,14 @@ var gTargetRotation;
|
|||
var gLiftKey = false; // SHIFT
|
||||
var gRotateKey = false; // CONTROL
|
||||
|
||||
var gInitialMouse = { x: 0, y: 0 };
|
||||
var gPreviousMouse = { x: 0, y: 0 };
|
||||
var gMouseCursorLocation = { x: 0, y: 0 };
|
||||
var gMouseAtRotateStart = { x: 0, y: 0 };
|
||||
|
||||
var gBeaconHeight = 0.10;
|
||||
|
||||
var gAngularVelocity = ZERO_VEC3;
|
||||
// var gAngularVelocity = ZERO_VEC3;
|
||||
|
||||
// TODO: play sounds again when we aren't leaking AudioInjector threads
|
||||
// var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav");
|
||||
|
@ -140,6 +141,10 @@ function mouseIntersectionWithPlane(pointOnPlane, planeNormal, event) {
|
|||
}
|
||||
|
||||
function computeNewGrabPlane() {
|
||||
if (!gIsGrabbing) {
|
||||
return;
|
||||
}
|
||||
|
||||
var maybeResetMousePosition = false;
|
||||
if (gGrabMode !== "rotate") {
|
||||
gMouseAtRotateStart = gMouseCursorLocation;
|
||||
|
@ -162,7 +167,7 @@ function computeNewGrabPlane() {
|
|||
var xzOffset = Vec3.subtract(gPointOnPlane, Camera.getPosition());
|
||||
xzOffset.y = 0;
|
||||
gXzDistanceToGrab = Vec3.length(xzOffset);
|
||||
|
||||
|
||||
if (gGrabMode !== "rotate" && maybeResetMousePosition) {
|
||||
// we reset the mouse position whenever we stop rotating
|
||||
Window.setCursorPosition(gMouseAtRotateStart.x, gMouseAtRotateStart.y);
|
||||
|
@ -173,6 +178,7 @@ function mousePressEvent(event) {
|
|||
if (!event.isLeftButton) {
|
||||
return;
|
||||
}
|
||||
gInitialMouse = {x: event.x, y: event.y };
|
||||
gPreviousMouse = {x: event.x, y: event.y };
|
||||
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
|
@ -189,12 +195,13 @@ function mousePressEvent(event) {
|
|||
|
||||
var clickedEntity = pickResults.entityID;
|
||||
var entityProperties = Entities.getEntityProperties(clickedEntity)
|
||||
var objectPosition = entityProperties.position;
|
||||
gStartPosition = entityProperties.position;
|
||||
gStartRotation = entityProperties.rotation;
|
||||
var cameraPosition = Camera.getPosition();
|
||||
|
||||
gBeaconHeight = Vec3.length(entityProperties.dimensions);
|
||||
gMaxGrabDistance = gBeaconHeight / MAX_SOLID_ANGLE;
|
||||
if (Vec3.distance(objectPosition, cameraPosition) > gMaxGrabDistance) {
|
||||
if (Vec3.distance(gStartPosition, cameraPosition) > gMaxGrabDistance) {
|
||||
// don't allow grabs of things far away
|
||||
return;
|
||||
}
|
||||
|
@ -205,20 +212,20 @@ function mousePressEvent(event) {
|
|||
gGrabbedEntity = clickedEntity;
|
||||
gCurrentPosition = entityProperties.position;
|
||||
gOriginalGravity = entityProperties.gravity;
|
||||
gTargetPosition = objectPosition;
|
||||
gTargetPosition = gStartPosition;
|
||||
|
||||
// compute the grab point
|
||||
var nearestPoint = Vec3.subtract(objectPosition, cameraPosition);
|
||||
var nearestPoint = Vec3.subtract(gStartPosition, cameraPosition);
|
||||
var distanceToGrab = Vec3.dot(nearestPoint, pickRay.direction);
|
||||
nearestPoint = Vec3.multiply(distanceToGrab, pickRay.direction);
|
||||
gPointOnPlane = Vec3.sum(cameraPosition, nearestPoint);
|
||||
|
||||
// compute the grab offset
|
||||
gGrabOffset = Vec3.subtract(objectPosition, gPointOnPlane);
|
||||
gGrabOffset = Vec3.subtract(gStartPosition, gPointOnPlane);
|
||||
|
||||
computeNewGrabPlane();
|
||||
|
||||
updateDropLine(objectPosition);
|
||||
updateDropLine(gStartPosition);
|
||||
|
||||
// TODO: play sounds again when we aren't leaking AudioInjector threads
|
||||
//Audio.playSound(grabSound, { position: entityProperties.position, volume: VOLUME });
|
||||
|
@ -231,6 +238,8 @@ function mouseReleaseEvent() {
|
|||
}
|
||||
|
||||
gIsGrabbing = false
|
||||
Entities.deleteAction(gGrabbedEntity, gActionID);
|
||||
gActionID = null;
|
||||
|
||||
Overlays.editOverlay(gBeacon, { visible: false });
|
||||
|
||||
|
@ -250,18 +259,24 @@ function mouseMoveEvent(event) {
|
|||
gOriginalGravity = entityProperties.gravity;
|
||||
}
|
||||
|
||||
var actionArgs = {};
|
||||
|
||||
if (gGrabMode === "rotate") {
|
||||
var deltaMouse = { x: 0, y: 0 };
|
||||
var dx = event.x - gPreviousMouse.x;
|
||||
var dy = event.y - gPreviousMouse.y;
|
||||
|
||||
var dx = event.x - gInitialMouse.x;
|
||||
var dy = event.y - gInitialMouse.y;
|
||||
var orientation = Camera.getOrientation();
|
||||
var dragOffset = Vec3.multiply(dx, Quat.getRight(orientation));
|
||||
dragOffset = Vec3.sum(dragOffset, Vec3.multiply(-dy, Quat.getUp(orientation)));
|
||||
var axis = Vec3.cross(dragOffset, Quat.getFront(orientation));
|
||||
var axis = Vec3.normalize(axis);
|
||||
var ROTATE_STRENGTH = 8.0; // magic number tuned by hand
|
||||
gAngularVelocity = Vec3.multiply(ROTATE_STRENGTH, axis);
|
||||
axis = Vec3.normalize(axis);
|
||||
var ROTATE_STRENGTH = 0.4; // magic number tuned by hand
|
||||
var angle = ROTATE_STRENGTH * Math.sqrt((dx * dx) + (dy * dy));
|
||||
var deltaQ = Quat.angleAxis(angle, axis);
|
||||
// var qZero = entityProperties.rotation;
|
||||
var qZero = gStartRotation;
|
||||
var qOne = Quat.multiply(deltaQ, qZero);
|
||||
actionArgs = {targetRotation: qOne, angularTimeScale: 0.1};
|
||||
} else {
|
||||
var newTargetPosition;
|
||||
if (gGrabMode === "verticalCylinder") {
|
||||
|
@ -284,9 +299,18 @@ function mouseMoveEvent(event) {
|
|||
}
|
||||
}
|
||||
gTargetPosition = Vec3.sum(newTargetPosition, gGrabOffset);
|
||||
actionArgs = {targetPosition: gTargetPosition, linearTimeScale: 0.1};
|
||||
}
|
||||
gPreviousMouse = { x: event.x, y: event.y };
|
||||
gMouseCursorLocation = { x: Window.getCursorPositionX(), y: Window.getCursorPositionY() };
|
||||
|
||||
if (!gActionID) {
|
||||
gActionID = Entities.addAction("spring", gGrabbedEntity, actionArgs);
|
||||
} else {
|
||||
Entities.updateAction(gGrabbedEntity, gActionID, actionArgs);
|
||||
}
|
||||
|
||||
updateDropLine(gTargetPosition);
|
||||
}
|
||||
|
||||
function keyReleaseEvent(event) {
|
||||
|
@ -309,38 +333,8 @@ function keyPressEvent(event) {
|
|||
computeNewGrabPlane();
|
||||
}
|
||||
|
||||
function update(deltaTime) {
|
||||
if (!gIsGrabbing) {
|
||||
return;
|
||||
}
|
||||
|
||||
var entityProperties = Entities.getEntityProperties(gGrabbedEntity);
|
||||
gCurrentPosition = entityProperties.position;
|
||||
if (gGrabMode === "rotate") {
|
||||
gAngularVelocity = Vec3.subtract(gAngularVelocity, Vec3.multiply(gAngularVelocity, ANGULAR_DAMPING_RATE));
|
||||
Entities.editEntity(gGrabbedEntity, { angularVelocity: gAngularVelocity, });
|
||||
}
|
||||
|
||||
// always push toward linear grab position, even when rotating
|
||||
var newVelocity = ZERO_VEC3;
|
||||
var dPosition = Vec3.subtract(gTargetPosition, gCurrentPosition);
|
||||
var delta = Vec3.length(dPosition);
|
||||
if (delta > CLOSE_ENOUGH) {
|
||||
var MAX_POSITION_DELTA = 4.0;
|
||||
if (delta > MAX_POSITION_DELTA) {
|
||||
dPosition = Vec3.multiply(dPosition, MAX_POSITION_DELTA / delta);
|
||||
}
|
||||
// desired speed is proportional to displacement by the inverse of timescale
|
||||
// (for critically damped motion)
|
||||
newVelocity = Vec3.multiply(dPosition, INV_MOVE_TIMESCALE);
|
||||
}
|
||||
Entities.editEntity(gGrabbedEntity, { velocity: newVelocity, });
|
||||
updateDropLine(gTargetPosition);
|
||||
}
|
||||
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
Script.update.connect(update);
|
||||
|
|
|
@ -304,7 +304,8 @@ SelectionDisplay = (function () {
|
|||
visible: false,
|
||||
dashed: true,
|
||||
lineWidth: 2.0,
|
||||
ignoreRayIntersection: true // this never ray intersects
|
||||
ignoreRayIntersection: true, // this never ray intersects
|
||||
drawInFront: true
|
||||
});
|
||||
|
||||
var selectionBox = Overlays.addOverlay("cube", {
|
||||
|
|
92
examples/stick.js
Normal file
92
examples/stick.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
// stick.js
|
||||
// examples
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-10
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Allow avatar to hold a stick
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var hand = "left";
|
||||
var nullActionID = "00000000-0000-0000-0000-000000000000";
|
||||
var controllerID;
|
||||
var controllerActive;
|
||||
var stickID = null;
|
||||
var actionID = nullActionID;
|
||||
// sometimes if this is run immediately the stick doesn't get created? use a timer.
|
||||
Script.setTimeout(function() {
|
||||
stickID = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx",
|
||||
compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj",
|
||||
dimensions: {x: .11, y: .11, z: .59},
|
||||
position: MyAvatar.getRightPalmPosition(), // initial position doesn't matter, as long as it's close
|
||||
rotation: MyAvatar.orientation,
|
||||
damping: .1,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
actionID = Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -0.9},
|
||||
hand: hand,
|
||||
timeScale: 0.15});
|
||||
}, 3000);
|
||||
|
||||
|
||||
function cleanUp() {
|
||||
Entities.deleteEntity(stickID);
|
||||
}
|
||||
|
||||
|
||||
function positionStick(stickOrientation) {
|
||||
var baseOffset = {x: 0.0, y: 0.0, z: -0.9};
|
||||
var offset = Vec3.multiplyQbyV(stickOrientation, baseOffset);
|
||||
Entities.updateAction(stickID, actionID, {relativePosition: offset,
|
||||
relativeRotation: stickOrientation});
|
||||
}
|
||||
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
if (!stickID || actionID == nullActionID) {
|
||||
return;
|
||||
}
|
||||
var windowCenterX = Window.innerWidth / 2;
|
||||
var windowCenterY = Window.innerHeight / 2;
|
||||
var mouseXCenterOffset = event.x - windowCenterX;
|
||||
var mouseYCenterOffset = event.y - windowCenterY;
|
||||
var mouseXRatio = mouseXCenterOffset / windowCenterX;
|
||||
var mouseYRatio = mouseYCenterOffset / windowCenterY;
|
||||
|
||||
var stickOrientation = Quat.fromPitchYawRollDegrees(mouseYRatio * -90, mouseXRatio * -90, 0);
|
||||
positionStick(stickOrientation);
|
||||
}
|
||||
|
||||
|
||||
function initControls(){
|
||||
if (hand == "right") {
|
||||
controllerID = 3; // right handed
|
||||
} else {
|
||||
controllerID = 4; // left handed
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function update(deltaTime){
|
||||
var palmPosition = Controller.getSpatialControlPosition(controllerID);
|
||||
controllerActive = (Vec3.length(palmPosition) > 0);
|
||||
if(!controllerActive){
|
||||
return;
|
||||
}
|
||||
|
||||
stickOrientation = Controller.getSpatialControlRawRotation(controllerID);
|
||||
var adjustment = Quat.fromPitchYawRollDegrees(180, 0, 0);
|
||||
stickOrientation = Quat.multiply(stickOrientation, adjustment);
|
||||
|
||||
positionStick(stickOrientation);
|
||||
}
|
||||
|
||||
|
||||
Script.scriptEnding.connect(cleanUp);
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
Script.update.connect(update);
|
|
@ -100,6 +100,7 @@
|
|||
#include "ModelPackager.h"
|
||||
#include "Util.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "InterfaceActionFactory.h"
|
||||
|
||||
#include "avatar/AvatarManager.h"
|
||||
|
||||
|
@ -257,6 +258,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
|
||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||
DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();
|
||||
DependencyManager::registerInheritance<EntityActionFactoryInterface, InterfaceActionFactory>();
|
||||
|
||||
Setting::init();
|
||||
|
||||
|
@ -293,7 +295,8 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
auto discoverabilityManager = DependencyManager::set<DiscoverabilityManager>();
|
||||
auto sceneScriptingInterface = DependencyManager::set<SceneScriptingInterface>();
|
||||
auto offscreenUi = DependencyManager::set<OffscreenUi>();
|
||||
auto pathUtils = DependencyManager::set<PathUtils>();
|
||||
auto pathUtils = DependencyManager::set<PathUtils>();
|
||||
auto actionFactory = DependencyManager::set<InterfaceActionFactory>();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -149,6 +149,7 @@ public:
|
|||
static glm::quat getOrientationForPath() { return getInstance()->_myAvatar->getOrientation(); }
|
||||
static glm::vec3 getPositionForAudio() { return getInstance()->_myAvatar->getHead()->getPosition(); }
|
||||
static glm::quat getOrientationForAudio() { return getInstance()->_myAvatar->getHead()->getFinalOrientationInWorldFrame(); }
|
||||
static UserInputMapper* getUserInputMapper() { return &getInstance()->_userInputMapper; }
|
||||
static void initPlugins();
|
||||
static void shutdownPlugins();
|
||||
|
||||
|
|
49
interface/src/InterfaceActionFactory.cpp
Normal file
49
interface/src/InterfaceActionFactory.cpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// InterfaceActionFactory.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-2
|
||||
// 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 <avatar/AvatarActionHold.h>
|
||||
#include <ObjectActionPullToPoint.h>
|
||||
#include <ObjectActionSpring.h>
|
||||
|
||||
#include "InterfaceActionFactory.h"
|
||||
|
||||
|
||||
EntityActionPointer InterfaceActionFactory::factory(EntitySimulation* simulation,
|
||||
EntityActionType type,
|
||||
QUuid id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) {
|
||||
EntityActionPointer action = nullptr;
|
||||
switch (type) {
|
||||
case ACTION_TYPE_NONE:
|
||||
return nullptr;
|
||||
case ACTION_TYPE_PULL_TO_POINT:
|
||||
action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity);
|
||||
break;
|
||||
case ACTION_TYPE_SPRING:
|
||||
action = (EntityActionPointer) new ObjectActionSpring(id, ownerEntity);
|
||||
break;
|
||||
case ACTION_TYPE_HOLD:
|
||||
action = (EntityActionPointer) new AvatarActionHold(id, ownerEntity);
|
||||
break;
|
||||
}
|
||||
|
||||
bool ok = action->updateArguments(arguments);
|
||||
if (ok) {
|
||||
ownerEntity->addAction(simulation, action);
|
||||
return action;
|
||||
}
|
||||
|
||||
action = nullptr;
|
||||
return action;
|
||||
}
|
28
interface/src/InterfaceActionFactory.h
Normal file
28
interface/src/InterfaceActionFactory.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// InterfaceActionFactory.cpp
|
||||
// interface/src/
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-10
|
||||
// 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_InterfaceActionFactory_h
|
||||
#define hifi_InterfaceActionFactory_h
|
||||
|
||||
#include "EntityActionFactoryInterface.h"
|
||||
|
||||
class InterfaceActionFactory : public EntityActionFactoryInterface {
|
||||
public:
|
||||
InterfaceActionFactory() : EntityActionFactoryInterface() { }
|
||||
virtual ~InterfaceActionFactory() { }
|
||||
virtual EntityActionPointer factory(EntitySimulation* simulation,
|
||||
EntityActionType type,
|
||||
QUuid id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments);
|
||||
};
|
||||
|
||||
#endif // hifi_InterfaceActionFactory_h
|
108
interface/src/avatar/AvatarActionHold.cpp
Normal file
108
interface/src/avatar/AvatarActionHold.cpp
Normal file
|
@ -0,0 +1,108 @@
|
|||
//
|
||||
// AvatarActionHold.cpp
|
||||
// interface/src/avatar/
|
||||
//
|
||||
// Created by Seth Alves 2015-6-9
|
||||
// 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 "avatar/MyAvatar.h"
|
||||
#include "avatar/AvatarManager.h"
|
||||
|
||||
#include "AvatarActionHold.h"
|
||||
|
||||
AvatarActionHold::AvatarActionHold(QUuid id, EntityItemPointer ownerEntity) :
|
||||
ObjectActionSpring(id, ownerEntity) {
|
||||
#if WANT_DEBUG
|
||||
qDebug() << "AvatarActionHold::AvatarActionHold";
|
||||
#endif
|
||||
}
|
||||
|
||||
AvatarActionHold::~AvatarActionHold() {
|
||||
#if WANT_DEBUG
|
||||
qDebug() << "AvatarActionHold::~AvatarActionHold";
|
||||
#endif
|
||||
}
|
||||
|
||||
void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
glm::vec3 palmPosition;
|
||||
if (_hand == "right") {
|
||||
palmPosition = myAvatar->getRightPalmPosition();
|
||||
} else {
|
||||
palmPosition = myAvatar->getLeftPalmPosition();
|
||||
}
|
||||
|
||||
auto rotation = myAvatar->getWorldAlignedOrientation();
|
||||
auto offset = rotation * _relativePosition;
|
||||
auto position = palmPosition + offset;
|
||||
rotation *= _relativeRotation;
|
||||
|
||||
lockForWrite();
|
||||
_positionalTarget = position;
|
||||
_rotationalTarget = rotation;
|
||||
unlock();
|
||||
|
||||
ObjectActionSpring::updateActionWorker(deltaTimeStep);
|
||||
}
|
||||
|
||||
|
||||
bool AvatarActionHold::updateArguments(QVariantMap arguments) {
|
||||
bool rPOk = true;
|
||||
glm::vec3 relativePosition =
|
||||
EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", rPOk, false);
|
||||
bool rROk = true;
|
||||
glm::quat relativeRotation =
|
||||
EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", rROk, false);
|
||||
bool tSOk = true;
|
||||
float timeScale =
|
||||
EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", tSOk, false);
|
||||
bool hOk = true;
|
||||
QString hand =
|
||||
EntityActionInterface::extractStringArgument("hold", arguments, "hand", hOk, false);
|
||||
|
||||
lockForWrite();
|
||||
if (rPOk) {
|
||||
_relativePosition = relativePosition;
|
||||
} else if (!_parametersSet) {
|
||||
_relativePosition = glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
if (rROk) {
|
||||
_relativeRotation = relativeRotation;
|
||||
} else if (!_parametersSet) {
|
||||
_relativeRotation = glm::quat(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
if (tSOk) {
|
||||
_linearTimeScale = timeScale;
|
||||
_angularTimeScale = timeScale;
|
||||
} else if (!_parametersSet) {
|
||||
_linearTimeScale = 0.2;
|
||||
_angularTimeScale = 0.2;
|
||||
}
|
||||
|
||||
if (hOk) {
|
||||
hand = hand.toLower();
|
||||
if (hand == "left") {
|
||||
_hand = "left";
|
||||
} else if (hand == "right") {
|
||||
_hand = "right";
|
||||
} else {
|
||||
qDebug() << "hold action -- invalid hand argument:" << hand;
|
||||
_hand = "right";
|
||||
}
|
||||
} else if (!_parametersSet) {
|
||||
_hand = "right";
|
||||
}
|
||||
|
||||
_parametersSet = true;
|
||||
_positionalTargetSet = true;
|
||||
_rotationalTargetSet = true;
|
||||
_active = true;
|
||||
unlock();
|
||||
return true;
|
||||
}
|
35
interface/src/avatar/AvatarActionHold.h
Normal file
35
interface/src/avatar/AvatarActionHold.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// AvatarActionHold.h
|
||||
// interface/src/avatar/
|
||||
//
|
||||
// Created by Seth Alves 2015-6-9
|
||||
// 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_AvatarActionHold_h
|
||||
#define hifi_AvatarActionHold_h
|
||||
|
||||
#include <QUuid>
|
||||
|
||||
#include <EntityItem.h>
|
||||
#include <ObjectActionSpring.h>
|
||||
|
||||
class AvatarActionHold : public ObjectActionSpring {
|
||||
public:
|
||||
AvatarActionHold(QUuid id, EntityItemPointer ownerEntity);
|
||||
virtual ~AvatarActionHold();
|
||||
|
||||
virtual bool updateArguments(QVariantMap arguments);
|
||||
virtual void updateActionWorker(float deltaTimeStep);
|
||||
|
||||
private:
|
||||
glm::vec3 _relativePosition;
|
||||
glm::quat _relativeRotation;
|
||||
QString _hand;
|
||||
bool _parametersSet = false;
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarActionHold_h
|
|
@ -159,9 +159,25 @@ void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) {
|
|||
// Grab the current free device ID
|
||||
_deviceID = mapper.getFreeDeviceID();
|
||||
|
||||
auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy());
|
||||
auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("Keyboard"));
|
||||
proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input._channel); };
|
||||
proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input._channel); };
|
||||
proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
|
||||
QVector<UserInputMapper::InputPair> availableInputs;
|
||||
for (int i = (int) Qt::Key_0; i <= (int) Qt::Key_9; i++) {
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
|
||||
}
|
||||
for (int i = (int) Qt::Key_A; i <= (int) Qt::Key_Z; i++) {
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
|
||||
}
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString()));
|
||||
return availableInputs;
|
||||
};
|
||||
proxy->resetDeviceBindings = [this, &mapper] () -> bool {
|
||||
mapper.removeAllInputChannelsForDevice(_deviceID);
|
||||
this->assignDefaultInputMapping(mapper);
|
||||
return true;
|
||||
};
|
||||
mapper.registerDevice(_deviceID, proxy);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,94 @@ ControllerScriptingInterface::ControllerScriptingInterface() :
|
|||
{
|
||||
|
||||
}
|
||||
static int actionMetaTypeId = qRegisterMetaType<UserInputMapper::Action>();
|
||||
static int inputChannelMetaTypeId = qRegisterMetaType<UserInputMapper::InputChannel>();
|
||||
static int inputMetaTypeId = qRegisterMetaType<UserInputMapper::Input>();
|
||||
static int inputPairMetaTypeId = qRegisterMetaType<UserInputMapper::InputPair>();
|
||||
|
||||
QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input);
|
||||
void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input);
|
||||
QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel);
|
||||
void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel);
|
||||
QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action);
|
||||
void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action);
|
||||
QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair);
|
||||
void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair);
|
||||
|
||||
QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("device", input.getDevice());
|
||||
obj.setProperty("channel", input.getChannel());
|
||||
obj.setProperty("type", (unsigned short) input.getType());
|
||||
obj.setProperty("id", input.getID());
|
||||
return obj;
|
||||
}
|
||||
|
||||
void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input) {
|
||||
input.setDevice(object.property("device").toUInt16());
|
||||
input.setChannel(object.property("channel").toUInt16());
|
||||
input.setType(object.property("type").toUInt16());
|
||||
input.setID(object.property("id").toInt32());
|
||||
}
|
||||
|
||||
QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("input", inputToScriptValue(engine, inputChannel.getInput()));
|
||||
obj.setProperty("modifier", inputToScriptValue(engine, inputChannel.getModifier()));
|
||||
obj.setProperty("action", inputChannel.getAction());
|
||||
obj.setProperty("scale", inputChannel.getScale());
|
||||
return obj;
|
||||
}
|
||||
|
||||
void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel) {
|
||||
UserInputMapper::Input input;
|
||||
UserInputMapper::Input modifier;
|
||||
inputFromScriptValue(object.property("input"), input);
|
||||
inputChannel.setInput(input);
|
||||
inputFromScriptValue(object.property("modifier"), modifier);
|
||||
inputChannel.setModifier(modifier);
|
||||
inputChannel.setAction(UserInputMapper::Action(object.property("action").toVariant().toInt()));
|
||||
inputChannel.setScale(object.property("scale").toVariant().toFloat());
|
||||
}
|
||||
|
||||
QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
QVector<UserInputMapper::InputChannel> inputChannels = Application::getUserInputMapper()->getInputChannelsForAction(action);
|
||||
QScriptValue _inputChannels = engine->newArray(inputChannels.size());
|
||||
for (int i = 0; i < inputChannels.size(); i++) {
|
||||
_inputChannels.setProperty(i, inputChannelToScriptValue(engine, inputChannels[i]));
|
||||
}
|
||||
obj.setProperty("action", (int) action);
|
||||
obj.setProperty("actionName", Application::getUserInputMapper()->getActionName(action));
|
||||
obj.setProperty("inputChannels", _inputChannels);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action) {
|
||||
action = UserInputMapper::Action(object.property("action").toVariant().toInt());
|
||||
}
|
||||
|
||||
QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("input", inputToScriptValue(engine, inputPair.first));
|
||||
obj.setProperty("inputName", inputPair.second);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair) {
|
||||
inputFromScriptValue(object.property("input"), inputPair.first);
|
||||
inputPair.second = QString(object.property("inputName").toVariant().toString());
|
||||
}
|
||||
|
||||
void ControllerScriptingInterface::registerControllerTypes(QScriptEngine* engine) {
|
||||
qScriptRegisterSequenceMetaType<QVector<UserInputMapper::Action> >(engine);
|
||||
qScriptRegisterSequenceMetaType<QVector<UserInputMapper::InputChannel> >(engine);
|
||||
qScriptRegisterSequenceMetaType<QVector<UserInputMapper::InputPair> >(engine);
|
||||
qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, inputChannelToScriptValue, inputChannelFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue);
|
||||
}
|
||||
|
||||
void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) {
|
||||
if (event->type() == HFActionEvent::startType()) {
|
||||
|
@ -337,6 +425,37 @@ void ControllerScriptingInterface::updateInputControllers() {
|
|||
}
|
||||
}
|
||||
|
||||
QVector<UserInputMapper::Action> ControllerScriptingInterface::getAllActions() {
|
||||
return Application::getUserInputMapper()->getAllActions();
|
||||
}
|
||||
|
||||
QVector<UserInputMapper::InputChannel> ControllerScriptingInterface::getInputChannelsForAction(UserInputMapper::Action action) {
|
||||
return Application::getUserInputMapper()->getInputChannelsForAction(action);
|
||||
}
|
||||
|
||||
QString ControllerScriptingInterface::getDeviceName(unsigned int device) {
|
||||
return Application::getUserInputMapper()->getDeviceName((unsigned short) device);
|
||||
}
|
||||
|
||||
QVector<UserInputMapper::InputChannel> ControllerScriptingInterface::getAllInputsForDevice(unsigned int device) {
|
||||
return Application::getUserInputMapper()->getAllInputsForDevice(device);
|
||||
}
|
||||
|
||||
bool ControllerScriptingInterface::addInputChannel(UserInputMapper::InputChannel inputChannel) {
|
||||
return Application::getUserInputMapper()->addInputChannel(inputChannel._action, inputChannel._input, inputChannel._modifier, inputChannel._scale);
|
||||
}
|
||||
|
||||
bool ControllerScriptingInterface::removeInputChannel(UserInputMapper::InputChannel inputChannel) {
|
||||
return Application::getUserInputMapper()->removeInputChannel(inputChannel);
|
||||
}
|
||||
|
||||
QVector<UserInputMapper::InputPair> ControllerScriptingInterface::getAvailableInputs(unsigned int device) {
|
||||
return Application::getUserInputMapper()->getAvailableInputs((unsigned short) device);
|
||||
}
|
||||
|
||||
void ControllerScriptingInterface::resetAllDeviceBindings() {
|
||||
Application::getUserInputMapper()->resetAllDeviceBindings();
|
||||
}
|
||||
|
||||
InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :
|
||||
AbstractInputController(),
|
||||
|
@ -373,4 +492,4 @@ const unsigned int INPUTCONTROLLER_KEY_DEVICE_MASK = 16;
|
|||
|
||||
InputController::Key InputController::getKey() const {
|
||||
return (((_deviceTrackerId & INPUTCONTROLLER_KEY_DEVICE_MASK) << INPUTCONTROLLER_KEY_DEVICE_OFFSET) | _subTrackerId);
|
||||
}
|
||||
}
|
|
@ -14,10 +14,11 @@
|
|||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include "ui/UserInputMapper.h"
|
||||
|
||||
#include <AbstractControllerScriptingInterface.h>
|
||||
class PalmData;
|
||||
|
||||
|
||||
class InputController : public AbstractInputController {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -54,6 +55,9 @@ class ControllerScriptingInterface : public AbstractControllerScriptingInterface
|
|||
|
||||
public:
|
||||
ControllerScriptingInterface();
|
||||
|
||||
virtual void registerControllerTypes(QScriptEngine* engine);
|
||||
|
||||
void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
|
||||
void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }
|
||||
|
||||
|
@ -79,6 +83,14 @@ public:
|
|||
void updateInputControllers();
|
||||
|
||||
public slots:
|
||||
Q_INVOKABLE virtual QVector<UserInputMapper::Action> getAllActions();
|
||||
Q_INVOKABLE virtual QVector<UserInputMapper::InputChannel> getInputChannelsForAction(UserInputMapper::Action action);
|
||||
Q_INVOKABLE virtual QString getDeviceName(unsigned int device);
|
||||
Q_INVOKABLE virtual QVector<UserInputMapper::InputChannel> getAllInputsForDevice(unsigned int device);
|
||||
Q_INVOKABLE virtual bool addInputChannel(UserInputMapper::InputChannel inputChannel);
|
||||
Q_INVOKABLE virtual bool removeInputChannel(UserInputMapper::InputChannel inputChannel);
|
||||
Q_INVOKABLE virtual QVector<UserInputMapper::InputPair> getAvailableInputs(unsigned int device);
|
||||
Q_INVOKABLE virtual void resetAllDeviceBindings();
|
||||
virtual bool isPrimaryButtonPressed() const;
|
||||
virtual glm::vec2 getPrimaryJoystickPosition() const;
|
||||
|
||||
|
|
|
@ -13,7 +13,15 @@
|
|||
|
||||
|
||||
// UserInputMapper Class
|
||||
|
||||
// Default contruct allocate the poutput size with the current hardcoded action channels
|
||||
UserInputMapper::UserInputMapper() {
|
||||
assignDefaulActionScales();
|
||||
createActionNames();
|
||||
}
|
||||
|
||||
bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){
|
||||
proxy->_name += " (" + QString::number(deviceID) + ")";
|
||||
_registeredDevices[deviceID] = proxy;
|
||||
return true;
|
||||
}
|
||||
|
@ -27,6 +35,12 @@ UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Inpu
|
|||
}
|
||||
}
|
||||
|
||||
void UserInputMapper::resetAllDeviceBindings() {
|
||||
for (auto device : _registeredDevices) {
|
||||
device.second->resetDeviceBindings();
|
||||
}
|
||||
}
|
||||
|
||||
bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) {
|
||||
return addInputChannel(action, input, Input(), scale);
|
||||
}
|
||||
|
@ -37,7 +51,7 @@ bool UserInputMapper::addInputChannel(Action action, const Input& input, const I
|
|||
qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped.";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
auto inputChannel = InputChannel(input, modifier, action, scale);
|
||||
|
||||
// Insert or replace the input to modifiers
|
||||
|
@ -61,6 +75,37 @@ int UserInputMapper::addInputChannels(const InputChannels& channels) {
|
|||
return nbAdded;
|
||||
}
|
||||
|
||||
bool UserInputMapper::removeInputChannel(InputChannel inputChannel) {
|
||||
// Remove from Input to Modifiers map
|
||||
if (inputChannel.hasModifier()) {
|
||||
_inputToModifiersMap.erase(inputChannel._input.getID());
|
||||
}
|
||||
|
||||
// Remove from Action to Inputs map
|
||||
std::pair<ActionToInputsMap::iterator, ActionToInputsMap::iterator> ret;
|
||||
ret = _actionToInputsMap.equal_range(inputChannel._action);
|
||||
for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) {
|
||||
if (it->second == inputChannel) {
|
||||
_actionToInputsMap.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UserInputMapper::removeAllInputChannels() {
|
||||
_inputToModifiersMap.clear();
|
||||
_actionToInputsMap.clear();
|
||||
}
|
||||
|
||||
void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) {
|
||||
QVector<InputChannel> channels = getAllInputsForDevice(device);
|
||||
for (auto& channel : channels) {
|
||||
removeInputChannel(channel);
|
||||
}
|
||||
}
|
||||
|
||||
int UserInputMapper::getInputChannels(InputChannels& channels) const {
|
||||
for (auto& channel : _actionToInputsMap) {
|
||||
channels.push_back(channel.second);
|
||||
|
@ -69,6 +114,20 @@ int UserInputMapper::getInputChannels(InputChannels& channels) const {
|
|||
return _actionToInputsMap.size();
|
||||
}
|
||||
|
||||
QVector<UserInputMapper::InputChannel> UserInputMapper::getAllInputsForDevice(uint16 device) {
|
||||
InputChannels allChannels;
|
||||
getInputChannels(allChannels);
|
||||
|
||||
QVector<InputChannel> channels;
|
||||
for (InputChannel inputChannel : allChannels) {
|
||||
if (inputChannel._input._device == device) {
|
||||
channels.push_back(inputChannel);
|
||||
}
|
||||
}
|
||||
|
||||
return channels;
|
||||
}
|
||||
|
||||
void UserInputMapper::update(float deltaTime) {
|
||||
|
||||
// Reset the axis state for next loop
|
||||
|
@ -130,6 +189,24 @@ void UserInputMapper::update(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
QVector<UserInputMapper::Action> UserInputMapper::getAllActions() {
|
||||
QVector<Action> actions;
|
||||
for (auto i = 0; i < NUM_ACTIONS; i++) {
|
||||
actions.append(Action(i));
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
||||
QVector<UserInputMapper::InputChannel> UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) {
|
||||
QVector<InputChannel> inputChannels;
|
||||
std::pair <ActionToInputsMap::iterator, ActionToInputsMap::iterator> ret;
|
||||
ret = _actionToInputsMap.equal_range(action);
|
||||
for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) {
|
||||
inputChannels.append(it->second);
|
||||
}
|
||||
return inputChannels;
|
||||
}
|
||||
|
||||
void UserInputMapper::assignDefaulActionScales() {
|
||||
_actionScales[LONGITUDINAL_BACKWARD] = 1.0f; // 1m per unit
|
||||
_actionScales[LONGITUDINAL_FORWARD] = 1.0f; // 1m per unit
|
||||
|
@ -144,3 +221,20 @@ void UserInputMapper::assignDefaulActionScales() {
|
|||
_actionScales[BOOM_IN] = 1.0f; // 1m per unit
|
||||
_actionScales[BOOM_OUT] = 1.0f; // 1m per unit
|
||||
}
|
||||
|
||||
// This is only necessary as long as the actions are hardcoded
|
||||
// Eventually you can just add the string when you add the action
|
||||
void UserInputMapper::createActionNames() {
|
||||
_actionNames[LONGITUDINAL_BACKWARD] = "LONGITUDINAL_BACKWARD";
|
||||
_actionNames[LONGITUDINAL_FORWARD] = "LONGITUDINAL_FORWARD";
|
||||
_actionNames[LATERAL_LEFT] = "LATERAL_LEFT";
|
||||
_actionNames[LATERAL_RIGHT] = "LATERAL_RIGHT";
|
||||
_actionNames[VERTICAL_DOWN] = "VERTICAL_DOWN";
|
||||
_actionNames[VERTICAL_UP] = "VERTICAL_UP";
|
||||
_actionNames[YAW_LEFT] = "YAW_LEFT";
|
||||
_actionNames[YAW_RIGHT] = "YAW_RIGHT";
|
||||
_actionNames[PITCH_DOWN] = "PITCH_DOWN";
|
||||
_actionNames[PITCH_UP] = "PITCH_UP";
|
||||
_actionNames[BOOM_IN] = "BOOM_IN";
|
||||
_actionNames[BOOM_OUT] = "BOOM_OUT";
|
||||
}
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
class UserInputMapper : public QObject {
|
||||
Q_OBJECT
|
||||
Q_ENUMS(Action)
|
||||
public:
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint32;
|
||||
|
@ -51,8 +52,13 @@ public:
|
|||
uint16 getDevice() const { return _device; }
|
||||
uint16 getChannel() const { return _channel; }
|
||||
uint32 getID() const { return _id; }
|
||||
|
||||
ChannelType getType() const { return (ChannelType) _type; }
|
||||
|
||||
void setDevice(uint16 device) { _device = device; }
|
||||
void setChannel(uint16 channel) { _channel = channel; }
|
||||
void setType(uint16 type) { _type = type; }
|
||||
void setID(uint32 ID) { _id = ID; }
|
||||
|
||||
bool isButton() const { return getType() == ChannelType::BUTTON; }
|
||||
bool isAxis() const { return getType() == ChannelType::AXIS; }
|
||||
bool isJoint() const { return getType() == ChannelType::JOINT; }
|
||||
|
@ -64,6 +70,7 @@ public:
|
|||
explicit Input(uint16 device, uint16 channel, ChannelType type) : _device(device), _channel(channel), _type(uint16(type)) {}
|
||||
Input(const Input& src) : _id(src._id) {}
|
||||
Input& operator = (const Input& src) { _id = src._id; return (*this); }
|
||||
bool operator ==(const Input& right) const { return _id == right._id; }
|
||||
bool operator < (const Input& src) const { return _id < src._id; }
|
||||
};
|
||||
|
||||
|
@ -83,22 +90,32 @@ public:
|
|||
typedef std::function<bool (const Input& input, int timestamp)> ButtonGetter;
|
||||
typedef std::function<float (const Input& input, int timestamp)> AxisGetter;
|
||||
typedef std::function<JointValue (const Input& input, int timestamp)> JointGetter;
|
||||
typedef QPair<Input, QString> InputPair;
|
||||
typedef std::function<QVector<InputPair> ()> AvailableInputGetter;
|
||||
typedef std::function<bool ()> ResetBindings;
|
||||
|
||||
typedef QVector<InputPair> AvailableInput;
|
||||
|
||||
class DeviceProxy {
|
||||
public:
|
||||
DeviceProxy() {}
|
||||
|
||||
ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; };
|
||||
AxisGetter getAxis = [] (const Input& input, int timestamp) -> bool { return 0.0f; };
|
||||
JointGetter getJoint = [] (const Input& input, int timestamp) -> JointValue { return JointValue(); };
|
||||
|
||||
typedef std::shared_ptr<DeviceProxy> Pointer;
|
||||
DeviceProxy(QString name) { _name = name; }
|
||||
|
||||
QString _name;
|
||||
ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; };
|
||||
AxisGetter getAxis = [] (const Input& input, int timestamp) -> bool { return 0.0f; };
|
||||
JointGetter getJoint = [] (const Input& input, int timestamp) -> JointValue { return JointValue(); };
|
||||
AvailableInputGetter getAvailabeInputs = [] () -> AvailableInput { return QVector<InputPair>(); };
|
||||
ResetBindings resetDeviceBindings = [] () -> bool { return true; };
|
||||
|
||||
typedef std::shared_ptr<DeviceProxy> Pointer;
|
||||
};
|
||||
// GetFreeDeviceID should be called before registering a device to use an ID not used by a different device.
|
||||
uint16 getFreeDeviceID() { return _nextFreeDeviceID++; }
|
||||
bool registerDevice(uint16 deviceID, const DeviceProxy::Pointer& device);
|
||||
DeviceProxy::Pointer getDeviceProxy(const Input& input);
|
||||
|
||||
QString getDeviceName(uint16 deviceID) { return _registeredDevices[deviceID]->_name; }
|
||||
QVector<InputPair> getAvailableInputs(uint16 deviceID) { return _registeredDevices[deviceID]->getAvailabeInputs(); }
|
||||
void resetAllDeviceBindings();
|
||||
|
||||
// Actions are the output channels of the Mapper, that's what the InputChannel map to
|
||||
// For now the Actions are hardcoded, this is bad, but we will fix that in the near future
|
||||
|
@ -123,7 +140,12 @@ public:
|
|||
|
||||
NUM_ACTIONS,
|
||||
};
|
||||
|
||||
std::vector<QString> _actionNames = std::vector<QString>(NUM_ACTIONS);
|
||||
void createActionNames();
|
||||
|
||||
QVector<Action> getAllActions();
|
||||
QString getActionName(Action action) { return UserInputMapper::_actionNames[(int) action]; }
|
||||
float getActionState(Action action) const { return _actionStates[action]; }
|
||||
void assignDefaulActionScales();
|
||||
|
||||
|
@ -140,27 +162,43 @@ public:
|
|||
Input _modifier = Input(); // make it invalid by default, meaning no modifier
|
||||
Action _action = LONGITUDINAL_BACKWARD;
|
||||
float _scale = 0.0f;
|
||||
|
||||
Input getInput() const { return _input; }
|
||||
Input getModifier() const { return _modifier; }
|
||||
Action getAction() const { return _action; }
|
||||
float getScale() const { return _scale; }
|
||||
|
||||
void setInput(Input input) { _input = input; }
|
||||
void setModifier(Input modifier) { _modifier = modifier; }
|
||||
void setAction(Action action) { _action = action; }
|
||||
void setScale(float scale) { _scale = scale; }
|
||||
|
||||
InputChannel() {}
|
||||
InputChannel(const Input& input, const Input& modifier, Action action, float scale = 1.0f) :
|
||||
_input(input), _modifier(modifier), _action(action), _scale(scale) {}
|
||||
InputChannel(const InputChannel& src) : InputChannel(src._input, src._modifier, src._action, src._scale) {}
|
||||
InputChannel& operator = (const InputChannel& src) { _input = src._input; _modifier = src._modifier; _action = src._action; _scale = src._scale; return (*this); }
|
||||
|
||||
bool operator ==(const InputChannel& right) const { return _input == right._input && _modifier == right._modifier && _action == right._action && _scale == right._scale; }
|
||||
bool hasModifier() { return _modifier.isValid(); }
|
||||
};
|
||||
typedef std::vector< InputChannel > InputChannels;
|
||||
|
||||
// Add a bunch of input channels, return the true number of channels that successfully were added
|
||||
int addInputChannels(const InputChannels& channels);
|
||||
// Remove the first found instance of the input channel from the input mapper, true if found
|
||||
bool removeInputChannel(InputChannel channel);
|
||||
void removeAllInputChannels();
|
||||
void removeAllInputChannelsForDevice(uint16 device);
|
||||
//Grab all the input channels currently in use, return the number
|
||||
int getInputChannels(InputChannels& channels) const;
|
||||
QVector<InputChannel> getAllInputsForDevice(uint16 device);
|
||||
QVector<InputChannel> getInputChannelsForAction(UserInputMapper::Action action);
|
||||
std::multimap<Action, InputChannel> getActionToInputsMap() { return _actionToInputsMap; }
|
||||
|
||||
// Update means go grab all the device input channels and update the output channel values
|
||||
void update(float deltaTime);
|
||||
|
||||
// Default contruct allocate the poutput size with the current hardcoded action channels
|
||||
UserInputMapper() { assignDefaulActionScales(); }
|
||||
|
||||
UserInputMapper();
|
||||
|
||||
protected:
|
||||
typedef std::map<int, DeviceProxy::Pointer> DevicesMap;
|
||||
|
@ -177,4 +215,12 @@ protected:
|
|||
std::vector<float> _actionScales = std::vector<float>(NUM_ACTIONS, 1.0f);
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(UserInputMapper::InputPair)
|
||||
Q_DECLARE_METATYPE(QVector<UserInputMapper::InputPair>)
|
||||
Q_DECLARE_METATYPE(UserInputMapper::Input)
|
||||
Q_DECLARE_METATYPE(UserInputMapper::InputChannel)
|
||||
Q_DECLARE_METATYPE(QVector<UserInputMapper::InputChannel>)
|
||||
Q_DECLARE_METATYPE(UserInputMapper::Action)
|
||||
Q_DECLARE_METATYPE(QVector<UserInputMapper::Action>)
|
||||
|
||||
#endif // hifi_UserInputMapper_h
|
||||
|
|
|
@ -123,6 +123,7 @@ protected:
|
|||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay);
|
||||
template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay);
|
||||
template <> int payloadGetLayer(const Overlay::Pointer& overlay);
|
||||
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args);
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace render {
|
|||
template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay) {
|
||||
if (overlay->is3D() && !static_cast<Base3DOverlay*>(overlay.get())->getDrawOnHUD()) {
|
||||
if (static_cast<Base3DOverlay*>(overlay.get())->getDrawInFront()) {
|
||||
return ItemKey::Builder().withTypeShape().withNoDepthSort().build();
|
||||
return ItemKey::Builder().withTypeShape().withLayered().build();
|
||||
} else {
|
||||
return ItemKey::Builder::opaqueShape();
|
||||
}
|
||||
|
@ -53,6 +53,17 @@ namespace render {
|
|||
return AABox(glm::vec3(bounds.x(), bounds.y(), 0.0f), glm::vec3(bounds.width(), bounds.height(), 0.1f));
|
||||
}
|
||||
}
|
||||
template <> int payloadGetLayer(const Overlay::Pointer& overlay) {
|
||||
// MAgic number while we are defining the layering mechanism:
|
||||
const int LAYER_2D = 2;
|
||||
const int LAYER_3D_FRONT = 1;
|
||||
const int LAYER_3D = 0;
|
||||
if (overlay->is3D()) {
|
||||
return (static_cast<Base3DOverlay*>(overlay.get())->getDrawInFront() ? LAYER_3D_FRONT : LAYER_3D);
|
||||
} else {
|
||||
return LAYER_2D;
|
||||
}
|
||||
}
|
||||
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) {
|
||||
if (args) {
|
||||
glPushMatrix();
|
||||
|
|
|
@ -1012,7 +1012,9 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
|
|||
void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) {
|
||||
checkAndCallPreload(entityID);
|
||||
auto entity = static_cast<EntityTree*>(_tree)->findEntityByID(entityID);
|
||||
addEntityToScene(entity);
|
||||
if (entity) {
|
||||
addEntityToScene(entity);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::addEntityToScene(EntityItemPointer entity) {
|
||||
|
|
|
@ -33,7 +33,7 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
|
|||
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
batch.setModelTransform(getTransformToCenter());
|
||||
batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(batch, 1.0f, cubeColor);
|
||||
|
||||
RenderableDebugableEntityItem::render(this, args);
|
||||
|
|
|
@ -24,9 +24,7 @@ void RenderableDebugableEntityItem::renderBoundingBox(EntityItem* entity, Render
|
|||
float puffedOut, glm::vec4& color) {
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
Transform transform = entity->getTransformToCenter();
|
||||
//transform.postScale(entity->getDimensions());
|
||||
batch.setModelTransform(transform);
|
||||
batch.setModelTransform(entity->getTransformToCenter()); // we want to include the scale as well
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f + puffedOut, color);
|
||||
}
|
||||
|
||||
|
@ -34,10 +32,9 @@ void RenderableDebugableEntityItem::render(EntityItem* entity, RenderArgs* args)
|
|||
if (args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP) {
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
Transform transform = entity->getTransformToCenter();
|
||||
transform.postScale(entity->getDimensions());
|
||||
batch.setModelTransform(transform);
|
||||
|
||||
batch.setModelTransform(entity->getTransformToCenter()); // we want to include the scale as well
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const QUuid& myNodeID = nodeList->getSessionUUID();
|
||||
bool highlightSimulationOwnership = (entity->getSimulatorID() == myNodeID);
|
||||
|
|
|
@ -39,7 +39,7 @@ void RenderableSphereEntityItem::render(RenderArgs* args) {
|
|||
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
batch.setModelTransform(getTransformToCenter());
|
||||
batch.setModelTransform(getTransformToCenter()); // use a transform with scale, rotation, registration point and translation
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor);
|
||||
|
||||
RenderableDebugableEntityItem::render(this, args);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <gpu/GPUConfig.h>
|
||||
#include <gpu/Batch.h>
|
||||
|
||||
#include <AbstractViewStateInterface.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <GeometryCache.h>
|
||||
|
@ -100,10 +101,17 @@ void RenderableZoneEntityItem::render(RenderArgs* args) {
|
|||
case SHAPE_TYPE_COMPOUND: {
|
||||
PerformanceTimer perfTimer("zone->renderCompound");
|
||||
updateGeometry();
|
||||
|
||||
if (_model && _model->isActive()) {
|
||||
// FIX ME: this is no longer available... we need to switch to payloads
|
||||
//_model->renderInScene(getLocalRenderAlpha(), args);
|
||||
if (_model && _model->needsFixupInScene()) {
|
||||
// check to see if when we added our models to the scene they were ready, if they were not ready, then
|
||||
// fix them up in the scene
|
||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
render::PendingChanges pendingChanges;
|
||||
_model->removeFromScene(scene, pendingChanges);
|
||||
_model->addToScene(scene, pendingChanges);
|
||||
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
|
||||
_model->setVisibleInScene(getVisible(), scene);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -131,6 +139,15 @@ void RenderableZoneEntityItem::render(RenderArgs* args) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!_drawZoneBoundaries || getShapeType() != SHAPE_TYPE_COMPOUND) &&
|
||||
_model && !_model->needsFixupInScene()) {
|
||||
// If the model is in the scene but doesn't need to be, remove it.
|
||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
render::PendingChanges pendingChanges;
|
||||
_model->removeFromScene(scene, pendingChanges);
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderableZoneEntityItem::contains(const glm::vec3& point) const {
|
||||
|
@ -145,3 +162,51 @@ bool RenderableZoneEntityItem::contains(const glm::vec3& point) const {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
class RenderableZoneEntityItemMeta {
|
||||
public:
|
||||
RenderableZoneEntityItemMeta(EntityItemPointer entity) : entity(entity){ }
|
||||
typedef render::Payload<RenderableZoneEntityItemMeta> Payload;
|
||||
typedef Payload::DataPointer Pointer;
|
||||
|
||||
EntityItemPointer entity;
|
||||
};
|
||||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const RenderableZoneEntityItemMeta::Pointer& payload) {
|
||||
return ItemKey::Builder::opaqueShape();
|
||||
}
|
||||
|
||||
template <> const Item::Bound payloadGetBound(const RenderableZoneEntityItemMeta::Pointer& payload) {
|
||||
if (payload && payload->entity) {
|
||||
return payload->entity->getAABox();
|
||||
}
|
||||
return render::Item::Bound();
|
||||
}
|
||||
template <> void payloadRender(const RenderableZoneEntityItemMeta::Pointer& payload, RenderArgs* args) {
|
||||
if (args) {
|
||||
if (payload && payload->entity) {
|
||||
payload->entity->render(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
|
||||
render::PendingChanges& pendingChanges) {
|
||||
_myMetaItem = scene->allocateID();
|
||||
|
||||
auto renderData = RenderableZoneEntityItemMeta::Pointer(new RenderableZoneEntityItemMeta(self));
|
||||
auto renderPayload = render::PayloadPointer(new RenderableZoneEntityItemMeta::Payload(renderData));
|
||||
|
||||
pendingChanges.resetItem(_myMetaItem, renderPayload);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
|
||||
render::PendingChanges& pendingChanges) {
|
||||
pendingChanges.removeItem(_myMetaItem);
|
||||
if (_model) {
|
||||
_model->removeFromScene(scene, pendingChanges);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,9 @@ public:
|
|||
virtual void render(RenderArgs* args);
|
||||
virtual bool contains(const glm::vec3& point) const;
|
||||
|
||||
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
|
||||
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
|
||||
|
||||
private:
|
||||
Model* getModel();
|
||||
void initialSimulation();
|
||||
|
@ -45,6 +48,8 @@ private:
|
|||
|
||||
Model* _model;
|
||||
bool _needsInitialSimulation;
|
||||
|
||||
render::ItemID _myMetaItem;
|
||||
};
|
||||
|
||||
#endif // hifi_RenderableZoneEntityItem_h
|
||||
|
|
33
libraries/entities/src/EntityActionFactoryInterface.h
Normal file
33
libraries/entities/src/EntityActionFactoryInterface.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// EntityActionFactoryInterface.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-2
|
||||
// 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_EntityActionFactoryInterface_h
|
||||
#define hifi_EntityActionFactoryInterface_h
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "EntityActionInterface.h"
|
||||
|
||||
class EntityActionFactoryInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
EntityActionFactoryInterface() { }
|
||||
virtual ~EntityActionFactoryInterface() { }
|
||||
virtual EntityActionPointer factory(EntitySimulation* simulation,
|
||||
EntityActionType type,
|
||||
QUuid id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) { assert(false); return nullptr; }
|
||||
};
|
||||
|
||||
#endif // hifi_EntityActionFactoryInterface_h
|
|
@ -22,6 +22,12 @@ EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeS
|
|||
if (normalizedActionTypeString == "pulltopoint") {
|
||||
return ACTION_TYPE_PULL_TO_POINT;
|
||||
}
|
||||
if (normalizedActionTypeString == "spring") {
|
||||
return ACTION_TYPE_SPRING;
|
||||
}
|
||||
if (normalizedActionTypeString == "hold") {
|
||||
return ACTION_TYPE_HOLD;
|
||||
}
|
||||
|
||||
qDebug() << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString;
|
||||
return ACTION_TYPE_NONE;
|
||||
|
@ -33,31 +39,37 @@ QString EntityActionInterface::actionTypeToString(EntityActionType actionType) {
|
|||
return "none";
|
||||
case ACTION_TYPE_PULL_TO_POINT:
|
||||
return "pullToPoint";
|
||||
case ACTION_TYPE_SPRING:
|
||||
return "spring";
|
||||
case ACTION_TYPE_HOLD:
|
||||
return "hold";
|
||||
}
|
||||
assert(false);
|
||||
return "none";
|
||||
}
|
||||
|
||||
glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok) {
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
qDebug() << objectName << "requires argument:" << argumentName;
|
||||
if (required) {
|
||||
qDebug() << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return vec3();
|
||||
return glm::vec3();
|
||||
}
|
||||
|
||||
QVariant resultV = arguments[argumentName];
|
||||
if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
|
||||
qDebug() << objectName << "argument" << argumentName << "must be a map";
|
||||
ok = false;
|
||||
return vec3();
|
||||
return glm::vec3();
|
||||
}
|
||||
|
||||
QVariantMap resultVM = resultV.toMap();
|
||||
if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) {
|
||||
qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z";
|
||||
ok = false;
|
||||
return vec3();
|
||||
return glm::vec3();
|
||||
}
|
||||
|
||||
QVariant xV = resultVM["x"];
|
||||
|
@ -73,17 +85,65 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian
|
|||
if (!xOk || !yOk || !zOk) {
|
||||
qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z and values of type float.";
|
||||
ok = false;
|
||||
return vec3();
|
||||
return glm::vec3();
|
||||
}
|
||||
|
||||
return vec3(x, y, z);
|
||||
return glm::vec3(x, y, z);
|
||||
}
|
||||
|
||||
glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qDebug() << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
QVariant resultV = arguments[argumentName];
|
||||
if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
|
||||
qDebug() << objectName << "argument" << argumentName << "must be a map, not" << resultV.typeName();
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
QVariantMap resultVM = resultV.toMap();
|
||||
if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) {
|
||||
qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z";
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
QVariant xV = resultVM["x"];
|
||||
QVariant yV = resultVM["y"];
|
||||
QVariant zV = resultVM["z"];
|
||||
QVariant wV = resultVM["w"];
|
||||
|
||||
bool xOk = true;
|
||||
bool yOk = true;
|
||||
bool zOk = true;
|
||||
bool wOk = true;
|
||||
float x = xV.toFloat(&xOk);
|
||||
float y = yV.toFloat(&yOk);
|
||||
float z = zV.toFloat(&zOk);
|
||||
float w = wV.toFloat(&wOk);
|
||||
if (!xOk || !yOk || !zOk || !wOk) {
|
||||
qDebug() << objectName << "argument" << argumentName
|
||||
<< "must be a map with keys of x, y, z, w and values of type float.";
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
return glm::quat(w, x, y, z);
|
||||
}
|
||||
|
||||
float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok) {
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
qDebug() << objectName << "requires argument:" << argumentName;
|
||||
if (required) {
|
||||
qDebug() << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return 0.0f;
|
||||
}
|
||||
|
@ -99,3 +159,18 @@ float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMa
|
|||
|
||||
return v;
|
||||
}
|
||||
|
||||
QString EntityActionInterface::extractStringArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qDebug() << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return "";
|
||||
}
|
||||
|
||||
QVariant vV = arguments[argumentName];
|
||||
QString v = vV.toString();
|
||||
return v;
|
||||
}
|
||||
|
|
|
@ -14,12 +14,16 @@
|
|||
|
||||
#include <QUuid>
|
||||
|
||||
#include "EntityItem.h"
|
||||
|
||||
class EntitySimulation;
|
||||
|
||||
enum EntityActionType {
|
||||
// keep these synchronized with actionTypeFromString and actionTypeToString
|
||||
ACTION_TYPE_NONE,
|
||||
ACTION_TYPE_PULL_TO_POINT
|
||||
ACTION_TYPE_PULL_TO_POINT,
|
||||
ACTION_TYPE_SPRING,
|
||||
ACTION_TYPE_HOLD
|
||||
};
|
||||
|
||||
|
||||
|
@ -32,18 +36,35 @@ public:
|
|||
virtual const EntityItemPointer& getOwnerEntity() const = 0;
|
||||
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) = 0;
|
||||
virtual bool updateArguments(QVariantMap arguments) = 0;
|
||||
// virtual QByteArray serialize() = 0;
|
||||
// static EntityActionPointer deserialize(EntityItemPointer ownerEntity, QByteArray data);
|
||||
|
||||
static EntityActionType actionTypeFromString(QString actionTypeString);
|
||||
static QString actionTypeToString(EntityActionType actionType);
|
||||
|
||||
protected:
|
||||
virtual glm::vec3 getPosition() = 0;
|
||||
virtual void setPosition(glm::vec3 position) = 0;
|
||||
virtual glm::quat getRotation() = 0;
|
||||
virtual void setRotation(glm::quat rotation) = 0;
|
||||
virtual glm::vec3 getLinearVelocity() = 0;
|
||||
virtual void setLinearVelocity(glm::vec3 linearVelocity) = 0;
|
||||
virtual glm::vec3 getAngularVelocity() = 0;
|
||||
virtual void setAngularVelocity(glm::vec3 angularVelocity) = 0;
|
||||
|
||||
// these look in the arguments map for a named argument. if it's not found or isn't well formed,
|
||||
// ok will be set to false (note that it's never set to true -- set it to true before calling these).
|
||||
// if required is true, failure to extract an argument will cause a warning to be printed.
|
||||
static glm::vec3 extractVec3Argument (QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required = true);
|
||||
static glm::quat extractQuatArgument (QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required = true);
|
||||
static float extractFloatArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required = true);
|
||||
static QString extractStringArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required = true);
|
||||
|
||||
static glm::vec3 extractVec3Argument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok);
|
||||
static float extractFloatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok);
|
||||
};
|
||||
|
||||
|
||||
typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
|
||||
|
||||
#endif // hifi_EntityActionInterface_h
|
||||
|
|
|
@ -28,13 +28,16 @@
|
|||
#include "EntityItemID.h"
|
||||
#include "EntityItemProperties.h"
|
||||
#include "EntityItemPropertiesDefaults.h"
|
||||
#include "EntityActionInterface.h"
|
||||
#include "EntityTypes.h"
|
||||
|
||||
class EntitySimulation;
|
||||
class EntityTreeElement;
|
||||
class EntityTreeElementExtraEncodeData;
|
||||
|
||||
class EntityActionInterface;
|
||||
typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
|
||||
|
||||
|
||||
namespace render {
|
||||
class Scene;
|
||||
class PendingChanges;
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include "ZoneEntityItem.h"
|
||||
#include "EntitiesLogging.h"
|
||||
#include "EntitySimulation.h"
|
||||
#include "EntityActionInterface.h"
|
||||
#include "EntityActionFactoryInterface.h"
|
||||
|
||||
#include "EntityScriptingInterface.h"
|
||||
|
||||
|
@ -491,12 +493,19 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
|
|||
const QUuid& entityID,
|
||||
const QVariantMap& arguments) {
|
||||
QUuid actionID = QUuid::createUuid();
|
||||
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
|
||||
bool success = actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
|
||||
// create this action even if the entity doesn't have physics info. it will often be the
|
||||
// case that a script adds an action immediately after an object is created, and the physicsInfo
|
||||
// is computed asynchronously.
|
||||
// if (!entity->getPhysicsInfo()) {
|
||||
// return false;
|
||||
// }
|
||||
EntityActionType actionType = EntityActionInterface::actionTypeFromString(actionTypeString);
|
||||
if (actionType == ACTION_TYPE_NONE) {
|
||||
return false;
|
||||
}
|
||||
if (simulation->actionFactory(actionType, actionID, entity, arguments)) {
|
||||
if (actionFactory->factory(simulation, actionType, actionID, entity, arguments)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "EntityActionInterface.h"
|
||||
#include "EntityItem.h"
|
||||
#include "EntityTree.h"
|
||||
|
||||
|
@ -56,10 +57,6 @@ public:
|
|||
|
||||
friend class EntityTree;
|
||||
|
||||
virtual EntityActionPointer actionFactory(EntityActionType type,
|
||||
QUuid id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) { return nullptr; }
|
||||
virtual void addAction(EntityActionPointer action) { _actionsToAdd += action; }
|
||||
virtual void removeAction(const QUuid actionID) { _actionsToRemove += actionID; }
|
||||
virtual void removeActions(QList<QUuid> actionIDsToRemove) { _actionsToRemove += actionIDsToRemove; }
|
||||
|
|
|
@ -24,7 +24,14 @@ ObjectAction::~ObjectAction() {
|
|||
}
|
||||
|
||||
void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) {
|
||||
qDebug() << "ObjectAction::updateAction called";
|
||||
if (!_active) {
|
||||
return;
|
||||
}
|
||||
if (!_ownerEntity) {
|
||||
qDebug() << "ObjectActionPullToPoint::updateAction no owner entity";
|
||||
return;
|
||||
}
|
||||
updateActionWorker(deltaTimeStep);
|
||||
}
|
||||
|
||||
void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) {
|
||||
|
@ -33,3 +40,87 @@ void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) {
|
|||
void ObjectAction::removeFromSimulation(EntitySimulation* simulation) const {
|
||||
simulation->removeAction(_id);
|
||||
}
|
||||
|
||||
btRigidBody* ObjectAction::getRigidBody() {
|
||||
if (!_ownerEntity) {
|
||||
return nullptr;
|
||||
}
|
||||
void* physicsInfo = _ownerEntity->getPhysicsInfo();
|
||||
if (!physicsInfo) {
|
||||
return nullptr;
|
||||
}
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
|
||||
return motionState->getRigidBody();
|
||||
}
|
||||
|
||||
glm::vec3 ObjectAction::getPosition() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getCenterOfMassPosition());
|
||||
}
|
||||
|
||||
void ObjectAction::setPosition(glm::vec3 position) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return;
|
||||
}
|
||||
// XXX
|
||||
// void setWorldTransform (const btTransform &worldTrans)
|
||||
assert(false);
|
||||
rigidBody->activate();
|
||||
}
|
||||
|
||||
glm::quat ObjectAction::getRotation() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::quat(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getOrientation());
|
||||
}
|
||||
|
||||
void ObjectAction::setRotation(glm::quat rotation) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return;
|
||||
}
|
||||
// XXX
|
||||
// void setWorldTransform (const btTransform &worldTrans)
|
||||
assert(false);
|
||||
rigidBody->activate();
|
||||
}
|
||||
|
||||
glm::vec3 ObjectAction::getLinearVelocity() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getLinearVelocity());
|
||||
}
|
||||
|
||||
void ObjectAction::setLinearVelocity(glm::vec3 linearVelocity) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return;
|
||||
}
|
||||
rigidBody->setLinearVelocity(glmToBullet(glm::vec3(0.0f)));
|
||||
rigidBody->activate();
|
||||
}
|
||||
|
||||
glm::vec3 ObjectAction::getAngularVelocity() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getAngularVelocity());
|
||||
}
|
||||
|
||||
void ObjectAction::setAngularVelocity(glm::vec3 angularVelocity) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return;
|
||||
}
|
||||
rigidBody->setAngularVelocity(glmToBullet(angularVelocity));
|
||||
rigidBody->activate();
|
||||
}
|
||||
|
|
|
@ -13,12 +13,17 @@
|
|||
#ifndef hifi_ObjectAction_h
|
||||
#define hifi_ObjectAction_h
|
||||
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
|
||||
#include <QUuid>
|
||||
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
|
||||
#include <EntityItem.h>
|
||||
|
||||
#include "ObjectMotionState.h"
|
||||
#include "BulletUtil.h"
|
||||
#include "EntityActionInterface.h"
|
||||
|
||||
|
||||
class ObjectAction : public btActionInterface, public EntityActionInterface {
|
||||
public:
|
||||
ObjectAction(QUuid id, EntityItemPointer ownerEntity);
|
||||
|
@ -30,6 +35,9 @@ public:
|
|||
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; }
|
||||
virtual bool updateArguments(QVariantMap arguments) { return false; }
|
||||
|
||||
// this is called from updateAction and should be overridden by subclasses
|
||||
virtual void updateActionWorker(float deltaTimeStep) {}
|
||||
|
||||
// these are from btActionInterface
|
||||
virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep);
|
||||
virtual void debugDraw(btIDebugDraw* debugDrawer);
|
||||
|
@ -39,6 +47,16 @@ private:
|
|||
QReadWriteLock _lock;
|
||||
|
||||
protected:
|
||||
virtual btRigidBody* getRigidBody();
|
||||
virtual glm::vec3 getPosition();
|
||||
virtual void setPosition(glm::vec3 position);
|
||||
virtual glm::quat getRotation();
|
||||
virtual void setRotation(glm::quat rotation);
|
||||
virtual glm::vec3 getLinearVelocity();
|
||||
virtual void setLinearVelocity(glm::vec3 linearVelocity);
|
||||
virtual glm::vec3 getAngularVelocity();
|
||||
virtual void setAngularVelocity(glm::vec3 angularVelocity);
|
||||
|
||||
bool tryLockForRead() { return _lock.tryLockForRead(); }
|
||||
void lockForWrite() { _lock.lockForWrite(); }
|
||||
void unlock() { _lock.unlock(); }
|
||||
|
|
|
@ -9,9 +9,6 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ObjectMotionState.h"
|
||||
#include "BulletUtil.h"
|
||||
|
||||
#include "ObjectActionPullToPoint.h"
|
||||
|
||||
ObjectActionPullToPoint::ObjectActionPullToPoint(QUuid id, EntityItemPointer ownerEntity) :
|
||||
|
@ -27,28 +24,34 @@ ObjectActionPullToPoint::~ObjectActionPullToPoint() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void ObjectActionPullToPoint::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) {
|
||||
void ObjectActionPullToPoint::updateActionWorker(btScalar deltaTimeStep) {
|
||||
if (!tryLockForRead()) {
|
||||
// don't risk hanging the thread running the physics simulation
|
||||
return;
|
||||
}
|
||||
void* physicsInfo = _ownerEntity->getPhysicsInfo();
|
||||
|
||||
if (_active && physicsInfo) {
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
|
||||
btRigidBody* rigidBody = motionState->getRigidBody();
|
||||
if (rigidBody) {
|
||||
glm::vec3 offset = _target - bulletToGLM(rigidBody->getCenterOfMassPosition());
|
||||
float offsetLength = glm::length(offset);
|
||||
if (offsetLength > IGNORE_POSITION_DELTA) {
|
||||
glm::vec3 newVelocity = glm::normalize(offset) * _speed;
|
||||
rigidBody->setLinearVelocity(glmToBullet(newVelocity));
|
||||
rigidBody->activate();
|
||||
} else {
|
||||
rigidBody->setLinearVelocity(glmToBullet(glm::vec3()));
|
||||
}
|
||||
}
|
||||
void* physicsInfo = _ownerEntity->getPhysicsInfo();
|
||||
if (!physicsInfo) {
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
|
||||
btRigidBody* rigidBody = motionState->getRigidBody();
|
||||
if (!rigidBody) {
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 offset = _target - bulletToGLM(rigidBody->getCenterOfMassPosition());
|
||||
float offsetLength = glm::length(offset);
|
||||
if (offsetLength > IGNORE_POSITION_DELTA) {
|
||||
glm::vec3 newVelocity = glm::normalize(offset) * _speed;
|
||||
rigidBody->setLinearVelocity(glmToBullet(newVelocity));
|
||||
rigidBody->activate();
|
||||
} else {
|
||||
rigidBody->setLinearVelocity(glmToBullet(glm::vec3()));
|
||||
}
|
||||
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
virtual ~ObjectActionPullToPoint();
|
||||
|
||||
virtual bool updateArguments(QVariantMap arguments);
|
||||
virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep);
|
||||
virtual void updateActionWorker(float deltaTimeStep);
|
||||
|
||||
private:
|
||||
|
||||
|
|
144
libraries/physics/src/ObjectActionSpring.cpp
Normal file
144
libraries/physics/src/ObjectActionSpring.cpp
Normal file
|
@ -0,0 +1,144 @@
|
|||
//
|
||||
// ObjectActionSpring.cpp
|
||||
// libraries/physics/src
|
||||
//
|
||||
// Created by Seth Alves 2015-6-5
|
||||
// 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 "ObjectActionSpring.h"
|
||||
|
||||
ObjectActionSpring::ObjectActionSpring(QUuid id, EntityItemPointer ownerEntity) :
|
||||
ObjectAction(id, ownerEntity) {
|
||||
#if WANT_DEBUG
|
||||
qDebug() << "ObjectActionSpring::ObjectActionSpring";
|
||||
#endif
|
||||
}
|
||||
|
||||
ObjectActionSpring::~ObjectActionSpring() {
|
||||
#if WANT_DEBUG
|
||||
qDebug() << "ObjectActionSpring::~ObjectActionSpring";
|
||||
#endif
|
||||
}
|
||||
|
||||
void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
|
||||
if (!tryLockForRead()) {
|
||||
// don't risk hanging the thread running the physics simulation
|
||||
qDebug() << "ObjectActionSpring::updateActionWorker lock failed";
|
||||
return;
|
||||
}
|
||||
|
||||
void* physicsInfo = _ownerEntity->getPhysicsInfo();
|
||||
if (!physicsInfo) {
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
|
||||
btRigidBody* rigidBody = motionState->getRigidBody();
|
||||
if (!rigidBody) {
|
||||
unlock();
|
||||
qDebug() << "ObjectActionSpring::updateActionWorker no rigidBody";
|
||||
return;
|
||||
}
|
||||
|
||||
// handle the linear part
|
||||
if (_positionalTargetSet) {
|
||||
glm::vec3 offset = _positionalTarget - bulletToGLM(rigidBody->getCenterOfMassPosition());
|
||||
float offsetLength = glm::length(offset);
|
||||
float speed = offsetLength / _linearTimeScale;
|
||||
|
||||
if (offsetLength > IGNORE_POSITION_DELTA) {
|
||||
glm::vec3 newVelocity = glm::normalize(offset) * speed;
|
||||
rigidBody->setLinearVelocity(glmToBullet(newVelocity));
|
||||
rigidBody->activate();
|
||||
} else {
|
||||
rigidBody->setLinearVelocity(glmToBullet(glm::vec3(0.0f)));
|
||||
}
|
||||
}
|
||||
|
||||
// handle rotation
|
||||
if (_rotationalTargetSet) {
|
||||
glm::quat bodyRotation = bulletToGLM(rigidBody->getOrientation());
|
||||
// if qZero and qOne are too close to each other, we can get NaN for angle.
|
||||
auto alignmentDot = glm::dot(bodyRotation, _rotationalTarget);
|
||||
const float almostOne = 0.99999f;
|
||||
if (glm::abs(alignmentDot) < almostOne) {
|
||||
glm::quat target = _rotationalTarget;
|
||||
if (alignmentDot < 0) {
|
||||
target = -target;
|
||||
}
|
||||
glm::quat qZeroInverse = glm::inverse(bodyRotation);
|
||||
glm::quat deltaQ = target * qZeroInverse;
|
||||
glm::vec3 axis = glm::axis(deltaQ);
|
||||
float angle = glm::angle(deltaQ);
|
||||
assert(!isNaN(angle));
|
||||
glm::vec3 newAngularVelocity = (angle / _angularTimeScale) * glm::normalize(axis);
|
||||
rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity));
|
||||
rigidBody->activate();
|
||||
} else {
|
||||
rigidBody->setAngularVelocity(glmToBullet(glm::vec3(0.0f)));
|
||||
}
|
||||
}
|
||||
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
||||
bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
|
||||
// targets are required, spring-constants are optional
|
||||
bool ptOk = true;
|
||||
glm::vec3 positionalTarget =
|
||||
EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ptOk, false);
|
||||
bool pscOk = true;
|
||||
float linearTimeScale =
|
||||
EntityActionInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", pscOk, false);
|
||||
if (ptOk && pscOk && linearTimeScale <= 0.0f) {
|
||||
qDebug() << "spring action -- linearTimeScale must be greater than zero.";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rtOk = true;
|
||||
glm::quat rotationalTarget =
|
||||
EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", rtOk, false);
|
||||
bool rscOk = true;
|
||||
float angularTimeScale =
|
||||
EntityActionInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", rscOk, false);
|
||||
|
||||
if (!ptOk && !rtOk) {
|
||||
qDebug() << "spring action requires either targetPosition or targetRotation argument";
|
||||
return false;
|
||||
}
|
||||
|
||||
lockForWrite();
|
||||
|
||||
_positionalTargetSet = _rotationalTargetSet = false;
|
||||
|
||||
if (ptOk) {
|
||||
_positionalTarget = positionalTarget;
|
||||
_positionalTargetSet = true;
|
||||
|
||||
if (pscOk) {
|
||||
_linearTimeScale = linearTimeScale;
|
||||
} else {
|
||||
_linearTimeScale = 0.1f;
|
||||
}
|
||||
}
|
||||
|
||||
if (rtOk) {
|
||||
_rotationalTarget = rotationalTarget;
|
||||
_rotationalTargetSet = true;
|
||||
|
||||
if (rscOk) {
|
||||
_angularTimeScale = angularTimeScale;
|
||||
} else {
|
||||
_angularTimeScale = 0.1f;
|
||||
}
|
||||
}
|
||||
|
||||
_active = true;
|
||||
unlock();
|
||||
return true;
|
||||
}
|
39
libraries/physics/src/ObjectActionSpring.h
Normal file
39
libraries/physics/src/ObjectActionSpring.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// ObjectActionSpring.h
|
||||
// libraries/physics/src
|
||||
//
|
||||
// Created by Seth Alves 2015-6-5
|
||||
// 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_ObjectActionSpring_h
|
||||
#define hifi_ObjectActionSpring_h
|
||||
|
||||
#include <QUuid>
|
||||
|
||||
#include <EntityItem.h>
|
||||
#include "ObjectAction.h"
|
||||
|
||||
class ObjectActionSpring : public ObjectAction {
|
||||
public:
|
||||
ObjectActionSpring(QUuid id, EntityItemPointer ownerEntity);
|
||||
virtual ~ObjectActionSpring();
|
||||
|
||||
virtual bool updateArguments(QVariantMap arguments);
|
||||
virtual void updateActionWorker(float deltaTimeStep);
|
||||
|
||||
protected:
|
||||
|
||||
glm::vec3 _positionalTarget;
|
||||
float _linearTimeScale;
|
||||
bool _positionalTargetSet;
|
||||
|
||||
glm::quat _rotationalTarget;
|
||||
float _angularTimeScale;
|
||||
bool _rotationalTargetSet;
|
||||
};
|
||||
|
||||
#endif // hifi_ObjectActionSpring_h
|
|
@ -9,10 +9,11 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
|
||||
#include "PhysicsHelpers.h"
|
||||
#include "PhysicsLogging.h"
|
||||
#include "ShapeManager.h"
|
||||
#include "ObjectActionPullToPoint.h"
|
||||
|
||||
#include "PhysicalEntitySimulation.h"
|
||||
|
||||
|
@ -234,29 +235,6 @@ void PhysicalEntitySimulation::handleCollisionEvents(CollisionEvents& collisionE
|
|||
}
|
||||
}
|
||||
|
||||
EntityActionPointer PhysicalEntitySimulation::actionFactory(EntityActionType type,
|
||||
QUuid id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) {
|
||||
EntityActionPointer action = nullptr;
|
||||
switch (type) {
|
||||
case ACTION_TYPE_NONE:
|
||||
return nullptr;
|
||||
case ACTION_TYPE_PULL_TO_POINT:
|
||||
action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity);
|
||||
break;
|
||||
}
|
||||
|
||||
bool ok = action->updateArguments(arguments);
|
||||
if (ok) {
|
||||
ownerEntity->addAction(this, action);
|
||||
return action;
|
||||
}
|
||||
|
||||
action = nullptr;
|
||||
return action;
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::applyActionChanges() {
|
||||
if (_physicsEngine) {
|
||||
foreach (EntityActionPointer actionToAdd, _actionsToAdd) {
|
||||
|
|
|
@ -32,10 +32,6 @@ public:
|
|||
|
||||
void init(EntityTree* tree, PhysicsEngine* engine, EntityEditPacketSender* packetSender);
|
||||
|
||||
virtual EntityActionPointer actionFactory(EntityActionType type,
|
||||
QUuid id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments);
|
||||
virtual void applyActionChanges();
|
||||
|
||||
protected: // only called by EntitySimulation
|
||||
|
|
|
@ -50,6 +50,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
|
|||
_jobs.push_back(Job(RenderDeferred()));
|
||||
_jobs.push_back(Job(ResolveDeferred()));
|
||||
_jobs.push_back(Job(DrawTransparentDeferred()));
|
||||
_jobs.push_back(Job(DrawPostLayered()));
|
||||
_jobs.push_back(Job(ResetGLState()));
|
||||
}
|
||||
|
||||
|
@ -85,7 +86,7 @@ template <> void render::jobRun(const DrawOpaqueDeferred& job, const SceneContex
|
|||
|
||||
// render opaques
|
||||
auto& scene = sceneContext->_scene;
|
||||
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape());
|
||||
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withoutLayered());
|
||||
auto& renderDetails = renderContext->args->_details;
|
||||
|
||||
ItemIDsBounds inItems;
|
||||
|
@ -163,7 +164,7 @@ template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneC
|
|||
|
||||
// render transparents
|
||||
auto& scene = sceneContext->_scene;
|
||||
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape());
|
||||
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape().withoutLayered());
|
||||
auto& renderDetails = renderContext->args->_details;
|
||||
|
||||
ItemIDsBounds inItems;
|
||||
|
|
|
@ -192,7 +192,6 @@ void render::renderItems(const SceneContextPointer& sceneContext, const RenderCo
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void addClearStateCommands(gpu::Batch& batch) {
|
||||
batch._glDepthMask(true);
|
||||
batch._glDepthFunc(GL_LESS);
|
||||
|
@ -446,6 +445,48 @@ template <> void render::jobRun(const DrawBackground& job, const SceneContextPoi
|
|||
args->_context->syncCache();
|
||||
}
|
||||
|
||||
template <> void render::jobRun(const DrawPostLayered& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
|
||||
PerformanceTimer perfTimer("DrawPostLayered");
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->_viewFrustum);
|
||||
|
||||
// render backgrounds
|
||||
auto& scene = sceneContext->_scene;
|
||||
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withLayered());
|
||||
|
||||
|
||||
ItemIDsBounds inItems;
|
||||
inItems.reserve(items.size());
|
||||
for (auto id : items) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (item.getKey().isVisible() && (item.getLayer() > 0)) {
|
||||
inItems.emplace_back(id);
|
||||
}
|
||||
}
|
||||
if (inItems.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RenderArgs* args = renderContext->args;
|
||||
gpu::Batch batch;
|
||||
args->_batch = &batch;
|
||||
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
args->_viewFrustum->evalProjectionMatrix(projMat);
|
||||
args->_viewFrustum->evalViewTransform(viewMat);
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewMat);
|
||||
|
||||
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0);
|
||||
|
||||
renderItems(sceneContext, renderContext, inItems);
|
||||
args->_context->render((*args->_batch));
|
||||
args->_batch = nullptr;
|
||||
|
||||
// Force the context sync
|
||||
args->_context->syncCache();
|
||||
}
|
||||
|
||||
|
||||
void ItemMaterialBucketMap::insert(const ItemID& id, const model::MaterialKey& key) {
|
||||
|
|
|
@ -87,6 +87,14 @@ public:
|
|||
};
|
||||
template <> void jobRun(const DrawBackground& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
|
||||
|
||||
|
||||
class DrawPostLayered {
|
||||
public:
|
||||
};
|
||||
template <> void jobRun(const DrawPostLayered& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
|
||||
|
||||
|
||||
|
||||
class ResetGLState {
|
||||
public:
|
||||
};
|
||||
|
|
|
@ -45,10 +45,12 @@ void ItemBucketMap::reset(const ItemID& id, const ItemKey& oldKey, const ItemKey
|
|||
}
|
||||
|
||||
void ItemBucketMap::allocateStandardOpaqueTranparentBuckets() {
|
||||
(*this)[ItemFilter::Builder::opaqueShape()];
|
||||
(*this)[ItemFilter::Builder::transparentShape()];
|
||||
(*this)[ItemFilter::Builder::opaqueShape().withoutLayered()];
|
||||
(*this)[ItemFilter::Builder::transparentShape().withoutLayered()];
|
||||
(*this)[ItemFilter::Builder::light()];
|
||||
(*this)[ItemFilter::Builder::background()];
|
||||
(*this)[ItemFilter::Builder::opaqueShape().withLayered()];
|
||||
(*this)[ItemFilter::Builder::transparentShape().withLayered()];
|
||||
}
|
||||
|
||||
|
||||
|
@ -61,11 +63,6 @@ void Item::resetPayload(const PayloadPointer& payload) {
|
|||
}
|
||||
}
|
||||
|
||||
void Item::kill() {
|
||||
_payload.reset();
|
||||
_key._flags.reset();
|
||||
}
|
||||
|
||||
void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) {
|
||||
_resetItems.push_back(id);
|
||||
_resetPayloads.push_back(payload);
|
||||
|
@ -164,27 +161,3 @@ void Scene::updateItems(const ItemIDs& ids, UpdateFunctors& functors) {
|
|||
_items[(*updateID)].update((*updateFunctor));
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::registerObserver(ObserverPointer& observer) {
|
||||
// make sure it's a valid observer
|
||||
if (observer && (observer->getScene() == nullptr)) {
|
||||
// Then register the observer
|
||||
_observers.push_back(observer);
|
||||
|
||||
// And let it do what it wants to do
|
||||
observer->registerScene(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::unregisterObserver(ObserverPointer& observer) {
|
||||
// make sure it's a valid observer currently registered
|
||||
if (observer && (observer->getScene() == this)) {
|
||||
// let it do what it wants to do
|
||||
observer->unregisterScene();
|
||||
|
||||
// Then unregister the observer
|
||||
auto it = std::find(_observers.begin(), _observers.end(), observer);
|
||||
_observers.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ public:
|
|||
enum FlagBit {
|
||||
TYPE_SHAPE = 0, // Item is a Shape
|
||||
TYPE_LIGHT, // Item is a Light
|
||||
TYPE_BACKGROUND, // Item is a Background
|
||||
TRANSLUCENT, // Transparent and not opaque, for some odd reason TRANSPARENCY doesn't work...
|
||||
VIEW_SPACE, // Transformed in view space, and not in world space
|
||||
DYNAMIC, // Dynamic and bound will change unlike static item
|
||||
|
@ -44,7 +43,7 @@ public:
|
|||
INVISIBLE, // Visible or not? could be just here to cast shadow
|
||||
SHADOW_CASTER, // Item cast shadows
|
||||
PICKABLE, // Item can be picked/selected
|
||||
NO_DEPTH_SORT, // Item should not be depth sorted
|
||||
LAYERED, // Item belongs to one of the layers different from the default layer
|
||||
|
||||
NUM_FLAGS, // Not a valid flag
|
||||
};
|
||||
|
@ -57,6 +56,7 @@ public:
|
|||
ItemKey(const Flags& flags) : _flags(flags) {}
|
||||
|
||||
class Builder {
|
||||
friend class ItemKey;
|
||||
Flags _flags{ 0 };
|
||||
public:
|
||||
Builder() {}
|
||||
|
@ -65,7 +65,6 @@ public:
|
|||
|
||||
Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); }
|
||||
Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); }
|
||||
Builder& withTypeBackground() { _flags.set(TYPE_BACKGROUND); return (*this); }
|
||||
Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); }
|
||||
Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); }
|
||||
Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); }
|
||||
|
@ -73,14 +72,15 @@ public:
|
|||
Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); }
|
||||
Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); }
|
||||
Builder& withPickable() { _flags.set(PICKABLE); return (*this); }
|
||||
Builder& withNoDepthSort() { _flags.set(NO_DEPTH_SORT); return (*this); }
|
||||
Builder& withLayered() { _flags.set(LAYERED); return (*this); }
|
||||
|
||||
// Convenient standard keys that we will keep on using all over the place
|
||||
static ItemKey opaqueShape() { return Builder().withTypeShape().build(); }
|
||||
static ItemKey transparentShape() { return Builder().withTypeShape().withTransparent().build(); }
|
||||
static ItemKey light() { return Builder().withTypeLight().build(); }
|
||||
static ItemKey background() { return Builder().withTypeBackground().build(); }
|
||||
static Builder opaqueShape() { return Builder().withTypeShape(); }
|
||||
static Builder transparentShape() { return Builder().withTypeShape().withTransparent(); }
|
||||
static Builder light() { return Builder().withTypeLight(); }
|
||||
static Builder background() { return Builder().withViewSpace().withLayered(); }
|
||||
};
|
||||
ItemKey(const Builder& builder) : ItemKey(builder._flags) {}
|
||||
|
||||
bool isOpaque() const { return !_flags[TRANSLUCENT]; }
|
||||
bool isTransparent() const { return _flags[TRANSLUCENT]; }
|
||||
|
@ -101,8 +101,7 @@ public:
|
|||
|
||||
bool isPickable() const { return _flags[PICKABLE]; }
|
||||
|
||||
bool isDepthSort() const { return !_flags[NO_DEPTH_SORT]; }
|
||||
bool isNoDepthSort() const { return _flags[NO_DEPTH_SORT]; }
|
||||
bool isLayered() const { return _flags[LAYERED]; }
|
||||
};
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const ItemKey& itemKey) {
|
||||
|
@ -122,6 +121,7 @@ public:
|
|||
ItemFilter(const ItemKey::Flags& value = ItemKey::Flags(0), const ItemKey::Flags& mask = ItemKey::Flags(0)) : _value(value), _mask(mask) {}
|
||||
|
||||
class Builder {
|
||||
friend class ItemFilter;
|
||||
ItemKey::Flags _value{ 0 };
|
||||
ItemKey::Flags _mask{ 0 };
|
||||
public:
|
||||
|
@ -131,7 +131,6 @@ public:
|
|||
|
||||
Builder& withTypeShape() { _value.set(ItemKey::TYPE_SHAPE); _mask.set(ItemKey::TYPE_SHAPE); return (*this); }
|
||||
Builder& withTypeLight() { _value.set(ItemKey::TYPE_LIGHT); _mask.set(ItemKey::TYPE_LIGHT); return (*this); }
|
||||
Builder& withTypeBackground() { _value.set(ItemKey::TYPE_BACKGROUND); _mask.set(ItemKey::TYPE_BACKGROUND); return (*this); }
|
||||
|
||||
Builder& withOpaque() { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); }
|
||||
Builder& withTransparent() { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); }
|
||||
|
@ -153,16 +152,18 @@ public:
|
|||
|
||||
Builder& withPickable() { _value.set(ItemKey::PICKABLE); _mask.set(ItemKey::PICKABLE); return (*this); }
|
||||
|
||||
Builder& withDepthSort() { _value.reset(ItemKey::NO_DEPTH_SORT); _mask.set(ItemKey::NO_DEPTH_SORT); return (*this); }
|
||||
Builder& withNotDepthSort() { _value.set(ItemKey::NO_DEPTH_SORT); _mask.set(ItemKey::NO_DEPTH_SORT); return (*this); }
|
||||
Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); }
|
||||
Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); }
|
||||
|
||||
// Convenient standard keys that we will keep on using all over the place
|
||||
static ItemFilter opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace().build(); }
|
||||
static ItemFilter transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace().build(); }
|
||||
static ItemFilter light() { return Builder().withTypeLight().build(); }
|
||||
static ItemFilter background() { return Builder().withTypeBackground().build(); }
|
||||
static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); }
|
||||
static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); }
|
||||
static Builder light() { return Builder().withTypeLight(); }
|
||||
static Builder background() { return Builder().withViewSpace().withLayered(); }
|
||||
};
|
||||
|
||||
ItemFilter(const Builder& builder) : ItemFilter(builder._value, builder._mask) {}
|
||||
|
||||
// Item Filter operator testing if a key pass the filter
|
||||
bool test(const ItemKey& key) const { return (key._flags & _mask) == (_value & _mask); }
|
||||
|
||||
|
@ -179,7 +180,7 @@ public:
|
|||
};
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const ItemFilter& me) {
|
||||
debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape())
|
||||
debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape().build())
|
||||
<< "]";
|
||||
return debug;
|
||||
}
|
||||
|
@ -214,38 +215,43 @@ public:
|
|||
public:
|
||||
virtual const ItemKey getKey() const = 0;
|
||||
virtual const Bound getBound() const = 0;
|
||||
virtual void render(RenderArgs* args) = 0;
|
||||
virtual int getLayer() const = 0;
|
||||
|
||||
virtual void update(const UpdateFunctorPointer& functor) = 0;
|
||||
virtual void render(RenderArgs* args) = 0;
|
||||
|
||||
virtual const model::MaterialKey getMaterialKey() const = 0;
|
||||
|
||||
~PayloadInterface() {}
|
||||
protected:
|
||||
friend class Item;
|
||||
virtual void update(const UpdateFunctorPointer& functor) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
typedef std::shared_ptr<PayloadInterface> PayloadPointer;
|
||||
|
||||
|
||||
|
||||
Item() {}
|
||||
~Item() {}
|
||||
|
||||
// Main scene / item managment interface Reset/Update/Kill
|
||||
void resetPayload(const PayloadPointer& payload);
|
||||
void kill();
|
||||
void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); } // Communicate update to the payload
|
||||
void kill() { _payload.reset(); _key._flags.reset(); } // Kill means forget the payload and key
|
||||
|
||||
// Check heuristic key
|
||||
const ItemKey& getKey() const { return _key; }
|
||||
|
||||
// Payload Interface
|
||||
|
||||
// Get the bound of the item expressed in world space (or eye space depending on the key.isWorldSpace())
|
||||
const Bound getBound() const { return _payload->getBound(); }
|
||||
|
||||
// Get the layer where the item belongs. 0 by default meaning NOT LAYERED
|
||||
int getLayer() const { return _payload->getLayer(); }
|
||||
|
||||
// Render call for the item
|
||||
void render(RenderArgs* args) { _payload->render(args); }
|
||||
void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); }
|
||||
|
||||
// Shape Type Interface
|
||||
const model::MaterialKey& getMaterialKey() const { return _payload->getMaterialKey(); }
|
||||
const model::MaterialKey getMaterialKey() const { return _payload->getMaterialKey(); }
|
||||
|
||||
protected:
|
||||
PayloadPointer _payload;
|
||||
|
@ -280,6 +286,7 @@ inline QDebug operator<<(QDebug debug, const Item& item) {
|
|||
// of the Payload interface
|
||||
template <class T> const ItemKey payloadGetKey(const std::shared_ptr<T>& payloadData) { return ItemKey(); }
|
||||
template <class T> const Item::Bound payloadGetBound(const std::shared_ptr<T>& payloadData) { return Item::Bound(); }
|
||||
template <class T> int payloadGetLayer(const std::shared_ptr<T>& payloadData) { return 0; }
|
||||
template <class T> void payloadRender(const std::shared_ptr<T>& payloadData, RenderArgs* args) { }
|
||||
|
||||
// Shape type interface
|
||||
|
@ -290,19 +297,25 @@ public:
|
|||
typedef std::shared_ptr<T> DataPointer;
|
||||
typedef UpdateFunctor<T> Updater;
|
||||
|
||||
virtual void update(const UpdateFunctorPointer& functor) { static_cast<Updater*>(functor.get())->_func((*_data)); }
|
||||
Payload(const DataPointer& data) : _data(data) {}
|
||||
|
||||
// Payload general interface
|
||||
virtual const ItemKey getKey() const { return payloadGetKey<T>(_data); }
|
||||
virtual const Item::Bound getBound() const { return payloadGetBound<T>(_data); }
|
||||
virtual int getLayer() const { return payloadGetLayer<T>(_data); }
|
||||
|
||||
|
||||
virtual void render(RenderArgs* args) { payloadRender<T>(_data, args); }
|
||||
|
||||
// Shape Type interface
|
||||
virtual const model::MaterialKey getMaterialKey() const { return shapeGetMaterialKey<T>(_data); }
|
||||
|
||||
Payload(const DataPointer& data) : _data(data) {}
|
||||
protected:
|
||||
DataPointer _data;
|
||||
|
||||
// Update mechanics
|
||||
virtual void update(const UpdateFunctorPointer& functor) { static_cast<Updater*>(functor.get())->_func((*_data)); }
|
||||
friend class Item;
|
||||
};
|
||||
|
||||
// Let's show how to make a simple FooPayload example:
|
||||
|
@ -358,6 +371,7 @@ public:
|
|||
|
||||
typedef std::vector< ItemIDAndBounds > ItemIDsBounds;
|
||||
|
||||
|
||||
// A map of ItemIDSets allowing to create bucket lists of items which are filtering correctly
|
||||
class ItemBucketMap : public std::map<ItemFilter, ItemIDSet, ItemFilter::Less> {
|
||||
public:
|
||||
|
@ -374,8 +388,6 @@ public:
|
|||
};
|
||||
|
||||
class Engine;
|
||||
class Observer;
|
||||
|
||||
|
||||
class PendingChanges {
|
||||
public:
|
||||
|
@ -411,36 +423,6 @@ typedef std::queue<PendingChanges> PendingChangesQueue;
|
|||
// Items are notified accordingly on any update message happening
|
||||
class Scene {
|
||||
public:
|
||||
|
||||
class Observer {
|
||||
public:
|
||||
Observer(Scene* scene) {
|
||||
|
||||
}
|
||||
~Observer() {}
|
||||
|
||||
const Scene* getScene() const { return _scene; }
|
||||
Scene* editScene() { return _scene; }
|
||||
|
||||
protected:
|
||||
Scene* _scene = nullptr;
|
||||
virtual void onRegisterScene() {}
|
||||
virtual void onUnregisterScene() {}
|
||||
|
||||
friend class Scene;
|
||||
void registerScene(Scene* scene) {
|
||||
_scene = scene;
|
||||
onRegisterScene();
|
||||
}
|
||||
|
||||
void unregisterScene() {
|
||||
onUnregisterScene();
|
||||
_scene = 0;
|
||||
}
|
||||
};
|
||||
typedef std::shared_ptr< Observer > ObserverPointer;
|
||||
typedef std::vector< ObserverPointer > Observers;
|
||||
|
||||
Scene();
|
||||
~Scene() {}
|
||||
|
||||
|
@ -450,11 +432,6 @@ public:
|
|||
/// Enqueue change batch to the scene
|
||||
void enqueuePendingChanges(const PendingChanges& pendingChanges);
|
||||
|
||||
/// Scene Observer listen to any change and get notified
|
||||
void registerObserver(ObserverPointer& observer);
|
||||
void unregisterObserver(ObserverPointer& observer);
|
||||
|
||||
|
||||
/// Access the main bucketmap of items
|
||||
const ItemBucketMap& getMasterBucket() const { return _masterBucketMap; }
|
||||
|
||||
|
@ -483,10 +460,6 @@ protected:
|
|||
void removeItems(const ItemIDs& ids);
|
||||
void updateItems(const ItemIDs& ids, UpdateFunctors& functors);
|
||||
|
||||
|
||||
// The scene context listening for any change to the database
|
||||
Observers _observers;
|
||||
|
||||
friend class Engine;
|
||||
};
|
||||
|
||||
|
|
|
@ -52,6 +52,8 @@ class AbstractControllerScriptingInterface : public QObject {
|
|||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
virtual void registerControllerTypes(QScriptEngine* engine) = 0;
|
||||
|
||||
virtual bool isPrimaryButtonPressed() const = 0;
|
||||
virtual glm::vec2 getPrimaryJoystickPosition() const = 0;
|
||||
|
||||
|
|
|
@ -317,6 +317,7 @@ void ScriptEngine::init() {
|
|||
registerAnimationTypes(this);
|
||||
registerAvatarTypes(this);
|
||||
registerAudioMetaTypes(this);
|
||||
_controllerScriptingInterface->registerControllerTypes(this);
|
||||
|
||||
qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly);
|
||||
qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue);
|
||||
|
|
Loading…
Reference in a new issue