mirror of
https://github.com/lubosz/overte.git
synced 2025-04-10 00:04:18 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into fix/skybox-tex
This commit is contained in:
commit
40378920b1
33 changed files with 1522 additions and 545 deletions
|
@ -46,6 +46,8 @@ function(AUTOSCRIBE_SHADER SHADER_FILE)
|
|||
set(SHADER_TARGET ${SHADER_TARGET}_frag.h)
|
||||
endif()
|
||||
|
||||
set(SHADER_TARGET "${SHADERS_DIR}/${SHADER_TARGET}")
|
||||
|
||||
# Target dependant Custom rule on the SHADER_FILE
|
||||
if (APPLE)
|
||||
set(GLPROFILE MAC_GL)
|
||||
|
@ -87,10 +89,14 @@ macro(AUTOSCRIBE_SHADER_LIB)
|
|||
file(GLOB_RECURSE SHADER_INCLUDE_FILES src/*.slh)
|
||||
file(GLOB_RECURSE SHADER_SOURCE_FILES src/*.slv src/*.slf)
|
||||
|
||||
#make the shader folder
|
||||
set(SHADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/shaders/${TARGET_NAME}")
|
||||
file(MAKE_DIRECTORY ${SHADERS_DIR})
|
||||
|
||||
#message(${SHADER_INCLUDE_FILES})
|
||||
foreach(SHADER_FILE ${SHADER_SOURCE_FILES})
|
||||
AUTOSCRIBE_SHADER(${SHADER_FILE} ${SHADER_INCLUDE_FILES})
|
||||
file(TO_CMAKE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${AUTOSCRIBE_SHADER_RETURN}" AUTOSCRIBE_GENERATED_FILE)
|
||||
file(TO_CMAKE_PATH "${AUTOSCRIBE_SHADER_RETURN}" AUTOSCRIBE_GENERATED_FILE)
|
||||
list(APPEND AUTOSCRIBE_SHADER_SRC ${AUTOSCRIBE_GENERATED_FILE})
|
||||
endforeach()
|
||||
#message(${AUTOSCRIBE_SHADER_SRC})
|
||||
|
@ -105,4 +111,7 @@ macro(AUTOSCRIBE_SHADER_LIB)
|
|||
list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${SHADER_SOURCE_FILES})
|
||||
list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${AUTOSCRIBE_SHADER_SRC})
|
||||
|
||||
# Link library shaders, if they exist
|
||||
include_directories("${SHADERS_DIR}")
|
||||
|
||||
endmacro()
|
||||
|
|
|
@ -19,20 +19,14 @@ macro(LINK_HIFI_LIBRARIES)
|
|||
endif ()
|
||||
|
||||
include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
|
||||
include_directories("${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}/shaders")
|
||||
|
||||
add_dependencies(${TARGET_NAME} ${HIFI_LIBRARY})
|
||||
|
||||
# link the actual library - it is static so don't bubble it up
|
||||
target_link_libraries(${TARGET_NAME} ${HIFI_LIBRARY})
|
||||
|
||||
# ask the library what its include dependencies are and link them
|
||||
get_target_property(LINKED_TARGET_DEPENDENCY_INCLUDES ${HIFI_LIBRARY} DEPENDENCY_INCLUDES)
|
||||
|
||||
if(LINKED_TARGET_DEPENDENCY_INCLUDES)
|
||||
list(APPEND ${TARGET_NAME}_DEPENDENCY_INCLUDES ${LINKED_TARGET_DEPENDENCY_INCLUDES})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
setup_memory_debugger()
|
||||
|
||||
endmacro(LINK_HIFI_LIBRARIES)
|
||||
endmacro(LINK_HIFI_LIBRARIES)
|
||||
|
|
|
@ -13,7 +13,6 @@ Script.load("progress.js");
|
|||
Script.load("edit.js");
|
||||
Script.load("marketplace.js");
|
||||
Script.load("selectAudioDevice.js");
|
||||
Script.load("inspect.js");
|
||||
Script.load("notifications.js");
|
||||
Script.load("users.js");
|
||||
Script.load("controllers/handControllerGrab.js");
|
||||
|
|
|
@ -1,285 +0,0 @@
|
|||
//
|
||||
// hmdControls.js
|
||||
// examples
|
||||
//
|
||||
// Created by Sam Gondelman on 6/17/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
|
||||
//
|
||||
|
||||
var MOVE_DISTANCE = 2.0;
|
||||
var PITCH_INCREMENT = 0.5; // degrees
|
||||
var pitchChange = 0; // degrees
|
||||
var YAW_INCREMENT = 0.5; // degrees
|
||||
var VR_YAW_INCREMENT = 15.0; // degrees
|
||||
var yawChange = 0;
|
||||
var BOOM_SPEED = 0.5;
|
||||
var THRESHOLD = 0.2;
|
||||
|
||||
var CAMERA_UPDATE_TIME = 0.5;
|
||||
var yawTimer = CAMERA_UPDATE_TIME;
|
||||
|
||||
var shifted = false;
|
||||
var SHIFT_UPDATE_TIME = 0.5;
|
||||
var shiftTimer = SHIFT_UPDATE_TIME;
|
||||
var SHIFT_MAG = 4.0;
|
||||
|
||||
var warpActive = false;
|
||||
var WARP_UPDATE_TIME = .5;
|
||||
var warpTimer = WARP_UPDATE_TIME;
|
||||
|
||||
var warpPosition = { x: 0, y: 0, z: 0 };
|
||||
|
||||
var WARP_SPHERE_SIZE = 1;
|
||||
var warpSphere = Overlays.addOverlay("sphere", {
|
||||
position: { x: 0, y: 0, z: 0 },
|
||||
size: WARP_SPHERE_SIZE,
|
||||
color: { red: 0, green: 255, blue: 0 },
|
||||
alpha: 1.0,
|
||||
solid: true,
|
||||
visible: false,
|
||||
});
|
||||
|
||||
var WARP_LINE_HEIGHT = 10;
|
||||
var warpLine = Overlays.addOverlay("line3d", {
|
||||
start: { x: 0, y: 0, z:0 },
|
||||
end: { x: 0, y: 0, z: 0 },
|
||||
color: { red: 0, green: 255, blue: 255},
|
||||
alpha: 1,
|
||||
lineWidth: 5,
|
||||
visible: false,
|
||||
});
|
||||
|
||||
var velocity = { x: 0, y: 0, z: 0 };
|
||||
var VERY_LONG_TIME = 1000000.0;
|
||||
|
||||
var active = HMD.active;
|
||||
var prevVRMode = HMD.active;
|
||||
|
||||
var hmdControls = (function () {
|
||||
|
||||
function onKeyPressEvent(event) {
|
||||
if (event.text == 'g' && event.isMeta) {
|
||||
active = !active;
|
||||
}
|
||||
}
|
||||
|
||||
function findAction(name) {
|
||||
var actions = Controller.getAllActions();
|
||||
for (var i = 0; i < actions.length; i++) {
|
||||
if (actions[i].actionName == name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// If the action isn't found, it will default to the first available action
|
||||
return 0;
|
||||
}
|
||||
|
||||
function onActionEvent(action, state) {
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
if (state < THRESHOLD) {
|
||||
if (action == findAction("YAW_LEFT") || action == findAction("YAW_RIGHT")) {
|
||||
yawTimer = CAMERA_UPDATE_TIME;
|
||||
} else if (action == findAction("PITCH_UP") || action == findAction("PITCH_DOWN")) {
|
||||
pitchTimer = CAMERA_UPDATE_TIME;
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (action) {
|
||||
case findAction("LONGITUDINAL_BACKWARD"):
|
||||
var direction = {x: 0.0, y: 0.0, z:1.0};
|
||||
direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
|
||||
velocity = Vec3.sum(velocity, direction);
|
||||
break;
|
||||
case findAction("LONGITUDINAL_FORWARD"):
|
||||
var direction = {x: 0.0, y: 0.0, z:-1.0};
|
||||
direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
|
||||
velocity = Vec3.sum(velocity, direction);
|
||||
break;
|
||||
case findAction("LATERAL_LEFT"):
|
||||
var direction = {x:-1.0, y: 0.0, z: 0.0}
|
||||
direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
|
||||
velocity = Vec3.sum(velocity, direction);
|
||||
break;
|
||||
case findAction("LATERAL_RIGHT"):
|
||||
var direction = {x:1.0, y: 0.0, z: 0.0};
|
||||
direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
|
||||
velocity = Vec3.sum(velocity, direction);
|
||||
break;
|
||||
case findAction("VERTICAL_DOWN"):
|
||||
var direction = {x: 0.0, y: -1.0, z: 0.0};
|
||||
direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
|
||||
velocity = Vec3.sum(velocity, direction);
|
||||
break;
|
||||
case findAction("VERTICAL_UP"):
|
||||
var direction = {x: 0.0, y: 1.0, z: 0.0};
|
||||
direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
|
||||
velocity = Vec3.sum(velocity, direction);
|
||||
break;
|
||||
case findAction("YAW_LEFT"):
|
||||
if (yawTimer < 0.0 && HMD.active) {
|
||||
yawChange = yawChange + (shifted ? SHIFT_MAG * VR_YAW_INCREMENT : VR_YAW_INCREMENT);
|
||||
yawTimer = CAMERA_UPDATE_TIME;
|
||||
} else if (!HMD.active) {
|
||||
yawChange = yawChange + (shifted ? SHIFT_MAG * YAW_INCREMENT : YAW_INCREMENT);
|
||||
}
|
||||
break;
|
||||
case findAction("YAW_RIGHT"):
|
||||
if (yawTimer < 0.0 && HMD.active) {
|
||||
yawChange = yawChange - (shifted ? SHIFT_MAG * VR_YAW_INCREMENT : VR_YAW_INCREMENT);
|
||||
yawTimer = CAMERA_UPDATE_TIME;
|
||||
} else if (!HMD.active) {
|
||||
yawChange = yawChange - (shifted ? SHIFT_MAG * YAW_INCREMENT : YAW_INCREMENT);
|
||||
}
|
||||
break;
|
||||
case findAction("PITCH_DOWN"):
|
||||
if (!HMD.active) {
|
||||
pitchChange = pitchChange - (shifted ? SHIFT_MAG * PITCH_INCREMENT : PITCH_INCREMENT);
|
||||
}
|
||||
break;
|
||||
case findAction("PITCH_UP"):
|
||||
if (!HMD.active) {
|
||||
pitchChange = pitchChange + (shifted ? SHIFT_MAG * PITCH_INCREMENT : PITCH_INCREMENT);
|
||||
}
|
||||
break;
|
||||
case findAction("SHIFT"): // speed up
|
||||
if (shiftTimer < 0.0) {
|
||||
shifted = !shifted;
|
||||
shiftTimer = SHIFT_UPDATE_TIME;
|
||||
}
|
||||
break;
|
||||
case findAction("ACTION1"): // start/end warp
|
||||
if (warpTimer < 0.0) {
|
||||
warpActive = !warpActive;
|
||||
if (!warpActive) {
|
||||
finishWarp();
|
||||
}
|
||||
warpTimer = WARP_UPDATE_TIME;
|
||||
}
|
||||
break;
|
||||
case findAction("ACTION2"): // cancel warp
|
||||
warpActive = false;
|
||||
Overlays.editOverlay(warpSphere, {
|
||||
visible: false,
|
||||
});
|
||||
Overlays.editOverlay(warpLine, {
|
||||
visible: false,
|
||||
});
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function update(dt) {
|
||||
if (prevVRMode != HMD.active) {
|
||||
active = HMD.active;
|
||||
prevVRMode = HMD.active;
|
||||
}
|
||||
|
||||
if (yawTimer >= 0.0) {
|
||||
yawTimer = yawTimer - dt;
|
||||
}
|
||||
if (shiftTimer >= 0.0) {
|
||||
shiftTimer = shiftTimer - dt;
|
||||
}
|
||||
if (warpTimer >= 0.0) {
|
||||
warpTimer = warpTimer - dt;
|
||||
}
|
||||
|
||||
if (warpActive) {
|
||||
updateWarp();
|
||||
}
|
||||
|
||||
if (active) {
|
||||
Controller.captureActionEvents();
|
||||
|
||||
MyAvatar.bodyYaw = MyAvatar.bodyYaw + yawChange;
|
||||
MyAvatar.headPitch = Math.max(-180, Math.min(180, MyAvatar.headPitch + pitchChange));
|
||||
yawChange = 0;
|
||||
pitchChange = 0;
|
||||
|
||||
MyAvatar.motorVelocity = velocity;
|
||||
MyAvatar.motorTimescale = 0.0;
|
||||
velocity = { x: 0, y: 0, z: 0 };
|
||||
} else {
|
||||
Controller.releaseActionEvents();
|
||||
yawChange = 0;
|
||||
pitchChange = 0;
|
||||
MyAvatar.motorVelocity = {x:0.0, y:0.0, z:0.0}
|
||||
MyAvatar.motorTimescale = VERY_LONG_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
function updateWarp() {
|
||||
var look = Quat.getFront(Camera.getOrientation());
|
||||
var pitch = Math.asin(look.y);
|
||||
|
||||
// Get relative to looking straight down
|
||||
pitch += Math.PI / 2;
|
||||
|
||||
// Scale up
|
||||
pitch *= 2;
|
||||
var distance = pitch * pitch * pitch;
|
||||
|
||||
var warpDirection = Vec3.normalize({ x: look.x, y: 0, z: look.z });
|
||||
warpPosition = Vec3.multiply(warpDirection, distance);
|
||||
warpPosition = Vec3.sum(MyAvatar.position, warpPosition);
|
||||
|
||||
// Commented out until ray picking can be fixed
|
||||
// var pickRay = {
|
||||
// origin: Vec3.sum(warpPosition, WARP_PICK_OFFSET),
|
||||
// direction: { x: 0, y: -1, z: 0 }
|
||||
// };
|
||||
|
||||
// var intersection = Entities.findRayIntersection(pickRay);
|
||||
|
||||
// if (intersection.intersects && intersection.distance < WARP_PICK_MAX_DISTANCE) {
|
||||
// // Warp 1 meter above the object - this is an approximation
|
||||
// // TODO Get the actual offset to the Avatar's feet and plant them to
|
||||
// // the object.
|
||||
// warpPosition = Vec3.sum(intersection.intersection, { x: 0, y: 1, z:0 });
|
||||
// }
|
||||
|
||||
// Adjust overlays to match warp position
|
||||
Overlays.editOverlay(warpSphere, {
|
||||
position: warpPosition,
|
||||
visible: true,
|
||||
});
|
||||
Overlays.editOverlay(warpLine, {
|
||||
start: warpPosition,
|
||||
end: Vec3.sum(warpPosition, { x: 0, y: WARP_LINE_HEIGHT, z: 0 }),
|
||||
visible: true,
|
||||
});
|
||||
}
|
||||
|
||||
function finishWarp() {
|
||||
Overlays.editOverlay(warpSphere, {
|
||||
visible: false,
|
||||
});
|
||||
Overlays.editOverlay(warpLine, {
|
||||
visible: false,
|
||||
});
|
||||
MyAvatar.position = warpPosition;
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
Controller.keyPressEvent.connect(onKeyPressEvent);
|
||||
|
||||
Controller.actionEvent.connect(onActionEvent);
|
||||
|
||||
Script.update.connect(update);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
Controller.releaseActionEvents();
|
||||
MyAvatar.motorVelocity = {x:0.0, y:0.0, z:0.0}
|
||||
MyAvatar.motorTimescale = VERY_LONG_TIME;
|
||||
}
|
||||
|
||||
setUp();
|
||||
Script.scriptEnding.connect(tearDown);
|
||||
}());
|
|
@ -1,17 +0,0 @@
|
|||
//
|
||||
// hmdDefaults.js
|
||||
// examples
|
||||
//
|
||||
// Created by David Rowe on 6 Mar 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
|
||||
//
|
||||
|
||||
Script.load("progress.js");
|
||||
Script.load("lobby.js");
|
||||
Script.load("notifications.js");
|
||||
Script.load("controllers/oculus/goTo.js");
|
||||
Script.load("hmdControls.js");
|
||||
//Script.load("scripts.js"); // Not created yet
|
Binary file not shown.
|
@ -4881,7 +4881,10 @@ void Application::updateDisplayMode() {
|
|||
}
|
||||
}
|
||||
emit activeDisplayPluginChanged();
|
||||
resetSensors();
|
||||
|
||||
// reset the avatar, to set head and hand palms back to a resonable default pose.
|
||||
getMyAvatar()->reset(false);
|
||||
|
||||
Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
|
||||
}
|
||||
|
||||
|
|
|
@ -464,6 +464,8 @@ Menu::Menu() {
|
|||
avatar, SLOT(setUseAnimPreAndPostRotations(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true,
|
||||
avatar, SLOT(setEnableInverseKinematics(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSensorToWorldMatrix, 0, false,
|
||||
avatar, SLOT(setEnableDebugDrawSensorToWorldMatrix(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::KeyboardMotorControl,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehaviorFromMenu()),
|
||||
|
|
|
@ -144,6 +144,7 @@ namespace MenuOption {
|
|||
const QString RenderResolutionHalf = "1/2";
|
||||
const QString RenderResolutionThird = "1/3";
|
||||
const QString RenderResolutionQuarter = "1/4";
|
||||
const QString RenderSensorToWorldMatrix = "Show SensorToWorld Matrix";
|
||||
const QString ResetAvatarSize = "Reset Avatar Size";
|
||||
const QString ResetSensors = "Reset Sensors";
|
||||
const QString RunningScripts = "Running Scripts...";
|
||||
|
|
|
@ -22,11 +22,11 @@
|
|||
#include <RenderArgs.h>
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
#include <stars_vert.h>
|
||||
#include <stars_frag.h>
|
||||
#include <render-utils/stars_vert.h>
|
||||
#include <render-utils/stars_frag.h>
|
||||
|
||||
#include <standardTransformPNTC_vert.h>
|
||||
#include <starsGrid_frag.h>
|
||||
#include <render-utils/standardTransformPNTC_vert.h>
|
||||
#include <render-utils/starsGrid_frag.h>
|
||||
|
||||
//static const float TILT = 0.23f;
|
||||
static const float TILT = 0.0f;
|
||||
|
|
|
@ -444,6 +444,10 @@ void MyAvatar::updateSensorToWorldMatrix() {
|
|||
|
||||
lateUpdatePalms();
|
||||
|
||||
if (_enableDebugDrawSensorToWorldMatrix) {
|
||||
DebugDraw::getInstance().addMarker("sensorToWorldMatrix", glmExtractRotation(_sensorToWorldMatrix), extractTranslation(_sensorToWorldMatrix), glm::vec4(1));
|
||||
}
|
||||
|
||||
_sensorToWorldMatrixCache.set(_sensorToWorldMatrix);
|
||||
}
|
||||
|
||||
|
@ -698,6 +702,14 @@ void MyAvatar::setEnableDebugDrawPosition(bool isEnabled) {
|
|||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setEnableDebugDrawSensorToWorldMatrix(bool isEnabled) {
|
||||
_enableDebugDrawSensorToWorldMatrix = isEnabled;
|
||||
|
||||
if (!isEnabled) {
|
||||
DebugDraw::getInstance().removeMarker("sensorToWorldMatrix");
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setEnableMeshVisible(bool isEnabled) {
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
_skeletonModel.setVisibleInScene(isEnabled, scene);
|
||||
|
|
|
@ -271,6 +271,7 @@ public slots:
|
|||
void setEnableDebugDrawDefaultPose(bool isEnabled);
|
||||
void setEnableDebugDrawAnimPose(bool isEnabled);
|
||||
void setEnableDebugDrawPosition(bool isEnabled);
|
||||
void setEnableDebugDrawSensorToWorldMatrix(bool isEnabled);
|
||||
bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); }
|
||||
void setEnableMeshVisible(bool isEnabled);
|
||||
void setUseAnimPreAndPostRotations(bool isEnabled);
|
||||
|
@ -434,6 +435,7 @@ private:
|
|||
|
||||
bool _enableDebugDrawDefaultPose { false };
|
||||
bool _enableDebugDrawAnimPose { false };
|
||||
bool _enableDebugDrawSensorToWorldMatrix { false };
|
||||
|
||||
AudioListenerMode _audioListenerMode;
|
||||
glm::vec3 _customListenPosition;
|
||||
|
|
|
@ -144,7 +144,10 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
} else {
|
||||
handParams.isRightEnabled = false;
|
||||
}
|
||||
|
||||
handParams.bodyCapsuleRadius = myAvatar->getCharacterController()->getCapsuleRadius();
|
||||
handParams.bodyCapsuleHalfHeight = myAvatar->getCharacterController()->getCapsuleHalfHeight();
|
||||
handParams.bodyCapsuleLocalOffset = myAvatar->getCharacterController()->getCapsuleLocalOffset();
|
||||
|
||||
_rig->updateFromHandParameters(handParams, deltaTime);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <SharedUtil.h>
|
||||
#include "Application.h"
|
||||
|
||||
|
||||
const float DEFAULT_LINE_WIDTH = 1.0f;
|
||||
|
@ -38,15 +39,17 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
|
|||
_drawInFront(base3DOverlay->_drawInFront)
|
||||
{
|
||||
}
|
||||
|
||||
void Base3DOverlay::setProperties(const QVariantMap& properties) {
|
||||
Overlay::setProperties(properties);
|
||||
|
||||
bool needRenderItemUpdate = false;
|
||||
|
||||
auto drawInFront = properties["drawInFront"];
|
||||
|
||||
if (drawInFront.isValid()) {
|
||||
bool value = drawInFront.toBool();
|
||||
setDrawInFront(value);
|
||||
needRenderItemUpdate = true;
|
||||
}
|
||||
|
||||
auto position = properties["position"];
|
||||
|
@ -60,16 +63,19 @@ void Base3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
if (position.isValid()) {
|
||||
setPosition(vec3FromVariant(position));
|
||||
needRenderItemUpdate = true;
|
||||
}
|
||||
|
||||
if (properties["lineWidth"].isValid()) {
|
||||
setLineWidth(properties["lineWidth"].toFloat());
|
||||
needRenderItemUpdate = true;
|
||||
}
|
||||
|
||||
auto rotation = properties["rotation"];
|
||||
|
||||
if (rotation.isValid()) {
|
||||
setRotation(quatFromVariant(rotation));
|
||||
needRenderItemUpdate = true;
|
||||
}
|
||||
|
||||
if (properties["isSolid"].isValid()) {
|
||||
|
@ -100,6 +106,17 @@ void Base3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
if (properties["ignoreRayIntersection"].isValid()) {
|
||||
setIgnoreRayIntersection(properties["ignoreRayIntersection"].toBool());
|
||||
}
|
||||
|
||||
// Communicate changes to the renderItem if needed
|
||||
if (needRenderItemUpdate) {
|
||||
auto itemID = getRenderItemID();
|
||||
if (render::Item::isValidID(itemID)) {
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
render::PendingChanges pendingChanges;
|
||||
pendingChanges.updateItem(itemID);
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariant Base3DOverlay::getProperty(const QString& property) {
|
||||
|
|
|
@ -233,19 +233,6 @@ bool Overlays::editOverlay(unsigned int id, const QVariant& properties) {
|
|||
if (thisOverlay) {
|
||||
thisOverlay->setProperties(properties.toMap());
|
||||
|
||||
if (thisOverlay->is3D()) {
|
||||
auto itemID = thisOverlay->getRenderItemID();
|
||||
if (render::Item::isValidID(itemID)) {
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
const render::Item& item = scene->getItem(itemID);
|
||||
if (item.getKey() != render::payloadGetKey(thisOverlay)) {
|
||||
render::PendingChanges pendingChanges;
|
||||
pendingChanges.updateItem(itemID);
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QWriteLocker>
|
||||
#include <QReadLocker>
|
||||
|
||||
#include <GeometryUtil.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <DebugDraw.h>
|
||||
|
||||
|
@ -1073,32 +1074,27 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) {
|
|||
if (_animSkeleton && _animNode) {
|
||||
|
||||
const float HAND_RADIUS = 0.05f;
|
||||
const float BODY_RADIUS = params.bodyCapsuleRadius;
|
||||
const float MIN_LENGTH = 1.0e-4f;
|
||||
|
||||
// project the hips onto the xz plane.
|
||||
int hipsIndex = indexOfJoint("Hips");
|
||||
glm::vec3 hipsTrans;
|
||||
if (hipsIndex >= 0) {
|
||||
hipsTrans = _internalPoseSet._absolutePoses[hipsIndex].trans;
|
||||
}
|
||||
const glm::vec2 bodyCircleCenter(hipsTrans.x, hipsTrans.z);
|
||||
|
||||
// Use this capsule to represent the avatar body.
|
||||
const float bodyCapsuleRadius = params.bodyCapsuleRadius;
|
||||
const glm::vec3 bodyCapsuleCenter = hipsTrans - params.bodyCapsuleLocalOffset;
|
||||
const glm::vec3 bodyCapsuleStart = bodyCapsuleCenter - glm::vec3(0, params.bodyCapsuleHalfHeight, 0);
|
||||
const glm::vec3 bodyCapsuleEnd = bodyCapsuleCenter + glm::vec3(0, params.bodyCapsuleHalfHeight, 0);
|
||||
|
||||
if (params.isLeftEnabled) {
|
||||
|
||||
// project the hand position onto the xz plane.
|
||||
glm::vec2 handCircleCenter(params.leftPosition.x, params.leftPosition.z);
|
||||
|
||||
// check for 2d overlap of the hand and body circles.
|
||||
auto circleToCircle = handCircleCenter - bodyCircleCenter;
|
||||
const float circleToCircleLength = glm::length(circleToCircle);
|
||||
const float penetrationDistance = HAND_RADIUS + BODY_RADIUS - circleToCircleLength;
|
||||
if (penetrationDistance > 0.0f && circleToCircleLength > MIN_LENGTH) {
|
||||
// push the hands out of the body
|
||||
handCircleCenter += penetrationDistance * glm::normalize(circleToCircle);
|
||||
// prevent the hand IK targets from intersecting the body capsule
|
||||
glm::vec3 handPosition = params.leftPosition;
|
||||
glm::vec3 displacement(glm::vec3::_null);
|
||||
if (findSphereCapsulePenetration(handPosition, HAND_RADIUS, bodyCapsuleStart, bodyCapsuleEnd, bodyCapsuleRadius, displacement)) {
|
||||
handPosition -= displacement;
|
||||
}
|
||||
|
||||
glm::vec3 handPosition(handCircleCenter.x, params.leftPosition.y, handCircleCenter.y);
|
||||
_animVars.set("leftHandPosition", handPosition);
|
||||
_animVars.set("leftHandRotation", params.leftOrientation);
|
||||
_animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
|
||||
|
@ -1110,19 +1106,13 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) {
|
|||
|
||||
if (params.isRightEnabled) {
|
||||
|
||||
// project the hand position onto the xz plane.
|
||||
glm::vec2 handCircleCenter(params.rightPosition.x, params.rightPosition.z);
|
||||
|
||||
// check for 2d overlap of the hand and body circles.
|
||||
auto circleToCircle = handCircleCenter - bodyCircleCenter;
|
||||
const float circleToCircleLength = glm::length(circleToCircle);
|
||||
const float penetrationDistance = HAND_RADIUS + BODY_RADIUS - circleToCircleLength;
|
||||
if (penetrationDistance > 0.0f && circleToCircleLength > MIN_LENGTH) {
|
||||
// push the hands out of the body
|
||||
handCircleCenter += penetrationDistance * glm::normalize(circleToCircle);
|
||||
// prevent the hand IK targets from intersecting the body capsule
|
||||
glm::vec3 handPosition = params.rightPosition;
|
||||
glm::vec3 displacement(glm::vec3::_null);
|
||||
if (findSphereCapsulePenetration(handPosition, HAND_RADIUS, bodyCapsuleStart, bodyCapsuleEnd, bodyCapsuleRadius, displacement)) {
|
||||
handPosition -= displacement;
|
||||
}
|
||||
|
||||
glm::vec3 handPosition(handCircleCenter.x, params.rightPosition.y, handCircleCenter.y);
|
||||
_animVars.set("rightHandPosition", handPosition);
|
||||
_animVars.set("rightHandRotation", params.rightOrientation);
|
||||
_animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
|
||||
|
|
|
@ -68,6 +68,8 @@ public:
|
|||
bool isLeftEnabled;
|
||||
bool isRightEnabled;
|
||||
float bodyCapsuleRadius;
|
||||
float bodyCapsuleHalfHeight;
|
||||
glm::vec3 bodyCapsuleLocalOffset;
|
||||
glm::vec3 leftPosition = glm::vec3(); // rig space
|
||||
glm::quat leftOrientation = glm::quat(); // rig space (z forward)
|
||||
glm::vec3 rightPosition = glm::vec3(); // rig space
|
||||
|
|
|
@ -349,12 +349,7 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
|
|||
static QString userData;
|
||||
if (userData != zone->getUserData()) {
|
||||
userData = zone->getUserData();
|
||||
auto procedural = std::make_shared<Procedural>(userData);
|
||||
if (procedural->_enabled) {
|
||||
skybox->setProcedural(procedural);
|
||||
} else {
|
||||
skybox->setProcedural(ProceduralPointer());
|
||||
}
|
||||
skybox->parse(userData);
|
||||
}
|
||||
if (zone->getSkyboxProperties().getURL().isEmpty()) {
|
||||
skybox->setCubemap(nullptr);
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
#include <ObjectMotionState.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "../render-utils/simple_vert.h"
|
||||
#include "../render-utils/simple_frag.h"
|
||||
#include <render-utils/simple_vert.h>
|
||||
#include <render-utils/simple_frag.h>
|
||||
|
||||
EntityItemPointer RenderableBoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItemPointer entity{ new RenderableBoxEntityItem(entityID) };
|
||||
|
@ -31,7 +31,9 @@ EntityItemPointer RenderableBoxEntityItem::factory(const EntityItemID& entityID,
|
|||
void RenderableBoxEntityItem::setUserData(const QString& value) {
|
||||
if (value != getUserData()) {
|
||||
BoxEntityItem::setUserData(value);
|
||||
_procedural.reset();
|
||||
if (_procedural) {
|
||||
_procedural->parse(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,7 +42,6 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
|
|||
Q_ASSERT(getType() == EntityTypes::Box);
|
||||
Q_ASSERT(args->_batch);
|
||||
|
||||
|
||||
if (!_procedural) {
|
||||
_procedural.reset(new Procedural(this->getUserData()));
|
||||
_procedural->_vertexSource = simple_vert;
|
||||
|
@ -62,7 +63,7 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
|
|||
}
|
||||
|
||||
batch.setModelTransform(transToCenter); // we want to include the scale as well
|
||||
if (_procedural && _procedural->ready()) {
|
||||
if (_procedural->ready()) {
|
||||
_procedural->prepare(batch, getPosition(), getDimensions());
|
||||
auto color = _procedural->getColor(cubeColor);
|
||||
batch._glColor4f(color.r, color.g, color.b, color.a);
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
#include <GeometryCache.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "../render-utils/simple_vert.h"
|
||||
#include "../render-utils/simple_frag.h"
|
||||
#include <render-utils/simple_vert.h>
|
||||
#include <render-utils/simple_frag.h>
|
||||
|
||||
// Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1
|
||||
// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.
|
||||
|
@ -36,7 +36,9 @@ EntityItemPointer RenderableSphereEntityItem::factory(const EntityItemID& entity
|
|||
void RenderableSphereEntityItem::setUserData(const QString& value) {
|
||||
if (value != getUserData()) {
|
||||
SphereEntityItem::setUserData(value);
|
||||
_procedural.reset();
|
||||
if (_procedural) {
|
||||
_procedural->parse(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -79,6 +79,8 @@ public:
|
|||
glm::vec3 getLinearVelocity() const;
|
||||
|
||||
float getCapsuleRadius() const { return _radius; }
|
||||
float getCapsuleHalfHeight() const { return _halfHeight; }
|
||||
glm::vec3 getCapsuleLocalOffset() const { return _shapeLocalOffset; }
|
||||
|
||||
enum class State {
|
||||
Ground = 0,
|
||||
|
|
|
@ -62,6 +62,9 @@ QJsonValue Procedural::getProceduralData(const QString& proceduralJson) {
|
|||
return doc.object()[PROCEDURAL_USER_DATA_KEY];
|
||||
}
|
||||
|
||||
Procedural::Procedural() {
|
||||
_state = std::make_shared<gpu::State>();
|
||||
}
|
||||
|
||||
Procedural::Procedural(const QString& userDataJson) {
|
||||
parse(userDataJson);
|
||||
|
@ -69,74 +72,110 @@ Procedural::Procedural(const QString& userDataJson) {
|
|||
}
|
||||
|
||||
void Procedural::parse(const QString& userDataJson) {
|
||||
_enabled = false;
|
||||
auto proceduralData = getProceduralData(userDataJson);
|
||||
if (proceduralData.isObject()) {
|
||||
parse(proceduralData.toObject());
|
||||
// Instead of parsing, prep for a parse on the rendering thread
|
||||
// This will be called by Procedural::ready
|
||||
std::lock_guard<std::mutex> lock(_proceduralDataMutex);
|
||||
_proceduralData = proceduralData.toObject();
|
||||
_proceduralDataDirty = true;
|
||||
}
|
||||
|
||||
bool Procedural::parseVersion(const QJsonValue& version) {
|
||||
if (version.isDouble()) {
|
||||
_version = (uint8_t)(floor(version.toDouble()));
|
||||
} else {
|
||||
// All unversioned shaders default to V1
|
||||
_version = 1;
|
||||
}
|
||||
return (_version == 1 || _version == 2);
|
||||
}
|
||||
|
||||
bool Procedural::parseUrl(const QUrl& shaderUrl) {
|
||||
if (!shaderUrl.isValid()) {
|
||||
qWarning() << "Invalid shader URL: " << shaderUrl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_shaderUrl == shaderUrl) {
|
||||
return true;
|
||||
}
|
||||
|
||||
_shaderUrl = shaderUrl;
|
||||
|
||||
if (_shaderUrl.isLocalFile()) {
|
||||
_shaderPath = _shaderUrl.toLocalFile();
|
||||
qDebug() << "Shader path: " << _shaderPath;
|
||||
if (!QFile(_shaderPath).exists()) {
|
||||
return false;;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Shader url: " << _shaderUrl;
|
||||
_networkShader = ShaderCache::instance().getShader(_shaderUrl);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Procedural::parseUniforms(const QJsonObject& uniforms) {
|
||||
if (_parsedUniforms != uniforms) {
|
||||
_parsedUniforms = uniforms;
|
||||
_uniformsDirty = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Procedural::parseTextures(const QJsonArray& channels) {
|
||||
if (_parsedChannels != channels) {
|
||||
_parsedChannels = channels;
|
||||
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
size_t channelCount = std::min(MAX_PROCEDURAL_TEXTURE_CHANNELS, (size_t)_parsedChannels.size());
|
||||
for (size_t i = 0; i < channelCount; ++i) {
|
||||
QString url = _parsedChannels.at((int)i).toString();
|
||||
_channels[i] = textureCache->getTexture(QUrl(url));
|
||||
}
|
||||
|
||||
_channelsDirty = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Procedural::parse(const QJsonObject& proceduralData) {
|
||||
// grab the version number
|
||||
{
|
||||
auto version = proceduralData[VERSION_KEY];
|
||||
if (version.isDouble()) {
|
||||
_version = (uint8_t)(floor(version.toDouble()));
|
||||
}
|
||||
_enabled = false;
|
||||
|
||||
if (proceduralData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the path to the shader
|
||||
{
|
||||
QString shaderUrl = proceduralData[URL_KEY].toString();
|
||||
shaderUrl = ResourceManager::normalizeURL(shaderUrl);
|
||||
_shaderUrl = QUrl(shaderUrl);
|
||||
if (!_shaderUrl.isValid()) {
|
||||
qWarning() << "Invalid shader URL: " << shaderUrl;
|
||||
return;
|
||||
}
|
||||
auto version = proceduralData[VERSION_KEY];
|
||||
auto shaderUrl = proceduralData[URL_KEY].toString();
|
||||
shaderUrl = ResourceManager::normalizeURL(shaderUrl);
|
||||
auto uniforms = proceduralData[UNIFORMS_KEY].toObject();
|
||||
auto channels = proceduralData[CHANNELS_KEY].toArray();
|
||||
|
||||
if (_shaderUrl.isLocalFile()) {
|
||||
_shaderPath = _shaderUrl.toLocalFile();
|
||||
qDebug() << "Shader path: " << _shaderPath;
|
||||
if (!QFile(_shaderPath).exists()) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Shader url: " << _shaderUrl;
|
||||
_networkShader = ShaderCache::instance().getShader(_shaderUrl);
|
||||
}
|
||||
if (parseVersion(version) &&
|
||||
parseUrl(shaderUrl) &&
|
||||
parseUniforms(uniforms) &&
|
||||
parseTextures(channels)) {
|
||||
_enabled = true;
|
||||
}
|
||||
|
||||
// Grab any custom uniforms
|
||||
{
|
||||
auto uniforms = proceduralData[UNIFORMS_KEY];
|
||||
if (uniforms.isObject()) {
|
||||
_parsedUniforms = uniforms.toObject();
|
||||
}
|
||||
}
|
||||
|
||||
// Grab any textures
|
||||
{
|
||||
auto channels = proceduralData[CHANNELS_KEY];
|
||||
if (channels.isArray()) {
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
_parsedChannels = channels.toArray();
|
||||
size_t channelCount = std::min(MAX_PROCEDURAL_TEXTURE_CHANNELS, (size_t)_parsedChannels.size());
|
||||
for (size_t i = 0; i < channelCount; ++i) {
|
||||
QString url = _parsedChannels.at((int)i).toString();
|
||||
_channels[i] = textureCache->getTexture(QUrl(url));
|
||||
}
|
||||
}
|
||||
}
|
||||
_enabled = true;
|
||||
}
|
||||
|
||||
bool Procedural::ready() {
|
||||
// Load any changes to the procedural
|
||||
if (_proceduralDataDirty) {
|
||||
std::lock_guard<std::mutex> lock(_proceduralDataMutex);
|
||||
parse(_proceduralData);
|
||||
_proceduralDataDirty = false;
|
||||
}
|
||||
|
||||
if (!_enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do we have a network or local shader
|
||||
// Do we have a network or local shader, and if so, is it loaded?
|
||||
if (_shaderPath.isEmpty() && (!_networkShader || !_networkShader->isLoaded())) {
|
||||
return false;
|
||||
}
|
||||
|
@ -160,15 +199,14 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm
|
|||
QFile file(_shaderPath);
|
||||
file.open(QIODevice::ReadOnly);
|
||||
_shaderSource = QTextStream(&file).readAll();
|
||||
_pipelineDirty = true;
|
||||
_shaderDirty = true;
|
||||
_shaderModified = lastModified;
|
||||
}
|
||||
} else if (_networkShader && _networkShader->isLoaded()) {
|
||||
_shaderSource = _networkShader->_source;
|
||||
}
|
||||
|
||||
if (!_pipeline || _pipelineDirty) {
|
||||
_pipelineDirty = true;
|
||||
if (!_pipeline || _shaderDirty) {
|
||||
if (!_vertexShader) {
|
||||
_vertexShader = gpu::Shader::createVertex(_vertexSource);
|
||||
}
|
||||
|
@ -214,11 +252,15 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm
|
|||
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
if (_pipelineDirty) {
|
||||
_pipelineDirty = false;
|
||||
if (_shaderDirty || _uniformsDirty) {
|
||||
setupUniforms();
|
||||
}
|
||||
|
||||
if (_shaderDirty || _uniformsDirty || _channelsDirty) {
|
||||
setupChannels(_shaderDirty || _uniformsDirty);
|
||||
}
|
||||
|
||||
_shaderDirty = _uniformsDirty = _channelsDirty = false;
|
||||
|
||||
for (auto lambda : _uniforms) {
|
||||
lambda(batch);
|
||||
|
@ -359,8 +401,14 @@ void Procedural::setupUniforms() {
|
|||
batch._glUniform(_standardUniformSlots[POSITION], _entityPosition);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Procedural::setupChannels(bool shouldCreate) {
|
||||
if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[CHANNEL_RESOLUTION]) {
|
||||
if (!shouldCreate) {
|
||||
// Instead of modifying the last element, just remove and recreate it.
|
||||
_uniforms.pop_back();
|
||||
}
|
||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||
vec3 channelSizes[MAX_PROCEDURAL_TEXTURE_CHANNELS];
|
||||
for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) {
|
||||
|
|
|
@ -28,28 +28,25 @@ const size_t MAX_PROCEDURAL_TEXTURE_CHANNELS{ 4 };
|
|||
// FIXME better encapsulation
|
||||
// FIXME better mechanism for extending to things rendered using shaders other than simple.slv
|
||||
struct Procedural {
|
||||
public:
|
||||
static QJsonValue getProceduralData(const QString& proceduralJson);
|
||||
|
||||
Procedural();
|
||||
Procedural(const QString& userDataJson);
|
||||
void parse(const QString& userDataJson);
|
||||
void parse(const QJsonObject&);
|
||||
|
||||
bool ready();
|
||||
void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size);
|
||||
const gpu::ShaderPointer& getShader() const { return _shader; }
|
||||
void setupUniforms();
|
||||
|
||||
glm::vec4 getColor(const glm::vec4& entityColor);
|
||||
|
||||
bool _enabled{ false };
|
||||
uint8_t _version{ 1 };
|
||||
uint8_t _version { 1 };
|
||||
|
||||
std::string _vertexSource;
|
||||
std::string _fragmentSource;
|
||||
|
||||
QString _shaderSource;
|
||||
QString _shaderPath;
|
||||
QUrl _shaderUrl;
|
||||
quint64 _shaderModified{ 0 };
|
||||
bool _pipelineDirty{ true };
|
||||
gpu::StatePointer _state;
|
||||
|
||||
enum StandardUniforms {
|
||||
DATE,
|
||||
|
@ -61,23 +58,50 @@ struct Procedural {
|
|||
NUM_STANDARD_UNIFORMS
|
||||
};
|
||||
|
||||
int32_t _standardUniformSlots[NUM_STANDARD_UNIFORMS];
|
||||
protected:
|
||||
// Procedural metadata
|
||||
bool _enabled { false };
|
||||
uint64_t _start { 0 };
|
||||
int32_t _frameCount { 0 };
|
||||
|
||||
uint64_t _start{ 0 };
|
||||
int32_t _frameCount{ 0 };
|
||||
// Rendering object descriptions, from userData
|
||||
QJsonObject _proceduralData;
|
||||
std::mutex _proceduralDataMutex;
|
||||
QString _shaderSource;
|
||||
QString _shaderPath;
|
||||
QUrl _shaderUrl;
|
||||
quint64 _shaderModified { 0 };
|
||||
NetworkShaderPointer _networkShader;
|
||||
QJsonObject _parsedUniforms;
|
||||
QJsonArray _parsedChannels;
|
||||
bool _proceduralDataDirty { true };
|
||||
bool _shaderDirty { true };
|
||||
bool _uniformsDirty { true };
|
||||
bool _channelsDirty { true };
|
||||
|
||||
// Rendering objects
|
||||
UniformLambdas _uniforms;
|
||||
int32_t _standardUniformSlots[NUM_STANDARD_UNIFORMS];
|
||||
NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS];
|
||||
gpu::PipelinePointer _pipeline;
|
||||
gpu::ShaderPointer _vertexShader;
|
||||
gpu::ShaderPointer _fragmentShader;
|
||||
gpu::ShaderPointer _shader;
|
||||
gpu::StatePointer _state;
|
||||
|
||||
// Entity metadata
|
||||
glm::vec3 _entityDimensions;
|
||||
glm::vec3 _entityPosition;
|
||||
|
||||
private:
|
||||
// This should only be called from the render thread, as it shares data with Procedural::prepare
|
||||
void parse(const QJsonObject&);
|
||||
bool parseVersion(const QJsonValue& version);
|
||||
bool parseUrl(const QUrl& url);
|
||||
bool parseUniforms(const QJsonObject& uniforms);
|
||||
bool parseTextures(const QJsonArray& channels);
|
||||
|
||||
void setupUniforms();
|
||||
void setupChannels(bool shouldCreate);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -19,26 +19,14 @@
|
|||
#include <model/skybox_frag.h>
|
||||
|
||||
ProceduralSkybox::ProceduralSkybox() : model::Skybox() {
|
||||
}
|
||||
|
||||
ProceduralSkybox::ProceduralSkybox(const ProceduralSkybox& skybox) :
|
||||
model::Skybox(skybox),
|
||||
_procedural(skybox._procedural) {
|
||||
|
||||
}
|
||||
|
||||
void ProceduralSkybox::setProcedural(const ProceduralPointer& procedural) {
|
||||
_procedural = procedural;
|
||||
if (_procedural) {
|
||||
_procedural->_vertexSource = skybox_vert;
|
||||
_procedural->_fragmentSource = skybox_frag;
|
||||
// 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));
|
||||
}
|
||||
_procedural._vertexSource = skybox_vert;
|
||||
_procedural._fragmentSource = skybox_frag;
|
||||
// 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));
|
||||
}
|
||||
|
||||
void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& frustum) const {
|
||||
if (_procedural) {
|
||||
if (_procedural.ready()) {
|
||||
ProceduralSkybox::render(batch, frustum, (*this));
|
||||
} else {
|
||||
Skybox::render(batch, frustum);
|
||||
|
@ -46,21 +34,20 @@ 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._procedural->_enabled && skybox._procedural->ready()) {
|
||||
glm::mat4 projMat;
|
||||
viewFrustum.evalProjectionMatrix(projMat);
|
||||
glm::mat4 projMat;
|
||||
viewFrustum.evalProjectionMatrix(projMat);
|
||||
|
||||
Transform viewTransform;
|
||||
viewFrustum.evalViewTransform(viewTransform);
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewTransform);
|
||||
batch.setModelTransform(Transform()); // only for Mac
|
||||
Transform viewTransform;
|
||||
viewFrustum.evalViewTransform(viewTransform);
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewTransform);
|
||||
batch.setModelTransform(Transform()); // only for Mac
|
||||
|
||||
const auto& procedural = skybox._procedural;
|
||||
procedural->prepare(batch, glm::vec3(0), glm::vec3(1));
|
||||
auto textureSlot = procedural->getShader()->getTextures().findLocation("cubeMap");
|
||||
auto bufferSlot = procedural->getShader()->getBuffers().findLocation("skyboxBuffer");
|
||||
skybox.prepare(batch, textureSlot, bufferSlot);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
}
|
||||
auto& procedural = skybox._procedural;
|
||||
procedural.prepare(batch, glm::vec3(0), glm::vec3(1));
|
||||
auto textureSlot = procedural.getShader()->getTextures().findLocation("cubeMap");
|
||||
auto bufferSlot = procedural.getShader()->getBuffers().findLocation("skyboxBuffer");
|
||||
skybox.prepare(batch, textureSlot, bufferSlot);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,22 +17,18 @@
|
|||
|
||||
#include "Procedural.h"
|
||||
|
||||
typedef std::shared_ptr<Procedural> ProceduralPointer;
|
||||
|
||||
class ProceduralSkybox: public model::Skybox {
|
||||
public:
|
||||
ProceduralSkybox();
|
||||
ProceduralSkybox(const ProceduralSkybox& skybox);
|
||||
ProceduralSkybox& operator= (const ProceduralSkybox& skybox);
|
||||
virtual ~ProceduralSkybox() {};
|
||||
|
||||
void setProcedural(const ProceduralPointer& procedural);
|
||||
void parse(const QString& userData) { _procedural.parse(userData); }
|
||||
|
||||
virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const;
|
||||
static void render(gpu::Batch& batch, const ViewFrustum& frustum, const ProceduralSkybox& skybox);
|
||||
|
||||
protected:
|
||||
ProceduralPointer _procedural;
|
||||
mutable Procedural _procedural;
|
||||
};
|
||||
typedef std::shared_ptr< ProceduralSkybox > ProceduralSkyboxPointer;
|
||||
|
||||
|
|
|
@ -48,6 +48,10 @@ ItemID Scene::allocateID() {
|
|||
return _IDAllocator.fetch_add(1);
|
||||
}
|
||||
|
||||
bool Scene::isAllocatedID(const ItemID& id) {
|
||||
return Item::isValidID(id) && (id < _numAllocatedItems.load());
|
||||
}
|
||||
|
||||
/// Enqueue change batch to the scene
|
||||
void Scene::enqueuePendingChanges(const PendingChanges& pendingChanges) {
|
||||
_changeQueueMutex.lock();
|
||||
|
@ -79,8 +83,17 @@ void Scene::processPendingChangesQueue() {
|
|||
}
|
||||
// Now we know for sure that we have enough items in the array to
|
||||
// capture anything coming from the pendingChanges
|
||||
|
||||
// resets and potential NEW items
|
||||
resetItems(consolidatedPendingChanges._resetItems, consolidatedPendingChanges._resetPayloads);
|
||||
|
||||
// Update the numItemsAtomic counter AFTER the reset changes went through
|
||||
_numAllocatedItems.exchange(maxID);
|
||||
|
||||
// updates
|
||||
updateItems(consolidatedPendingChanges._updatedItems, consolidatedPendingChanges._updateFunctors);
|
||||
|
||||
// removes
|
||||
removeItems(consolidatedPendingChanges._removedItems);
|
||||
|
||||
// ready to go back to rendering activities
|
||||
|
|
|
@ -60,18 +60,24 @@ public:
|
|||
// This call is thread safe, can be called from anywhere to allocate a new ID
|
||||
ItemID allocateID();
|
||||
|
||||
// Check that the ID is valid and allocated for this scene, this a threadsafe call
|
||||
bool isAllocatedID(const ItemID& id);
|
||||
|
||||
// THis is the total number of allocated items, this a threadsafe call
|
||||
size_t getNumItems() const { return _numAllocatedItems.load(); }
|
||||
|
||||
// Enqueue change batch to the scene
|
||||
void enqueuePendingChanges(const PendingChanges& pendingChanges);
|
||||
|
||||
// Process the penging changes equeued
|
||||
void processPendingChangesQueue();
|
||||
|
||||
// This next call are NOT threadsafe, you have to call them from the correct thread to avoid any potential issues
|
||||
|
||||
// Access a particular item form its ID
|
||||
// WARNING, There is No check on the validity of the ID, so this could return a bad Item
|
||||
const Item& getItem(const ItemID& id) const { return _items[id]; }
|
||||
|
||||
size_t getNumItems() const { return _items.size(); }
|
||||
|
||||
// Access the spatialized items
|
||||
const ItemSpatialTree& getSpatialTree() const { return _masterSpatialTree; }
|
||||
|
||||
|
@ -81,6 +87,7 @@ public:
|
|||
protected:
|
||||
// Thread safe elements that can be accessed from anywhere
|
||||
std::atomic<unsigned int> _IDAllocator{ 1 }; // first valid itemID will be One
|
||||
std::atomic<unsigned int> _numAllocatedItems{ 1 }; // num of allocated items, matching the _items.size()
|
||||
std::mutex _changeQueueMutex;
|
||||
PendingChangesQueue _changeQueue;
|
||||
|
||||
|
|
|
@ -68,6 +68,21 @@ void OpenVrDisplayPlugin::activate() {
|
|||
_compositor = vr::VRCompositor();
|
||||
Q_ASSERT(_compositor);
|
||||
HmdDisplayPlugin::activate();
|
||||
|
||||
// set up default sensor space such that the UI overlay will align with the front of the room.
|
||||
auto chaperone = vr::VRChaperone();
|
||||
if (chaperone) {
|
||||
float const UI_RADIUS = 1.0f;
|
||||
float const UI_HEIGHT = 1.6f;
|
||||
float const UI_Z_OFFSET = 0.5;
|
||||
|
||||
float xSize, zSize;
|
||||
chaperone->GetPlayAreaSize(&xSize, &zSize);
|
||||
glm::vec3 uiPos(0.0f, UI_HEIGHT, UI_RADIUS - (0.5f * zSize) - UI_Z_OFFSET);
|
||||
_sensorResetMat = glm::inverse(createMatFromQuatAndPos(glm::quat(), uiPos));
|
||||
} else {
|
||||
qDebug() << "OpenVR: error could not get chaperone pointer";
|
||||
}
|
||||
}
|
||||
|
||||
void OpenVrDisplayPlugin::deactivate() {
|
||||
|
@ -115,7 +130,7 @@ glm::mat4 OpenVrDisplayPlugin::getHeadPose(uint32_t frameIndex) const {
|
|||
#endif
|
||||
|
||||
vr::TrackedDevicePose_t predictedTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
|
||||
_system->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseSeated, predictedSecondsFromNow, predictedTrackedDevicePose, vr::k_unMaxTrackedDeviceCount);
|
||||
_system->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseStanding, predictedSecondsFromNow, predictedTrackedDevicePose, vr::k_unMaxTrackedDeviceCount);
|
||||
|
||||
// copy and process predictedTrackedDevicePoses
|
||||
for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) {
|
||||
|
|
|
@ -23,83 +23,77 @@
|
|||
#include <gl/QOpenGLDebugLoggerWrapper.h>
|
||||
#include <gl/QOpenGLContextWrapper.h>
|
||||
|
||||
#include "../model/Skybox_vert.h"
|
||||
#include "../model/Skybox_frag.h"
|
||||
#include <render-utils/simple_vert.h>
|
||||
#include <render-utils/simple_frag.h>
|
||||
#include <render-utils/simple_textured_frag.h>
|
||||
#include <render-utils/simple_textured_emisive_frag.h>
|
||||
|
||||
#include "simple_vert.h"
|
||||
#include "simple_frag.h"
|
||||
#include "simple_textured_frag.h"
|
||||
#include "simple_textured_emisive_frag.h"
|
||||
#include <render-utils/deferred_light_vert.h>
|
||||
#include <render-utils/deferred_light_limited_vert.h>
|
||||
|
||||
#include "deferred_light_vert.h"
|
||||
#include "deferred_light_limited_vert.h"
|
||||
#include <render-utils/directional_light_frag.h>
|
||||
#include <render-utils/directional_ambient_light_frag.h>
|
||||
#include <render-utils/directional_skybox_light_frag.h>
|
||||
|
||||
#include "directional_light_frag.h"
|
||||
#include <render-utils/point_light_frag.h>
|
||||
#include <render-utils/spot_light_frag.h>
|
||||
|
||||
#include "directional_ambient_light_frag.h"
|
||||
#include <render-utils/standardTransformPNTC_vert.h>
|
||||
#include <render-utils/standardDrawTexture_frag.h>
|
||||
|
||||
#include "directional_skybox_light_frag.h"
|
||||
#include <render-utils/model_vert.h>
|
||||
#include <render-utils/model_shadow_vert.h>
|
||||
#include <render-utils/model_normal_map_vert.h>
|
||||
#include <render-utils/model_lightmap_vert.h>
|
||||
#include <render-utils/model_lightmap_normal_map_vert.h>
|
||||
#include <render-utils/skin_model_vert.h>
|
||||
#include <render-utils/skin_model_shadow_vert.h>
|
||||
#include <render-utils/skin_model_normal_map_vert.h>
|
||||
|
||||
#include "point_light_frag.h"
|
||||
#include "spot_light_frag.h"
|
||||
#include <render-utils/model_frag.h>
|
||||
#include <render-utils/model_shadow_frag.h>
|
||||
#include <render-utils/model_normal_map_frag.h>
|
||||
#include <render-utils/model_normal_specular_map_frag.h>
|
||||
#include <render-utils/model_specular_map_frag.h>
|
||||
#include <render-utils/model_lightmap_frag.h>
|
||||
#include <render-utils/model_lightmap_normal_map_frag.h>
|
||||
#include <render-utils/model_lightmap_normal_specular_map_frag.h>
|
||||
#include <render-utils/model_lightmap_specular_map_frag.h>
|
||||
#include <render-utils/model_translucent_frag.h>
|
||||
|
||||
#include "standardTransformPNTC_vert.h"
|
||||
#include "standardDrawTexture_frag.h"
|
||||
#include <entities-renderer/untextured_particle_frag.h>
|
||||
#include <entities-renderer/untextured_particle_vert.h>
|
||||
#include <entities-renderer/textured_particle_frag.h>
|
||||
#include <entities-renderer/textured_particle_vert.h>
|
||||
|
||||
#include "model_vert.h"
|
||||
#include "model_shadow_vert.h"
|
||||
#include "model_normal_map_vert.h"
|
||||
#include "model_lightmap_vert.h"
|
||||
#include "model_lightmap_normal_map_vert.h"
|
||||
#include "skin_model_vert.h"
|
||||
#include "skin_model_shadow_vert.h"
|
||||
#include "skin_model_normal_map_vert.h"
|
||||
#include <render-utils/hit_effect_vert.h>
|
||||
#include <render-utils/hit_effect_frag.h>
|
||||
|
||||
#include "model_frag.h"
|
||||
#include "model_shadow_frag.h"
|
||||
#include "model_normal_map_frag.h"
|
||||
#include "model_normal_specular_map_frag.h"
|
||||
#include "model_specular_map_frag.h"
|
||||
#include "model_lightmap_frag.h"
|
||||
#include "model_lightmap_normal_map_frag.h"
|
||||
#include "model_lightmap_normal_specular_map_frag.h"
|
||||
#include "model_lightmap_specular_map_frag.h"
|
||||
#include "model_translucent_frag.h"
|
||||
#include <render-utils/overlay3D_vert.h>
|
||||
#include <render-utils/overlay3D_frag.h>
|
||||
|
||||
#include "untextured_particle_frag.h"
|
||||
#include "untextured_particle_vert.h"
|
||||
#include "textured_particle_frag.h"
|
||||
#include "textured_particle_vert.h"
|
||||
#include <model/Skybox_vert.h>
|
||||
#include <model/Skybox_frag.h>
|
||||
|
||||
#include <render-utils/stars_vert.h>
|
||||
#include <render-utils/stars_frag.h>
|
||||
#include <render-utils/starsGrid_frag.h>
|
||||
|
||||
#include "hit_effect_vert.h"
|
||||
#include "hit_effect_frag.h"
|
||||
#include <gpu/DrawTransformUnitQuad_vert.h>
|
||||
#include <gpu/DrawTexcoordRectTransformUnitQuad_vert.h>
|
||||
#include <gpu/DrawViewportQuadTransformTexcoord_vert.h>
|
||||
#include <gpu/DrawTexture_frag.h>
|
||||
#include <gpu/DrawTextureOpaque_frag.h>
|
||||
#include <gpu/DrawColoredTexture_frag.h>
|
||||
|
||||
#include "overlay3D_vert.h"
|
||||
#include "overlay3D_frag.h"
|
||||
#include <render-utils/sdf_text3D_vert.h>
|
||||
#include <render-utils/sdf_text3D_frag.h>
|
||||
|
||||
#include "Skybox_vert.h"
|
||||
#include "Skybox_frag.h"
|
||||
#include <entities-renderer/paintStroke_vert.h>
|
||||
#include <entities-renderer/paintStroke_frag.h>
|
||||
|
||||
#include "stars_vert.h"
|
||||
#include "stars_frag.h"
|
||||
#include "starsGrid_frag.h"
|
||||
|
||||
#include "DrawTransformUnitQuad_vert.h"
|
||||
#include "DrawTexcoordRectTransformUnitQuad_vert.h"
|
||||
#include "DrawViewportQuadTransformTexcoord_vert.h"
|
||||
#include "DrawTexture_frag.h"
|
||||
#include "DrawTextureOpaque_frag.h"
|
||||
#include "DrawColoredTexture_frag.h"
|
||||
|
||||
#include "sdf_text3D_vert.h"
|
||||
#include "sdf_text3D_frag.h"
|
||||
|
||||
#include "paintStroke_vert.h"
|
||||
#include "paintStroke_frag.h"
|
||||
|
||||
#include "polyvox_vert.h"
|
||||
#include "polyvox_frag.h"
|
||||
#include <entities-renderer/polyvox_vert.h>
|
||||
#include <entities-renderer/polyvox_frag.h>
|
||||
|
||||
// Create a simple OpenGL window that renders text in various ways
|
||||
class QTestWindow : public QWindow {
|
||||
|
|
|
@ -212,3 +212,15 @@ void GeometryUtilTests::testTwistSwingDecomposition() {
|
|||
}
|
||||
|
||||
|
||||
void GeometryUtilTests::testSphereCapsulePenetration() {
|
||||
glm::vec3 sphereCenter(1.5, 0.0, 0.0);
|
||||
float sphereRadius = 1.0f;
|
||||
glm::vec3 capsuleStart(0.0f, -10.0f, 0.0f);
|
||||
glm::vec3 capsuleEnd(0.0f, 10.0f, 0.0f);
|
||||
float capsuleRadius = 1.0f;
|
||||
|
||||
glm::vec3 penetration(glm::vec3::_null);
|
||||
bool hit = findSphereCapsulePenetration(sphereCenter, sphereRadius, capsuleStart, capsuleEnd, capsuleRadius, penetration);
|
||||
QCOMPARE(hit, true);
|
||||
QCOMPARE_WITH_ABS_ERROR(penetration, glm::vec3(-0.5f, 0.0f, 0.0f), EPSILON);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ private slots:
|
|||
void testLocalRayRectangleIntersection();
|
||||
void testWorldRayRectangleIntersection();
|
||||
void testTwistSwingDecomposition();
|
||||
void testSphereCapsulePenetration();
|
||||
};
|
||||
|
||||
float getErrorDifference(const float& a, const float& b);
|
||||
|
|
452
unpublishedScripts/DomainContent/Home/fishTank/createFishTank.js
Normal file
452
unpublishedScripts/DomainContent/Home/fishTank/createFishTank.js
Normal file
|
@ -0,0 +1,452 @@
|
|||
//
|
||||
// createTank.js
|
||||
//
|
||||
//
|
||||
// created by James b. Pollack @imgntn on 3/9/2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Adds a fish tank and base, decorations, particle bubble systems, and a bubble sound. Attaches a script that does fish swimming.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
var fishTank, tankBase, bubbleSystem, secondBubbleSystem, thirdBubbleSystem, innerContainer, bubbleInjector, lowerCorner, upperCorner, urchin, treasure, rocks;
|
||||
var CLEANUP = true;
|
||||
|
||||
var TANK_DIMENSIONS = {
|
||||
x: 0.8212,
|
||||
y: 0.8116,
|
||||
z: 2.1404
|
||||
};
|
||||
|
||||
|
||||
var INNER_TANK_SCALE = 0.7;
|
||||
var INNER_TANK_DIMENSIONS = Vec3.multiply(INNER_TANK_SCALE, TANK_DIMENSIONS);
|
||||
INNER_TANK_DIMENSIONS.y = INNER_TANK_DIMENSIONS.y - 0.4;
|
||||
var TANK_WIDTH = TANK_DIMENSIONS.z;
|
||||
var TANK_HEIGHT = TANK_DIMENSIONS.y;
|
||||
|
||||
var DEBUG_COLOR = {
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 255
|
||||
}
|
||||
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), 1 * TANK_WIDTH));
|
||||
|
||||
var TANK_POSITION = center;
|
||||
|
||||
var TANK_SCRIPT = Script.resolvePath('tank.js?' + Math.random())
|
||||
|
||||
var TANK_MODEL_URL = "http://hifi-content.s3.amazonaws.com/DomainContent/Home/fishTank/aquariumTank.fbx";
|
||||
|
||||
var TANK_BASE_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/DomainContent/Home/fishTank/aquariumBase.fbx';
|
||||
|
||||
var TANK_BASE_COLLISION_HULL = 'http://hifi-content.s3.amazonaws.com/DomainContent/Home/fishTank/aquariumBase.obj'
|
||||
|
||||
var TANK_BASE_DIMENSIONS = {
|
||||
x: 0.8599,
|
||||
y: 1.8450,
|
||||
z: 2.1936
|
||||
};
|
||||
|
||||
var BASE_VERTICAL_OFFSET = 0.42;
|
||||
|
||||
var BUBBLE_SYSTEM_FORWARD_OFFSET = TANK_DIMENSIONS.x + 0.06;
|
||||
var BUBBLE_SYSTEM_LATERAL_OFFSET = 0.025;
|
||||
var BUBBLE_SYSTEM_VERTICAL_OFFSET = -0.30;
|
||||
|
||||
var BUBBLE_SYSTEM_DIMENSIONS = {
|
||||
x: TANK_DIMENSIONS.x / 8,
|
||||
y: TANK_DIMENSIONS.y,
|
||||
z: TANK_DIMENSIONS.z / 8
|
||||
}
|
||||
|
||||
var BUBBLE_SOUND_URL = "http://hifi-content.s3.amazonaws.com/DomainContent/Home/Sounds/aquarium_small.L.wav";
|
||||
var bubbleSound = SoundCache.getSound(BUBBLE_SOUND_URL);
|
||||
|
||||
|
||||
var URCHIN_FORWARD_OFFSET = TANK_DIMENSIONS.x - 0.35;
|
||||
var URCHIN_LATERAL_OFFSET = -0.05;
|
||||
var URCHIN_VERTICAL_OFFSET = -0.12;
|
||||
|
||||
|
||||
var URCHIN_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/DomainContent/Home/fishTank/Urchin.fbx';
|
||||
|
||||
var URCHIN_DIMENSIONS = {
|
||||
x: 0.4,
|
||||
y: 0.4,
|
||||
z: 0.4
|
||||
}
|
||||
|
||||
var ROCKS_FORWARD_OFFSET = 0;
|
||||
var ROCKS_LATERAL_OFFSET = 0.0;
|
||||
var ROCKS_VERTICAL_OFFSET = (-TANK_DIMENSIONS.y / 2) + 0.25;
|
||||
|
||||
var ROCK_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/DomainContent/Home/fishTank/Aquarium-Rocks-2.fbx';
|
||||
|
||||
var ROCK_DIMENSIONS = {
|
||||
x: 0.707,
|
||||
y: 0.33,
|
||||
z: 1.64
|
||||
}
|
||||
|
||||
var TREASURE_FORWARD_OFFSET = -TANK_DIMENSIONS.x;
|
||||
var TREASURE_LATERAL_OFFSET = -0.15;
|
||||
var TREASURE_VERTICAL_OFFSET = -0.23;
|
||||
|
||||
var TREASURE_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/DomainContent/Home/fishTank/Treasure-Chest2-SM.fbx';
|
||||
|
||||
var TREASURE_DIMENSIONS = {
|
||||
x: 0.1199,
|
||||
y: 0.1105,
|
||||
z: 0.1020
|
||||
}
|
||||
|
||||
|
||||
function createFishTank() {
|
||||
var tankProperties = {
|
||||
name: 'hifi-home-fishtank',
|
||||
type: 'Model',
|
||||
modelURL: TANK_MODEL_URL,
|
||||
dimensions: TANK_DIMENSIONS,
|
||||
position: TANK_POSITION,
|
||||
color: DEBUG_COLOR,
|
||||
collisionless: true,
|
||||
script: TANK_SCRIPT,
|
||||
visible: true
|
||||
}
|
||||
|
||||
fishTank = Entities.addEntity(tankProperties);
|
||||
}
|
||||
|
||||
function createBubbleSystems() {
|
||||
|
||||
var tankProperties = Entities.getEntityProperties(fishTank);
|
||||
var bubbleProperties = {
|
||||
"name": 'hifi-home-fishtank-bubbles',
|
||||
"isEmitting": 1,
|
||||
"maxParticles": 1880,
|
||||
"lifespan": 1.6,
|
||||
"emitRate": 10,
|
||||
"emitSpeed": 0.025,
|
||||
"speedSpread": 0.025,
|
||||
"emitOrientation": {
|
||||
"x": 0,
|
||||
"y": 0.5,
|
||||
"z": 0.5,
|
||||
"w": 0
|
||||
},
|
||||
"emitDimensions": {
|
||||
"x": -0.2,
|
||||
"y": TANK_DIMENSIONS.y,
|
||||
"z": 0
|
||||
},
|
||||
"polarStart": 0,
|
||||
"polarFinish": 0,
|
||||
"azimuthStart": 0.2,
|
||||
"azimuthFinish": 0.1,
|
||||
"emitAcceleration": {
|
||||
"x": 0,
|
||||
"y": 0.3,
|
||||
"z": 0
|
||||
},
|
||||
"accelerationSpread": {
|
||||
"x": 0.01,
|
||||
"y": 0.01,
|
||||
"z": 0.01
|
||||
},
|
||||
"particleRadius": 0.005,
|
||||
"radiusSpread": 0,
|
||||
"radiusStart": 0.01,
|
||||
"radiusFinish": 0.01,
|
||||
"alpha": 0.2,
|
||||
"alphaSpread": 0,
|
||||
"alphaStart": 0.3,
|
||||
"alphaFinish": 0,
|
||||
"emitterShouldTrail": 0,
|
||||
"textures": "http://hifi-content.s3.amazonaws.com/DomainContent/Home/fishTank/bubble-white.png"
|
||||
};
|
||||
|
||||
bubbleProperties.type = "ParticleEffect";
|
||||
bubbleProperties.parentID = fishTank;
|
||||
bubbleProperties.dimensions = BUBBLE_SYSTEM_DIMENSIONS;
|
||||
|
||||
var finalOffset = getOffsetFromTankCenter(BUBBLE_SYSTEM_VERTICAL_OFFSET, BUBBLE_SYSTEM_FORWARD_OFFSET, BUBBLE_SYSTEM_LATERAL_OFFSET);
|
||||
|
||||
bubbleProperties.position = finalOffset;
|
||||
bubbleSystem = Entities.addEntity(bubbleProperties);
|
||||
|
||||
bubbleProperties.position.x += -0.076;
|
||||
secondBubbleSystem = Entities.addEntity(bubbleProperties)
|
||||
|
||||
bubbleProperties.position.x += -0.076;
|
||||
thirdBubbleSystem = Entities.addEntity(bubbleProperties)
|
||||
|
||||
createBubbleSound(finalOffset);
|
||||
}
|
||||
|
||||
function getOffsetFromTankCenter(VERTICAL_OFFSET, FORWARD_OFFSET, LATERAL_OFFSET) {
|
||||
|
||||
var tankProperties = Entities.getEntityProperties(fishTank);
|
||||
|
||||
var upVector = Quat.getUp(tankProperties.rotation);
|
||||
var frontVector = Quat.getFront(tankProperties.rotation);
|
||||
var rightVector = Quat.getRight(tankProperties.rotation);
|
||||
|
||||
var upOffset = Vec3.multiply(upVector, VERTICAL_OFFSET);
|
||||
var frontOffset = Vec3.multiply(frontVector, FORWARD_OFFSET);
|
||||
var rightOffset = Vec3.multiply(rightVector, LATERAL_OFFSET);
|
||||
|
||||
var finalOffset = Vec3.sum(tankProperties.position, upOffset);
|
||||
finalOffset = Vec3.sum(finalOffset, frontOffset);
|
||||
finalOffset = Vec3.sum(finalOffset, rightOffset);
|
||||
return finalOffset
|
||||
}
|
||||
|
||||
function createBubbleSound(position) {
|
||||
var audioProperties = {
|
||||
volume: 0.05,
|
||||
position: position,
|
||||
loop: true
|
||||
};
|
||||
|
||||
bubbleInjector = Audio.playSound(bubbleSound, audioProperties);
|
||||
|
||||
}
|
||||
|
||||
function createInnerContainer(position) {
|
||||
|
||||
var tankProperties = Entities.getEntityProperties(fishTank);
|
||||
|
||||
var containerProps = {
|
||||
name: "hifi-home-fishtank-inner-container",
|
||||
type: 'Box',
|
||||
color: {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 255
|
||||
},
|
||||
parentID: fishTank,
|
||||
dimensions: INNER_TANK_DIMENSIONS,
|
||||
position: tankProperties.position,
|
||||
visible: false,
|
||||
collisionless: true,
|
||||
dynamic: false
|
||||
};
|
||||
|
||||
innerContainer = Entities.addEntity(containerProps);
|
||||
}
|
||||
|
||||
function createEntitiesAtCorners() {
|
||||
|
||||
var bounds = Entities.getEntityProperties(innerContainer, "boundingBox").boundingBox;
|
||||
|
||||
var lowerProps = {
|
||||
name: 'hifi-home-fishtank-lower-corner',
|
||||
type: "Box",
|
||||
parentID: fishTank,
|
||||
dimensions: {
|
||||
x: 0.2,
|
||||
y: 0.2,
|
||||
z: 0.2
|
||||
},
|
||||
color: {
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 0
|
||||
},
|
||||
collisionless: true,
|
||||
position: bounds.brn,
|
||||
visible: false
|
||||
}
|
||||
|
||||
var upperProps = {
|
||||
name: 'hifi-home-fishtank-upper-corner',
|
||||
type: "Box",
|
||||
parentID: fishTank,
|
||||
dimensions: {
|
||||
x: 0.2,
|
||||
y: 0.2,
|
||||
z: 0.2
|
||||
},
|
||||
color: {
|
||||
red: 0,
|
||||
green: 255,
|
||||
blue: 0
|
||||
},
|
||||
collisionless: true,
|
||||
position: bounds.tfl,
|
||||
visible: false
|
||||
}
|
||||
|
||||
lowerCorner = Entities.addEntity(lowerProps);
|
||||
upperCorner = Entities.addEntity(upperProps);
|
||||
|
||||
}
|
||||
|
||||
function createRocks() {
|
||||
var finalPosition = getOffsetFromTankCenter(ROCKS_VERTICAL_OFFSET, ROCKS_FORWARD_OFFSET, ROCKS_LATERAL_OFFSET);
|
||||
|
||||
var properties = {
|
||||
name: 'hifi-home-fishtank-rock',
|
||||
type: 'Model',
|
||||
parentID: fishTank,
|
||||
modelURL: ROCK_MODEL_URL,
|
||||
position: finalPosition,
|
||||
dimensions: ROCK_DIMENSIONS
|
||||
}
|
||||
|
||||
rocks = Entities.addEntity(properties);
|
||||
}
|
||||
|
||||
function createUrchin() {
|
||||
var finalPosition = getOffsetFromTankCenter(URCHIN_VERTICAL_OFFSET, URCHIN_FORWARD_OFFSET, URCHIN_LATERAL_OFFSET);
|
||||
|
||||
var properties = {
|
||||
name: 'hifi-home-fishtank-urchin',
|
||||
type: 'Model',
|
||||
parentID: fishTank,
|
||||
modelURL: URCHIN_MODEL_URL,
|
||||
position: finalPosition,
|
||||
shapeType: 'Sphere',
|
||||
dimensions: URCHIN_DIMENSIONS
|
||||
}
|
||||
|
||||
urchin = Entities.addEntity(properties);
|
||||
|
||||
}
|
||||
|
||||
function createTreasureChest() {
|
||||
var finalPosition = getOffsetFromTankCenter(TREASURE_VERTICAL_OFFSET, TREASURE_FORWARD_OFFSET, TREASURE_LATERAL_OFFSET);
|
||||
|
||||
var properties = {
|
||||
name: 'hifi-home-fishtank-treasure-chest',
|
||||
type: 'Model',
|
||||
parentID: fishTank,
|
||||
modelURL: TREASURE_MODEL_URL,
|
||||
position: finalPosition,
|
||||
dimensions: TREASURE_DIMENSIONS,
|
||||
rotation: Quat.fromPitchYawRollDegrees(10, -45, 10)
|
||||
}
|
||||
|
||||
treasure = Entities.addEntity(properties);
|
||||
}
|
||||
|
||||
function createTankBase() {
|
||||
var properties = {
|
||||
name: 'hifi-home-fishtank-base',
|
||||
type: 'Model',
|
||||
modelURL: TANK_BASE_MODEL_URL,
|
||||
parentID: fishTank,
|
||||
shapeType: 'compound',
|
||||
compoundShapeURL: TANK_BASE_COLLISION_HULL,
|
||||
position: {
|
||||
x: TANK_POSITION.x,
|
||||
y: TANK_POSITION.y - BASE_VERTICAL_OFFSET,
|
||||
z: TANK_POSITION.z
|
||||
},
|
||||
dimensions: TANK_BASE_DIMENSIONS
|
||||
}
|
||||
|
||||
tankBase = Entities.addEntity(properties);
|
||||
}
|
||||
|
||||
createFishTank();
|
||||
|
||||
createInnerContainer();
|
||||
|
||||
createBubbleSystems();
|
||||
|
||||
createEntitiesAtCorners();
|
||||
|
||||
createUrchin();
|
||||
|
||||
createRocks();
|
||||
|
||||
createTankBase();
|
||||
|
||||
createTreasureChest();
|
||||
var customKey = 'hifi-home-fishtank';
|
||||
|
||||
|
||||
var data = {
|
||||
fishLoaded: false,
|
||||
bubbleSystem: bubbleSystem,
|
||||
bubbleSound: bubbleSound,
|
||||
corners: {
|
||||
brn: lowerCorner,
|
||||
tfl: upperCorner
|
||||
},
|
||||
innerContainer: innerContainer,
|
||||
|
||||
}
|
||||
|
||||
Script.setTimeout(function() {
|
||||
setEntityCustomData(customKey, fishTank, data);
|
||||
}, 2000)
|
||||
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(fishTank);
|
||||
Entities.deleteEntity(tankBase);
|
||||
Entities.deleteEntity(bubbleSystem);
|
||||
Entities.deleteEntity(secondBubbleSystem);
|
||||
Entities.deleteEntity(thirdBubbleSystem);
|
||||
Entities.deleteEntity(innerContainer);
|
||||
Entities.deleteEntity(lowerCorner);
|
||||
Entities.deleteEntity(upperCorner);
|
||||
Entities.deleteEntity(urchin);
|
||||
Entities.deleteEntity(rocks);
|
||||
bubbleInjector.stop();
|
||||
bubbleInjector = null;
|
||||
}
|
||||
|
||||
|
||||
if (CLEANUP === true) {
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
}
|
||||
|
||||
function setEntityUserData(id, data) {
|
||||
var json = JSON.stringify(data)
|
||||
Entities.editEntity(id, {
|
||||
userData: json
|
||||
});
|
||||
}
|
||||
|
||||
function getEntityUserData(id) {
|
||||
var results = null;
|
||||
var properties = Entities.getEntityProperties(id, "userData");
|
||||
if (properties.userData) {
|
||||
try {
|
||||
results = JSON.parse(properties.userData);
|
||||
} catch (err) {
|
||||
// print('error parsing json');
|
||||
// print('properties are:'+ properties.userData);
|
||||
}
|
||||
}
|
||||
return results ? results : {};
|
||||
}
|
||||
|
||||
|
||||
// Non-destructively modify the user data of an entity.
|
||||
function setEntityCustomData(customKey, id, data) {
|
||||
var userData = getEntityUserData(id);
|
||||
if (data == null) {
|
||||
delete userData[customKey];
|
||||
} else {
|
||||
userData[customKey] = data;
|
||||
}
|
||||
setEntityUserData(id, userData);
|
||||
}
|
||||
|
||||
function getEntityCustomData(customKey, id, defaultValue) {
|
||||
var userData = getEntityUserData(id);
|
||||
if (undefined != userData[customKey]) {
|
||||
return userData[customKey];
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
709
unpublishedScripts/DomainContent/Home/fishTank/tank.js
Normal file
709
unpublishedScripts/DomainContent/Home/fishTank/tank.js
Normal file
|
@ -0,0 +1,709 @@
|
|||
//
|
||||
// tank.js
|
||||
//
|
||||
//
|
||||
// created by James b. Pollack @imgntn on 3/9/2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// looks for fish to swim around, if there are none it makes some. adds an attractor where the person simulating it is looking.
|
||||
//
|
||||
// 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('../../../../examples/libraries/virtualBaton.js');
|
||||
|
||||
//only one person should simulate the tank at a time -- we pass around a virtual baton
|
||||
var baton;
|
||||
var iOwn = false;
|
||||
var _entityID;
|
||||
var _this;
|
||||
var connected = false;
|
||||
|
||||
var TANK_SEARCH_RADIUS = 5;
|
||||
var WANT_LOOK_DEBUG_LINE = false;
|
||||
var WANT_LOOK_DEBUG_SPHERE = false;
|
||||
|
||||
var INTERSECT_COLOR = {
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 255
|
||||
}
|
||||
|
||||
function FishTank() {
|
||||
_this = this;
|
||||
}
|
||||
|
||||
function startUpdate() {
|
||||
//when the baton is claimed;
|
||||
// print('trying to claim the object' + _entityID)
|
||||
iOwn = true;
|
||||
connected = true;
|
||||
Script.update.connect(_this.update);
|
||||
}
|
||||
|
||||
function stopUpdateAndReclaim() {
|
||||
//when the baton is released;
|
||||
// print('i released the object ' + _entityID)
|
||||
iOwn = false;
|
||||
if (connected === true) {
|
||||
connected = false;
|
||||
Script.update.disconnect(_this.update);
|
||||
_this.clearLookAttractor();
|
||||
}
|
||||
//hook up callbacks to the baton
|
||||
baton.claim(startUpdate, stopUpdateAndReclaim);
|
||||
|
||||
}
|
||||
|
||||
FishTank.prototype = {
|
||||
fish: null,
|
||||
tankLocked: false,
|
||||
hasLookAttractor: false,
|
||||
lookAttractor: null,
|
||||
overlayLine: null,
|
||||
overlayLineDistance: 3,
|
||||
debugSphere: null,
|
||||
findFishInTank: function() {
|
||||
// print('looking for a fish in the tank')
|
||||
var results = Entities.findEntities(_this.currentProperties.position, TANK_SEARCH_RADIUS);
|
||||
var fishList = [];
|
||||
|
||||
results.forEach(function(fish) {
|
||||
var properties = Entities.getEntityProperties(fish, 'name');
|
||||
if (properties.name.indexOf('hifi-fishtank-fish' + _this.entityID) > -1) {
|
||||
fishList.push(fish);
|
||||
}
|
||||
})
|
||||
|
||||
// print('fish? ' + fishList.length)
|
||||
return fishList;
|
||||
},
|
||||
|
||||
initialize: function(entityID) {
|
||||
var properties = Entities.getEntityProperties(entityID)
|
||||
|
||||
if (properties.hasOwnProperty('userData') === false || properties.userData.length === 0) {
|
||||
_this.initTimeout = Script.setTimeout(function() {
|
||||
if (properties.hasOwnProperty('userData')) {
|
||||
// print('has user data property')
|
||||
}
|
||||
if (properties.userData.length === 0) {
|
||||
// print('user data length is zero')
|
||||
}
|
||||
|
||||
// print('try again in one second')
|
||||
_this.initialize(entityID);
|
||||
}, 1000)
|
||||
|
||||
} else {
|
||||
// print('userdata before parse attempt' + properties.userData)
|
||||
_this.userData = null;
|
||||
try {
|
||||
_this.userData = JSON.parse(properties.userData);
|
||||
} catch (err) {
|
||||
// print('error parsing json');
|
||||
// print('properties are:' + properties.userData);
|
||||
return;
|
||||
}
|
||||
// print('after parse')
|
||||
_this.currentProperties = Entities.getEntityProperties(entityID);
|
||||
baton = virtualBaton({
|
||||
batonName: 'io.highfidelity.fishtank:' + entityID, // One winner for each entity
|
||||
});
|
||||
|
||||
stopUpdateAndReclaim();
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
// print("preload");
|
||||
this.entityID = entityID;
|
||||
_entityID = entityID;
|
||||
this.initialize(entityID);
|
||||
this.initTimeout = null;
|
||||
|
||||
},
|
||||
|
||||
unload: function() {
|
||||
// print(' UNLOAD')
|
||||
if (connected === true) {
|
||||
Script.update.disconnect(_this.update);
|
||||
}
|
||||
if (WANT_LOOK_DEBUG_LINE === true) {
|
||||
_this.overlayLineOff();
|
||||
}
|
||||
if (baton) {
|
||||
// print('BATON RELEASE ')
|
||||
baton.release(function() {});
|
||||
}
|
||||
|
||||
},
|
||||
update: function(deltaTime) {
|
||||
|
||||
if (iOwn === false) {
|
||||
// print('i dont own')
|
||||
//exit if we're not supposed to be simulating the fish
|
||||
return
|
||||
}
|
||||
// print('i am the owner!')
|
||||
//do stuff
|
||||
updateFish(deltaTime);
|
||||
_this.seeIfOwnerIsLookingAtTheTank();
|
||||
},
|
||||
|
||||
debugSphereOn: function(position) {
|
||||
if (_this.debugSphere !== null) {
|
||||
Entities.editEntity(_this.debugSphere, {
|
||||
visible: true
|
||||
})
|
||||
return;
|
||||
}
|
||||
var sphereProperties = {
|
||||
type: 'Sphere',
|
||||
parentID: _this.entityID,
|
||||
dimensions: {
|
||||
x: 0.1,
|
||||
y: 0.1,
|
||||
z: 0.1,
|
||||
},
|
||||
color: INTERSECT_COLOR,
|
||||
position: position,
|
||||
collisionless: true
|
||||
}
|
||||
_this.debugSphere = Entities.addEntity(sphereProperties);
|
||||
},
|
||||
|
||||
updateDebugSphere: function(position) {
|
||||
Entities.editEntity(_this.debugSphere, {
|
||||
visible: true,
|
||||
position: position
|
||||
})
|
||||
},
|
||||
|
||||
debugSphereOff: function() {
|
||||
Entities.editEntity(_this.debugSphere, {
|
||||
visible: false
|
||||
})
|
||||
|
||||
},
|
||||
|
||||
overlayLineOn: function(closePoint, farPoint, color) {
|
||||
if (_this.overlayLine === null) {
|
||||
var lineProperties = {
|
||||
lineWidth: 5,
|
||||
start: closePoint,
|
||||
end: farPoint,
|
||||
color: color,
|
||||
ignoreRayIntersection: true, // always ignore this
|
||||
visible: true,
|
||||
alpha: 1
|
||||
};
|
||||
this.overlayLine = Overlays.addOverlay("line3d", lineProperties);
|
||||
|
||||
} else {
|
||||
var success = Overlays.editOverlay(_this.overlayLine, {
|
||||
lineWidth: 5,
|
||||
start: closePoint,
|
||||
end: farPoint,
|
||||
color: color,
|
||||
visible: true,
|
||||
ignoreRayIntersection: true, // always ignore this
|
||||
alpha: 1
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
overlayLineOff: function() {
|
||||
if (_this.overlayLine !== null) {
|
||||
Overlays.deleteOverlay(this.overlayLine);
|
||||
}
|
||||
_this.overlayLine = null;
|
||||
},
|
||||
|
||||
seeIfOwnerIsLookingAtTheTank: function() {
|
||||
var cameraPosition = Camera.getPosition();
|
||||
var cameraOrientation = Camera.getOrientation();
|
||||
|
||||
var front = Quat.getFront(cameraOrientation);
|
||||
var pickRay = {
|
||||
origin: cameraPosition,
|
||||
direction: front
|
||||
};
|
||||
|
||||
if (WANT_LOOK_DEBUG_LINE === true) {
|
||||
_this.overlayLineOn(pickRay.origin, Vec3.sum(pickRay.origin, Vec3.multiply(front, _this.overlayLineDistance)), INTERSECT_COLOR);
|
||||
};
|
||||
|
||||
var brn = _this.userData['hifi-home-fishtank']['corners'].brn;
|
||||
var tfl = _this.userData['hifi-home-fishtank']['corners'].tfl;
|
||||
var innerContainer = _this.userData['hifi-home-fishtank'].innerContainer;
|
||||
|
||||
var intersection = Entities.findRayIntersection(pickRay, true, [innerContainer], [_this.entityID, brn, tfl]);
|
||||
|
||||
if (intersection.intersects && intersection.entityID === innerContainer) {
|
||||
//print('intersecting a tank')
|
||||
if (WANT_LOOK_DEBUG_SPHERE === true) {
|
||||
if (_this.debugSphere === null) {
|
||||
_this.debugSphereOn(intersection.intersection);
|
||||
} else {
|
||||
_this.updateDebugSphere(intersection.intersection);
|
||||
}
|
||||
}
|
||||
if (intersection.distance > LOOK_ATTRACTOR_DISTANCE) {
|
||||
if (WANT_LOOK_DEBUG_SPHERE === true) {
|
||||
_this.debugSphereOff();
|
||||
}
|
||||
return
|
||||
}
|
||||
// print('intersection:: ' + JSON.stringify(intersection));
|
||||
if (_this.hasLookAttractor === false) {
|
||||
_this.createLookAttractor(intersection.intersection, intersection.distance);
|
||||
} else if (_this.hasLookAttractor === true) {
|
||||
_this.updateLookAttractor(intersection.intersection, intersection.distance);
|
||||
}
|
||||
} else {
|
||||
if (_this.hasLookAttractor === true) {
|
||||
_this.clearLookAttractor();
|
||||
}
|
||||
}
|
||||
},
|
||||
createLookAttractor: function(position, distance) {
|
||||
_this.lookAttractor = {
|
||||
position: position,
|
||||
distance: distance
|
||||
};
|
||||
_this.hasLookAttractor = true;
|
||||
},
|
||||
updateLookAttractor: function(position, distance) {
|
||||
_this.lookAttractor = {
|
||||
position: position,
|
||||
distance: distance
|
||||
};
|
||||
},
|
||||
clearLookAttractor: function() {
|
||||
_this.hasLookAttractor = false;
|
||||
_this.lookAttractor = null;
|
||||
},
|
||||
createLookAttractorEntity: function() {
|
||||
|
||||
},
|
||||
findLookAttractorEntities: function() {
|
||||
|
||||
},
|
||||
seeIfAnyoneIsLookingAtTheTank: function() {
|
||||
|
||||
// get avatars
|
||||
// get their positions
|
||||
// get their gazes
|
||||
// check for intersection
|
||||
// add attractor for closest person (?)
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// flockOfFish.js
|
||||
// examples
|
||||
//
|
||||
// Philip Rosedale
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
// Fish smimming around in a space in front of you
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
var FISHTANK_USERDATA_KEY = 'hifi-home-fishtank'
|
||||
|
||||
var LIFETIME = 300; // Fish live for 5 minutes
|
||||
var NUM_FISH = 8;
|
||||
var TANK_DIMENSIONS = {
|
||||
x: 1.3393,
|
||||
y: 1.3515,
|
||||
z: 3.5914
|
||||
};
|
||||
|
||||
var TANK_WIDTH = TANK_DIMENSIONS.z / 2;
|
||||
var TANK_HEIGHT = TANK_DIMENSIONS.y / 2;
|
||||
|
||||
var FISH_DIMENSIONS = {
|
||||
x: 0.0149,
|
||||
y: 0.02546,
|
||||
z: 0.0823
|
||||
}
|
||||
|
||||
var MAX_SIGHT_DISTANCE = 1.5;
|
||||
var MIN_SEPARATION = 0.15;
|
||||
var AVOIDANCE_FORCE = 0.032;
|
||||
var COHESION_FORCE = 0.025;
|
||||
var ALIGNMENT_FORCE = 0.025;
|
||||
var LOOK_ATTRACTOR_FORCE = 0.02;
|
||||
var LOOK_ATTRACTOR_DISTANCE = 1.75;
|
||||
var SWIMMING_FORCE = 0.025;
|
||||
var SWIMMING_SPEED = 0.5;
|
||||
var FISH_DAMPING = 0.55;
|
||||
var FISH_ANGULAR_DAMPING = 0.55;
|
||||
|
||||
var THROTTLE = false;
|
||||
var THROTTLE_RATE = 100;
|
||||
var sinceLastUpdate = 0;
|
||||
|
||||
// var FISH_MODEL_URL = "http://hifi-content.s3.amazonaws.com/DomainContent/Home/fishTank/Fish-1.fbx";
|
||||
|
||||
// var FISH_MODEL_TWO_URL = "http://hifi-content.s3.amazonaws.com/DomainContent/Home/fishTank/Fish-2.fbx";
|
||||
var FISH_MODEL_URL = "http://hifi-content.s3.amazonaws.com/DomainContent/Home/fishTank/goodfish5.fbx";
|
||||
var FISH_MODEL_TWO_URL = "http://hifi-content.s3.amazonaws.com/DomainContent/Home/fishTank/goodfish5.fbx";
|
||||
var fishLoaded = false;
|
||||
|
||||
function randomVector(scale) {
|
||||
return {
|
||||
x: Math.random() * scale - scale / 2.0,
|
||||
y: Math.random() * scale - scale / 2.0,
|
||||
z: Math.random() * scale - scale / 2.0
|
||||
};
|
||||
}
|
||||
|
||||
function updateFish(deltaTime) {
|
||||
// print('update loop')
|
||||
|
||||
if (_this.tankLocked === true) {
|
||||
return;
|
||||
}
|
||||
if (!Entities.serversExist() || !Entities.canRez()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (THROTTLE === true) {
|
||||
sinceLastUpdate = sinceLastUpdate + deltaTime * 100;
|
||||
if (sinceLastUpdate > THROTTLE_RATE) {
|
||||
sinceLastUpdate = 0;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// print('has userdata fish??' + _this.userData['hifi-home-fishtank'].fishLoaded)
|
||||
|
||||
if (_this.userData['hifi-home-fishtank'].fishLoaded === false) {
|
||||
//no fish in the user data
|
||||
_this.tankLocked = true;
|
||||
print('NO FISH YET SO LOAD EM!!!')
|
||||
loadFish(NUM_FISH);
|
||||
var data = {
|
||||
fishLoaded: true,
|
||||
bubbleSystem: _this.userData['hifi-home-fishtank'].bubbleSystem,
|
||||
bubbleSound: _this.userData['hifi-home-fishtank'].bubbleSound,
|
||||
corners: {
|
||||
brn: _this.userData['hifi-home-fishtank'].lowerCorner,
|
||||
tfl: _this.userData['hifi-home-fishtank'].upperCorner
|
||||
},
|
||||
innerContainer: _this.userData['hifi-home-fishtank'].innerContainer,
|
||||
|
||||
}
|
||||
setEntityCustomData(FISHTANK_USERDATA_KEY, _this.entityID, data);
|
||||
_this.userData['hifi-home-fishtank'].fishLoaded = true;
|
||||
Script.setTimeout(function() {
|
||||
_this.fish = _this.findFishInTank();
|
||||
}, 2000)
|
||||
return;
|
||||
} else {
|
||||
|
||||
//fish in userdata already
|
||||
if (_this.fish === null) {
|
||||
_this.fish = _this.findFishInTank();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
var fish = _this.fish;
|
||||
// print('how many fish do i find?' + fish.length)
|
||||
|
||||
if (fish.length === 0) {
|
||||
// print('no fish...')
|
||||
return
|
||||
};
|
||||
|
||||
var averageVelocity = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
|
||||
var averagePosition = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
|
||||
|
||||
var userData = JSON.parse(_this.currentProperties.userData);
|
||||
var innerContainer = userData['hifi-home-fishtank']['innerContainer'];
|
||||
var bounds = Entities.getEntityProperties(innerContainer, "boundingBox").boundingBox;
|
||||
lowerCorner = bounds.brn;
|
||||
upperCorner = bounds.tfl;
|
||||
|
||||
// First pre-load an array with properties on all the other fish so our per-fish loop
|
||||
// isn't doing it.
|
||||
var flockProperties = [];
|
||||
for (var i = 0; i < fish.length; i++) {
|
||||
var otherProps = Entities.getEntityProperties(fish[i], ["position", "velocity", "rotation"]);
|
||||
flockProperties.push(otherProps);
|
||||
}
|
||||
|
||||
for (var i = 0; i < fish.length; i++) {
|
||||
if (fish[i]) {
|
||||
// Get only the properties we need, because that is faster
|
||||
var properties = flockProperties[i];
|
||||
// If fish has been deleted, bail
|
||||
if (properties.id != fish[i]) {
|
||||
fish[i] = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Store old values so we can check if they have changed enough to update
|
||||
var velocity = {
|
||||
x: properties.velocity.x,
|
||||
y: properties.velocity.y,
|
||||
z: properties.velocity.z
|
||||
};
|
||||
var position = {
|
||||
x: properties.position.x,
|
||||
y: properties.position.y,
|
||||
z: properties.position.z
|
||||
};
|
||||
averageVelocity = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
averagePosition = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
|
||||
var othersCounted = 0;
|
||||
for (var j = 0; j < fish.length; j++) {
|
||||
if (i != j) {
|
||||
// Get only the properties we need, because that is faster
|
||||
var otherProps = flockProperties[j];
|
||||
var separation = Vec3.distance(properties.position, otherProps.position);
|
||||
if (separation < MAX_SIGHT_DISTANCE) {
|
||||
averageVelocity = Vec3.sum(averageVelocity, otherProps.velocity);
|
||||
averagePosition = Vec3.sum(averagePosition, otherProps.position);
|
||||
othersCounted++;
|
||||
}
|
||||
if (separation < MIN_SEPARATION) {
|
||||
var pushAway = Vec3.multiply(Vec3.normalize(Vec3.subtract(properties.position, otherProps.position)), AVOIDANCE_FORCE);
|
||||
velocity = Vec3.sum(velocity, pushAway);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (othersCounted > 0) {
|
||||
averageVelocity = Vec3.multiply(averageVelocity, 1.0 / othersCounted);
|
||||
averagePosition = Vec3.multiply(averagePosition, 1.0 / othersCounted);
|
||||
// Alignment: Follow group's direction and speed
|
||||
velocity = Vec3.mix(velocity, Vec3.multiply(Vec3.normalize(averageVelocity), Vec3.length(velocity)), ALIGNMENT_FORCE);
|
||||
// Cohesion: Steer towards center of flock
|
||||
var towardCenter = Vec3.subtract(averagePosition, position);
|
||||
velocity = Vec3.mix(velocity, Vec3.multiply(Vec3.normalize(towardCenter), Vec3.length(velocity)), COHESION_FORCE);
|
||||
|
||||
//attractors
|
||||
//[position, radius, force]
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (_this.hasLookAttractor === true) {
|
||||
//print('has a look attractor, so use it')
|
||||
var attractorPosition = _this.lookAttractor.position;
|
||||
var towardAttractor = Vec3.subtract(attractorPosition, position);
|
||||
velocity = Vec3.mix(velocity, Vec3.multiply(Vec3.normalize(towardAttractor), Vec3.length(velocity)), LOOK_ATTRACTOR_FORCE);
|
||||
}
|
||||
|
||||
// Try to swim at a constant speed
|
||||
velocity = Vec3.mix(velocity, Vec3.multiply(Vec3.normalize(velocity), SWIMMING_SPEED), SWIMMING_FORCE);
|
||||
|
||||
// Keep fish in their 'tank'
|
||||
if (position.x < lowerCorner.x) {
|
||||
position.x = lowerCorner.x;
|
||||
velocity.x *= -0.15
|
||||
} else if (position.x > upperCorner.x) {
|
||||
position.x = upperCorner.x;
|
||||
velocity.x *= -0.15
|
||||
}
|
||||
if (position.y < lowerCorner.y) {
|
||||
position.y = lowerCorner.y;
|
||||
velocity.y *= -0.15
|
||||
} else if (position.y > upperCorner.y) {
|
||||
position.y = upperCorner.y;
|
||||
velocity.y *= -0.15
|
||||
}
|
||||
if (position.z < lowerCorner.z) {
|
||||
position.z = lowerCorner.z;
|
||||
velocity.z *= -0.15
|
||||
} else if (position.z > upperCorner.z) {
|
||||
position.z = upperCorner.z;
|
||||
velocity.z *= -0.15
|
||||
}
|
||||
|
||||
// Orient in direction of velocity
|
||||
var rotation = Quat.rotationBetween(Vec3.UNIT_NEG_Z, velocity);
|
||||
|
||||
var mixedRotation = Quat.mix(properties.rotation, rotation, VELOCITY_FOLLOW_RATE);
|
||||
var VELOCITY_FOLLOW_RATE = 0.30;
|
||||
|
||||
var slerpedRotation = Quat.slerp(properties.rotation, rotation, VELOCITY_FOLLOW_RATE);
|
||||
|
||||
var safeEuler = Quat.safeEulerAngles(rotation);
|
||||
safeEuler.z = safeEuler.z *= 0.925;
|
||||
|
||||
//note: a we want the fish to both rotate toward its velocity and not roll over, and also not pitch more than 30 degrees positive or negative (not doing that last bit right quite yet)
|
||||
var newQuat = Quat.fromPitchYawRollDegrees(safeEuler.x, safeEuler.y, safeEuler.z);
|
||||
|
||||
var finalQuat = Quat.slerp(slerpedRotation, newQuat, 0.5);
|
||||
|
||||
|
||||
// Only update properties if they have changed, to save bandwidth
|
||||
var MIN_POSITION_CHANGE_FOR_UPDATE = 0.001;
|
||||
if (Vec3.distance(properties.position, position) < MIN_POSITION_CHANGE_FOR_UPDATE) {
|
||||
Entities.editEntity(fish[i], {
|
||||
velocity: velocity,
|
||||
rotation: finalQuat
|
||||
});
|
||||
} else {
|
||||
Entities.editEntity(fish[i], {
|
||||
position: position,
|
||||
velocity: velocity,
|
||||
rotation: finalQuat
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var STARTING_FRACTION = 0.25;
|
||||
|
||||
function loadFish(howMany) {
|
||||
// print('LOADING FISH: ' + howMany)
|
||||
|
||||
var center = _this.currentProperties.position;
|
||||
|
||||
lowerCorner = {
|
||||
x: center.x - (_this.currentProperties.dimensions.z / 2),
|
||||
y: center.y,
|
||||
z: center.z - (_this.currentProperties.dimensions.z / 2)
|
||||
};
|
||||
upperCorner = {
|
||||
x: center.x + (_this.currentProperties.dimensions.z / 2),
|
||||
y: center.y + _this.currentProperties.dimensions.y,
|
||||
z: center.z + (_this.currentProperties.dimensions.z / 2)
|
||||
};
|
||||
|
||||
var fish = [];
|
||||
|
||||
for (var i = 0; i < howMany; i++) {
|
||||
var position = {
|
||||
x: lowerCorner.x + (upperCorner.x - lowerCorner.x) / 2.0 + (Math.random() - 0.5) * (upperCorner.x - lowerCorner.x) * STARTING_FRACTION,
|
||||
y: lowerCorner.y + (upperCorner.y - lowerCorner.y) / 2.0 + (Math.random() - 0.5) * (upperCorner.y - lowerCorner.y) * STARTING_FRACTION,
|
||||
z: lowerCorner.z + (upperCorner.z - lowerCorner.z) / 2.0 + (Math.random() - 0.5) * (upperCorner.z - lowerCorner.z) * STARTING_FRACTION
|
||||
};
|
||||
|
||||
|
||||
fish.push(
|
||||
Entities.addEntity({
|
||||
name: 'hifi-fishtank-fish' + _this.entityID,
|
||||
type: "Model",
|
||||
modelURL: fish.length % 2 === 0 ? FISH_MODEL_URL : FISH_MODEL_TWO_URL,
|
||||
position: position,
|
||||
parentID: _this.entityID,
|
||||
// rotation: {
|
||||
// x: 0,
|
||||
// y: 0,
|
||||
// z: 0,
|
||||
// w: 1
|
||||
// },
|
||||
// type: "Box",
|
||||
dimensions: FISH_DIMENSIONS,
|
||||
velocity: {
|
||||
x: SWIMMING_SPEED,
|
||||
y: SWIMMING_SPEED,
|
||||
z: SWIMMING_SPEED
|
||||
},
|
||||
damping: FISH_DAMPING,
|
||||
angularDamping: FISH_ANGULAR_DAMPING,
|
||||
dynamic: false,
|
||||
// lifetime: LIFETIME,
|
||||
color: {
|
||||
red: 0,
|
||||
green: 255,
|
||||
blue: 255
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
// print('initial fish::' + fish.length)
|
||||
_this.tankLocked = false;
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
Script.update.disconnect(_this.update);
|
||||
})
|
||||
|
||||
|
||||
function setEntityUserData(id, data) {
|
||||
var json = JSON.stringify(data)
|
||||
Entities.editEntity(id, {
|
||||
userData: json
|
||||
});
|
||||
}
|
||||
|
||||
// FIXME do non-destructive modification of the existing user data
|
||||
function getEntityUserData(id) {
|
||||
var results = null;
|
||||
var properties = Entities.getEntityProperties(id, "userData");
|
||||
if (properties.userData) {
|
||||
try {
|
||||
results = JSON.parse(properties.userData);
|
||||
} catch (err) {
|
||||
// print('error parsing json');
|
||||
// print('properties are:'+ properties.userData);
|
||||
}
|
||||
}
|
||||
return results ? results : {};
|
||||
}
|
||||
|
||||
|
||||
// Non-destructively modify the user data of an entity.
|
||||
function setEntityCustomData(customKey, id, data) {
|
||||
var userData = getEntityUserData(id);
|
||||
if (data == null) {
|
||||
delete userData[customKey];
|
||||
} else {
|
||||
userData[customKey] = data;
|
||||
}
|
||||
setEntityUserData(id, userData);
|
||||
}
|
||||
|
||||
function getEntityCustomData(customKey, id, defaultValue) {
|
||||
var userData = getEntityUserData(id);
|
||||
if (undefined != userData[customKey]) {
|
||||
return userData[customKey];
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
return new FishTank();
|
||||
});
|
Loading…
Reference in a new issue