mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-17 12:49:49 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into 19507
This commit is contained in:
commit
a1c31522ba
57 changed files with 1004 additions and 225 deletions
|
@ -54,7 +54,7 @@
|
|||
const short JITTER_BUFFER_MSECS = 12;
|
||||
const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_MSECS * (SAMPLE_RATE / 1000.0);
|
||||
|
||||
const float LOUDNESS_TO_DISTANCE_RATIO = 0.00305f;
|
||||
const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f;
|
||||
|
||||
const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer";
|
||||
|
||||
|
@ -348,6 +348,16 @@ void AudioMixer::readPendingDatagrams() {
|
|||
|| mixerPacketType == PacketTypeSilentAudioFrame) {
|
||||
|
||||
nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket);
|
||||
} else if (mixerPacketType == PacketTypeMuteEnvironment) {
|
||||
QByteArray packet = receivedPacket;
|
||||
populatePacketHeader(packet, PacketTypeMuteEnvironment);
|
||||
|
||||
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
||||
if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData() && node != nodeList->sendingNodeForPacket(receivedPacket)) {
|
||||
nodeList->writeDatagram(packet, packet.size(), node);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// let processNodeData handle it.
|
||||
nodeList->processNodeData(senderSockAddr, receivedPacket);
|
||||
|
|
96
examples/airGuitar.js
Normal file
96
examples/airGuitar.js
Normal file
|
@ -0,0 +1,96 @@
|
|||
//
|
||||
// airGuitar.js
|
||||
// examples
|
||||
//
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// This example musical instrument script plays guitar chords based on a strum motion and hand position
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
function length(v) {
|
||||
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
|
||||
}
|
||||
|
||||
|
||||
function printVector(v) {
|
||||
print(v.x + ", " + v.y + ", " + v.z);
|
||||
return;
|
||||
}
|
||||
|
||||
function vMinus(a, b) {
|
||||
var rval = { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
|
||||
return rval;
|
||||
}
|
||||
|
||||
// First, load two percussion sounds to be used on the sticks
|
||||
|
||||
var guitarType = 2;
|
||||
|
||||
if (guitarType == 1) {
|
||||
var chord1 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+A.raw");
|
||||
var chord2 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+B.raw");
|
||||
var chord3 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+E.raw");
|
||||
} else {
|
||||
var chord1 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+A+short.raw");
|
||||
var chord2 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+B+short.raw");
|
||||
var chord3 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+E+short.raw");
|
||||
}
|
||||
|
||||
|
||||
var whichChord = chord1;
|
||||
|
||||
var leftHanded = false;
|
||||
if (leftHanded) {
|
||||
var strumHand = 0;
|
||||
var chordHand = 1;
|
||||
} else {
|
||||
var strumHand = 1;
|
||||
var chordHand = 0;
|
||||
}
|
||||
|
||||
var lastPosition = { x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0 };
|
||||
|
||||
|
||||
function checkHands(deltaTime) {
|
||||
for (var palm = 0; palm < 2; palm++) {
|
||||
var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1);
|
||||
var speed = length(palmVelocity);
|
||||
var position = Controller.getSpatialControlPosition(palm * 2 + 1);
|
||||
var myPelvis = MyAvatar.position;
|
||||
|
||||
if (palm == strumHand) {
|
||||
|
||||
var STRUM_HEIGHT_ABOVE_PELVIS = -0.30;
|
||||
var strumTriggerHeight = myPelvis.y + STRUM_HEIGHT_ABOVE_PELVIS;
|
||||
//printVector(position);
|
||||
if ((position.y < strumTriggerHeight) && (lastPosition.y >= strumTriggerHeight)) {
|
||||
// If hand passes downward through guitar strings, play a chord!
|
||||
var options = new AudioInjectionOptions();
|
||||
options.position = position;
|
||||
if (speed > 1.0) { speed = 1.0; }
|
||||
options.volume = speed;
|
||||
Audio.playSound(whichChord, options);
|
||||
}
|
||||
lastPosition = Controller.getSpatialControlPosition(palm * 2 + 1);
|
||||
} else {
|
||||
// This is the chord controller
|
||||
var distanceFromPelvis = Vec3.length(Vec3.subtract(position, myPelvis));
|
||||
//print(distanceFromPelvis);
|
||||
if (distanceFromPelvis > 0.63) {
|
||||
whichChord = chord3;
|
||||
} else if (distanceFromPelvis > 0.55) {
|
||||
whichChord = chord2;
|
||||
} else {
|
||||
whichChord = chord1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Connect a call back that happens every frame
|
||||
Script.update.connect(checkHands);
|
|
@ -370,7 +370,7 @@ function moveOverlays() {
|
|||
toolsX = windowDimensions.x - 8 - toolWidth;
|
||||
toolsY = (windowDimensions.y - toolsHeight) / 2;
|
||||
|
||||
Overlays.addOverlay(firstModel, {
|
||||
Overlays.editOverlay(firstModel, {
|
||||
x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * 0), width: toolWidth, height: toolHeight,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ var radiusMinimum = 0.05;
|
|||
var radiusMaximum = 0.5;
|
||||
|
||||
var modelURLs = [
|
||||
"https://s3-us-west-1.amazonaws.com/highfidelity-public/models/music/EVHFrankenstein.fbx",
|
||||
"https://s3-us-west-1.amazonaws.com/highfidelity-public/models/attachments/topHat.fst",
|
||||
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX",
|
||||
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/birarda/birarda_head.fbx",
|
||||
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/pug.fbx",
|
||||
|
@ -72,7 +72,6 @@ function keyPressEvent(event) {
|
|||
}
|
||||
} else if (event.text == "m") {
|
||||
var URL = Window.prompt("Model URL", "Enter URL, e.g. http://foo.com/model.fbx");
|
||||
Window.alert("Your response was: " + prompt);
|
||||
var modelPosition = getNewVoxelPosition();
|
||||
var properties = { position: { x: modelPosition.x,
|
||||
y: modelPosition.y,
|
||||
|
|
47
interface/resources/shaders/model_normal_specular_map.frag
Normal file
47
interface/resources/shaders/model_normal_specular_map.frag
Normal file
|
@ -0,0 +1,47 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// model_normal_specular_map.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/6/14.
|
||||
// Copyright 2014 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
|
||||
//
|
||||
|
||||
// the diffuse texture
|
||||
uniform sampler2D diffuseMap;
|
||||
|
||||
// the normal map texture
|
||||
uniform sampler2D normalMap;
|
||||
|
||||
// the specular map texture
|
||||
uniform sampler2D specularMap;
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 interpolatedNormal;
|
||||
|
||||
// the interpolated tangent
|
||||
varying vec4 interpolatedTangent;
|
||||
|
||||
void main(void) {
|
||||
vec3 normalizedNormal = normalize(vec3(interpolatedNormal));
|
||||
vec3 normalizedTangent = normalize(vec3(interpolatedTangent));
|
||||
vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent));
|
||||
vec3 localNormal = vec3(texture2D(normalMap, gl_TexCoord[0].st)) * 2.0 - vec3(1.0, 1.0, 1.0);
|
||||
|
||||
// compute the base color based on OpenGL lighting model
|
||||
vec4 viewNormal = vec4(normalizedTangent * localNormal.x +
|
||||
normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0);
|
||||
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
|
||||
gl_FrontLightProduct[0].diffuse * max(0.0, dot(viewNormal, gl_LightSource[0].position)));
|
||||
|
||||
// compute the specular component (sans exponent)
|
||||
float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), viewNormal));
|
||||
|
||||
// modulate texture by base color and add specular contribution
|
||||
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + vec4(pow(specular, gl_FrontMaterial.shininess) *
|
||||
gl_FrontLightProduct[0].specular.rgb * texture2D(specularMap, gl_TexCoord[0].st).rgb, 0.0);
|
||||
}
|
35
interface/resources/shaders/model_specular_map.frag
Normal file
35
interface/resources/shaders/model_specular_map.frag
Normal file
|
@ -0,0 +1,35 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// model_specular_map.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/6/14.
|
||||
// Copyright 2014 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
|
||||
//
|
||||
|
||||
// the diffuse texture
|
||||
uniform sampler2D diffuseMap;
|
||||
|
||||
// the specular texture
|
||||
uniform sampler2D specularMap;
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 normal;
|
||||
|
||||
void main(void) {
|
||||
// compute the base color based on OpenGL lighting model
|
||||
vec4 normalizedNormal = normalize(normal);
|
||||
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
|
||||
gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[0].position)));
|
||||
|
||||
// compute the specular component (sans exponent)
|
||||
float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), normalizedNormal));
|
||||
|
||||
// modulate texture by base color and add specular contribution
|
||||
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + vec4(pow(specular, gl_FrontMaterial.shininess) *
|
||||
gl_FrontLightProduct[0].specular.rgb * texture2D(specularMap, gl_TexCoord[0].st).rgb, 0.0);
|
||||
}
|
|
@ -151,6 +151,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
_lastQueriedTime(usecTimestampNow()),
|
||||
_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
|
||||
_cameraPushback(0.0f),
|
||||
_scaleMirror(1.0f),
|
||||
_mouseX(0),
|
||||
_mouseY(0),
|
||||
_lastMouseMove(usecTimestampNow()),
|
||||
|
@ -570,9 +571,9 @@ void Application::paintGL() {
|
|||
_myCamera.setTightness(0.0f);
|
||||
glm::vec3 eyePosition = _myAvatar->getHead()->calculateAverageEyePosition();
|
||||
float headHeight = eyePosition.y - _myAvatar->getPosition().y;
|
||||
_myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _myAvatar->getScale());
|
||||
_myCamera.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight, 0));
|
||||
_myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f)));
|
||||
_myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _myAvatar->getScale() * _scaleMirror);
|
||||
_myCamera.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight + (_raiseMirror * _myAvatar->getScale()), 0));
|
||||
_myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)));
|
||||
|
||||
// if the head would intersect the near clip plane, we must push the camera out
|
||||
glm::vec3 relativePosition = glm::inverse(_myCamera.getTargetRotation()) *
|
||||
|
@ -868,19 +869,43 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
break;
|
||||
|
||||
case Qt::Key_Up:
|
||||
_myAvatar->setDriveKeys(isShifted ? UP : FWD, 1.f);
|
||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
if (!isShifted) {
|
||||
_scaleMirror *= 0.95f;
|
||||
} else {
|
||||
_raiseMirror += 0.05f;
|
||||
}
|
||||
} else {
|
||||
_myAvatar->setDriveKeys(isShifted ? UP : FWD, 1.f);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Down:
|
||||
_myAvatar->setDriveKeys(isShifted ? DOWN : BACK, 1.f);
|
||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
if (!isShifted) {
|
||||
_scaleMirror *= 1.05f;
|
||||
} else {
|
||||
_raiseMirror -= 0.05f;
|
||||
}
|
||||
} else {
|
||||
_myAvatar->setDriveKeys(isShifted ? DOWN : BACK, 1.f);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Left:
|
||||
_myAvatar->setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1.f);
|
||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
_rotateMirror += PI / 20.f;
|
||||
} else {
|
||||
_myAvatar->setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1.f);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Right:
|
||||
_myAvatar->setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1.f);
|
||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
_rotateMirror -= PI / 20.f;
|
||||
} else {
|
||||
_myAvatar->setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1.f);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_I:
|
||||
|
@ -2348,8 +2373,8 @@ void Application::updateShadowMap() {
|
|||
updateUntranslatedViewMatrix();
|
||||
|
||||
_avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE);
|
||||
_particles.render();
|
||||
_models.render();
|
||||
_particles.render(OctreeRenderer::SHADOW_RENDER_MODE);
|
||||
_models.render(OctreeRenderer::SHADOW_RENDER_MODE);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
|
|
|
@ -456,6 +456,10 @@ private:
|
|||
glm::mat4 _untranslatedViewMatrix;
|
||||
glm::vec3 _viewMatrixTranslation;
|
||||
glm::mat4 _projectionMatrix;
|
||||
|
||||
float _scaleMirror;
|
||||
float _rotateMirror;
|
||||
float _raiseMirror;
|
||||
|
||||
glm::mat4 _shadowMatrix;
|
||||
|
||||
|
|
|
@ -131,6 +131,20 @@ void DatagramProcessor::processDatagrams() {
|
|||
|
||||
break;
|
||||
}
|
||||
case PacketTypeMuteEnvironment: {
|
||||
glm::vec3 position;
|
||||
float radius;
|
||||
|
||||
int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment);
|
||||
memcpy(&position, incomingPacket.constData() + headerSize, sizeof(glm::vec3));
|
||||
memcpy(&radius, incomingPacket.constData() + headerSize + sizeof(glm::vec3), sizeof(float));
|
||||
|
||||
if (glm::distance(Application::getInstance()->getAvatar()->getPosition(), position) < radius
|
||||
&& !Application::getInstance()->getAudio()->getMuted()) {
|
||||
Application::getInstance()->getAudio()->toggleMute();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
nodeList->processNodeData(senderSockAddr, incomingPacket);
|
||||
break;
|
||||
|
|
|
@ -68,6 +68,7 @@ const float DEFAULT_FACESHIFT_EYE_DEFLECTION = 0.25f;
|
|||
const float DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER = 1.0f;
|
||||
const int ONE_SECOND_OF_FRAMES = 60;
|
||||
const int FIVE_SECONDS_OF_FRAMES = 5 * ONE_SECOND_OF_FRAMES;
|
||||
const float MUTE_RADIUS = 50;
|
||||
|
||||
Menu::Menu() :
|
||||
_actionHash(),
|
||||
|
@ -291,7 +292,6 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::BuckyBalls, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Particles, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Models, 0, true);
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools()));
|
||||
|
||||
QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxel Options");
|
||||
|
@ -308,6 +308,12 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontFadeOnVoxelServerChanges);
|
||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DisableAutoAdjustLOD);
|
||||
|
||||
QMenu* modelOptionsMenu = developerMenu->addMenu("Model Options");
|
||||
addCheckableActionToQMenuAndActionHash(modelOptionsMenu, MenuOption::Models, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(modelOptionsMenu, MenuOption::DisplayModelBounds, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(modelOptionsMenu, MenuOption::DisplayModelElementProxy, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(modelOptionsMenu, MenuOption::DisplayModelElementChildProxies, 0, false);
|
||||
|
||||
QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true);
|
||||
|
@ -397,6 +403,11 @@ Menu::Menu() :
|
|||
false,
|
||||
appInstance->getAudio(),
|
||||
SLOT(toggleMute()));
|
||||
addActionToQMenuAndActionHash(audioDebugMenu,
|
||||
MenuOption::MuteEnvironment,
|
||||
0,
|
||||
this,
|
||||
SLOT(muteEnvironment()));
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioToneInjection,
|
||||
0,
|
||||
false,
|
||||
|
@ -1000,6 +1011,30 @@ void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJson
|
|||
disconnect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision);
|
||||
}
|
||||
|
||||
void Menu::muteEnvironment() {
|
||||
int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment);
|
||||
int packetSize = headerSize + sizeof(glm::vec3) + sizeof(float);
|
||||
|
||||
glm::vec3 position = Application::getInstance()->getAvatar()->getPosition();
|
||||
|
||||
char* packet = (char*)malloc(packetSize);
|
||||
populatePacketHeader(packet, PacketTypeMuteEnvironment);
|
||||
memcpy(packet + headerSize, &position, sizeof(glm::vec3));
|
||||
memcpy(packet + headerSize + sizeof(glm::vec3), &MUTE_RADIUS, sizeof(float));
|
||||
|
||||
QByteArray mutePacket(packet, packetSize);
|
||||
|
||||
// grab our audio mixer from the NodeList, if it exists
|
||||
SharedNodePointer audioMixer = NodeList::getInstance()->soloNodeOfType(NodeType::AudioMixer);
|
||||
|
||||
if (audioMixer) {
|
||||
// send off this mute packet
|
||||
NodeList::getInstance()->writeDatagram(mutePacket, audioMixer);
|
||||
}
|
||||
|
||||
free(packet);
|
||||
}
|
||||
|
||||
void Menu::goToLocation() {
|
||||
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
|
||||
glm::vec3 avatarPos = myAvatar->getPosition();
|
||||
|
|
|
@ -191,6 +191,7 @@ private slots:
|
|||
void audioMuteToggled();
|
||||
void namedLocationCreated(LocationManager::NamedLocationCreateResponse response);
|
||||
void multipleDestinationsDecision(const QJsonObject& userData, const QJsonObject& placeData);
|
||||
void muteEnvironment();
|
||||
|
||||
private:
|
||||
static Menu* _instance;
|
||||
|
@ -303,6 +304,9 @@ namespace MenuOption {
|
|||
const QString DisplayFrustum = "Display Frustum";
|
||||
const QString DisplayHands = "Display Hands";
|
||||
const QString DisplayHandTargets = "Display Hand Targets";
|
||||
const QString DisplayModelBounds = "Display Model Bounds";
|
||||
const QString DisplayModelElementProxy = "Display Model Element Bounds";
|
||||
const QString DisplayModelElementChildProxies = "Display Model Element Children";
|
||||
const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes";
|
||||
const QString EchoLocalAudio = "Echo Local Audio";
|
||||
const QString EchoServerAudio = "Echo Server Audio";
|
||||
|
@ -337,8 +341,10 @@ namespace MenuOption {
|
|||
const QString Metavoxels = "Metavoxels";
|
||||
const QString Mirror = "Mirror";
|
||||
const QString Models = "Models";
|
||||
const QString ModelOptions = "Model Options";
|
||||
const QString MoveWithLean = "Move with Lean";
|
||||
const QString MuteAudio = "Mute Microphone";
|
||||
const QString MuteEnvironment = "Mute Environment";
|
||||
const QString NameLocation = "Name this location";
|
||||
const QString NewVoxelCullingMode = "New Voxel Culling Mode";
|
||||
const QString OctreeStats = "Voxel and Particle Statistics";
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QDebug>
|
||||
#include <QDialogButtonBox>
|
||||
|
@ -42,6 +43,9 @@ static const QString TEXDIR_FIELD = "texdir";
|
|||
static const QString LOD_FIELD = "lod";
|
||||
static const QString JOINT_INDEX_FIELD = "jointIndex";
|
||||
static const QString SCALE_FIELD = "scale";
|
||||
static const QString TRANSLATION_X_FIELD = "tx";
|
||||
static const QString TRANSLATION_Y_FIELD = "ty";
|
||||
static const QString TRANSLATION_Z_FIELD = "tz";
|
||||
static const QString JOINT_FIELD = "joint";
|
||||
static const QString FREE_JOINT_FIELD = "freeJoint";
|
||||
|
||||
|
@ -443,6 +447,14 @@ bool ModelUploader::addTextures(const QString& texdir, const FBXGeometry& geomet
|
|||
}
|
||||
added.insert(part.normalTexture.filename);
|
||||
}
|
||||
if (!part.specularTexture.filename.isEmpty() && part.specularTexture.content.isEmpty() &&
|
||||
!added.contains(part.specularTexture.filename)) {
|
||||
if (!addPart(texdir + "/" + part.specularTexture.filename,
|
||||
QString("texture%1").arg(++_texturesCount), true)) {
|
||||
return false;
|
||||
}
|
||||
added.insert(part.specularTexture.filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -511,6 +523,14 @@ bool ModelUploader::addPart(const QFile& file, const QByteArray& contents, const
|
|||
return true;
|
||||
}
|
||||
|
||||
static QDoubleSpinBox* createTranslationBox() {
|
||||
QDoubleSpinBox* box = new QDoubleSpinBox();
|
||||
const double MAX_TRANSLATION = 1000000.0;
|
||||
box->setMinimum(-MAX_TRANSLATION);
|
||||
box->setMaximum(MAX_TRANSLATION);
|
||||
return box;
|
||||
}
|
||||
|
||||
ModelPropertiesDialog::ModelPropertiesDialog(ModelType modelType, const QVariantHash& originalMapping,
|
||||
const QString& basePath, const FBXGeometry& geometry) :
|
||||
_modelType(modelType),
|
||||
|
@ -532,7 +552,18 @@ ModelPropertiesDialog::ModelPropertiesDialog(ModelType modelType, const QVariant
|
|||
_scale->setMaximum(FLT_MAX);
|
||||
_scale->setSingleStep(0.01);
|
||||
|
||||
if (_modelType != ATTACHMENT_MODEL) {
|
||||
if (_modelType == ATTACHMENT_MODEL) {
|
||||
QHBoxLayout* translation = new QHBoxLayout();
|
||||
form->addRow("Translation:", translation);
|
||||
translation->addWidget(_translationX = createTranslationBox());
|
||||
translation->addWidget(_translationY = createTranslationBox());
|
||||
translation->addWidget(_translationZ = createTranslationBox());
|
||||
form->addRow("Pivot About Center:", _pivotAboutCenter = new QCheckBox());
|
||||
form->addRow("Pivot Joint:", _pivotJoint = createJointBox());
|
||||
connect(_pivotAboutCenter, SIGNAL(toggled(bool)), SLOT(updatePivotJoint()));
|
||||
_pivotAboutCenter->setChecked(true);
|
||||
|
||||
} else {
|
||||
form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox());
|
||||
form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox());
|
||||
form->addRow("Neck Joint:", _neckJoint = createJointBox());
|
||||
|
@ -576,7 +607,19 @@ QVariantHash ModelPropertiesDialog::getMapping() const {
|
|||
mapping.insert(JOINT_INDEX_FIELD, jointIndices);
|
||||
|
||||
QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
|
||||
if (_modelType != ATTACHMENT_MODEL) {
|
||||
if (_modelType == ATTACHMENT_MODEL) {
|
||||
glm::vec3 pivot;
|
||||
if (_pivotAboutCenter->isChecked()) {
|
||||
pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f;
|
||||
|
||||
} else if (_pivotJoint->currentIndex() != 0) {
|
||||
pivot = extractTranslation(_geometry.joints.at(_pivotJoint->currentIndex() - 1).transform);
|
||||
}
|
||||
mapping.insert(TRANSLATION_X_FIELD, -pivot.x * _scale->value() + _translationX->value());
|
||||
mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * _scale->value() + _translationY->value());
|
||||
mapping.insert(TRANSLATION_Z_FIELD, -pivot.z * _scale->value() + _translationZ->value());
|
||||
|
||||
} else {
|
||||
insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText());
|
||||
insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText());
|
||||
insertJointMapping(joints, "jointNeck", _neckJoint->currentText());
|
||||
|
@ -609,7 +652,14 @@ void ModelPropertiesDialog::reset() {
|
|||
_scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble());
|
||||
|
||||
QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash();
|
||||
if (_modelType != ATTACHMENT_MODEL) {
|
||||
if (_modelType == ATTACHMENT_MODEL) {
|
||||
_translationX->setValue(_originalMapping.value(TRANSLATION_X_FIELD).toDouble());
|
||||
_translationY->setValue(_originalMapping.value(TRANSLATION_Y_FIELD).toDouble());
|
||||
_translationZ->setValue(_originalMapping.value(TRANSLATION_Z_FIELD).toDouble());
|
||||
_pivotAboutCenter->setChecked(true);
|
||||
_pivotJoint->setCurrentIndex(0);
|
||||
|
||||
} else {
|
||||
setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString());
|
||||
setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString());
|
||||
setJointText(_neckJoint, jointHash.value("jointNeck").toString());
|
||||
|
@ -646,6 +696,10 @@ void ModelPropertiesDialog::chooseTextureDirectory() {
|
|||
_textureDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1));
|
||||
}
|
||||
|
||||
void ModelPropertiesDialog::updatePivotJoint() {
|
||||
_pivotJoint->setEnabled(!_pivotAboutCenter->isChecked());
|
||||
}
|
||||
|
||||
void ModelPropertiesDialog::createNewFreeJoint(const QString& joint) {
|
||||
QWidget* freeJoint = new QWidget();
|
||||
QHBoxLayout* freeJointLayout = new QHBoxLayout();
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "ui/ModelsBrowser.h"
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QDoubleSpinBox;
|
||||
class QFileInfo;
|
||||
|
@ -83,6 +84,7 @@ public:
|
|||
private slots:
|
||||
void reset();
|
||||
void chooseTextureDirectory();
|
||||
void updatePivotJoint();
|
||||
void createNewFreeJoint(const QString& joint = QString());
|
||||
|
||||
private:
|
||||
|
@ -96,6 +98,11 @@ private:
|
|||
QLineEdit* _name;
|
||||
QPushButton* _textureDirectory;
|
||||
QDoubleSpinBox* _scale;
|
||||
QDoubleSpinBox* _translationX;
|
||||
QDoubleSpinBox* _translationY;
|
||||
QDoubleSpinBox* _translationZ;
|
||||
QCheckBox* _pivotAboutCenter;
|
||||
QComboBox* _pivotJoint;
|
||||
QComboBox* _leftEyeJoint;
|
||||
QComboBox* _rightEyeJoint;
|
||||
QComboBox* _neckJoint;
|
||||
|
|
|
@ -215,17 +215,20 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
|
|||
if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
|
||||
renderBody(renderMode, glowLevel);
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) {
|
||||
if (renderMode != SHADOW_RENDER_MODE &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) {
|
||||
_skeletonModel.updateShapePositions();
|
||||
_skeletonModel.renderJointCollisionShapes(0.7f);
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes)) {
|
||||
if (renderMode != SHADOW_RENDER_MODE &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes)) {
|
||||
if (shouldRenderHead(cameraPosition, renderMode)) {
|
||||
getHead()->getFaceModel().updateShapePositions();
|
||||
getHead()->getFaceModel().renderJointCollisionShapes(0.7f);
|
||||
}
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes)) {
|
||||
if (renderMode != SHADOW_RENDER_MODE &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes)) {
|
||||
if (shouldRenderHead(cameraPosition, renderMode)) {
|
||||
getHead()->getFaceModel().updateShapePositions();
|
||||
getHead()->getFaceModel().renderBoundingCollisionShapes(0.7f);
|
||||
|
@ -234,7 +237,7 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
|
|||
}
|
||||
}
|
||||
// If this is the avatar being looked at, render a little ball above their head
|
||||
if (_isLookAtTarget) {
|
||||
if (renderMode != SHADOW_RENDER_MODE &&_isLookAtTarget) {
|
||||
const float LOOK_AT_INDICATOR_RADIUS = 0.03f;
|
||||
const float LOOK_AT_INDICATOR_HEIGHT = 0.60f;
|
||||
const float LOOK_AT_INDICATOR_COLOR[] = { 0.8f, 0.0f, 0.0f, 0.5f };
|
||||
|
@ -340,7 +343,8 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
|
|||
|
||||
void Avatar::renderBody(RenderMode renderMode, float glowLevel) {
|
||||
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
|
||||
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
|
||||
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
|
||||
|
||||
{
|
||||
Glower glower(glowLevel);
|
||||
|
||||
|
@ -351,7 +355,7 @@ void Avatar::renderBody(RenderMode renderMode, float glowLevel) {
|
|||
}
|
||||
_skeletonModel.render(1.0f, modelRenderMode);
|
||||
renderAttachments(modelRenderMode);
|
||||
getHand()->render(false);
|
||||
getHand()->render(false, modelRenderMode);
|
||||
}
|
||||
getHead()->render(1.0f, modelRenderMode);
|
||||
}
|
||||
|
@ -634,8 +638,8 @@ bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float parti
|
|||
penetration)) {
|
||||
CollisionInfo* collision = collisions.getNewCollision();
|
||||
if (collision) {
|
||||
collision->_type = PADDLE_HAND_COLLISION;
|
||||
collision->_flags = jointIndex;
|
||||
collision->_type = COLLISION_TYPE_PADDLE_HAND;
|
||||
collision->_intData = jointIndex;
|
||||
collision->_penetration = penetration;
|
||||
collision->_addedVelocity = palm->getVelocity();
|
||||
collided = true;
|
||||
|
@ -844,11 +848,11 @@ float Avatar::getHeadHeight() const {
|
|||
}
|
||||
|
||||
bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const {
|
||||
if (!collision._data || collision._type != MODEL_COLLISION) {
|
||||
if (!collision._data || collision._type != COLLISION_TYPE_MODEL) {
|
||||
return false;
|
||||
}
|
||||
Model* model = static_cast<Model*>(collision._data);
|
||||
int jointIndex = collision._flags;
|
||||
int jointIndex = collision._intData;
|
||||
|
||||
if (model == &(_skeletonModel) && jointIndex != -1) {
|
||||
// collision response of skeleton is temporarily disabled
|
||||
|
@ -856,7 +860,7 @@ bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const {
|
|||
//return _skeletonModel.collisionHitsMoveableJoint(collision);
|
||||
}
|
||||
if (model == &(getHead()->getFaceModel())) {
|
||||
// ATM we always handle MODEL_COLLISIONS against the face.
|
||||
// ATM we always handle COLLISION_TYPE_MODEL against the face.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -192,11 +192,11 @@ void Hand::calculateGeometry() {
|
|||
}
|
||||
}
|
||||
|
||||
void Hand::render(bool isMine) {
|
||||
|
||||
void Hand::render(bool isMine, Model::RenderMode renderMode) {
|
||||
_renderAlpha = 1.0;
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) {
|
||||
if (renderMode != Model::SHADOW_RENDER_MODE &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) {
|
||||
// draw a green sphere at hand joint location, which is actually near the wrist)
|
||||
for (size_t i = 0; i < getNumPalms(); i++) {
|
||||
PalmData& palm = getPalms()[i];
|
||||
|
@ -212,7 +212,7 @@ void Hand::render(bool isMine) {
|
|||
}
|
||||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayHands)) {
|
||||
if (renderMode != Model::SHADOW_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHands)) {
|
||||
renderLeapHands(isMine);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <HandData.h>
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
#include "renderer/Model.h"
|
||||
#include "world.h"
|
||||
|
||||
|
||||
|
@ -52,7 +53,7 @@ public:
|
|||
void init();
|
||||
void reset();
|
||||
void simulate(float deltaTime, bool isMine);
|
||||
void render(bool isMine);
|
||||
void render(bool isMine, Model::RenderMode renderMode = Model::DEFAULT_RENDER_MODE);
|
||||
|
||||
// getters
|
||||
const glm::vec3& getLeapFingerTipBallPosition (int ball) const { return _leapFingerTipBalls [ball].position;}
|
||||
|
|
|
@ -182,7 +182,7 @@ void Head::relaxLean(float deltaTime) {
|
|||
}
|
||||
|
||||
void Head::render(float alpha, Model::RenderMode mode) {
|
||||
if (_faceModel.render(alpha, mode) && _renderLookatVectors) {
|
||||
if (_faceModel.render(alpha, mode) && _renderLookatVectors && mode != Model::SHADOW_RENDER_MODE) {
|
||||
renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <QtCore/QTimer>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <GeometryUtil.h>
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
|
@ -63,6 +64,7 @@ MyAvatar::MyAvatar() :
|
|||
_distanceToNearestAvatar(std::numeric_limits<float>::max()),
|
||||
_wasPushing(false),
|
||||
_isPushing(false),
|
||||
_wasStuck(false),
|
||||
_thrust(0.0f),
|
||||
_motorVelocity(0.0f),
|
||||
_motorTimescale(DEFAULT_MOTOR_TIMESCALE),
|
||||
|
@ -212,6 +214,8 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
}
|
||||
if (_collisionGroups & COLLISION_GROUP_VOXELS) {
|
||||
updateCollisionWithVoxels(deltaTime, radius);
|
||||
} else {
|
||||
_wasStuck = false;
|
||||
}
|
||||
if (_collisionGroups & COLLISION_GROUP_AVATARS) {
|
||||
updateCollisionWithAvatars(deltaTime);
|
||||
|
@ -333,7 +337,9 @@ void MyAvatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
|
|||
return; // exit early
|
||||
}
|
||||
Avatar::render(cameraPosition, renderMode);
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::ShowIKConstraints)) {
|
||||
|
||||
// don't display IK constraints in shadow mode
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::ShowIKConstraints) && renderMode != SHADOW_RENDER_MODE) {
|
||||
_skeletonModel.renderIKConstraints();
|
||||
}
|
||||
}
|
||||
|
@ -582,7 +588,7 @@ void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) {
|
|||
if (shouldRenderHead(Application::getInstance()->getCamera()->getPosition(), renderMode)) {
|
||||
getHead()->render(1.0f, modelRenderMode);
|
||||
}
|
||||
getHand()->render(true);
|
||||
getHand()->render(true, modelRenderMode);
|
||||
}
|
||||
|
||||
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f;
|
||||
|
@ -958,65 +964,48 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) {
|
|||
static CollisionList myCollisions(64);
|
||||
|
||||
void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
|
||||
const float MIN_STUCK_SPEED = 100.0f;
|
||||
float speed = glm::length(_velocity);
|
||||
if (speed > MIN_STUCK_SPEED) {
|
||||
// don't even bother to try to collide against voxles when moving very fast
|
||||
return;
|
||||
}
|
||||
myCollisions.clear();
|
||||
const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape();
|
||||
if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions)) {
|
||||
const float VOXEL_ELASTICITY = 0.0f;
|
||||
const float VOXEL_DAMPING = 0.0f;
|
||||
float capsuleRadius = boundingShape.getRadius();
|
||||
|
||||
if (glm::length2(_gravity) > EPSILON) {
|
||||
if (myCollisions.size() == 1) {
|
||||
// trivial case
|
||||
CollisionInfo* collision = myCollisions[0];
|
||||
applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
|
||||
_lastFloorContactPoint = collision->_contactPoint - collision->_penetration;
|
||||
} else {
|
||||
// This is special collision handling for when walking on a voxel field which
|
||||
// prevents snagging at corners and seams.
|
||||
|
||||
// sift through the collisions looking for one against the "floor"
|
||||
int floorIndex = 0;
|
||||
float distanceToFloor = 0.0f;
|
||||
float penetrationWithFloor = 0.0f;
|
||||
for (int i = 0; i < myCollisions.size(); ++i) {
|
||||
CollisionInfo* collision = myCollisions[i];
|
||||
float distance = glm::dot(_gravity, collision->_contactPoint - _position);
|
||||
if (distance > distanceToFloor) {
|
||||
distanceToFloor = distance;
|
||||
penetrationWithFloor = glm::dot(_gravity, collision->_penetration);
|
||||
floorIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// step through the collisions again and apply each that is not redundant
|
||||
glm::vec3 oldPosition = _position;
|
||||
for (int i = 0; i < myCollisions.size(); ++i) {
|
||||
CollisionInfo* collision = myCollisions[i];
|
||||
if (i == floorIndex) {
|
||||
applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
|
||||
_lastFloorContactPoint = collision->_contactPoint - collision->_penetration;
|
||||
} else {
|
||||
float distance = glm::dot(_gravity, collision->_contactPoint - oldPosition);
|
||||
float penetration = glm::dot(_gravity, collision->_penetration);
|
||||
if (fabsf(distance - distanceToFloor) > penetrationWithFloor || penetration > penetrationWithFloor) {
|
||||
// resolution of the deepest penetration would not resolve this one
|
||||
// so we apply the collision
|
||||
applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
|
||||
}
|
||||
glm::vec3 totalPenetration(0.0f);
|
||||
bool isStuck = false;
|
||||
for (int i = 0; i < myCollisions.size(); ++i) {
|
||||
CollisionInfo* collision = myCollisions[i];
|
||||
float depth = glm::length(collision->_penetration);
|
||||
if (depth > capsuleRadius) {
|
||||
isStuck = true;
|
||||
if (_wasStuck) {
|
||||
glm::vec3 cubeCenter = collision->_vecData;
|
||||
float cubeSide = collision->_floatData;
|
||||
float distance = glm::dot(boundingShape.getPosition() - cubeCenter, _worldUpDirection);
|
||||
if (distance < 0.0f) {
|
||||
distance = fabsf(distance) + 0.5f * cubeSide;
|
||||
}
|
||||
distance += capsuleRadius + boundingShape.getHalfHeight();
|
||||
totalPenetration = addPenetrations(totalPenetration, - distance * _worldUpDirection);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// no gravity -- apply all collisions
|
||||
for (int i = 0; i < myCollisions.size(); ++i) {
|
||||
CollisionInfo* collision = myCollisions[i];
|
||||
applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
|
||||
}
|
||||
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
|
||||
}
|
||||
applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
|
||||
_wasStuck = isStuck;
|
||||
|
||||
const float VOXEL_COLLISION_FREQUENCY = 0.5f;
|
||||
updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
|
||||
}
|
||||
} else {
|
||||
_wasStuck = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity, float damping) {
|
||||
|
|
|
@ -125,6 +125,7 @@ private:
|
|||
|
||||
bool _wasPushing;
|
||||
bool _isPushing;
|
||||
bool _wasStuck;
|
||||
glm::vec3 _thrust; // final acceleration from outside sources for the current frame
|
||||
|
||||
glm::vec3 _motorVelocity; // intended velocity of avatar motion
|
||||
|
|
|
@ -39,8 +39,8 @@ void ModelTreeRenderer::update() {
|
|||
}
|
||||
}
|
||||
|
||||
void ModelTreeRenderer::render() {
|
||||
OctreeRenderer::render();
|
||||
void ModelTreeRenderer::render(RenderMode renderMode) {
|
||||
OctreeRenderer::render(renderMode);
|
||||
}
|
||||
|
||||
Model* ModelTreeRenderer::getModel(const QString& url) {
|
||||
|
@ -66,54 +66,130 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
|||
const QList<ModelItem>& modelItems = modelTreeElement->getModels();
|
||||
|
||||
uint16_t numberOfModels = modelItems.size();
|
||||
|
||||
bool isShadowMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE;
|
||||
|
||||
bool displayModelBounds = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelBounds);
|
||||
bool displayElementProxy = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelElementProxy);
|
||||
bool displayElementChildProxies = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelElementChildProxies);
|
||||
|
||||
|
||||
if (!isShadowMode && displayElementProxy && numberOfModels > 0) {
|
||||
glm::vec3 elementCenter = modelTreeElement->getAABox().calcCenter() * (float)TREE_SCALE;
|
||||
float elementSize = modelTreeElement->getScale() * (float)TREE_SCALE;
|
||||
glColor3f(1.0f, 0.0f, 0.0f);
|
||||
glPushMatrix();
|
||||
glTranslatef(elementCenter.x, elementCenter.y, elementCenter.z);
|
||||
glutWireCube(elementSize);
|
||||
glPopMatrix();
|
||||
|
||||
if (displayElementChildProxies) {
|
||||
// draw the children
|
||||
float halfSize = elementSize / 2.0f;
|
||||
float quarterSize = elementSize / 4.0f;
|
||||
glColor3f(1.0f, 1.0f, 0.0f);
|
||||
glPushMatrix();
|
||||
glTranslatef(elementCenter.x - quarterSize, elementCenter.y - quarterSize, elementCenter.z - quarterSize);
|
||||
glutWireCube(halfSize);
|
||||
glPopMatrix();
|
||||
|
||||
glColor3f(1.0f, 0.0f, 1.0f);
|
||||
glPushMatrix();
|
||||
glTranslatef(elementCenter.x + quarterSize, elementCenter.y - quarterSize, elementCenter.z - quarterSize);
|
||||
glutWireCube(halfSize);
|
||||
glPopMatrix();
|
||||
|
||||
glColor3f(0.0f, 1.0f, 0.0f);
|
||||
glPushMatrix();
|
||||
glTranslatef(elementCenter.x - quarterSize, elementCenter.y + quarterSize, elementCenter.z - quarterSize);
|
||||
glutWireCube(halfSize);
|
||||
glPopMatrix();
|
||||
|
||||
glColor3f(0.0f, 0.0f, 1.0f);
|
||||
glPushMatrix();
|
||||
glTranslatef(elementCenter.x - quarterSize, elementCenter.y - quarterSize, elementCenter.z + quarterSize);
|
||||
glutWireCube(halfSize);
|
||||
glPopMatrix();
|
||||
|
||||
glColor3f(1.0f, 1.0f, 1.0f);
|
||||
glPushMatrix();
|
||||
glTranslatef(elementCenter.x + quarterSize, elementCenter.y + quarterSize, elementCenter.z + quarterSize);
|
||||
glutWireCube(halfSize);
|
||||
glPopMatrix();
|
||||
|
||||
glColor3f(0.0f, 0.5f, 0.5f);
|
||||
glPushMatrix();
|
||||
glTranslatef(elementCenter.x - quarterSize, elementCenter.y + quarterSize, elementCenter.z + quarterSize);
|
||||
glutWireCube(halfSize);
|
||||
glPopMatrix();
|
||||
|
||||
glColor3f(0.5f, 0.0f, 0.0f);
|
||||
glPushMatrix();
|
||||
glTranslatef(elementCenter.x + quarterSize, elementCenter.y - quarterSize, elementCenter.z + quarterSize);
|
||||
glutWireCube(halfSize);
|
||||
glPopMatrix();
|
||||
|
||||
glColor3f(0.0f, 0.5f, 0.0f);
|
||||
glPushMatrix();
|
||||
glTranslatef(elementCenter.x + quarterSize, elementCenter.y + quarterSize, elementCenter.z - quarterSize);
|
||||
glutWireCube(halfSize);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < numberOfModels; i++) {
|
||||
const ModelItem& modelItem = modelItems[i];
|
||||
// render modelItem aspoints
|
||||
glm::vec3 position = modelItem.getPosition() * (float)TREE_SCALE;
|
||||
glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]);
|
||||
float radius = modelItem.getRadius() * (float)TREE_SCALE;
|
||||
//glm::vec3 center = position + glm::vec3(radius, radius, radius); // center it around the position
|
||||
AABox modelBox = modelItem.getAABox();
|
||||
modelBox.scale(TREE_SCALE);
|
||||
if (args->_viewFrustum->boxInFrustum(modelBox) != ViewFrustum::OUTSIDE) {
|
||||
glm::vec3 position = modelItem.getPosition() * (float)TREE_SCALE;
|
||||
float radius = modelItem.getRadius() * (float)TREE_SCALE;
|
||||
float size = modelItem.getSize() * (float)TREE_SCALE;
|
||||
|
||||
bool drawAsModel = modelItem.hasModel();
|
||||
bool drawAsModel = modelItem.hasModel();
|
||||
|
||||
args->_renderedItems++;
|
||||
args->_renderedItems++;
|
||||
|
||||
if (drawAsModel) {
|
||||
glPushMatrix();
|
||||
const float alpha = 1.0f;
|
||||
if (drawAsModel) {
|
||||
glPushMatrix();
|
||||
const float alpha = 1.0f;
|
||||
|
||||
Model* model = getModel(modelItem.getModelURL());
|
||||
Model* model = getModel(modelItem.getModelURL());
|
||||
|
||||
model->setScaleToFit(true, radius * 2.0f);
|
||||
model->setSnapModelToCenter(true);
|
||||
model->setScaleToFit(true, radius * 2.0f);
|
||||
model->setSnapModelToCenter(true);
|
||||
|
||||
// set the rotation
|
||||
glm::quat rotation = modelItem.getModelRotation();
|
||||
model->setRotation(rotation);
|
||||
// set the rotation
|
||||
glm::quat rotation = modelItem.getModelRotation();
|
||||
model->setRotation(rotation);
|
||||
|
||||
// set the position
|
||||
model->setTranslation(position);
|
||||
// set the position
|
||||
model->setTranslation(position);
|
||||
model->simulate(0.0f);
|
||||
|
||||
model->simulate(0.0f);
|
||||
// TODO: should we allow modelItems to have alpha on their models?
|
||||
Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE
|
||||
? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
|
||||
model->render(alpha, modelRenderMode);
|
||||
|
||||
if (!isShadowMode && displayModelBounds) {
|
||||
glColor3f(0.0f, 1.0f, 0.0f);
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glutWireCube(size);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
model->render(alpha); // TODO: should we allow modelItems to have alpha on their models?
|
||||
|
||||
const bool wantDebugSphere = false;
|
||||
if (wantDebugSphere) {
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glutWireSphere(radius, 15, 15);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
} else {
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glutSolidSphere(radius, 15, 15);
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
} else {
|
||||
glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]);
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glutSolidSphere(radius, 15, 15);
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ public:
|
|||
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
|
||||
|
||||
virtual void init();
|
||||
virtual void render();
|
||||
virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE);
|
||||
|
||||
protected:
|
||||
Model* getModel(const QString& url);
|
||||
|
|
|
@ -39,8 +39,8 @@ void ParticleTreeRenderer::update() {
|
|||
}
|
||||
}
|
||||
|
||||
void ParticleTreeRenderer::render() {
|
||||
OctreeRenderer::render();
|
||||
void ParticleTreeRenderer::render(RenderMode renderMode) {
|
||||
OctreeRenderer::render(renderMode);
|
||||
}
|
||||
|
||||
Model* ParticleTreeRenderer::getModel(const QString& url) {
|
||||
|
@ -102,7 +102,11 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg
|
|||
model->setScale(scale * MODEL_SCALE * radius * modelScale);
|
||||
|
||||
model->simulate(0.0f);
|
||||
model->render(alpha); // TODO: should we allow particles to have alpha on their models?
|
||||
|
||||
// TODO: should we allow particles to have alpha on their models?
|
||||
Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE
|
||||
? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
|
||||
model->render(alpha, modelRenderMode);
|
||||
|
||||
const bool wantDebugSphere = false;
|
||||
if (wantDebugSphere) {
|
||||
|
|
|
@ -43,7 +43,7 @@ public:
|
|||
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
|
||||
|
||||
virtual void init();
|
||||
virtual void render();
|
||||
virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE);
|
||||
|
||||
protected:
|
||||
Model* getModel(const QString& url);
|
||||
|
|
|
@ -342,7 +342,8 @@ bool NetworkGeometry::isLoadedWithTextures() const {
|
|||
foreach (const NetworkMesh& mesh, _meshes) {
|
||||
foreach (const NetworkMeshPart& part, mesh.parts) {
|
||||
if ((part.diffuseTexture && !part.diffuseTexture->isLoaded()) ||
|
||||
(part.normalTexture && !part.normalTexture->isLoaded())) {
|
||||
(part.normalTexture && !part.normalTexture->isLoaded()) ||
|
||||
(part.specularTexture && !part.specularTexture->isLoaded())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -416,6 +417,9 @@ void NetworkGeometry::setLoadPriority(const QPointer<QObject>& owner, float prio
|
|||
if (part.normalTexture) {
|
||||
part.normalTexture->setLoadPriority(owner, priority);
|
||||
}
|
||||
if (part.specularTexture) {
|
||||
part.specularTexture->setLoadPriority(owner, priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -433,6 +437,9 @@ void NetworkGeometry::setLoadPriorities(const QHash<QPointer<QObject>, float>& p
|
|||
if (part.normalTexture) {
|
||||
part.normalTexture->setLoadPriorities(priorities);
|
||||
}
|
||||
if (part.specularTexture) {
|
||||
part.specularTexture->setLoadPriorities(priorities);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -450,6 +457,9 @@ void NetworkGeometry::clearLoadPriority(const QPointer<QObject>& owner) {
|
|||
if (part.normalTexture) {
|
||||
part.normalTexture->clearLoadPriority(owner);
|
||||
}
|
||||
if (part.specularTexture) {
|
||||
part.specularTexture->clearLoadPriority(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -566,6 +576,11 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) {
|
|||
_textureBase.resolved(QUrl(part.normalTexture.filename)), true, false, part.normalTexture.content);
|
||||
networkPart.normalTexture->setLoadPriorities(_loadPriorities);
|
||||
}
|
||||
if (!part.specularTexture.filename.isEmpty()) {
|
||||
networkPart.specularTexture = Application::getInstance()->getTextureCache()->getTexture(
|
||||
_textureBase.resolved(QUrl(part.specularTexture.filename)), true, false, part.specularTexture.content);
|
||||
networkPart.specularTexture->setLoadPriorities(_loadPriorities);
|
||||
}
|
||||
networkMesh.parts.append(networkPart);
|
||||
|
||||
totalIndices += (part.quadIndices.size() + part.triangleIndices.size());
|
||||
|
|
|
@ -124,6 +124,7 @@ public:
|
|||
|
||||
QSharedPointer<NetworkTexture> diffuseTexture;
|
||||
QSharedPointer<NetworkTexture> normalTexture;
|
||||
QSharedPointer<NetworkTexture> specularTexture;
|
||||
|
||||
bool isTranslucent() const;
|
||||
};
|
||||
|
|
|
@ -56,13 +56,20 @@ Model::~Model() {
|
|||
|
||||
ProgramObject Model::_program;
|
||||
ProgramObject Model::_normalMapProgram;
|
||||
ProgramObject Model::_specularMapProgram;
|
||||
ProgramObject Model::_normalSpecularMapProgram;
|
||||
ProgramObject Model::_shadowProgram;
|
||||
ProgramObject Model::_skinProgram;
|
||||
ProgramObject Model::_skinNormalMapProgram;
|
||||
ProgramObject Model::_skinSpecularMapProgram;
|
||||
ProgramObject Model::_skinNormalSpecularMapProgram;
|
||||
ProgramObject Model::_skinShadowProgram;
|
||||
int Model::_normalMapTangentLocation;
|
||||
int Model::_normalSpecularMapTangentLocation;
|
||||
Model::SkinLocations Model::_skinLocations;
|
||||
Model::SkinLocations Model::_skinNormalMapLocations;
|
||||
Model::SkinLocations Model::_skinSpecularMapLocations;
|
||||
Model::SkinLocations Model::_skinNormalSpecularMapLocations;
|
||||
Model::SkinLocations Model::_skinShadowLocations;
|
||||
|
||||
void Model::setScale(const glm::vec3& scale) {
|
||||
|
@ -92,7 +99,7 @@ void Model::setOffset(const glm::vec3& offset) {
|
|||
}
|
||||
|
||||
|
||||
void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations) {
|
||||
void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations, int specularTextureUnit) {
|
||||
program.bind();
|
||||
locations.clusterMatrices = program.uniformLocation("clusterMatrices");
|
||||
locations.clusterIndices = program.attributeLocation("clusterIndices");
|
||||
|
@ -100,6 +107,7 @@ void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locati
|
|||
locations.tangent = program.attributeLocation("tangent");
|
||||
program.setUniformValue("diffuseMap", 0);
|
||||
program.setUniformValue("normalMap", 1);
|
||||
program.setUniformValue("specularMap", specularTextureUnit);
|
||||
program.release();
|
||||
}
|
||||
|
||||
|
@ -162,10 +170,10 @@ void Model::init() {
|
|||
_program.setUniformValue("texture", 0);
|
||||
_program.release();
|
||||
|
||||
_normalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath()
|
||||
+ "shaders/model_normal_map.vert");
|
||||
_normalMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath()
|
||||
+ "shaders/model_normal_map.frag");
|
||||
_normalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/model_normal_map.vert");
|
||||
_normalMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_normal_map.frag");
|
||||
_normalMapProgram.link();
|
||||
|
||||
_normalMapProgram.bind();
|
||||
|
@ -174,27 +182,65 @@ void Model::init() {
|
|||
_normalMapTangentLocation = _normalMapProgram.attributeLocation("tangent");
|
||||
_normalMapProgram.release();
|
||||
|
||||
_specularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/model.vert");
|
||||
_specularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_specular_map.frag");
|
||||
_specularMapProgram.link();
|
||||
|
||||
_specularMapProgram.bind();
|
||||
_specularMapProgram.setUniformValue("diffuseMap", 0);
|
||||
_specularMapProgram.setUniformValue("specularMap", 1);
|
||||
_specularMapProgram.release();
|
||||
|
||||
_normalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/model_normal_map.vert");
|
||||
_normalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_normal_specular_map.frag");
|
||||
_normalSpecularMapProgram.link();
|
||||
|
||||
_normalSpecularMapProgram.bind();
|
||||
_normalSpecularMapProgram.setUniformValue("diffuseMap", 0);
|
||||
_normalSpecularMapProgram.setUniformValue("normalMap", 1);
|
||||
_normalSpecularMapProgram.setUniformValue("specularMap", 2);
|
||||
_normalSpecularMapTangentLocation = _normalMapProgram.attributeLocation("tangent");
|
||||
_normalSpecularMapProgram.release();
|
||||
|
||||
_shadowProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_shadow.vert");
|
||||
_shadowProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
|
||||
"shaders/model_shadow.frag");
|
||||
_shadowProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_shadow.frag");
|
||||
_shadowProgram.link();
|
||||
|
||||
_skinProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath()
|
||||
+ "shaders/skin_model.vert");
|
||||
_skinProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath()
|
||||
+ "shaders/model.frag");
|
||||
_skinProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/skin_model.vert");
|
||||
_skinProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/model.frag");
|
||||
_skinProgram.link();
|
||||
|
||||
initSkinProgram(_skinProgram, _skinLocations);
|
||||
|
||||
_skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath()
|
||||
+ "shaders/skin_model_normal_map.vert");
|
||||
_skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath()
|
||||
+ "shaders/model_normal_map.frag");
|
||||
_skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/skin_model_normal_map.vert");
|
||||
_skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_normal_map.frag");
|
||||
_skinNormalMapProgram.link();
|
||||
|
||||
initSkinProgram(_skinNormalMapProgram, _skinNormalMapLocations);
|
||||
|
||||
_skinSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/skin_model.vert");
|
||||
_skinSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_specular_map.frag");
|
||||
_skinSpecularMapProgram.link();
|
||||
|
||||
initSkinProgram(_skinSpecularMapProgram, _skinSpecularMapLocations);
|
||||
|
||||
_skinNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/skin_model_normal_map.vert");
|
||||
_skinNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_normal_specular_map.frag");
|
||||
_skinNormalSpecularMapProgram.link();
|
||||
|
||||
initSkinProgram(_skinNormalSpecularMapProgram, _skinNormalSpecularMapLocations, 2);
|
||||
|
||||
_skinShadowProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/skin_model_shadow.vert");
|
||||
_skinShadowProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
|
@ -743,9 +789,9 @@ bool Model::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadi
|
|||
}
|
||||
if (ShapeCollider::collideShapes(&sphere, _jointShapes[i], collisions)) {
|
||||
CollisionInfo* collision = collisions.getLastCollision();
|
||||
collision->_type = MODEL_COLLISION;
|
||||
collision->_type = COLLISION_TYPE_MODEL;
|
||||
collision->_data = (void*)(this);
|
||||
collision->_flags = i;
|
||||
collision->_intData = i;
|
||||
collided = true;
|
||||
}
|
||||
outerContinue: ;
|
||||
|
@ -759,9 +805,9 @@ bool Model::findPlaneCollisions(const glm::vec4& plane, CollisionList& collision
|
|||
for (int i = 0; i < _jointShapes.size(); i++) {
|
||||
if (ShapeCollider::collideShapes(&planeShape, _jointShapes[i], collisions)) {
|
||||
CollisionInfo* collision = collisions.getLastCollision();
|
||||
collision->_type = MODEL_COLLISION;
|
||||
collision->_type = COLLISION_TYPE_MODEL;
|
||||
collision->_data = (void*)(this);
|
||||
collision->_flags = i;
|
||||
collision->_intData = i;
|
||||
collided = true;
|
||||
}
|
||||
}
|
||||
|
@ -1210,15 +1256,15 @@ void Model::renderBoundingCollisionShapes(float alpha) {
|
|||
}
|
||||
|
||||
bool Model::collisionHitsMoveableJoint(CollisionInfo& collision) const {
|
||||
if (collision._type == MODEL_COLLISION) {
|
||||
if (collision._type == COLLISION_TYPE_MODEL) {
|
||||
// the joint is pokable by a collision if it exists and is free to move
|
||||
const FBXJoint& joint = _geometry->getFBXGeometry().joints[collision._flags];
|
||||
const FBXJoint& joint = _geometry->getFBXGeometry().joints[collision._intData];
|
||||
if (joint.parentIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// an empty freeLineage means the joint can't move
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
int jointIndex = collision._flags;
|
||||
int jointIndex = collision._intData;
|
||||
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
|
||||
return !freeLineage.isEmpty();
|
||||
}
|
||||
|
@ -1226,12 +1272,12 @@ bool Model::collisionHitsMoveableJoint(CollisionInfo& collision) const {
|
|||
}
|
||||
|
||||
void Model::applyCollision(CollisionInfo& collision) {
|
||||
if (collision._type != MODEL_COLLISION) {
|
||||
if (collision._type != COLLISION_TYPE_MODEL) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 jointPosition(0.f);
|
||||
int jointIndex = collision._flags;
|
||||
int jointIndex = collision._intData;
|
||||
if (getJointPosition(jointIndex, jointPosition)) {
|
||||
const FBXJoint& joint = _geometry->getFBXGeometry().joints[jointIndex];
|
||||
if (joint.parentIndex != -1) {
|
||||
|
@ -1331,15 +1377,29 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
|
|||
ProgramObject* program = &_program;
|
||||
ProgramObject* skinProgram = &_skinProgram;
|
||||
SkinLocations* skinLocations = &_skinLocations;
|
||||
GLenum specularTextureUnit = 0;
|
||||
if (mode == SHADOW_RENDER_MODE) {
|
||||
program = &_shadowProgram;
|
||||
skinProgram = &_skinShadowProgram;
|
||||
skinLocations = &_skinShadowLocations;
|
||||
|
||||
} else if (!mesh.tangents.isEmpty()) {
|
||||
program = &_normalMapProgram;
|
||||
skinProgram = &_skinNormalMapProgram;
|
||||
skinLocations = &_skinNormalMapLocations;
|
||||
if (mesh.hasSpecularTexture()) {
|
||||
program = &_normalSpecularMapProgram;
|
||||
skinProgram = &_skinNormalSpecularMapProgram;
|
||||
skinLocations = &_skinNormalSpecularMapLocations;
|
||||
specularTextureUnit = GL_TEXTURE2;
|
||||
|
||||
} else {
|
||||
program = &_normalMapProgram;
|
||||
skinProgram = &_skinNormalMapProgram;
|
||||
skinLocations = &_skinNormalMapLocations;
|
||||
}
|
||||
} else if (mesh.hasSpecularTexture()) {
|
||||
program = &_specularMapProgram;
|
||||
skinProgram = &_skinSpecularMapProgram;
|
||||
skinLocations = &_skinSpecularMapLocations;
|
||||
specularTextureUnit = GL_TEXTURE1;
|
||||
}
|
||||
|
||||
const MeshState& state = _meshStates.at(i);
|
||||
|
@ -1427,13 +1487,23 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
|
|||
glBindTexture(GL_TEXTURE_2D, !diffuseMap ?
|
||||
Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID());
|
||||
|
||||
|
||||
if (!mesh.tangents.isEmpty()) {
|
||||
specularTextureUnit = GL_TEXTURE2;
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
Texture* normalMap = networkPart.normalTexture.data();
|
||||
glBindTexture(GL_TEXTURE_2D, !normalMap ?
|
||||
Application::getInstance()->getTextureCache()->getBlueTextureID() : normalMap->getID());
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
if (specularTextureUnit) {
|
||||
glActiveTexture(specularTextureUnit);
|
||||
Texture* specularMap = networkPart.specularTexture.data();
|
||||
glBindTexture(GL_TEXTURE_2D, !specularMap ?
|
||||
Application::getInstance()->getTextureCache()->getWhiteTextureID() : specularMap->getID());
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
}
|
||||
glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset);
|
||||
offset += part.quadIndices.size() * sizeof(int);
|
||||
|
@ -1456,7 +1526,13 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
|
|||
|
||||
activeProgram->disableAttributeArray(tangentLocation);
|
||||
}
|
||||
|
||||
|
||||
if (specularTextureUnit) {
|
||||
glActiveTexture(specularTextureUnit);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
if (state.clusterMatrices.size() > 1) {
|
||||
skinProgram->disableAttributeArray(skinLocations->clusterIndices);
|
||||
skinProgram->disableAttributeArray(skinLocations->clusterWeights);
|
||||
|
|
|
@ -318,12 +318,17 @@ private:
|
|||
|
||||
static ProgramObject _program;
|
||||
static ProgramObject _normalMapProgram;
|
||||
static ProgramObject _specularMapProgram;
|
||||
static ProgramObject _normalSpecularMapProgram;
|
||||
static ProgramObject _shadowProgram;
|
||||
static ProgramObject _skinProgram;
|
||||
static ProgramObject _skinNormalMapProgram;
|
||||
static ProgramObject _skinSpecularMapProgram;
|
||||
static ProgramObject _skinNormalSpecularMapProgram;
|
||||
static ProgramObject _skinShadowProgram;
|
||||
|
||||
static int _normalMapTangentLocation;
|
||||
static int _normalSpecularMapTangentLocation;
|
||||
|
||||
class SkinLocations {
|
||||
public:
|
||||
|
@ -335,9 +340,11 @@ private:
|
|||
|
||||
static SkinLocations _skinLocations;
|
||||
static SkinLocations _skinNormalMapLocations;
|
||||
static SkinLocations _skinSpecularMapLocations;
|
||||
static SkinLocations _skinNormalSpecularMapLocations;
|
||||
static SkinLocations _skinShadowLocations;
|
||||
|
||||
static void initSkinProgram(ProgramObject& program, SkinLocations& locations);
|
||||
static void initSkinProgram(ProgramObject& program, SkinLocations& locations, int specularTextureUnit = 1);
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(QPointer<Model>)
|
||||
|
|
|
@ -21,6 +21,12 @@
|
|||
|
||||
#include "AudioInjector.h"
|
||||
|
||||
AudioInjector::AudioInjector(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions) :
|
||||
_sound(sound),
|
||||
_options(injectorOptions)
|
||||
|
@ -80,7 +86,7 @@ void AudioInjector::injectAudio() {
|
|||
int numPreAudioDataBytes = injectAudioPacket.size();
|
||||
|
||||
// loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks
|
||||
while (currentSendPosition < soundByteArray.size()) {
|
||||
while (currentSendPosition < soundByteArray.size() && !_shouldStop) {
|
||||
|
||||
int bytesToCopy = std::min(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL,
|
||||
soundByteArray.size() - currentSendPosition);
|
||||
|
|
|
@ -24,14 +24,19 @@
|
|||
class AudioInjector : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioInjector(QObject* parent);
|
||||
AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions);
|
||||
public slots:
|
||||
void injectAudio();
|
||||
void stop() { _shouldStop = true; }
|
||||
signals:
|
||||
void finished();
|
||||
private:
|
||||
Sound* _sound;
|
||||
AudioInjectorOptions _options;
|
||||
public slots:
|
||||
void injectAudio();
|
||||
signals:
|
||||
void finished();
|
||||
bool _shouldStop;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(AudioInjector*)
|
||||
|
||||
#endif // hifi_AudioInjector_h
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#include "AudioScriptingInterface.h"
|
||||
|
||||
void AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions* injectorOptions) {
|
||||
AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions* injectorOptions) {
|
||||
|
||||
AudioInjector* injector = new AudioInjector(sound, *injectorOptions);
|
||||
|
||||
|
@ -28,6 +28,18 @@ void AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions
|
|||
connect(injectorThread, SIGNAL(finished()), injectorThread, SLOT(deleteLater()));
|
||||
|
||||
injectorThread->start();
|
||||
|
||||
return injector;
|
||||
}
|
||||
|
||||
void AudioScriptingInterface::stopInjector(AudioInjector* injector) {
|
||||
if (injector) {
|
||||
injector->stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioScriptingInterface::isInjectorPlaying(AudioInjector* injector) {
|
||||
return (injector != NULL);
|
||||
}
|
||||
|
||||
void AudioScriptingInterface::startDrumSound(float volume, float frequency, float duration, float decay,
|
||||
|
|
|
@ -20,7 +20,9 @@ const AudioInjectorOptions DEFAULT_INJECTOR_OPTIONS;
|
|||
class AudioScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
public slots:
|
||||
static void playSound(Sound* sound, const AudioInjectorOptions* injectorOptions = NULL);
|
||||
static AudioInjector* playSound(Sound* sound, const AudioInjectorOptions* injectorOptions = NULL);
|
||||
static void stopInjector(AudioInjector* injector);
|
||||
static bool isInjectorPlaying(AudioInjector* injector);
|
||||
static void startDrumSound(float volume, float frequency, float duration, float decay,
|
||||
const AudioInjectorOptions* injectorOptions = NULL);
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ void PositionalAudioRingBuffer::updateNextOutputTrailingLoudness() {
|
|||
const int TRAILING_AVERAGE_FRAMES = 100;
|
||||
const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES;
|
||||
const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO;
|
||||
const float LOUDNESS_EPSILON = 0.01f;
|
||||
const float LOUDNESS_EPSILON = 0.000001f;
|
||||
|
||||
if (nextLoudness >= _nextOutputTrailingLoudness) {
|
||||
_nextOutputTrailingLoudness = nextLoudness;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <StreamUtils.h>
|
||||
#include <UUID.h>
|
||||
#include <VoxelConstants.h>
|
||||
|
||||
|
|
|
@ -45,7 +45,6 @@ typedef unsigned long long quint64;
|
|||
|
||||
#include <CollisionInfo.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <StreamUtils.h>
|
||||
|
||||
#include <Node.h>
|
||||
|
||||
|
|
|
@ -54,6 +54,15 @@ void Extents::addPoint(const glm::vec3& point) {
|
|||
maximum = glm::max(maximum, point);
|
||||
}
|
||||
|
||||
bool FBXMesh::hasSpecularTexture() const {
|
||||
foreach (const FBXMeshPart& part, parts) {
|
||||
if (!part.specularTexture.filename.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList FBXGeometry::getJointNames() const {
|
||||
QStringList names;
|
||||
foreach (const FBXJoint& joint, joints) {
|
||||
|
@ -817,7 +826,7 @@ ExtractedMesh extractMesh(const FBXNode& object) {
|
|||
while (endIndex < data.polygonIndices.size() && data.polygonIndices.at(endIndex++) >= 0);
|
||||
|
||||
QPair<int, int> materialTexture((polygonIndex < materials.size()) ? materials.at(polygonIndex) : 0,
|
||||
(polygonIndex < textures.size()) ? textures.at(polygonIndex) : -1);
|
||||
(polygonIndex < textures.size()) ? textures.at(polygonIndex) : 0);
|
||||
int& partIndex = materialTextureParts[materialTexture];
|
||||
if (partIndex == 0) {
|
||||
data.extracted.partMaterialTextures.append(materialTexture);
|
||||
|
@ -963,6 +972,18 @@ FBXTexture getTexture(const QString& textureID, const QHash<QString, QByteArray>
|
|||
return texture;
|
||||
}
|
||||
|
||||
bool checkMaterialsHaveTextures(const QHash<QString, Material>& materials,
|
||||
const QHash<QString, QByteArray>& textureFilenames, const QMultiHash<QString, QString>& childMap) {
|
||||
foreach (const QString& materialID, materials.keys()) {
|
||||
foreach (const QString& childID, childMap.values(materialID)) {
|
||||
if (textureFilenames.contains(childID)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) {
|
||||
QHash<QString, ExtractedMesh> meshes;
|
||||
QVector<ExtractedBlendshape> blendshapes;
|
||||
|
@ -976,6 +997,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
QHash<QString, Material> materials;
|
||||
QHash<QString, QString> diffuseTextures;
|
||||
QHash<QString, QString> bumpTextures;
|
||||
QHash<QString, QString> specularTextures;
|
||||
QHash<QString, QString> localRotations;
|
||||
QHash<QString, QString> xComponents;
|
||||
QHash<QString, QString> yComponents;
|
||||
|
@ -1330,6 +1352,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
} else if (type.contains("bump") || type.contains("normal")) {
|
||||
bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||
|
||||
} else if (type.contains("specular")) {
|
||||
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));
|
||||
|
||||
|
@ -1502,6 +1527,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
geometry.bindExtents.reset();
|
||||
geometry.meshExtents.reset();
|
||||
|
||||
// see if any materials have texture children
|
||||
bool materialsHaveTextures = checkMaterialsHaveTextures(materials, textureFilenames, childMap);
|
||||
|
||||
for (QHash<QString, ExtractedMesh>::iterator it = meshes.begin(); it != meshes.end(); it++) {
|
||||
ExtractedMesh& extracted = it.value();
|
||||
|
||||
|
@ -1546,6 +1574,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
generateTangents = true;
|
||||
}
|
||||
|
||||
FBXTexture specularTexture;
|
||||
QString specularTextureID = specularTextures.value(childID);
|
||||
if (!specularTextureID.isNull()) {
|
||||
specularTexture = getTexture(specularTextureID, textureFilenames, textureContent);
|
||||
}
|
||||
|
||||
for (int j = 0; j < extracted.partMaterialTextures.size(); j++) {
|
||||
if (extracted.partMaterialTextures.at(j).first == materialIndex) {
|
||||
FBXMeshPart& part = extracted.mesh.parts[j];
|
||||
|
@ -1558,6 +1592,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
if (!normalTexture.filename.isNull()) {
|
||||
part.normalTexture = normalTexture;
|
||||
}
|
||||
if (!specularTexture.filename.isNull()) {
|
||||
part.specularTexture = specularTexture;
|
||||
}
|
||||
}
|
||||
}
|
||||
materialIndex++;
|
||||
|
@ -1565,7 +1602,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
} else if (textureFilenames.contains(childID)) {
|
||||
FBXTexture texture = getTexture(childID, textureFilenames, textureContent);
|
||||
for (int j = 0; j < extracted.partMaterialTextures.size(); j++) {
|
||||
if (extracted.partMaterialTextures.at(j).second == textureIndex) {
|
||||
int partTexture = extracted.partMaterialTextures.at(j).second;
|
||||
if (partTexture == textureIndex && !(partTexture == 0 && materialsHaveTextures)) {
|
||||
extracted.mesh.parts[j].diffuseTexture = texture;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,6 +129,7 @@ public:
|
|||
|
||||
FBXTexture diffuseTexture;
|
||||
FBXTexture normalTexture;
|
||||
FBXTexture specularTexture;
|
||||
};
|
||||
|
||||
/// A single mesh (with optional blendshapes) extracted from an FBX document.
|
||||
|
@ -150,6 +151,8 @@ public:
|
|||
bool isEye;
|
||||
|
||||
QVector<FBXBlendshape> blendshapes;
|
||||
|
||||
bool hasSpecularTexture() const;
|
||||
};
|
||||
|
||||
/// A single animation frame extracted from an FBX document.
|
||||
|
|
|
@ -85,6 +85,9 @@ public:
|
|||
|
||||
/// used by ModelScriptingInterface to return ModelItemProperties for unknown models
|
||||
void setIsUnknownID() { _id = UNKNOWN_MODEL_ID; _idSet = true; }
|
||||
|
||||
glm::vec3 getMinimumPoint() const { return _position - glm::vec3(_radius, _radius, _radius); }
|
||||
glm::vec3 getMaximumPoint() const { return _position + glm::vec3(_radius, _radius, _radius); }
|
||||
|
||||
private:
|
||||
glm::vec3 _position;
|
||||
|
@ -156,11 +159,20 @@ public:
|
|||
/// get position in domain scale units (0.0 - 1.0)
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
|
||||
glm::vec3 getMinimumPoint() const { return _position - glm::vec3(_radius, _radius, _radius); }
|
||||
glm::vec3 getMaximumPoint() const { return _position + glm::vec3(_radius, _radius, _radius); }
|
||||
|
||||
const rgbColor& getColor() const { return _color; }
|
||||
xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
|
||||
|
||||
/// get radius in domain scale units (0.0 - 1.0)
|
||||
float getRadius() const { return _radius; }
|
||||
|
||||
/// get maximum dimension in domain scale units (0.0 - 1.0)
|
||||
float getSize() const { return _radius * 2.0f; }
|
||||
|
||||
/// get maximum dimension in domain scale units (0.0 - 1.0)
|
||||
AABox getAABox() const { return AABox(getMinimumPoint(), getSize()); }
|
||||
|
||||
// model related properties
|
||||
bool hasModel() const { return !_modelURL.isEmpty(); }
|
||||
|
|
|
@ -113,13 +113,10 @@ void ModelTree::storeModel(const ModelItem& model, const SharedNodePointer& send
|
|||
FindAndUpdateModelOperator theOperator(model);
|
||||
recurseTreeWithOperator(&theOperator);
|
||||
|
||||
|
||||
// if we didn't find it in the tree, then store it...
|
||||
if (!theOperator.wasFound()) {
|
||||
glm::vec3 position = model.getPosition();
|
||||
float size = std::max(MINIMUM_MODEL_ELEMENT_SIZE, model.getRadius());
|
||||
|
||||
ModelTreeElement* element = (ModelTreeElement*)getOrCreateChildElementAt(position.x, position.y, position.z, size);
|
||||
AABox modelBox = model.getAABox();
|
||||
ModelTreeElement* element = (ModelTreeElement*)getOrCreateChildElementContaining(model.getAABox());
|
||||
element->storeModel(model);
|
||||
}
|
||||
// what else do we need to do here to get reaveraging to work
|
||||
|
@ -494,12 +491,29 @@ void ModelTree::update() {
|
|||
lockForWrite();
|
||||
_isDirty = true;
|
||||
|
||||
// TODO: we don't need to update models yet, but when we do, for example
|
||||
// when we add animation support, we will revisit this code.
|
||||
//ModelTreeUpdateArgs args = { };
|
||||
//recurseTreeWithOperation(updateOperation, &args);
|
||||
ModelTreeUpdateArgs args = { };
|
||||
recurseTreeWithOperation(updateOperation, &args);
|
||||
|
||||
// Now is a reasonable time to prune the tree...
|
||||
// now add back any of the particles that moved elements....
|
||||
int movingModels = args._movingModels.size();
|
||||
for (int i = 0; i < movingModels; i++) {
|
||||
bool shouldDie = args._movingModels[i].getShouldDie();
|
||||
|
||||
// if the particle is still inside our total bounds, then re-add it
|
||||
AABox treeBounds = getRoot()->getAABox();
|
||||
|
||||
if (!shouldDie && treeBounds.contains(args._movingModels[i].getPosition())) {
|
||||
storeModel(args._movingModels[i]);
|
||||
} else {
|
||||
uint32_t modelItemID = args._movingModels[i].getID();
|
||||
quint64 deletedAt = usecTimestampNow();
|
||||
_recentlyDeletedModelsLock.lockForWrite();
|
||||
_recentlyDeletedModelItemIDs.insert(deletedAt, modelItemID);
|
||||
_recentlyDeletedModelsLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// prune the tree...
|
||||
recurseTreeWithOperation(pruneOperation, NULL);
|
||||
unlock();
|
||||
}
|
||||
|
|
|
@ -47,15 +47,32 @@ ModelTreeElement* ModelTreeElement::addChildAtIndex(int index) {
|
|||
}
|
||||
|
||||
|
||||
bool ModelTreeElement::appendElementData(OctreePacketData* packetData) const {
|
||||
bool ModelTreeElement::appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const {
|
||||
bool success = true; // assume the best...
|
||||
|
||||
// write our models out...
|
||||
uint16_t numberOfModels = _modelItems->size();
|
||||
// write our models out... first determine which of the models are in view based on our params
|
||||
uint16_t numberOfModels = 0;
|
||||
QVector<uint16_t> indexesOfModelsToInclude;
|
||||
|
||||
for (uint16_t i = 0; i < _modelItems->size(); i++) {
|
||||
if (params.viewFrustum) {
|
||||
const ModelItem& model = (*_modelItems)[i];
|
||||
AABox modelBox = model.getAABox();
|
||||
modelBox.scale(TREE_SCALE);
|
||||
if (params.viewFrustum->boxInFrustum(modelBox) != ViewFrustum::OUTSIDE) {
|
||||
indexesOfModelsToInclude << i;
|
||||
numberOfModels++;
|
||||
}
|
||||
} else {
|
||||
indexesOfModelsToInclude << i;
|
||||
numberOfModels++;
|
||||
}
|
||||
}
|
||||
|
||||
success = packetData->appendValue(numberOfModels);
|
||||
|
||||
if (success) {
|
||||
for (uint16_t i = 0; i < numberOfModels; i++) {
|
||||
foreach (uint16_t i, indexesOfModelsToInclude) {
|
||||
const ModelItem& model = (*_modelItems)[i];
|
||||
success = model.appendModelData(packetData);
|
||||
if (!success) {
|
||||
|
@ -66,10 +83,25 @@ bool ModelTreeElement::appendElementData(OctreePacketData* packetData) const {
|
|||
return success;
|
||||
}
|
||||
|
||||
void ModelTreeElement::update(ModelTreeUpdateArgs& args) {
|
||||
markWithChangedTime();
|
||||
// TODO: early exit when _modelItems is empty
|
||||
bool ModelTreeElement::containsModelBounds(const ModelItem& model) const {
|
||||
return _box.contains(model.getMinimumPoint()) && _box.contains(model.getMaximumPoint());
|
||||
}
|
||||
|
||||
bool ModelTreeElement::bestFitModelBounds(const ModelItem& model) const {
|
||||
if (_box.contains(model.getMinimumPoint()) && _box.contains(model.getMaximumPoint())) {
|
||||
int childForMinimumPoint = getMyChildContainingPoint(model.getMinimumPoint());
|
||||
int childForMaximumPoint = getMyChildContainingPoint(model.getMaximumPoint());
|
||||
|
||||
// If I contain both the minimum and maximum point, but two different children of mine
|
||||
// contain those points, then I am the best fit for that model
|
||||
if (childForMinimumPoint != childForMaximumPoint) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ModelTreeElement::update(ModelTreeUpdateArgs& args) {
|
||||
// update our contained models
|
||||
QList<ModelItem>::iterator modelItr = _modelItems->begin();
|
||||
while(modelItr != _modelItems->end()) {
|
||||
|
@ -78,19 +110,18 @@ void ModelTreeElement::update(ModelTreeUpdateArgs& args) {
|
|||
|
||||
// If the model wants to die, or if it's left our bounding box, then move it
|
||||
// into the arguments moving models. These will be added back or deleted completely
|
||||
if (model.getShouldDie() || !_box.contains(model.getPosition())) {
|
||||
if (model.getShouldDie() || !bestFitModelBounds(model)) {
|
||||
args._movingModels.push_back(model);
|
||||
|
||||
// erase this model
|
||||
modelItr = _modelItems->erase(modelItr);
|
||||
|
||||
// this element has changed so mark it...
|
||||
markWithChangedTime();
|
||||
} else {
|
||||
++modelItr;
|
||||
}
|
||||
}
|
||||
// TODO: if _modelItems is empty after while loop consider freeing memory in _modelItems if
|
||||
// internal array is too big (QList internal array does not decrease size except in dtor and
|
||||
// assignment operator). Otherwise _modelItems could become a "resource leak" for large
|
||||
// roaming piles of models.
|
||||
}
|
||||
|
||||
bool ModelTreeElement::findSpherePenetration(const glm::vec3& center, float radius,
|
||||
|
@ -136,6 +167,7 @@ bool ModelTreeElement::updateModel(const ModelItem& model) {
|
|||
(localOlder ? "OLDER" : "NEWER"),
|
||||
difference, debug::valueOf(model.isNewlyCreated()) );
|
||||
}
|
||||
|
||||
thisModel.copyChangedProperties(model);
|
||||
markWithChangedTime();
|
||||
} else {
|
||||
|
|
|
@ -74,7 +74,7 @@ public:
|
|||
virtual bool requiresSplit() const { return false; }
|
||||
|
||||
/// Override to serialize the state of this element. This is used for persistance and for transmission across the network.
|
||||
virtual bool appendElementData(OctreePacketData* packetData) const;
|
||||
virtual bool appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const;
|
||||
|
||||
/// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading
|
||||
/// from the network.
|
||||
|
@ -118,6 +118,9 @@ public:
|
|||
|
||||
bool removeModelWithID(uint32_t id);
|
||||
|
||||
bool containsModelBounds(const ModelItem& model) const;
|
||||
bool bestFitModelBounds(const ModelItem& model) const;
|
||||
|
||||
protected:
|
||||
virtual void init(unsigned char * octalCode);
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ enum PacketType {
|
|||
PacketTypeRequestAssignment,
|
||||
PacketTypeCreateAssignment,
|
||||
PacketTypeDomainOAuthRequest,
|
||||
PacketTypeDataServerGet, // reusable
|
||||
PacketTypeMuteEnvironment,
|
||||
PacketTypeDataServerSend, // reusable
|
||||
PacketTypeDataServerConfirm,
|
||||
PacketTypeVoxelQuery,
|
||||
|
|
|
@ -578,6 +578,10 @@ OctreeElement* Octree::getOrCreateChildElementAt(float x, float y, float z, floa
|
|||
return getRoot()->getOrCreateChildElementAt(x, y, z, s);
|
||||
}
|
||||
|
||||
OctreeElement* Octree::getOrCreateChildElementContaining(const AABox& box) {
|
||||
return getRoot()->getOrCreateChildElementContaining(box);
|
||||
}
|
||||
|
||||
|
||||
// combines the ray cast arguments into a single object
|
||||
class RayArgs {
|
||||
|
@ -1001,7 +1005,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element,
|
|||
// Keep track of how deep we've encoded.
|
||||
currentEncodeLevel++;
|
||||
|
||||
params.maxLevelReached = std::max(currentEncodeLevel,params.maxLevelReached);
|
||||
params.maxLevelReached = std::max(currentEncodeLevel, params.maxLevelReached);
|
||||
|
||||
// If we've reached our max Search Level, then stop searching.
|
||||
if (currentEncodeLevel >= params.maxEncodeLevel) {
|
||||
|
@ -1342,7 +1346,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element,
|
|||
OctreeElement* childElement = element->getChildAtIndex(i);
|
||||
if (childElement) {
|
||||
int bytesBeforeChild = packetData->getUncompressedSize();
|
||||
continueThisLevel = childElement->appendElementData(packetData);
|
||||
continueThisLevel = childElement->appendElementData(packetData, params);
|
||||
int bytesAfterChild = packetData->getUncompressedSize();
|
||||
|
||||
if (!continueThisLevel) {
|
||||
|
|
|
@ -227,6 +227,7 @@ public:
|
|||
OctreeElement* getOctreeEnclosingElementAt(float x, float y, float z, float s) const;
|
||||
|
||||
OctreeElement* getOrCreateChildElementAt(float x, float y, float z, float s);
|
||||
OctreeElement* getOrCreateChildElementContaining(const AABox& box);
|
||||
|
||||
void recurseTreeWithOperation(RecurseOctreeOperation operation, void* extraData = NULL);
|
||||
void recurseTreeWithPostOperation(RecurseOctreeOperation operation, void* extraData = NULL);
|
||||
|
|
|
@ -1379,3 +1379,90 @@ OctreeElement* OctreeElement::getOrCreateChildElementAt(float x, float y, float
|
|||
// Now that we have the child to recurse down, let it answer the original question...
|
||||
return child->getOrCreateChildElementAt(x, y, z, s);
|
||||
}
|
||||
|
||||
|
||||
OctreeElement* OctreeElement::getOrCreateChildElementContaining(const AABox& box) {
|
||||
OctreeElement* child = NULL;
|
||||
|
||||
float ourScale = getScale();
|
||||
float boxScale = box.getScale();
|
||||
|
||||
if(boxScale > ourScale) {
|
||||
qDebug("UNEXPECTED -- OctreeElement::getOrCreateChildElementContaining() "
|
||||
"boxScale=[%f] > ourScale=[%f] ", boxScale, ourScale);
|
||||
}
|
||||
|
||||
// Determine which of our children the minimum and maximum corners of the box live in...
|
||||
glm::vec3 boxCornerMinimum = box.getCorner();
|
||||
glm::vec3 boxCornerMaximum = box.calcTopFarLeft();
|
||||
|
||||
int childIndexBoxMinimum = getMyChildContainingPoint(boxCornerMinimum);
|
||||
int childIndexBoxMaximum = getMyChildContainingPoint(boxCornerMaximum);
|
||||
|
||||
// If the minimum and maximum corners of the box are in two different children's boxes, then we are the containing element
|
||||
if (childIndexBoxMinimum != childIndexBoxMaximum) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// otherwise, they are the same and that child should be considered as the correct element
|
||||
int childIndex = childIndexBoxMinimum; // both the same...
|
||||
|
||||
// Now, check if we have a child at that location
|
||||
child = getChildAtIndex(childIndex);
|
||||
if (!child) {
|
||||
child = addChildAtIndex(childIndex);
|
||||
}
|
||||
|
||||
// Now that we have the child to recurse down, let it answer the original question...
|
||||
return child->getOrCreateChildElementContaining(box);
|
||||
}
|
||||
|
||||
int OctreeElement::getMyChildContainingPoint(const glm::vec3& point) const {
|
||||
glm::vec3 ourCenter = _box.calcCenter();
|
||||
int childIndex = CHILD_UNKNOWN;
|
||||
// left half
|
||||
if (point.x > ourCenter.x) {
|
||||
if (point.y > ourCenter.y) {
|
||||
// top left
|
||||
if (point.z > ourCenter.z) {
|
||||
// top left far
|
||||
childIndex = CHILD_TOP_LEFT_FAR;
|
||||
} else {
|
||||
// top left near
|
||||
childIndex = CHILD_TOP_LEFT_NEAR;
|
||||
}
|
||||
} else {
|
||||
// bottom left
|
||||
if (point.z > ourCenter.z) {
|
||||
// bottom left far
|
||||
childIndex = CHILD_BOTTOM_LEFT_FAR;
|
||||
} else {
|
||||
// bottom left near
|
||||
childIndex = CHILD_BOTTOM_LEFT_NEAR;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// right half
|
||||
if (point.y > ourCenter.y) {
|
||||
// top right
|
||||
if (point.z > ourCenter.z) {
|
||||
// top right far
|
||||
childIndex = CHILD_TOP_RIGHT_FAR;
|
||||
} else {
|
||||
// top right near
|
||||
childIndex = CHILD_TOP_RIGHT_NEAR;
|
||||
}
|
||||
} else {
|
||||
// bottom right
|
||||
if (point.z > ourCenter.z) {
|
||||
// bottom right far
|
||||
childIndex = CHILD_BOTTOM_RIGHT_FAR;
|
||||
} else {
|
||||
// bottom right near
|
||||
childIndex = CHILD_BOTTOM_RIGHT_NEAR;
|
||||
}
|
||||
}
|
||||
}
|
||||
return childIndex;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,14 +23,14 @@
|
|||
#include "AABox.h"
|
||||
#include "ViewFrustum.h"
|
||||
#include "OctreeConstants.h"
|
||||
//#include "Octree.h"
|
||||
|
||||
class EncodeBitstreamParams;
|
||||
class Octree;
|
||||
class OctreeElement;
|
||||
class OctreeElementDeleteHook;
|
||||
class OctreePacketData;
|
||||
class VoxelSystem;
|
||||
class ReadBitstreamToTreeParams;
|
||||
class VoxelSystem;
|
||||
|
||||
// Callers who want delete hook callbacks should implement this class
|
||||
class OctreeElementDeleteHook {
|
||||
|
@ -81,7 +81,7 @@ public:
|
|||
virtual bool requiresSplit() const { return false; }
|
||||
|
||||
/// Override to serialize the state of this element. This is used for persistance and for transmission across the network.
|
||||
virtual bool appendElementData(OctreePacketData* packetData) const { return true; }
|
||||
virtual bool appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const { return true; }
|
||||
|
||||
/// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading
|
||||
/// from the network.
|
||||
|
@ -217,6 +217,8 @@ public:
|
|||
|
||||
|
||||
OctreeElement* getOrCreateChildElementAt(float x, float y, float z, float s);
|
||||
OctreeElement* getOrCreateChildElementContaining(const AABox& box);
|
||||
int getMyChildContainingPoint(const glm::vec3& point) const;
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ public:
|
|||
virtual void renderElement(OctreeElement* element, RenderArgs* args) { /* swallow these */ };
|
||||
|
||||
virtual void init();
|
||||
virtual void render() { /* swallow these */ };
|
||||
virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE) { /* swallow these */ };
|
||||
|
||||
void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; }
|
||||
|
||||
|
|
|
@ -154,8 +154,8 @@ bool OctreeRenderer::renderOperation(OctreeElement* element, void* extraData) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void OctreeRenderer::render() {
|
||||
RenderArgs args = { 0, this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust() };
|
||||
void OctreeRenderer::render(RenderMode renderMode) {
|
||||
RenderArgs args = { 0, this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode };
|
||||
if (_tree) {
|
||||
_tree->lockForRead();
|
||||
_tree->recurseTreeWithOperation(renderOperation, &args);
|
||||
|
|
|
@ -25,15 +25,7 @@
|
|||
#include "ViewFrustum.h"
|
||||
|
||||
class OctreeRenderer;
|
||||
|
||||
class RenderArgs {
|
||||
public:
|
||||
int _renderedItems;
|
||||
OctreeRenderer* _renderer;
|
||||
ViewFrustum* _viewFrustum;
|
||||
float _sizeScale;
|
||||
int _boundaryLevelAdjust;
|
||||
};
|
||||
class RenderArgs;
|
||||
|
||||
|
||||
// Generic client side Octree renderer class.
|
||||
|
@ -59,8 +51,10 @@ public:
|
|||
/// initialize and GPU/rendering related resources
|
||||
virtual void init();
|
||||
|
||||
enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE };
|
||||
|
||||
/// render the content of the octree
|
||||
virtual void render();
|
||||
virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE);
|
||||
|
||||
ViewFrustum* getViewFrustum() const { return _viewFrustum; }
|
||||
void setViewFrustum(ViewFrustum* viewFrustum) { _viewFrustum = viewFrustum; }
|
||||
|
@ -75,4 +69,15 @@ protected:
|
|||
ViewFrustum* _viewFrustum;
|
||||
};
|
||||
|
||||
class RenderArgs {
|
||||
public:
|
||||
int _renderedItems;
|
||||
OctreeRenderer* _renderer;
|
||||
ViewFrustum* _viewFrustum;
|
||||
float _sizeScale;
|
||||
int _boundaryLevelAdjust;
|
||||
OctreeRenderer::RenderMode _renderMode;
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_OctreeRenderer_h
|
||||
|
|
|
@ -224,7 +224,7 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
|||
// (doing this prevents some "collision snagging" when particle penetrates the object)
|
||||
|
||||
// HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against them.
|
||||
if (collision->_type == PADDLE_HAND_COLLISION) {
|
||||
if (collision->_type == COLLISION_TYPE_PADDLE_HAND) {
|
||||
// NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle.
|
||||
// TODO: make this less hacky when we have more per-collision details
|
||||
float elasticity = ELASTICITY;
|
||||
|
|
|
@ -47,7 +47,7 @@ ParticleTreeElement* ParticleTreeElement::addChildAtIndex(int index) {
|
|||
}
|
||||
|
||||
|
||||
bool ParticleTreeElement::appendElementData(OctreePacketData* packetData) const {
|
||||
bool ParticleTreeElement::appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const {
|
||||
bool success = true; // assume the best...
|
||||
|
||||
// write our particles out...
|
||||
|
|
|
@ -76,7 +76,7 @@ public:
|
|||
virtual bool requiresSplit() const { return false; }
|
||||
|
||||
/// Override to serialize the state of this element. This is used for persistance and for transmission across the network.
|
||||
virtual bool appendElementData(OctreePacketData* packetData) const;
|
||||
virtual bool appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const;
|
||||
|
||||
/// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading
|
||||
/// from the network.
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QScriptEngine>
|
||||
|
||||
#include <AudioInjector.h>
|
||||
#include <AudioRingBuffer.h>
|
||||
#include <AvatarData.h>
|
||||
#include <CollisionInfo.h>
|
||||
|
@ -52,6 +53,14 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){
|
|||
return QScriptValue();
|
||||
}
|
||||
|
||||
QScriptValue injectorToScriptValue(QScriptEngine *engine, AudioInjector* const &in) {
|
||||
return engine->newQObject(in);
|
||||
}
|
||||
|
||||
void injectorFromScriptValue(const QScriptValue &object, AudioInjector* &out) {
|
||||
out = qobject_cast<AudioInjector*>(object.toQObject());
|
||||
}
|
||||
|
||||
ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString,
|
||||
AbstractControllerScriptingInterface* controllerScriptingInterface) :
|
||||
|
||||
|
@ -226,6 +235,8 @@ void ScriptEngine::init() {
|
|||
|
||||
QScriptValue localVoxelsValue = _engine.scriptValueFromQMetaObject<LocalVoxels>();
|
||||
_engine.globalObject().setProperty("LocalVoxels", localVoxelsValue);
|
||||
|
||||
qScriptRegisterMetaType(&_engine, injectorToScriptValue, injectorFromScriptValue);
|
||||
|
||||
registerGlobalObject("Script", this);
|
||||
registerGlobalObject("Audio", &_audioScriptingInterface);
|
||||
|
|
|
@ -38,19 +38,24 @@ CollisionInfo* CollisionList::getLastCollision() {
|
|||
}
|
||||
|
||||
void CollisionList::clear() {
|
||||
// we rely on the external context to properly set or clear the data members of a collision
|
||||
// whenever it is used.
|
||||
/*
|
||||
for (int i = 0; i < _size; ++i) {
|
||||
// we only clear the important stuff
|
||||
CollisionInfo& collision = _collisions[i];
|
||||
collision._type = BASE_COLLISION;
|
||||
collision._data = NULL; // CollisionInfo does not own whatever this points to.
|
||||
collision._flags = 0;
|
||||
// we rely on the consumer to properly overwrite these fields when the collision is "created"
|
||||
collision._type = COLLISION_TYPE_UNKNOWN;
|
||||
//collision._data = NULL;
|
||||
//collision._intData = 0;
|
||||
//collision._floatDAta = 0.0f;
|
||||
//collision._vecData = glm::vec3(0.0f);
|
||||
//collision._damping;
|
||||
//collision._elasticity;
|
||||
//collision._contactPoint;
|
||||
//collision._penetration;
|
||||
//collision._addedVelocity;
|
||||
}
|
||||
*/
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,9 +18,14 @@
|
|||
#include <QVector>
|
||||
|
||||
enum CollisionType {
|
||||
BASE_COLLISION = 0,
|
||||
PADDLE_HAND_COLLISION,
|
||||
MODEL_COLLISION,
|
||||
COLLISION_TYPE_UNKNOWN = 0,
|
||||
COLLISION_TYPE_PADDLE_HAND,
|
||||
COLLISION_TYPE_MODEL,
|
||||
// _data = pointer to Model that owns joint
|
||||
// _intData = joint index
|
||||
COLLISION_TYPE_AACUBE,
|
||||
// _floatData = cube side
|
||||
// _vecData = cube center
|
||||
};
|
||||
|
||||
const quint32 COLLISION_GROUP_ENVIRONMENT = 1U << 0;
|
||||
|
@ -39,7 +44,7 @@ public:
|
|||
CollisionInfo()
|
||||
: _type(0),
|
||||
_data(NULL),
|
||||
_flags(0),
|
||||
_intData(0),
|
||||
_damping(0.f),
|
||||
_elasticity(1.f),
|
||||
_contactPoint(0.f),
|
||||
|
@ -50,7 +55,7 @@ public:
|
|||
CollisionInfo(qint32 type)
|
||||
: _type(type),
|
||||
_data(NULL),
|
||||
_flags(0),
|
||||
_intData(0),
|
||||
_damping(0.f),
|
||||
_elasticity(1.f),
|
||||
_contactPoint(0.f),
|
||||
|
@ -60,9 +65,13 @@ public:
|
|||
|
||||
~CollisionInfo() {}
|
||||
|
||||
qint32 _type; // type of Collision (will determine what is supposed to be in _data and _flags)
|
||||
void* _data; // pointer to user supplied data
|
||||
quint32 _flags; // 32 bits for whatever
|
||||
int _type; // type of Collision
|
||||
|
||||
// the value of the *Data fields depend on the type
|
||||
void* _data;
|
||||
int _intData;
|
||||
float _floatData;
|
||||
glm::vec3 _vecData;
|
||||
|
||||
float _damping; // range [0,1] of friction coeficient
|
||||
float _elasticity; // range [0,1] of energy conservation
|
||||
|
|
|
@ -132,6 +132,7 @@ bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, Collis
|
|||
// penetration points from A into B
|
||||
CollisionInfo* collision = collisions.getNewCollision();
|
||||
if (collision) {
|
||||
collision->_type = COLLISION_TYPE_UNKNOWN;
|
||||
collision->_penetration = BA * (totalRadius - distance);
|
||||
// contactPoint is on surface of A
|
||||
collision->_contactPoint = sphereA->getPosition() + sphereA->getRadius() * BA;
|
||||
|
@ -179,6 +180,7 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col
|
|||
collision->_penetration = (totalRadius - radialDistance) * radialAxis; // points from A into B
|
||||
// contactPoint is on surface of sphereA
|
||||
collision->_contactPoint = sphereA->getPosition() + sphereA->getRadius() * radialAxis;
|
||||
collision->_type = COLLISION_TYPE_UNKNOWN;
|
||||
} else {
|
||||
// A is on B's axis, so the penetration is undefined...
|
||||
if (absAxialDistance > capsuleB->getHalfHeight()) {
|
||||
|
@ -200,6 +202,7 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col
|
|||
collision->_penetration = (sign * (totalRadius + capsuleB->getHalfHeight() - absAxialDistance)) * capsuleAxis;
|
||||
// contactPoint is on surface of sphereA
|
||||
collision->_contactPoint = sphereA->getPosition() + (sign * sphereA->getRadius()) * capsuleAxis;
|
||||
collision->_type = COLLISION_TYPE_UNKNOWN;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -215,6 +218,7 @@ bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, Collision
|
|||
}
|
||||
collision->_penetration = penetration;
|
||||
collision->_contactPoint = sphereA->getPosition() + sphereA->getRadius() * glm::normalize(penetration);
|
||||
collision->_type = COLLISION_TYPE_UNKNOWN;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -264,6 +268,7 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col
|
|||
collision->_penetration = (radialDistance - totalRadius) * radialAxis; // points from A into B
|
||||
// contactPoint is on surface of capsuleA
|
||||
collision->_contactPoint = closestApproach - capsuleA->getRadius() * radialAxis;
|
||||
collision->_type = COLLISION_TYPE_UNKNOWN;
|
||||
} else {
|
||||
// A is on B's axis, so the penetration is undefined...
|
||||
if (absAxialDistance > capsuleA->getHalfHeight()) {
|
||||
|
@ -284,6 +289,7 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col
|
|||
collision->_penetration = (sign * (totalRadius + capsuleA->getHalfHeight() - absAxialDistance)) * capsuleAxis;
|
||||
// contactPoint is on surface of sphereA
|
||||
collision->_contactPoint = closestApproach + (sign * capsuleA->getRadius()) * capsuleAxis;
|
||||
collision->_type = COLLISION_TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -355,6 +361,7 @@ bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB,
|
|||
collision->_penetration = BA * (totalRadius - distance);
|
||||
// contactPoint is on surface of A
|
||||
collision->_contactPoint = centerA + distanceA * axisA + capsuleA->getRadius() * BA;
|
||||
collision->_type = COLLISION_TYPE_UNKNOWN;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
|
@ -420,6 +427,7 @@ bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB,
|
|||
// average the internal pair, and then do the math from centerB
|
||||
collision->_contactPoint = centerB + (0.5f * (points[1] + points[2])) * axisB
|
||||
+ (capsuleA->getRadius() - distance) * BA;
|
||||
collision->_type = COLLISION_TYPE_UNKNOWN;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -439,6 +447,7 @@ bool capsulePlane(const CapsuleShape* capsuleA, const PlaneShape* planeB, Collis
|
|||
collision->_penetration = penetration;
|
||||
glm::vec3 deepestEnd = (glm::dot(start, glm::vec3(plane)) < glm::dot(end, glm::vec3(plane))) ? start : end;
|
||||
collision->_contactPoint = deepestEnd + capsuleA->getRadius() * glm::normalize(penetration);
|
||||
collision->_type = COLLISION_TYPE_UNKNOWN;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -454,6 +463,7 @@ bool planeSphere(const PlaneShape* planeA, const SphereShape* sphereB, Collision
|
|||
collision->_penetration = -penetration;
|
||||
collision->_contactPoint = sphereB->getPosition() +
|
||||
(sphereB->getRadius() / glm::length(penetration) - 1.0f) * penetration;
|
||||
collision->_type = COLLISION_TYPE_UNKNOWN;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -472,6 +482,7 @@ bool planeCapsule(const PlaneShape* planeA, const CapsuleShape* capsuleB, Collis
|
|||
collision->_penetration = -penetration;
|
||||
glm::vec3 deepestEnd = (glm::dot(start, glm::vec3(plane)) < glm::dot(end, glm::vec3(plane))) ? start : end;
|
||||
collision->_contactPoint = deepestEnd + (capsuleB->getRadius() / glm::length(penetration) - 1.0f) * penetration;
|
||||
collision->_type = COLLISION_TYPE_UNKNOWN;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -653,12 +664,19 @@ bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm::
|
|||
glm::vec3 direction;
|
||||
BA /= maxBA;
|
||||
glm::modf(BA, direction);
|
||||
direction = glm::normalize(direction);
|
||||
float lengthDirection = glm::length(direction);
|
||||
direction /= lengthDirection;
|
||||
|
||||
// compute collision details
|
||||
collision->_penetration = (halfCubeSide + sphereRadius - distance * glm::dot(BA, direction)) * direction;
|
||||
collision->_type = COLLISION_TYPE_AACUBE;
|
||||
collision->_floatData = cubeSide;
|
||||
collision->_vecData = cubeCenter;
|
||||
collision->_penetration = (halfCubeSide * lengthDirection + sphereRadius - maxBA * glm::dot(BA, direction)) * direction;
|
||||
collision->_contactPoint = sphereCenter + sphereRadius * direction;
|
||||
}
|
||||
collision->_type = COLLISION_TYPE_AACUBE;
|
||||
collision->_floatData = cubeSide;
|
||||
collision->_vecData = cubeCenter;
|
||||
return true;
|
||||
} else if (sphereRadius + halfCubeSide > distance) {
|
||||
// NOTE: for cocentric approximation we collide sphere and cube as two spheres which means
|
||||
|
@ -669,6 +687,10 @@ bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm::
|
|||
collision->_penetration = (sphereRadius + halfCubeSide) * glm::vec3(0.0f, -1.0f, 0.0f);
|
||||
// contactPoint is on surface of A
|
||||
collision->_contactPoint = sphereCenter + collision->_penetration;
|
||||
|
||||
collision->_type = COLLISION_TYPE_AACUBE;
|
||||
collision->_floatData = cubeSide;
|
||||
collision->_vecData = cubeCenter;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ void VoxelTreeElement::splitChildren() {
|
|||
}
|
||||
}
|
||||
|
||||
bool VoxelTreeElement::appendElementData(OctreePacketData* packetData) const {
|
||||
bool VoxelTreeElement::appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const {
|
||||
return packetData->appendColor(getColor());
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ public:
|
|||
virtual bool hasContent() const { return isColored(); }
|
||||
virtual void splitChildren();
|
||||
virtual bool requiresSplit() const;
|
||||
virtual bool appendElementData(OctreePacketData* packetData) const;
|
||||
virtual bool appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const;
|
||||
virtual int readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args);
|
||||
virtual void calculateAverageFromChildren();
|
||||
virtual bool collapseChildren();
|
||||
|
|
Loading…
Reference in a new issue