mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
2600fe577e
86 changed files with 4193 additions and 616 deletions
|
@ -77,7 +77,19 @@ void ScriptableAvatar::update(float deltatime) {
|
|||
int mapping = animationJoints.indexOf(modelJoints[i]);
|
||||
if (mapping != -1 && !_maskedJoints.contains(modelJoints[i])) {
|
||||
JointData& data = _jointData[i];
|
||||
data.rotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
|
||||
|
||||
auto newRotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
|
||||
auto newTranslation = floorFrame.translations.at(i) * (1.0f - frameFraction) +
|
||||
ceilFrame.translations.at(i) * frameFraction;
|
||||
|
||||
if (data.rotation != newRotation) {
|
||||
data.rotation = newRotation;
|
||||
data.rotationSet = true;
|
||||
}
|
||||
if (data.translation != newTranslation) {
|
||||
data.translation = newTranslation;
|
||||
data.translationSet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -18,7 +18,7 @@ var RainSquall = function (properties) {
|
|||
dropFallSpeed = 1, // m/s
|
||||
dropLifetime = 60, // Seconds
|
||||
dropSpinMax = 0, // Maximum angular velocity per axis; deg/s
|
||||
debug = false, // Display origin circle; don't use running on Stack Manager
|
||||
debug = true, // Display origin circle; don't use running on Stack Manager
|
||||
// Other
|
||||
squallCircle,
|
||||
SQUALL_CIRCLE_COLOR = { red: 255, green: 0, blue: 0 },
|
||||
|
@ -151,8 +151,10 @@ var RainSquall = function (properties) {
|
|||
};
|
||||
};
|
||||
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));
|
||||
center.y += 10;
|
||||
var rainSquall1 = new RainSquall({
|
||||
origin: { x: 1195, y: 1223, z: 1020 },
|
||||
origin:center,
|
||||
radius: 25,
|
||||
dropsPerMinute: 120,
|
||||
dropSize: { x: 0.1, y: 0.1, z: 0.1 },
|
||||
|
|
289
examples/closePaint.js
Normal file
289
examples/closePaint.js
Normal file
|
@ -0,0 +1,289 @@
|
|||
//
|
||||
// closePaint.js
|
||||
// examples
|
||||
//
|
||||
// Created by Eric Levina on 9/30/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Run this script to be able to paint on entities you are close to, with hydras.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("libraries/utils.js");
|
||||
|
||||
|
||||
var RIGHT_HAND = 1;
|
||||
var LEFT_HAND = 0;
|
||||
|
||||
var MIN_POINT_DISTANCE = 0.02;
|
||||
var MAX_POINT_DISTANCE = 0.5;
|
||||
|
||||
var SPATIAL_CONTROLLERS_PER_PALM = 2;
|
||||
var TIP_CONTROLLER_OFFSET = 1;
|
||||
|
||||
var TRIGGER_ON_VALUE = 0.3;
|
||||
|
||||
var MAX_DISTANCE = 10;
|
||||
|
||||
var MAX_POINTS_PER_LINE = 40;
|
||||
|
||||
var RIGHT_4_ACTION = 18;
|
||||
var RIGHT_2_ACTION = 16;
|
||||
|
||||
var LEFT_4_ACTION = 17;
|
||||
var LEFT_2_ACTION = 16;
|
||||
|
||||
var HUE_INCREMENT = 0.02;
|
||||
|
||||
var MIN_STROKE_WIDTH = 0.002;
|
||||
var MAX_STROKE_WIDTH = 0.04;
|
||||
|
||||
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
|
||||
|
||||
function MyController(hand, triggerAction) {
|
||||
this.hand = hand;
|
||||
this.strokes = [];
|
||||
this.painting = false;
|
||||
this.currentStrokeWidth = MIN_STROKE_WIDTH;
|
||||
|
||||
if (this.hand === RIGHT_HAND) {
|
||||
this.getHandPosition = MyAvatar.getRightPalmPosition;
|
||||
this.getHandRotation = MyAvatar.getRightPalmRotation;
|
||||
} else {
|
||||
this.getHandPosition = MyAvatar.getLeftPalmPosition;
|
||||
this.getHandRotation = MyAvatar.getLeftPalmRotation;
|
||||
}
|
||||
|
||||
this.triggerAction = triggerAction;
|
||||
this.palm = SPATIAL_CONTROLLERS_PER_PALM * hand;
|
||||
this.tip = SPATIAL_CONTROLLERS_PER_PALM * hand + TIP_CONTROLLER_OFFSET;
|
||||
|
||||
|
||||
this.strokeColor = {
|
||||
h: 0.8,
|
||||
s: 0.8,
|
||||
l: 0.4
|
||||
};
|
||||
|
||||
|
||||
this.laserPointer = Overlays.addOverlay("circle3d", {
|
||||
color: hslToRgb(this.strokeColor),
|
||||
solid: true,
|
||||
position: center
|
||||
});
|
||||
this.triggerValue = 0;
|
||||
this.prevTriggerValue = 0;
|
||||
var _this = this;
|
||||
|
||||
this.update = function() {
|
||||
this.updateControllerState();
|
||||
this.search();
|
||||
if (this.canPaint === true) {
|
||||
this.paint(this.intersection.intersection, this.intersection.surfaceNormal);
|
||||
}
|
||||
};
|
||||
|
||||
this.paint = function(position, normal) {
|
||||
if (this.painting === false) {
|
||||
if (this.oldPosition) {
|
||||
this.newStroke(this.oldPosition);
|
||||
} else {
|
||||
this.newStroke(position);
|
||||
}
|
||||
this.painting = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var localPoint = Vec3.subtract(position, this.strokeBasePosition);
|
||||
//Move stroke a bit forward along normal so it doesnt zfight with mesh its drawing on
|
||||
localPoint = Vec3.sum(localPoint, Vec3.multiply(normal, 0.001 + Math.random() * .001)); //rand avoid z fighting
|
||||
|
||||
var distance = Vec3.distance(localPoint, this.strokePoints[this.strokePoints.length - 1]);
|
||||
if (this.strokePoints.length > 0 && distance < MIN_POINT_DISTANCE) {
|
||||
//need a minimum distance to avoid binormal NANs
|
||||
return;
|
||||
}
|
||||
if (this.strokePoints.length > 0 && distance > MAX_POINT_DISTANCE) {
|
||||
//Prevents drawing lines accross models
|
||||
this.painting = false;
|
||||
return;
|
||||
}
|
||||
if (this.strokePoints.length === 0) {
|
||||
localPoint = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
}
|
||||
|
||||
this.strokePoints.push(localPoint);
|
||||
this.strokeNormals.push(normal);
|
||||
this.strokeWidths.push(this.currentStrokeWidth);
|
||||
Entities.editEntity(this.currentStroke, {
|
||||
linePoints: this.strokePoints,
|
||||
normals: this.strokeNormals,
|
||||
strokeWidths: this.strokeWidths
|
||||
});
|
||||
if (this.strokePoints.length === MAX_POINTS_PER_LINE) {
|
||||
this.painting = false;
|
||||
return;
|
||||
}
|
||||
this.oldPosition = position;
|
||||
}
|
||||
|
||||
this.newStroke = function(position) {
|
||||
this.strokeBasePosition = position;
|
||||
this.currentStroke = Entities.addEntity({
|
||||
position: position,
|
||||
type: "PolyLine",
|
||||
color: hslToRgb(this.strokeColor),
|
||||
dimensions: {
|
||||
x: 50,
|
||||
y: 50,
|
||||
z: 50
|
||||
},
|
||||
lifetime: 200
|
||||
});
|
||||
this.strokePoints = [];
|
||||
this.strokeNormals = [];
|
||||
this.strokeWidths = [];
|
||||
|
||||
this.strokes.push(this.currentStroke);
|
||||
|
||||
}
|
||||
|
||||
this.updateControllerState = function() {
|
||||
this.triggerValue = Controller.getActionValue(this.triggerAction);
|
||||
if (this.triggerValue > TRIGGER_ON_VALUE && this.prevTriggerValue <= TRIGGER_ON_VALUE) {
|
||||
this.squeeze();
|
||||
} else if (this.triggerValue < TRIGGER_ON_VALUE && this.prevTriggerValue >= TRIGGER_ON_VALUE) {
|
||||
this.release();
|
||||
}
|
||||
|
||||
this.prevTriggerValue = this.triggerValue;
|
||||
}
|
||||
|
||||
this.squeeze = function() {
|
||||
this.tryPainting = true;
|
||||
|
||||
}
|
||||
this.release = function() {
|
||||
this.painting = false;
|
||||
this.tryPainting = false;
|
||||
this.canPaint = false;
|
||||
this.oldPosition = null;
|
||||
}
|
||||
this.search = function() {
|
||||
|
||||
// the trigger is being pressed, do a ray test
|
||||
var handPosition = this.getHandPosition();
|
||||
var pickRay = {
|
||||
origin: handPosition,
|
||||
direction: Quat.getUp(this.getHandRotation())
|
||||
};
|
||||
|
||||
|
||||
this.intersection = Entities.findRayIntersection(pickRay, true);
|
||||
if (this.intersection.intersects) {
|
||||
var distance = Vec3.distance(handPosition, this.intersection.intersection);
|
||||
if (distance < MAX_DISTANCE) {
|
||||
var displayPoint = this.intersection.intersection;
|
||||
displayPoint = Vec3.sum(displayPoint, Vec3.multiply(this.intersection.surfaceNormal, .01));
|
||||
if (this.tryPainting) {
|
||||
this.canPaint = true;
|
||||
}
|
||||
this.currentStrokeWidth = map(this.triggerValue, TRIGGER_ON_VALUE, 1, MIN_STROKE_WIDTH, MAX_STROKE_WIDTH);
|
||||
var laserSize = map(distance, 1, MAX_DISTANCE, 0.01, 0.1);
|
||||
laserSize += this.currentStrokeWidth / 2;
|
||||
Overlays.editOverlay(this.laserPointer, {
|
||||
visible: true,
|
||||
position: displayPoint,
|
||||
rotation: orientationOf(this.intersection.surfaceNormal),
|
||||
size: {
|
||||
x: laserSize,
|
||||
y: laserSize
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
this.hitFail();
|
||||
}
|
||||
} else {
|
||||
this.hitFail();
|
||||
}
|
||||
};
|
||||
|
||||
this.hitFail = function() {
|
||||
this.canPaint = false;
|
||||
|
||||
Overlays.editOverlay(this.laserPointer, {
|
||||
visible: false
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
this.cleanup = function() {
|
||||
Overlays.deleteOverlay(this.laserPointer);
|
||||
this.strokes.forEach(function(stroke) {
|
||||
Entities.deleteEntity(stroke);
|
||||
});
|
||||
}
|
||||
|
||||
this.cycleColorDown = function() {
|
||||
this.strokeColor.h -= HUE_INCREMENT;
|
||||
if (this.strokeColor.h < 0) {
|
||||
this.strokeColor = 1;
|
||||
}
|
||||
Overlays.editOverlay(this.laserPointer, {
|
||||
color: hslToRgb(this.strokeColor)
|
||||
});
|
||||
}
|
||||
|
||||
this.cycleColorUp = function() {
|
||||
this.strokeColor.h += HUE_INCREMENT;
|
||||
if (this.strokeColor.h > 1) {
|
||||
this.strokeColor.h = 0;
|
||||
}
|
||||
Overlays.editOverlay(this.laserPointer, {
|
||||
color: hslToRgb(this.strokeColor)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var rightController = new MyController(RIGHT_HAND, Controller.findAction("RIGHT_HAND_CLICK"));
|
||||
var leftController = new MyController(LEFT_HAND, Controller.findAction("LEFT_HAND_CLICK"));
|
||||
|
||||
Controller.actionEvent.connect(function(action, state) {
|
||||
if (state === 0) {
|
||||
return;
|
||||
}
|
||||
if (action === RIGHT_4_ACTION) {
|
||||
rightController.cycleColorUp();
|
||||
} else if (action === RIGHT_2_ACTION) {
|
||||
rightController.cycleColorDown();
|
||||
}
|
||||
if (action === LEFT_4_ACTION) {
|
||||
leftController.cycleColorUp();
|
||||
} else if (action === LEFT_2_ACTION) {
|
||||
leftController.cycleColorDown();
|
||||
}
|
||||
});
|
||||
|
||||
function update() {
|
||||
rightController.update();
|
||||
leftController.update();
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
rightController.cleanup();
|
||||
leftController.cleanup();
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
Script.update.connect(update);
|
|
@ -264,7 +264,6 @@ var toolBar = (function () {
|
|||
cameraManager.disable();
|
||||
} else {
|
||||
hasShownPropertiesTool = false;
|
||||
cameraManager.enable();
|
||||
entityListTool.setVisible(true);
|
||||
gridTool.setVisible(true);
|
||||
grid.setEnabled(true);
|
||||
|
@ -675,15 +674,11 @@ function mouseMove(event) {
|
|||
|
||||
lastMousePosition = { x: event.x, y: event.y };
|
||||
|
||||
highlightEntityUnderCursor(lastMousePosition, false);
|
||||
idleMouseTimerId = Script.setTimeout(handleIdleMouse, IDLE_MOUSE_TIMEOUT);
|
||||
}
|
||||
|
||||
function handleIdleMouse() {
|
||||
idleMouseTimerId = null;
|
||||
if (isActive) {
|
||||
highlightEntityUnderCursor(lastMousePosition, true);
|
||||
}
|
||||
}
|
||||
|
||||
function highlightEntityUnderCursor(position, accurateRay) {
|
||||
|
@ -807,6 +802,7 @@ function mouseClickEvent(event) {
|
|||
selectionDisplay.select(selectedEntityID, event);
|
||||
|
||||
if (Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)) {
|
||||
cameraManager.enable();
|
||||
cameraManager.focus(selectionManager.worldPosition,
|
||||
selectionManager.worldDimensions,
|
||||
Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
|
||||
|
@ -1147,6 +1143,7 @@ Controller.keyReleaseEvent.connect(function (event) {
|
|||
} else if (event.text == "f") {
|
||||
if (isActive) {
|
||||
if (selectionManager.hasSelection()) {
|
||||
cameraManager.enable();
|
||||
cameraManager.focus(selectionManager.worldPosition,
|
||||
selectionManager.worldDimensions,
|
||||
Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
|
||||
|
|
|
@ -1,252 +0,0 @@
|
|||
(function() {
|
||||
// Script.include("../libraries/utils.js");
|
||||
//Need absolute path for now, for testing before PR merge and s3 cloning. Will change post-merge
|
||||
|
||||
Script.include("../libraries/utils.js");
|
||||
GRAB_FRAME_USER_DATA_KEY = "grabFrame";
|
||||
this.userData = {};
|
||||
|
||||
var TIP_OFFSET_Z = 0.14;
|
||||
var TIP_OFFSET_Y = 0.04;
|
||||
|
||||
var ZERO_VEC = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
}
|
||||
|
||||
var MAX_POINTS_PER_LINE = 40;
|
||||
var MIN_POINT_DISTANCE = 0.01;
|
||||
var STROKE_WIDTH = 0.02;
|
||||
|
||||
var self = this;
|
||||
|
||||
var timeSinceLastMoved = 0;
|
||||
var RESET_TIME_THRESHOLD = 5;
|
||||
var DISTANCE_FROM_HOME_THRESHOLD = 0.5;
|
||||
var HOME_POSITION = {
|
||||
x: 549.12,
|
||||
y: 495.555,
|
||||
z: 503.77
|
||||
};
|
||||
this.getUserData = function() {
|
||||
|
||||
|
||||
if (this.properties.userData) {
|
||||
this.userData = JSON.parse(this.properties.userData);
|
||||
}
|
||||
}
|
||||
|
||||
this.updateUserData = function() {
|
||||
Entities.editEntity(this.entityId, {
|
||||
userData: JSON.stringify(this.userData)
|
||||
});
|
||||
}
|
||||
|
||||
this.update = function(deltaTime) {
|
||||
self.getUserData();
|
||||
self.properties = Entities.getEntityProperties(self.entityId);
|
||||
|
||||
if (Vec3.length(self.properties.velocity) < 0.1 && Vec3.distance(HOME_POSITION, self.properties.position) > DISTANCE_FROM_HOME_THRESHOLD) {
|
||||
timeSinceLastMoved += deltaTime;
|
||||
if (timeSinceLastMoved > RESET_TIME_THRESHOLD) {
|
||||
self.reset();
|
||||
timeSinceLastMoved = 0;
|
||||
}
|
||||
} else {
|
||||
timeSinceLastMoved = 0;
|
||||
}
|
||||
|
||||
//Only activate for the user who grabbed the object
|
||||
if (self.userData.grabKey && self.userData.grabKey.activated === true && self.userData.grabKey.avatarId == MyAvatar.sessionUUID) {
|
||||
if (self.activated !== true) {
|
||||
//We were just grabbed, so create a particle system
|
||||
self.grab();
|
||||
}
|
||||
//Move emitter to where entity is always when its activated
|
||||
self.sprayStream();
|
||||
} else if (self.userData.grabKey && self.userData.grabKey.activated === false && self.activated) {
|
||||
self.letGo();
|
||||
}
|
||||
}
|
||||
|
||||
this.grab = function() {
|
||||
this.activated = true;
|
||||
var animationSettings = JSON.stringify({
|
||||
fps: 30,
|
||||
loop: true,
|
||||
firstFrame: 1,
|
||||
lastFrame: 10000,
|
||||
running: true
|
||||
});
|
||||
var PI = 3.141593;
|
||||
var DEG_TO_RAD = PI / 180.0;
|
||||
|
||||
this.paintStream = Entities.addEntity({
|
||||
type: "ParticleEffect",
|
||||
animationSettings: animationSettings,
|
||||
position: this.properties.position,
|
||||
textures: "https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png",
|
||||
emitSpeed: 0,
|
||||
speedSpread: 0.02,
|
||||
polarFinish: 2 * DEG_TO_RAD,
|
||||
emitAcceleration: ZERO_VEC,
|
||||
emitRate: 100,
|
||||
particleRadius: 0.01,
|
||||
color: {
|
||||
red: 170,
|
||||
green: 20,
|
||||
blue: 150
|
||||
},
|
||||
lifetime: 50, //probably wont be holding longer than this straight
|
||||
});
|
||||
}
|
||||
|
||||
this.letGo = function() {
|
||||
this.activated = false;
|
||||
Entities.deleteEntity(this.paintStream);
|
||||
this.paintStream = null;
|
||||
}
|
||||
|
||||
this.reset = function() {
|
||||
Entities.editEntity(self.entityId, {
|
||||
position: HOME_POSITION,
|
||||
rotation: Quat.fromPitchYawRollDegrees(0, 0, 0),
|
||||
angularVelocity: ZERO_VEC,
|
||||
velocity: ZERO_VEC
|
||||
});
|
||||
}
|
||||
|
||||
this.sprayStream = function() {
|
||||
var forwardVec = Quat.getFront(Quat.multiply(self.properties.rotation , Quat.fromPitchYawRollDegrees(0, 90, 0)));
|
||||
forwardVec = Vec3.normalize(forwardVec);
|
||||
|
||||
var upVec = Quat.getUp(self.properties.rotation);
|
||||
var position = Vec3.sum(self.properties.position, Vec3.multiply(forwardVec, TIP_OFFSET_Z));
|
||||
position = Vec3.sum(position, Vec3.multiply(upVec, TIP_OFFSET_Y))
|
||||
Entities.editEntity(self.paintStream, {
|
||||
position: position,
|
||||
emitOrientation: forwardVec,
|
||||
emitSpeed: 5
|
||||
});
|
||||
|
||||
//Now check for an intersection with an entity
|
||||
//move forward so ray doesnt intersect with gun
|
||||
var origin = Vec3.sum(position, forwardVec);
|
||||
var pickRay = {
|
||||
origin: origin,
|
||||
direction: Vec3.multiply(forwardVec, 2)
|
||||
}
|
||||
|
||||
var intersection = Entities.findRayIntersection(pickRay, true);
|
||||
if (intersection.intersects) {
|
||||
var normal = Vec3.multiply(-1, Quat.getFront(intersection.properties.rotation));
|
||||
this.paint(intersection.intersection, normal);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
this.paint = function(position, normal) {
|
||||
if (!this.painting) {
|
||||
|
||||
this.newStroke(position);
|
||||
this.painting = true;
|
||||
}
|
||||
|
||||
if (this.strokePoints.length > MAX_POINTS_PER_LINE) {
|
||||
this.painting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var localPoint = Vec3.subtract(position, this.strokeBasePosition);
|
||||
//Move stroke a bit forward along normal so it doesnt zfight with mesh its drawing on
|
||||
localPoint = Vec3.sum(localPoint, Vec3.multiply(normal, .1));
|
||||
|
||||
if (this.strokePoints.length > 0 && Vec3.distance(localPoint, this.strokePoints[this.strokePoints.length - 1]) < MIN_POINT_DISTANCE) {
|
||||
//need a minimum distance to avoid binormal NANs
|
||||
return;
|
||||
}
|
||||
|
||||
this.strokePoints.push(localPoint);
|
||||
this.strokeNormals.push(normal);
|
||||
this.strokeWidths.push(STROKE_WIDTH);
|
||||
Entities.editEntity(this.currentStroke, {
|
||||
linePoints: this.strokePoints,
|
||||
normals: this.strokeNormals,
|
||||
strokeWidths: this.strokeWidths
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
this.newStroke = function(position) {
|
||||
this.strokeBasePosition = position;
|
||||
this.currentStroke = Entities.addEntity({
|
||||
position: position,
|
||||
type: "PolyLine",
|
||||
color: {
|
||||
red: randInt(160, 250),
|
||||
green: randInt(10, 20),
|
||||
blue: randInt(190, 250)
|
||||
},
|
||||
dimensions: {
|
||||
x: 50,
|
||||
y: 50,
|
||||
z: 50
|
||||
},
|
||||
lifetime: 100
|
||||
});
|
||||
this.strokePoints = [];
|
||||
this.strokeNormals = [];
|
||||
this.strokeWidths = [];
|
||||
|
||||
this.strokes.push(this.currentStroke);
|
||||
}
|
||||
|
||||
this.preload = function(entityId) {
|
||||
this.strokes = [];
|
||||
this.activated = false;
|
||||
this.entityId = entityId;
|
||||
this.properties = Entities.getEntityProperties(self.entityId);
|
||||
this.getUserData();
|
||||
|
||||
//Only activate for the avatar who is grabbing the can!
|
||||
if (this.userData.grabKey && this.userData.grabKey.activated) {
|
||||
this.activated = true;
|
||||
}
|
||||
if (!this.userData.grabFrame) {
|
||||
var data = {
|
||||
relativePosition: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(0, 0, 0)
|
||||
}
|
||||
setEntityCustomData(GRAB_FRAME_USER_DATA_KEY, this.entityId, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.unload = function() {
|
||||
Script.update.disconnect(this.update);
|
||||
if(this.paintStream) {
|
||||
Entities.deleteEntity(this.paintStream);
|
||||
}
|
||||
this.strokes.forEach(function(stroke) {
|
||||
Entities.deleteEntity(stroke);
|
||||
});
|
||||
}
|
||||
Script.update.connect(this.update);
|
||||
});
|
||||
|
||||
|
||||
|
||||
function randFloat(min, max) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
function randInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min)) + min;
|
||||
}
|
|
@ -56,7 +56,7 @@ EntityListTool = function(opts) {
|
|||
|
||||
var selectedIDs = [];
|
||||
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||
selectedIDs.push(selectionManager.selections[i].id); // ?
|
||||
selectedIDs.push(selectionManager.selections[i].id);
|
||||
}
|
||||
|
||||
var data = {
|
||||
|
@ -77,6 +77,7 @@ EntityListTool = function(opts) {
|
|||
}
|
||||
selectionManager.setSelections(entityIDs);
|
||||
if (data.focus) {
|
||||
cameraManager.enable();
|
||||
cameraManager.focus(selectionManager.worldPosition,
|
||||
selectionManager.worldDimensions,
|
||||
Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
|
||||
|
|
|
@ -179,3 +179,69 @@ pointInExtents = function(point, minPoint, maxPoint) {
|
|||
(point.y >= minPoint.y && point.y <= maxPoint.y) &&
|
||||
(point.z >= minPoint.z && point.z <= maxPoint.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an HSL color value to RGB. Conversion formula
|
||||
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
||||
* Assumes h, s, and l are contained in the set [0, 1] and
|
||||
* returns r, g, and b in the set [0, 255].
|
||||
*
|
||||
* @param Number h The hue
|
||||
* @param Number s The saturation
|
||||
* @param Number l The lightness
|
||||
* @return Array The RGB representation
|
||||
*/
|
||||
hslToRgb = function(hsl, hueOffset) {
|
||||
var r, g, b;
|
||||
if (hsl.s == 0) {
|
||||
r = g = b = hsl.l; // achromatic
|
||||
} else {
|
||||
var hue2rgb = function hue2rgb(p, q, t) {
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
||||
if (t < 1 / 2) return q;
|
||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
||||
return p;
|
||||
}
|
||||
|
||||
var q = hsl.l < 0.5 ? hsl.l * (1 + hsl.s) : hsl.l + hsl.s - hsl.l * hsl.s;
|
||||
var p = 2 * hsl.l - q;
|
||||
r = hue2rgb(p, q, hsl.h + 1 / 3);
|
||||
g = hue2rgb(p, q, hsl.h);
|
||||
b = hue2rgb(p, q, hsl.h - 1 / 3);
|
||||
}
|
||||
|
||||
return {
|
||||
red: Math.round(r * 255),
|
||||
green: Math.round(g * 255),
|
||||
blue: Math.round(b * 255)
|
||||
};
|
||||
}
|
||||
|
||||
map = function(value, min1, max1, min2, max2) {
|
||||
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
|
||||
}
|
||||
|
||||
orientationOf = function(vector) {
|
||||
var Y_AXIS = {
|
||||
x: 0,
|
||||
y: 1,
|
||||
z: 0
|
||||
};
|
||||
var X_AXIS = {
|
||||
x: 1,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
|
||||
var theta = 0.0;
|
||||
|
||||
var RAD_TO_DEG = 180.0 / Math.PI;
|
||||
var direction, yaw, pitch;
|
||||
direction = Vec3.normalize(vector);
|
||||
yaw = Quat.angleAxis(Math.atan2(direction.x, direction.z) * RAD_TO_DEG, Y_AXIS);
|
||||
pitch = Quat.angleAxis(Math.asin(-direction.y) * RAD_TO_DEG, X_AXIS);
|
||||
return Quat.multiply(yaw, pitch);
|
||||
}
|
||||
|
||||
|
|
95
examples/particle_explorer/dat.gui.min.js
vendored
Normal file
95
examples/particle_explorer/dat.gui.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
58
examples/particle_explorer/index.html
Normal file
58
examples/particle_explorer/index.html
Normal file
|
@ -0,0 +1,58 @@
|
|||
<!--
|
||||
// main.js
|
||||
//
|
||||
//
|
||||
// Created by James B. Pollack @imgntn on 9/26/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Loads dat.gui, underscore, and the app
|
||||
// Quickly edit the aesthetics of a particle system.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="dat.gui.min.js"></script>
|
||||
<script type="text/javascript" src="underscore-min.js"></script>
|
||||
<script type="text/javascript" src="main.js?123"></script>
|
||||
<script>
|
||||
</script>
|
||||
<style>
|
||||
|
||||
body{
|
||||
background-color:black;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#my-gui-container{
|
||||
|
||||
}
|
||||
|
||||
.importer{
|
||||
margin-bottom:4px;
|
||||
}
|
||||
|
||||
::-webkit-input-placeholder {
|
||||
text-align: center;
|
||||
font-family: Helvetica
|
||||
}
|
||||
|
||||
#importer-input{
|
||||
width:90%;
|
||||
line-height: 2;
|
||||
margin-left:5%;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="importer">
|
||||
<input type='text' id="importer-input" placeholder="Import: Paste JSON here." onkeypress="handleInputKeyPress(event)">
|
||||
</div>
|
||||
<div id="my-gui-container">
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
513
examples/particle_explorer/main.js
Normal file
513
examples/particle_explorer/main.js
Normal file
|
@ -0,0 +1,513 @@
|
|||
//
|
||||
// main.js
|
||||
//
|
||||
// Created by James B. Pollack @imgntn on 9/26/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Web app side of the App - contains GUI.
|
||||
// This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
/*global window, alert, EventBridge, dat, convertBinaryToBoolean, listenForSettingsUpdates,createVec3Folder,createQuatFolder,writeVec3ToInterface,writeDataToInterface*/
|
||||
|
||||
var Settings = function() {
|
||||
this.exportSettings = function() {
|
||||
//copyExportSettingsToClipboard();
|
||||
showPreselectedPrompt();
|
||||
};
|
||||
this.importSettings = function() {
|
||||
importSettings();
|
||||
};
|
||||
};
|
||||
|
||||
//2-way bindings-aren't quite ready yet. see bottom of file.
|
||||
var AUTO_UPDATE = false;
|
||||
var UPDATE_ALL_FREQUENCY = 100;
|
||||
|
||||
var controllers = [];
|
||||
var colorControllers = [];
|
||||
var folders = [];
|
||||
var gui = null;
|
||||
var settings = new Settings();
|
||||
var updateInterval;
|
||||
|
||||
var currentInputField;
|
||||
var storedController;
|
||||
var keysToIgnore = [
|
||||
'importSettings',
|
||||
'exportSettings',
|
||||
'script',
|
||||
'visible',
|
||||
'locked',
|
||||
'userData',
|
||||
'position',
|
||||
'dimensions',
|
||||
'rotation',
|
||||
'id',
|
||||
'description',
|
||||
'type',
|
||||
'created',
|
||||
'age',
|
||||
'ageAsText',
|
||||
'boundingBox',
|
||||
'naturalDimensions',
|
||||
'naturalPosition',
|
||||
'velocity',
|
||||
'gravity',
|
||||
'acceleration',
|
||||
'damping',
|
||||
'restitution',
|
||||
'friction',
|
||||
'density',
|
||||
'lifetime',
|
||||
'scriptTimestamp',
|
||||
'registrationPoint',
|
||||
'angularVelocity',
|
||||
'angularDamping',
|
||||
'ignoreForCollisions',
|
||||
'collisionsWillMove',
|
||||
'href',
|
||||
'actionData',
|
||||
'marketplaceID',
|
||||
'collisionSoundURL',
|
||||
'shapeType',
|
||||
'animationSettings',
|
||||
'animationFrameIndex',
|
||||
'animationIsPlaying',
|
||||
'animationFPS',
|
||||
'sittingPoints',
|
||||
'originalTextures'
|
||||
];
|
||||
|
||||
var individualKeys = [];
|
||||
var vec3Keys = [];
|
||||
var quatKeys = [];
|
||||
var colorKeys = [];
|
||||
|
||||
window.onload = function() {
|
||||
if (typeof EventBridge !== 'undefined') {
|
||||
|
||||
var stringifiedData = JSON.stringify({
|
||||
messageType: 'page_loaded'
|
||||
});
|
||||
|
||||
EventBridge.emitWebEvent(
|
||||
stringifiedData
|
||||
);
|
||||
|
||||
listenForSettingsUpdates();
|
||||
window.onresize = setGUIWidthToWindowWidth;
|
||||
} else {
|
||||
console.log('No event bridge, probably not in interface.');
|
||||
}
|
||||
};
|
||||
|
||||
function loadGUI() {
|
||||
//whether or not to autoplace
|
||||
gui = new dat.GUI({
|
||||
autoPlace: false
|
||||
});
|
||||
|
||||
//if not autoplacing, put gui in a custom container
|
||||
if (gui.autoPlace === false) {
|
||||
var customContainer = document.getElementById('my-gui-container');
|
||||
customContainer.appendChild(gui.domElement);
|
||||
}
|
||||
|
||||
// presets for the GUI itself. a little confusing and import/export is mostly what we want to do at the moment.
|
||||
// gui.remember(settings);
|
||||
|
||||
var keys = _.keys(settings);
|
||||
|
||||
_.each(keys, function(key) {
|
||||
var shouldIgnore = _.contains(keysToIgnore, key);
|
||||
|
||||
if (shouldIgnore) {
|
||||
return;
|
||||
}
|
||||
|
||||
var subKeys = _.keys(settings[key]);
|
||||
var hasX = _.contains(subKeys, 'x');
|
||||
var hasY = _.contains(subKeys, 'y');
|
||||
var hasZ = _.contains(subKeys, 'z');
|
||||
var hasW = _.contains(subKeys, 'w');
|
||||
var hasRed = _.contains(subKeys, 'red');
|
||||
var hasGreen = _.contains(subKeys, 'green');
|
||||
var hasBlue = _.contains(subKeys, 'blue');
|
||||
|
||||
if ((hasX && hasY && hasZ) && hasW === false) {
|
||||
vec3Keys.push(key);
|
||||
} else if (hasX && hasY && hasZ && hasW) {
|
||||
quatKeys.push(key);
|
||||
} else if (hasRed || hasGreen || hasBlue) {
|
||||
colorKeys.push(key);
|
||||
|
||||
} else {
|
||||
individualKeys.push(key);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
//alphabetize our keys
|
||||
individualKeys.sort();
|
||||
vec3Keys.sort();
|
||||
quatKeys.sort();
|
||||
colorKeys.sort();
|
||||
|
||||
//add to gui in the order they should appear
|
||||
gui.add(settings, 'importSettings');
|
||||
gui.add(settings, 'exportSettings');
|
||||
addIndividualKeys();
|
||||
addFolders();
|
||||
|
||||
//set the gui width to match the web window width
|
||||
gui.width = window.innerWidth;
|
||||
|
||||
//2-way binding stuff
|
||||
// if (AUTO_UPDATE) {
|
||||
// setInterval(manuallyUpdateDisplay, UPDATE_ALL_FREQUENCY);
|
||||
// registerDOMElementsForListenerBlocking();
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
function addIndividualKeys() {
|
||||
_.each(individualKeys, function(key) {
|
||||
//temporary patch for not crashing when this goes below 0
|
||||
var controller;
|
||||
|
||||
if (key.indexOf('emitRate') > -1) {
|
||||
controller = gui.add(settings, key).min(0);
|
||||
} else {
|
||||
controller = gui.add(settings, key);
|
||||
|
||||
}
|
||||
|
||||
//2-way - need to fix not being able to input exact values if constantly listening
|
||||
//controller.listen();
|
||||
|
||||
//keep track of our controller
|
||||
controllers.push(controller);
|
||||
|
||||
//hook into change events for this gui controller
|
||||
controller.onChange(function(value) {
|
||||
// Fires on every change, drag, keypress, etc.
|
||||
writeDataToInterface(this.property, value);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function addFolders() {
|
||||
_.each(colorKeys, function(key) {
|
||||
createColorPicker(key);
|
||||
});
|
||||
_.each(vec3Keys, function(key) {
|
||||
createVec3Folder(key);
|
||||
});
|
||||
_.each(quatKeys, function(key) {
|
||||
createQuatFolder(key);
|
||||
});
|
||||
}
|
||||
|
||||
function createColorPicker(key) {
|
||||
var colorObject = settings[key];
|
||||
var colorArray = convertColorObjectToArray(colorObject);
|
||||
settings[key] = colorArray;
|
||||
var controller = gui.addColor(settings, key);
|
||||
controller.onChange(function(value) {
|
||||
var obj = {};
|
||||
obj[key] = convertColorArrayToObject(value);
|
||||
writeVec3ToInterface(obj);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function createVec3Folder(category) {
|
||||
var folder = gui.addFolder(category);
|
||||
|
||||
folder.add(settings[category], 'x').step(0.1).onChange(function(value) {
|
||||
// Fires when a controller loses focus.
|
||||
var obj = {};
|
||||
obj[category] = {};
|
||||
obj[category][this.property] = value;
|
||||
obj[category].y = settings[category].y;
|
||||
obj[category].z = settings[category].z;
|
||||
writeVec3ToInterface(obj);
|
||||
});
|
||||
|
||||
folder.add(settings[category], 'y').step(0.1).onChange(function(value) {
|
||||
// Fires when a controller loses focus.
|
||||
var obj = {};
|
||||
obj[category] = {};
|
||||
obj[category].x = settings[category].x;
|
||||
obj[category][this.property] = value;
|
||||
obj[category].z = settings[category].z;
|
||||
writeVec3ToInterface(obj);
|
||||
});
|
||||
|
||||
folder.add(settings[category], 'z').step(0.1).onChange(function(value) {
|
||||
// Fires when a controller loses focus.
|
||||
var obj = {};
|
||||
obj[category] = {};
|
||||
obj[category].y = settings[category].y;
|
||||
obj[category].x = settings[category].x;
|
||||
obj[category][this.property] = value;
|
||||
writeVec3ToInterface(obj);
|
||||
});
|
||||
|
||||
folders.push(folder);
|
||||
folder.open();
|
||||
}
|
||||
|
||||
function createQuatFolder(category) {
|
||||
var folder = gui.addFolder(category);
|
||||
|
||||
folder.add(settings[category], 'x').step(0.1).onChange(function(value) {
|
||||
// Fires when a controller loses focus.
|
||||
var obj = {};
|
||||
obj[category] = {};
|
||||
obj[category][this.property] = value;
|
||||
obj[category].y = settings[category].y;
|
||||
obj[category].z = settings[category].z;
|
||||
obj[category].w = settings[category].w;
|
||||
writeVec3ToInterface(obj);
|
||||
});
|
||||
|
||||
folder.add(settings[category], 'y').step(0.1).onChange(function(value) {
|
||||
// Fires when a controller loses focus.
|
||||
var obj = {};
|
||||
obj[category] = {};
|
||||
obj[category].x = settings[category].x;
|
||||
obj[category][this.property] = value;
|
||||
obj[category].z = settings[category].z;
|
||||
obj[category].w = settings[category].w;
|
||||
writeVec3ToInterface(obj);
|
||||
});
|
||||
|
||||
folder.add(settings[category], 'z').step(0.1).onChange(function(value) {
|
||||
// Fires when a controller loses focus.
|
||||
var obj = {};
|
||||
obj[category] = {};
|
||||
obj[category].x = settings[category].x;
|
||||
obj[category].y = settings[category].y;
|
||||
obj[category][this.property] = value;
|
||||
obj[category].w = settings[category].w;
|
||||
writeVec3ToInterface(obj);
|
||||
});
|
||||
|
||||
folder.add(settings[category], 'w').step(0.1).onChange(function(value) {
|
||||
// Fires when a controller loses focus.
|
||||
var obj = {};
|
||||
obj[category] = {};
|
||||
obj[category].x = settings[category].x;
|
||||
obj[category].y = settings[category].y;
|
||||
obj[category].z = settings[category].z;
|
||||
obj[category][this.property] = value;
|
||||
writeVec3ToInterface(obj);
|
||||
});
|
||||
|
||||
folders.push(folder);
|
||||
folder.open();
|
||||
}
|
||||
|
||||
function convertColorObjectToArray(colorObject) {
|
||||
var colorArray = [];
|
||||
|
||||
_.each(colorObject, function(singleColor) {
|
||||
colorArray.push(singleColor);
|
||||
});
|
||||
|
||||
return colorArray;
|
||||
}
|
||||
|
||||
function convertColorArrayToObject(colorArray) {
|
||||
var colorObject = {
|
||||
red: colorArray[0],
|
||||
green: colorArray[1],
|
||||
blue: colorArray[2]
|
||||
};
|
||||
|
||||
return colorObject;
|
||||
}
|
||||
|
||||
function writeDataToInterface(property, value) {
|
||||
var data = {};
|
||||
data[property] = value;
|
||||
|
||||
var sendData = {
|
||||
messageType: "settings_update",
|
||||
updatedSettings: data
|
||||
};
|
||||
|
||||
var stringifiedData = JSON.stringify(sendData);
|
||||
|
||||
EventBridge.emitWebEvent(stringifiedData);
|
||||
}
|
||||
|
||||
function writeVec3ToInterface(obj) {
|
||||
var sendData = {
|
||||
messageType: "settings_update",
|
||||
updatedSettings: obj
|
||||
};
|
||||
|
||||
var stringifiedData = JSON.stringify(sendData);
|
||||
|
||||
EventBridge.emitWebEvent(stringifiedData);
|
||||
}
|
||||
|
||||
function listenForSettingsUpdates() {
|
||||
EventBridge.scriptEventReceived.connect(function(data) {
|
||||
data = JSON.parse(data);
|
||||
|
||||
//2-way
|
||||
// if (data.messageType === 'object_update') {
|
||||
// _.each(data.objectSettings, function(value, key) {
|
||||
// settings[key] = value;
|
||||
// });
|
||||
// }
|
||||
|
||||
if (data.messageType === 'initial_settings') {
|
||||
_.each(data.initialSettings, function(value, key) {
|
||||
settings[key] = {};
|
||||
settings[key] = value;
|
||||
});
|
||||
|
||||
loadGUI();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function manuallyUpdateDisplay() {
|
||||
// Iterate over all controllers
|
||||
// this is expensive, write a method for indiviudal controllers and use it when the value is different than a cached value, perhaps.
|
||||
var i;
|
||||
for (i in gui.__controllers) {
|
||||
gui.__controllers[i].updateDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
function setGUIWidthToWindowWidth() {
|
||||
if (gui !== null) {
|
||||
gui.width = window.innerWidth;
|
||||
}
|
||||
}
|
||||
|
||||
function handleInputKeyPress(e) {
|
||||
if (e.keyCode === 13) {
|
||||
importSettings();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function importSettings() {
|
||||
var importInput = document.getElementById('importer-input');
|
||||
|
||||
try {
|
||||
var importedSettings = JSON.parse(importInput.value);
|
||||
|
||||
var keys = _.keys(importedSettings);
|
||||
|
||||
_.each(keys, function(key) {
|
||||
var shouldIgnore = _.contains(keysToIgnore, key);
|
||||
|
||||
if (shouldIgnore) {
|
||||
return;
|
||||
}
|
||||
|
||||
settings[key] = importedSettings[key];
|
||||
});
|
||||
|
||||
writeVec3ToInterface(settings);
|
||||
|
||||
manuallyUpdateDisplay();
|
||||
} catch (e) {
|
||||
alert('Not properly formatted JSON');
|
||||
}
|
||||
}
|
||||
|
||||
function prepareSettingsForExport() {
|
||||
var keys = _.keys(settings);
|
||||
|
||||
var exportSettings = {};
|
||||
|
||||
_.each(keys, function(key) {
|
||||
var shouldIgnore = _.contains(keysToIgnore, key);
|
||||
|
||||
if (shouldIgnore) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.indexOf('color') > -1) {
|
||||
var colorObject = convertColorArrayToObject(settings[key]);
|
||||
settings[key] = colorObject;
|
||||
}
|
||||
|
||||
exportSettings[key] = settings[key];
|
||||
});
|
||||
|
||||
return JSON.stringify(exportSettings);
|
||||
}
|
||||
|
||||
function showPreselectedPrompt() {
|
||||
window.prompt("Ctrl-C to copy, then Enter.", prepareSettingsForExport());
|
||||
}
|
||||
|
||||
function removeContainerDomElement() {
|
||||
var elem = document.getElementById("my-gui-container");
|
||||
elem.parentNode.removeChild(elem);
|
||||
}
|
||||
|
||||
function removeListenerFromGUI(key) {
|
||||
_.each(gui.__listening, function(controller, index) {
|
||||
if (controller.property === key) {
|
||||
storedController = controller;
|
||||
gui.__listening.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//the section below is to try to work at achieving two way bindings;
|
||||
function addListenersBackToGUI() {
|
||||
gui.__listening.push(storedController);
|
||||
storedController = null;
|
||||
}
|
||||
|
||||
function registerDOMElementsForListenerBlocking() {
|
||||
_.each(gui.__controllers, function(controller) {
|
||||
var input = controller.domElement.childNodes[0];
|
||||
input.addEventListener('focus', function() {
|
||||
console.log('INPUT ELEMENT GOT FOCUS!' + controller.property);
|
||||
removeListenerFromGUI(controller.property);
|
||||
});
|
||||
});
|
||||
|
||||
_.each(gui.__controllers, function(controller) {
|
||||
var input = controller.domElement.childNodes[0];
|
||||
input.addEventListener('blur', function() {
|
||||
console.log('INPUT ELEMENT GOT BLUR!' + controller.property);
|
||||
addListenersBackToGUI();
|
||||
});
|
||||
});
|
||||
|
||||
// also listen to inputs inside of folders
|
||||
_.each(gui.__folders, function(folder) {
|
||||
_.each(folder.__controllers, function(controller) {
|
||||
var input = controller.__input;
|
||||
input.addEventListener('focus', function() {
|
||||
console.log('FOLDER ELEMENT GOT FOCUS!' + controller.property);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
///utility method for converting weird collisionWillMove type propertyies from binary to new Boolean()
|
||||
//
|
||||
// function convertBinaryToBoolean(value) {
|
||||
// if (value === 0) {
|
||||
// return false;
|
||||
// }
|
||||
// return true;
|
||||
// }
|
226
examples/particle_explorer/particleExplorer.js
Normal file
226
examples/particle_explorer/particleExplorer.js
Normal file
|
@ -0,0 +1,226 @@
|
|||
//
|
||||
// particleExplorer.js
|
||||
//
|
||||
// Created by James B. Pollack @imgntn on 9/26/2015
|
||||
// includes setup from @ctrlaltdavid's particlesTest.js
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Interface side of the App.
|
||||
// Quickly edit the aesthetics of a particle system.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// next version: 2 way bindings, integrate with edit.js
|
||||
//
|
||||
/*global print, WebWindow, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||
|
||||
var box,
|
||||
sphere,
|
||||
sphereDimensions = {
|
||||
x: 0.4,
|
||||
y: 0.8,
|
||||
z: 0.2
|
||||
},
|
||||
pointDimensions = {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0
|
||||
},
|
||||
sphereOrientation = Quat.fromPitchYawRollDegrees(-60.0, 30.0, 0.0),
|
||||
verticalOrientation = Quat.fromPitchYawRollDegrees(-90.0, 0.0, 0.0),
|
||||
particles,
|
||||
particleExample = -1,
|
||||
PARTICLE_RADIUS = 0.04,
|
||||
SLOW_EMIT_RATE = 2.0,
|
||||
HALF_EMIT_RATE = 50.0,
|
||||
FAST_EMIT_RATE = 100.0,
|
||||
SLOW_EMIT_SPEED = 0.025,
|
||||
FAST_EMIT_SPEED = 1.0,
|
||||
GRAVITY_EMIT_ACCELERATON = {
|
||||
x: 0.0,
|
||||
y: -0.3,
|
||||
z: 0.0
|
||||
},
|
||||
ZERO_EMIT_ACCELERATON = {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0
|
||||
},
|
||||
PI = 3.141593,
|
||||
DEG_TO_RAD = PI / 180.0,
|
||||
NUM_PARTICLE_EXAMPLES = 18;
|
||||
|
||||
var particleProperties;
|
||||
|
||||
function setUp() {
|
||||
var boxPoint,
|
||||
spawnPoint,
|
||||
animation = {
|
||||
fps: 30,
|
||||
frameIndex: 0,
|
||||
running: true,
|
||||
firstFrame: 0,
|
||||
lastFrame: 30,
|
||||
loop: true
|
||||
};
|
||||
|
||||
boxPoint = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation())));
|
||||
boxPoint = Vec3.sum(boxPoint, {
|
||||
x: 0.0,
|
||||
y: -0.5,
|
||||
z: 0.0
|
||||
});
|
||||
spawnPoint = Vec3.sum(boxPoint, {
|
||||
x: 0.0,
|
||||
y: 1.0,
|
||||
z: 0.0
|
||||
});
|
||||
|
||||
box = Entities.addEntity({
|
||||
type: "Box",
|
||||
name: "ParticlesTest Box",
|
||||
position: boxPoint,
|
||||
rotation: verticalOrientation,
|
||||
dimensions: {
|
||||
x: 0.3,
|
||||
y: 0.3,
|
||||
z: 0.3
|
||||
},
|
||||
color: {
|
||||
red: 128,
|
||||
green: 128,
|
||||
blue: 128
|
||||
},
|
||||
lifetime: 3600, // 1 hour; just in case
|
||||
visible: true
|
||||
});
|
||||
|
||||
// Same size and orientation as emitter when ellipsoid.
|
||||
sphere = Entities.addEntity({
|
||||
type: "Sphere",
|
||||
name: "ParticlesTest Sphere",
|
||||
position: boxPoint,
|
||||
rotation: sphereOrientation,
|
||||
dimensions: sphereDimensions,
|
||||
color: {
|
||||
red: 128,
|
||||
green: 128,
|
||||
blue: 128
|
||||
},
|
||||
lifetime: 3600, // 1 hour; just in case
|
||||
visible: false
|
||||
});
|
||||
|
||||
// 1.0m above the box or ellipsoid.
|
||||
particles = Entities.addEntity({
|
||||
type: "ParticleEffect",
|
||||
name: "ParticlesTest Emitter",
|
||||
position: spawnPoint,
|
||||
emitOrientation: verticalOrientation,
|
||||
particleRadius: PARTICLE_RADIUS,
|
||||
radiusSpread: 0.0,
|
||||
emitRate: SLOW_EMIT_RATE,
|
||||
emitSpeed: FAST_EMIT_SPEED,
|
||||
speedSpread: 0.0,
|
||||
emitAcceleration: GRAVITY_EMIT_ACCELERATON,
|
||||
accelerationSpread: {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0
|
||||
},
|
||||
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
lifespan: 5.0,
|
||||
visible: false,
|
||||
locked: false,
|
||||
animationSettings: animation,
|
||||
animationIsPlaying: true,
|
||||
lifetime: 3600 // 1 hour; just in case
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
SettingsWindow = function() {
|
||||
var _this = this;
|
||||
|
||||
this.webWindow = null;
|
||||
|
||||
this.init = function() {
|
||||
Script.update.connect(waitForObjectAuthorization);
|
||||
_this.webWindow = new WebWindow('Particle Explorer', Script.resolvePath('index.html'), 400, 600, false);
|
||||
_this.webWindow.eventBridge.webEventReceived.connect(_this.onWebEventReceived);
|
||||
};
|
||||
|
||||
this.sendData = function(data) {
|
||||
_this.webWindow.eventBridge.emitScriptEvent(JSON.stringify(data));
|
||||
};
|
||||
|
||||
this.onWebEventReceived = function(data) {
|
||||
var _data = JSON.parse(data);
|
||||
if (_data.messageType === 'page_loaded') {
|
||||
_this.webWindow.setVisible(true);
|
||||
_this.pageLoaded = true;
|
||||
sendInitialSettings(particleProperties);
|
||||
}
|
||||
if (_data.messageType === 'settings_update') {
|
||||
editEntity(_data.updatedSettings);
|
||||
return;
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
function waitForObjectAuthorization() {
|
||||
var properties = Entities.getEntityProperties(particles, "isKnownID");
|
||||
var isKnownID = properties.isKnownID;
|
||||
if (isKnownID === false || SettingsWindow.pageLoaded === false) {
|
||||
return;
|
||||
}
|
||||
var currentProperties = Entities.getEntityProperties(particles);
|
||||
particleProperties = currentProperties;
|
||||
Script.update.connect(sendObjectUpdates);
|
||||
Script.update.disconnect(waitForObjectAuthorization);
|
||||
}
|
||||
|
||||
function sendObjectUpdates() {
|
||||
var currentProperties = Entities.getEntityProperties(particles);
|
||||
sendUpdatedObject(currentProperties);
|
||||
}
|
||||
|
||||
function sendInitialSettings(properties) {
|
||||
var settings = {
|
||||
messageType: 'initial_settings',
|
||||
initialSettings: properties
|
||||
};
|
||||
|
||||
settingsWindow.sendData(settings);
|
||||
}
|
||||
|
||||
function sendUpdatedObject(properties) {
|
||||
var settings = {
|
||||
messageType: 'object_update',
|
||||
objectSettings: properties
|
||||
};
|
||||
settingsWindow.sendData(settings);
|
||||
}
|
||||
|
||||
function editEntity(properties) {
|
||||
Entities.editEntity(particles, properties);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
Entities.deleteEntity(particles);
|
||||
Entities.deleteEntity(box);
|
||||
Entities.deleteEntity(sphere);
|
||||
Script.update.disconnect(sendObjectUpdates);
|
||||
}
|
||||
|
||||
var settingsWindow = new SettingsWindow();
|
||||
settingsWindow.init();
|
||||
setUp();
|
||||
Script.scriptEnding.connect(tearDown);
|
6
examples/particle_explorer/underscore-min.js
vendored
Normal file
6
examples/particle_explorer/underscore-min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,41 +0,0 @@
|
|||
// sprayPaintSpawner.js
|
||||
//
|
||||
// Created by Eric Levin on 9/3/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is script spwans a spreay paint can model with the sprayPaintCan.js entity script attached
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
//Just temporarily using my own bucket here so others can test the entity. Once PR is tested and merged, then the entity script will appear in its proper place in S3, and I wil switch it
|
||||
// var scriptURL = "https://hifi-public.s3.amazonaws.com/eric/scripts/sprayPaintCan.js?=v6 ";
|
||||
var scriptURL = Script.resolvePath("entityScripts/sprayPaintCan.js?v2");
|
||||
var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx";
|
||||
|
||||
var sprayCan = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "spraycan",
|
||||
modelURL: modelURL,
|
||||
position: {x: 549.12, y:495.55, z:503.77},
|
||||
rotation: {x: 0, y: 0, z: 0, w: 1},
|
||||
dimensions: {
|
||||
x: 0.07,
|
||||
y: 0.17,
|
||||
z: 0.07
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
shapeType: 'box',
|
||||
script: scriptURL,
|
||||
gravity: {x: 0, y: -0.5, z: 0},
|
||||
velocity: {x: 0, y: -1, z: 0}
|
||||
});
|
||||
|
||||
function cleanup() {
|
||||
|
||||
// Uncomment the below line to delete sprayCan on script reload- for faster iteration during development
|
||||
// Entities.deleteEntity(sprayCan);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
|
52
examples/toys/cat.js
Normal file
52
examples/toys/cat.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// cat.js
|
||||
// examples/toybox/entityScripts
|
||||
//
|
||||
// Created by Eric Levin on 9/21/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
/*global Cat, print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||
|
||||
|
||||
(function() {
|
||||
|
||||
var _this;
|
||||
Cat = function() {
|
||||
_this = this;
|
||||
this.meowSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Animals/cat_meow.wav");
|
||||
};
|
||||
|
||||
Cat.prototype = {
|
||||
isMeowing: false,
|
||||
injector: null,
|
||||
startTouch: function() {
|
||||
if (this.isMeowing !== true) {
|
||||
this.meow();
|
||||
this.isMeowing = true;
|
||||
}
|
||||
|
||||
},
|
||||
meow: function() {
|
||||
this.injector = Audio.playSound(this.meowSound, {
|
||||
position: this.position,
|
||||
volume: 0.1
|
||||
});
|
||||
Script.setTimeout(function() {
|
||||
_this.isMeowing = false;
|
||||
}, this.soundClipTime);
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
this.position = Entities.getEntityProperties(this.entityID, "position").position;
|
||||
this.soundClipTime = 700;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new Cat();
|
||||
});
|
|
@ -15,21 +15,14 @@
|
|||
(function() {
|
||||
Script.include("../../utilities.js");
|
||||
Script.include("../../libraries/utils.js");
|
||||
|
||||
var _this;
|
||||
// this is the "constructor" for the entity as a JS object we don't do much here
|
||||
var Doll = function() {
|
||||
_this = this;
|
||||
this.screamSounds = [SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/KenDoll_1%2303.wav")];
|
||||
};
|
||||
|
||||
Doll.prototype = {
|
||||
startAnimationSetting: JSON.stringify({
|
||||
running: true,
|
||||
fps: 30,
|
||||
startFrame: 0,
|
||||
lastFrame: 128,
|
||||
startAutomatically: true
|
||||
}),
|
||||
stopAnimationSetting: JSON.stringify({running: false}),
|
||||
audioInjector: null,
|
||||
isGrabbed: false,
|
||||
setLeftHand: function() {
|
||||
|
@ -41,26 +34,27 @@
|
|||
},
|
||||
|
||||
startNearGrab: function() {
|
||||
if (this.isGrabbed === false) {
|
||||
Entities.editEntity(this.entityID, {
|
||||
animationURL: "https://hifi-public.s3.amazonaws.com/models/Bboys/zombie_scream.fbx",
|
||||
animationFrameIndex: 0
|
||||
});
|
||||
|
||||
Entities.editEntity(this.entityID, {
|
||||
animationURL: "https://hifi-public.s3.amazonaws.com/models/Bboys/zombie_scream.fbx",
|
||||
animationSettings: this.startAnimationSetting
|
||||
});
|
||||
Entities.editEntity(_this.entityID, {
|
||||
animationIsPlaying: true
|
||||
});
|
||||
|
||||
var position = Entities.getEntityProperties(this.entityID, "position").position;
|
||||
this.audioInjector = Audio.playSound(this.screamSounds[randInt(0, this.screamSounds.length)], {
|
||||
position: position,
|
||||
volume: 0.1
|
||||
});
|
||||
var position = Entities.getEntityProperties(this.entityID, "position").position;
|
||||
this.audioInjector = Audio.playSound(this.screamSounds[randInt(0, this.screamSounds.length)], {
|
||||
position: position,
|
||||
volume: 0.1
|
||||
});
|
||||
|
||||
this.isGrabbed = true;
|
||||
this.initialHand = this.hand;
|
||||
}
|
||||
this.isGrabbed = true;
|
||||
this.initialHand = this.hand;
|
||||
},
|
||||
|
||||
continueNearGrab: function() {
|
||||
var props = Entities.getEntityProperties(this.entityID, "position");
|
||||
var props = Entities.getEntityProperties(this.entityID, ["position"]);
|
||||
var audioOptions = {
|
||||
position: props.position
|
||||
};
|
||||
|
@ -69,24 +63,23 @@
|
|||
|
||||
releaseGrab: function() {
|
||||
if (this.isGrabbed === true && this.hand === this.initialHand) {
|
||||
|
||||
this.audioInjector.stop();
|
||||
Entities.editEntity(this.entityID, {
|
||||
animationFrameIndex: 0
|
||||
});
|
||||
|
||||
Entities.editEntity(this.entityID, {
|
||||
animationSettings: this.stopAnimationSetting,
|
||||
animationURL: "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx",
|
||||
animationURL: "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx"
|
||||
});
|
||||
|
||||
this.isGrabbed = false;
|
||||
}
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
// preload() will be called when the entity has become visible (or known) to the interface
|
||||
// it gives us a chance to set our local JavaScript object up. In this case it means:
|
||||
// * remembering our entityID, so we can access it in cases where we're called without an entityID
|
||||
this.entityID = entityID;
|
||||
},
|
||||
};
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new Doll();
|
||||
});
|
||||
});
|
||||
|
|
202
examples/toys/lightSwitchGarage.js
Normal file
202
examples/toys/lightSwitchGarage.js
Normal file
|
@ -0,0 +1,202 @@
|
|||
//
|
||||
// lightSwitchGarage.js.js
|
||||
// examples/entityScripts
|
||||
//
|
||||
// Created by Eric Levin on 9/21/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() {
|
||||
|
||||
var _this;
|
||||
|
||||
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
|
||||
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
|
||||
LightSwitchGarage = function() {
|
||||
_this = this;
|
||||
|
||||
this.lightStateKey = "lightStateKey";
|
||||
this.resetKey = "resetMe";
|
||||
|
||||
this.switchSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/lamp_switch_2.wav");
|
||||
|
||||
};
|
||||
|
||||
LightSwitchGarage.prototype = {
|
||||
|
||||
clickReleaseOnEntity: function(entityID, mouseEvent) {
|
||||
if (!mouseEvent.isLeftButton) {
|
||||
return;
|
||||
}
|
||||
this.toggleLights();
|
||||
},
|
||||
|
||||
startNearGrabNonColliding: function() {
|
||||
this.toggleLights();
|
||||
},
|
||||
|
||||
toggleLights: function() {
|
||||
var defaultLightData = {
|
||||
on: false
|
||||
};
|
||||
var lightState = getEntityCustomData(this.lightStateKey, this.entityID, defaultLightData);
|
||||
if (lightState.on === true) {
|
||||
this.clearLights();
|
||||
} else if (lightState.on === false) {
|
||||
this.createLights();
|
||||
}
|
||||
|
||||
this.flipLights();
|
||||
|
||||
Audio.playSound(this.switchSound, {
|
||||
volume: 0.5,
|
||||
position: this.position
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
clearLights: function() {
|
||||
var entities = Entities.findEntities(MyAvatar.position, 100);
|
||||
var self = this;
|
||||
entities.forEach(function(entity) {
|
||||
var resetData = getEntityCustomData(self.resetKey, entity, {})
|
||||
if (resetData.resetMe === true && resetData.lightType === "Sconce Light Garage") {
|
||||
Entities.deleteEntity(entity);
|
||||
}
|
||||
});
|
||||
|
||||
setEntityCustomData(this.lightStateKey, this.entityID, {
|
||||
on: false
|
||||
});
|
||||
},
|
||||
|
||||
createLights: function() {
|
||||
|
||||
var sconceLight3 = Entities.addEntity({
|
||||
type: "Light",
|
||||
position: {
|
||||
x: 545.49468994140625,
|
||||
y: 496.24026489257812,
|
||||
z: 500.63516235351562
|
||||
},
|
||||
|
||||
name: "Sconce 3 Light",
|
||||
dimensions: {
|
||||
x: 2.545,
|
||||
y: 2.545,
|
||||
z: 2.545
|
||||
},
|
||||
cutoff: 90,
|
||||
color: {
|
||||
red: 217,
|
||||
green: 146,
|
||||
blue: 24
|
||||
}
|
||||
});
|
||||
|
||||
setEntityCustomData(this.resetKey, sconceLight3, {
|
||||
resetMe: true,
|
||||
lightType: "Sconce Light Garage"
|
||||
});
|
||||
|
||||
var sconceLight4 = Entities.addEntity({
|
||||
type: "Light",
|
||||
position: {
|
||||
x: 550.90399169921875,
|
||||
y: 496.24026489257812,
|
||||
z: 507.90237426757812
|
||||
},
|
||||
name: "Sconce 4 Light",
|
||||
dimensions: {
|
||||
x: 2.545,
|
||||
y: 2.545,
|
||||
z: 2.545
|
||||
},
|
||||
cutoff: 90,
|
||||
color: {
|
||||
red: 217,
|
||||
green: 146,
|
||||
blue: 24
|
||||
}
|
||||
});
|
||||
|
||||
setEntityCustomData(this.resetKey, sconceLight4, {
|
||||
resetMe: true,
|
||||
lightType: "Sconce Light Garage"
|
||||
});
|
||||
|
||||
var sconceLight5 = Entities.addEntity({
|
||||
type: "Light",
|
||||
position: {
|
||||
x: 548.407958984375,
|
||||
y: 496.24026489257812,
|
||||
z: 509.5504150390625
|
||||
},
|
||||
name: "Sconce 5 Light",
|
||||
dimensions: {
|
||||
x: 2.545,
|
||||
y: 2.545,
|
||||
z: 2.545
|
||||
},
|
||||
cutoff: 90,
|
||||
color: {
|
||||
red: 217,
|
||||
green: 146,
|
||||
blue: 24
|
||||
}
|
||||
});
|
||||
|
||||
setEntityCustomData(this.resetKey, sconceLight5, {
|
||||
resetMe: true,
|
||||
lightType: "Sconce Light Garage"
|
||||
});
|
||||
|
||||
setEntityCustomData(this.lightStateKey, this.entityID, {
|
||||
on: true
|
||||
});
|
||||
},
|
||||
|
||||
flipLights: function() {
|
||||
// flip model to give illusion of light switch being flicked
|
||||
var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation;
|
||||
var axis = {
|
||||
x: 0,
|
||||
y: 1,
|
||||
z: 0
|
||||
};
|
||||
var dQ = Quat.angleAxis(180, axis);
|
||||
rotation = Quat.multiply(rotation, dQ);
|
||||
|
||||
|
||||
Entities.editEntity(this.entityID, {
|
||||
rotation: rotation
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
|
||||
//The light switch is static, so just cache its position once
|
||||
this.position = Entities.getEntityProperties(this.entityID, "position").position;
|
||||
var defaultLightData = {
|
||||
on: false
|
||||
};
|
||||
var lightState = getEntityCustomData(this.lightStateKey, this.entityID, defaultLightData);
|
||||
|
||||
//If light is off, then we create two new lights- at the position of the sconces
|
||||
if (lightState.on === false) {
|
||||
this.createLights();
|
||||
this.flipLights();
|
||||
}
|
||||
//If lights are on, do nothing!
|
||||
},
|
||||
};
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new LightSwitchGarage();
|
||||
})
|
179
examples/toys/lightSwitchHall.js
Normal file
179
examples/toys/lightSwitchHall.js
Normal file
|
@ -0,0 +1,179 @@
|
|||
//
|
||||
// lightSwitchHall.js
|
||||
// examples/entityScripts
|
||||
//
|
||||
// Created by Eric Levin on 9/21/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() {
|
||||
|
||||
var _this;
|
||||
|
||||
|
||||
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
|
||||
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
|
||||
LightSwitchHall = function() {
|
||||
_this = this;
|
||||
|
||||
this.lightStateKey = "lightStateKey";
|
||||
this.resetKey = "resetMe";
|
||||
|
||||
this.switchSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/lamp_switch_2.wav");
|
||||
};
|
||||
|
||||
LightSwitchHall.prototype = {
|
||||
|
||||
clickReleaseOnEntity: function(entityId, mouseEvent) {
|
||||
if (!mouseEvent.isLeftButton) {
|
||||
return;
|
||||
}
|
||||
this.toggleLights();
|
||||
},
|
||||
|
||||
startNearGrabNonColliding: function() {
|
||||
this.toggleLights();
|
||||
},
|
||||
|
||||
toggleLights: function() {
|
||||
var defaultLightData = {
|
||||
on: false
|
||||
};
|
||||
var lightState = getEntityCustomData(this.lightStateKey, this.entityID, defaultLightData);
|
||||
if (lightState.on === true) {
|
||||
this.clearLights();
|
||||
} else if (lightState.on === false) {
|
||||
this.createLights();
|
||||
}
|
||||
|
||||
// flip model to give illusion of light switch being flicked
|
||||
this.flipLights();
|
||||
|
||||
Audio.playSound(this.switchSound, {
|
||||
volume: 0.5,
|
||||
position: this.position
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
clearLights: function() {
|
||||
var entities = Entities.findEntities(MyAvatar.position, 100);
|
||||
var self = this;
|
||||
entities.forEach(function(entity) {
|
||||
var resetData = getEntityCustomData(self.resetKey, entity, {})
|
||||
if (resetData.resetMe === true && resetData.lightType === "Sconce Light Hall") {
|
||||
Entities.deleteEntity(entity);
|
||||
}
|
||||
});
|
||||
|
||||
setEntityCustomData(this.lightStateKey, this.entityID, {
|
||||
on: false
|
||||
});
|
||||
},
|
||||
|
||||
createLights: function() {
|
||||
var sconceLight1 = Entities.addEntity({
|
||||
type: "Light",
|
||||
position: {
|
||||
x: 543.75,
|
||||
y: 496.24,
|
||||
z: 511.13
|
||||
},
|
||||
name: "Sconce 1 Light",
|
||||
dimensions: {
|
||||
x: 2.545,
|
||||
y: 2.545,
|
||||
z: 2.545
|
||||
},
|
||||
cutoff: 90,
|
||||
color: {
|
||||
red: 217,
|
||||
green: 146,
|
||||
blue: 24
|
||||
}
|
||||
});
|
||||
|
||||
setEntityCustomData(this.resetKey, sconceLight1, {
|
||||
resetMe: true,
|
||||
lightType: "Sconce Light Hall"
|
||||
});
|
||||
|
||||
var sconceLight2 = Entities.addEntity({
|
||||
type: "Light",
|
||||
position: {
|
||||
x: 540.1,
|
||||
y: 496.24,
|
||||
z: 505.57
|
||||
},
|
||||
name: "Sconce 2 Light",
|
||||
dimensions: {
|
||||
x: 2.545,
|
||||
y: 2.545,
|
||||
z: 2.545
|
||||
},
|
||||
cutoff: 90,
|
||||
color: {
|
||||
red: 217,
|
||||
green: 146,
|
||||
blue: 24
|
||||
}
|
||||
});
|
||||
|
||||
setEntityCustomData(this.resetKey, sconceLight2, {
|
||||
resetMe: true,
|
||||
lightType: "Sconce Light Hall"
|
||||
});
|
||||
|
||||
setEntityCustomData(this.lightStateKey, this.entityID, {
|
||||
on: true
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
flipLights: function() {
|
||||
// flip model to give illusion of light switch being flicked
|
||||
var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation;
|
||||
var axis = {
|
||||
x: 0,
|
||||
y: 1,
|
||||
z: 0
|
||||
};
|
||||
var dQ = Quat.angleAxis(180, axis);
|
||||
rotation = Quat.multiply(rotation, dQ);
|
||||
|
||||
|
||||
Entities.editEntity(this.entityID, {
|
||||
rotation: rotation
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
// preload() will be called when the entity has become visible (or known) to the interface
|
||||
// it gives us a chance to set our local JavaScript object up. In this case it means:
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
|
||||
//The light switch is static, so just cache its position once
|
||||
this.position = Entities.getEntityProperties(this.entityID, "position").position;
|
||||
var defaultLightData = {
|
||||
on: false
|
||||
};
|
||||
var lightState = getEntityCustomData(this.lightStateKey, this.entityID, defaultLightData);
|
||||
|
||||
//If light is off, then we create two new lights- at the position of the sconces
|
||||
if (lightState.on === false) {
|
||||
this.createLights();
|
||||
this.flipLights();
|
||||
|
||||
}
|
||||
//If lights are on, do nothing!
|
||||
},
|
||||
};
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new LightSwitchHall();
|
||||
})
|
162
examples/toys/sprayPaintCan.js
Normal file
162
examples/toys/sprayPaintCan.js
Normal file
|
@ -0,0 +1,162 @@
|
|||
//
|
||||
// sprayPaintCan.js
|
||||
// examples/entityScripts
|
||||
//
|
||||
// Created by Eric Levin on 9/21/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
(function() {
|
||||
// Script.include("../libraries/utils.js");
|
||||
//Need absolute path for now, for testing before PR merge and s3 cloning. Will change post-merge
|
||||
|
||||
Script.include("../libraries/utils.js");
|
||||
|
||||
this.spraySound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/sprayPaintSound.wav");
|
||||
|
||||
var TIP_OFFSET_Z = 0.02;
|
||||
var TIP_OFFSET_Y = 0.08;
|
||||
|
||||
var ZERO_VEC = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
|
||||
// if the trigger value goes below this while held, the can will stop spraying. if it goes above, it will spray
|
||||
var DISABLE_SPRAY_THRESHOLD = 0.5;
|
||||
|
||||
var MAX_POINTS_PER_LINE = 40;
|
||||
var MIN_POINT_DISTANCE = 0.01;
|
||||
var STROKE_WIDTH = 0.02;
|
||||
|
||||
this.setRightHand = function() {
|
||||
this.hand = 'RIGHT';
|
||||
}
|
||||
|
||||
this.setLeftHand = function() {
|
||||
this.hand = 'LEFT';
|
||||
}
|
||||
|
||||
this.startNearGrab = function() {
|
||||
this.whichHand = this.hand;
|
||||
}
|
||||
|
||||
this.toggleWithTriggerPressure = function() {
|
||||
var handClickString = this.whichHand + "_HAND_CLICK";
|
||||
|
||||
var handClick = Controller.findAction(handClickString);
|
||||
|
||||
this.triggerValue = Controller.getActionValue(handClick);
|
||||
if (this.triggerValue < DISABLE_SPRAY_THRESHOLD && this.spraying === true) {
|
||||
this.spraying = false;
|
||||
this.disableStream();
|
||||
} else if (this.triggerValue >= DISABLE_SPRAY_THRESHOLD && this.spraying === false) {
|
||||
this.spraying = true;
|
||||
this.enableStream();
|
||||
}
|
||||
}
|
||||
|
||||
this.enableStream = function() {
|
||||
var position = Entities.getEntityProperties(this.entityId, "position").position;
|
||||
var animationSettings = JSON.stringify({
|
||||
fps: 30,
|
||||
loop: true,
|
||||
firstFrame: 1,
|
||||
lastFrame: 10000,
|
||||
running: true
|
||||
});
|
||||
var PI = 3.141593;
|
||||
var DEG_TO_RAD = PI / 180.0;
|
||||
|
||||
this.paintStream = Entities.addEntity({
|
||||
type: "ParticleEffect",
|
||||
name: "streamEffect",
|
||||
animationSettings: animationSettings,
|
||||
position: position,
|
||||
textures: "https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png",
|
||||
emitSpeed: 3,
|
||||
speedSpread: 0.02,
|
||||
emitAcceleration: ZERO_VEC,
|
||||
emitRate: 100,
|
||||
particleRadius: 0.01,
|
||||
radiusSpread: 0.005,
|
||||
polarFinish: 0.05,
|
||||
color: {
|
||||
red: 170,
|
||||
green: 20,
|
||||
blue: 150
|
||||
},
|
||||
lifetime: 50, //probably wont be holding longer than this straight
|
||||
});
|
||||
|
||||
setEntityCustomData(this.resetKey, this.paintStream, {
|
||||
resetMe: true
|
||||
});
|
||||
|
||||
this.sprayInjector = Audio.playSound(this.spraySound, {
|
||||
position: position,
|
||||
volume: this.sprayVolume,
|
||||
loop: true
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
this.releaseGrab = function() {
|
||||
this.disableStream();
|
||||
}
|
||||
|
||||
this.disableStream = function() {
|
||||
Entities.deleteEntity(this.paintStream);
|
||||
this.paintStream = null;
|
||||
this.spraying = false;
|
||||
this.sprayInjector.stop();
|
||||
}
|
||||
|
||||
|
||||
this.continueNearGrab = function() {
|
||||
|
||||
this.toggleWithTriggerPressure();
|
||||
|
||||
if (this.spraying === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
var props = Entities.getEntityProperties(this.entityId, ["position, rotation"]);
|
||||
var forwardVec = Quat.getFront(Quat.multiply(props.rotation, Quat.fromPitchYawRollDegrees(0, 90, 0)));
|
||||
forwardVec = Vec3.normalize(forwardVec);
|
||||
var forwardQuat = orientationOf(forwardVec);
|
||||
var upVec = Quat.getUp(props.rotation);
|
||||
var position = Vec3.sum(props.position, Vec3.multiply(forwardVec, TIP_OFFSET_Z));
|
||||
position = Vec3.sum(position, Vec3.multiply(upVec, TIP_OFFSET_Y))
|
||||
Entities.editEntity(this.paintStream, {
|
||||
position: position,
|
||||
emitOrientation: forwardQuat,
|
||||
});
|
||||
|
||||
this.sprayInjector.setOptions({
|
||||
position: position,
|
||||
volume: this.sprayVolume
|
||||
});
|
||||
}
|
||||
|
||||
this.preload = function(entityId) {
|
||||
this.sprayVolume = 0.1;
|
||||
this.spraying = false;
|
||||
this.entityId = entityId;
|
||||
this.resetKey = "resetMe";
|
||||
}
|
||||
|
||||
|
||||
this.unload = function() {
|
||||
if (this.paintStream) {
|
||||
Entities.deleteEntity(this.paintStream);
|
||||
}
|
||||
this.strokes.forEach(function(stroke) {
|
||||
Entities.deleteEntity(stroke);
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,6 +1,8 @@
|
|||
set(TARGET_NAME interface)
|
||||
project(${TARGET_NAME})
|
||||
|
||||
add_definitions(-DGLEW_STATIC)
|
||||
|
||||
# set a default root dir for each of our optional externals if it was not passed
|
||||
set(OPTIONAL_EXTERNALS "Faceshift" "LeapMotion" "RtMidi" "RSSDK" "3DConnexionClient" "iViewHMD")
|
||||
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
||||
|
|
|
@ -2764,12 +2764,19 @@ void Application::cameraMenuChanged() {
|
|||
}
|
||||
|
||||
void Application::reloadResourceCaches() {
|
||||
// Clear entities out of view frustum
|
||||
_viewFrustum.setPosition(glm::vec3(0.0f, 0.0f, TREE_SCALE));
|
||||
_viewFrustum.setOrientation(glm::quat());
|
||||
queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions);
|
||||
|
||||
emptyLocalCache();
|
||||
|
||||
|
||||
DependencyManager::get<AnimationCache>()->refreshAll();
|
||||
DependencyManager::get<ModelCache>()->refreshAll();
|
||||
DependencyManager::get<SoundCache>()->refreshAll();
|
||||
DependencyManager::get<TextureCache>()->refreshAll();
|
||||
|
||||
DependencyManager::get<NodeList>()->reset(); // Force redownload of .fst models
|
||||
}
|
||||
|
||||
void Application::rotationModeChanged() {
|
||||
|
|
|
@ -141,6 +141,7 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
|
|||
auto state = gpu::StatePointer(new gpu::State());
|
||||
// enable decal blend
|
||||
state->setDepthTest(gpu::State::DepthTest(false));
|
||||
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
_gridPipeline.reset(gpu::Pipeline::create(program, state));
|
||||
}
|
||||
|
@ -152,6 +153,7 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
|
|||
auto state = gpu::StatePointer(new gpu::State());
|
||||
// enable decal blend
|
||||
state->setDepthTest(gpu::State::DepthTest(false));
|
||||
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
state->setAntialiasedLineEnable(true); // line smoothing also smooth points
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
_starsPipeline.reset(gpu::Pipeline::create(program, state));
|
||||
|
|
|
@ -202,11 +202,14 @@ void Avatar::simulate(float deltaTime) {
|
|||
PerformanceTimer perfTimer("skeleton");
|
||||
for (int i = 0; i < _jointData.size(); i++) {
|
||||
const JointData& data = _jointData.at(i);
|
||||
_skeletonModel.setJointState(i, true, data.rotation);
|
||||
_skeletonModel.setJointRotation(i, data.rotationSet, data.rotation, 1.0f);
|
||||
_skeletonModel.setJointTranslation(i, data.translationSet, data.translation, 1.0f);
|
||||
}
|
||||
_skeletonModel.simulate(deltaTime, _hasNewJointRotations);
|
||||
|
||||
_skeletonModel.simulate(deltaTime, _hasNewJointRotations || _hasNewJointTranslations);
|
||||
simulateAttachments(deltaTime);
|
||||
_hasNewJointRotations = false;
|
||||
_hasNewJointTranslations = false;
|
||||
}
|
||||
{
|
||||
PerformanceTimer perfTimer("head");
|
||||
|
@ -879,6 +882,16 @@ glm::quat Avatar::getJointRotation(int index) const {
|
|||
return rotation;
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::getJointTranslation(int index) const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
return AvatarData::getJointTranslation(index);
|
||||
}
|
||||
glm::vec3 translation;
|
||||
_skeletonModel.getJointTranslation(index, translation);
|
||||
return translation;
|
||||
}
|
||||
|
||||
|
||||
int Avatar::getJointIndex(const QString& name) const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
int result;
|
||||
|
|
|
@ -115,6 +115,7 @@ public:
|
|||
|
||||
virtual QVector<glm::quat> getJointRotations() const;
|
||||
virtual glm::quat getJointRotation(int index) const;
|
||||
virtual glm::vec3 getJointTranslation(int index) const;
|
||||
virtual int getJointIndex(const QString& name) const;
|
||||
virtual QStringList getJointNames() const;
|
||||
|
||||
|
|
|
@ -240,9 +240,11 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
PerformanceTimer perfTimer("joints");
|
||||
// copy out the skeleton joints from the model
|
||||
_jointData.resize(_rig->getJointStateCount());
|
||||
|
||||
for (int i = 0; i < _jointData.size(); i++) {
|
||||
JointData& data = _jointData[i];
|
||||
_rig->getJointStateRotation(i, data.rotation);
|
||||
data.rotationSet |= _rig->getJointStateRotation(i, data.rotation);
|
||||
data.translationSet |= _rig->getJointStateTranslation(i, data.translation);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1115,21 +1117,43 @@ void MyAvatar::setJointRotations(QVector<glm::quat> jointRotations) {
|
|||
int numStates = glm::min(_skeletonModel.getJointStateCount(), jointRotations.size());
|
||||
for (int i = 0; i < numStates; ++i) {
|
||||
// HACK: ATM only Recorder calls setJointRotations() so we hardcode its priority here
|
||||
_skeletonModel.setJointState(i, true, jointRotations[i], RECORDER_PRIORITY);
|
||||
_skeletonModel.setJointRotation(i, true, jointRotations[i], RECORDER_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setJointData(int index, const glm::quat& rotation) {
|
||||
void MyAvatar::setJointTranslations(QVector<glm::vec3> jointTranslations) {
|
||||
int numStates = glm::min(_skeletonModel.getJointStateCount(), jointTranslations.size());
|
||||
for (int i = 0; i < numStates; ++i) {
|
||||
// HACK: ATM only Recorder calls setJointTranslations() so we hardcode its priority here
|
||||
_skeletonModel.setJointTranslation(i, true, jointTranslations[i], RECORDER_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) {
|
||||
if (QThread::currentThread() == thread()) {
|
||||
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
|
||||
_rig->setJointState(index, true, rotation, SCRIPT_PRIORITY);
|
||||
_rig->setJointState(index, true, rotation, translation, SCRIPT_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setJointRotation(int index, const glm::quat& rotation) {
|
||||
if (QThread::currentThread() == thread()) {
|
||||
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
|
||||
_rig->setJointRotation(index, true, rotation, SCRIPT_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setJointTranslation(int index, const glm::vec3& translation) {
|
||||
if (QThread::currentThread() == thread()) {
|
||||
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
|
||||
_rig->setJointTranslation(index, true, translation, SCRIPT_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::clearJointData(int index) {
|
||||
if (QThread::currentThread() == thread()) {
|
||||
// HACK: ATM only JS scripts call clearJointData() on MyAvatar so we hardcode the priority
|
||||
_rig->setJointState(index, false, glm::quat(), 0.0f);
|
||||
_rig->setJointState(index, false, glm::quat(), glm::vec3(), 0.0f);
|
||||
_rig->clearJointAnimationPriority(index);
|
||||
}
|
||||
}
|
||||
|
@ -1428,7 +1452,10 @@ void MyAvatar::preRender(RenderArgs* renderArgs) {
|
|||
AnimPose pose = _debugDrawSkeleton->getRelativeBindPose(i);
|
||||
glm::quat jointRot;
|
||||
_rig->getJointRotationInConstrainedFrame(i, jointRot);
|
||||
glm::vec3 jointTrans;
|
||||
_rig->getJointTranslation(i, jointTrans);
|
||||
pose.rot = pose.rot * jointRot;
|
||||
pose.trans = jointTrans;
|
||||
poses.push_back(pose);
|
||||
}
|
||||
|
||||
|
|
|
@ -140,7 +140,10 @@ public:
|
|||
void clearLookAtTargetAvatar();
|
||||
|
||||
virtual void setJointRotations(QVector<glm::quat> jointRotations);
|
||||
virtual void setJointData(int index, const glm::quat& rotation);
|
||||
virtual void setJointTranslations(QVector<glm::vec3> jointTranslations);
|
||||
virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation);
|
||||
virtual void setJointRotation(int index, const glm::quat& rotation);
|
||||
virtual void setJointTranslation(int index, const glm::vec3& translation);
|
||||
virtual void clearJointData(int index);
|
||||
virtual void clearJointsData();
|
||||
|
||||
|
|
|
@ -246,6 +246,12 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
Hand* hand = _owningAvatar->getHand();
|
||||
hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
|
||||
|
||||
// let rig compute the model offset
|
||||
glm::vec3 modelOffset;
|
||||
if (_rig->getModelOffset(modelOffset)) {
|
||||
setOffset(modelOffset);
|
||||
}
|
||||
|
||||
// Don't Relax toward hand positions when in animGraph mode.
|
||||
if (!_rig->getEnableAnimGraph()) {
|
||||
const float HAND_RESTORATION_RATE = 0.25f;
|
||||
|
@ -651,6 +657,7 @@ void SkeletonModel::computeBoundingShape() {
|
|||
// RECOVER FROM BOUNINDG SHAPE HACK: now that we're all done, restore the default pose
|
||||
for (int i = 0; i < numStates; i++) {
|
||||
_rig->restoreJointRotation(i, 1.0f, 1.0f);
|
||||
_rig->restoreJointTranslation(i, 1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -135,23 +135,46 @@ void AnimClip::copyFromNetworkAnim() {
|
|||
const int frameCount = geom.animationFrames.size();
|
||||
const int skeletonJointCount = _skeleton->getNumJoints();
|
||||
_anim.resize(frameCount);
|
||||
for (int i = 0; i < frameCount; i++) {
|
||||
|
||||
const glm::vec3 offsetScale = extractScale(geom.offset);
|
||||
|
||||
for (int frame = 0; frame < frameCount; frame++) {
|
||||
|
||||
// init all joints in animation to bind pose
|
||||
_anim[i].reserve(skeletonJointCount);
|
||||
for (int j = 0; j < skeletonJointCount; j++) {
|
||||
_anim[i].push_back(_skeleton->getRelativeBindPose(j));
|
||||
// this will give us a resonable result for bones in the skeleton but not in the animation.
|
||||
_anim[frame].reserve(skeletonJointCount);
|
||||
for (int skeletonJoint = 0; skeletonJoint < skeletonJointCount; skeletonJoint++) {
|
||||
_anim[frame].push_back(_skeleton->getRelativeBindPose(skeletonJoint));
|
||||
}
|
||||
|
||||
// init over all joint animations
|
||||
for (int j = 0; j < animJointCount; j++) {
|
||||
int k = jointMap[j];
|
||||
if (k >= 0 && k < skeletonJointCount) {
|
||||
// currently FBX animations only have rotation.
|
||||
_anim[i][k].rot = _skeleton->getRelativeBindPose(k).rot * geom.animationFrames[i].rotations[j];
|
||||
for (int animJoint = 0; animJoint < animJointCount; animJoint++) {
|
||||
|
||||
int skeletonJoint = jointMap[animJoint];
|
||||
|
||||
// skip joints that are in the animation but not in the skeleton.
|
||||
if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) {
|
||||
|
||||
const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint] * offsetScale;
|
||||
const AnimPose& relBindPose = _skeleton->getRelativeBindPose(skeletonJoint);
|
||||
|
||||
// used to adjust translation offsets, so large translation animatons on the reference skeleton
|
||||
// will be adjusted when played on a skeleton with short limbs.
|
||||
float limbLengthScale = fabs(glm::length(fbxZeroTrans)) <= 0.0001f ? 1.0f : (glm::length(relBindPose.trans) / glm::length(fbxZeroTrans));
|
||||
|
||||
AnimPose& pose = _anim[frame][skeletonJoint];
|
||||
const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame];
|
||||
|
||||
// rotation in fbxAnimationFrame is a delta from a reference skeleton bind pose.
|
||||
pose.rot = relBindPose.rot * fbxAnimFrame.rotations[animJoint];
|
||||
|
||||
// translation in fbxAnimationFrame is not a delta.
|
||||
// convert it into a delta by subtracting from the first frame.
|
||||
const glm::vec3& fbxTrans = fbxAnimFrame.translations[animJoint] * offsetScale;
|
||||
pose.trans = relBindPose.trans + limbLengthScale * (fbxTrans - fbxZeroTrans);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_poses.resize(skeletonJointCount);
|
||||
}
|
||||
|
||||
|
|
|
@ -348,6 +348,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
|||
} else {
|
||||
_relativePoses[i].rot = underPoses[i].rot;
|
||||
}
|
||||
_relativePoses[i].trans = underPoses[i].trans;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -173,6 +173,7 @@ void AnimationHandle::applyFrame(float frameIndex) {
|
|||
const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(frameIndex) % frameCount);
|
||||
const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(frameIndex) % frameCount);
|
||||
float frameFraction = glm::fract(frameIndex);
|
||||
|
||||
for (int i = 0; i < _jointMappings.size(); i++) {
|
||||
int mapping = _jointMappings.at(i);
|
||||
if (mapping != -1) { // allow missing bones
|
||||
|
@ -182,6 +183,15 @@ void AnimationHandle::applyFrame(float frameIndex) {
|
|||
frameFraction),
|
||||
_priority,
|
||||
_mix);
|
||||
|
||||
// This isn't working.
|
||||
// glm::vec3 floorTranslationPart = floorFrame.translations.at(i) * (1.0f - frameFraction);
|
||||
// glm::vec3 ceilTranslationPart = ceilFrame.translations.at(i) * frameFraction;
|
||||
// glm::vec3 floorCeilFraction = floorTranslationPart + ceilTranslationPart;
|
||||
// glm::vec3 defaultTrans = _rig->getJointDefaultTranslationInConstrainedFrame(i);
|
||||
// glm::vec3 mixedTranslation = floorCeilFraction * (1.0f - _mix) + defaultTrans * _mix;
|
||||
// _rig->setJointTranslation(mapping, true, mixedTranslation, _priority);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,6 +212,7 @@ void AnimationHandle::restoreJoints() {
|
|||
int mapping = _jointMappings.at(i);
|
||||
if (mapping != -1) {
|
||||
_rig->restoreJointRotation(mapping, 1.0f, _rig->getJointAnimatinoPriority(mapping));
|
||||
_rig->restoreJointTranslation(mapping, 1.0f, _rig->getJointAnimatinoPriority(mapping));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,7 @@ void AvatarRig::updateJointState(int index, glm::mat4 rootTransform) {
|
|||
|
||||
// compute model transforms
|
||||
if (index == _rootJointIndex) {
|
||||
// we always zero-out the translation part of an avatar's root join-transform.
|
||||
state.computeTransform(rootTransform);
|
||||
clearJointTransformTranslation(index);
|
||||
} else {
|
||||
// guard against out-of-bounds access to _jointStates
|
||||
int parentIndex = state.getParentIndex();
|
||||
|
@ -97,3 +95,28 @@ void AvatarRig::setHandPosition(int jointIndex,
|
|||
shoulderRotation, priority);
|
||||
setJointRotationInBindFrame(jointIndex, rotation, priority);
|
||||
}
|
||||
|
||||
void AvatarRig::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) {
|
||||
if (index != -1 && index < _jointStates.size()) {
|
||||
JointState& state = _jointStates[index];
|
||||
if (valid) {
|
||||
state.setTranslation(translation, priority);
|
||||
} else {
|
||||
state.restoreTranslation(1.0f, priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AvatarRig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) {
|
||||
if (index != -1 && index < _jointStates.size()) {
|
||||
JointState& state = _jointStates[index];
|
||||
if (valid) {
|
||||
state.setRotationInConstrainedFrame(rotation, priority);
|
||||
state.setTranslation(translation, priority);
|
||||
} else {
|
||||
state.restoreRotation(1.0f, priority);
|
||||
state.restoreTranslation(1.0f, priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,10 @@ class AvatarRig : public Rig {
|
|||
virtual void updateJointState(int index, glm::mat4 rootTransform);
|
||||
virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation,
|
||||
float scale, float priority);
|
||||
virtual void setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority);
|
||||
virtual void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // hifi_AvatarRig_h
|
||||
|
|
|
@ -27,3 +27,17 @@ void EntityRig::updateJointState(int index, glm::mat4 rootTransform) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void EntityRig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) {
|
||||
if (index != -1 && index < _jointStates.size()) {
|
||||
JointState& state = _jointStates[index];
|
||||
if (valid) {
|
||||
state.setRotationInConstrainedFrame(rotation, priority);
|
||||
// state.setTranslation(translation, priority);
|
||||
} else {
|
||||
state.restoreRotation(1.0f, priority);
|
||||
// state.restoreTranslation(1.0f, priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ class EntityRig : public Rig {
|
|||
virtual void updateJointState(int index, glm::mat4 rootTransform);
|
||||
virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation,
|
||||
float scale, float priority) {}
|
||||
virtual void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority);
|
||||
};
|
||||
|
||||
#endif // hifi_EntityRig_h
|
||||
|
|
|
@ -34,6 +34,7 @@ void JointState::copyState(const JointState& other) {
|
|||
_isFree = other._isFree;
|
||||
_parentIndex = other._parentIndex;
|
||||
_defaultRotation = other._defaultRotation;
|
||||
_defaultTranslation = other._defaultTranslation;
|
||||
_inverseDefaultRotation = other._inverseDefaultRotation;
|
||||
_translation = other._translation;
|
||||
_preRotation = other._preRotation;
|
||||
|
@ -49,6 +50,7 @@ JointState::JointState(const FBXJoint& joint) {
|
|||
_parentIndex = joint.parentIndex;
|
||||
_translation = joint.translation;
|
||||
_defaultRotation = joint.rotation;
|
||||
_defaultTranslation = _translation;
|
||||
_inverseDefaultRotation = joint.inverseDefaultRotation;
|
||||
_preRotation = joint.preRotation;
|
||||
_postRotation = joint.postRotation;
|
||||
|
@ -70,6 +72,9 @@ glm::quat JointState::getRotation() const {
|
|||
}
|
||||
|
||||
void JointState::initTransform(const glm::mat4& parentTransform) {
|
||||
|
||||
_unitsScale = extractScale(parentTransform);
|
||||
|
||||
computeTransform(parentTransform);
|
||||
_positionInParentFrame = glm::inverse(extractRotation(parentTransform)) * (extractTranslation(_transform) - extractTranslation(parentTransform));
|
||||
_distanceToParent = glm::length(_positionInParentFrame);
|
||||
|
@ -79,11 +84,11 @@ void JointState::computeTransform(const glm::mat4& parentTransform, bool parentT
|
|||
if (!parentTransformChanged && !_transformChanged) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
glm::quat rotationInParentFrame = _preRotation * _rotationInConstrainedFrame * _postRotation;
|
||||
glm::mat4 transformInParentFrame = _preTransform * glm::mat4_cast(rotationInParentFrame) * _postTransform;
|
||||
glm::mat4 newTransform = parentTransform * glm::translate(_translation) * transformInParentFrame;
|
||||
|
||||
|
||||
if (newTransform != _transform) {
|
||||
_transform = newTransform;
|
||||
_transformChanged = true;
|
||||
|
@ -106,6 +111,13 @@ void JointState::restoreRotation(float fraction, float priority) {
|
|||
}
|
||||
}
|
||||
|
||||
void JointState::restoreTranslation(float fraction, float priority) {
|
||||
if (priority == _animationPriority || _animationPriority == 0.0f) {
|
||||
_translation = _translation * (1.0f - fraction) + _defaultTranslation * fraction;
|
||||
_animationPriority = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void JointState::setRotationInBindFrame(const glm::quat& rotation, float priority) {
|
||||
// rotation is from bind- to model-frame
|
||||
if (priority >= _animationPriority) {
|
||||
|
@ -176,6 +188,14 @@ void JointState::setRotationInConstrainedFrame(glm::quat targetRotation, float p
|
|||
}
|
||||
}
|
||||
|
||||
void JointState::setTranslation(const glm::vec3& translation, float priority) {
|
||||
if (priority >= _animationPriority || _animationPriority == 0.0f) {
|
||||
_translation = translation / _unitsScale;
|
||||
_transformChanged = true;
|
||||
_animationPriority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
void JointState::setRotationInConstrainedFrameInternal(const glm::quat& targetRotation) {
|
||||
if (_rotationInConstrainedFrame != targetRotation) {
|
||||
glm::quat parentRotation = computeParentRotation();
|
||||
|
@ -194,11 +214,15 @@ bool JointState::rotationIsDefault(const glm::quat& rotation, float tolerance) c
|
|||
glm::abs(rotation.w - defaultRotation.w) < tolerance;
|
||||
}
|
||||
|
||||
bool JointState::translationIsDefault(const glm::vec3& translation, float tolerance) const {
|
||||
return glm::distance(_defaultTranslation * _unitsScale, translation * _unitsScale) < tolerance;
|
||||
}
|
||||
|
||||
glm::quat JointState::getDefaultRotationInParentFrame() const {
|
||||
// NOTE: the result is constant and could be cached
|
||||
return _preRotation * _defaultRotation * _postRotation;
|
||||
}
|
||||
|
||||
const glm::vec3& JointState::getDefaultTranslationInConstrainedFrame() const {
|
||||
return _translation;
|
||||
glm::vec3 JointState::getDefaultTranslationInConstrainedFrame() const {
|
||||
return _defaultTranslation * _unitsScale;
|
||||
}
|
||||
|
|
|
@ -68,6 +68,8 @@ public:
|
|||
/// \param priority priority level of this animation blend
|
||||
void restoreRotation(float fraction, float priority);
|
||||
|
||||
void restoreTranslation(float fraction, float priority);
|
||||
|
||||
/// \param rotation is from bind- to model-frame
|
||||
/// computes and sets new _rotationInConstrainedFrame
|
||||
/// NOTE: the JointState's model-frame transform/rotation are NOT updated!
|
||||
|
@ -78,13 +80,17 @@ public:
|
|||
/// NOTE: the JointState's model-frame transform/rotation are NOT updated!
|
||||
void setRotationInModelFrame(const glm::quat& rotationInModelFrame, float priority);
|
||||
|
||||
void setTranslation(const glm::vec3& translation, float priority);
|
||||
|
||||
void setRotationInConstrainedFrame(glm::quat targetRotation, float priority, float mix = 1.0f);
|
||||
|
||||
const glm::quat& getRotationInConstrainedFrame() const { return _rotationInConstrainedFrame; }
|
||||
|
||||
bool rotationIsDefault(const glm::quat& rotation, float tolerance = EPSILON) const;
|
||||
bool translationIsDefault(const glm::vec3& translation, float tolerance = EPSILON) const;
|
||||
|
||||
glm::quat getDefaultRotationInParentFrame() const;
|
||||
const glm::vec3& getDefaultTranslationInConstrainedFrame() const;
|
||||
glm::vec3 getDefaultTranslationInConstrainedFrame() const;
|
||||
|
||||
|
||||
void clearTransformTranslation();
|
||||
|
@ -95,12 +101,13 @@ public:
|
|||
|
||||
void setTransform(const glm::mat4& transform) { _transform = transform; }
|
||||
|
||||
const glm::vec3& getTranslation() const { return _translation; }
|
||||
glm::vec3 getTranslation() const { return _translation * _unitsScale; }
|
||||
const glm::mat4& getPreTransform() const { return _preTransform; }
|
||||
const glm::mat4& getPostTransform() const { return _postTransform; }
|
||||
const glm::quat& getPreRotation() const { return _preRotation; }
|
||||
const glm::quat& getPostRotation() const { return _postRotation; }
|
||||
const glm::quat& getDefaultRotation() const { return _defaultRotation; }
|
||||
glm::vec3 getDefaultTranslation() const { return _defaultTranslation * _unitsScale; }
|
||||
const glm::quat& getInverseDefaultRotation() const { return _inverseDefaultRotation; }
|
||||
const QString& getName() const { return _name; }
|
||||
bool getIsFree() const { return _isFree; }
|
||||
|
@ -124,6 +131,7 @@ private:
|
|||
|
||||
glm::quat _defaultRotation; // Not necessarilly bind rotation. See FBXJoint transform/bindTransform
|
||||
glm::quat _inverseDefaultRotation;
|
||||
glm::vec3 _defaultTranslation;
|
||||
glm::vec3 _translation;
|
||||
QString _name;
|
||||
int _parentIndex;
|
||||
|
@ -133,6 +141,8 @@ private:
|
|||
glm::mat4 _preTransform;
|
||||
glm::mat4 _postTransform;
|
||||
glm::quat _inverseBindRotation;
|
||||
|
||||
glm::vec3 _unitsScale{1.0f, 1.0f, 1.0f};
|
||||
};
|
||||
|
||||
#endif // hifi_JointState_h
|
||||
|
|
|
@ -266,6 +266,7 @@ void Rig::reset(const QVector<FBXJoint>& fbxJoints) {
|
|||
}
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].setRotationInConstrainedFrame(fbxJoints.at(i).rotation, 0.0f);
|
||||
_jointStates[i].setTranslation(fbxJoints.at(i).translation, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -285,10 +286,20 @@ bool Rig::getJointStateRotation(int index, glm::quat& rotation) const {
|
|||
return !state.rotationIsDefault(rotation);
|
||||
}
|
||||
|
||||
bool Rig::getJointStateTranslation(int index, glm::vec3& translation) const {
|
||||
if (index == -1 || index >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
const JointState& state = _jointStates.at(index);
|
||||
translation = state.getTranslation();
|
||||
return !state.translationIsDefault(translation);
|
||||
}
|
||||
|
||||
void Rig::clearJointState(int index) {
|
||||
if (index != -1 && index < _jointStates.size()) {
|
||||
JointState& state = _jointStates[index];
|
||||
state.setRotationInConstrainedFrame(glm::quat(), 0.0f);
|
||||
state.setTranslation(state.getDefaultTranslationInConstrainedFrame(), 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -315,7 +326,7 @@ void Rig::setJointAnimatinoPriority(int index, float newPriority) {
|
|||
}
|
||||
}
|
||||
|
||||
void Rig::setJointState(int index, bool valid, const glm::quat& rotation, float priority) {
|
||||
void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, float priority) {
|
||||
if (index != -1 && index < _jointStates.size()) {
|
||||
JointState& state = _jointStates[index];
|
||||
if (valid) {
|
||||
|
@ -332,6 +343,12 @@ void Rig::restoreJointRotation(int index, float fraction, float priority) {
|
|||
}
|
||||
}
|
||||
|
||||
void Rig::restoreJointTranslation(int index, float fraction, float priority) {
|
||||
if (index != -1 && index < _jointStates.size()) {
|
||||
_jointStates[index].restoreTranslation(fraction, priority);
|
||||
}
|
||||
}
|
||||
|
||||
bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
|
||||
glm::vec3 translation, glm::quat rotation) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
|
@ -367,6 +384,14 @@ bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Rig::getJointTranslation(int jointIndex, glm::vec3& translation) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
translation = _jointStates[jointIndex].getTranslation();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
|
@ -554,6 +579,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
|
|||
const float PRIORITY = 1.0f;
|
||||
for (size_t i = 0; i < poses.size(); i++) {
|
||||
setJointRotationInConstrainedFrame((int)i, glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * poses[i].rot, PRIORITY, 1.0f);
|
||||
setJointTranslation((int)i, true, poses[i].trans, PRIORITY);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -827,6 +853,7 @@ bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority, c
|
|||
foreach (int index, freeLineage) {
|
||||
JointState& state = _jointStates[index];
|
||||
state.restoreRotation(fraction, priority);
|
||||
state.restoreTranslation(fraction, priority);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1175,3 +1202,12 @@ void Rig::initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry) {
|
|||
qCCritical(animation) << "Error loading" << url.toDisplayString() << "code = " << error << "str =" << str;
|
||||
});
|
||||
}
|
||||
|
||||
bool Rig::getModelOffset(glm::vec3& modelOffsetOut) const {
|
||||
if (_animSkeleton && _rootJointIndex >= 0) {
|
||||
modelOffsetOut = -_animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,12 +123,13 @@ public:
|
|||
int rightShoulderJointIndex);
|
||||
bool jointStatesEmpty() { return _jointStates.isEmpty(); };
|
||||
int getJointStateCount() const { return _jointStates.size(); }
|
||||
int indexOfJoint(const QString& jointName) ;
|
||||
int indexOfJoint(const QString& jointName);
|
||||
|
||||
void initJointTransforms(glm::mat4 rootTransform);
|
||||
void clearJointTransformTranslation(int jointIndex);
|
||||
void reset(const QVector<FBXJoint>& fbxJoints);
|
||||
bool getJointStateRotation(int index, glm::quat& rotation) const;
|
||||
bool getJointStateTranslation(int index, glm::vec3& translation) const;
|
||||
void applyJointRotationDelta(int jointIndex, const glm::quat& delta, float priority);
|
||||
JointState getJointState(int jointIndex) const; // XXX
|
||||
bool getVisibleJointState(int index, glm::quat& rotation) const;
|
||||
|
@ -137,14 +138,21 @@ public:
|
|||
void clearJointAnimationPriority(int index);
|
||||
float getJointAnimatinoPriority(int index);
|
||||
void setJointAnimatinoPriority(int index, float newPriority);
|
||||
void setJointState(int index, bool valid, const glm::quat& rotation, float priority);
|
||||
|
||||
virtual void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation,
|
||||
float priority) = 0;
|
||||
virtual void setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) {}
|
||||
void setJointRotation(int index, bool valid, const glm::quat& rotation, float priority);
|
||||
|
||||
void restoreJointRotation(int index, float fraction, float priority);
|
||||
void restoreJointTranslation(int index, float fraction, float priority);
|
||||
bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
|
||||
glm::vec3 translation, glm::quat rotation) const;
|
||||
|
||||
bool getJointPosition(int jointIndex, glm::vec3& position) const;
|
||||
bool getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const;
|
||||
bool getJointRotation(int jointIndex, glm::quat& rotation) const;
|
||||
bool getJointTranslation(int jointIndex, glm::vec3& translation) const;
|
||||
bool getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const;
|
||||
bool getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
|
||||
glm::vec3 translation, glm::quat rotation) const;
|
||||
|
@ -196,6 +204,8 @@ public:
|
|||
AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; }
|
||||
bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics)
|
||||
|
||||
bool getModelOffset(glm::vec3& modelOffsetOut) const;
|
||||
|
||||
protected:
|
||||
|
||||
void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
|
||||
|
|
|
@ -558,30 +558,8 @@ static const float Q32_TO_FLOAT = 1.0f / (1ULL << 32);
|
|||
// blocking size in frames, chosen so block processing fits in L1 cache
|
||||
static const int SRC_BLOCK = 1024;
|
||||
|
||||
//#define SRC_DITHER
|
||||
#define RAND16(r) (((r) = (r) * 69069u + 1u) >> 16)
|
||||
|
||||
// these are performance sensitive
|
||||
#define lo32(a) (((uint32_t* )&(a))[0])
|
||||
#define hi32(a) (((int32_t* )&(a))[1])
|
||||
|
||||
//#define lo32(a) ((uint32_t)(a))
|
||||
//#define hi32(a) ((int32_t)((a) >> 32))
|
||||
|
||||
//static inline uint32_t lo32(int64_t a) {
|
||||
// union {
|
||||
// int64_t val;
|
||||
// struct { uint32_t lo; int32_t hi; } reg;
|
||||
// } b = { a };
|
||||
// return b.reg.lo;
|
||||
//}
|
||||
//static inline int32_t hi32(int64_t a) {
|
||||
// union {
|
||||
// int64_t val;
|
||||
// struct { uint32_t lo; int32_t hi; } reg;
|
||||
// } b = { a };
|
||||
// return b.reg.hi;
|
||||
//}
|
||||
#define lo32(a) ((uint32_t)(a))
|
||||
#define hi32(a) ((int32_t)((a) >> 32))
|
||||
|
||||
//
|
||||
// Portable aligned malloc/free
|
||||
|
@ -671,6 +649,7 @@ int AudioSRC::createRationalFilter(int upFactor, int downFactor, float gain) {
|
|||
numTaps = (numCoefs + upFactor - 1) / upFactor;
|
||||
gain *= (float)oldCoefs / numCoefs;
|
||||
}
|
||||
numTaps = (numTaps + 3) & ~3; // SIMD4
|
||||
|
||||
// interpolate the coefficients of the prototype filter
|
||||
float* tempFilter = new float[numTaps * numPhases];
|
||||
|
@ -679,7 +658,7 @@ int AudioSRC::createRationalFilter(int upFactor, int downFactor, float gain) {
|
|||
cubicInterpolation(prototypeFilter, tempFilter, prototypeCoefs, numCoefs, gain);
|
||||
|
||||
// create the polyphase filter
|
||||
_polyphaseFilter = (float*)aligned_malloc(numTaps * numPhases * sizeof(float), 32);
|
||||
_polyphaseFilter = (float*)aligned_malloc(numTaps * numPhases * sizeof(float), 16); // SIMD4
|
||||
|
||||
// rearrange into polyphase form, ordered by use
|
||||
for (int i = 0; i < numPhases; i++) {
|
||||
|
@ -720,6 +699,7 @@ int AudioSRC::createIrrationalFilter(int upFactor, int downFactor, float gain) {
|
|||
numTaps = (numCoefs + upFactor - 1) / upFactor;
|
||||
gain *= (float)oldCoefs / numCoefs;
|
||||
}
|
||||
numTaps = (numTaps + 3) & ~3; // SIMD4
|
||||
|
||||
// interpolate the coefficients of the prototype filter
|
||||
float* tempFilter = new float[numTaps * numPhases];
|
||||
|
@ -728,7 +708,7 @@ int AudioSRC::createIrrationalFilter(int upFactor, int downFactor, float gain) {
|
|||
cubicInterpolation(prototypeFilter, tempFilter, prototypeCoefs, numCoefs, gain);
|
||||
|
||||
// create the polyphase filter, with extra phase at the end to simplify coef interpolation
|
||||
_polyphaseFilter = (float*)aligned_malloc(numTaps * (numPhases + 1) * sizeof(float), 32);
|
||||
_polyphaseFilter = (float*)aligned_malloc(numTaps * (numPhases + 1) * sizeof(float), 16); // SIMD4
|
||||
|
||||
// rearrange into polyphase form, ordered by fractional delay
|
||||
for (int phase = 0; phase < numPhases; phase++) {
|
||||
|
@ -754,6 +734,336 @@ int AudioSRC::createIrrationalFilter(int upFactor, int downFactor, float gain) {
|
|||
return numTaps;
|
||||
}
|
||||
|
||||
//
|
||||
// on x86 architecture, assume that SSE2 is present
|
||||
//
|
||||
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__)
|
||||
|
||||
#include <emmintrin.h>
|
||||
|
||||
int AudioSRC::multirateFilter1(const float* input0, float* output0, int inputFrames) {
|
||||
int outputFrames = 0;
|
||||
|
||||
assert((_numTaps & 0x3) == 0); // SIMD4
|
||||
|
||||
if (_step == 0) { // rational
|
||||
|
||||
int32_t i = hi32(_offset);
|
||||
|
||||
while (i < inputFrames) {
|
||||
|
||||
const float* c0 = &_polyphaseFilter[_numTaps * _phase];
|
||||
|
||||
__m128 acc0 = _mm_setzero_ps();
|
||||
|
||||
for (int j = 0; j < _numTaps; j += 4) {
|
||||
|
||||
//float coef = c0[j];
|
||||
__m128 coef0 = _mm_loadu_ps(&c0[j]);
|
||||
|
||||
//acc0 += input0[i + j] * coef;
|
||||
acc0 = _mm_add_ps(_mm_mul_ps(_mm_loadu_ps(&input0[i + j]), coef0), acc0);
|
||||
}
|
||||
|
||||
// horizontal sum
|
||||
acc0 = _mm_add_ps(acc0, _mm_movehl_ps(acc0, acc0));
|
||||
acc0 = _mm_add_ss(acc0, _mm_shuffle_ps(acc0, acc0, _MM_SHUFFLE(0,0,0,1)));
|
||||
|
||||
_mm_store_ss(&output0[outputFrames], acc0);
|
||||
outputFrames += 1;
|
||||
|
||||
i += _stepTable[_phase];
|
||||
if (++_phase == _upFactor) {
|
||||
_phase = 0;
|
||||
}
|
||||
}
|
||||
_offset = (int64_t)(i - inputFrames) << 32;
|
||||
|
||||
} else { // irrational
|
||||
|
||||
while (hi32(_offset) < inputFrames) {
|
||||
|
||||
int32_t i = hi32(_offset);
|
||||
uint32_t f = lo32(_offset);
|
||||
|
||||
uint32_t phase = f >> SRC_FRACBITS;
|
||||
__m128 frac = _mm_set1_ps((f & SRC_FRACMASK) * QFRAC_TO_FLOAT);
|
||||
|
||||
const float* c0 = &_polyphaseFilter[_numTaps * (phase + 0)];
|
||||
const float* c1 = &_polyphaseFilter[_numTaps * (phase + 1)];
|
||||
|
||||
__m128 acc0 = _mm_setzero_ps();
|
||||
|
||||
for (int j = 0; j < _numTaps; j += 4) {
|
||||
|
||||
//float coef = c0[j] + frac * (c1[j] - c0[j]);
|
||||
__m128 coef0 = _mm_loadu_ps(&c0[j]);
|
||||
__m128 coef1 = _mm_loadu_ps(&c1[j]);
|
||||
coef1 = _mm_sub_ps(coef1, coef0);
|
||||
coef0 = _mm_add_ps(_mm_mul_ps(coef1, frac), coef0);
|
||||
|
||||
//acc0 += input0[i + j] * coef;
|
||||
acc0 = _mm_add_ps(_mm_mul_ps(_mm_loadu_ps(&input0[i + j]), coef0), acc0);
|
||||
}
|
||||
|
||||
// horizontal sum
|
||||
acc0 = _mm_add_ps(acc0, _mm_movehl_ps(acc0, acc0));
|
||||
acc0 = _mm_add_ss(acc0, _mm_shuffle_ps(acc0, acc0, _MM_SHUFFLE(0,0,0,1)));
|
||||
|
||||
_mm_store_ss(&output0[outputFrames], acc0);
|
||||
outputFrames += 1;
|
||||
|
||||
_offset += _step;
|
||||
}
|
||||
_offset -= (int64_t)inputFrames << 32;
|
||||
}
|
||||
|
||||
return outputFrames;
|
||||
}
|
||||
|
||||
int AudioSRC::multirateFilter2(const float* input0, const float* input1, float* output0, float* output1, int inputFrames) {
|
||||
int outputFrames = 0;
|
||||
|
||||
assert((_numTaps & 0x3) == 0); // SIMD4
|
||||
|
||||
if (_step == 0) { // rational
|
||||
|
||||
int32_t i = hi32(_offset);
|
||||
|
||||
while (i < inputFrames) {
|
||||
|
||||
const float* c0 = &_polyphaseFilter[_numTaps * _phase];
|
||||
|
||||
__m128 acc0 = _mm_setzero_ps();
|
||||
__m128 acc1 = _mm_setzero_ps();
|
||||
|
||||
for (int j = 0; j < _numTaps; j += 4) {
|
||||
|
||||
//float coef = c0[j];
|
||||
__m128 coef0 = _mm_loadu_ps(&c0[j]);
|
||||
|
||||
//acc0 += input0[i + j] * coef;
|
||||
acc0 = _mm_add_ps(_mm_mul_ps(_mm_loadu_ps(&input0[i + j]), coef0), acc0);
|
||||
acc1 = _mm_add_ps(_mm_mul_ps(_mm_loadu_ps(&input1[i + j]), coef0), acc1);
|
||||
}
|
||||
|
||||
// horizontal sum
|
||||
acc0 = _mm_add_ps(acc0, _mm_movehl_ps(acc0, acc0));
|
||||
acc1 = _mm_add_ps(acc1, _mm_movehl_ps(acc1, acc1));
|
||||
acc0 = _mm_add_ss(acc0, _mm_shuffle_ps(acc0, acc0, _MM_SHUFFLE(0,0,0,1)));
|
||||
acc1 = _mm_add_ss(acc1, _mm_shuffle_ps(acc1, acc1, _MM_SHUFFLE(0,0,0,1)));
|
||||
|
||||
_mm_store_ss(&output0[outputFrames], acc0);
|
||||
_mm_store_ss(&output1[outputFrames], acc1);
|
||||
outputFrames += 1;
|
||||
|
||||
i += _stepTable[_phase];
|
||||
if (++_phase == _upFactor) {
|
||||
_phase = 0;
|
||||
}
|
||||
}
|
||||
_offset = (int64_t)(i - inputFrames) << 32;
|
||||
|
||||
} else { // irrational
|
||||
|
||||
while (hi32(_offset) < inputFrames) {
|
||||
|
||||
int32_t i = hi32(_offset);
|
||||
uint32_t f = lo32(_offset);
|
||||
|
||||
uint32_t phase = f >> SRC_FRACBITS;
|
||||
__m128 frac = _mm_set1_ps((f & SRC_FRACMASK) * QFRAC_TO_FLOAT);
|
||||
|
||||
const float* c0 = &_polyphaseFilter[_numTaps * (phase + 0)];
|
||||
const float* c1 = &_polyphaseFilter[_numTaps * (phase + 1)];
|
||||
|
||||
__m128 acc0 = _mm_setzero_ps();
|
||||
__m128 acc1 = _mm_setzero_ps();
|
||||
|
||||
for (int j = 0; j < _numTaps; j += 4) {
|
||||
|
||||
//float coef = c0[j] + frac * (c1[j] - c0[j]);
|
||||
__m128 coef0 = _mm_loadu_ps(&c0[j]);
|
||||
__m128 coef1 = _mm_loadu_ps(&c1[j]);
|
||||
coef1 = _mm_sub_ps(coef1, coef0);
|
||||
coef0 = _mm_add_ps(_mm_mul_ps(coef1, frac), coef0);
|
||||
|
||||
//acc0 += input0[i + j] * coef;
|
||||
acc0 = _mm_add_ps(_mm_mul_ps(_mm_loadu_ps(&input0[i + j]), coef0), acc0);
|
||||
acc1 = _mm_add_ps(_mm_mul_ps(_mm_loadu_ps(&input1[i + j]), coef0), acc1);
|
||||
}
|
||||
|
||||
// horizontal sum
|
||||
acc0 = _mm_add_ps(acc0, _mm_movehl_ps(acc0, acc0));
|
||||
acc1 = _mm_add_ps(acc1, _mm_movehl_ps(acc1, acc1));
|
||||
acc0 = _mm_add_ss(acc0, _mm_shuffle_ps(acc0, acc0, _MM_SHUFFLE(0,0,0,1)));
|
||||
acc1 = _mm_add_ss(acc1, _mm_shuffle_ps(acc1, acc1, _MM_SHUFFLE(0,0,0,1)));
|
||||
|
||||
_mm_store_ss(&output0[outputFrames], acc0);
|
||||
_mm_store_ss(&output1[outputFrames], acc1);
|
||||
outputFrames += 1;
|
||||
|
||||
_offset += _step;
|
||||
}
|
||||
_offset -= (int64_t)inputFrames << 32;
|
||||
}
|
||||
|
||||
return outputFrames;
|
||||
}
|
||||
|
||||
// convert int16_t to float, deinterleave stereo
|
||||
void AudioSRC::convertInputFromInt16(const int16_t* input, float** outputs, int numFrames) {
|
||||
__m128 scale = _mm_set1_ps(1/32768.0f);
|
||||
|
||||
if (_numChannels == 1) {
|
||||
|
||||
int i = 0;
|
||||
for (; i < numFrames - 3; i += 4) {
|
||||
__m128i a0 = _mm_loadl_epi64((__m128i*)&input[i]);
|
||||
|
||||
// sign-extend
|
||||
a0 = _mm_srai_epi32(_mm_unpacklo_epi16(a0, a0), 16);
|
||||
|
||||
__m128 f0 = _mm_mul_ps(_mm_cvtepi32_ps(a0), scale);
|
||||
|
||||
_mm_storeu_ps(&outputs[0][i], f0);
|
||||
}
|
||||
for (; i < numFrames; i++) {
|
||||
__m128i a0 = _mm_insert_epi16(_mm_setzero_si128(), input[i], 0);
|
||||
|
||||
// sign-extend
|
||||
a0 = _mm_srai_epi32(_mm_unpacklo_epi16(a0, a0), 16);
|
||||
|
||||
__m128 f0 = _mm_mul_ps(_mm_cvtepi32_ps(a0), scale);
|
||||
|
||||
_mm_store_ss(&outputs[0][i], f0);
|
||||
}
|
||||
|
||||
} else if (_numChannels == 2) {
|
||||
|
||||
int i = 0;
|
||||
for (; i < numFrames - 3; i += 4) {
|
||||
__m128i a0 = _mm_loadu_si128((__m128i*)&input[2*i]);
|
||||
__m128i a1 = a0;
|
||||
|
||||
// deinterleave and sign-extend
|
||||
a0 = _mm_madd_epi16(a0, _mm_set1_epi32(0x00000001));
|
||||
a1 = _mm_madd_epi16(a1, _mm_set1_epi32(0x00010000));
|
||||
|
||||
__m128 f0 = _mm_mul_ps(_mm_cvtepi32_ps(a0), scale);
|
||||
__m128 f1 = _mm_mul_ps(_mm_cvtepi32_ps(a1), scale);
|
||||
|
||||
_mm_storeu_ps(&outputs[0][i], f0);
|
||||
_mm_storeu_ps(&outputs[1][i], f1);
|
||||
}
|
||||
for (; i < numFrames; i++) {
|
||||
__m128i a0 = _mm_cvtsi32_si128(*(int32_t*)&input[2*i]);
|
||||
__m128i a1 = a0;
|
||||
|
||||
// deinterleave and sign-extend
|
||||
a0 = _mm_madd_epi16(a0, _mm_set1_epi32(0x00000001));
|
||||
a1 = _mm_madd_epi16(a1, _mm_set1_epi32(0x00010000));
|
||||
|
||||
__m128 f0 = _mm_mul_ps(_mm_cvtepi32_ps(a0), scale);
|
||||
__m128 f1 = _mm_mul_ps(_mm_cvtepi32_ps(a1), scale);
|
||||
|
||||
_mm_store_ss(&outputs[0][i], f0);
|
||||
_mm_store_ss(&outputs[1][i], f1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fast TPDF dither in [-1.0f, 1.0f]
|
||||
static inline __m128 dither4() {
|
||||
static __m128i rz;
|
||||
|
||||
// update the 8 different maximum-length LCGs
|
||||
rz = _mm_mullo_epi16(rz, _mm_set_epi16(25173, -25511, -5975, -23279, 19445, -27591, 30185, -3495));
|
||||
rz = _mm_add_epi16(rz, _mm_set_epi16(13849, -32767, 105, -19675, -7701, -32679, -13225, 28013));
|
||||
|
||||
// promote to 32-bit
|
||||
__m128i r0 = _mm_unpacklo_epi16(rz, _mm_setzero_si128());
|
||||
__m128i r1 = _mm_unpackhi_epi16(rz, _mm_setzero_si128());
|
||||
|
||||
// return (r0 - r1) * (1/65536.0f);
|
||||
__m128 d0 = _mm_cvtepi32_ps(_mm_sub_epi32(r0, r1));
|
||||
return _mm_mul_ps(d0, _mm_set1_ps(1/65536.0f));
|
||||
}
|
||||
|
||||
// convert float to int16_t, interleave stereo
|
||||
void AudioSRC::convertOutputToInt16(float** inputs, int16_t* output, int numFrames) {
|
||||
__m128 scale = _mm_set1_ps(32768.0f);
|
||||
|
||||
if (_numChannels == 1) {
|
||||
|
||||
int i = 0;
|
||||
for (; i < numFrames - 3; i += 4) {
|
||||
__m128 f0 = _mm_mul_ps(_mm_loadu_ps(&inputs[0][i]), scale);
|
||||
|
||||
f0 = _mm_add_ps(f0, dither4());
|
||||
|
||||
// round and saturate
|
||||
__m128i a0 = _mm_cvtps_epi32(f0);
|
||||
a0 = _mm_packs_epi32(a0, a0);
|
||||
|
||||
_mm_storel_epi64((__m128i*)&output[i], a0);
|
||||
}
|
||||
for (; i < numFrames; i++) {
|
||||
__m128 f0 = _mm_mul_ps(_mm_load_ss(&inputs[0][i]), scale);
|
||||
|
||||
f0 = _mm_add_ps(f0, dither4());
|
||||
|
||||
// round and saturate
|
||||
__m128i a0 = _mm_cvtps_epi32(f0);
|
||||
a0 = _mm_packs_epi32(a0, a0);
|
||||
|
||||
output[i] = (int16_t)_mm_extract_epi16(a0, 0);
|
||||
}
|
||||
|
||||
} else if (_numChannels == 2) {
|
||||
|
||||
int i = 0;
|
||||
for (; i < numFrames - 3; i += 4) {
|
||||
__m128 f0 = _mm_mul_ps(_mm_loadu_ps(&inputs[0][i]), scale);
|
||||
__m128 f1 = _mm_mul_ps(_mm_loadu_ps(&inputs[1][i]), scale);
|
||||
|
||||
__m128 d0 = dither4();
|
||||
f0 = _mm_add_ps(f0, d0);
|
||||
f1 = _mm_add_ps(f1, d0);
|
||||
|
||||
// round and saturate
|
||||
__m128i a0 = _mm_cvtps_epi32(f0);
|
||||
__m128i a1 = _mm_cvtps_epi32(f1);
|
||||
a0 = _mm_packs_epi32(a0, a0);
|
||||
a1 = _mm_packs_epi32(a1, a1);
|
||||
|
||||
// interleave
|
||||
a0 = _mm_unpacklo_epi16(a0, a1);
|
||||
_mm_storeu_si128((__m128i*)&output[2*i], a0);
|
||||
}
|
||||
for (; i < numFrames; i++) {
|
||||
__m128 f0 = _mm_mul_ps(_mm_load_ss(&inputs[0][i]), scale);
|
||||
__m128 f1 = _mm_mul_ps(_mm_load_ss(&inputs[1][i]), scale);
|
||||
|
||||
__m128 d0 = dither4();
|
||||
f0 = _mm_add_ps(f0, d0);
|
||||
f1 = _mm_add_ps(f1, d0);
|
||||
|
||||
// round and saturate
|
||||
__m128i a0 = _mm_cvtps_epi32(f0);
|
||||
__m128i a1 = _mm_cvtps_epi32(f1);
|
||||
a0 = _mm_packs_epi32(a0, a0);
|
||||
a1 = _mm_packs_epi32(a1, a1);
|
||||
|
||||
// interleave
|
||||
a0 = _mm_unpacklo_epi16(a0, a1);
|
||||
*(int32_t*)&output[2*i] = _mm_cvtsi128_si32(a0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int AudioSRC::multirateFilter1(const float* input0, float* output0, int inputFrames) {
|
||||
int outputFrames = 0;
|
||||
|
||||
|
@ -886,45 +1196,73 @@ int AudioSRC::multirateFilter2(const float* input0, const float* input1, float*
|
|||
return outputFrames;
|
||||
}
|
||||
|
||||
// convert int16_t to float
|
||||
// deinterleave stereo samples
|
||||
// convert int16_t to float, deinterleave stereo
|
||||
void AudioSRC::convertInputFromInt16(const int16_t* input, float** outputs, int numFrames) {
|
||||
for (int i = 0; i < numFrames; i++) {
|
||||
for (int j = 0; j < _numChannels; j++) {
|
||||
const float scale = 1/32768.0f;
|
||||
|
||||
float f = (float)input[_numChannels*i + j];
|
||||
outputs[j][i] = f * (1.0f/32768.0f);
|
||||
if (_numChannels == 1) {
|
||||
for (int i = 0; i < numFrames; i++) {
|
||||
outputs[0][i] = (float)input[i] * scale;
|
||||
}
|
||||
} else if (_numChannels == 2) {
|
||||
for (int i = 0; i < numFrames; i++) {
|
||||
outputs[0][i] = (float)input[2*i + 0] * scale;
|
||||
outputs[1][i] = (float)input[2*i + 1] * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convert float to int16_t
|
||||
// interleave stereo samples
|
||||
// fast TPDF dither in [-1.0f, 1.0f]
|
||||
static inline float dither() {
|
||||
static uint32_t rz = 0;
|
||||
rz = rz * 69069 + 1;
|
||||
int32_t r0 = rz & 0xffff;
|
||||
int32_t r1 = rz >> 16;
|
||||
return (r0 - r1) * (1/65536.0f);
|
||||
}
|
||||
|
||||
// convert float to int16_t, interleave stereo
|
||||
void AudioSRC::convertOutputToInt16(float** inputs, int16_t* output, int numFrames) {
|
||||
for (int i = 0; i < numFrames; i++) {
|
||||
for (int j = 0; j < _numChannels; j++) {
|
||||
const float scale = 32768.0f;
|
||||
|
||||
float f = inputs[j][i] * 32768.0f;
|
||||
if (_numChannels == 1) {
|
||||
for (int i = 0; i < numFrames; i++) {
|
||||
|
||||
#ifdef SRC_DITHER
|
||||
// TPDF dither in [-1.0f, 1.0f]
|
||||
static uint32_t rz = 1;
|
||||
int r0 = RAND16(rz);
|
||||
int r1 = RAND16(rz);
|
||||
f += (r0 - r1) * (1.0f/65536.0f);
|
||||
float f = inputs[0][i] * scale;
|
||||
|
||||
// round
|
||||
f += dither();
|
||||
|
||||
// round and saturate
|
||||
f += (f < 0.0f ? -0.5f : +0.5f);
|
||||
#endif
|
||||
// saturate
|
||||
f = std::min(f, 32767.0f);
|
||||
f = std::max(f, -32768.0f);
|
||||
f = std::max(std::min(f, 32767.0f), -32768.0f);
|
||||
|
||||
output[_numChannels * i + j] = (int16_t)f;
|
||||
output[i] = (int16_t)f;
|
||||
}
|
||||
} else if (_numChannels == 2) {
|
||||
for (int i = 0; i < numFrames; i++) {
|
||||
|
||||
float f0 = inputs[0][i] * scale;
|
||||
float f1 = inputs[1][i] * scale;
|
||||
|
||||
float d = dither();
|
||||
f0 += d;
|
||||
f1 += d;
|
||||
|
||||
// round and saturate
|
||||
f0 += (f0 < 0.0f ? -0.5f : +0.5f);
|
||||
f1 += (f1 < 0.0f ? -0.5f : +0.5f);
|
||||
f0 = std::max(std::min(f0, 32767.0f), -32768.0f);
|
||||
f1 = std::max(std::min(f1, 32767.0f), -32768.0f);
|
||||
|
||||
// interleave
|
||||
output[2*i + 0] = (int16_t)f0;
|
||||
output[2*i + 1] = (int16_t)f1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int AudioSRC::processFloat(float** inputs, float** outputs, int inputFrames) {
|
||||
int outputFrames = 0;
|
||||
|
||||
|
@ -1019,10 +1357,10 @@ AudioSRC::AudioSRC(int inputSampleRate, int outputSampleRate, int numChannels) {
|
|||
_history[1] = new float[2 * _numHistory];
|
||||
|
||||
// format conversion buffers
|
||||
_inputs[0] = new float[SRC_BLOCK];
|
||||
_inputs[1] = new float[SRC_BLOCK];
|
||||
_outputs[0] = new float[SRC_BLOCK];
|
||||
_outputs[1] = new float[SRC_BLOCK];
|
||||
_inputs[0] = (float*)aligned_malloc(SRC_BLOCK * sizeof(float), 16); // SIMD4
|
||||
_inputs[1] = (float*)aligned_malloc(SRC_BLOCK * sizeof(float), 16);
|
||||
_outputs[0] = (float*)aligned_malloc(SRC_BLOCK * sizeof(float), 16);
|
||||
_outputs[1] = (float*)aligned_malloc(SRC_BLOCK * sizeof(float), 16);
|
||||
|
||||
// input blocking size, such that input and output are both guaranteed not to exceed SRC_BLOCK frames
|
||||
_inputBlock = std::min(SRC_BLOCK, getMaxInput(SRC_BLOCK));
|
||||
|
@ -1041,10 +1379,10 @@ AudioSRC::~AudioSRC() {
|
|||
delete[] _history[0];
|
||||
delete[] _history[1];
|
||||
|
||||
delete[] _inputs[0];
|
||||
delete[] _inputs[1];
|
||||
delete[] _outputs[0];
|
||||
delete[] _outputs[1];
|
||||
aligned_free(_inputs[0]);
|
||||
aligned_free(_inputs[1]);
|
||||
aligned_free(_outputs[0]);
|
||||
aligned_free(_outputs[1]);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -49,6 +49,7 @@ AvatarData::AvatarData() :
|
|||
_keyState(NO_KEY_DOWN),
|
||||
_forceFaceTrackerConnected(false),
|
||||
_hasNewJointRotations(true),
|
||||
_hasNewJointTranslations(true),
|
||||
_headData(NULL),
|
||||
_handData(NULL),
|
||||
_faceModelURL("http://invalid.com"),
|
||||
|
@ -278,12 +279,16 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
|
|||
// pupil dilation
|
||||
destinationBuffer += packFloatToByte(destinationBuffer, _headData->_pupilDilation, 1.0f);
|
||||
|
||||
// joint data
|
||||
// joint rotation data
|
||||
*destinationBuffer++ = _jointData.size();
|
||||
unsigned char* validityPosition = destinationBuffer;
|
||||
unsigned char validity = 0;
|
||||
int validityBit = 0;
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
int rotationSentCount = 0;
|
||||
#endif
|
||||
|
||||
_lastSentJointData.resize(_jointData.size());
|
||||
|
||||
for (int i=0; i < _jointData.size(); i++) {
|
||||
|
@ -292,7 +297,12 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
|
|||
if (sendAll ||
|
||||
!cullSmallChanges ||
|
||||
fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) {
|
||||
validity |= (1 << validityBit);
|
||||
if (data.rotationSet) {
|
||||
validity |= (1 << validityBit);
|
||||
#ifdef WANT_DEBUG
|
||||
rotationSentCount++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
if (++validityBit == BITS_IN_BYTE) {
|
||||
|
@ -317,6 +327,73 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// joint translation data
|
||||
validityPosition = destinationBuffer;
|
||||
validity = 0;
|
||||
validityBit = 0;
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
int translationSentCount = 0;
|
||||
#endif
|
||||
|
||||
float maxTranslationDimension = 0.0;
|
||||
for (int i=0; i < _jointData.size(); i++) {
|
||||
const JointData& data = _jointData.at(i);
|
||||
if (sendAll || _lastSentJointData[i].translation != data.translation) {
|
||||
if (sendAll ||
|
||||
!cullSmallChanges ||
|
||||
glm::distance(data.translation, _lastSentJointData[i].translation) > AVATAR_MIN_TRANSLATION) {
|
||||
if (data.translationSet) {
|
||||
validity |= (1 << validityBit);
|
||||
#ifdef WANT_DEBUG
|
||||
translationSentCount++;
|
||||
#endif
|
||||
maxTranslationDimension = glm::max(fabsf(data.translation.x), maxTranslationDimension);
|
||||
maxTranslationDimension = glm::max(fabsf(data.translation.y), maxTranslationDimension);
|
||||
maxTranslationDimension = glm::max(fabsf(data.translation.z), maxTranslationDimension);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (++validityBit == BITS_IN_BYTE) {
|
||||
*destinationBuffer++ = validity;
|
||||
validityBit = validity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (validityBit != 0) {
|
||||
*destinationBuffer++ = validity;
|
||||
}
|
||||
|
||||
// TODO -- automatically pick translationCompressionRadix
|
||||
int translationCompressionRadix = 12;
|
||||
|
||||
*destinationBuffer++ = translationCompressionRadix;
|
||||
|
||||
validityBit = 0;
|
||||
validity = *validityPosition++;
|
||||
for (int i = 0; i < _jointData.size(); i ++) {
|
||||
const JointData& data = _jointData[ i ];
|
||||
if (validity & (1 << validityBit)) {
|
||||
destinationBuffer +=
|
||||
packFloatVec3ToSignedTwoByteFixed(destinationBuffer, data.translation, translationCompressionRadix);
|
||||
}
|
||||
if (++validityBit == BITS_IN_BYTE) {
|
||||
validityBit = 0;
|
||||
validity = *validityPosition++;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
if (sendAll) {
|
||||
qDebug() << "SENDING -- rotations:" << rotationSentCount << "translations:" << translationSentCount
|
||||
<< "largest:" << maxTranslationDimension
|
||||
<< "radix:" << translationCompressionRadix
|
||||
<< "size:" << (int)(destinationBuffer - startPosition);
|
||||
}
|
||||
#endif
|
||||
|
||||
return avatarDataByteArray.left(destinationBuffer - startPosition);
|
||||
}
|
||||
|
||||
|
@ -328,7 +405,16 @@ void AvatarData::doneEncoding(bool cullSmallChanges) {
|
|||
if (_lastSentJointData[i].rotation != data.rotation) {
|
||||
if (!cullSmallChanges ||
|
||||
fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) {
|
||||
_lastSentJointData[i].rotation = data.rotation;
|
||||
if (data.rotationSet) {
|
||||
_lastSentJointData[i].rotation = data.rotation;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cullSmallChanges ||
|
||||
glm::distance(data.translation, _lastSentJointData[i].translation) > AVATAR_MIN_TRANSLATION) {
|
||||
if (data.translationSet) {
|
||||
_lastSentJointData[i].translation = data.translation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -374,7 +460,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
// + 1 byte for numJoints (0)
|
||||
// = 39 bytes
|
||||
int minPossibleSize = 39;
|
||||
|
||||
|
||||
int maxAvailableSize = buffer.size();
|
||||
if (minPossibleSize > maxAvailableSize) {
|
||||
if (shouldLogError(now)) {
|
||||
|
@ -556,7 +642,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
sourceBuffer += unpackFloatFromByte(sourceBuffer, _headData->_pupilDilation, 1.0f);
|
||||
} // 1 byte
|
||||
|
||||
// joint data
|
||||
// joint rotations
|
||||
int numJoints = *sourceBuffer++;
|
||||
int bytesOfValidity = (int)ceil((float)numJoints / (float)BITS_IN_BYTE);
|
||||
minPossibleSize += bytesOfValidity;
|
||||
|
@ -569,13 +655,13 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
}
|
||||
return maxAvailableSize;
|
||||
}
|
||||
int numValidJoints = 0;
|
||||
int numValidJointRotations = 0;
|
||||
_jointData.resize(numJoints);
|
||||
|
||||
QVector<bool> valids;
|
||||
valids.resize(numJoints);
|
||||
QVector<bool> validRotations;
|
||||
validRotations.resize(numJoints);
|
||||
|
||||
{ // validity bits
|
||||
{ // rotation validity bits
|
||||
unsigned char validity = 0;
|
||||
int validityBit = 0;
|
||||
for (int i = 0; i < numJoints; i++) {
|
||||
|
@ -584,20 +670,19 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
}
|
||||
bool valid = (bool)(validity & (1 << validityBit));
|
||||
if (valid) {
|
||||
++numValidJoints;
|
||||
++numValidJointRotations;
|
||||
}
|
||||
valids[i] = valid;
|
||||
validRotations[i] = valid;
|
||||
validityBit = (validityBit + 1) % BITS_IN_BYTE;
|
||||
}
|
||||
}
|
||||
// 1 + bytesOfValidity bytes
|
||||
} // 1 + bytesOfValidity bytes
|
||||
|
||||
// each joint rotation component is stored in two bytes (sizeof(uint16_t))
|
||||
int COMPONENTS_PER_QUATERNION = 4;
|
||||
minPossibleSize += numValidJoints * COMPONENTS_PER_QUATERNION * sizeof(uint16_t);
|
||||
minPossibleSize += numValidJointRotations * COMPONENTS_PER_QUATERNION * sizeof(uint16_t);
|
||||
if (minPossibleSize > maxAvailableSize) {
|
||||
if (shouldLogError(now)) {
|
||||
qCDebug(avatars) << "Malformed AvatarData packet after JointData;"
|
||||
qCDebug(avatars) << "Malformed AvatarData packet after JointData rotation validity;"
|
||||
<< " displayName = '" << _displayName << "'"
|
||||
<< " minPossibleSize = " << minPossibleSize
|
||||
<< " maxAvailableSize = " << maxAvailableSize;
|
||||
|
@ -608,13 +693,71 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
{ // joint data
|
||||
for (int i = 0; i < numJoints; i++) {
|
||||
JointData& data = _jointData[i];
|
||||
if (valids[i]) {
|
||||
if (validRotations[i]) {
|
||||
_hasNewJointRotations = true;
|
||||
data.rotationSet = true;
|
||||
sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, data.rotation);
|
||||
}
|
||||
}
|
||||
} // numJoints * 8 bytes
|
||||
|
||||
// joint translations
|
||||
// get translation validity bits -- these indicate which translations were packed
|
||||
int numValidJointTranslations = 0;
|
||||
QVector<bool> validTranslations;
|
||||
validTranslations.resize(numJoints);
|
||||
|
||||
{ // translation validity bits
|
||||
unsigned char validity = 0;
|
||||
int validityBit = 0;
|
||||
for (int i = 0; i < numJoints; i++) {
|
||||
if (validityBit == 0) {
|
||||
validity = *sourceBuffer++;
|
||||
}
|
||||
bool valid = (bool)(validity & (1 << validityBit));
|
||||
if (valid) {
|
||||
++numValidJointTranslations;
|
||||
}
|
||||
validTranslations[i] = valid;
|
||||
validityBit = (validityBit + 1) % BITS_IN_BYTE;
|
||||
}
|
||||
} // 1 + bytesOfValidity bytes
|
||||
|
||||
// each joint translation component is stored in 6 bytes. 1 byte for translationCompressionRadix
|
||||
minPossibleSize += numValidJointTranslations * 6 + 1;
|
||||
if (minPossibleSize > maxAvailableSize) {
|
||||
if (shouldLogError(now)) {
|
||||
qCDebug(avatars) << "Malformed AvatarData packet after JointData translation validity;"
|
||||
<< " displayName = '" << _displayName << "'"
|
||||
<< " minPossibleSize = " << minPossibleSize
|
||||
<< " maxAvailableSize = " << maxAvailableSize;
|
||||
}
|
||||
return maxAvailableSize;
|
||||
}
|
||||
|
||||
int translationCompressionRadix = *sourceBuffer++;
|
||||
|
||||
{ // joint data
|
||||
for (int i = 0; i < numJoints; i++) {
|
||||
JointData& data = _jointData[i];
|
||||
if (validTranslations[i]) {
|
||||
sourceBuffer +=
|
||||
unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, data.translation, translationCompressionRadix);
|
||||
_hasNewJointTranslations = true;
|
||||
data.translationSet = true;
|
||||
}
|
||||
}
|
||||
} // numJoints * 12 bytes
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
if (numValidJointRotations > 15) {
|
||||
qDebug() << "RECEIVING -- rotations:" << numValidJointRotations
|
||||
<< "translations:" << numValidJointTranslations
|
||||
<< "radix:" << translationCompressionRadix
|
||||
<< "size:" << (int)(sourceBuffer - startPosition);
|
||||
}
|
||||
#endif
|
||||
|
||||
int numBytesRead = sourceBuffer - startPosition;
|
||||
_averageBytesReceived.updateAverage(numBytesRead);
|
||||
return numBytesRead;
|
||||
|
@ -800,7 +943,7 @@ void AvatarData::changeReferential(Referential* ref) {
|
|||
_referential = ref;
|
||||
}
|
||||
|
||||
void AvatarData::setJointData(int index, const glm::quat& rotation) {
|
||||
void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) {
|
||||
if (index == -1) {
|
||||
return;
|
||||
}
|
||||
|
@ -813,6 +956,7 @@ void AvatarData::setJointData(int index, const glm::quat& rotation) {
|
|||
}
|
||||
JointData& data = _jointData[index];
|
||||
data.rotation = rotation;
|
||||
data.translation = translation;
|
||||
}
|
||||
|
||||
void AvatarData::clearJointData(int index) {
|
||||
|
@ -854,13 +998,67 @@ glm::quat AvatarData::getJointRotation(int index) const {
|
|||
return index < _jointData.size() ? _jointData.at(index).rotation : glm::quat();
|
||||
}
|
||||
|
||||
void AvatarData::setJointData(const QString& name, const glm::quat& rotation) {
|
||||
|
||||
glm::vec3 AvatarData::getJointTranslation(int index) const {
|
||||
if (index == -1) {
|
||||
return glm::vec3();
|
||||
}
|
||||
if (QThread::currentThread() != thread()) {
|
||||
glm::vec3 result;
|
||||
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "getJointTranslation", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(glm::vec3, result), Q_ARG(int, index));
|
||||
return result;
|
||||
}
|
||||
return index < _jointData.size() ? _jointData.at(index).translation : glm::vec3();
|
||||
}
|
||||
|
||||
glm::vec3 AvatarData::getJointTranslation(const QString& name) const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
glm::vec3 result;
|
||||
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "getJointTranslation", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(glm::vec3, result), Q_ARG(const QString&, name));
|
||||
return result;
|
||||
}
|
||||
return getJointTranslation(getJointIndex(name));
|
||||
}
|
||||
|
||||
void AvatarData::setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setJointData", Q_ARG(const QString&, name),
|
||||
Q_ARG(const glm::quat&, rotation));
|
||||
return;
|
||||
}
|
||||
setJointData(getJointIndex(name), rotation);
|
||||
setJointData(getJointIndex(name), rotation, translation);
|
||||
}
|
||||
|
||||
void AvatarData::setJointRotation(int index, const glm::quat& rotation) {
|
||||
if (index == -1) {
|
||||
return;
|
||||
}
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setJointRotation", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation));
|
||||
return;
|
||||
}
|
||||
if (_jointData.size() <= index) {
|
||||
_jointData.resize(index + 1);
|
||||
}
|
||||
JointData& data = _jointData[index];
|
||||
data.rotation = rotation;
|
||||
}
|
||||
|
||||
void AvatarData::setJointTranslation(int index, const glm::vec3& translation) {
|
||||
if (index == -1) {
|
||||
return;
|
||||
}
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setJointTranslation", Q_ARG(int, index), Q_ARG(const glm::vec3&, translation));
|
||||
return;
|
||||
}
|
||||
if (_jointData.size() <= index) {
|
||||
_jointData.resize(index + 1);
|
||||
}
|
||||
JointData& data = _jointData[index];
|
||||
data.translation = translation;
|
||||
}
|
||||
|
||||
void AvatarData::clearJointData(const QString& name) {
|
||||
|
@ -918,7 +1116,25 @@ void AvatarData::setJointRotations(QVector<glm::quat> jointRotations) {
|
|||
}
|
||||
for (int i = 0; i < jointRotations.size(); ++i) {
|
||||
if (i < _jointData.size()) {
|
||||
setJointData(i, jointRotations[i]);
|
||||
setJointRotation(i, jointRotations[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarData::setJointTranslations(QVector<glm::vec3> jointTranslations) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QVector<glm::quat> result;
|
||||
QMetaObject::invokeMethod(const_cast<AvatarData*>(this),
|
||||
"setJointTranslations", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(QVector<glm::vec3>, jointTranslations));
|
||||
}
|
||||
|
||||
if (_jointData.size() < jointTranslations.size()) {
|
||||
_jointData.resize(jointTranslations.size());
|
||||
}
|
||||
for (int i = 0; i < jointTranslations.size(); ++i) {
|
||||
if (i < _jointData.size()) {
|
||||
setJointTranslation(i, jointTranslations[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,6 +117,7 @@ const QString DEFAULT_FULL_AVATAR_MODEL_NAME = QString("Default");
|
|||
const float AVATAR_SEND_FULL_UPDATE_RATIO = 0.02f;
|
||||
// this controls how large a change in joint-rotation must be before the interface sends it to the avatar mixer
|
||||
const float AVATAR_MIN_ROTATION_DOT = 0.9999999f;
|
||||
const float AVATAR_MIN_TRANSLATION = 0.0001f;
|
||||
|
||||
|
||||
// Where one's own Avatar begins in the world (will be overwritten if avatar data file is found).
|
||||
|
@ -240,20 +241,24 @@ public:
|
|||
Q_INVOKABLE char getHandState() const { return _handState; }
|
||||
|
||||
const QVector<JointData>& getJointData() const { return _jointData; }
|
||||
void setJointData(const QVector<JointData>& jointData) { _jointData = jointData; }
|
||||
|
||||
Q_INVOKABLE virtual void setJointData(int index, const glm::quat& rotation);
|
||||
Q_INVOKABLE virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation);
|
||||
Q_INVOKABLE virtual void setJointRotation(int index, const glm::quat& rotation);
|
||||
Q_INVOKABLE virtual void setJointTranslation(int index, const glm::vec3& translation);
|
||||
Q_INVOKABLE virtual void clearJointData(int index);
|
||||
Q_INVOKABLE bool isJointDataValid(int index) const;
|
||||
Q_INVOKABLE virtual glm::quat getJointRotation(int index) const;
|
||||
Q_INVOKABLE virtual glm::vec3 getJointTranslation(int index) const;
|
||||
|
||||
Q_INVOKABLE void setJointData(const QString& name, const glm::quat& rotation);
|
||||
Q_INVOKABLE void setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation);
|
||||
Q_INVOKABLE void clearJointData(const QString& name);
|
||||
Q_INVOKABLE bool isJointDataValid(const QString& name) const;
|
||||
Q_INVOKABLE glm::quat getJointRotation(const QString& name) const;
|
||||
Q_INVOKABLE glm::vec3 getJointTranslation(const QString& name) const;
|
||||
|
||||
Q_INVOKABLE virtual QVector<glm::quat> getJointRotations() const;
|
||||
Q_INVOKABLE virtual void setJointRotations(QVector<glm::quat> jointRotations);
|
||||
Q_INVOKABLE virtual void setJointTranslations(QVector<glm::vec3> jointTranslations);
|
||||
|
||||
Q_INVOKABLE virtual void clearJointsData();
|
||||
|
||||
|
@ -387,6 +392,7 @@ protected:
|
|||
|
||||
bool _forceFaceTrackerConnected;
|
||||
bool _hasNewJointRotations; // set in AvatarData, cleared in Avatar
|
||||
bool _hasNewJointTranslations; // set in AvatarData, cleared in Avatar
|
||||
|
||||
HeadData* _headData;
|
||||
HandData* _handData;
|
||||
|
@ -435,6 +441,9 @@ Q_DECLARE_METATYPE(AvatarData*)
|
|||
class JointData {
|
||||
public:
|
||||
glm::quat rotation;
|
||||
bool rotationSet = false;
|
||||
glm::vec3 translation;
|
||||
bool translationSet = false;
|
||||
};
|
||||
|
||||
class AttachmentData {
|
||||
|
|
|
@ -254,8 +254,17 @@ void Player::play() {
|
|||
nextFrame.getJointRotations()[i],
|
||||
_frameInterpolationFactor);
|
||||
}
|
||||
|
||||
QVector<glm::vec3> jointTranslations(currentFrame.getJointTranslations().size());
|
||||
for (int i = 0; i < currentFrame.getJointTranslations().size(); ++i) {
|
||||
jointTranslations[i] =
|
||||
currentFrame.getJointTranslations()[i] * (1.0f - _frameInterpolationFactor) +
|
||||
nextFrame.getJointTranslations()[i] * _frameInterpolationFactor;
|
||||
}
|
||||
|
||||
_avatar->setJointRotations(jointRotations);
|
||||
|
||||
_avatar->setJointTranslations(jointTranslations);
|
||||
|
||||
HeadData* head = const_cast<HeadData*>(_avatar->getHeadData());
|
||||
if (head) {
|
||||
// Make sure fake face tracker connection doesn't get turned off
|
||||
|
|
|
@ -239,6 +239,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) {
|
|||
if (i == 0 ||
|
||||
frame._jointRotations[j] != previousFrame._jointRotations[j]) {
|
||||
writeQuat(stream, frame._jointRotations[j]);
|
||||
// TODO -- handle translations
|
||||
mask.setBit(maskIndex);
|
||||
}
|
||||
maskIndex++;
|
||||
|
@ -561,7 +562,9 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString
|
|||
frame._jointRotations[j] = previousFrame._jointRotations[j];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO -- handle translations
|
||||
|
||||
if (!mask[maskIndex++] || !readVec3(stream, frame._translation)) {
|
||||
frame._translation = previousFrame._translation;
|
||||
}
|
||||
|
@ -670,7 +673,9 @@ RecordingPointer readRecordingFromRecFile(RecordingPointer recording, const QStr
|
|||
for (int i = 0; i < jointRotationSize; ++i) {
|
||||
fileStream >> baseFrame._jointRotations[i].x >> baseFrame._jointRotations[i].y >> baseFrame._jointRotations[i].z >> baseFrame._jointRotations[i].w;
|
||||
}
|
||||
|
||||
|
||||
// TODO -- handle translations
|
||||
|
||||
fileStream >> baseFrame._translation.x >> baseFrame._translation.y >> baseFrame._translation.z;
|
||||
fileStream >> baseFrame._rotation.x >> baseFrame._rotation.y >> baseFrame._rotation.z >> baseFrame._rotation.w;
|
||||
fileStream >> baseFrame._scale;
|
||||
|
@ -736,7 +741,9 @@ RecordingPointer readRecordingFromRecFile(RecordingPointer recording, const QStr
|
|||
frame._jointRotations[i] = previousFrame._jointRotations[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO -- handle translations
|
||||
|
||||
if (mask[maskIndex++]) {
|
||||
stream >> frame._translation.x >> frame._translation.y >> frame._translation.z;
|
||||
frame._translation = context.orientationInv * frame._translation;
|
||||
|
|
|
@ -83,6 +83,7 @@ class RecordingFrame {
|
|||
public:
|
||||
QVector<float> getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
|
||||
QVector<glm::quat> getJointRotations() const { return _jointRotations; }
|
||||
QVector<glm::vec3> getJointTranslations() const { return _jointTranslations; }
|
||||
glm::vec3 getTranslation() const { return _translation; }
|
||||
glm::quat getRotation() const { return _rotation; }
|
||||
float getScale() const { return _scale; }
|
||||
|
@ -94,6 +95,7 @@ public:
|
|||
protected:
|
||||
void setBlendshapeCoefficients(QVector<float> blendshapeCoefficients);
|
||||
void setJointRotations(QVector<glm::quat> jointRotations) { _jointRotations = jointRotations; }
|
||||
void setJointTranslations(QVector<glm::vec3> jointTranslations) { _jointTranslations = jointTranslations; }
|
||||
void setTranslation(const glm::vec3& translation) { _translation = translation; }
|
||||
void setRotation(const glm::quat& rotation) { _rotation = rotation; }
|
||||
void setScale(float scale) { _scale = scale; }
|
||||
|
@ -105,6 +107,7 @@ protected:
|
|||
private:
|
||||
QVector<float> _blendshapeCoefficients;
|
||||
QVector<glm::quat> _jointRotations;
|
||||
QVector<glm::vec3> _jointTranslations;
|
||||
glm::vec3 _translation;
|
||||
glm::quat _rotation;
|
||||
float _scale;
|
||||
|
@ -124,4 +127,4 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename);
|
|||
RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString& filename);
|
||||
RecordingPointer readRecordingFromRecFile(RecordingPointer recording, const QString& filename, const QByteArray& byteArray);
|
||||
|
||||
#endif // hifi_Recording_h
|
||||
#endif // hifi_Recording_h
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
set(TARGET_NAME display-plugins)
|
||||
|
||||
add_definitions(-DGLEW_STATIC)
|
||||
|
||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||
setup_hifi_library(OpenGL)
|
||||
|
||||
|
|
|
@ -276,10 +276,13 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
|
||||
if (jointsMapped()) {
|
||||
bool newFrame;
|
||||
auto frameData = getAnimationFrame(newFrame);
|
||||
QVector<glm::quat> frameDataRotations;
|
||||
QVector<glm::vec3> frameDataTranslations;
|
||||
getAnimationFrame(newFrame, frameDataRotations, frameDataTranslations);
|
||||
assert(frameDataRotations.size() == frameDataTranslations.size());
|
||||
if (newFrame) {
|
||||
for (int i = 0; i < frameData.size(); i++) {
|
||||
_model->setJointState(i, true, frameData[i]);
|
||||
for (int i = 0; i < frameDataRotations.size(); i++) {
|
||||
_model->setJointState(i, true, frameDataRotations[i], frameDataTranslations[i], 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,6 +79,7 @@ public:
|
|||
glm::mat4 localToVoxelMatrix() const;
|
||||
|
||||
virtual ShapeType getShapeType() const;
|
||||
virtual bool shouldBePhysical() const { return true; }
|
||||
virtual bool isReadyToComputeShape();
|
||||
virtual void computeShapeInfo(ShapeInfo& info);
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ public:
|
|||
}
|
||||
|
||||
virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; }
|
||||
virtual bool shouldBePhysical() const { return true; }
|
||||
|
||||
virtual void debugDump() const;
|
||||
|
||||
|
|
|
@ -334,7 +334,7 @@ public:
|
|||
bool getCollisionsWillMove() const { return _collisionsWillMove; }
|
||||
void setCollisionsWillMove(bool value) { _collisionsWillMove = value; }
|
||||
|
||||
virtual bool shouldBePhysical() const { return !_ignoreForCollisions; }
|
||||
virtual bool shouldBePhysical() const { return false; }
|
||||
|
||||
bool getLocked() const { return _locked; }
|
||||
void setLocked(bool value) { _locked = value; }
|
||||
|
|
|
@ -1573,14 +1573,17 @@ void EntityItemProperties::markAllChanged() {
|
|||
_accelerationSpreadChanged = true;
|
||||
_particleRadiusChanged = true;
|
||||
_radiusSpreadChanged = true;
|
||||
_radiusStartChanged = true;
|
||||
_radiusFinishChanged = true;
|
||||
_colorSpreadChanged = true;
|
||||
_colorStartChanged = true;
|
||||
_colorFinishChanged = true;
|
||||
_alphaSpreadChanged = true;
|
||||
_alphaStartChanged = true;
|
||||
_alphaFinishChanged = true;
|
||||
|
||||
// Only mark the following as changed if their values are specified in the properties when the particle is created. If their
|
||||
// values are specified then they are marked as changed in getChangedProperties().
|
||||
//_radiusStartChanged = true;
|
||||
//_radiusFinishChanged = true;
|
||||
//_colorStartChanged = true;
|
||||
//_colorFinishChanged = true;
|
||||
//_alphaStartChanged = true;
|
||||
//_alphaFinishChanged = true;
|
||||
|
||||
_marketplaceIDChanged = true;
|
||||
|
||||
|
|
|
@ -218,18 +218,20 @@ void ModelEntityItem::mapJoints(const QStringList& modelJointNames) {
|
|||
}
|
||||
}
|
||||
|
||||
const QVector<glm::quat>& ModelEntityItem::getAnimationFrame(bool& newFrame) {
|
||||
void ModelEntityItem::getAnimationFrame(bool& newFrame,
|
||||
QVector<glm::quat>& rotationsResult, QVector<glm::vec3>& translationsResult) {
|
||||
newFrame = false;
|
||||
|
||||
if (!hasAnimation() || !_jointMappingCompleted) {
|
||||
return _lastKnownFrameData;
|
||||
rotationsResult = _lastKnownFrameDataRotations;
|
||||
translationsResult = _lastKnownFrameDataTranslations;
|
||||
}
|
||||
|
||||
|
||||
AnimationPointer myAnimation = getAnimation(_animationURL); // FIXME: this could be optimized
|
||||
if (myAnimation && myAnimation->isLoaded()) {
|
||||
|
||||
|
||||
const QVector<FBXAnimationFrame>& frames = myAnimation->getFramesReference(); // NOTE: getFrames() is too heavy
|
||||
|
||||
|
||||
int frameCount = frames.size();
|
||||
if (frameCount > 0) {
|
||||
int animationFrameIndex = (int)(glm::floor(getAnimationFrameIndex())) % frameCount;
|
||||
|
@ -240,20 +242,27 @@ const QVector<glm::quat>& ModelEntityItem::getAnimationFrame(bool& newFrame) {
|
|||
if (animationFrameIndex != _lastKnownFrameIndex) {
|
||||
_lastKnownFrameIndex = animationFrameIndex;
|
||||
newFrame = true;
|
||||
|
||||
const QVector<glm::quat>& rotations = frames[animationFrameIndex].rotations;
|
||||
|
||||
_lastKnownFrameData.resize(_jointMapping.size());
|
||||
const QVector<glm::quat>& rotations = frames[animationFrameIndex].rotations;
|
||||
const QVector<glm::vec3>& translations = frames[animationFrameIndex].translations;
|
||||
|
||||
_lastKnownFrameDataRotations.resize(_jointMapping.size());
|
||||
_lastKnownFrameDataTranslations.resize(_jointMapping.size());
|
||||
for (int j = 0; j < _jointMapping.size(); j++) {
|
||||
int rotationIndex = _jointMapping[j];
|
||||
if (rotationIndex != -1 && rotationIndex < rotations.size()) {
|
||||
_lastKnownFrameData[j] = rotations[rotationIndex];
|
||||
int index = _jointMapping[j];
|
||||
if (index != -1 && index < rotations.size()) {
|
||||
_lastKnownFrameDataRotations[j] = rotations[index];
|
||||
}
|
||||
if (index != -1 && index < translations.size()) {
|
||||
_lastKnownFrameDataTranslations[j] = translations[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return _lastKnownFrameData;
|
||||
|
||||
rotationsResult = _lastKnownFrameDataRotations;
|
||||
translationsResult = _lastKnownFrameDataTranslations;
|
||||
}
|
||||
|
||||
bool ModelEntityItem::isAnimatingSomething() const {
|
||||
|
@ -457,5 +466,5 @@ QString ModelEntityItem::getAnimationSettings() const {
|
|||
|
||||
// virtual
|
||||
bool ModelEntityItem::shouldBePhysical() const {
|
||||
return EntityItem::shouldBePhysical() && getShapeType() != SHAPE_TYPE_NONE;
|
||||
return getShapeType() != SHAPE_TYPE_NONE;
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ public:
|
|||
float getAnimationLastFrame() const { return _animationLoop.getLastFrame(); }
|
||||
|
||||
void mapJoints(const QStringList& modelJointNames);
|
||||
const QVector<glm::quat>& getAnimationFrame(bool& newFrame);
|
||||
void getAnimationFrame(bool& newFrame, QVector<glm::quat>& rotationsResult, QVector<glm::vec3>& translationsResult);
|
||||
bool jointsMapped() const { return _jointMappingCompleted; }
|
||||
|
||||
bool getAnimationIsPlaying() const { return _animationLoop.isRunning(); }
|
||||
|
@ -123,7 +123,8 @@ public:
|
|||
static void cleanupLoadedAnimations();
|
||||
|
||||
protected:
|
||||
QVector<glm::quat> _lastKnownFrameData;
|
||||
QVector<glm::quat> _lastKnownFrameDataRotations;
|
||||
QVector<glm::vec3> _lastKnownFrameDataTranslations;
|
||||
int _lastKnownFrameIndex;
|
||||
|
||||
|
||||
|
|
|
@ -177,12 +177,12 @@ public:
|
|||
float getParticleRadius() const { return _particleRadius; }
|
||||
|
||||
static const float DEFAULT_RADIUS_START;
|
||||
bool _isRadiusStartInitialized;
|
||||
bool _isRadiusStartInitialized = false;
|
||||
void setRadiusStart(float radiusStart) { _radiusStart = radiusStart; _isRadiusStartInitialized = true; }
|
||||
float getRadiusStart() const { return _isRadiusStartInitialized ? _radiusStart : _particleRadius; }
|
||||
|
||||
static const float DEFAULT_RADIUS_FINISH;
|
||||
bool _isRadiusFinishInitialized;
|
||||
bool _isRadiusFinishInitialized = false;
|
||||
void setRadiusFinish(float radiusFinish) { _radiusFinish = radiusFinish; _isRadiusFinishInitialized = true; }
|
||||
float getRadiusFinish() const { return _isRadiusFinishInitialized ? _radiusFinish : _particleRadius; }
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ public:
|
|||
}
|
||||
|
||||
virtual ShapeType getShapeType() const { return SHAPE_TYPE_SPHERE; }
|
||||
virtual bool shouldBePhysical() const { return true; }
|
||||
|
||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
|
|
|
@ -530,6 +530,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
QHash<QString, QString> typeFlags;
|
||||
|
||||
QHash<QString, QString> localRotations;
|
||||
QHash<QString, QString> localTranslations;
|
||||
QHash<QString, QString> xComponents;
|
||||
QHash<QString, QString> yComponents;
|
||||
QHash<QString, QString> zComponents;
|
||||
|
@ -1112,16 +1113,16 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
normalTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||
} else if (type.contains("specular") || type.contains("reflection")) {
|
||||
specularTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||
|
||||
|
||||
} else if (type == "lcl rotation") {
|
||||
localRotations.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||
|
||||
} else if (type == "lcl translation") {
|
||||
localTranslations.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||
|
||||
} else if (type == "d|x") {
|
||||
xComponents.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||
|
||||
} else if (type == "d|y") {
|
||||
yComponents.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||
|
||||
} else if (type == "d|z") {
|
||||
zComponents.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||
|
||||
|
@ -1232,6 +1233,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
for (int i = 0; i < frameCount; i++) {
|
||||
FBXAnimationFrame frame;
|
||||
frame.rotations.resize(modelIDs.size());
|
||||
frame.translations.resize(modelIDs.size());
|
||||
geometry.animationFrames.append(frame);
|
||||
}
|
||||
|
||||
|
@ -1255,7 +1257,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
joint.freeLineage.append(index);
|
||||
}
|
||||
joint.freeLineage.remove(lastFreeIndex + 1, joint.freeLineage.size() - lastFreeIndex - 1);
|
||||
joint.translation = model.translation;
|
||||
joint.translation = model.translation; // these are usually in centimeters
|
||||
joint.preTransform = model.preTransform;
|
||||
joint.preRotation = model.preRotation;
|
||||
joint.rotation = model.rotation;
|
||||
|
@ -1280,7 +1282,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
}
|
||||
joint.inverseBindRotation = joint.inverseDefaultRotation;
|
||||
joint.name = model.name;
|
||||
|
||||
|
||||
foreach (const QString& childID, _connectionChildMap.values(modelID)) {
|
||||
QString type = typeFlags.value(childID);
|
||||
if (!type.isEmpty()) {
|
||||
|
@ -1293,17 +1295,29 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
|
||||
geometry.joints.append(joint);
|
||||
geometry.jointIndices.insert(model.name, geometry.joints.size());
|
||||
|
||||
|
||||
QString rotationID = localRotations.value(modelID);
|
||||
AnimationCurve xCurve = animationCurves.value(xComponents.value(rotationID));
|
||||
AnimationCurve yCurve = animationCurves.value(yComponents.value(rotationID));
|
||||
AnimationCurve zCurve = animationCurves.value(zComponents.value(rotationID));
|
||||
glm::vec3 defaultValues = glm::degrees(safeEulerAngles(joint.rotation));
|
||||
AnimationCurve xRotCurve = animationCurves.value(xComponents.value(rotationID));
|
||||
AnimationCurve yRotCurve = animationCurves.value(yComponents.value(rotationID));
|
||||
AnimationCurve zRotCurve = animationCurves.value(zComponents.value(rotationID));
|
||||
|
||||
QString translationID = localTranslations.value(modelID);
|
||||
AnimationCurve xPosCurve = animationCurves.value(xComponents.value(translationID));
|
||||
AnimationCurve yPosCurve = animationCurves.value(yComponents.value(translationID));
|
||||
AnimationCurve zPosCurve = animationCurves.value(zComponents.value(translationID));
|
||||
|
||||
glm::vec3 defaultRotValues = glm::degrees(safeEulerAngles(joint.rotation));
|
||||
glm::vec3 defaultPosValues = joint.translation;
|
||||
|
||||
for (int i = 0; i < frameCount; i++) {
|
||||
geometry.animationFrames[i].rotations[jointIndex] = glm::quat(glm::radians(glm::vec3(
|
||||
xCurve.values.isEmpty() ? defaultValues.x : xCurve.values.at(i % xCurve.values.size()),
|
||||
yCurve.values.isEmpty() ? defaultValues.y : yCurve.values.at(i % yCurve.values.size()),
|
||||
zCurve.values.isEmpty() ? defaultValues.z : zCurve.values.at(i % zCurve.values.size()))));
|
||||
xRotCurve.values.isEmpty() ? defaultRotValues.x : xRotCurve.values.at(i % xRotCurve.values.size()),
|
||||
yRotCurve.values.isEmpty() ? defaultRotValues.y : yRotCurve.values.at(i % yRotCurve.values.size()),
|
||||
zRotCurve.values.isEmpty() ? defaultRotValues.z : zRotCurve.values.at(i % zRotCurve.values.size()))));
|
||||
geometry.animationFrames[i].translations[jointIndex] = glm::vec3(
|
||||
xPosCurve.values.isEmpty() ? defaultPosValues.x : xPosCurve.values.at(i % xPosCurve.values.size()),
|
||||
yPosCurve.values.isEmpty() ? defaultPosValues.y : yPosCurve.values.at(i % yPosCurve.values.size()),
|
||||
zPosCurve.values.isEmpty() ? defaultPosValues.z : zPosCurve.values.at(i % zPosCurve.values.size()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -211,8 +211,8 @@ public:
|
|||
/// A single animation frame extracted from an FBX document.
|
||||
class FBXAnimationFrame {
|
||||
public:
|
||||
|
||||
QVector<glm::quat> rotations;
|
||||
QVector<glm::vec3> translations;
|
||||
};
|
||||
|
||||
/// A light in an FBX document.
|
||||
|
|
28
libraries/gpu/src/gpu/DrawUnitQuadTexcoord.slv
Normal file
28
libraries/gpu/src/gpu/DrawUnitQuadTexcoord.slv
Normal file
|
@ -0,0 +1,28 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// Draw the unit quad [-1,-1 -> 1,1] amd pass along the unit texcoords [0, 0 -> 1, 1]. Not transform used.
|
||||
// Simply draw a Triangle_strip of 2 triangles, no input buffers or index buffer needed
|
||||
//
|
||||
// Created by Sam Gateau on 6/22/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
out vec2 varTexCoord0;
|
||||
|
||||
void main(void) {
|
||||
const vec4 UNIT_QUAD[4] = vec4[4](
|
||||
vec4(-1.0, -1.0, 0.0, 1.0),
|
||||
vec4(1.0, -1.0, 0.0, 1.0),
|
||||
vec4(-1.0, 1.0, 0.0, 1.0),
|
||||
vec4(1.0, 1.0, 0.0, 1.0)
|
||||
);
|
||||
vec4 pos = UNIT_QUAD[gl_VertexID];
|
||||
|
||||
varTexCoord0 = (pos.xy + 1) * 0.5;
|
||||
|
||||
gl_Position = pos;
|
||||
}
|
|
@ -247,7 +247,13 @@ bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const For
|
|||
|
||||
_bufferMask = ( _bufferMask & ~BUFFER_DEPTHSTENCIL);
|
||||
if (texture) {
|
||||
_bufferMask |= BUFFER_DEPTHSTENCIL;
|
||||
if (format.getSemantic() == gpu::DEPTH) {
|
||||
_bufferMask |= BUFFER_DEPTH;
|
||||
} else if (format.getSemantic() == gpu::STENCIL) {
|
||||
_bufferMask |= BUFFER_STENCIL;
|
||||
} else if (format.getSemantic() == gpu::DEPTH_STENCIL) {
|
||||
_bufferMask |= BUFFER_DEPTHSTENCIL;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -116,6 +116,8 @@ public:
|
|||
bool isEmpty() const { return (_bufferMask == 0); }
|
||||
bool hasColor() const { return (getBufferMask() & BUFFER_COLORS); }
|
||||
bool hasDepthStencil() const { return (getBufferMask() & BUFFER_DEPTHSTENCIL); }
|
||||
bool hasDepth() const { return (getBufferMask() & BUFFER_DEPTH); }
|
||||
bool hasStencil() const { return (getBufferMask() & BUFFER_STENCIL); }
|
||||
|
||||
bool validateTargetCompatibility(const Texture& texture, uint32 subresource = 0) const;
|
||||
|
||||
|
|
|
@ -132,20 +132,26 @@ void GLBackend::renderPassTransfer(Batch& batch) {
|
|||
const size_t numCommands = batch.getCommands().size();
|
||||
const Batch::Commands::value_type* command = batch.getCommands().data();
|
||||
const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data();
|
||||
|
||||
for (auto& cached : batch._buffers._items) {
|
||||
if (cached._data) {
|
||||
syncGPUObject(*cached._data);
|
||||
|
||||
{ // Sync all the buffers
|
||||
PROFILE_RANGE("syncGPUBuffer");
|
||||
|
||||
for (auto& cached : batch._buffers._items) {
|
||||
if (cached._data) {
|
||||
syncGPUObject(*cached._data);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Reset the transform buffers
|
||||
_transform._cameras.resize(0);
|
||||
_transform._cameraOffsets.clear();
|
||||
_transform._objects.resize(0);
|
||||
_transform._objectOffsets.clear();
|
||||
|
||||
for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) {
|
||||
switch (*command) {
|
||||
{ // Sync all the buffers
|
||||
PROFILE_RANGE("syncCPUTransform");
|
||||
_transform._cameras.resize(0);
|
||||
_transform._cameraOffsets.clear();
|
||||
_transform._objects.resize(0);
|
||||
_transform._objectOffsets.clear();
|
||||
|
||||
for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) {
|
||||
switch (*command) {
|
||||
case Batch::COMMAND_draw:
|
||||
case Batch::COMMAND_drawIndexed:
|
||||
case Batch::COMMAND_drawInstanced:
|
||||
|
@ -164,11 +170,16 @@ void GLBackend::renderPassTransfer(Batch& batch) {
|
|||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
command++;
|
||||
offset++;
|
||||
}
|
||||
command++;
|
||||
offset++;
|
||||
}
|
||||
_transform.transfer();
|
||||
|
||||
{ // Sync the transform buffers
|
||||
PROFILE_RANGE("syncGPUTransform");
|
||||
_transform.transfer();
|
||||
}
|
||||
}
|
||||
|
||||
void GLBackend::renderPassDraw(Batch& batch) {
|
||||
|
|
|
@ -100,7 +100,18 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe
|
|||
if (surface) {
|
||||
auto gltexture = GLBackend::syncGPUObject(*surface);
|
||||
if (gltexture) {
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, gltexture->_texture, 0);
|
||||
GLenum attachement = GL_DEPTH_STENCIL_ATTACHMENT;
|
||||
if (!framebuffer.hasStencil()) {
|
||||
attachement = GL_DEPTH_ATTACHMENT;
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, attachement, GL_TEXTURE_2D, gltexture->_texture, 0);
|
||||
} else if (!framebuffer.hasDepth()) {
|
||||
attachement = GL_STENCIL_ATTACHMENT;
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, attachement, GL_TEXTURE_2D, gltexture->_texture, 0);
|
||||
} else {
|
||||
attachement = GL_DEPTH_STENCIL_ATTACHMENT;
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachement, GL_RENDERBUFFER, gltexture->_texture);
|
||||
}
|
||||
(void) CHECK_GL_ERROR();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -642,8 +642,13 @@ void GLBackend::do_setStateStencil(State::StencilActivation activation, State::S
|
|||
|
||||
if (activation.isEnabled()) {
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glStencilMaskSeparate(GL_FRONT, activation.getWriteMaskFront());
|
||||
glStencilMaskSeparate(GL_BACK, activation.getWriteMaskBack());
|
||||
|
||||
if (activation.getWriteMaskFront() != activation.getWriteMaskBack()) {
|
||||
glStencilMaskSeparate(GL_FRONT, activation.getWriteMaskFront());
|
||||
glStencilMaskSeparate(GL_BACK, activation.getWriteMaskBack());
|
||||
} else {
|
||||
glStencilMask(activation.getWriteMaskFront());
|
||||
}
|
||||
|
||||
static GLenum STENCIL_OPS[] = {
|
||||
GL_KEEP,
|
||||
|
@ -655,11 +660,16 @@ void GLBackend::do_setStateStencil(State::StencilActivation activation, State::S
|
|||
GL_INCR,
|
||||
GL_DECR };
|
||||
|
||||
glStencilFuncSeparate(GL_FRONT, STENCIL_OPS[frontTest.getFailOp()], STENCIL_OPS[frontTest.getPassOp()], STENCIL_OPS[frontTest.getDepthFailOp()]);
|
||||
glStencilFuncSeparate(GL_FRONT, GL_COMPARISON_FUNCTIONS[frontTest.getFunction()], frontTest.getReference(), frontTest.getReadMask());
|
||||
if (frontTest != backTest) {
|
||||
glStencilOpSeparate(GL_FRONT, STENCIL_OPS[frontTest.getFailOp()], STENCIL_OPS[frontTest.getPassOp()], STENCIL_OPS[frontTest.getDepthFailOp()]);
|
||||
glStencilFuncSeparate(GL_FRONT, GL_COMPARISON_FUNCTIONS[frontTest.getFunction()], frontTest.getReference(), frontTest.getReadMask());
|
||||
|
||||
glStencilFuncSeparate(GL_BACK, STENCIL_OPS[backTest.getFailOp()], STENCIL_OPS[backTest.getPassOp()], STENCIL_OPS[backTest.getDepthFailOp()]);
|
||||
glStencilFuncSeparate(GL_BACK, GL_COMPARISON_FUNCTIONS[backTest.getFunction()], backTest.getReference(), backTest.getReadMask());
|
||||
glStencilOpSeparate(GL_BACK, STENCIL_OPS[backTest.getFailOp()], STENCIL_OPS[backTest.getPassOp()], STENCIL_OPS[backTest.getDepthFailOp()]);
|
||||
glStencilFuncSeparate(GL_BACK, GL_COMPARISON_FUNCTIONS[backTest.getFunction()], backTest.getReference(), backTest.getReadMask());
|
||||
} else {
|
||||
glStencilOp(STENCIL_OPS[frontTest.getFailOp()], STENCIL_OPS[frontTest.getPassOp()], STENCIL_OPS[frontTest.getDepthFailOp()]);
|
||||
glStencilFunc(GL_COMPARISON_FUNCTIONS[frontTest.getFunction()], frontTest.getReference(), frontTest.getReadMask());
|
||||
}
|
||||
} else {
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
}
|
||||
|
|
|
@ -66,7 +66,9 @@ public:
|
|||
texel.internalFormat = GL_RG;
|
||||
break;
|
||||
case gpu::DEPTH_STENCIL:
|
||||
texel.internalFormat = GL_DEPTH_STENCIL;
|
||||
texel.type = GL_UNSIGNED_BYTE;
|
||||
texel.format = GL_DEPTH_STENCIL;
|
||||
texel.internalFormat = GL_DEPTH24_STENCIL8;
|
||||
break;
|
||||
default:
|
||||
qCDebug(gpulogging) << "Unknown combination of texel format";
|
||||
|
@ -197,7 +199,9 @@ public:
|
|||
texel.internalFormat = GL_RG;
|
||||
break;
|
||||
case gpu::DEPTH_STENCIL:
|
||||
texel.internalFormat = GL_DEPTH_STENCIL;
|
||||
texel.type = GL_UNSIGNED_BYTE;
|
||||
texel.format = GL_DEPTH_STENCIL;
|
||||
texel.internalFormat = GL_DEPTH24_STENCIL8;
|
||||
break;
|
||||
default:
|
||||
qCDebug(gpulogging) << "Unknown combination of texel format";
|
||||
|
@ -334,22 +338,34 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
|
|||
}
|
||||
|
||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0,
|
||||
texelFormat.internalFormat, texture.getWidth(), texture.getHeight(), 0,
|
||||
texelFormat.format, texelFormat.type, bytes);
|
||||
|
||||
if (bytes && texture.isAutogenerateMips()) {
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
}/* else {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
}*/
|
||||
|
||||
object->_target = GL_TEXTURE_2D;
|
||||
auto semantic = texture.getTexelFormat().getSemantic();
|
||||
|
||||
syncSampler(texture.getSampler(), texture.getType(), object);
|
||||
if (semantic == gpu::DEPTH_STENCIL) {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDeleteTextures(1, &object->_texture);
|
||||
|
||||
glGenRenderbuffers(1, &object->_texture);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, object->_texture);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, texture.getWidth(), texture.getHeight());
|
||||
// At this point the mip pixels have been loaded, we can notify
|
||||
texture.notifyMipFaceGPULoaded(0, 0);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
} else {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0,
|
||||
texelFormat.internalFormat, texture.getWidth(), texture.getHeight(), 0,
|
||||
texelFormat.format, texelFormat.type, bytes);
|
||||
|
||||
if (bytes && texture.isAutogenerateMips()) {
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
}
|
||||
|
||||
object->_target = GL_TEXTURE_2D;
|
||||
|
||||
syncSampler(texture.getSampler(), texture.getType(), object);
|
||||
}
|
||||
|
||||
// At this point the mip pixels have been loaded, we can notify
|
||||
texture.notifyMipFaceGPULoaded(0, 0);
|
||||
|
|
|
@ -130,19 +130,27 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo
|
|||
|
||||
void GLBackend::TransformStageState::transfer() const {
|
||||
static QByteArray bufferData;
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, _cameraBuffer);
|
||||
bufferData.resize(_cameraUboSize * _cameras.size());
|
||||
for (size_t i = 0; i < _cameras.size(); ++i) {
|
||||
memcpy(bufferData.data() + (_cameraUboSize * i), &_cameras[i], sizeof(TransformCamera));
|
||||
if (!_cameras.empty()) {
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, _cameraBuffer);
|
||||
bufferData.resize(_cameraUboSize * _cameras.size());
|
||||
for (size_t i = 0; i < _cameras.size(); ++i) {
|
||||
memcpy(bufferData.data() + (_cameraUboSize * i), &_cameras[i], sizeof(TransformCamera));
|
||||
}
|
||||
glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
|
||||
}
|
||||
glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, _objectBuffer);
|
||||
bufferData.resize(_objectUboSize * _objects.size());
|
||||
for (size_t i = 0; i < _objects.size(); ++i) {
|
||||
memcpy(bufferData.data() + (_objectUboSize * i), &_objects[i], sizeof(TransformObject));
|
||||
|
||||
if (!_objects.empty()) {
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, _objectBuffer);
|
||||
bufferData.resize(_objectUboSize * _objects.size());
|
||||
for (size_t i = 0; i < _objects.size(); ++i) {
|
||||
memcpy(bufferData.data() + (_objectUboSize * i), &_objects[i], sizeof(TransformObject));
|
||||
}
|
||||
glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
|
||||
}
|
||||
|
||||
if (!_cameras.empty() || !_objects.empty()) {
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
//
|
||||
#include "StandardShaderLib.h"
|
||||
|
||||
#include "DrawUnitQuadTexcoord_vert.h"
|
||||
#include "DrawTransformUnitQuad_vert.h"
|
||||
#include "DrawTexcoordRectTransformUnitQuad_vert.h"
|
||||
#include "DrawViewportQuadTransformTexcoord_vert.h"
|
||||
|
@ -21,6 +22,7 @@
|
|||
|
||||
using namespace gpu;
|
||||
|
||||
ShaderPointer StandardShaderLib::_drawUnitQuadTexcoordVS;
|
||||
ShaderPointer StandardShaderLib::_drawTransformUnitQuadVS;
|
||||
ShaderPointer StandardShaderLib::_drawTexcoordRectTransformUnitQuadVS;
|
||||
ShaderPointer StandardShaderLib::_drawViewportQuadTransformTexcoordVS;
|
||||
|
@ -55,6 +57,12 @@ ShaderPointer StandardShaderLib::getProgram(GetShader getVS, GetShader getPS) {
|
|||
}
|
||||
|
||||
|
||||
ShaderPointer StandardShaderLib::getDrawUnitQuadTexcoordVS() {
|
||||
if (!_drawUnitQuadTexcoordVS) {
|
||||
_drawUnitQuadTexcoordVS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(DrawUnitQuadTexcoord_vert)));
|
||||
}
|
||||
return _drawUnitQuadTexcoordVS;
|
||||
}
|
||||
|
||||
ShaderPointer StandardShaderLib::getDrawTransformUnitQuadVS() {
|
||||
if (!_drawTransformUnitQuadVS) {
|
||||
|
|
|
@ -23,6 +23,9 @@ namespace gpu {
|
|||
class StandardShaderLib {
|
||||
public:
|
||||
|
||||
// Shader draws the unit quad in the full viewport clipPos = ([(-1,-1),(1,1)]) and the unit texcoord = [(0,0),(1,1)].
|
||||
static ShaderPointer getDrawUnitQuadTexcoordVS();
|
||||
|
||||
// Shader draw the unit quad objectPos = ([(-1,-1),(1,1)]) and transform it by the full model transform stack (Model, View, Proj).
|
||||
// A texcoord attribute is also generated texcoord = [(0,0),(1,1)]
|
||||
static ShaderPointer getDrawTransformUnitQuadVS();
|
||||
|
@ -44,6 +47,7 @@ public:
|
|||
|
||||
protected:
|
||||
|
||||
static ShaderPointer _drawUnitQuadTexcoordVS;
|
||||
static ShaderPointer _drawTransformUnitQuadVS;
|
||||
static ShaderPointer _drawTexcoordRectTransformUnitQuadVS;
|
||||
static ShaderPointer _drawViewportQuadTransformTexcoordVS;
|
||||
|
|
|
@ -143,11 +143,11 @@ public:
|
|||
static const int PASS_OP_OFFSET = 12;
|
||||
|
||||
uint16 _functionAndOperations;
|
||||
uint8 _reference = 0;
|
||||
int8 _reference = 0;
|
||||
uint8 _readMask = 0xff;
|
||||
public:
|
||||
|
||||
StencilTest(uint8 reference = 0, uint8 readMask =0xFF, ComparisonFunction func = ALWAYS, StencilOp failOp = STENCIL_OP_KEEP, StencilOp depthFailOp = STENCIL_OP_KEEP, StencilOp passOp = STENCIL_OP_KEEP) :
|
||||
StencilTest(int8 reference = 0, uint8 readMask =0xFF, ComparisonFunction func = ALWAYS, StencilOp failOp = STENCIL_OP_KEEP, StencilOp depthFailOp = STENCIL_OP_KEEP, StencilOp passOp = STENCIL_OP_KEEP) :
|
||||
_functionAndOperations(func | (failOp << FAIL_OP_OFFSET) | (depthFailOp << DEPTH_FAIL_OP_OFFSET) | (passOp << PASS_OP_OFFSET)),
|
||||
_reference(reference), _readMask(readMask)
|
||||
{}
|
||||
|
@ -157,7 +157,7 @@ public:
|
|||
StencilOp getDepthFailOp() const { return StencilOp((_functionAndOperations & DEPTH_FAIL_OP_MASK) >> DEPTH_FAIL_OP_OFFSET); }
|
||||
StencilOp getPassOp() const { return StencilOp((_functionAndOperations & PASS_OP_MASK) >> PASS_OP_OFFSET); }
|
||||
|
||||
uint8 getReference() const { return _reference; }
|
||||
int8 getReference() const { return _reference; }
|
||||
uint8 getReadMask() const { return _readMask; }
|
||||
|
||||
int32 getRaw() const { return *(reinterpret_cast<const int32*>(this)); }
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
using namespace model;
|
||||
|
||||
Skybox::Skybox() {
|
||||
Data data;
|
||||
_dataBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Data), (const gpu::Byte*) &data));
|
||||
|
||||
/* // PLease create a default engineer skybox
|
||||
_cubemap.reset( gpu::Texture::createCube(gpu::Element::COLOR_RGBA_32, 1));
|
||||
|
@ -36,7 +38,7 @@ Skybox::Skybox() {
|
|||
}
|
||||
|
||||
void Skybox::setColor(const Color& color) {
|
||||
_color = color;
|
||||
_dataBuffer.edit<Data>()._color = color;
|
||||
}
|
||||
|
||||
void Skybox::setCubemap(const gpu::TexturePointer& cubemap) {
|
||||
|
@ -44,12 +46,40 @@ void Skybox::setCubemap(const gpu::TexturePointer& cubemap) {
|
|||
}
|
||||
|
||||
|
||||
void Skybox::updateDataBuffer() const {
|
||||
auto blend = 0.0f;
|
||||
if (getCubemap() && getCubemap()->isDefined()) {
|
||||
blend = 1.0f;
|
||||
// If pitch black neutralize the color
|
||||
if (glm::all(glm::equal(getColor(), glm::vec3(0.0f)))) {
|
||||
blend = 2.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (blend != _dataBuffer.get<Data>()._blend) {
|
||||
_dataBuffer.edit<Data>()._blend = blend;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Skybox::render(gpu::Batch& batch, const ViewFrustum& frustum) const {
|
||||
updateDataBuffer();
|
||||
Skybox::render(batch, frustum, (*this));
|
||||
}
|
||||
|
||||
|
||||
void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Skybox& skybox) {
|
||||
// Create the static shared elements used to render the skybox
|
||||
static gpu::BufferPointer theBuffer;
|
||||
static gpu::Stream::FormatPointer theFormat;
|
||||
|
||||
if (skybox.getCubemap()) {
|
||||
if (!theBuffer) {
|
||||
static gpu::BufferPointer theConstants;
|
||||
static gpu::PipelinePointer thePipeline;
|
||||
const int SKYBOX_SKYMAP_SLOT = 0;
|
||||
const int SKYBOX_CONSTANTS_SLOT = 0;
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
{
|
||||
const float CLIP = 1.0f;
|
||||
const glm::vec2 vertices[4] = { { -CLIP, -CLIP }, { CLIP, -CLIP }, { -CLIP, CLIP }, { CLIP, CLIP } };
|
||||
theBuffer = std::make_shared<gpu::Buffer>(sizeof(vertices), (const gpu::Byte*) vertices);
|
||||
|
@ -57,62 +87,49 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
|
|||
theFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ));
|
||||
}
|
||||
|
||||
glm::mat4 projMat;
|
||||
viewFrustum.evalProjectionMatrix(projMat);
|
||||
{
|
||||
auto skyVS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(Skybox_vert)));
|
||||
auto skyFS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(Skybox_frag)));
|
||||
auto skyShader = gpu::ShaderPointer(gpu::Shader::createProgram(skyVS, skyFS));
|
||||
|
||||
Transform viewTransform;
|
||||
viewFrustum.evalViewTransform(viewTransform);
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewTransform);
|
||||
batch.setModelTransform(Transform()); // only for Mac
|
||||
batch.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8);
|
||||
batch.setInputFormat(theFormat);
|
||||
gpu::Shader::BindingSet bindings;
|
||||
bindings.insert(gpu::Shader::Binding(std::string("cubeMap"), SKYBOX_SKYMAP_SLOT));
|
||||
bindings.insert(gpu::Shader::Binding(std::string("skyboxBuffer"), SKYBOX_CONSTANTS_SLOT));
|
||||
if (!gpu::Shader::makeProgram(*skyShader, bindings)) {
|
||||
|
||||
if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) {
|
||||
static gpu::BufferPointer theConstants;
|
||||
static gpu::PipelinePointer thePipeline;
|
||||
static int SKYBOX_CONSTANTS_SLOT = 0; // need to be defined by the compilation of the shader
|
||||
if (!thePipeline) {
|
||||
auto skyVS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(Skybox_vert)));
|
||||
auto skyFS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(Skybox_frag)));
|
||||
auto skyShader = gpu::ShaderPointer(gpu::Shader::createProgram(skyVS, skyFS));
|
||||
|
||||
gpu::Shader::BindingSet bindings;
|
||||
bindings.insert(gpu::Shader::Binding(std::string("cubeMap"), 0));
|
||||
if (!gpu::Shader::makeProgram(*skyShader, bindings)) {
|
||||
|
||||
}
|
||||
|
||||
SKYBOX_CONSTANTS_SLOT = skyShader->getBuffers().findLocation("skyboxBuffer");
|
||||
if (SKYBOX_CONSTANTS_SLOT == gpu::Shader::INVALID_LOCATION) {
|
||||
SKYBOX_CONSTANTS_SLOT = skyShader->getUniforms().findLocation("skyboxBuffer");
|
||||
}
|
||||
|
||||
auto skyState = std::make_shared<gpu::State>();
|
||||
|
||||
thePipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, skyState));
|
||||
|
||||
auto color = glm::vec4(1.0f);
|
||||
theConstants = std::make_shared<gpu::Buffer>(sizeof(color), (const gpu::Byte*) &color);
|
||||
}
|
||||
|
||||
if (glm::all(glm::equal(skybox.getColor(), glm::vec3(0.0f)))) {
|
||||
auto color = glm::vec4(1.0f);
|
||||
theConstants->setSubData(0, sizeof(color), (const gpu::Byte*) &color);
|
||||
} else {
|
||||
theConstants->setSubData(0, sizeof(Color), (const gpu::Byte*) &skybox.getColor());
|
||||
}
|
||||
auto skyState = std::make_shared<gpu::State>();
|
||||
skyState->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
|
||||
batch.setPipeline(thePipeline);
|
||||
batch.setUniformBuffer(SKYBOX_CONSTANTS_SLOT, theConstants, 0, theConstants->getSize());
|
||||
batch.setResourceTexture(0, skybox.getCubemap());
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
thePipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, skyState));
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
// skybox has no cubemap, just clear the color buffer
|
||||
auto color = skybox.getColor();
|
||||
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(color, 0.0f), 0.0f, 0, true);
|
||||
// Render
|
||||
glm::mat4 projMat;
|
||||
viewFrustum.evalProjectionMatrix(projMat);
|
||||
|
||||
Transform viewTransform;
|
||||
viewFrustum.evalViewTransform(viewTransform);
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewTransform);
|
||||
batch.setModelTransform(Transform()); // only for Mac
|
||||
batch.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8);
|
||||
batch.setInputFormat(theFormat);
|
||||
|
||||
gpu::TexturePointer skymap;
|
||||
if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) {
|
||||
skymap = skybox.getCubemap();
|
||||
}
|
||||
|
||||
batch.setPipeline(thePipeline);
|
||||
batch.setUniformBuffer(SKYBOX_CONSTANTS_SLOT, skybox._dataBuffer);
|
||||
batch.setResourceTexture(SKYBOX_SKYMAP_SLOT, skymap);
|
||||
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
batch.setResourceTexture(SKYBOX_SKYMAP_SLOT, nullptr);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -30,20 +30,28 @@ public:
|
|||
virtual ~Skybox() {};
|
||||
|
||||
void setColor(const Color& color);
|
||||
const Color& getColor() const { return _color; }
|
||||
const Color getColor() const { return _dataBuffer.get<Data>()._color; }
|
||||
|
||||
void setCubemap(const gpu::TexturePointer& cubemap);
|
||||
const gpu::TexturePointer& getCubemap() const { return _cubemap; }
|
||||
|
||||
virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const {
|
||||
render(batch, frustum, (*this));
|
||||
}
|
||||
virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const;
|
||||
|
||||
|
||||
static void render(gpu::Batch& batch, const ViewFrustum& frustum, const Skybox& skybox);
|
||||
|
||||
protected:
|
||||
gpu::TexturePointer _cubemap;
|
||||
Color _color{1.0f, 1.0f, 1.0f};
|
||||
|
||||
class Data {
|
||||
public:
|
||||
glm::vec3 _color{ 1.0f, 1.0f, 1.0f };
|
||||
float _blend = 1.0f;
|
||||
};
|
||||
|
||||
mutable gpu::BufferView _dataBuffer;
|
||||
|
||||
void updateDataBuffer() const;
|
||||
};
|
||||
typedef std::shared_ptr< Skybox > SkyboxPointer;
|
||||
|
||||
|
|
|
@ -40,8 +40,18 @@ void main(void) {
|
|||
#else
|
||||
|
||||
vec3 coord = normalize(_normal);
|
||||
vec3 texel = texture(cubeMap, coord).rgb;
|
||||
vec3 color = texel * _skybox._color.rgb;
|
||||
|
||||
// Skybox color or blend with skymap
|
||||
vec3 color = _skybox._color.rgb;
|
||||
if (_skybox._color.a > 0.0) {
|
||||
vec3 texel = texture(cubeMap, coord).rgb;
|
||||
if (_skybox._color.a < 2.0) {
|
||||
color *= texel;
|
||||
} else {
|
||||
color = texel;
|
||||
}
|
||||
}
|
||||
|
||||
vec3 pixel = pow(color, vec3(1.0/2.2)); // manual Gamma correction
|
||||
_fragColor = vec4(pixel, 0.0);
|
||||
|
||||
|
|
|
@ -39,6 +39,9 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::EntityEdit:
|
||||
case PacketType::EntityData:
|
||||
return VERSION_ENTITIES_PARTICLE_ELLIPSOID_EMITTER;
|
||||
case PacketType::AvatarData:
|
||||
case PacketType::BulkAvatarData:
|
||||
return 15;
|
||||
default:
|
||||
return 14;
|
||||
}
|
||||
|
|
|
@ -596,6 +596,12 @@ QString EntityMotionState::getName() {
|
|||
|
||||
// virtual
|
||||
int16_t EntityMotionState::computeCollisionGroup() {
|
||||
if (!_entity) {
|
||||
return COLLISION_GROUP_STATIC;
|
||||
}
|
||||
if (_entity->getIgnoreForCollisions()) {
|
||||
return COLLISION_GROUP_COLLISIONLESS;
|
||||
}
|
||||
switch (computeObjectMotionType()){
|
||||
case MOTION_TYPE_STATIC:
|
||||
return COLLISION_GROUP_STATIC;
|
||||
|
|
|
@ -37,11 +37,11 @@ enum MotionStateType {
|
|||
|
||||
// The update flags trigger two varieties of updates: "hard" which require the body to be pulled
|
||||
// and re-added to the physics engine and "easy" which just updates the body properties.
|
||||
const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE);
|
||||
const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE |
|
||||
EntityItem::DIRTY_COLLISION_GROUP);
|
||||
const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES |
|
||||
EntityItem::DIRTY_MASS | EntityItem::DIRTY_COLLISION_GROUP |
|
||||
EntityItem::DIRTY_MATERIAL | EntityItem::DIRTY_SIMULATOR_ID |
|
||||
EntityItem::DIRTY_SIMULATOR_OWNERSHIP);
|
||||
EntityItem::DIRTY_MASS | EntityItem::DIRTY_MATERIAL |
|
||||
EntityItem::DIRTY_SIMULATOR_ID | EntityItem::DIRTY_SIMULATOR_OWNERSHIP);
|
||||
|
||||
// These are the set of incoming flags that the PhysicsEngine needs to hear about:
|
||||
const uint32_t DIRTY_PHYSICS_FLAGS = (uint32_t)(HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS |
|
||||
|
|
|
@ -32,7 +32,8 @@ void ProceduralSkybox::setProcedural(const ProceduralPointer& procedural) {
|
|||
if (_procedural) {
|
||||
_procedural->_vertexSource = ProceduralSkybox_vert;
|
||||
_procedural->_fragmentSource = ProceduralSkybox_frag;
|
||||
// No pipeline state customization
|
||||
// Adjust the pipeline state for background using the stencil test
|
||||
_procedural->_state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,6 +43,7 @@ void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& frustum) con
|
|||
|
||||
void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const ProceduralSkybox& skybox) {
|
||||
if (!(skybox._procedural)) {
|
||||
skybox.updateDataBuffer();
|
||||
Skybox::render(batch, viewFrustum, skybox);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ add_dependency_external_projects(oglplus)
|
|||
find_package(OGLPLUS REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${OGLPLUS_INCLUDE_DIRS})
|
||||
|
||||
add_definitions(-DGLEW_STATIC)
|
||||
|
||||
if (WIN32)
|
||||
if (USE_NSIGHT)
|
||||
# try to find the Nsight package and add it to the build if we find it
|
||||
|
|
|
@ -62,7 +62,7 @@ void Environment::setupAtmosphereProgram(const char* vertSource, const char* fra
|
|||
auto state = std::make_shared<gpu::State>();
|
||||
|
||||
state->setCullMode(gpu::State::CULL_NONE);
|
||||
state->setDepthTest(false);
|
||||
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
state->setBlendFunction(true,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
|
|
|
@ -35,7 +35,9 @@ void FramebufferCache::setFrameBufferSize(QSize frameBufferSize) {
|
|||
_frameBufferSize = frameBufferSize;
|
||||
_primaryFramebufferFull.reset();
|
||||
_primaryFramebufferDepthColor.reset();
|
||||
_primaryFramebufferStencilColor.reset();
|
||||
_primaryDepthTexture.reset();
|
||||
_primaryStencilTexture.reset();
|
||||
_primaryColorTexture.reset();
|
||||
_primaryNormalTexture.reset();
|
||||
_primarySpecularTexture.reset();
|
||||
|
@ -47,6 +49,7 @@ void FramebufferCache::setFrameBufferSize(QSize frameBufferSize) {
|
|||
void FramebufferCache::createPrimaryFramebuffer() {
|
||||
_primaryFramebufferFull = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
||||
_primaryFramebufferDepthColor = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
||||
_primaryFramebufferStencilColor = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
||||
|
||||
auto colorFormat = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
|
||||
auto width = _frameBufferSize.width();
|
||||
|
@ -63,12 +66,19 @@ void FramebufferCache::createPrimaryFramebuffer() {
|
|||
|
||||
_primaryFramebufferDepthColor->setRenderBuffer(0, _primaryColorTexture);
|
||||
|
||||
_primaryFramebufferStencilColor->setRenderBuffer(0, _primaryColorTexture);
|
||||
|
||||
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);
|
||||
_primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, width, height, defaultSampler));
|
||||
|
||||
auto stencilFormat = gpu::Element(gpu::VEC2, gpu::UINT32, gpu::DEPTH_STENCIL);
|
||||
_primaryStencilTexture = gpu::TexturePointer(gpu::Texture::create2D(stencilFormat, width, height, defaultSampler));
|
||||
|
||||
_primaryFramebufferFull->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
|
||||
|
||||
_primaryFramebufferDepthColor->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
|
||||
|
||||
_primaryFramebufferStencilColor->setDepthStencilBuffer(_primaryStencilTexture, stencilFormat);
|
||||
|
||||
_selfieFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
||||
auto tex = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width * 0.5, height * 0.5, defaultSampler));
|
||||
|
@ -89,6 +99,12 @@ gpu::FramebufferPointer FramebufferCache::getPrimaryFramebufferDepthColor() {
|
|||
return _primaryFramebufferDepthColor;
|
||||
}
|
||||
|
||||
gpu::FramebufferPointer FramebufferCache::getPrimaryFramebufferStencilColor() {
|
||||
if (!_primaryFramebufferStencilColor) {
|
||||
createPrimaryFramebuffer();
|
||||
}
|
||||
return _primaryFramebufferStencilColor;
|
||||
}
|
||||
|
||||
gpu::TexturePointer FramebufferCache::getPrimaryDepthTexture() {
|
||||
if (!_primaryDepthTexture) {
|
||||
|
@ -97,6 +113,13 @@ gpu::TexturePointer FramebufferCache::getPrimaryDepthTexture() {
|
|||
return _primaryDepthTexture;
|
||||
}
|
||||
|
||||
gpu::TexturePointer FramebufferCache::getPrimaryStencilTexture() {
|
||||
if (!_primaryStencilTexture) {
|
||||
createPrimaryFramebuffer();
|
||||
}
|
||||
return _primaryStencilTexture;
|
||||
}
|
||||
|
||||
gpu::TexturePointer FramebufferCache::getPrimaryColorTexture() {
|
||||
if (!_primaryColorTexture) {
|
||||
createPrimaryFramebuffer();
|
||||
|
|
|
@ -31,8 +31,10 @@ public:
|
|||
/// used for scene rendering.
|
||||
gpu::FramebufferPointer getPrimaryFramebuffer();
|
||||
gpu::FramebufferPointer getPrimaryFramebufferDepthColor();
|
||||
gpu::FramebufferPointer getPrimaryFramebufferStencilColor();
|
||||
|
||||
gpu::TexturePointer getPrimaryDepthTexture();
|
||||
gpu::TexturePointer getPrimaryStencilTexture();
|
||||
gpu::TexturePointer getPrimaryColorTexture();
|
||||
gpu::TexturePointer getPrimaryNormalTexture();
|
||||
gpu::TexturePointer getPrimarySpecularTexture();
|
||||
|
@ -58,7 +60,9 @@ private:
|
|||
|
||||
gpu::FramebufferPointer _primaryFramebufferFull;
|
||||
gpu::FramebufferPointer _primaryFramebufferDepthColor;
|
||||
gpu::FramebufferPointer _primaryFramebufferStencilColor;
|
||||
gpu::TexturePointer _primaryDepthTexture;
|
||||
gpu::TexturePointer _primaryStencilTexture;
|
||||
gpu::TexturePointer _primaryColorTexture;
|
||||
gpu::TexturePointer _primaryNormalTexture;
|
||||
gpu::TexturePointer _primarySpecularTexture;
|
||||
|
|
|
@ -1001,8 +1001,16 @@ void Model::clearJointState(int index) {
|
|||
_rig->clearJointState(index);
|
||||
}
|
||||
|
||||
void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) {
|
||||
_rig->setJointState(index, valid, rotation, priority);
|
||||
void Model::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) {
|
||||
_rig->setJointState(index, valid, rotation, translation, priority);
|
||||
}
|
||||
|
||||
void Model::setJointRotation(int index, bool valid, const glm::quat& rotation, float priority) {
|
||||
_rig->setJointRotation(index, valid, rotation, priority);
|
||||
}
|
||||
|
||||
void Model::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) {
|
||||
_rig->setJointTranslation(index, valid, translation, priority);
|
||||
}
|
||||
|
||||
int Model::getParentJointIndex(int jointIndex) const {
|
||||
|
@ -1074,6 +1082,10 @@ bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const {
|
|||
return _rig->getJointRotation(jointIndex, rotation);
|
||||
}
|
||||
|
||||
bool Model::getJointTranslation(int jointIndex, glm::vec3& translation) const {
|
||||
return _rig->getJointTranslation(jointIndex, translation);
|
||||
}
|
||||
|
||||
bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const {
|
||||
return _rig->getJointCombinedRotation(jointIndex, rotation, _rotation);
|
||||
}
|
||||
|
|
|
@ -126,7 +126,9 @@ public:
|
|||
QStringList getJointNames() const;
|
||||
|
||||
/// Sets the joint state at the specified index.
|
||||
void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat(), float priority = 1.0f);
|
||||
void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority);
|
||||
void setJointRotation(int index, bool valid, const glm::quat& rotation, float priority);
|
||||
void setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority);
|
||||
|
||||
bool findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
|
@ -161,6 +163,7 @@ public:
|
|||
/// \param rotation[out] rotation of joint in model-frame
|
||||
/// \return true if joint exists
|
||||
bool getJointRotation(int jointIndex, glm::quat& rotation) const;
|
||||
bool getJointTranslation(int jointIndex, glm::vec3& translation) const;
|
||||
|
||||
/// Returns the index of the parent of the indexed joint, or -1 if not found.
|
||||
int getParentJointIndex(int jointIndex) const;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <RenderArgs.h>
|
||||
#include <ViewFrustum.h>
|
||||
#include <gpu/Context.h>
|
||||
#include <gpu/StandardShaderLib.h>
|
||||
|
||||
#include "FramebufferCache.h"
|
||||
#include "DeferredLightingEffect.h"
|
||||
|
@ -28,21 +29,27 @@
|
|||
#include "overlay3D_vert.h"
|
||||
#include "overlay3D_frag.h"
|
||||
|
||||
#include "drawOpaqueStencil_frag.h"
|
||||
|
||||
using namespace render;
|
||||
|
||||
void SetupDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
|
||||
RenderArgs* args = renderContext->args;
|
||||
gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
|
||||
|
||||
auto primaryFboStencil = DependencyManager::get<FramebufferCache>()->getPrimaryFramebufferStencilColor();
|
||||
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebufferDepthColor();
|
||||
|
||||
batch.enableStereo(false);
|
||||
batch.setFramebuffer(nullptr);
|
||||
batch.setFramebuffer(primaryFbo);
|
||||
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setStateScissorRect(args->_viewport);
|
||||
|
||||
batch.setFramebuffer(primaryFboStencil);
|
||||
batch.clearFramebuffer(
|
||||
gpu::Framebuffer::BUFFER_STENCIL,
|
||||
vec4(vec3(0), 1), 1.0, 0.0, true);
|
||||
|
||||
batch.setFramebuffer(primaryFbo);
|
||||
batch.clearFramebuffer(
|
||||
gpu::Framebuffer::BUFFER_COLOR0 |
|
||||
gpu::Framebuffer::BUFFER_DEPTH,
|
||||
|
@ -65,7 +72,6 @@ void ResolveDeferred::run(const SceneContextPointer& sceneContext, const RenderC
|
|||
|
||||
RenderDeferredTask::RenderDeferredTask() : Task() {
|
||||
_jobs.push_back(Job(new SetupDeferred::JobModel("SetupFramebuffer")));
|
||||
_jobs.push_back(Job(new DrawBackground::JobModel("DrawBackground")));
|
||||
|
||||
_jobs.push_back(Job(new PrepareDeferred::JobModel("PrepareDeferred")));
|
||||
_jobs.push_back(Job(new FetchItems::JobModel("FetchOpaque",
|
||||
|
@ -79,7 +85,10 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
|
|||
_jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortOpaque", _jobs.back().getOutput())));
|
||||
auto& renderedOpaques = _jobs.back().getOutput();
|
||||
_jobs.push_back(Job(new DrawOpaqueDeferred::JobModel("DrawOpaqueDeferred", _jobs.back().getOutput())));
|
||||
|
||||
|
||||
_jobs.push_back(Job(new DrawStencilDeferred::JobModel("DrawOpaqueStencil")));
|
||||
_jobs.push_back(Job(new DrawBackgroundDeferred::JobModel("DrawBackgroundDeferred")));
|
||||
|
||||
_jobs.push_back(Job(new DrawLight::JobModel("DrawLight")));
|
||||
_jobs.push_back(Job(new RenderDeferred::JobModel("RenderDeferred")));
|
||||
_jobs.push_back(Job(new ResolveDeferred::JobModel("ResolveDeferred")));
|
||||
|
@ -226,7 +235,9 @@ const gpu::PipelinePointer& DrawOverlay3D::getOpaquePipeline() {
|
|||
auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
|
||||
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
state->setDepthTest(false);
|
||||
// additive blending
|
||||
state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
|
||||
_opaquePipeline.reset(gpu::Pipeline::create(program, state));
|
||||
}
|
||||
|
@ -289,3 +300,95 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon
|
|||
args->_whiteTexture.reset();
|
||||
}
|
||||
}
|
||||
|
||||
gpu::PipelinePointer DrawStencilDeferred::_opaquePipeline;
|
||||
const gpu::PipelinePointer& DrawStencilDeferred::getOpaquePipeline() {
|
||||
if (!_opaquePipeline) {
|
||||
const gpu::int8 STENCIL_OPAQUE = 1;
|
||||
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
|
||||
auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(drawOpaqueStencil_frag)));
|
||||
auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
|
||||
|
||||
|
||||
gpu::Shader::makeProgram((*program));
|
||||
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_OPAQUE, 0xFF, gpu::ALWAYS, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_REPLACE));
|
||||
state->setColorWriteMask(0);
|
||||
|
||||
_opaquePipeline.reset(gpu::Pipeline::create(program, state));
|
||||
}
|
||||
return _opaquePipeline;
|
||||
}
|
||||
|
||||
void DrawStencilDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->_viewFrustum);
|
||||
|
||||
// from the touched pixel generate the stencil buffer
|
||||
RenderArgs* args = renderContext->args;
|
||||
doInBatch(args->_context, [=](gpu::Batch& batch) {
|
||||
args->_batch = &batch;
|
||||
|
||||
auto primaryFboColorDepthStencil = DependencyManager::get<FramebufferCache>()->getPrimaryFramebufferStencilColor();
|
||||
auto primaryDepth = DependencyManager::get<FramebufferCache>()->getPrimaryDepthTexture();
|
||||
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setFramebuffer(primaryFboColorDepthStencil);
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setStateScissorRect(args->_viewport);
|
||||
|
||||
batch.setPipeline(getOpaquePipeline());
|
||||
batch.setResourceTexture(0, primaryDepth);
|
||||
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
batch.setResourceTexture(0, nullptr);
|
||||
|
||||
});
|
||||
args->_batch = nullptr;
|
||||
}
|
||||
|
||||
void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->_viewFrustum);
|
||||
|
||||
// render backgrounds
|
||||
auto& scene = sceneContext->_scene;
|
||||
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::background());
|
||||
|
||||
|
||||
ItemIDsBounds inItems;
|
||||
inItems.reserve(items.size());
|
||||
for (auto id : items) {
|
||||
inItems.emplace_back(id);
|
||||
}
|
||||
RenderArgs* args = renderContext->args;
|
||||
doInBatch(args->_context, [=](gpu::Batch& batch) {
|
||||
args->_batch = &batch;
|
||||
|
||||
auto primaryFboColorStencil = DependencyManager::get<FramebufferCache>()->getPrimaryFramebufferStencilColor();
|
||||
auto primaryFboFull = DependencyManager::get<FramebufferCache>()->getPrimaryFramebuffer();
|
||||
|
||||
batch.enableSkybox(true);
|
||||
|
||||
batch.setFramebuffer(primaryFboColorStencil);
|
||||
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setStateScissorRect(args->_viewport);
|
||||
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
args->_viewFrustum->evalProjectionMatrix(projMat);
|
||||
args->_viewFrustum->evalViewTransform(viewMat);
|
||||
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewMat);
|
||||
|
||||
renderItems(sceneContext, renderContext, inItems);
|
||||
|
||||
batch.setFramebuffer(primaryFboFull);
|
||||
|
||||
});
|
||||
args->_batch = nullptr;
|
||||
}
|
|
@ -59,6 +59,23 @@ public:
|
|||
typedef render::Job::ModelI<DrawTransparentDeferred, render::ItemIDsBounds> JobModel;
|
||||
};
|
||||
|
||||
class DrawStencilDeferred {
|
||||
static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable
|
||||
public:
|
||||
static const gpu::PipelinePointer& getOpaquePipeline();
|
||||
|
||||
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
|
||||
|
||||
typedef render::Job::Model<DrawStencilDeferred> JobModel;
|
||||
};
|
||||
|
||||
class DrawBackgroundDeferred {
|
||||
public:
|
||||
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
|
||||
|
||||
typedef render::Job::Model<DrawBackgroundDeferred> JobModel;
|
||||
};
|
||||
|
||||
class DrawOverlay3D {
|
||||
static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable
|
||||
public:
|
||||
|
|
24
libraries/render-utils/src/drawOpaqueStencil.slf
Normal file
24
libraries/render-utils/src/drawOpaqueStencil.slf
Normal file
|
@ -0,0 +1,24 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// drawOpaqueStencil.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Sam Gateau on 9/29/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
|
||||
//
|
||||
|
||||
in vec2 varTexCoord0;
|
||||
|
||||
uniform sampler2D depthTexture;
|
||||
|
||||
void main(void) {
|
||||
float depth = texture(depthTexture, varTexCoord0.xy).r;
|
||||
if (depth >= 1.0) {
|
||||
discard;
|
||||
}
|
||||
}
|
|
@ -257,6 +257,7 @@ class DrawBackground {
|
|||
public:
|
||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
|
||||
|
||||
|
||||
typedef Job::Model<DrawBackground> JobModel;
|
||||
};
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@ const vec3& Vectors::RIGHT = Vectors::UNIT_X;
|
|||
const vec3& Vectors::UP = Vectors::UNIT_Y;
|
||||
const vec3& Vectors::FRONT = Vectors::UNIT_NEG_Z;
|
||||
|
||||
const quat Quaternions::IDENTITY{ 1.0f, 0.0f, 0.0f, 0.0f };
|
||||
|
||||
// Safe version of glm::mix; based on the code in Nick Bobick's article,
|
||||
// http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde,
|
||||
// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java)
|
||||
|
|
|
@ -53,6 +53,12 @@ const glm::vec3 IDENTITY_FRONT = glm::vec3( 0.0f, 0.0f,-1.0f);
|
|||
|
||||
glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha);
|
||||
|
||||
|
||||
class Quaternions {
|
||||
public:
|
||||
static const quat IDENTITY;
|
||||
};
|
||||
|
||||
class Vectors {
|
||||
public:
|
||||
static const vec3 UNIT_X;
|
||||
|
|
834
unpublishedScripts/masterReset.js
Normal file
834
unpublishedScripts/masterReset.js
Normal file
|
@ -0,0 +1,834 @@
|
|||
// masterReset.js
|
||||
// Created by Eric Levin on 9/23/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */
|
||||
//per script
|
||||
/*global deleteAllToys, createAllToys, createGates, createPingPongBallGun, createFire, createPottedPlant, createCombinedArmChair, createBasketballHoop, createBasketBall, createSprayCan, createDoll, createWand, createDice, createCat, deleteAllToys, createFlashlight, createBlocks, createMagballs, createLightSwitches */
|
||||
var utilitiesScript = Script.resolvePath("../examples/libraries/utils.js");
|
||||
Script.include(utilitiesScript);
|
||||
|
||||
var resetKey = "resetMe";
|
||||
var GRABBABLE_DATA_KEY = "grabbableKey";
|
||||
|
||||
var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
|
||||
var shouldDeleteOnEndScript = false;
|
||||
|
||||
//Before creating anything, first search a radius and delete all the things that should be deleted
|
||||
deleteAllToys();
|
||||
createAllToys();
|
||||
|
||||
function createAllToys() {
|
||||
createBlocks({
|
||||
x: 548.3,
|
||||
y: 495.55,
|
||||
z: 504.4
|
||||
});
|
||||
|
||||
createSprayCan({
|
||||
x: 549.7,
|
||||
y: 495.6,
|
||||
z: 503.91
|
||||
});
|
||||
|
||||
createBasketBall({
|
||||
x: 547.73,
|
||||
y: 495.5,
|
||||
z: 505.47
|
||||
});
|
||||
|
||||
createDoll({
|
||||
x: 546.67,
|
||||
y: 495.41,
|
||||
z: 505.09
|
||||
});
|
||||
|
||||
createWand({
|
||||
x: 546.71,
|
||||
y: 495.55,
|
||||
z: 506.15
|
||||
});
|
||||
|
||||
createDice();
|
||||
|
||||
createFlashlight({
|
||||
x: 545.72,
|
||||
y: 495.41,
|
||||
z: 505.78
|
||||
});
|
||||
|
||||
createCat({
|
||||
x: 551.09,
|
||||
y: 494.98,
|
||||
z: 503.49
|
||||
});
|
||||
|
||||
|
||||
createCombinedArmChair({
|
||||
x: 549.29,
|
||||
y: 495.05,
|
||||
z: 508.22
|
||||
});
|
||||
|
||||
createPottedPlant({
|
||||
x: 554.26,
|
||||
y: 495.23,
|
||||
z: 504.53
|
||||
});
|
||||
|
||||
createPingPongBallGun();
|
||||
|
||||
createBasketballHoop();
|
||||
|
||||
createGates();
|
||||
|
||||
createFire();
|
||||
// //Handles toggling of all sconce lights
|
||||
createLightSwitches();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
function deleteAllToys() {
|
||||
var entities = Entities.findEntities(MyAvatar.position, 100);
|
||||
|
||||
entities.forEach(function (entity) {
|
||||
//params: customKey, id, defaultValue
|
||||
var shouldReset = getEntityCustomData(resetKey, entity, {}).resetMe;
|
||||
if (shouldReset === true) {
|
||||
Entities.deleteEntity(entity);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createFire() {
|
||||
|
||||
|
||||
var myOrientation = Quat.fromPitchYawRollDegrees(-90, 0, 0.0);
|
||||
|
||||
var animationSettings = JSON.stringify({
|
||||
fps: 30,
|
||||
running: true,
|
||||
loop: true,
|
||||
firstFrame: 1,
|
||||
lastFrame: 10000
|
||||
});
|
||||
|
||||
|
||||
var fire = Entities.addEntity({
|
||||
type: "ParticleEffect",
|
||||
name: "fire",
|
||||
animationSettings: animationSettings,
|
||||
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
|
||||
position: {
|
||||
x: 551.45,
|
||||
y: 494.82,
|
||||
z: 502.05
|
||||
},
|
||||
emitRate: 100,
|
||||
colorStart: {
|
||||
red: 70,
|
||||
green: 70,
|
||||
blue: 137
|
||||
},
|
||||
color: {
|
||||
red: 200,
|
||||
green: 99,
|
||||
blue: 42
|
||||
},
|
||||
colorFinish: {
|
||||
red: 255,
|
||||
green: 99,
|
||||
blue: 32
|
||||
},
|
||||
radiusSpread: 0.01,
|
||||
radiusStart: 0.02,
|
||||
radiusEnd: 0.001,
|
||||
particleRadius: 0.05,
|
||||
radiusFinish: 0.0,
|
||||
emitOrientation: myOrientation,
|
||||
emitSpeed: 0.3,
|
||||
speedSpread: 0.1,
|
||||
alphaStart: 0.05,
|
||||
alpha: 0.1,
|
||||
alphaFinish: 0.05,
|
||||
emitDimensions: {
|
||||
x: 1,
|
||||
y: 1,
|
||||
z: 0.1
|
||||
},
|
||||
polarFinish: 0.1,
|
||||
emitAcceleration: {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0
|
||||
},
|
||||
accelerationSpread: {
|
||||
x: 0.1,
|
||||
y: 0.01,
|
||||
z: 0.1
|
||||
},
|
||||
lifespan: 1
|
||||
});
|
||||
|
||||
|
||||
setEntityCustomData(resetKey, fire, {
|
||||
resetMe: true
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function createCat(position) {
|
||||
var scriptURL = Script.resolvePath("../examples/toys/cat.js");
|
||||
var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/Dark_Cat.fbx";
|
||||
var animationURL = "http://hifi-public.s3.amazonaws.com/ryan/sleeping.fbx";
|
||||
var animationSettings = JSON.stringify({
|
||||
running: true,
|
||||
});
|
||||
var cat = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: modelURL,
|
||||
name: "cat",
|
||||
script: scriptURL,
|
||||
animationURL: animationURL,
|
||||
animationSettings: animationSettings,
|
||||
position: position,
|
||||
rotation: {
|
||||
w: 0.35020983219146729,
|
||||
x: -4.57763671875e-05,
|
||||
y: 0.93664455413818359,
|
||||
z: -1.52587890625e-05
|
||||
},
|
||||
dimensions: {
|
||||
x: 0.15723302960395813,
|
||||
y: 0.50762706995010376,
|
||||
z: 0.90716040134429932
|
||||
},
|
||||
});
|
||||
|
||||
setEntityCustomData(resetKey, cat, {
|
||||
resetMe: true
|
||||
});
|
||||
}
|
||||
|
||||
function createFlashlight(position) {
|
||||
var scriptURL = Script.resolvePath('../examples/toys/flashlight/flashlight.js');
|
||||
var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx";
|
||||
|
||||
var flashlight = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: modelURL,
|
||||
name: "flashlight",
|
||||
script: scriptURL,
|
||||
position: position,
|
||||
dimensions: {
|
||||
x: 0.08,
|
||||
y: 0.30,
|
||||
z: 0.08
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -3.5,
|
||||
z: 0
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -0.01,
|
||||
z: 0
|
||||
},
|
||||
shapeType: 'box',
|
||||
});
|
||||
|
||||
setEntityCustomData(resetKey, flashlight, {
|
||||
resetMe: true
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function createLightSwitches() {
|
||||
var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/lightswitch.fbx?v1";
|
||||
var scriptURL = Script.resolvePath("../examples/toys/lightSwitchHall.js");
|
||||
|
||||
var lightSwitchHall = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: modelURL,
|
||||
name: "Light Switch Hall",
|
||||
script: scriptURL,
|
||||
position: {
|
||||
x: 543.27764892578125,
|
||||
y: 495.67999267578125,
|
||||
z: 511.00564575195312
|
||||
},
|
||||
rotation: {
|
||||
w: 0.63280689716339111,
|
||||
x: 0.63280689716339111,
|
||||
y: -0.31551080942153931,
|
||||
z: 0.31548023223876953
|
||||
},
|
||||
dimensions: {
|
||||
x: 0.10546875,
|
||||
y: 0.032372996211051941,
|
||||
z: 0.16242524981498718
|
||||
}
|
||||
});
|
||||
|
||||
setEntityCustomData(resetKey, lightSwitchHall, {
|
||||
resetMe: true
|
||||
});
|
||||
|
||||
scriptURL = Script.resolvePath("../examples/toys/lightSwitchGarage.js");
|
||||
|
||||
var lightSwitchGarage = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: modelURL,
|
||||
name: "Light Switch Garage",
|
||||
script: scriptURL,
|
||||
position: {
|
||||
x: 545.62,
|
||||
y: 495.68,
|
||||
z: 500.21
|
||||
},
|
||||
rotation: {
|
||||
w: 0.20082402229309082,
|
||||
x: 0.20082402229309082,
|
||||
y: -0.67800414562225342,
|
||||
z: 0.67797362804412842
|
||||
},
|
||||
dimensions: {
|
||||
x: 0.10546875,
|
||||
y: 0.032372996211051941,
|
||||
z: 0.16242524981498718
|
||||
}
|
||||
});
|
||||
|
||||
setEntityCustomData(resetKey, lightSwitchGarage, {
|
||||
resetMe: true
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function createDice() {
|
||||
var diceProps = {
|
||||
type: "Model",
|
||||
modelURL: "http://s3.amazonaws.com/hifi-public/models/props/Dice/goldDie.fbx",
|
||||
collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav",
|
||||
name: "dice",
|
||||
position: {
|
||||
x: 541,
|
||||
y: 494.96,
|
||||
z: 509.1
|
||||
},
|
||||
dimensions: {
|
||||
x: 0.09,
|
||||
y: 0.09,
|
||||
z: 0.09
|
||||
},
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -3.5,
|
||||
z: 0
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -0.01,
|
||||
z: 0
|
||||
},
|
||||
shapeType: "box",
|
||||
collisionsWillMove: true
|
||||
};
|
||||
var dice1 = Entities.addEntity(diceProps);
|
||||
|
||||
diceProps.position = {
|
||||
x: 541.05,
|
||||
y: 494.96,
|
||||
z: 509.0
|
||||
};
|
||||
|
||||
var dice2 = Entities.addEntity(diceProps);
|
||||
|
||||
setEntityCustomData(resetKey, dice1, {
|
||||
resetMe: true
|
||||
});
|
||||
|
||||
setEntityCustomData(resetKey, dice2, {
|
||||
resetMe: true
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function createGates() {
|
||||
var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/ryan/fence.fbx';
|
||||
|
||||
var rotation1 = Quat.fromPitchYawRollDegrees(0, 36, 0);
|
||||
|
||||
var gate1 = Entities.addEntity({
|
||||
name: 'Back Door Gate',
|
||||
type: 'Model',
|
||||
shapeType: 'box',
|
||||
modelURL: MODEL_URL,
|
||||
position: {
|
||||
x: 546.52,
|
||||
y: 494.76,
|
||||
z: 498.87
|
||||
},
|
||||
dimensions: {
|
||||
x: 1.42,
|
||||
y: 1.13,
|
||||
z: 0.25
|
||||
},
|
||||
rotation: rotation1,
|
||||
collisionsWillMove: true,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -50,
|
||||
z: 0
|
||||
},
|
||||
linearDamping: 1,
|
||||
angularDamping: 10,
|
||||
mass: 10,
|
||||
|
||||
});
|
||||
|
||||
setEntityCustomData(resetKey, gate1, {
|
||||
resetMe: true
|
||||
});
|
||||
|
||||
setEntityCustomData(GRABBABLE_DATA_KEY, gate1, {
|
||||
grabbable: false
|
||||
});
|
||||
|
||||
var rotation2 = Quat.fromPitchYawRollDegrees(0, -16, 0);
|
||||
var gate2 = Entities.addEntity({
|
||||
name: 'Front Door Fence',
|
||||
type: 'Model',
|
||||
modelURL: MODEL_URL,
|
||||
shapeType: 'box',
|
||||
position: {
|
||||
x: 531.15,
|
||||
y: 495.11,
|
||||
z: 520.20
|
||||
},
|
||||
dimensions: {
|
||||
x: 1.42,
|
||||
y: 1.13,
|
||||
z: 0.2
|
||||
},
|
||||
rotation: rotation2,
|
||||
collisionsWillMove: true,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -100,
|
||||
z: 0
|
||||
},
|
||||
linearDamping: 1,
|
||||
angularDamping: 10,
|
||||
mass: 10,
|
||||
});
|
||||
|
||||
setEntityCustomData(resetKey, gate2, {
|
||||
resetMe: true
|
||||
});
|
||||
|
||||
setEntityCustomData(GRABBABLE_DATA_KEY, gate2, {
|
||||
grabbable: false
|
||||
});
|
||||
}
|
||||
|
||||
function createPingPongBallGun() {
|
||||
var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx';
|
||||
var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj';
|
||||
var scriptURL = Script.resolvePath('../examples/toys/ping_pong_gun/pingPongGun.js');
|
||||
|
||||
var position = {
|
||||
x: 548.6,
|
||||
y: 495.4,
|
||||
z: 503.39
|
||||
};
|
||||
|
||||
var rotation = Quat.fromPitchYawRollDegrees(0, 36, 0);
|
||||
|
||||
var pingPongGun = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: MODEL_URL,
|
||||
shapeType: 'compound',
|
||||
compoundShapeURL: COLLISION_HULL_URL,
|
||||
script: scriptURL,
|
||||
position: position,
|
||||
rotation: rotation,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -9.8,
|
||||
z: 0
|
||||
},
|
||||
dimensions: {
|
||||
x: 0.67,
|
||||
y: 0.14,
|
||||
z: 0.09
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
});
|
||||
|
||||
setEntityCustomData(resetKey, pingPongGun, {
|
||||
resetMe: true
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
function createBasketballHoop() {
|
||||
var position = {
|
||||
x: 539.23,
|
||||
y: 496.13,
|
||||
z: 475.89
|
||||
};
|
||||
var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0);
|
||||
|
||||
var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx";
|
||||
var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj";
|
||||
|
||||
var hoop = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: hoopURL,
|
||||
position: position,
|
||||
rotation: rotation,
|
||||
shapeType: 'compound',
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -9.8,
|
||||
z: 0
|
||||
},
|
||||
dimensions: {
|
||||
x: 1.89,
|
||||
y: 3.99,
|
||||
z: 3.79
|
||||
},
|
||||
compoundShapeURL: hoopCollisionHullURL
|
||||
});
|
||||
|
||||
setEntityCustomData(resetKey, hoop, {
|
||||
resetMe: true
|
||||
});
|
||||
|
||||
setEntityCustomData(GRABBABLE_DATA_KEY, hoop, {
|
||||
grabbable: false
|
||||
});
|
||||
}
|
||||
|
||||
function createWand(position) {
|
||||
var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx';
|
||||
var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj';
|
||||
var scriptURL = Script.resolvePath("../examples/toys/bubblewand/wand.js");
|
||||
|
||||
var entity = Entities.addEntity({
|
||||
name: 'Bubble Wand',
|
||||
type: "Model",
|
||||
modelURL: WAND_MODEL,
|
||||
position: position,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -9.8,
|
||||
z: 0
|
||||
},
|
||||
dimensions: {
|
||||
x: 0.05,
|
||||
y: 0.25,
|
||||
z: 0.05
|
||||
},
|
||||
//must be enabled to be grabbable in the physics engine
|
||||
shapeType: 'compound',
|
||||
collisionsWillMove: true,
|
||||
compoundShapeURL: WAND_COLLISION_SHAPE,
|
||||
//Look into why bubble wand is going through table when gravity is enabled
|
||||
// gravity: {x: 0, y: -3.5, z: 0},
|
||||
// velocity: {x: 0, y: -0.01, z:0},
|
||||
script: scriptURL
|
||||
});
|
||||
|
||||
setEntityCustomData(resetKey, entity, {
|
||||
resetMe: true
|
||||
});
|
||||
}
|
||||
|
||||
function createBasketBall(position) {
|
||||
|
||||
var modelURL = "http://s3.amazonaws.com/hifi-public/models/content/basketball2.fbx";
|
||||
|
||||
var entity = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: modelURL,
|
||||
position: position,
|
||||
collisionsWillMove: true,
|
||||
shapeType: "sphere",
|
||||
name: "basketball",
|
||||
dimensions: {
|
||||
x: 0.25,
|
||||
y: 0.26,
|
||||
z: 0.25
|
||||
},
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -7,
|
||||
z: 0
|
||||
},
|
||||
restitution: 10,
|
||||
linearDamping: 0.0,
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -0.01,
|
||||
z: 0
|
||||
},
|
||||
collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/basketball/basketball.wav"
|
||||
});
|
||||
|
||||
setEntityCustomData(resetKey, entity, {
|
||||
resetMe: true
|
||||
});
|
||||
}
|
||||
|
||||
function createDoll(position) {
|
||||
var modelURL = "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx";
|
||||
var scriptURL = Script.resolvePath("../examples/toys/doll/doll.js");
|
||||
|
||||
var naturalDimensions = {
|
||||
x: 1.63,
|
||||
y: 1.67,
|
||||
z: 0.26
|
||||
};
|
||||
var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15);
|
||||
var entity = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "doll",
|
||||
modelURL: modelURL,
|
||||
script: scriptURL,
|
||||
position: position,
|
||||
shapeType: 'box',
|
||||
dimensions: desiredDimensions,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -5,
|
||||
z: 0
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -0.1,
|
||||
z: 0
|
||||
},
|
||||
collisionsWillMove: true
|
||||
});
|
||||
|
||||
setEntityCustomData(resetKey, entity, {
|
||||
resetMe: true
|
||||
});
|
||||
}
|
||||
|
||||
function createSprayCan(position) {
|
||||
var scriptURL = Script.resolvePath("../examples/toys/sprayPaintCan.js");
|
||||
var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx";
|
||||
|
||||
var entity = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "spraycan",
|
||||
modelURL: modelURL,
|
||||
position: position,
|
||||
dimensions: {
|
||||
x: 0.07,
|
||||
y: 0.17,
|
||||
z: 0.07
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
shapeType: 'box',
|
||||
script: scriptURL,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -0.5,
|
||||
z: 0
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -1,
|
||||
z: 0
|
||||
}
|
||||
});
|
||||
|
||||
setEntityCustomData(resetKey, entity, {
|
||||
resetMe: true
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function createPottedPlant(position) {
|
||||
var modelURL = "http://hifi-public.s3.amazonaws.com/models/potted_plant/potted_plant.fbx";
|
||||
|
||||
var entity = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "Potted Plant",
|
||||
modelURL: modelURL,
|
||||
position: position,
|
||||
dimensions: {
|
||||
x: 1.10,
|
||||
y: 2.18,
|
||||
z: 1.07
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
shapeType: 'box',
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -9.8,
|
||||
z: 0
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
linearDamping: 0.4
|
||||
});
|
||||
|
||||
setEntityCustomData(resetKey, entity, {
|
||||
resetMe: true
|
||||
});
|
||||
|
||||
|
||||
setEntityCustomData(GRABBABLE_DATA_KEY, entity, {
|
||||
grabbable: false
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function createCombinedArmChair(position) {
|
||||
var modelURL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/combined_chair.fbx";
|
||||
var RED_ARM_CHAIR_COLLISION_HULL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/red_arm_chair_collision_hull.obj";
|
||||
|
||||
var rotation = Quat.fromPitchYawRollDegrees(0, -143, 0);
|
||||
|
||||
var entity = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "Red Arm Chair",
|
||||
modelURL: modelURL,
|
||||
shapeType: 'compound',
|
||||
compoundShapeURL: RED_ARM_CHAIR_COLLISION_HULL,
|
||||
position: position,
|
||||
rotation: rotation,
|
||||
dimensions: {
|
||||
x: 1.26,
|
||||
y: 1.56,
|
||||
z: 1.35
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -0.8,
|
||||
z: 0
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
linearDamping: 0.2
|
||||
});
|
||||
|
||||
setEntityCustomData(resetKey, entity, {
|
||||
resetMe: true
|
||||
});
|
||||
|
||||
setEntityCustomData(GRABBABLE_DATA_KEY, entity, {
|
||||
grabbable: false
|
||||
});
|
||||
}
|
||||
|
||||
function createBlocks(position) {
|
||||
var baseURL = HIFI_PUBLIC_BUCKET + "models/content/planky/";
|
||||
var collisionSoundURL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/ToyWoodBlock.L.wav";
|
||||
var NUM_BLOCKS_PER_COLOR = 4;
|
||||
var i, j;
|
||||
|
||||
var blockTypes = [{
|
||||
url: "planky_blue.fbx",
|
||||
dimensions: {
|
||||
x: 0.05,
|
||||
y: 0.05,
|
||||
z: 0.25
|
||||
}
|
||||
}, {
|
||||
url: "planky_green.fbx",
|
||||
dimensions: {
|
||||
x: 0.1,
|
||||
y: 0.1,
|
||||
z: 0.25
|
||||
}
|
||||
}, {
|
||||
url: "planky_natural.fbx",
|
||||
dimensions: {
|
||||
x: 0.05,
|
||||
y: 0.05,
|
||||
z: 0.05
|
||||
}
|
||||
}, {
|
||||
url: "planky_yellow.fbx",
|
||||
dimensions: {
|
||||
x: 0.03,
|
||||
y: 0.05,
|
||||
z: 0.25
|
||||
}
|
||||
}, {
|
||||
url: "planky_red.fbx",
|
||||
dimensions: {
|
||||
x: 0.1,
|
||||
y: 0.05,
|
||||
z: 0.25
|
||||
}
|
||||
}, ];
|
||||
|
||||
var modelURL, entity;
|
||||
for (i = 0; i < blockTypes.length; i++) {
|
||||
for (j = 0; j < NUM_BLOCKS_PER_COLOR; j++) {
|
||||
modelURL = baseURL + blockTypes[i].url;
|
||||
entity = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: modelURL,
|
||||
position: Vec3.sum(position, {
|
||||
x: j / 10,
|
||||
y: i / 10,
|
||||
z: 0
|
||||
}),
|
||||
shapeType: 'box',
|
||||
name: "block",
|
||||
dimensions: blockTypes[i].dimensions,
|
||||
collisionsWillMove: true,
|
||||
collisionSoundURL: collisionSoundURL,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -2.5,
|
||||
z: 0
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -0.01,
|
||||
z: 0
|
||||
}
|
||||
});
|
||||
|
||||
//customKey, id, data
|
||||
setEntityCustomData(resetKey, entity, {
|
||||
resetMe: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
deleteAllToys();
|
||||
}
|
||||
|
||||
if (shouldDeleteOnEndScript) {
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
}
|
Loading…
Reference in a new issue