mirror of
https://github.com/overte-org/overte.git
synced 2025-04-16 21:02:17 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into ambient-bis
This commit is contained in:
commit
79bcf95792
59 changed files with 586 additions and 488 deletions
|
@ -443,13 +443,8 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
|
|||
(viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
|
||||
|
||||
EncodeBitstreamParams params(INT_MAX, WANT_EXISTS_BITS, DONT_CHOP,
|
||||
viewFrustumChanged,
|
||||
boundaryLevelAdjust, octreeSizeScale,
|
||||
nodeData->getLastTimeBagEmpty(),
|
||||
isFullScene, &nodeData->stats, _myServer->getJurisdiction(),
|
||||
&nodeData->extraEncodeData,
|
||||
nodeData->getUsesFrustum(),
|
||||
nodeData);
|
||||
viewFrustumChanged, boundaryLevelAdjust, octreeSizeScale,
|
||||
isFullScene, _myServer->getJurisdiction(), nodeData);
|
||||
nodeData->copyCurrentViewFrustum(params.viewFrustum);
|
||||
if (viewFrustumChanged) {
|
||||
nodeData->copyLastKnownViewFrustum(params.lastViewFrustum);
|
||||
|
|
|
@ -6,56 +6,60 @@
|
|||
# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(SETUP_HIFI_CLIENT_SERVER_PLUGIN)
|
||||
set(${TARGET_NAME}_SHARED 1)
|
||||
setup_hifi_library(${ARGV})
|
||||
if (NOT DEFINED SERVER_ONLY)
|
||||
add_dependencies(interface ${TARGET_NAME})
|
||||
endif()
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Plugins")
|
||||
set(${TARGET_NAME}_SHARED 1)
|
||||
setup_hifi_library(${ARGV})
|
||||
|
||||
if (APPLE)
|
||||
set(CLIENT_PLUGIN_PATH "${INTERFACE_BUNDLE_NAME}.app/Contents/PlugIns")
|
||||
set(SERVER_PLUGIN_PATH "plugins")
|
||||
else()
|
||||
set(CLIENT_PLUGIN_PATH "plugins")
|
||||
set(SERVER_PLUGIN_PATH "plugins")
|
||||
endif()
|
||||
if (NOT DEFINED SERVER_ONLY)
|
||||
add_dependencies(interface ${TARGET_NAME})
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_GENERATOR STREQUAL "Unix Makefiles")
|
||||
set(CLIENT_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/interface/${CLIENT_PLUGIN_PATH}/")
|
||||
set(SERVER_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/assignment-client/${SERVER_PLUGIN_PATH}/")
|
||||
elseif (APPLE)
|
||||
set(CLIENT_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/interface/$<CONFIGURATION>/${CLIENT_PLUGIN_PATH}/")
|
||||
set(SERVER_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/assignment-client/$<CONFIGURATION>/${SERVER_PLUGIN_PATH}/")
|
||||
else()
|
||||
set(CLIENT_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/interface/$<CONFIGURATION>/${CLIENT_PLUGIN_PATH}/")
|
||||
set(SERVER_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/assignment-client/$<CONFIGURATION>/${SERVER_PLUGIN_PATH}/")
|
||||
endif()
|
||||
add_dependencies(assignment-client ${TARGET_NAME})
|
||||
|
||||
# create the destination for the client plugin binaries
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory
|
||||
${CLIENT_PLUGIN_FULL_PATH}
|
||||
)
|
||||
# copy the client plugin binaries
|
||||
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||
"$<TARGET_FILE:${TARGET_NAME}>"
|
||||
${CLIENT_PLUGIN_FULL_PATH}
|
||||
)
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Plugins")
|
||||
|
||||
# create the destination for the server plugin binaries
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory
|
||||
${SERVER_PLUGIN_FULL_PATH}
|
||||
)
|
||||
# copy the server plugin binaries
|
||||
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||
"$<TARGET_FILE:${TARGET_NAME}>"
|
||||
${SERVER_PLUGIN_FULL_PATH}
|
||||
)
|
||||
if (APPLE)
|
||||
set(CLIENT_PLUGIN_PATH "${INTERFACE_BUNDLE_NAME}.app/Contents/PlugIns")
|
||||
set(SERVER_PLUGIN_PATH "plugins")
|
||||
else()
|
||||
set(CLIENT_PLUGIN_PATH "plugins")
|
||||
set(SERVER_PLUGIN_PATH "plugins")
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_GENERATOR STREQUAL "Unix Makefiles")
|
||||
set(CLIENT_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/interface/${CLIENT_PLUGIN_PATH}/")
|
||||
set(SERVER_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/assignment-client/${SERVER_PLUGIN_PATH}/")
|
||||
elseif (APPLE)
|
||||
set(CLIENT_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/interface/$<CONFIGURATION>/${CLIENT_PLUGIN_PATH}/")
|
||||
set(SERVER_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/assignment-client/$<CONFIGURATION>/${SERVER_PLUGIN_PATH}/")
|
||||
else()
|
||||
set(CLIENT_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/interface/$<CONFIGURATION>/${CLIENT_PLUGIN_PATH}/")
|
||||
set(SERVER_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/assignment-client/$<CONFIGURATION>/${SERVER_PLUGIN_PATH}/")
|
||||
endif()
|
||||
|
||||
# create the destination for the client plugin binaries
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory
|
||||
${CLIENT_PLUGIN_FULL_PATH}
|
||||
)
|
||||
# copy the client plugin binaries
|
||||
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||
"$<TARGET_FILE:${TARGET_NAME}>"
|
||||
${CLIENT_PLUGIN_FULL_PATH}
|
||||
)
|
||||
|
||||
# create the destination for the server plugin binaries
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory
|
||||
${SERVER_PLUGIN_FULL_PATH}
|
||||
)
|
||||
# copy the server plugin binaries
|
||||
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||
"$<TARGET_FILE:${TARGET_NAME}>"
|
||||
${SERVER_PLUGIN_FULL_PATH}
|
||||
)
|
||||
|
||||
endmacro()
|
||||
|
|
|
@ -192,7 +192,7 @@ link_hifi_libraries(
|
|||
shared octree ktx gpu gl gpu-gl procedural model render
|
||||
recording fbx networking model-networking entities avatars
|
||||
audio audio-client animation script-engine physics
|
||||
render-utils entities-renderer ui auto-updater
|
||||
render-utils entities-renderer avatars-renderer ui auto-updater
|
||||
controllers plugins
|
||||
ui-plugins display-plugins input-plugins
|
||||
${NON_ANDROID_LIBRARIES}
|
||||
|
|
|
@ -66,7 +66,11 @@ TabletModalWindow {
|
|||
HifiConstants { id: hifi }
|
||||
|
||||
onCanceled: {
|
||||
loginDialogRoot.Stack.view.pop()
|
||||
if (loginDialogRoot.Stack.view !== null) {
|
||||
loginDialogRoot.Stack.view.pop()
|
||||
} else {
|
||||
Tablet.getTablet("com.highfidelity.interface.tablet.system").gotoHomeScreen();
|
||||
}
|
||||
}
|
||||
|
||||
LoginDialog {
|
||||
|
|
|
@ -81,6 +81,25 @@ Item {
|
|||
anchors.fill: parent;
|
||||
visible: userImage.status != Image.Ready;
|
||||
}
|
||||
StateImage {
|
||||
id: infoHoverImage;
|
||||
visible: false;
|
||||
imageURL: "../../images/info-icon-2-state.svg";
|
||||
size: 32;
|
||||
buttonState: 1;
|
||||
anchors.centerIn: parent;
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: (selected || isMyCard) && activeTab == "nearbyTab";
|
||||
hoverEnabled: enabled
|
||||
onClicked: {
|
||||
userInfoViewer.url = defaultBaseUrl + "/users/" + userName;
|
||||
userInfoViewer.visible = true;
|
||||
}
|
||||
onEntered: infoHoverImage.visible = true;
|
||||
onExited: infoHoverImage.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Colored border around avatarImage
|
||||
|
@ -302,7 +321,7 @@ Item {
|
|||
height: usernameTextPixelSize + 4
|
||||
// Anchors
|
||||
anchors.top: isMyCard ? myDisplayName.bottom : pal.activeTab == "nearbyTab" ? displayNameContainer.bottom : undefined //(parent.height - displayNameTextPixelSize/2));
|
||||
anchors.verticalCenter: pal.activeTab == "connectionsTab" ? avatarImage.verticalCenter : undefined
|
||||
anchors.verticalCenter: pal.activeTab == "connectionsTab" && !isMyCard ? avatarImage.verticalCenter : undefined
|
||||
anchors.left: avatarImage.right;
|
||||
anchors.leftMargin: avatarImage.visible ? 5 : 0;
|
||||
anchors.rightMargin: 5;
|
||||
|
|
|
@ -910,17 +910,21 @@ Rectangle {
|
|||
color: hifi.colors.darkGray
|
||||
wrapMode: Text.WordWrap
|
||||
textFormat: Text.StyledText;
|
||||
property string hmdMountedInstructions:
|
||||
"1. Put your hand out onto their hand and squeeze your controller's grip button on its side.<br>" +
|
||||
"2. Once the other person puts their hand onto yours, you'll see your connection form.<br>" +
|
||||
"3. After about 3 seconds, you're connected!"
|
||||
property string hmdNotMountedInstructions:
|
||||
"1. Press and hold the 'x' key to extend your arm.<br>" +
|
||||
"2. Once the other person puts their hand onto yours, you'll see your connection form.<br>" +
|
||||
"3. After about 3 seconds, you're connected!";
|
||||
property string notLoggedInInstructions: "<b><font color='red'>You must be logged into your High Fidelity account to make connections.</b></font><br>"
|
||||
property string instructions:
|
||||
"<b>When you meet someone you want to remember later, you can <font color='purple'>connect</font> with a handshake:</b><br><br>"
|
||||
// Text
|
||||
text: HMD.isMounted ?
|
||||
"<b>When you meet someone you want to remember later, you can <font color='purple'>connect</font> with a handshake:</b><br><br>" +
|
||||
"1. Put your hand out onto their hand and squeeze your controller's grip button on its side.<br>" +
|
||||
"2. Once the other person puts their hand onto yours, you'll see your connection form.<br>" +
|
||||
"3. After about 3 seconds, you're connected!"
|
||||
:
|
||||
"<b>When you meet someone you want to remember later, you can <font color='purple'>connect</font> with a handshake:</b><br><br>" +
|
||||
"1. Press and hold the 'x' key to extend your arm.<br>" +
|
||||
"2. Once the other person puts their hand onto yours, you'll see your connection form.<br>" +
|
||||
"3. After about 3 seconds, you're connected!";
|
||||
text:
|
||||
Account.isLoggedIn() ? ( HMD.mounted ? instructions + hmdMountedInstructions : instructions + hmdNotMountedInstructions)
|
||||
: ( HMD.mounted ? notLoggedInInstructions + instructions + hmdMountedInstructions : notLoggedInInstructions + instructions + hmdNotMountedInstructions)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ Rectangle {
|
|||
property alias text: label.text
|
||||
property alias pixelSize: label.font.pixelSize;
|
||||
property bool selected: false
|
||||
property bool hovered: false
|
||||
property int spacing: 2
|
||||
property var action: function () {}
|
||||
property string highlightColor: hifi.colors.blueHighlight;
|
||||
|
@ -37,14 +38,14 @@ Rectangle {
|
|||
Rectangle {
|
||||
id: indicator
|
||||
width: parent.width
|
||||
height: 3
|
||||
height: selected ? 3 : 1
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
color: hifi.colors.blueHighlight
|
||||
visible: parent.selected
|
||||
visible: parent.selected || hovered
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
|
@ -53,6 +54,8 @@ Rectangle {
|
|||
acceptedButtons: Qt.LeftButton;
|
||||
onClicked: action(parent);
|
||||
hoverEnabled: true;
|
||||
onEntered: hovered = true
|
||||
onExited: hovered = false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -255,7 +255,7 @@ StackView {
|
|||
TabletTextButton {
|
||||
id: allTab;
|
||||
text: "ALL";
|
||||
property string includeActions: 'snapshot, concurrency';
|
||||
property string includeActions: 'snapshot,concurrency';
|
||||
selected: allTab === selectedTab;
|
||||
action: tabSelect;
|
||||
}
|
||||
|
|
|
@ -1802,10 +1802,11 @@ Application::~Application() {
|
|||
_physicsEngine->setCharacterController(nullptr);
|
||||
|
||||
// remove avatars from physics engine
|
||||
DependencyManager::get<AvatarManager>()->clearAllAvatars();
|
||||
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
|
||||
VectorOfMotionStates motionStates;
|
||||
DependencyManager::get<AvatarManager>()->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
DependencyManager::get<AvatarManager>()->deleteAllAvatars();
|
||||
|
||||
DependencyManager::destroy<AvatarManager>();
|
||||
DependencyManager::destroy<AnimationCache>();
|
||||
|
|
|
@ -193,6 +193,9 @@ void Avatar::animateScaleChanges(float deltaTime) {
|
|||
}
|
||||
setScale(glm::vec3(animatedScale)); // avatar scale is uniform
|
||||
|
||||
// flag the joints as having changed for force update to RenderItem
|
||||
_hasNewJointData = true;
|
||||
|
||||
// TODO: rebuilding the shape constantly is somehwat expensive.
|
||||
// We should only rebuild after significant change.
|
||||
rebuildCollisionShape();
|
||||
|
@ -200,8 +203,12 @@ void Avatar::animateScaleChanges(float deltaTime) {
|
|||
}
|
||||
|
||||
void Avatar::setTargetScale(float targetScale) {
|
||||
AvatarData::setTargetScale(targetScale);
|
||||
_isAnimatingScale = true;
|
||||
float newValue = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
|
||||
if (_targetScale != newValue) {
|
||||
_targetScale = newValue;
|
||||
_scaleChanged = usecTimestampNow();
|
||||
_isAnimatingScale = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::updateAvatarEntities() {
|
||||
|
@ -476,7 +483,7 @@ static TextRenderer3D* textRenderer(TextRendererType type) {
|
|||
return displayNameRenderer;
|
||||
}
|
||||
|
||||
bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
|
||||
void Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
|
||||
auto avatarPayload = new render::Payload<AvatarData>(self);
|
||||
auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload);
|
||||
_renderItemID = scene->allocateID();
|
||||
|
@ -486,9 +493,6 @@ bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene>
|
|||
for (auto& attachmentModel : _attachmentModels) {
|
||||
attachmentModel->addToScene(scene, transaction);
|
||||
}
|
||||
|
||||
_inScene = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
|
||||
|
@ -498,7 +502,6 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::S
|
|||
for (auto& attachmentModel : _attachmentModels) {
|
||||
attachmentModel->removeFromScene(scene, transaction);
|
||||
}
|
||||
_inScene = false;
|
||||
}
|
||||
|
||||
void Avatar::updateRenderItem(render::Transaction& transaction) {
|
||||
|
@ -933,6 +936,21 @@ glm::vec3 Avatar::getDefaultJointTranslation(int index) const {
|
|||
return translation;
|
||||
}
|
||||
|
||||
glm::quat Avatar::getAbsoluteDefaultJointRotationInObjectFrame(int index) const {
|
||||
glm::quat rotation;
|
||||
auto rig = _skeletonModel->getRig();
|
||||
glm::quat rot = rig->getAnimSkeleton()->getAbsoluteDefaultPose(index).rot();
|
||||
return Quaternions::Y_180 * rot;
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::getAbsoluteDefaultJointTranslationInObjectFrame(int index) const {
|
||||
glm::vec3 translation;
|
||||
auto rig = _skeletonModel->getRig();
|
||||
glm::vec3 trans = rig->getAnimSkeleton()->getAbsoluteDefaultPose(index).trans();
|
||||
glm::mat4 y180Mat = createMatFromQuatAndPos(Quaternions::Y_180, glm::vec3());
|
||||
return transformPoint(y180Mat * rig->getGeometryToRigTransform(), trans);
|
||||
}
|
||||
|
||||
glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const {
|
||||
if (index < 0) {
|
||||
index += numeric_limits<unsigned short>::max() + 1; // 65536
|
||||
|
@ -1435,7 +1453,7 @@ void Avatar::addToScene(AvatarSharedPointer myHandle) {
|
|||
}
|
||||
}
|
||||
void Avatar::ensureInScene(AvatarSharedPointer self) {
|
||||
if (!_inScene) {
|
||||
if (!render::Item::isValidID(_renderItemID)) {
|
||||
addToScene(self);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ public:
|
|||
|
||||
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition);
|
||||
|
||||
bool addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene,
|
||||
void addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene,
|
||||
render::Transaction& transaction);
|
||||
|
||||
void removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene,
|
||||
|
@ -121,6 +121,26 @@ public:
|
|||
Q_INVOKABLE virtual glm::quat getDefaultJointRotation(int index) const;
|
||||
Q_INVOKABLE virtual glm::vec3 getDefaultJointTranslation(int index) const;
|
||||
|
||||
/**jsdoc
|
||||
* Provides read only access to the default joint rotations in avatar coordinates.
|
||||
* The default pose of the avatar is defined by the position and orientation of all bones
|
||||
* in the avatar's model file. Typically this is a t-pose.
|
||||
* @function Avatar.getAbsoluteDefaultJointRotationInObjectFrame
|
||||
* @param index {number} index number
|
||||
* @returns {Quat} The rotation of this joint in avatar coordinates.
|
||||
*/
|
||||
Q_INVOKABLE virtual glm::quat getAbsoluteDefaultJointRotationInObjectFrame(int index) const;
|
||||
|
||||
/**jsdoc
|
||||
* Provides read only access to the default joint translations in avatar coordinates.
|
||||
* The default pose of the avatar is defined by the position and orientation of all bones
|
||||
* in the avatar's model file. Typically this is a t-pose.
|
||||
* @function Avatar.getAbsoluteDefaultJointTranslationInObjectFrame
|
||||
* @param index {number} index number
|
||||
* @returns {Vec3} The position of this joint in avatar coordinates.
|
||||
*/
|
||||
Q_INVOKABLE virtual glm::vec3 getAbsoluteDefaultJointTranslationInObjectFrame(int index) const;
|
||||
|
||||
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
|
||||
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
|
||||
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; }
|
||||
|
@ -285,6 +305,7 @@ protected:
|
|||
|
||||
void addToScene(AvatarSharedPointer self);
|
||||
void ensureInScene(AvatarSharedPointer self);
|
||||
bool isInScene() const { return render::Item::isValidID(_renderItemID); }
|
||||
|
||||
// Some rate tracking support
|
||||
RateCounter<> _simulationRate;
|
||||
|
@ -310,7 +331,6 @@ private:
|
|||
int _nameRectGeometryID { 0 };
|
||||
bool _initialized;
|
||||
bool _isLookAtTarget { false };
|
||||
bool _inScene { false };
|
||||
bool _isAnimatingScale { false };
|
||||
|
||||
float getBoundingRadius() const;
|
||||
|
|
|
@ -68,7 +68,7 @@ void AvatarManager::registerMetaTypes(QScriptEngine* engine) {
|
|||
}
|
||||
|
||||
AvatarManager::AvatarManager(QObject* parent) :
|
||||
_avatarFades(),
|
||||
_avatarsToFade(),
|
||||
_myAvatar(std::make_shared<MyAvatar>(std::make_shared<Rig>()))
|
||||
{
|
||||
// register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar
|
||||
|
@ -100,15 +100,16 @@ void AvatarManager::init() {
|
|||
_avatarHash.insert(MY_AVATAR_KEY, _myAvatar);
|
||||
}
|
||||
|
||||
_shouldRender = DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars();
|
||||
connect(DependencyManager::get<SceneScriptingInterface>().data(), &SceneScriptingInterface::shouldRenderAvatarsChanged,
|
||||
this, &AvatarManager::updateAvatarRenderStatus, Qt::QueuedConnection);
|
||||
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars()) {
|
||||
if (_shouldRender) {
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
_myAvatar->addToScene(_myAvatar, scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
void AvatarManager::updateMyAvatar(float deltaTime) {
|
||||
|
@ -151,7 +152,7 @@ float AvatarManager::getAvatarSimulationRate(const QUuid& sessionID, const QStri
|
|||
void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||
// lock the hash for read to check the size
|
||||
QReadLocker lock(&_hashLock);
|
||||
if (_avatarHash.size() < 2 && _avatarFades.isEmpty()) {
|
||||
if (_avatarHash.size() < 2 && _avatarsToFade.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
lock.unlock();
|
||||
|
@ -181,30 +182,24 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
// DO NOT update or fade out uninitialized Avatars
|
||||
return true; // ignore it
|
||||
}
|
||||
if (avatar->shouldDie()) {
|
||||
removeAvatar(avatar->getID());
|
||||
return true; // ignore it
|
||||
}
|
||||
if (avatar->isDead()) {
|
||||
return true; // ignore it
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
render::Transaction transaction;
|
||||
uint64_t startTime = usecTimestampNow();
|
||||
const uint64_t UPDATE_BUDGET = 2000; // usec
|
||||
uint64_t updateExpiry = startTime + UPDATE_BUDGET;
|
||||
|
||||
int numAvatarsUpdated = 0;
|
||||
int numAVatarsNotUpdated = 0;
|
||||
|
||||
render::Transaction transaction;
|
||||
while (!sortedAvatars.empty()) {
|
||||
const AvatarPriority& sortData = sortedAvatars.top();
|
||||
const auto& avatar = std::static_pointer_cast<Avatar>(sortData.avatar);
|
||||
|
||||
// for ALL avatars...
|
||||
avatar->ensureInScene(avatar);
|
||||
if (_shouldRender) {
|
||||
avatar->ensureInScene(avatar);
|
||||
}
|
||||
if (!avatar->getMotionState()) {
|
||||
ShapeInfo shapeInfo;
|
||||
avatar->computeShapeInfo(shapeInfo);
|
||||
|
@ -218,6 +213,10 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
}
|
||||
}
|
||||
avatar->animateScaleChanges(deltaTime);
|
||||
if (avatar->shouldDie()) {
|
||||
avatar->die();
|
||||
removeAvatar(avatar->getID());
|
||||
}
|
||||
|
||||
const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY;
|
||||
uint64_t now = usecTimestampNow();
|
||||
|
@ -259,10 +258,24 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
sortedAvatars.pop();
|
||||
}
|
||||
|
||||
if (_shouldRender) {
|
||||
if (!_avatarsToFade.empty()) {
|
||||
QReadLocker lock(&_hashLock);
|
||||
QVector<AvatarSharedPointer>::iterator itr = _avatarsToFade.begin();
|
||||
while (itr != _avatarsToFade.end() && usecTimestampNow() > updateExpiry) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(*itr);
|
||||
avatar->animateScaleChanges(deltaTime);
|
||||
avatar->simulate(deltaTime, true);
|
||||
avatar->updateRenderItem(transaction);
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
qApp->getMain3DScene()->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
_avatarSimulationTime = (float)(usecTimestampNow() - startTime) / (float)USECS_PER_MSEC;
|
||||
_numAvatarsUpdated = numAvatarsUpdated;
|
||||
_numAvatarsNotUpdated = numAVatarsNotUpdated;
|
||||
qApp->getMain3DScene()->enqueueTransaction(transaction);
|
||||
|
||||
simulateAvatarFades(deltaTime);
|
||||
}
|
||||
|
@ -277,54 +290,76 @@ void AvatarManager::postUpdate(float deltaTime) {
|
|||
}
|
||||
|
||||
void AvatarManager::simulateAvatarFades(float deltaTime) {
|
||||
QVector<AvatarSharedPointer>::iterator fadingIterator = _avatarFades.begin();
|
||||
if (_avatarsToFade.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const float SHRINK_RATE = 0.15f;
|
||||
const float MIN_FADE_SCALE = MIN_AVATAR_SCALE;
|
||||
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
while (fadingIterator != _avatarFades.end()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(*fadingIterator);
|
||||
QReadLocker locker(&_hashLock);
|
||||
QVector<AvatarSharedPointer>::iterator itr = _avatarsToFade.begin();
|
||||
while (itr != _avatarsToFade.end()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(*itr);
|
||||
avatar->setTargetScale(avatar->getUniformScale() * SHRINK_RATE);
|
||||
avatar->animateScaleChanges(deltaTime);
|
||||
if (avatar->getTargetScale() <= MIN_FADE_SCALE) {
|
||||
avatar->removeFromScene(*fadingIterator, scene, transaction);
|
||||
// only remove from _avatarFades if we're sure its motionState has been removed from PhysicsEngine
|
||||
// fading to zero is such a rare event we push unique transaction for each one
|
||||
if (avatar->isInScene()) {
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
avatar->removeFromScene(*itr, scene, transaction);
|
||||
if (scene) {
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
// only remove from _avatarsToFade if we're sure its motionState has been removed from PhysicsEngine
|
||||
if (_motionStatesToRemoveFromPhysics.empty()) {
|
||||
fadingIterator = _avatarFades.erase(fadingIterator);
|
||||
itr = _avatarsToFade.erase(itr);
|
||||
} else {
|
||||
++fadingIterator;
|
||||
++itr;
|
||||
}
|
||||
} else {
|
||||
const bool inView = true; // HACK
|
||||
avatar->simulate(deltaTime, inView);
|
||||
++fadingIterator;
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
AvatarSharedPointer AvatarManager::newSharedAvatar() {
|
||||
return std::make_shared<Avatar>(std::make_shared<Rig>());
|
||||
}
|
||||
|
||||
AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
|
||||
auto newAvatar = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer);
|
||||
auto rawRenderableAvatar = std::static_pointer_cast<Avatar>(newAvatar);
|
||||
|
||||
rawRenderableAvatar->addToScene(rawRenderableAvatar);
|
||||
|
||||
return newAvatar;
|
||||
}
|
||||
|
||||
// virtual
|
||||
void AvatarManager::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) {
|
||||
QWriteLocker locker(&_hashLock);
|
||||
|
||||
auto removedAvatar = _avatarHash.take(sessionUUID);
|
||||
if (removedAvatar) {
|
||||
handleRemovedAvatar(removedAvatar, removalReason);
|
||||
void AvatarManager::processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
PerformanceTimer perfTimer("receiveAvatar");
|
||||
// enumerate over all of the avatars in this packet
|
||||
// only add them if mixerWeakPointer points to something (meaning that mixer is still around)
|
||||
while (message->getBytesLeftToRead()) {
|
||||
AvatarSharedPointer avatarData = parseAvatarData(message, sendingNode);
|
||||
if (avatarData) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(avatarData);
|
||||
if (avatar->isInScene()) {
|
||||
if (!_shouldRender) {
|
||||
// rare transition so we process the transaction immediately
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
if (scene) {
|
||||
render::Transaction transaction;
|
||||
avatar->removeFromScene(avatar, scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
}
|
||||
} else if (_shouldRender) {
|
||||
// very rare transition so we process the transaction immediately
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
if (scene) {
|
||||
render::Transaction transaction;
|
||||
avatar->addToScene(avatar, scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -353,35 +388,46 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
|
|||
DependencyManager::get<NodeList>()->removeFromIgnoreMuteSets(avatar->getSessionUUID());
|
||||
DependencyManager::get<UsersScriptingInterface>()->avatarDisconnected(avatar->getSessionUUID());
|
||||
}
|
||||
_avatarFades.push_back(removedAvatar);
|
||||
_avatarsToFade.push_back(removedAvatar);
|
||||
}
|
||||
|
||||
void AvatarManager::clearOtherAvatars() {
|
||||
// clear any avatars that came from an avatar-mixer
|
||||
QWriteLocker locker(&_hashLock);
|
||||
// Remove other avatars from the world but don't actually remove them from _avatarHash
|
||||
// each will either be removed on timeout or will re-added to the world on receipt of update.
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
|
||||
QReadLocker locker(&_hashLock);
|
||||
AvatarHash::iterator avatarIterator = _avatarHash.begin();
|
||||
while (avatarIterator != _avatarHash.end()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(avatarIterator.value());
|
||||
if (avatar == _myAvatar || !avatar->isInitialized()) {
|
||||
// don't remove myAvatar or uninitialized avatars from the list
|
||||
++avatarIterator;
|
||||
} else {
|
||||
auto removedAvatar = avatarIterator.value();
|
||||
avatarIterator = _avatarHash.erase(avatarIterator);
|
||||
|
||||
handleRemovedAvatar(removedAvatar);
|
||||
if (avatar != _myAvatar) {
|
||||
if (avatar->isInScene()) {
|
||||
avatar->removeFromScene(avatar, scene, transaction);
|
||||
}
|
||||
AvatarMotionState* motionState = avatar->getMotionState();
|
||||
if (motionState) {
|
||||
_motionStatesThatMightUpdate.remove(motionState);
|
||||
_motionStatesToAddToPhysics.remove(motionState);
|
||||
_motionStatesToRemoveFromPhysics.push_back(motionState);
|
||||
}
|
||||
}
|
||||
++avatarIterator;
|
||||
}
|
||||
if (scene) {
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
_myAvatar->clearLookAtTargetAvatar();
|
||||
}
|
||||
|
||||
void AvatarManager::clearAllAvatars() {
|
||||
clearOtherAvatars();
|
||||
|
||||
QWriteLocker locker(&_hashLock);
|
||||
|
||||
handleRemovedAvatar(_myAvatar);
|
||||
void AvatarManager::deleteAllAvatars() {
|
||||
QReadLocker locker(&_hashLock);
|
||||
AvatarHash::iterator avatarIterator = _avatarHash.begin();
|
||||
while (avatarIterator != _avatarHash.end()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(avatarIterator.value());
|
||||
avatarIterator = _avatarHash.erase(avatarIterator);
|
||||
avatar->die();
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarManager::setLocalLights(const QVector<AvatarManager::LocalLight>& localLights) {
|
||||
|
@ -475,26 +521,25 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents
|
|||
}
|
||||
|
||||
void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) {
|
||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars()) {
|
||||
_shouldRender = shouldRenderAvatars;
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
if (_shouldRender) {
|
||||
for (auto avatarData : _avatarHash) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(avatarData);
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
avatar->addToScene(avatar, scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
} else {
|
||||
for (auto avatarData : _avatarHash) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(avatarData);
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
avatar->removeFromScene(avatar, scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
}
|
||||
if (scene) {
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) const {
|
||||
if (sessionID == AVATAR_SELF_ID || sessionID == _myAvatar->getSessionUUID()) {
|
||||
return _myAvatar;
|
||||
|
|
|
@ -53,7 +53,7 @@ public:
|
|||
void postUpdate(float deltaTime);
|
||||
|
||||
void clearOtherAvatars();
|
||||
void clearAllAvatars();
|
||||
void deleteAllAvatars();
|
||||
|
||||
bool shouldShowReceiveStats() const { return _shouldShowReceiveStats; }
|
||||
|
||||
|
@ -91,8 +91,8 @@ public slots:
|
|||
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
|
||||
void updateAvatarRenderStatus(bool shouldRenderAvatars);
|
||||
|
||||
private slots:
|
||||
virtual void removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason = KillAvatarReason::NoReason) override;
|
||||
protected slots:
|
||||
void processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) override;
|
||||
|
||||
private:
|
||||
explicit AvatarManager(QObject* parent = 0);
|
||||
|
@ -100,12 +100,15 @@ private:
|
|||
|
||||
void simulateAvatarFades(float deltaTime);
|
||||
|
||||
// virtual overrides
|
||||
virtual AvatarSharedPointer newSharedAvatar() override;
|
||||
virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) override;
|
||||
virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override;
|
||||
AvatarSharedPointer newSharedAvatar() override;
|
||||
void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override;
|
||||
|
||||
QVector<AvatarSharedPointer> _avatarsToFade;
|
||||
|
||||
SetOfAvatarMotionStates _motionStatesThatMightUpdate;
|
||||
VectorOfMotionStates _motionStatesToRemoveFromPhysics;
|
||||
SetOfMotionStates _motionStatesToAddToPhysics;
|
||||
|
||||
QVector<AvatarSharedPointer> _avatarFades;
|
||||
std::shared_ptr<MyAvatar> _myAvatar;
|
||||
quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate.
|
||||
|
||||
|
@ -115,14 +118,11 @@ private:
|
|||
|
||||
std::list<QPointer<AudioInjector>> _collisionInjectors;
|
||||
|
||||
SetOfAvatarMotionStates _motionStatesThatMightUpdate;
|
||||
SetOfMotionStates _motionStatesToAddToPhysics;
|
||||
VectorOfMotionStates _motionStatesToRemoveFromPhysics;
|
||||
|
||||
RateCounter<> _myAvatarSendRate;
|
||||
int _numAvatarsUpdated { 0 };
|
||||
int _numAvatarsNotUpdated { 0 };
|
||||
float _avatarSimulationTime { 0.0f };
|
||||
bool _shouldRender { true };
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(AvatarManager::LocalLight)
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "DebugDraw.h"
|
||||
#include "EntityEditPacketSender.h"
|
||||
#include "MovingEntitiesOperator.h"
|
||||
#include "SceneScriptingInterface.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -1634,7 +1635,7 @@ void MyAvatar::postUpdate(float deltaTime) {
|
|||
Avatar::postUpdate(deltaTime);
|
||||
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
if (_skeletonModel->initWhenReady(scene)) {
|
||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars() && _skeletonModel->initWhenReady(scene)) {
|
||||
initHeadBones();
|
||||
_skeletonModel->setCauterizeBoneSet(_headBoneSet);
|
||||
_fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl();
|
||||
|
|
|
@ -109,7 +109,7 @@ void AnimVariantMap::animVariantMapFromScriptValue(const QScriptValue& source) {
|
|||
if (z.isNumber()) {
|
||||
QScriptValue w = value.property("w");
|
||||
if (w.isNumber()) {
|
||||
set(property.name(), glm::quat(x.toNumber(), y.toNumber(), z.toNumber(), w.toNumber()));
|
||||
set(property.name(), glm::quat(w.toNumber(), x.toNumber(), y.toNumber(), z.toNumber()));
|
||||
} else {
|
||||
set(property.name(), glm::vec3(x.toNumber(), y.toNumber(), z.toNumber()));
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ void AnimationReader::run() {
|
|||
// Parse the FBX directly from the QNetworkReply
|
||||
FBXGeometry::Pointer fbxgeo;
|
||||
if (_url.path().toLower().endsWith(".fbx")) {
|
||||
fbxgeo.reset(readFBX(_data, QVariantHash(), _url));
|
||||
fbxgeo.reset(readFBX(_data, QVariantHash(), _url.path()));
|
||||
} else {
|
||||
QString errorStr("usupported format");
|
||||
emit onError(299, errorStr);
|
||||
|
|
6
libraries/avatars-renderer/CMakeLists.txt
Normal file
6
libraries/avatars-renderer/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
set(TARGET_NAME avatars-renderer)
|
||||
AUTOSCRIBE_SHADER_LIB(gpu model render render-utils)
|
||||
setup_hifi_library(Widgets Network Script)
|
||||
link_hifi_libraries(shared gpu model animation physics model-networking script-engine render render-utils)
|
||||
|
||||
target_bullet()
|
11
libraries/avatars-renderer/src/AvatarsRendererLogging.cpp
Normal file
11
libraries/avatars-renderer/src/AvatarsRendererLogging.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/12/06
|
||||
// Copyright 2013-2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AvatarsRendererLogging.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(avatars_renderer, "hifi.avatars.rendering")
|
16
libraries/avatars-renderer/src/AvatarsRendererLogging.h
Normal file
16
libraries/avatars-renderer/src/AvatarsRendererLogging.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/12/06
|
||||
// Copyright 2013-2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AvatarsRendererLogging_h
|
||||
#define hifi_AvatarsRendererLogging_h
|
||||
|
||||
#include <QtCore/QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(avatars_renderer)
|
||||
|
||||
#endif // hifi_AvatarsRendererLogging_h
|
|
@ -123,6 +123,7 @@ void AvatarData::setTargetScale(float targetScale) {
|
|||
if (_targetScale != newValue) {
|
||||
_targetScale = newValue;
|
||||
_scaleChanged = usecTimestampNow();
|
||||
_avatarScaleChanged = _scaleChanged;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -80,13 +80,10 @@ AvatarSharedPointer AvatarHashMap::addAvatar(const QUuid& sessionUUID, const QWe
|
|||
|
||||
AvatarSharedPointer AvatarHashMap::newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
|
||||
QWriteLocker locker(&_hashLock);
|
||||
|
||||
auto avatar = _avatarHash.value(sessionUUID);
|
||||
|
||||
if (!avatar) {
|
||||
avatar = addAvatar(sessionUUID, mixerWeakPointer);
|
||||
}
|
||||
|
||||
return avatar;
|
||||
}
|
||||
|
||||
|
@ -103,27 +100,33 @@ void AvatarHashMap::processAvatarDataPacket(QSharedPointer<ReceivedMessage> mess
|
|||
// enumerate over all of the avatars in this packet
|
||||
// only add them if mixerWeakPointer points to something (meaning that mixer is still around)
|
||||
while (message->getBytesLeftToRead()) {
|
||||
QUuid sessionUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
parseAvatarData(message, sendingNode);
|
||||
}
|
||||
}
|
||||
|
||||
int positionBeforeRead = message->getPosition();
|
||||
AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
QUuid sessionUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
QByteArray byteArray = message->readWithoutCopy(message->getBytesLeftToRead());
|
||||
int positionBeforeRead = message->getPosition();
|
||||
|
||||
// make sure this isn't our own avatar data or for a previously ignored node
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
QByteArray byteArray = message->readWithoutCopy(message->getBytesLeftToRead());
|
||||
|
||||
if (sessionUUID != _lastOwnerSessionUUID && (!nodeList->isIgnoringNode(sessionUUID) || nodeList->getRequestsDomainListData())) {
|
||||
auto avatar = newOrExistingAvatar(sessionUUID, sendingNode);
|
||||
// make sure this isn't our own avatar data or for a previously ignored node
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// have the matching (or new) avatar parse the data from the packet
|
||||
int bytesRead = avatar->parseDataFromBuffer(byteArray);
|
||||
message->seek(positionBeforeRead + bytesRead);
|
||||
} else {
|
||||
// create a dummy AvatarData class to throw this data on the ground
|
||||
AvatarData dummyData;
|
||||
int bytesRead = dummyData.parseDataFromBuffer(byteArray);
|
||||
message->seek(positionBeforeRead + bytesRead);
|
||||
}
|
||||
if (sessionUUID != _lastOwnerSessionUUID && (!nodeList->isIgnoringNode(sessionUUID) || nodeList->getRequestsDomainListData())) {
|
||||
auto avatar = newOrExistingAvatar(sessionUUID, sendingNode);
|
||||
|
||||
// have the matching (or new) avatar parse the data from the packet
|
||||
int bytesRead = avatar->parseDataFromBuffer(byteArray);
|
||||
message->seek(positionBeforeRead + bytesRead);
|
||||
return avatar;
|
||||
} else {
|
||||
// create a dummy AvatarData class to throw this data on the ground
|
||||
AvatarData dummyData;
|
||||
int bytesRead = dummyData.parseDataFromBuffer(byteArray);
|
||||
message->seek(positionBeforeRead + bytesRead);
|
||||
return std::make_shared<AvatarData>();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,11 +49,11 @@ signals:
|
|||
|
||||
public slots:
|
||||
bool isAvatarInRange(const glm::vec3 & position, const float range);
|
||||
|
||||
private slots:
|
||||
|
||||
protected slots:
|
||||
void sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID);
|
||||
|
||||
void processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
|
||||
virtual void processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
void processAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
void processKillAvatar(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
void processExitingSpaceBubble(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
|
@ -61,12 +61,13 @@ private slots:
|
|||
protected:
|
||||
AvatarHashMap();
|
||||
|
||||
virtual AvatarSharedPointer parseAvatarData(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
virtual AvatarSharedPointer newSharedAvatar();
|
||||
virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer);
|
||||
AvatarSharedPointer newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer);
|
||||
virtual AvatarSharedPointer findAvatar(const QUuid& sessionUUID) const; // uses a QReadLocker on the hashLock
|
||||
virtual void removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason = KillAvatarReason::NoReason);
|
||||
|
||||
|
||||
virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason);
|
||||
|
||||
AvatarHash _avatarHash;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <QJsonDocument>
|
||||
#include <QtCore/QThread>
|
||||
#include <QUrlQuery>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include <AbstractViewStateInterface.h>
|
||||
|
@ -607,11 +608,19 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori
|
|||
face, surfaceNormal, extraInfo, precisionPicking);
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::getCollisionGeometryResource() {
|
||||
QUrl hullURL(getCompoundShapeURL());
|
||||
QUrlQuery queryArgs(hullURL);
|
||||
queryArgs.addQueryItem("collision-hull", "");
|
||||
hullURL.setQuery(queryArgs);
|
||||
_compoundShapeResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(hullURL);
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::setShapeType(ShapeType type) {
|
||||
ModelEntityItem::setShapeType(type);
|
||||
if (getShapeType() == SHAPE_TYPE_COMPOUND) {
|
||||
if (!_compoundShapeResource && !getCompoundShapeURL().isEmpty()) {
|
||||
_compoundShapeResource = DependencyManager::get<ModelCache>()->getGeometryResource(getCompoundShapeURL());
|
||||
getCollisionGeometryResource();
|
||||
}
|
||||
} else if (_compoundShapeResource && !getCompoundShapeURL().isEmpty()) {
|
||||
// the compoundURL has been set but the shapeType does not agree
|
||||
|
@ -620,6 +629,10 @@ void RenderableModelEntityItem::setShapeType(ShapeType type) {
|
|||
}
|
||||
|
||||
void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) {
|
||||
// because the caching system only allows one Geometry per url, and because this url might also be used
|
||||
// as a visual model, we need to change this url in some way. We add a "collision-hull" query-arg so it
|
||||
// will end up in a different hash-key in ResourceCache. TODO: It would be better to use the same URL and
|
||||
// parse it twice.
|
||||
auto currentCompoundShapeURL = getCompoundShapeURL();
|
||||
ModelEntityItem::setCompoundShapeURL(url);
|
||||
|
||||
|
@ -629,7 +642,7 @@ void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) {
|
|||
QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID()));
|
||||
}
|
||||
if (getShapeType() == SHAPE_TYPE_COMPOUND) {
|
||||
_compoundShapeResource = DependencyManager::get<ModelCache>()->getGeometryResource(url);
|
||||
getCollisionGeometryResource();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -661,7 +674,7 @@ bool RenderableModelEntityItem::isReadyToComputeShape() {
|
|||
}
|
||||
return true;
|
||||
} else if (!getCompoundShapeURL().isEmpty()) {
|
||||
_compoundShapeResource = DependencyManager::get<ModelCache>()->getGeometryResource(getCompoundShapeURL());
|
||||
getCollisionGeometryResource();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -108,6 +108,7 @@ private:
|
|||
QVariantMap parseTexturesToMap(QString textures);
|
||||
void remapTextures();
|
||||
|
||||
void getCollisionGeometryResource();
|
||||
GeometryResource::Pointer _compoundShapeResource;
|
||||
ModelPointer _model = nullptr;
|
||||
bool _needsInitialSimulation = true;
|
||||
|
|
|
@ -126,7 +126,7 @@ public:
|
|||
void markAsChangedOnServer();
|
||||
quint64 getLastChangedOnServer() const;
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
|
||||
|
||||
virtual OctreeElement::AppendState appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
|
|
|
@ -51,7 +51,10 @@ void EntityTreeElement::debugExtraEncodeData(EncodeBitstreamParams& params) cons
|
|||
qCDebug(entities) << "EntityTreeElement::debugExtraEncodeData()... ";
|
||||
qCDebug(entities) << " element:" << _cube;
|
||||
|
||||
OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
|
||||
auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
|
||||
assert(entityNodeData);
|
||||
|
||||
OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
|
||||
assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
|
||||
|
||||
if (extraEncodeData->contains(this)) {
|
||||
|
@ -65,7 +68,11 @@ void EntityTreeElement::debugExtraEncodeData(EncodeBitstreamParams& params) cons
|
|||
|
||||
void EntityTreeElement::initializeExtraEncodeData(EncodeBitstreamParams& params) {
|
||||
|
||||
OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
|
||||
auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
|
||||
assert(entityNodeData);
|
||||
|
||||
OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
|
||||
|
||||
assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
|
||||
// Check to see if this element yet has encode data... if it doesn't create it
|
||||
if (!extraEncodeData->contains(this)) {
|
||||
|
@ -93,7 +100,11 @@ void EntityTreeElement::initializeExtraEncodeData(EncodeBitstreamParams& params)
|
|||
}
|
||||
|
||||
bool EntityTreeElement::shouldIncludeChildData(int childIndex, EncodeBitstreamParams& params) const {
|
||||
OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
|
||||
|
||||
auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
|
||||
assert(entityNodeData);
|
||||
|
||||
OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
|
||||
assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
|
||||
|
||||
if (extraEncodeData->contains(this)) {
|
||||
|
@ -122,7 +133,10 @@ bool EntityTreeElement::shouldRecurseChildTree(int childIndex, EncodeBitstreamPa
|
|||
}
|
||||
|
||||
bool EntityTreeElement::alreadyFullyEncoded(EncodeBitstreamParams& params) const {
|
||||
OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
|
||||
auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
|
||||
assert(entityNodeData);
|
||||
|
||||
OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
|
||||
assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
|
||||
|
||||
if (extraEncodeData->contains(this)) {
|
||||
|
@ -137,8 +151,12 @@ bool EntityTreeElement::alreadyFullyEncoded(EncodeBitstreamParams& params) const
|
|||
}
|
||||
|
||||
void EntityTreeElement::updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const {
|
||||
OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
|
||||
auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
|
||||
assert(entityNodeData);
|
||||
|
||||
OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
|
||||
assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
|
||||
|
||||
if (extraEncodeData->contains(this)) {
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData
|
||||
= std::static_pointer_cast<EntityTreeElementExtraEncodeData>((*extraEncodeData)[this]);
|
||||
|
@ -161,7 +179,10 @@ void EntityTreeElement::elementEncodeComplete(EncodeBitstreamParams& params) con
|
|||
qCDebug(entities) << "EntityTreeElement::elementEncodeComplete() element:" << _cube;
|
||||
}
|
||||
|
||||
OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
|
||||
auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
|
||||
assert(entityNodeData);
|
||||
|
||||
OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
|
||||
assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
|
||||
assert(extraEncodeData->contains(this));
|
||||
|
||||
|
@ -236,8 +257,12 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
|
|||
|
||||
OctreeElement::AppendState appendElementState = OctreeElement::COMPLETED; // assume the best...
|
||||
|
||||
auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
|
||||
Q_ASSERT_X(entityNodeData, "EntityTreeElement::appendElementData", "expected params.nodeData not to be null");
|
||||
|
||||
// first, check the params.extraEncodeData to see if there's any partial re-encode data for this element
|
||||
OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
|
||||
OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
|
||||
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData = NULL;
|
||||
bool hadElementExtraData = false;
|
||||
if (extraEncodeData && extraEncodeData->contains(this)) {
|
||||
|
@ -285,20 +310,17 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
|
|||
// need to handle the case where our sibling elements need encoding but we don't.
|
||||
if (!entityTreeElementExtraEncodeData->elementCompleted) {
|
||||
|
||||
QJsonObject jsonFilters;
|
||||
auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
|
||||
|
||||
if (entityNodeData) {
|
||||
// we have an EntityNodeData instance
|
||||
// so we should assume that means we might have JSON filters to check
|
||||
jsonFilters = entityNodeData->getJSONParameters();
|
||||
}
|
||||
// we have an EntityNodeData instance
|
||||
// so we should assume that means we might have JSON filters to check
|
||||
auto jsonFilters = entityNodeData->getJSONParameters();
|
||||
|
||||
|
||||
for (uint16_t i = 0; i < _entityItems.size(); i++) {
|
||||
EntityItemPointer entity = _entityItems[i];
|
||||
bool includeThisEntity = true;
|
||||
|
||||
if (!params.forceSendScene && entity->getLastChangedOnServer() < params.lastQuerySent) {
|
||||
if (!params.forceSendScene && entity->getLastChangedOnServer() < entityNodeData->getLastTimeBagEmpty()) {
|
||||
includeThisEntity = false;
|
||||
}
|
||||
|
||||
|
@ -330,7 +352,7 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
|
|||
|
||||
// we only check the bounds against our frustum and LOD if the query has asked us to check against the frustum
|
||||
// which can sometimes not be the case when JSON filters are sent
|
||||
if (params.usesFrustum && (includeThisEntity || params.recurseEverything)) {
|
||||
if (entityNodeData->getUsesFrustum() && (includeThisEntity || params.recurseEverything)) {
|
||||
|
||||
// we want to use the maximum possible box for this, so that we don't have to worry about the nuance of
|
||||
// simulation changing what's visible. consider the case where the entity contains an angular velocity
|
||||
|
|
|
@ -195,7 +195,7 @@ int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
}
|
||||
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
EntityPropertyFlags LightEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
requestedProperties += PROP_IS_SPOTLIGHT;
|
||||
|
|
|
@ -131,7 +131,7 @@ int LineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
}
|
||||
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
EntityPropertyFlags LineEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
requestedProperties += PROP_COLOR;
|
||||
|
|
|
@ -26,7 +26,7 @@ class LineEntityItem : public EntityItem {
|
|||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
|
|
|
@ -160,7 +160,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
return bytesRead;
|
||||
}
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
|
|
|
@ -469,7 +469,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
|
|||
}
|
||||
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
|
||||
|
|
|
@ -178,7 +178,7 @@ int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* da
|
|||
}
|
||||
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
EntityPropertyFlags PolyLineEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
requestedProperties += PROP_COLOR;
|
||||
|
|
|
@ -26,7 +26,7 @@ class PolyLineEntityItem : public EntityItem {
|
|||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
|
|
|
@ -179,7 +179,7 @@ int PolyVoxEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* dat
|
|||
}
|
||||
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
EntityPropertyFlags PolyVoxEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
requestedProperties += PROP_VOXEL_VOLUME_SIZE;
|
||||
|
|
|
@ -26,7 +26,7 @@ class PolyVoxEntityItem : public EntityItem {
|
|||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
|
|
|
@ -137,7 +137,7 @@ int ShapeEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
}
|
||||
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
EntityPropertyFlags ShapeEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
requestedProperties += PROP_SHAPE;
|
||||
|
|
|
@ -99,7 +99,7 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
}
|
||||
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
requestedProperties += PROP_TEXT;
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
|
|
|
@ -84,7 +84,7 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i
|
|||
}
|
||||
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
requestedProperties += PROP_SOURCE_URL;
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
|
|
|
@ -137,7 +137,7 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
}
|
||||
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastQuerySent time
|
||||
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
|
||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
|
|
|
@ -376,10 +376,10 @@ public:
|
|||
};
|
||||
|
||||
bool checkMaterialsHaveTextures(const QHash<QString, FBXMaterial>& materials,
|
||||
const QHash<QString, QByteArray>& textureFilepaths, const QMultiMap<QString, QString>& _connectionChildMap) {
|
||||
const QHash<QString, QByteArray>& textureFilenames, const QMultiMap<QString, QString>& _connectionChildMap) {
|
||||
foreach (const QString& materialID, materials.keys()) {
|
||||
foreach (const QString& childID, _connectionChildMap.values(materialID)) {
|
||||
if (textureFilepaths.contains(childID)) {
|
||||
if (textureFilenames.contains(childID)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -443,48 +443,21 @@ FBXLight extractLight(const FBXNode& object) {
|
|||
return light;
|
||||
}
|
||||
|
||||
QByteArray fixedTextureFilepath(QByteArray fbxRelativeFilepath, QUrl url) {
|
||||
// first setup a QFileInfo for the passed relative filepath, with backslashes replaced by forward slashes
|
||||
auto fileInfo = QFileInfo { fbxRelativeFilepath.replace("\\", "/") };
|
||||
QByteArray fileOnUrl(const QByteArray& filepath, const QString& url) {
|
||||
QString path = QFileInfo(url).path();
|
||||
QByteArray filename = filepath;
|
||||
QFileInfo checkFile(path + "/" + filepath);
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
// it turns out that absolute windows paths starting with drive letters look like relative paths to QFileInfo on UNIX
|
||||
// so we add a check for that here to work around it
|
||||
bool isRelative = fbxRelativeFilepath[1] != ':' && fileInfo.isRelative();
|
||||
#else
|
||||
bool isRelative = fileInfo.isRelative();
|
||||
#endif
|
||||
|
||||
if (isRelative) {
|
||||
// the RelativeFilename pulled from the FBX is already correctly relative
|
||||
// so simply return this as the filepath to use
|
||||
return fbxRelativeFilepath;
|
||||
} else {
|
||||
// the RelativeFilename pulled from the FBX is an absolute path
|
||||
|
||||
// use the URL to figure out where the FBX is being loaded from
|
||||
auto filename = fileInfo.fileName();
|
||||
|
||||
if (url.isLocalFile()) {
|
||||
// the FBX is being loaded from the local filesystem
|
||||
|
||||
if (fileInfo.exists() && fileInfo.isFile()) {
|
||||
// found a file at the absolute path in the FBX, return that path
|
||||
return fbxRelativeFilepath;
|
||||
} else {
|
||||
// didn't find a file at the absolute path, assume it is right beside the FBX
|
||||
// return just the filename as the relative path
|
||||
return filename.toUtf8();
|
||||
}
|
||||
} else {
|
||||
// this is a remote file, meaning we can't really do anything with the absolute path to the texture
|
||||
// so assume it will be right beside the fbx
|
||||
return filename.toUtf8();
|
||||
}
|
||||
// check if the file exists at the RelativeFilename
|
||||
if (!(checkFile.exists() && checkFile.isFile())) {
|
||||
// if not, assume it is in the fbx directory
|
||||
filename = filename.mid(filename.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QUrl& url) {
|
||||
FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QString& url) {
|
||||
const FBXNode& node = _fbxNode;
|
||||
QMap<QString, ExtractedMesh> meshes;
|
||||
QHash<QString, QString> modelIDsToNames;
|
||||
|
@ -860,9 +833,11 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QU
|
|||
const int MODEL_UV_SCALING_MIN_SIZE = 2;
|
||||
const int CROPPING_MIN_SIZE = 4;
|
||||
if (subobject.name == "RelativeFilename" && subobject.properties.length() >= RELATIVE_FILENAME_MIN_SIZE) {
|
||||
auto filepath = fixedTextureFilepath(subobject.properties.at(0).toByteArray(), url);
|
||||
|
||||
QByteArray filename = subobject.properties.at(0).toByteArray();
|
||||
QByteArray filepath = filename.replace('\\', '/');
|
||||
filename = fileOnUrl(filepath, url);
|
||||
_textureFilepaths.insert(getID(object.properties), filepath);
|
||||
_textureFilenames.insert(getID(object.properties), filename);
|
||||
} else if (subobject.name == "TextureName" && subobject.properties.length() >= TEXTURE_NAME_MIN_SIZE) {
|
||||
// trim the name from the timestamp
|
||||
QString name = QString(subobject.properties.at(0).toByteArray());
|
||||
|
@ -955,7 +930,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QU
|
|||
QByteArray content;
|
||||
foreach (const FBXNode& subobject, object.children) {
|
||||
if (subobject.name == "RelativeFilename") {
|
||||
filepath = subobject.properties.at(0).toByteArray();
|
||||
filepath= subobject.properties.at(0).toByteArray();
|
||||
filepath = filepath.replace('\\', '/');
|
||||
|
||||
} else if (subobject.name == "Content" && !subobject.properties.isEmpty()) {
|
||||
|
@ -1527,7 +1502,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QU
|
|||
geometry.materials = _fbxMaterials;
|
||||
|
||||
// see if any materials have texture children
|
||||
bool materialsHaveTextures = checkMaterialsHaveTextures(_fbxMaterials, _textureFilepaths, _connectionChildMap);
|
||||
bool materialsHaveTextures = checkMaterialsHaveTextures(_fbxMaterials, _textureFilenames, _connectionChildMap);
|
||||
|
||||
for (QMap<QString, ExtractedMesh>::iterator it = meshes.begin(); it != meshes.end(); it++) {
|
||||
ExtractedMesh& extracted = it.value();
|
||||
|
@ -1572,7 +1547,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QU
|
|||
|
||||
materialIndex++;
|
||||
|
||||
} else if (_textureFilepaths.contains(childID)) {
|
||||
} else if (_textureFilenames.contains(childID)) {
|
||||
FBXTexture texture = getTexture(childID);
|
||||
for (int j = 0; j < extracted.partMaterialTextures.size(); j++) {
|
||||
int partTexture = extracted.partMaterialTextures.at(j).second;
|
||||
|
@ -1843,13 +1818,13 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QU
|
|||
return geometryPtr;
|
||||
}
|
||||
|
||||
FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QUrl& url, bool loadLightmaps, float lightmapLevel) {
|
||||
FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) {
|
||||
QBuffer buffer(const_cast<QByteArray*>(&model));
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
return readFBX(&buffer, mapping, url, loadLightmaps, lightmapLevel);
|
||||
}
|
||||
|
||||
FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QUrl& url, bool loadLightmaps, float lightmapLevel) {
|
||||
FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) {
|
||||
FBXReader reader;
|
||||
reader._fbxNode = FBXReader::parseFBX(device);
|
||||
reader._loadLightmaps = loadLightmaps;
|
||||
|
|
|
@ -268,7 +268,7 @@ class FBXGeometry {
|
|||
public:
|
||||
using Pointer = std::shared_ptr<FBXGeometry>;
|
||||
|
||||
QUrl originalURL;
|
||||
QString originalURL;
|
||||
QString author;
|
||||
QString applicationName; ///< the name of the application that generated the model
|
||||
|
||||
|
@ -330,11 +330,11 @@ Q_DECLARE_METATYPE(FBXGeometry::Pointer)
|
|||
|
||||
/// Reads FBX geometry from the supplied model and mapping data.
|
||||
/// \exception QString if an error occurs in parsing
|
||||
FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QUrl& url = QUrl(), bool loadLightmaps = true, float lightmapLevel = 1.0f);
|
||||
FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f);
|
||||
|
||||
/// Reads FBX geometry from the supplied model and mapping data.
|
||||
/// \exception QString if an error occurs in parsing
|
||||
FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QUrl& url = QUrl(), bool loadLightmaps = true, float lightmapLevel = 1.0f);
|
||||
FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f);
|
||||
|
||||
class TextureParam {
|
||||
public:
|
||||
|
@ -402,17 +402,19 @@ public:
|
|||
FBXNode _fbxNode;
|
||||
static FBXNode parseFBX(QIODevice* device);
|
||||
|
||||
FBXGeometry* extractFBXGeometry(const QVariantHash& mapping, const QUrl& url);
|
||||
FBXGeometry* extractFBXGeometry(const QVariantHash& mapping, const QString& url);
|
||||
|
||||
ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex);
|
||||
QHash<QString, ExtractedMesh> meshes;
|
||||
static void buildModelMesh(FBXMesh& extractedMesh, const QUrl& url);
|
||||
static void buildModelMesh(FBXMesh& extractedMesh, const QString& url);
|
||||
|
||||
FBXTexture getTexture(const QString& textureID);
|
||||
|
||||
QHash<QString, QString> _textureNames;
|
||||
// Hashes the original RelativeFilename of textures
|
||||
QHash<QString, QByteArray> _textureFilepaths;
|
||||
// Hashes the place to look for textures, in case they are not inlined
|
||||
QHash<QString, QByteArray> _textureFilenames;
|
||||
// Hashes texture content by filepath, in case they are inlined
|
||||
QHash<QByteArray, QByteArray> _textureContent;
|
||||
QHash<QString, TextureParam> _textureParams;
|
||||
|
|
|
@ -85,7 +85,12 @@ FBXTexture FBXReader::getTexture(const QString& textureID) {
|
|||
FBXTexture texture;
|
||||
const QByteArray& filepath = _textureFilepaths.value(textureID);
|
||||
texture.content = _textureContent.value(filepath);
|
||||
texture.filename = filepath;
|
||||
|
||||
if (texture.content.isEmpty()) { // the content is not inlined
|
||||
texture.filename = _textureFilenames.value(textureID);
|
||||
} else { // use supplied filepath for inlined content
|
||||
texture.filename = filepath;
|
||||
}
|
||||
|
||||
texture.name = _textureNames.value(textureID);
|
||||
texture.transform.setIdentity();
|
||||
|
@ -150,7 +155,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) {
|
|||
|
||||
// FBX files generated by 3DSMax have an intermediate texture parent, apparently
|
||||
foreach(const QString& childTextureID, _connectionChildMap.values(diffuseTextureID)) {
|
||||
if (_textureFilepaths.contains(childTextureID)) {
|
||||
if (_textureFilenames.contains(childTextureID)) {
|
||||
diffuseTexture = getTexture(diffuseTextureID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -388,7 +388,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
|
|||
return data.extracted;
|
||||
}
|
||||
|
||||
void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QUrl& url) {
|
||||
void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
|
||||
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("buildModelMesh failed -- .*");
|
||||
|
||||
unsigned int totalSourceIndices = 0;
|
||||
|
|
|
@ -302,7 +302,8 @@ QNetworkReply* OBJReader::request(QUrl& url, bool isTest) {
|
|||
}
|
||||
|
||||
|
||||
bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess) {
|
||||
bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry,
|
||||
float& scaleGuess, bool combineParts) {
|
||||
FaceGroup faces;
|
||||
FBXMesh& mesh = geometry.meshes[0];
|
||||
mesh.parts.append(FBXMeshPart());
|
||||
|
@ -348,6 +349,9 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
|
|||
}
|
||||
QByteArray groupName = tokenizer.getDatum();
|
||||
currentGroup = groupName;
|
||||
if (!combineParts) {
|
||||
currentMaterialName = QString("part-") + QString::number(_partCounter++);
|
||||
}
|
||||
} else if (token == "mtllib" && !_url.isEmpty()) {
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
|
||||
break;
|
||||
|
@ -361,7 +365,9 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
|
|||
}
|
||||
QString nextName = tokenizer.getDatum();
|
||||
if (nextName != currentMaterialName) {
|
||||
currentMaterialName = nextName;
|
||||
if (combineParts) {
|
||||
currentMaterialName = nextName;
|
||||
}
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(modelformat) << "OBJ Reader new current material:" << currentMaterialName;
|
||||
#endif
|
||||
|
@ -419,7 +425,7 @@ done:
|
|||
}
|
||||
|
||||
|
||||
FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, const QUrl& url) {
|
||||
FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url) {
|
||||
PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xffff0000, nullptr);
|
||||
QBuffer buffer { &model };
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
|
@ -438,7 +444,7 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping,
|
|||
try {
|
||||
// call parseOBJGroup as long as it's returning true. Each successful call will
|
||||
// add a new meshPart to the geometry's single mesh.
|
||||
while (parseOBJGroup(tokenizer, mapping, geometry, scaleGuess)) {}
|
||||
while (parseOBJGroup(tokenizer, mapping, geometry, scaleGuess, combineParts)) {}
|
||||
|
||||
FBXMesh& mesh = geometry.meshes[0];
|
||||
mesh.meshIndex = 0;
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
glm::vec3 getVec3();
|
||||
glm::vec2 getVec2();
|
||||
float getFloat() { return std::stof((nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : getDatum().data()); }
|
||||
|
||||
|
||||
private:
|
||||
QIODevice* _device;
|
||||
QByteArray _datum;
|
||||
|
@ -73,15 +73,18 @@ public:
|
|||
QHash<QString, OBJMaterial> materials;
|
||||
|
||||
QNetworkReply* request(QUrl& url, bool isTest);
|
||||
FBXGeometry* readOBJ(QByteArray& model, const QVariantHash& mapping, const QUrl& url = QUrl());
|
||||
|
||||
FBXGeometry* readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl());
|
||||
|
||||
private:
|
||||
QUrl _url;
|
||||
|
||||
QHash<QByteArray, bool> librariesSeen;
|
||||
bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess);
|
||||
bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry,
|
||||
float& scaleGuess, bool combineParts);
|
||||
void parseMaterialLibrary(QIODevice* device);
|
||||
bool isValidTexture(const QByteArray &filename); // true if the file exists. TODO?: check content-type header and that it is a supported format.
|
||||
|
||||
int _partCounter { 0 };
|
||||
};
|
||||
|
||||
// What are these utilities doing here? One is used by fbx loading code in VHACD Utils, and the other a general debugging utility.
|
||||
|
|
|
@ -32,6 +32,7 @@ class GeometryExtra {
|
|||
public:
|
||||
const QVariantHash& mapping;
|
||||
const QUrl& textureBaseUrl;
|
||||
bool combineParts;
|
||||
};
|
||||
|
||||
QUrl resolveTextureBaseUrl(const QUrl& url, const QUrl& textureBaseUrl) {
|
||||
|
@ -89,7 +90,7 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
|
|||
}
|
||||
|
||||
auto modelCache = DependencyManager::get<ModelCache>();
|
||||
GeometryExtra extra{ mapping, _textureBaseUrl };
|
||||
GeometryExtra extra{ mapping, _textureBaseUrl, false };
|
||||
|
||||
// Get the raw GeometryResource
|
||||
_geometryResource = modelCache->getResource(url, QUrl(), &extra).staticCast<GeometryResource>();
|
||||
|
@ -129,8 +130,8 @@ void GeometryMappingResource::onGeometryMappingLoaded(bool success) {
|
|||
class GeometryReader : public QRunnable {
|
||||
public:
|
||||
GeometryReader(QWeakPointer<Resource>& resource, const QUrl& url, const QVariantHash& mapping,
|
||||
const QByteArray& data) :
|
||||
_resource(resource), _url(url), _mapping(mapping), _data(data) {
|
||||
const QByteArray& data, bool combineParts) :
|
||||
_resource(resource), _url(url), _mapping(mapping), _data(data), _combineParts(combineParts) {
|
||||
|
||||
DependencyManager::get<StatTracker>()->incrementStat("PendingProcessing");
|
||||
}
|
||||
|
@ -142,6 +143,7 @@ private:
|
|||
QUrl _url;
|
||||
QVariantHash _mapping;
|
||||
QByteArray _data;
|
||||
bool _combineParts;
|
||||
};
|
||||
|
||||
void GeometryReader::run() {
|
||||
|
@ -173,12 +175,12 @@ void GeometryReader::run() {
|
|||
FBXGeometry::Pointer fbxGeometry;
|
||||
|
||||
if (_url.path().toLower().endsWith(".fbx")) {
|
||||
fbxGeometry.reset(readFBX(_data, _mapping, _url));
|
||||
fbxGeometry.reset(readFBX(_data, _mapping, _url.path()));
|
||||
if (fbxGeometry->meshes.size() == 0 && fbxGeometry->joints.size() == 0) {
|
||||
throw QString("empty geometry, possibly due to an unsupported FBX version");
|
||||
}
|
||||
} else if (_url.path().toLower().endsWith(".obj")) {
|
||||
fbxGeometry.reset(OBJReader().readOBJ(_data, _mapping, _url));
|
||||
fbxGeometry.reset(OBJReader().readOBJ(_data, _mapping, _combineParts, _url));
|
||||
} else {
|
||||
throw QString("unsupported format");
|
||||
}
|
||||
|
@ -209,8 +211,8 @@ void GeometryReader::run() {
|
|||
class GeometryDefinitionResource : public GeometryResource {
|
||||
Q_OBJECT
|
||||
public:
|
||||
GeometryDefinitionResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) :
|
||||
GeometryResource(url, resolveTextureBaseUrl(url, textureBaseUrl)), _mapping(mapping) {}
|
||||
GeometryDefinitionResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl, bool combineParts) :
|
||||
GeometryResource(url, resolveTextureBaseUrl(url, textureBaseUrl)), _mapping(mapping), _combineParts(combineParts) {}
|
||||
|
||||
QString getType() const override { return "GeometryDefinition"; }
|
||||
|
||||
|
@ -221,10 +223,11 @@ protected:
|
|||
|
||||
private:
|
||||
QVariantHash _mapping;
|
||||
bool _combineParts;
|
||||
};
|
||||
|
||||
void GeometryDefinitionResource::downloadFinished(const QByteArray& data) {
|
||||
QThreadPool::globalInstance()->start(new GeometryReader(_self, _url, _mapping, data));
|
||||
QThreadPool::globalInstance()->start(new GeometryReader(_self, _url, _mapping, data, _combineParts));
|
||||
}
|
||||
|
||||
void GeometryDefinitionResource::setGeometryDefinition(FBXGeometry::Pointer fbxGeometry) {
|
||||
|
@ -265,7 +268,7 @@ ModelCache::ModelCache() {
|
|||
}
|
||||
|
||||
QSharedPointer<Resource> ModelCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
|
||||
const void* extra) {
|
||||
const void* extra) {
|
||||
Resource* resource = nullptr;
|
||||
if (url.path().toLower().endsWith(".fst")) {
|
||||
resource = new GeometryMappingResource(url);
|
||||
|
@ -273,14 +276,30 @@ QSharedPointer<Resource> ModelCache::createResource(const QUrl& url, const QShar
|
|||
const GeometryExtra* geometryExtra = static_cast<const GeometryExtra*>(extra);
|
||||
auto mapping = geometryExtra ? geometryExtra->mapping : QVariantHash();
|
||||
auto textureBaseUrl = geometryExtra ? geometryExtra->textureBaseUrl : QUrl();
|
||||
resource = new GeometryDefinitionResource(url, mapping, textureBaseUrl);
|
||||
bool combineParts = geometryExtra ? geometryExtra->combineParts : true;
|
||||
resource = new GeometryDefinitionResource(url, mapping, textureBaseUrl, combineParts);
|
||||
}
|
||||
|
||||
return QSharedPointer<Resource>(resource, &Resource::deleter);
|
||||
}
|
||||
|
||||
GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) {
|
||||
GeometryExtra geometryExtra = { mapping, textureBaseUrl };
|
||||
GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url,
|
||||
const QVariantHash& mapping, const QUrl& textureBaseUrl) {
|
||||
bool combineParts = true;
|
||||
GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts };
|
||||
GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra).staticCast<GeometryResource>();
|
||||
if (resource) {
|
||||
if (resource->isLoaded() && resource->shouldSetTextures()) {
|
||||
resource->setTextures();
|
||||
}
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
GeometryResource::Pointer ModelCache::getCollisionGeometryResource(const QUrl& url,
|
||||
const QVariantHash& mapping, const QUrl& textureBaseUrl) {
|
||||
bool combineParts = false;
|
||||
GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts };
|
||||
GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra).staticCast<GeometryResource>();
|
||||
if (resource) {
|
||||
if (resource->isLoaded() && resource->shouldSetTextures()) {
|
||||
|
|
|
@ -136,13 +136,18 @@ class ModelCache : public ResourceCache, public Dependency {
|
|||
|
||||
public:
|
||||
GeometryResource::Pointer getGeometryResource(const QUrl& url,
|
||||
const QVariantHash& mapping = QVariantHash(), const QUrl& textureBaseUrl = QUrl());
|
||||
const QVariantHash& mapping = QVariantHash(),
|
||||
const QUrl& textureBaseUrl = QUrl());
|
||||
|
||||
GeometryResource::Pointer getCollisionGeometryResource(const QUrl& url,
|
||||
const QVariantHash& mapping = QVariantHash(),
|
||||
const QUrl& textureBaseUrl = QUrl());
|
||||
|
||||
protected:
|
||||
friend class GeometryMappingResource;
|
||||
|
||||
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
|
||||
const void* extra) override;
|
||||
const void* extra) override;
|
||||
|
||||
private:
|
||||
ModelCache();
|
||||
|
|
|
@ -191,7 +191,7 @@ ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra) {
|
|||
result->setObjectName(url.toString());
|
||||
|
||||
result->_resource = resource;
|
||||
if (resource->isLoaded()) {
|
||||
if (resource->isLoaded() || resource->_failedToLoad) {
|
||||
result->finished(!resource->_failedToLoad);
|
||||
} else {
|
||||
result->_progressConnection = connect(
|
||||
|
|
|
@ -295,8 +295,8 @@ protected:
|
|||
|
||||
/// Creates a new resource.
|
||||
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
|
||||
const void* extra) = 0;
|
||||
|
||||
const void* extra) = 0;
|
||||
|
||||
void addUnusedResource(const QSharedPointer<Resource>& resource);
|
||||
void removeUnusedResource(const QSharedPointer<Resource>& resource);
|
||||
|
||||
|
|
|
@ -43,14 +43,15 @@
|
|||
#include <PathUtils.h>
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
#include "Octree.h"
|
||||
#include "OctreeConstants.h"
|
||||
#include "OctreeElementBag.h"
|
||||
#include "Octree.h"
|
||||
#include "OctreeUtils.h"
|
||||
#include "OctreeLogging.h"
|
||||
#include "OctreeQueryNode.h"
|
||||
#include "OctreeUtils.h"
|
||||
|
||||
|
||||
QVector<QString> PERSIST_EXTENSIONS = {"svo", "json", "json.gz"};
|
||||
QVector<QString> PERSIST_EXTENSIONS = {"json", "json.gz"};
|
||||
|
||||
Octree::Octree(bool shouldReaverage) :
|
||||
_rootElement(NULL),
|
||||
|
@ -898,8 +899,16 @@ int Octree::encodeTreeBitstream(OctreeElementPointer element,
|
|||
return bytesWritten;
|
||||
}
|
||||
|
||||
// you can't call this without a valid nodeData
|
||||
auto octreeQueryNode = static_cast<OctreeQueryNode*>(params.nodeData);
|
||||
if (!octreeQueryNode) {
|
||||
qCDebug(octree, "WARNING! encodeTreeBitstream() called with nodeData=NULL");
|
||||
params.stopReason = EncodeBitstreamParams::NULL_NODE_DATA;
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
// If we're at a element that is out of view, then we can return, because no nodes below us will be in view!
|
||||
if (params.usesFrustum && !params.recurseEverything && !element->isInView(params.viewFrustum)) {
|
||||
if (octreeQueryNode->getUsesFrustum() && !params.recurseEverything && !element->isInView(params.viewFrustum)) {
|
||||
params.stopReason = EncodeBitstreamParams::OUT_OF_VIEW;
|
||||
return bytesWritten;
|
||||
}
|
||||
|
@ -935,9 +944,7 @@ int Octree::encodeTreeBitstream(OctreeElementPointer element,
|
|||
|
||||
// record some stats, this is the one element that we won't record below in the recursion function, so we need to
|
||||
// track it here
|
||||
if (params.stats) {
|
||||
params.stats->traversed(element);
|
||||
}
|
||||
octreeQueryNode->stats.traversed(element);
|
||||
|
||||
ViewFrustum::intersection parentLocationThisView = ViewFrustum::INTERSECT; // assume parent is in view, but not fully
|
||||
|
||||
|
@ -993,6 +1000,15 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
return bytesAtThisLevel;
|
||||
}
|
||||
|
||||
// you can't call this without a valid nodeData
|
||||
auto octreeQueryNode = static_cast<OctreeQueryNode*>(params.nodeData);
|
||||
if (!octreeQueryNode) {
|
||||
qCDebug(octree, "WARNING! encodeTreeBitstream() called with nodeData=NULL");
|
||||
params.stopReason = EncodeBitstreamParams::NULL_NODE_DATA;
|
||||
return bytesAtThisLevel;
|
||||
}
|
||||
|
||||
|
||||
// Keep track of how deep we've encoded.
|
||||
currentEncodeLevel++;
|
||||
|
||||
|
@ -1015,15 +1031,13 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
}
|
||||
|
||||
ViewFrustum::intersection nodeLocationThisView = ViewFrustum::INSIDE; // assume we're inside
|
||||
if (params.usesFrustum && !params.recurseEverything) {
|
||||
if (octreeQueryNode->getUsesFrustum() && !params.recurseEverything) {
|
||||
float boundaryDistance = boundaryDistanceForRenderLevel(element->getLevel() + params.boundaryLevelAdjust,
|
||||
params.octreeElementSizeScale);
|
||||
|
||||
// If we're too far away for our render level, then just return
|
||||
if (element->distanceToCamera(params.viewFrustum) >= boundaryDistance) {
|
||||
if (params.stats) {
|
||||
params.stats->skippedDistance(element);
|
||||
}
|
||||
octreeQueryNode->stats.skippedDistance(element);
|
||||
params.stopReason = EncodeBitstreamParams::LOD_SKIP;
|
||||
return bytesAtThisLevel;
|
||||
}
|
||||
|
@ -1039,9 +1053,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
// although technically, we really shouldn't ever be here, because our callers shouldn't be calling us if
|
||||
// we're out of view
|
||||
if (nodeLocationThisView == ViewFrustum::OUTSIDE) {
|
||||
if (params.stats) {
|
||||
params.stats->skippedOutOfView(element);
|
||||
}
|
||||
octreeQueryNode->stats.skippedOutOfView(element);
|
||||
params.stopReason = EncodeBitstreamParams::OUT_OF_VIEW;
|
||||
return bytesAtThisLevel;
|
||||
}
|
||||
|
@ -1066,7 +1078,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
// as "was in view"...
|
||||
if (wasInView) {
|
||||
float boundaryDistance = boundaryDistanceForRenderLevel(element->getLevel() + params.boundaryLevelAdjust,
|
||||
params.octreeElementSizeScale);
|
||||
params.octreeElementSizeScale);
|
||||
if (element->distanceToCamera(params.lastViewFrustum) >= boundaryDistance) {
|
||||
// This would have been invisible... but now should be visible (we wouldn't be here otherwise)...
|
||||
wasInView = false;
|
||||
|
@ -1077,22 +1089,20 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
// If we were previously in the view, then we normally will return out of here and stop recursing. But
|
||||
// if we're in deltaView mode, and this element has changed since it was last sent, then we do
|
||||
// need to send it.
|
||||
if (wasInView && !(params.deltaView && element->hasChangedSince(params.lastQuerySent - CHANGE_FUDGE))) {
|
||||
if (params.stats) {
|
||||
params.stats->skippedWasInView(element);
|
||||
}
|
||||
if (wasInView && !(params.deltaView && element->hasChangedSince(octreeQueryNode->getLastTimeBagEmpty() - CHANGE_FUDGE))) {
|
||||
octreeQueryNode->stats.skippedWasInView(element);
|
||||
params.stopReason = EncodeBitstreamParams::WAS_IN_VIEW;
|
||||
return bytesAtThisLevel;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're not in delta sending mode, and we weren't asked to do a force send, and the octree element hasn't changed,
|
||||
// If we're not in delta sending mode, and we weren't asked to do a force send, and the voxel hasn't changed,
|
||||
// then we can also bail early and save bits
|
||||
if (!params.forceSendScene && !params.deltaView &&
|
||||
!element->hasChangedSince(params.lastQuerySent - CHANGE_FUDGE)) {
|
||||
if (params.stats) {
|
||||
params.stats->skippedNoChange(element);
|
||||
}
|
||||
!element->hasChangedSince(octreeQueryNode->getLastTimeBagEmpty() - CHANGE_FUDGE)) {
|
||||
|
||||
octreeQueryNode->stats.skippedNoChange(element);
|
||||
|
||||
params.stopReason = EncodeBitstreamParams::NO_CHANGE;
|
||||
return bytesAtThisLevel;
|
||||
}
|
||||
|
@ -1164,8 +1174,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
|
||||
// track stats
|
||||
// must check childElement here, because it could be we got here with no childElement
|
||||
if (params.stats && childElement) {
|
||||
params.stats->traversed(childElement);
|
||||
if (childElement) {
|
||||
octreeQueryNode->stats.traversed(childElement);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1176,7 +1186,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
int originalIndex = indexOfChildren[i];
|
||||
|
||||
bool childIsInView = (childElement &&
|
||||
(params.recurseEverything || !params.usesFrustum ||
|
||||
(params.recurseEverything || !octreeQueryNode->getUsesFrustum() ||
|
||||
(nodeLocationThisView == ViewFrustum::INSIDE) || // parent was fully in view, we can assume ALL children are
|
||||
(nodeLocationThisView == ViewFrustum::INTERSECT &&
|
||||
childElement->isInView(params.viewFrustum)) // the parent intersects and the child is in view
|
||||
|
@ -1184,20 +1194,18 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
|
||||
if (!childIsInView) {
|
||||
// must check childElement here, because it could be we got here because there was no childElement
|
||||
if (params.stats && childElement) {
|
||||
params.stats->skippedOutOfView(childElement);
|
||||
if (childElement) {
|
||||
octreeQueryNode->stats.skippedOutOfView(childElement);
|
||||
}
|
||||
} else {
|
||||
// Before we consider this further, let's see if it's in our LOD scope...
|
||||
float boundaryDistance = params.recurseEverything || !params.usesFrustum ? 1 :
|
||||
float boundaryDistance = params.recurseEverything || !octreeQueryNode->getUsesFrustum() ? 1 :
|
||||
boundaryDistanceForRenderLevel(childElement->getLevel() + params.boundaryLevelAdjust,
|
||||
params.octreeElementSizeScale);
|
||||
|
||||
if (!(distancesToChildren[i] < boundaryDistance)) {
|
||||
// don't need to check childElement here, because we can't get here with no childElement
|
||||
if (params.stats) {
|
||||
params.stats->skippedDistance(childElement);
|
||||
}
|
||||
octreeQueryNode->stats.skippedDistance(childElement);
|
||||
} else {
|
||||
inViewCount++;
|
||||
|
||||
|
@ -1211,20 +1219,18 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
|
||||
bool childIsOccluded = false; // assume it's not occluded
|
||||
|
||||
bool shouldRender = params.recurseEverything || !params.usesFrustum ||
|
||||
bool shouldRender = params.recurseEverything || !octreeQueryNode->getUsesFrustum() ||
|
||||
childElement->calculateShouldRender(params.viewFrustum,
|
||||
params.octreeElementSizeScale, params.boundaryLevelAdjust);
|
||||
|
||||
// track some stats
|
||||
if (params.stats) {
|
||||
// don't need to check childElement here, because we can't get here with no childElement
|
||||
if (!shouldRender && childElement->isLeaf()) {
|
||||
params.stats->skippedDistance(childElement);
|
||||
}
|
||||
// don't need to check childElement here, because we can't get here with no childElement
|
||||
if (childIsOccluded) {
|
||||
params.stats->skippedOccluded(childElement);
|
||||
}
|
||||
// don't need to check childElement here, because we can't get here with no childElement
|
||||
if (!shouldRender && childElement->isLeaf()) {
|
||||
octreeQueryNode->stats.skippedDistance(childElement);
|
||||
}
|
||||
// don't need to check childElement here, because we can't get here with no childElement
|
||||
if (childIsOccluded) {
|
||||
octreeQueryNode->stats.skippedOccluded(childElement);
|
||||
}
|
||||
|
||||
// track children with actual color, only if the child wasn't previously in view!
|
||||
|
@ -1247,19 +1253,17 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
// need to send it.
|
||||
if (!childWasInView ||
|
||||
(params.deltaView &&
|
||||
childElement->hasChangedSince(params.lastQuerySent - CHANGE_FUDGE))){
|
||||
childElement->hasChangedSince(octreeQueryNode->getLastTimeBagEmpty() - CHANGE_FUDGE))){
|
||||
|
||||
childrenDataBits += (1 << (7 - originalIndex));
|
||||
inViewWithColorCount++;
|
||||
} else {
|
||||
// otherwise just track stats of the items we discarded
|
||||
// don't need to check childElement here, because we can't get here with no childElement
|
||||
if (params.stats) {
|
||||
if (childWasInView) {
|
||||
params.stats->skippedWasInView(childElement);
|
||||
} else {
|
||||
params.stats->skippedNoChange(childElement);
|
||||
}
|
||||
if (childWasInView) {
|
||||
octreeQueryNode->stats.skippedWasInView(childElement);
|
||||
} else {
|
||||
octreeQueryNode->stats.skippedNoChange(childElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1277,9 +1281,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
|
||||
assert(continueThisLevel); // since we used reserved bits, this really shouldn't fail
|
||||
bytesAtThisLevel += sizeof(childrenDataBits); // keep track of byte count
|
||||
if (params.stats) {
|
||||
params.stats->colorBitsWritten(); // really data bits not just color bits
|
||||
}
|
||||
|
||||
octreeQueryNode->stats.colorBitsWritten(); // really data bits not just color bits
|
||||
|
||||
// NOW might be a good time to give our tree subclass and this element a chance to set up and check any extra encode data
|
||||
element->initializeExtraEncodeData(params);
|
||||
|
@ -1349,8 +1352,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
bytesAtThisLevel += (bytesAfterChild - bytesBeforeChild); // keep track of byte count for this child
|
||||
|
||||
// don't need to check childElement here, because we can't get here with no childElement
|
||||
if (params.stats && (childAppendState != OctreeElement::NONE)) {
|
||||
params.stats->colorSent(childElement);
|
||||
if (childAppendState != OctreeElement::NONE) {
|
||||
octreeQueryNode->stats.colorSent(childElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1377,9 +1380,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
continueThisLevel = packetData->appendBitMask(childrenExistInTreeBits);
|
||||
if (continueThisLevel) {
|
||||
bytesAtThisLevel += sizeof(childrenExistInTreeBits); // keep track of byte count
|
||||
if (params.stats) {
|
||||
params.stats->existsBitsWritten();
|
||||
}
|
||||
|
||||
octreeQueryNode->stats.existsBitsWritten();
|
||||
} else {
|
||||
qCDebug(octree) << "WARNING UNEXPECTED CASE: Failed to append childrenExistInTreeBits";
|
||||
qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE....";
|
||||
|
@ -1392,9 +1394,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
continueThisLevel = packetData->appendBitMask(childrenExistInPacketBits);
|
||||
if (continueThisLevel) {
|
||||
bytesAtThisLevel += sizeof(childrenExistInPacketBits); // keep track of byte count
|
||||
if (params.stats) {
|
||||
params.stats->existsInPacketBitsWritten();
|
||||
}
|
||||
|
||||
octreeQueryNode->stats.existsInPacketBitsWritten();
|
||||
} else {
|
||||
qCDebug(octree) << "WARNING UNEXPECTED CASE: Failed to append childrenExistInPacketBits";
|
||||
qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE....";
|
||||
|
@ -1451,7 +1452,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
// called databits), then we wouldn't send the children. So those types of Octree's should tell us to keep
|
||||
// recursing, by returning TRUE in recurseChildrenWithData().
|
||||
|
||||
if (params.recurseEverything || !params.usesFrustum
|
||||
if (params.recurseEverything || !octreeQueryNode->getUsesFrustum()
|
||||
|| recurseChildrenWithData() || !oneAtBit(childrenDataBits, originalIndex)) {
|
||||
|
||||
// Allow the datatype a chance to determine if it really wants to recurse this tree. Usually this
|
||||
|
@ -1502,8 +1503,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
}
|
||||
|
||||
// If this is the last of the child exists bits, then we're actually be rolling out the entire tree
|
||||
if (params.stats && childrenExistInPacketBits == 0) {
|
||||
params.stats->childBitsRemoved(params.includeExistsBits);
|
||||
if (childrenExistInPacketBits == 0) {
|
||||
octreeQueryNode->stats.childBitsRemoved(params.includeExistsBits);
|
||||
}
|
||||
|
||||
if (!continueThisLevel) {
|
||||
|
@ -1558,9 +1559,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
if (continueThisLevel) {
|
||||
bytesAtThisLevel += (bytesAfterChild - bytesBeforeChild); // keep track of byte count for this child
|
||||
|
||||
if (params.stats) {
|
||||
params.stats->colorSent(element);
|
||||
}
|
||||
octreeQueryNode->stats.colorSent(element);
|
||||
}
|
||||
|
||||
if (!continueThisLevel) {
|
||||
|
@ -1595,9 +1594,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
|
|||
bag.insert(element);
|
||||
|
||||
// don't need to check element here, because we can't get here with no element
|
||||
if (params.stats) {
|
||||
params.stats->didntFit(element);
|
||||
}
|
||||
octreeQueryNode->stats.didntFit(element);
|
||||
|
||||
params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
|
||||
bytesAtThisLevel = 0; // didn't fit
|
||||
|
@ -1876,9 +1873,7 @@ bool Octree::writeToFile(const char* fileName, OctreeElementPointer element, QSt
|
|||
const char* cFileName = byteArray.constData();
|
||||
|
||||
bool success = false;
|
||||
if (persistAsFileType == "svo") {
|
||||
success = writeToSVOFile(fileName, element);
|
||||
} else if (persistAsFileType == "json") {
|
||||
if (persistAsFileType == "json") {
|
||||
success = writeToJSONFile(cFileName, element);
|
||||
} else if (persistAsFileType == "json.gz") {
|
||||
success = writeToJSONFile(cFileName, element, true);
|
||||
|
@ -1936,95 +1931,6 @@ bool Octree::writeToJSONFile(const char* fileName, OctreeElementPointer element,
|
|||
return success;
|
||||
}
|
||||
|
||||
bool Octree::writeToSVOFile(const char* fileName, OctreeElementPointer element) {
|
||||
qWarning() << "SVO file format deprecated. Support for reading SVO files is no longer support and will be removed soon.";
|
||||
bool success = false;
|
||||
|
||||
std::ofstream file(fileName, std::ios::out|std::ios::binary);
|
||||
|
||||
if(file.is_open()) {
|
||||
qCDebug(octree, "Saving binary SVO to file %s...", fileName);
|
||||
|
||||
PacketType expectedPacketType = expectedDataPacketType();
|
||||
int expectedIntType = (int) expectedPacketType;
|
||||
PacketVersion expectedVersion = versionForPacketType(expectedPacketType);
|
||||
bool hasBufferBreaks = versionHasSVOfileBreaks(expectedVersion);
|
||||
|
||||
// before reading the file, check to see if this version of the Octree supports file versions
|
||||
if (getWantSVOfileVersions()) {
|
||||
// if so, read the first byte of the file and see if it matches the expected version code
|
||||
file.write(reinterpret_cast<char*>(&expectedIntType), sizeof(expectedIntType));
|
||||
file.write(&expectedVersion, sizeof(expectedVersion));
|
||||
qCDebug(octree) << "SVO file type: " << expectedPacketType << " version: " << (int)expectedVersion;
|
||||
|
||||
hasBufferBreaks = versionHasSVOfileBreaks(expectedVersion);
|
||||
}
|
||||
if (hasBufferBreaks) {
|
||||
qCDebug(octree) << " this version includes buffer breaks";
|
||||
} else {
|
||||
qCDebug(octree) << " this version does not include buffer breaks";
|
||||
}
|
||||
|
||||
|
||||
OctreeElementBag elementBag;
|
||||
OctreeElementExtraEncodeData extraEncodeData;
|
||||
// If we were given a specific element, start from there, otherwise start from root
|
||||
if (element) {
|
||||
elementBag.insert(element);
|
||||
} else {
|
||||
elementBag.insert(_rootElement);
|
||||
}
|
||||
|
||||
OctreePacketData packetData;
|
||||
int bytesWritten = 0;
|
||||
bool lastPacketWritten = false;
|
||||
|
||||
while (OctreeElementPointer subTree = elementBag.extract()) {
|
||||
EncodeBitstreamParams params(INT_MAX, NO_EXISTS_BITS);
|
||||
params.recurseEverything = true;
|
||||
withReadLock([&] {
|
||||
params.extraEncodeData = &extraEncodeData;
|
||||
bytesWritten = encodeTreeBitstream(subTree, &packetData, elementBag, params);
|
||||
});
|
||||
|
||||
// if the subTree couldn't fit, and so we should reset the packet and reinsert the element in our bag and try again
|
||||
if (bytesWritten == 0 && (params.stopReason == EncodeBitstreamParams::DIDNT_FIT)) {
|
||||
if (packetData.hasContent()) {
|
||||
// if this type of SVO file should have buffer breaks, then we will write a buffer size before each
|
||||
// buffer to allow the reader to read this file in chunks.
|
||||
if (hasBufferBreaks) {
|
||||
quint16 bufferSize = packetData.getFinalizedSize();
|
||||
file.write((const char*)&bufferSize, sizeof(bufferSize));
|
||||
}
|
||||
file.write((const char*)packetData.getFinalizedData(), packetData.getFinalizedSize());
|
||||
lastPacketWritten = true;
|
||||
}
|
||||
packetData.reset(); // is there a better way to do this? could we fit more?
|
||||
elementBag.insert(subTree);
|
||||
} else {
|
||||
lastPacketWritten = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!lastPacketWritten) {
|
||||
// if this type of SVO file should have buffer breaks, then we will write a buffer size before each
|
||||
// buffer to allow the reader to read this file in chunks.
|
||||
if (hasBufferBreaks) {
|
||||
quint16 bufferSize = packetData.getFinalizedSize();
|
||||
file.write((const char*)&bufferSize, sizeof(bufferSize));
|
||||
}
|
||||
file.write((const char*)packetData.getFinalizedData(), packetData.getFinalizedSize());
|
||||
}
|
||||
|
||||
releaseSceneEncodeData(&extraEncodeData);
|
||||
|
||||
success = true;
|
||||
}
|
||||
file.close();
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
unsigned long Octree::getOctreeElementsCount() {
|
||||
unsigned long nodeCount = 0;
|
||||
recurseTreeWithOperation(countOctreeElementsOperation, &nodeCount);
|
||||
|
|
|
@ -59,9 +59,7 @@ const bool DONT_COLLAPSE = false;
|
|||
const int DONT_CHOP = 0;
|
||||
const int NO_BOUNDARY_ADJUST = 0;
|
||||
const int LOW_RES_MOVING_ADJUST = 1;
|
||||
const quint64 IGNORE_LAST_SENT = 0;
|
||||
|
||||
#define IGNORE_SCENE_STATS NULL
|
||||
#define IGNORE_COVERAGE_MAP NULL
|
||||
#define IGNORE_JURISDICTION_MAP NULL
|
||||
|
||||
|
@ -69,7 +67,6 @@ class EncodeBitstreamParams {
|
|||
public:
|
||||
ViewFrustum viewFrustum;
|
||||
ViewFrustum lastViewFrustum;
|
||||
quint64 lastQuerySent;
|
||||
int maxEncodeLevel;
|
||||
int maxLevelReached;
|
||||
bool includeExistsBits;
|
||||
|
@ -79,10 +76,7 @@ public:
|
|||
int boundaryLevelAdjust;
|
||||
float octreeElementSizeScale;
|
||||
bool forceSendScene;
|
||||
OctreeSceneStats* stats;
|
||||
JurisdictionMap* jurisdictionMap;
|
||||
OctreeElementExtraEncodeData* extraEncodeData;
|
||||
bool usesFrustum;
|
||||
NodeData* nodeData;
|
||||
|
||||
// output hints from the encode process
|
||||
|
@ -90,6 +84,7 @@ public:
|
|||
UNKNOWN,
|
||||
DIDNT_FIT,
|
||||
NULL_NODE,
|
||||
NULL_NODE_DATA,
|
||||
TOO_DEEP,
|
||||
OUT_OF_JURISDICTION,
|
||||
LOD_SKIP,
|
||||
|
@ -107,14 +102,9 @@ public:
|
|||
bool useDeltaView = false,
|
||||
int boundaryLevelAdjust = NO_BOUNDARY_ADJUST,
|
||||
float octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE,
|
||||
quint64 lastQuerySent = IGNORE_LAST_SENT,
|
||||
bool forceSendScene = true,
|
||||
OctreeSceneStats* stats = IGNORE_SCENE_STATS,
|
||||
JurisdictionMap* jurisdictionMap = IGNORE_JURISDICTION_MAP,
|
||||
OctreeElementExtraEncodeData* extraEncodeData = nullptr,
|
||||
bool usesFrustum = true,
|
||||
NodeData* nodeData = nullptr) :
|
||||
lastQuerySent(lastQuerySent),
|
||||
maxEncodeLevel(maxEncodeLevel),
|
||||
maxLevelReached(0),
|
||||
includeExistsBits(includeExistsBits),
|
||||
|
@ -123,10 +113,7 @@ public:
|
|||
boundaryLevelAdjust(boundaryLevelAdjust),
|
||||
octreeElementSizeScale(octreeElementSizeScale),
|
||||
forceSendScene(forceSendScene),
|
||||
stats(stats),
|
||||
jurisdictionMap(jurisdictionMap),
|
||||
extraEncodeData(extraEncodeData),
|
||||
usesFrustum(usesFrustum),
|
||||
nodeData(nodeData),
|
||||
stopReason(UNKNOWN)
|
||||
{
|
||||
|
@ -306,9 +293,8 @@ public:
|
|||
void loadOctreeFile(const char* fileName);
|
||||
|
||||
// Octree exporters
|
||||
bool writeToFile(const char* filename, OctreeElementPointer element = NULL, QString persistAsFileType = "svo");
|
||||
bool writeToFile(const char* filename, OctreeElementPointer element = NULL, QString persistAsFileType = "json.gz");
|
||||
bool writeToJSONFile(const char* filename, OctreeElementPointer element = NULL, bool doGzip = false);
|
||||
bool writeToSVOFile(const char* filename, OctreeElementPointer element = NULL);
|
||||
virtual bool writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues,
|
||||
bool skipThoseWithBadParents) = 0;
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ public:
|
|||
|
||||
OctreePersistThread(OctreePointer tree, const QString& filename, const QString& backupDirectory,
|
||||
int persistInterval = DEFAULT_PERSIST_INTERVAL, bool wantBackup = false,
|
||||
const QJsonObject& settings = QJsonObject(), bool debugTimestampNow = false, QString persistAsFileType="svo");
|
||||
const QJsonObject& settings = QJsonObject(), bool debugTimestampNow = false, QString persistAsFileType="json.gz");
|
||||
|
||||
bool isInitialLoadComplete() const { return _initialLoadComplete; }
|
||||
quint64 getLoadElapsedTime() const { return _loadTimeUSecs; }
|
||||
|
|
|
@ -511,7 +511,6 @@ WebTablet.prototype.mousePressEvent = function (event) {
|
|||
tablet.gotoHomeScreen();
|
||||
this.setHomeButtonTexture();
|
||||
}
|
||||
Messages.sendLocalMessage("home", this.homeButtonID);
|
||||
}
|
||||
} else if (!HMD.active && (!overlayPickResults.intersects || overlayPickResults.overlayID !== this.webOverlayID)) {
|
||||
this.dragging = true;
|
||||
|
|
|
@ -43,7 +43,8 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) {
|
|||
QByteArray fbxContents = fbx.readAll();
|
||||
FBXGeometry* geom;
|
||||
if (filename.toLower().endsWith(".obj")) {
|
||||
geom = OBJReader().readOBJ(fbxContents, QVariantHash());
|
||||
bool combineParts = false;
|
||||
geom = OBJReader().readOBJ(fbxContents, QVariantHash(), combineParts);
|
||||
} else if (filename.toLower().endsWith(".fbx")) {
|
||||
geom = readFBX(fbxContents, QVariantHash(), filename);
|
||||
} else {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
//
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <Trace.h>
|
||||
#include <VHACD.h>
|
||||
#include "VHACDUtilApp.h"
|
||||
#include "VHACDUtil.h"
|
||||
|
@ -98,6 +99,8 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
|||
{
|
||||
vhacd::VHACDUtil vUtil;
|
||||
|
||||
DependencyManager::set<tracing::Tracer>();
|
||||
|
||||
// parse command-line
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("High Fidelity Object Decomposer");
|
||||
|
|
Loading…
Reference in a new issue