mirror of
https://github.com/overte-org/overte.git
synced 2025-04-23 10:53:34 +02:00
Merge branch 'master' into 21249
# Conflicts: # libraries/audio-client/src/AudioClient.cpp
This commit is contained in:
commit
4a5f84f4b9
365 changed files with 93627 additions and 2603 deletions
BUILD_ANDROID.md
assignment-client/src
Agent.cppAssignmentAction.cppAssignmentActionFactory.cppAssignmentActionFactory.hAssignmentClient.cppAssignmentDynamic.cppAssignmentDynamic.hAssignmentDynamicFactory.cppAssignmentDynamicFactory.h
cmake
domain-server/src
interface
libraries
audio-client/src
display-plugins/src/display-plugins
entities-renderer
entities/src
EntityActionInterface.cppEntityDynamicFactoryInterface.hEntityDynamicInterface.cppEntityDynamicInterface.hEntityItem.cppEntityItem.hEntityScriptingInterface.cppEntitySimulation.cppEntitySimulation.hEntityTree.cpp
gpu-gl/src/gpu
gl
gl41
gl45
gpu/src/gpu
image
ktx/src/ktx
model-networking
model
networking/src
physics/src
EntityMotionState.cppObjectAction.cppObjectAction.hObjectActionOffset.cppObjectActionSpring.cppObjectActionSpring.hObjectActionTravelOriented.cppObjectConstraint.cppObjectConstraint.hObjectConstraintHinge.cppObjectConstraintHinge.hObjectDynamic.cppObjectDynamic.hPhysicalEntitySimulation.cppPhysicalEntitySimulation.hPhysicsEngine.cppPhysicsEngine.h
procedural
render-utils
|
@ -5,7 +5,7 @@ Please read the [general build guide](BUILD.md) for information on dependencies
|
|||
You will need the following tools to build our Android targets.
|
||||
|
||||
* [cmake](http://www.cmake.org/download/) ~> 3.5.1
|
||||
* [Qt](http://www.qt.io/download-open-source/#) ~> 5.5.1
|
||||
* [Qt](http://www.qt.io/download-open-source/#) ~> 5.6.2
|
||||
* [ant](http://ant.apache.org/bindownload.cgi) ~> 1.9.4
|
||||
* [Android NDK](https://developer.android.com/tools/sdk/ndk/index.html) ~> r10d
|
||||
* [Android SDK](http://developer.android.com/sdk/installing/index.html) ~> 24.4.1.1
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <ScriptCache.h>
|
||||
#include <ScriptEngines.h>
|
||||
#include <SoundCache.h>
|
||||
#include <UsersScriptingInterface.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include <recording/ClipCache.h>
|
||||
|
@ -75,6 +76,8 @@ Agent::Agent(ReceivedMessage& message) :
|
|||
DependencyManager::set<ScriptEngines>(ScriptEngine::AGENT_SCRIPT);
|
||||
|
||||
DependencyManager::set<RecordingScriptingInterface>();
|
||||
DependencyManager::set<UsersScriptingInterface>();
|
||||
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
|
||||
|
@ -351,6 +354,10 @@ void Agent::executeScript() {
|
|||
// give this AvatarData object to the script engine
|
||||
_scriptEngine->registerGlobalObject("Avatar", scriptedAvatar.data());
|
||||
|
||||
// give scripts access to the Users object
|
||||
_scriptEngine->registerGlobalObject("Users", DependencyManager::get<UsersScriptingInterface>().data());
|
||||
|
||||
|
||||
auto player = DependencyManager::get<recording::Deck>();
|
||||
connect(player.data(), &recording::Deck::playbackStateChanged, [=] {
|
||||
if (player->isPlaying()) {
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
//
|
||||
// AssignmentAction.cpp
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves 2015-6-19
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "EntitySimulation.h"
|
||||
|
||||
#include "AssignmentAction.h"
|
||||
|
||||
AssignmentAction::AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
EntityActionInterface(type, id),
|
||||
_data(QByteArray()),
|
||||
_active(false),
|
||||
_ownerEntity(ownerEntity) {
|
||||
}
|
||||
|
||||
AssignmentAction::~AssignmentAction() {
|
||||
}
|
||||
|
||||
void AssignmentAction::removeFromSimulation(EntitySimulationPointer simulation) const {
|
||||
withReadLock([&]{
|
||||
simulation->removeAction(_id);
|
||||
simulation->applyActionChanges();
|
||||
});
|
||||
}
|
||||
|
||||
QByteArray AssignmentAction::serialize() const {
|
||||
QByteArray result;
|
||||
withReadLock([&]{
|
||||
result = _data;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void AssignmentAction::deserialize(QByteArray serializedArguments) {
|
||||
withWriteLock([&]{
|
||||
_data = serializedArguments;
|
||||
});
|
||||
}
|
||||
|
||||
bool AssignmentAction::updateArguments(QVariantMap arguments) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::updateArguments called in assignment-client.";
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariantMap AssignmentAction::getArguments() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::getArguments called in assignment-client.";
|
||||
return QVariantMap();
|
||||
}
|
||||
|
||||
glm::vec3 AssignmentAction::getPosition() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::getPosition called in assignment-client.";
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void AssignmentAction::setPosition(glm::vec3 position) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::setPosition called in assignment-client.";
|
||||
}
|
||||
|
||||
glm::quat AssignmentAction::getRotation() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::getRotation called in assignment-client.";
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
void AssignmentAction::setRotation(glm::quat rotation) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::setRotation called in assignment-client.";
|
||||
}
|
||||
|
||||
glm::vec3 AssignmentAction::getLinearVelocity() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::getLinearVelocity called in assignment-client.";
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void AssignmentAction::setLinearVelocity(glm::vec3 linearVelocity) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::setLinearVelocity called in assignment-client.";
|
||||
}
|
||||
|
||||
glm::vec3 AssignmentAction::getAngularVelocity() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::getAngularVelocity called in assignment-client.";
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void AssignmentAction::setAngularVelocity(glm::vec3 angularVelocity) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::setAngularVelocity called in assignment-client.";
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
//
|
||||
// AssignmentActionFactory.cpp
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-19
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AssignmentActionFactory.h"
|
||||
|
||||
|
||||
EntityActionPointer assignmentActionFactory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) {
|
||||
return EntityActionPointer(new AssignmentAction(type, id, ownerEntity));
|
||||
}
|
||||
|
||||
EntityActionPointer AssignmentActionFactory::factory(EntityActionType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) {
|
||||
EntityActionPointer action = assignmentActionFactory(type, id, ownerEntity);
|
||||
if (action) {
|
||||
bool ok = action->updateArguments(arguments);
|
||||
if (ok) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
EntityActionPointer AssignmentActionFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
|
||||
QDataStream serializedActionDataStream(data);
|
||||
EntityActionType type;
|
||||
QUuid id;
|
||||
|
||||
serializedActionDataStream >> type;
|
||||
serializedActionDataStream >> id;
|
||||
|
||||
EntityActionPointer action = assignmentActionFactory(type, id, ownerEntity);
|
||||
|
||||
if (action) {
|
||||
action->deserialize(data);
|
||||
}
|
||||
return action;
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
//
|
||||
// AssignmentActionFactory.cpp
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-19
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AssignmentActionFactory_h
|
||||
#define hifi_AssignmentActionFactory_h
|
||||
|
||||
#include "EntityActionFactoryInterface.h"
|
||||
#include "AssignmentAction.h"
|
||||
|
||||
class AssignmentActionFactory : public EntityActionFactoryInterface {
|
||||
public:
|
||||
AssignmentActionFactory() : EntityActionFactoryInterface() { }
|
||||
virtual ~AssignmentActionFactory() { }
|
||||
virtual EntityActionPointer factory(EntityActionType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) override;
|
||||
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override;
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentActionFactory_h
|
|
@ -33,7 +33,7 @@
|
|||
#include <UserActivityLoggerScriptingInterface.h>
|
||||
|
||||
#include "AssignmentFactory.h"
|
||||
#include "AssignmentActionFactory.h"
|
||||
#include "AssignmentDynamicFactory.h"
|
||||
|
||||
#include "AssignmentClient.h"
|
||||
#include "AssignmentClientLogging.h"
|
||||
|
@ -64,8 +64,8 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
auto animationCache = DependencyManager::set<AnimationCache>();
|
||||
auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>(false);
|
||||
|
||||
DependencyManager::registerInheritance<EntityActionFactoryInterface, AssignmentActionFactory>();
|
||||
auto actionFactory = DependencyManager::set<AssignmentActionFactory>();
|
||||
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, AssignmentDynamicFactory>();
|
||||
auto dynamicFactory = DependencyManager::set<AssignmentDynamicFactory>();
|
||||
DependencyManager::set<ResourceScriptingInterface>();
|
||||
DependencyManager::set<UserActivityLoggerScriptingInterface>();
|
||||
|
||||
|
|
83
assignment-client/src/AssignmentDynamic.cpp
Normal file
83
assignment-client/src/AssignmentDynamic.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// AssignmentDynamic.cpp
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves 2015-6-19
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "EntitySimulation.h"
|
||||
|
||||
#include "AssignmentDynamic.h"
|
||||
|
||||
AssignmentDynamic::AssignmentDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
EntityDynamicInterface(type, id),
|
||||
_data(QByteArray()),
|
||||
_active(false),
|
||||
_ownerEntity(ownerEntity) {
|
||||
}
|
||||
|
||||
AssignmentDynamic::~AssignmentDynamic() {
|
||||
}
|
||||
|
||||
void AssignmentDynamic::removeFromSimulation(EntitySimulationPointer simulation) const {
|
||||
withReadLock([&]{
|
||||
simulation->removeDynamic(_id);
|
||||
simulation->applyDynamicChanges();
|
||||
});
|
||||
}
|
||||
|
||||
QByteArray AssignmentDynamic::serialize() const {
|
||||
QByteArray result;
|
||||
withReadLock([&]{
|
||||
result = _data;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void AssignmentDynamic::deserialize(QByteArray serializedArguments) {
|
||||
withWriteLock([&]{
|
||||
_data = serializedArguments;
|
||||
});
|
||||
}
|
||||
|
||||
bool AssignmentDynamic::updateArguments(QVariantMap arguments) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentDynamic::updateArguments called in assignment-client.";
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariantMap AssignmentDynamic::getArguments() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentDynamic::getArguments called in assignment-client.";
|
||||
return QVariantMap();
|
||||
}
|
||||
|
||||
glm::vec3 AssignmentDynamic::getPosition() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentDynamic::getPosition called in assignment-client.";
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
glm::quat AssignmentDynamic::getRotation() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentDynamic::getRotation called in assignment-client.";
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
glm::vec3 AssignmentDynamic::getLinearVelocity() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentDynamic::getLinearVelocity called in assignment-client.";
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void AssignmentDynamic::setLinearVelocity(glm::vec3 linearVelocity) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentDynamic::setLinearVelocity called in assignment-client.";
|
||||
}
|
||||
|
||||
glm::vec3 AssignmentDynamic::getAngularVelocity() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentDynamic::getAngularVelocity called in assignment-client.";
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void AssignmentDynamic::setAngularVelocity(glm::vec3 angularVelocity) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentDynamic::setAngularVelocity called in assignment-client.";
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// AssignmentAction.h
|
||||
// AssignmentDynamic.h
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves 2015-6-19
|
||||
|
@ -8,21 +8,21 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// http://bulletphysics.org/Bullet/BulletFull/classbtActionInterface.html
|
||||
// http://bulletphysics.org/Bullet/BulletFull/classbtDynamicInterface.html
|
||||
|
||||
#ifndef hifi_AssignmentAction_h
|
||||
#define hifi_AssignmentAction_h
|
||||
#ifndef hifi_AssignmentDynamic_h
|
||||
#define hifi_AssignmentDynamic_h
|
||||
|
||||
#include <QUuid>
|
||||
#include <EntityItem.h>
|
||||
|
||||
#include "EntityActionInterface.h"
|
||||
#include "EntityDynamicInterface.h"
|
||||
|
||||
|
||||
class AssignmentAction : public EntityActionInterface, public ReadWriteLockable {
|
||||
class AssignmentDynamic : public EntityDynamicInterface, public ReadWriteLockable {
|
||||
public:
|
||||
AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~AssignmentAction();
|
||||
AssignmentDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~AssignmentDynamic();
|
||||
|
||||
virtual void removeFromSimulation(EntitySimulationPointer simulation) const override;
|
||||
virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; }
|
||||
|
@ -38,9 +38,7 @@ private:
|
|||
|
||||
protected:
|
||||
virtual glm::vec3 getPosition() override;
|
||||
virtual void setPosition(glm::vec3 position) override;
|
||||
virtual glm::quat getRotation() override;
|
||||
virtual void setRotation(glm::quat rotation) override;
|
||||
virtual glm::vec3 getLinearVelocity() override;
|
||||
virtual void setLinearVelocity(glm::vec3 linearVelocity) override;
|
||||
virtual glm::vec3 getAngularVelocity() override;
|
||||
|
@ -50,4 +48,4 @@ protected:
|
|||
EntityItemWeakPointer _ownerEntity;
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentAction_h
|
||||
#endif // hifi_AssignmentDynamic_h
|
48
assignment-client/src/AssignmentDynamicFactory.cpp
Normal file
48
assignment-client/src/AssignmentDynamicFactory.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// AssignmentDynamcFactory.cpp
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-19
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AssignmentDynamicFactory.h"
|
||||
|
||||
|
||||
EntityDynamicPointer assignmentDynamicFactory(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) {
|
||||
return EntityDynamicPointer(new AssignmentDynamic(type, id, ownerEntity));
|
||||
}
|
||||
|
||||
EntityDynamicPointer AssignmentDynamicFactory::factory(EntityDynamicType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) {
|
||||
EntityDynamicPointer dynamic = assignmentDynamicFactory(type, id, ownerEntity);
|
||||
if (dynamic) {
|
||||
bool ok = dynamic->updateArguments(arguments);
|
||||
if (ok) {
|
||||
return dynamic;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
EntityDynamicPointer AssignmentDynamicFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
|
||||
QDataStream serializedDynamicDataStream(data);
|
||||
EntityDynamicType type;
|
||||
QUuid id;
|
||||
|
||||
serializedDynamicDataStream >> type;
|
||||
serializedDynamicDataStream >> id;
|
||||
|
||||
EntityDynamicPointer dynamic = assignmentDynamicFactory(type, id, ownerEntity);
|
||||
|
||||
if (dynamic) {
|
||||
dynamic->deserialize(data);
|
||||
}
|
||||
return dynamic;
|
||||
}
|
29
assignment-client/src/AssignmentDynamicFactory.h
Normal file
29
assignment-client/src/AssignmentDynamicFactory.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// AssignmentDynamicFactory.cpp
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-19
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AssignmentDynamicFactory_h
|
||||
#define hifi_AssignmentDynamicFactory_h
|
||||
|
||||
#include "EntityDynamicFactoryInterface.h"
|
||||
#include "AssignmentDynamic.h"
|
||||
|
||||
class AssignmentDynamicFactory : public EntityDynamicFactoryInterface {
|
||||
public:
|
||||
AssignmentDynamicFactory() : EntityDynamicFactoryInterface() { }
|
||||
virtual ~AssignmentDynamicFactory() { }
|
||||
virtual EntityDynamicPointer factory(EntityDynamicType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) override;
|
||||
virtual EntityDynamicPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override;
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentDynamicFactory_h
|
87
cmake/externals/nvtt/CMakeLists.txt
vendored
Normal file
87
cmake/externals/nvtt/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,87 @@
|
|||
include(ExternalProject)
|
||||
include(SelectLibraryConfigurations)
|
||||
|
||||
set(EXTERNAL_NAME nvtt)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
if (WIN32)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://s3.amazonaws.com/hifi-public/dependencies/nvtt-win-2.1.0.zip
|
||||
URL_MD5 3ea6eeadbcc69071acf9c49ba565760e
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE PATH "Location of NVTT include directory")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/Release/x64/nvtt.lib CACHE FILEPATH "Path to NVTT release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/Release>/x64" CACHE PATH "Location of NVTT release DLL")
|
||||
else ()
|
||||
|
||||
if (ANDROID)
|
||||
set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
|
||||
endif ()
|
||||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/nvidia-texture-tools-2.1.0.zip
|
||||
URL_MD5 81b8fa6a9ee3f986088eb6e2215d6a57
|
||||
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DNVTT_SHARED=1 -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
)
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "Location of NVTT include directory")
|
||||
|
||||
if (APPLE)
|
||||
set(_LIB_EXT "dylib")
|
||||
else ()
|
||||
set(_LIB_EXT "so")
|
||||
endif ()
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libnvtt.${_LIB_EXT} CACHE FILEPATH "Path to NVTT library")
|
||||
|
||||
if (APPLE)
|
||||
# on OS X we have to use install_name_tool to fix the paths found in the NVTT shared libraries
|
||||
# so that they can be found and linked during the linking phase
|
||||
set(_NVTT_LIB_DIR "${INSTALL_DIR}/lib")
|
||||
|
||||
# first fix the install names of all present libraries
|
||||
ExternalProject_Add_Step(
|
||||
${EXTERNAL_NAME}
|
||||
change-install-name
|
||||
COMMENT "Calling install_name_tool on NVTT libraries to fix install name for dylib linking"
|
||||
COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_NVTT_LIB_DIR} -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake
|
||||
DEPENDEES install
|
||||
WORKING_DIRECTORY <INSTALL_DIR>
|
||||
LOG 1
|
||||
)
|
||||
|
||||
# then, for the main library (libnvtt) fix the paths to the dependency libraries (core, image, math)
|
||||
ExternalProject_Add_Step(
|
||||
${EXTERNAL_NAME}
|
||||
change-dependency-paths
|
||||
COMMENT "Calling install_name_tool on NVTT libraries to fix paths for dependency libraries"
|
||||
COMMAND install_name_tool -change libnvimage.dylib ${INSTALL_DIR}/lib/libnvimage.dylib libnvtt.dylib
|
||||
COMMAND install_name_tool -change libnvcore.dylib ${INSTALL_DIR}/lib/libnvcore.dylib libnvtt.dylib
|
||||
COMMAND install_name_tool -change libnvmath.dylib ${INSTALL_DIR}/lib/libnvmath.dylib libnvtt.dylib
|
||||
COMMAND install_name_tool -change libnvcore.dylib ${INSTALL_DIR}/lib/libnvcore.dylib libnvimage.dylib
|
||||
COMMAND install_name_tool -change libnvmath.dylib ${INSTALL_DIR}/lib/libnvmath.dylib libnvimage.dylib
|
||||
COMMAND install_name_tool -change libnvcore.dylib ${INSTALL_DIR}/lib/libnvcore.dylib libnvmath.dylib
|
||||
DEPENDEES install
|
||||
WORKING_DIRECTORY <INSTALL_DIR>/lib
|
||||
LOG 1
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Hide this external target (for IDE users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
37
cmake/modules/FindNVTT.cmake
Normal file
37
cmake/modules/FindNVTT.cmake
Normal file
|
@ -0,0 +1,37 @@
|
|||
#
|
||||
# FindNVTT.cmake
|
||||
#
|
||||
# Try to find NVIDIA texture tools library and include path.
|
||||
# Once done this will define
|
||||
#
|
||||
# NVTT_FOUND
|
||||
# NVTT_INCLUDE_DIRS
|
||||
# NVTT_LIBRARIES
|
||||
# NVTT_DLL_PATH
|
||||
#
|
||||
# Created on 4/14/2017 by Stephen Birarda
|
||||
# Copyright 2017 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("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("nvtt")
|
||||
|
||||
find_path(NVTT_INCLUDE_DIRS nvtt/nvtt.h PATH_SUFFIXES include HINTS ${NVTT_SEARCH_DIRS})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_library(NVTT_LIBRARY_RELEASE nvtt PATH_SUFFIXES "lib" "Release.x64/lib" HINTS ${NVTT_SEARCH_DIRS})
|
||||
find_library(NVTT_LIBRARY_DEBUG nvtt PATH_SUFFIXES "lib" "Debug.x64/lib" HINTS ${NVTT_SEARCH_DIRS})
|
||||
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(NVTT)
|
||||
|
||||
if (WIN32)
|
||||
find_path(NVTT_DLL_PATH nvtt.dll PATH_SUFFIXES "Release.x64/bin" HINTS ${NVTT_SEARCH_DIRS})
|
||||
find_package_handle_standard_args(NVTT DEFAULT_MSG NVTT_INCLUDE_DIRS NVTT_LIBRARIES NVTT_DLL_PATH)
|
||||
else ()
|
||||
find_package_handle_standard_args(NVTT DEFAULT_MSG NVTT_INCLUDE_DIRS NVTT_LIBRARIES)
|
||||
endif ()
|
|
@ -435,23 +435,8 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
return SharedNodePointer();
|
||||
}
|
||||
|
||||
QUuid hintNodeID;
|
||||
|
||||
// in case this is a node that's failing to connect
|
||||
// double check we don't have a node whose sockets match exactly already in the list
|
||||
limitedNodeList->eachNodeBreakable([&nodeConnection, &hintNodeID](const SharedNodePointer& node){
|
||||
if (node->getPublicSocket() == nodeConnection.publicSockAddr
|
||||
&& node->getLocalSocket() == nodeConnection.localSockAddr) {
|
||||
// we have a node that already has these exact sockets - this occurs if a node
|
||||
// is unable to connect to the domain
|
||||
hintNodeID = node->getUUID();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// add the connecting node (or re-use the matched one from eachNodeBreakable above)
|
||||
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection, hintNodeID);
|
||||
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection);
|
||||
|
||||
// set the edit rights for this user
|
||||
newNode->setPermissions(userPerms);
|
||||
|
@ -479,11 +464,12 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
return newNode;
|
||||
}
|
||||
|
||||
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
QUuid nodeID) {
|
||||
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection) {
|
||||
HifiSockAddr discoveredSocket = nodeConnection.senderSockAddr;
|
||||
SharedNetworkPeer connectedPeer = _icePeers.value(nodeConnection.connectUUID);
|
||||
|
||||
QUuid nodeID;
|
||||
|
||||
if (connectedPeer) {
|
||||
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
|
||||
nodeID = nodeConnection.connectUUID;
|
||||
|
@ -493,10 +479,8 @@ SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const Node
|
|||
discoveredSocket = *connectedPeer->getActiveSocket();
|
||||
}
|
||||
} else {
|
||||
// we got a connectUUID we didn't recognize, either use the hinted node ID or randomly generate a new one
|
||||
if (nodeID.isNull()) {
|
||||
nodeID = QUuid::createUuid();
|
||||
}
|
||||
// we got a connectUUID we didn't recognize, randomly generate a new one
|
||||
nodeID = QUuid::createUuid();
|
||||
}
|
||||
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
|
|
@ -76,8 +76,7 @@ private:
|
|||
SharedNodePointer processAgentConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
const QString& username,
|
||||
const QByteArray& usernameSignature);
|
||||
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
QUuid nodeID = QUuid());
|
||||
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection);
|
||||
|
||||
bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature,
|
||||
const HifiSockAddr& senderSockAddr);
|
||||
|
|
|
@ -194,7 +194,7 @@ link_hifi_libraries(
|
|||
recording fbx networking model-networking entities avatars
|
||||
audio audio-client animation script-engine physics
|
||||
render-utils entities-renderer avatars-renderer ui auto-updater
|
||||
controllers plugins
|
||||
controllers plugins image
|
||||
ui-plugins display-plugins input-plugins
|
||||
${NON_ANDROID_LIBRARIES}
|
||||
)
|
||||
|
|
Binary file not shown.
|
@ -211,6 +211,11 @@ Item {
|
|||
text: "Downloads: " + root.downloads + "/" + root.downloadLimit +
|
||||
", Pending: " + root.downloadsPending;
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "Processing: " + root.processing +
|
||||
", Pending: " + root.processingPending;
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded && root.downloadUrls.length > 0;
|
||||
text: "Download URLs:"
|
||||
|
|
|
@ -140,7 +140,7 @@
|
|||
#include "devices/Leapmotion.h"
|
||||
#include "DiscoverabilityManager.h"
|
||||
#include "GLCanvas.h"
|
||||
#include "InterfaceActionFactory.h"
|
||||
#include "InterfaceDynamicFactory.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "LODManager.h"
|
||||
#include "ModelPackager.h"
|
||||
|
@ -462,7 +462,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
|
||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||
DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();
|
||||
DependencyManager::registerInheritance<EntityActionFactoryInterface, InterfaceActionFactory>();
|
||||
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, InterfaceDynamicFactory>();
|
||||
DependencyManager::registerInheritance<SpatialParentFinder, InterfaceParentFinder>();
|
||||
|
||||
// Set dependencies
|
||||
|
@ -516,7 +516,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
DependencyManager::set<OffscreenUi>();
|
||||
DependencyManager::set<AutoUpdater>();
|
||||
DependencyManager::set<PathUtils>();
|
||||
DependencyManager::set<InterfaceActionFactory>();
|
||||
DependencyManager::set<InterfaceDynamicFactory>();
|
||||
DependencyManager::set<AudioInjectorManager>();
|
||||
DependencyManager::set<MessagesClient>();
|
||||
controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR,
|
||||
|
@ -1455,8 +1455,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
QString skyboxUrl { PathUtils::resourcesPath() + "images/Default-Sky-9-cubemap.jpg" };
|
||||
QString skyboxAmbientUrl { PathUtils::resourcesPath() + "images/Default-Sky-9-ambient.jpg" };
|
||||
|
||||
_defaultSkyboxTexture = textureCache->getImageTexture(skyboxUrl, NetworkTexture::CUBE_TEXTURE, { { "generateIrradiance", false } });
|
||||
_defaultSkyboxAmbientTexture = textureCache->getImageTexture(skyboxAmbientUrl, NetworkTexture::CUBE_TEXTURE, { { "generateIrradiance", true } });
|
||||
_defaultSkyboxTexture = textureCache->getImageTexture(skyboxUrl, image::TextureUsage::CUBE_TEXTURE, { { "generateIrradiance", false } });
|
||||
_defaultSkyboxAmbientTexture = textureCache->getImageTexture(skyboxAmbientUrl, image::TextureUsage::CUBE_TEXTURE, { { "generateIrradiance", true } });
|
||||
|
||||
_defaultSkybox->setCubemap(_defaultSkyboxTexture);
|
||||
|
||||
|
@ -4450,7 +4450,7 @@ void Application::update(float deltaTime) {
|
|||
_entitySimulation->setObjectsToChange(stillNeedChange);
|
||||
});
|
||||
|
||||
_entitySimulation->applyActionChanges();
|
||||
_entitySimulation->applyDynamicChanges();
|
||||
|
||||
avatarManager->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
|
@ -4460,8 +4460,8 @@ void Application::update(float deltaTime) {
|
|||
_physicsEngine->changeObjects(motionStates);
|
||||
|
||||
myAvatar->prepareForPhysicsSimulation();
|
||||
_physicsEngine->forEachAction([&](EntityActionPointer action) {
|
||||
action->prepareForPhysicsSimulation();
|
||||
_physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
|
||||
dynamic->prepareForPhysicsSimulation();
|
||||
});
|
||||
}
|
||||
{
|
||||
|
@ -6769,11 +6769,6 @@ void Application::updateDisplayMode() {
|
|||
return;
|
||||
}
|
||||
|
||||
UserActivityLogger::getInstance().logAction("changed_display_mode", {
|
||||
{ "previous_display_mode", _displayPlugin ? _displayPlugin->getName() : "" },
|
||||
{ "display_mode", newDisplayPlugin ? newDisplayPlugin->getName() : "" }
|
||||
});
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
|
||||
// Make the switch atomic from the perspective of other threads
|
||||
|
@ -6828,13 +6823,16 @@ void Application::updateDisplayMode() {
|
|||
offscreenUi->getDesktop()->setProperty("repositionLocked", wasRepositionLocked);
|
||||
}
|
||||
|
||||
bool isHmd = _displayPlugin->isHmd();
|
||||
qCDebug(interfaceapp) << "Entering into" << (isHmd ? "HMD" : "Desktop") << "Mode";
|
||||
|
||||
// Only log/emit after a successful change
|
||||
UserActivityLogger::getInstance().logAction("changed_display_mode", {
|
||||
{ "previous_display_mode", _displayPlugin ? _displayPlugin->getName() : "" },
|
||||
{ "display_mode", newDisplayPlugin ? newDisplayPlugin->getName() : "" },
|
||||
{ "hmd", isHmd }
|
||||
});
|
||||
emit activeDisplayPluginChanged();
|
||||
|
||||
if (_displayPlugin->isHmd()) {
|
||||
qCDebug(interfaceapp) << "Entering into HMD Mode";
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Entering into Desktop Mode";
|
||||
}
|
||||
|
||||
// reset the avatar, to set head and hand palms back to a reasonable default pose.
|
||||
getMyAvatar()->reset(false);
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
//
|
||||
// InterfaceActionFactory.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-2
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
|
||||
#include <avatar/AvatarActionHold.h>
|
||||
#include <ObjectActionOffset.h>
|
||||
#include <ObjectActionSpring.h>
|
||||
#include <ObjectActionTravelOriented.h>
|
||||
#include <LogHandler.h>
|
||||
|
||||
#include "InterfaceActionFactory.h"
|
||||
|
||||
|
||||
EntityActionPointer interfaceActionFactory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) {
|
||||
switch (type) {
|
||||
case ACTION_TYPE_NONE:
|
||||
return EntityActionPointer();
|
||||
case ACTION_TYPE_OFFSET:
|
||||
return std::make_shared<ObjectActionOffset>(id, ownerEntity);
|
||||
case ACTION_TYPE_SPRING:
|
||||
return std::make_shared<ObjectActionSpring>(id, ownerEntity);
|
||||
case ACTION_TYPE_HOLD:
|
||||
return std::make_shared<AvatarActionHold>(id, ownerEntity);
|
||||
case ACTION_TYPE_TRAVEL_ORIENTED:
|
||||
return std::make_shared<ObjectActionTravelOriented>(id, ownerEntity);
|
||||
}
|
||||
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity action type");
|
||||
return EntityActionPointer();
|
||||
}
|
||||
|
||||
|
||||
EntityActionPointer InterfaceActionFactory::factory(EntityActionType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) {
|
||||
EntityActionPointer action = interfaceActionFactory(type, id, ownerEntity);
|
||||
if (action) {
|
||||
bool ok = action->updateArguments(arguments);
|
||||
if (ok) {
|
||||
if (action->lifetimeIsOver()) {
|
||||
return nullptr;
|
||||
}
|
||||
return action;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
EntityActionPointer InterfaceActionFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
|
||||
QDataStream serializedArgumentStream(data);
|
||||
EntityActionType type;
|
||||
QUuid id;
|
||||
|
||||
serializedArgumentStream >> type;
|
||||
serializedArgumentStream >> id;
|
||||
|
||||
EntityActionPointer action = interfaceActionFactory(type, id, ownerEntity);
|
||||
|
||||
if (action) {
|
||||
action->deserialize(data);
|
||||
if (action->lifetimeIsOver()) {
|
||||
static QString repeatedMessage =
|
||||
LogHandler::getInstance().addRepeatedMessageRegex(".*factoryBA lifetimeIsOver during action creation.*");
|
||||
qDebug() << "InterfaceActionFactory::factoryBA lifetimeIsOver during action creation --"
|
||||
<< action->getExpires() << "<" << usecTimestampNow();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
88
interface/src/InterfaceDynamicFactory.cpp
Normal file
88
interface/src/InterfaceDynamicFactory.cpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
//
|
||||
// InterfaceDynamicFactory.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-2
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
|
||||
#include <avatar/AvatarActionHold.h>
|
||||
#include <avatar/AvatarActionFarGrab.h>
|
||||
#include <ObjectActionOffset.h>
|
||||
#include <ObjectActionSpring.h>
|
||||
#include <ObjectActionTravelOriented.h>
|
||||
#include <ObjectConstraintHinge.h>
|
||||
#include <LogHandler.h>
|
||||
|
||||
#include "InterfaceDynamicFactory.h"
|
||||
|
||||
|
||||
EntityDynamicPointer interfaceDynamicFactory(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) {
|
||||
switch (type) {
|
||||
case DYNAMIC_TYPE_NONE:
|
||||
return EntityDynamicPointer();
|
||||
case DYNAMIC_TYPE_OFFSET:
|
||||
return std::make_shared<ObjectActionOffset>(id, ownerEntity);
|
||||
case DYNAMIC_TYPE_SPRING:
|
||||
return std::make_shared<ObjectActionSpring>(id, ownerEntity);
|
||||
case DYNAMIC_TYPE_HOLD:
|
||||
return std::make_shared<AvatarActionHold>(id, ownerEntity);
|
||||
case DYNAMIC_TYPE_TRAVEL_ORIENTED:
|
||||
return std::make_shared<ObjectActionTravelOriented>(id, ownerEntity);
|
||||
case DYNAMIC_TYPE_HINGE:
|
||||
return std::make_shared<ObjectConstraintHinge>(id, ownerEntity);
|
||||
case DYNAMIC_TYPE_FAR_GRAB:
|
||||
return std::make_shared<AvatarActionFarGrab>(id, ownerEntity);
|
||||
}
|
||||
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity dynamic type");
|
||||
return EntityDynamicPointer();
|
||||
}
|
||||
|
||||
|
||||
EntityDynamicPointer InterfaceDynamicFactory::factory(EntityDynamicType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) {
|
||||
EntityDynamicPointer dynamic = interfaceDynamicFactory(type, id, ownerEntity);
|
||||
if (dynamic) {
|
||||
bool ok = dynamic->updateArguments(arguments);
|
||||
if (ok) {
|
||||
if (dynamic->lifetimeIsOver()) {
|
||||
return nullptr;
|
||||
}
|
||||
return dynamic;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
EntityDynamicPointer InterfaceDynamicFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
|
||||
QDataStream serializedArgumentStream(data);
|
||||
EntityDynamicType type;
|
||||
QUuid id;
|
||||
|
||||
serializedArgumentStream >> type;
|
||||
serializedArgumentStream >> id;
|
||||
|
||||
EntityDynamicPointer dynamic = interfaceDynamicFactory(type, id, ownerEntity);
|
||||
|
||||
if (dynamic) {
|
||||
dynamic->deserialize(data);
|
||||
if (dynamic->lifetimeIsOver()) {
|
||||
static QString repeatedMessage =
|
||||
LogHandler::getInstance().addRepeatedMessageRegex(".*factoryBA lifetimeIsOver during dynamic creation.*");
|
||||
qDebug() << "InterfaceDynamicFactory::factoryBA lifetimeIsOver during dynamic creation --"
|
||||
<< dynamic->getExpires() << "<" << usecTimestampNow();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return dynamic;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// InterfaceActionFactory.cpp
|
||||
// InterfaceDynamicFactory.cpp
|
||||
// interface/src/
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-10
|
||||
|
@ -9,21 +9,21 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_InterfaceActionFactory_h
|
||||
#define hifi_InterfaceActionFactory_h
|
||||
#ifndef hifi_InterfaceDynamicFactory_h
|
||||
#define hifi_InterfaceDynamicFactory_h
|
||||
|
||||
#include "EntityActionFactoryInterface.h"
|
||||
#include "EntityDynamicFactoryInterface.h"
|
||||
|
||||
class InterfaceActionFactory : public EntityActionFactoryInterface {
|
||||
class InterfaceDynamicFactory : public EntityDynamicFactoryInterface {
|
||||
public:
|
||||
InterfaceActionFactory() : EntityActionFactoryInterface() { }
|
||||
virtual ~InterfaceActionFactory() { }
|
||||
virtual EntityActionPointer factory(EntityActionType type,
|
||||
InterfaceDynamicFactory() : EntityDynamicFactoryInterface() { }
|
||||
virtual ~InterfaceDynamicFactory() { }
|
||||
virtual EntityDynamicPointer factory(EntityDynamicType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) override;
|
||||
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity,
|
||||
virtual EntityDynamicPointer factoryBA(EntityItemPointer ownerEntity,
|
||||
QByteArray data) override;
|
||||
};
|
||||
|
||||
#endif // hifi_InterfaceActionFactory_h
|
||||
#endif // hifi_InterfaceDynamicFactory_h
|
64
interface/src/avatar/AvatarActionFarGrab.cpp
Normal file
64
interface/src/avatar/AvatarActionFarGrab.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// AvatarActionFarGrab.cpp
|
||||
// interface/src/avatar/
|
||||
//
|
||||
// Created by Seth Alves 2017-4-14
|
||||
// Copyright 2017 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 "AvatarActionFarGrab.h"
|
||||
|
||||
AvatarActionFarGrab::AvatarActionFarGrab(const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectActionSpring(id, ownerEntity) {
|
||||
_type = DYNAMIC_TYPE_FAR_GRAB;
|
||||
#if WANT_DEBUG
|
||||
qDebug() << "AvatarActionFarGrab::AvatarActionFarGrab";
|
||||
#endif
|
||||
}
|
||||
|
||||
AvatarActionFarGrab::~AvatarActionFarGrab() {
|
||||
#if WANT_DEBUG
|
||||
qDebug() << "AvatarActionFarGrab::~AvatarActionFarGrab";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
QByteArray AvatarActionFarGrab::serialize() const {
|
||||
QByteArray serializedActionArguments;
|
||||
QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly);
|
||||
|
||||
dataStream << DYNAMIC_TYPE_FAR_GRAB;
|
||||
dataStream << getID();
|
||||
dataStream << ObjectActionSpring::springVersion;
|
||||
|
||||
serializeParameters(dataStream);
|
||||
|
||||
return serializedActionArguments;
|
||||
}
|
||||
|
||||
void AvatarActionFarGrab::deserialize(QByteArray serializedArguments) {
|
||||
QDataStream dataStream(serializedArguments);
|
||||
|
||||
EntityDynamicType type;
|
||||
dataStream >> type;
|
||||
|
||||
QUuid id;
|
||||
dataStream >> id;
|
||||
|
||||
if (type != getType() || id != getID()) {
|
||||
qDebug() << "AvatarActionFarGrab::deserialize type or ID don't match." << type << id << getID();
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t serializationVersion;
|
||||
dataStream >> serializationVersion;
|
||||
if (serializationVersion != ObjectActionSpring::springVersion) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
deserializeParameters(serializedArguments, dataStream);
|
||||
}
|
27
interface/src/avatar/AvatarActionFarGrab.h
Normal file
27
interface/src/avatar/AvatarActionFarGrab.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// AvatarActionFarGrab.h
|
||||
// interface/src/avatar/
|
||||
//
|
||||
// Created by Seth Alves 2017-4-14
|
||||
// Copyright 2017 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_AvatarActionFarGrab_h
|
||||
#define hifi_AvatarActionFarGrab_h
|
||||
|
||||
#include <EntityItem.h>
|
||||
#include <ObjectActionSpring.h>
|
||||
|
||||
class AvatarActionFarGrab : public ObjectActionSpring {
|
||||
public:
|
||||
AvatarActionFarGrab(const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~AvatarActionFarGrab();
|
||||
|
||||
QByteArray serialize() const override;
|
||||
virtual void deserialize(QByteArray serializedArguments) override;
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarActionFarGrab_h
|
|
@ -23,7 +23,7 @@ const int AvatarActionHold::velocitySmoothFrames = 6;
|
|||
AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectActionSpring(id, ownerEntity)
|
||||
{
|
||||
_type = ACTION_TYPE_HOLD;
|
||||
_type = DYNAMIC_TYPE_HOLD;
|
||||
_measuredLinearVelocities.resize(AvatarActionHold::velocitySmoothFrames);
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
@ -323,28 +323,28 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
|
|||
bool ignoreIK;
|
||||
bool needUpdate = false;
|
||||
|
||||
bool somethingChanged = ObjectAction::updateArguments(arguments);
|
||||
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
|
||||
withReadLock([&]{
|
||||
bool ok = true;
|
||||
relativePosition = EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false);
|
||||
relativePosition = EntityDynamicInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false);
|
||||
if (!ok) {
|
||||
relativePosition = _relativePosition;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
relativeRotation = EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false);
|
||||
relativeRotation = EntityDynamicInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false);
|
||||
if (!ok) {
|
||||
relativeRotation = _relativeRotation;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
timeScale = EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false);
|
||||
timeScale = EntityDynamicInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false);
|
||||
if (!ok) {
|
||||
timeScale = _linearTimeScale;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
hand = EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false);
|
||||
hand = EntityDynamicInterface::extractStringArgument("hold", arguments, "hand", ok, false);
|
||||
if (!ok || !(hand == "left" || hand == "right")) {
|
||||
hand = _hand;
|
||||
}
|
||||
|
@ -353,20 +353,20 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
|
|||
holderID = myAvatar->getSessionUUID();
|
||||
|
||||
ok = true;
|
||||
kinematic = EntityActionInterface::extractBooleanArgument("hold", arguments, "kinematic", ok, false);
|
||||
kinematic = EntityDynamicInterface::extractBooleanArgument("hold", arguments, "kinematic", ok, false);
|
||||
if (!ok) {
|
||||
kinematic = _kinematic;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
kinematicSetVelocity = EntityActionInterface::extractBooleanArgument("hold", arguments,
|
||||
kinematicSetVelocity = EntityDynamicInterface::extractBooleanArgument("hold", arguments,
|
||||
"kinematicSetVelocity", ok, false);
|
||||
if (!ok) {
|
||||
kinematicSetVelocity = _kinematicSetVelocity;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
ignoreIK = EntityActionInterface::extractBooleanArgument("hold", arguments, "ignoreIK", ok, false);
|
||||
ignoreIK = EntityDynamicInterface::extractBooleanArgument("hold", arguments, "ignoreIK", ok, false);
|
||||
if (!ok) {
|
||||
ignoreIK = _ignoreIK;
|
||||
}
|
||||
|
@ -400,8 +400,8 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
|
|||
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setActionDataDirty(true);
|
||||
ownerEntity->setActionDataNeedsTransmit(true);
|
||||
ownerEntity->setDynamicDataDirty(true);
|
||||
ownerEntity->setDynamicDataNeedsTransmit(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -410,7 +410,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
|
|||
}
|
||||
|
||||
QVariantMap AvatarActionHold::getArguments() {
|
||||
QVariantMap arguments = ObjectAction::getArguments();
|
||||
QVariantMap arguments = ObjectDynamic::getArguments();
|
||||
withReadLock([&]{
|
||||
arguments["holderID"] = _holderID;
|
||||
arguments["relativePosition"] = glmToQMap(_relativePosition);
|
||||
|
@ -429,7 +429,7 @@ QByteArray AvatarActionHold::serialize() const {
|
|||
QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly);
|
||||
|
||||
withReadLock([&]{
|
||||
dataStream << ACTION_TYPE_HOLD;
|
||||
dataStream << DYNAMIC_TYPE_HOLD;
|
||||
dataStream << getID();
|
||||
dataStream << AvatarActionHold::holdVersion;
|
||||
|
||||
|
@ -451,7 +451,7 @@ QByteArray AvatarActionHold::serialize() const {
|
|||
void AvatarActionHold::deserialize(QByteArray serializedArguments) {
|
||||
QDataStream dataStream(serializedArguments);
|
||||
|
||||
EntityActionType type;
|
||||
EntityDynamicType type;
|
||||
dataStream >> type;
|
||||
assert(type == getType());
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
|
|||
PROFILE_RANGE(app, __FUNCTION__);
|
||||
|
||||
if (!_uiTexture) {
|
||||
_uiTexture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()));
|
||||
_uiTexture = gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda());
|
||||
_uiTexture->setSource(__FUNCTION__);
|
||||
}
|
||||
// Once we move UI rendering and screen rendering to different
|
||||
|
@ -207,13 +207,13 @@ void ApplicationOverlay::buildFramebufferObject() {
|
|||
auto width = uiSize.x;
|
||||
auto height = uiSize.y;
|
||||
if (!_overlayFramebuffer->getDepthStencilBuffer()) {
|
||||
auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(DEPTH_FORMAT, width, height, gpu::Texture::SINGLE_MIP, DEFAULT_SAMPLER));
|
||||
auto overlayDepthTexture = gpu::Texture::createRenderBuffer(DEPTH_FORMAT, width, height, gpu::Texture::SINGLE_MIP, DEFAULT_SAMPLER);
|
||||
_overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT);
|
||||
}
|
||||
|
||||
if (!_overlayFramebuffer->getRenderBuffer(0)) {
|
||||
const gpu::Sampler OVERLAY_SAMPLER(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP);
|
||||
auto colorBuffer = gpu::TexturePointer(gpu::Texture::createRenderBuffer(COLOR_FORMAT, width, height, gpu::Texture::SINGLE_MIP, OVERLAY_SAMPLER));
|
||||
auto colorBuffer = gpu::Texture::createRenderBuffer(COLOR_FORMAT, width, height, gpu::Texture::SINGLE_MIP, OVERLAY_SAMPLER);
|
||||
_overlayFramebuffer->setRenderBuffer(0, colorBuffer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "Menu.h"
|
||||
#include "Util.h"
|
||||
#include "SequenceNumberStats.h"
|
||||
#include "StatTracker.h"
|
||||
|
||||
HIFI_QML_DEF(Stats)
|
||||
|
||||
|
@ -250,6 +251,9 @@ void Stats::updateStats(bool force) {
|
|||
STAT_UPDATE(downloads, loadingRequests.size());
|
||||
STAT_UPDATE(downloadLimit, ResourceCache::getRequestLimit())
|
||||
STAT_UPDATE(downloadsPending, ResourceCache::getPendingRequestCount());
|
||||
STAT_UPDATE(processing, DependencyManager::get<StatTracker>()->getStat("Processing").toInt());
|
||||
STAT_UPDATE(processingPending, DependencyManager::get<StatTracker>()->getStat("PendingProcessing").toInt());
|
||||
|
||||
|
||||
// See if the active download urls have changed
|
||||
bool shouldUpdateUrls = _downloads != _downloadUrls.size();
|
||||
|
|
|
@ -89,6 +89,8 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(int, downloadLimit, 0)
|
||||
STATS_PROPERTY(int, downloadsPending, 0)
|
||||
Q_PROPERTY(QStringList downloadUrls READ downloadUrls NOTIFY downloadUrlsChanged)
|
||||
STATS_PROPERTY(int, processing, 0)
|
||||
STATS_PROPERTY(int, processingPending, 0)
|
||||
STATS_PROPERTY(int, triangles, 0)
|
||||
STATS_PROPERTY(int, quads, 0)
|
||||
STATS_PROPERTY(int, materialSwitches, 0)
|
||||
|
@ -214,6 +216,8 @@ signals:
|
|||
void downloadLimitChanged();
|
||||
void downloadsPendingChanged();
|
||||
void downloadUrlsChanged();
|
||||
void processingChanged();
|
||||
void processingPendingChanged();
|
||||
void trianglesChanged();
|
||||
void quadsChanged();
|
||||
void materialSwitchesChanged();
|
||||
|
|
|
@ -298,7 +298,7 @@ void Web3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
if (!_texture) {
|
||||
auto webSurface = _webSurface;
|
||||
_texture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()));
|
||||
_texture = gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda());
|
||||
_texture->setSource(__FUNCTION__);
|
||||
}
|
||||
OffscreenQmlSurface::TextureAndFence newTextureAndFence;
|
||||
|
|
|
@ -1006,8 +1006,7 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
|
|||
_timeSinceLastClip += (float)numSamples / (float)AudioConstants::SAMPLE_RATE;
|
||||
}
|
||||
|
||||
emit inputReceived({ audioBuffer.data(), numSamples });
|
||||
emit inputReceivedBuffer(audioBuffer); // FIXME: Almost a duplicate of inputReceived().
|
||||
emit inputReceived(audioBuffer);
|
||||
|
||||
if (_noiseGate.openedInLastBlock()) {
|
||||
emit noiseGateOpened();
|
||||
|
|
|
@ -354,12 +354,11 @@ void OpenGLDisplayPlugin::customizeContext() {
|
|||
}
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
|
||||
cursorData.texture.reset(
|
||||
gpu::Texture::createStrict(
|
||||
cursorData.texture = gpu::Texture::createStrict(
|
||||
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
|
||||
image.width(), image.height(),
|
||||
gpu::Texture::MAX_NUM_MIPS,
|
||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
|
||||
cursorData.texture->setSource("cursor texture");
|
||||
auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha();
|
||||
cursorData.texture->setUsage(usage.build());
|
||||
|
|
|
@ -295,12 +295,11 @@ void HmdDisplayPlugin::internalPresent() {
|
|||
image = image.mirrored();
|
||||
image = image.convertToFormat(QImage::Format_RGBA8888);
|
||||
if (!_previewTexture) {
|
||||
_previewTexture.reset(
|
||||
gpu::Texture::createStrict(
|
||||
_previewTexture = gpu::Texture::createStrict(
|
||||
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
|
||||
image.width(), image.height(),
|
||||
gpu::Texture::MAX_NUM_MIPS,
|
||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
|
||||
_previewTexture->setSource("HMD Preview Texture");
|
||||
_previewTexture->setUsage(gpu::Texture::Usage::Builder().withColor().build());
|
||||
_previewTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
set(TARGET_NAME entities-renderer)
|
||||
AUTOSCRIBE_SHADER_LIB(gpu model procedural render render-utils)
|
||||
setup_hifi_library(Widgets Network Script)
|
||||
link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils)
|
||||
link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils image)
|
||||
|
||||
target_bullet()
|
||||
|
||||
|
|
|
@ -495,7 +495,7 @@ bool EntityTreeRenderer::applySkyboxAndHasAmbient() {
|
|||
|
||||
bool isAmbientSet = false;
|
||||
if (_pendingAmbientTexture && !_ambientTexture) {
|
||||
_ambientTexture = textureCache->getTexture(_ambientTextureURL, NetworkTexture::CUBE_TEXTURE);
|
||||
_ambientTexture = textureCache->getTexture(_ambientTextureURL, image::TextureUsage::CUBE_TEXTURE);
|
||||
}
|
||||
if (_ambientTexture && _ambientTexture->isLoaded()) {
|
||||
_pendingAmbientTexture = false;
|
||||
|
@ -512,7 +512,7 @@ bool EntityTreeRenderer::applySkyboxAndHasAmbient() {
|
|||
|
||||
if (_pendingSkyboxTexture &&
|
||||
(!_skyboxTexture || (_skyboxTexture->getURL() != _skyboxTextureURL))) {
|
||||
_skyboxTexture = textureCache->getTexture(_skyboxTextureURL, NetworkTexture::CUBE_TEXTURE);
|
||||
_skyboxTexture = textureCache->getTexture(_skyboxTextureURL, image::TextureUsage::CUBE_TEXTURE);
|
||||
}
|
||||
if (_skyboxTexture && _skyboxTexture->isLoaded()) {
|
||||
_pendingSkyboxTexture = false;
|
||||
|
|
|
@ -216,7 +216,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
|
|||
|
||||
if (!_texture) {
|
||||
auto webSurface = _webSurface;
|
||||
_texture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()));
|
||||
_texture = gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda());
|
||||
_texture->setSource(__FUNCTION__);
|
||||
}
|
||||
OffscreenQmlSurface::TextureAndFence newTextureAndFence;
|
||||
|
|
|
@ -1,332 +0,0 @@
|
|||
//
|
||||
// EntityActionInterface.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-4
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
|
||||
|
||||
|
||||
+-----------------------+ +-------------------+ +------------------------------+
|
||||
| | | | | |
|
||||
| EntityActionInterface | | btActionInterface | | EntityActionFactoryInterface |
|
||||
| (entities) | | (bullet) | | (entities) |
|
||||
+-----------------------+ +-------------------+ +------------------------------+
|
||||
| | | | |
|
||||
+----+ +--+ +----------+ | |
|
||||
| | | | |
|
||||
+-------------------+ +--------------+ +------------------------+ +-------------------------+
|
||||
| | | | | | | |
|
||||
| AssignmentAction | | ObjectAction | | InterfaceActionFactory | | AssignmentActionFactory |
|
||||
|(assignment client)| | (physics) | | (interface) | | (assignment client) |
|
||||
+-------------------+ +--------------+ +------------------------+ +-------------------------+
|
||||
|
|
||||
|
|
||||
|
|
||||
+--------------------+
|
||||
| |
|
||||
| ObjectActionSpring |
|
||||
| (physics) |
|
||||
+--------------------+
|
||||
|
||||
|
||||
|
||||
|
||||
An action is a callback which is registered with bullet. An action is called-back every physics
|
||||
simulation step and can do whatever it wants with the various datastructures it has available. An
|
||||
action, for example, can pull an EntityItem toward a point as if that EntityItem were connected to that
|
||||
point by a spring.
|
||||
|
||||
In this system, an action is a property of an EntityItem (rather, an EntityItem has a property which
|
||||
encodes a list of actions). Each action has a type and some arguments. Actions can be created by a
|
||||
script or when receiving information via an EntityTree data-stream (either over the network or from an
|
||||
svo file).
|
||||
|
||||
In the interface, if an EntityItem has actions, this EntityItem will have pointers to ObjectAction
|
||||
subclass (like ObjectActionSpring) instantiations. Code in the entities library affects an action-object
|
||||
via the EntityActionInterface (which knows nothing about bullet). When the ObjectAction subclass
|
||||
instance is created, it is registered as an action with bullet. Bullet will call into code in this
|
||||
instance with the btActionInterface every physics-simulation step.
|
||||
|
||||
Because the action can exist next to the interface's EntityTree or the entity-server's EntityTree,
|
||||
parallel versions of the factories and actions are needed.
|
||||
|
||||
In an entity-server, any type of action is instantiated as an AssignmentAction. This action isn't called
|
||||
by bullet (which isn't part of an assignment-client). It does nothing but remember its type and its
|
||||
arguments. This may change as we try to make the entity-server's simple physics simulation better, but
|
||||
right now the AssignmentAction class is a place-holder.
|
||||
|
||||
The action-objects are instantiated by singleton (dependecy) subclasses of EntityActionFactoryInterface.
|
||||
In the interface, the subclass is an InterfaceActionFactory and it will produce things like
|
||||
ObjectActionSpring. In an entity-server the subclass is an AssignmentActionFactory and it always
|
||||
produces AssignmentActions.
|
||||
|
||||
Depending on the action's type, it will have various arguments. When a script changes an argument of an
|
||||
action, the argument-holding member-variables of ObjectActionSpring (in this example) are updated and
|
||||
also serialized into _actionData in the EntityItem. Each subclass of ObjectAction knows how to serialize
|
||||
and deserialize its own arguments. _actionData is what gets sent over the wire or saved in an svo file.
|
||||
When a packet-reader receives data for _actionData, it will save it in the EntityItem; this causes the
|
||||
deserializer in the ObjectAction subclass to be called with the new data, thereby updating its argument
|
||||
variables. These argument variables are used by the code which is run when bullet does a callback.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#include "EntityItem.h"
|
||||
|
||||
#include "EntityActionInterface.h"
|
||||
|
||||
|
||||
EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeString) {
|
||||
QString normalizedActionTypeString = actionTypeString.toLower().remove('-').remove('_');
|
||||
if (normalizedActionTypeString == "none") {
|
||||
return ACTION_TYPE_NONE;
|
||||
}
|
||||
if (normalizedActionTypeString == "offset") {
|
||||
return ACTION_TYPE_OFFSET;
|
||||
}
|
||||
if (normalizedActionTypeString == "spring") {
|
||||
return ACTION_TYPE_SPRING;
|
||||
}
|
||||
if (normalizedActionTypeString == "hold") {
|
||||
return ACTION_TYPE_HOLD;
|
||||
}
|
||||
if (normalizedActionTypeString == "traveloriented") {
|
||||
return ACTION_TYPE_TRAVEL_ORIENTED;
|
||||
}
|
||||
|
||||
qCDebug(entities) << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString;
|
||||
return ACTION_TYPE_NONE;
|
||||
}
|
||||
|
||||
QString EntityActionInterface::actionTypeToString(EntityActionType actionType) {
|
||||
switch(actionType) {
|
||||
case ACTION_TYPE_NONE:
|
||||
return "none";
|
||||
case ACTION_TYPE_OFFSET:
|
||||
return "offset";
|
||||
case ACTION_TYPE_SPRING:
|
||||
return "spring";
|
||||
case ACTION_TYPE_HOLD:
|
||||
return "hold";
|
||||
case ACTION_TYPE_TRAVEL_ORIENTED:
|
||||
return "travel-oriented";
|
||||
}
|
||||
assert(false);
|
||||
return "none";
|
||||
}
|
||||
|
||||
glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
QVariant resultV = arguments[argumentName];
|
||||
if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map";
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
QVariantMap resultVM = resultV.toMap();
|
||||
if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z";
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
QVariant xV = resultVM["x"];
|
||||
QVariant yV = resultVM["y"];
|
||||
QVariant zV = resultVM["z"];
|
||||
|
||||
bool xOk = true;
|
||||
bool yOk = true;
|
||||
bool zOk = true;
|
||||
float x = xV.toFloat(&xOk);
|
||||
float y = yV.toFloat(&yOk);
|
||||
float z = zV.toFloat(&zOk);
|
||||
if (!xOk || !yOk || !zOk) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, and z of type float.";
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
if (x != x || y != y || z != z) {
|
||||
// at least one of the values is NaN
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
return glm::vec3(x, y, z);
|
||||
}
|
||||
|
||||
glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
QVariant resultV = arguments[argumentName];
|
||||
if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map, not" << resultV.typeName();
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
QVariantMap resultVM = resultV.toMap();
|
||||
if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z") || !resultVM.contains("w")) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z, and w";
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
QVariant xV = resultVM["x"];
|
||||
QVariant yV = resultVM["y"];
|
||||
QVariant zV = resultVM["z"];
|
||||
QVariant wV = resultVM["w"];
|
||||
|
||||
bool xOk = true;
|
||||
bool yOk = true;
|
||||
bool zOk = true;
|
||||
bool wOk = true;
|
||||
float x = xV.toFloat(&xOk);
|
||||
float y = yV.toFloat(&yOk);
|
||||
float z = zV.toFloat(&zOk);
|
||||
float w = wV.toFloat(&wOk);
|
||||
if (!xOk || !yOk || !zOk || !wOk) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName
|
||||
<< "must be a map with keys: x, y, z, and w of type float.";
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
if (x != x || y != y || z != z || w != w) {
|
||||
// at least one of the components is NaN!
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
return glm::normalize(glm::quat(w, x, y, z));
|
||||
}
|
||||
|
||||
float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
QVariant variant = arguments[argumentName];
|
||||
bool variantOk = true;
|
||||
float value = variant.toFloat(&variantOk);
|
||||
|
||||
if (!variantOk || std::isnan(value)) {
|
||||
ok = false;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
int EntityActionInterface::extractIntegerArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
QVariant variant = arguments[argumentName];
|
||||
bool variantOk = true;
|
||||
int value = variant.toInt(&variantOk);
|
||||
|
||||
if (!variantOk) {
|
||||
ok = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
QString EntityActionInterface::extractStringArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return "";
|
||||
}
|
||||
return arguments[argumentName].toString();
|
||||
}
|
||||
|
||||
bool EntityActionInterface::extractBooleanArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return false;
|
||||
}
|
||||
return arguments[argumentName].toBool();
|
||||
}
|
||||
|
||||
|
||||
|
||||
QDataStream& operator<<(QDataStream& stream, const EntityActionType& entityActionType)
|
||||
{
|
||||
return stream << (quint16)entityActionType;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream& stream, EntityActionType& entityActionType)
|
||||
{
|
||||
quint16 actionTypeAsInt;
|
||||
stream >> actionTypeAsInt;
|
||||
entityActionType = (EntityActionType)actionTypeAsInt;
|
||||
return stream;
|
||||
}
|
||||
|
||||
QString serializedActionsToDebugString(QByteArray data) {
|
||||
if (data.size() == 0) {
|
||||
return QString();
|
||||
}
|
||||
QVector<QByteArray> serializedActions;
|
||||
QDataStream serializedActionsStream(data);
|
||||
serializedActionsStream >> serializedActions;
|
||||
|
||||
QString result;
|
||||
foreach(QByteArray serializedAction, serializedActions) {
|
||||
QDataStream serializedActionStream(serializedAction);
|
||||
EntityActionType actionType;
|
||||
QUuid actionID;
|
||||
serializedActionStream >> actionType;
|
||||
serializedActionStream >> actionID;
|
||||
result += EntityActionInterface::actionTypeToString(actionType) + "-" + actionID.toString() + " ";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// EntityActionFactoryInterface.cpp
|
||||
// EntityDynamicFactoryInterface.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-2
|
||||
|
@ -9,26 +9,26 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_EntityActionFactoryInterface_h
|
||||
#define hifi_EntityActionFactoryInterface_h
|
||||
#ifndef hifi_EntityDynamicFactoryInterface_h
|
||||
#define hifi_EntityDynamicFactoryInterface_h
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "EntityActionInterface.h"
|
||||
#include "EntityDynamicInterface.h"
|
||||
|
||||
class EntityActionFactoryInterface : public QObject, public Dependency {
|
||||
class EntityDynamicFactoryInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
EntityActionFactoryInterface() { }
|
||||
virtual ~EntityActionFactoryInterface() { }
|
||||
virtual EntityActionPointer factory(EntityActionType type,
|
||||
EntityDynamicFactoryInterface() { }
|
||||
virtual ~EntityDynamicFactoryInterface() { }
|
||||
virtual EntityDynamicPointer factory(EntityDynamicType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) { assert(false); return nullptr; }
|
||||
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity,
|
||||
virtual EntityDynamicPointer factoryBA(EntityItemPointer ownerEntity,
|
||||
QByteArray data) { assert(false); return nullptr; }
|
||||
};
|
||||
|
||||
#endif // hifi_EntityActionFactoryInterface_h
|
||||
#endif // hifi_EntityDynamicFactoryInterface_h
|
347
libraries/entities/src/EntityDynamicInterface.cpp
Normal file
347
libraries/entities/src/EntityDynamicInterface.cpp
Normal file
|
@ -0,0 +1,347 @@
|
|||
//
|
||||
// EntityDynamicInterface.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-4
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
+-------------------------+ +--------------------------------+
|
||||
| | | |
|
||||
| EntityDynamicsInterface | | EntityDynamicsFactoryInterface |
|
||||
| (entities) | | (entities) |
|
||||
+-------------------------+ +--------------------------------+
|
||||
| | | |
|
||||
+----+ +--+ | |
|
||||
| | | |
|
||||
+---------------------+ +----------------+ +--------------------------+ +---------------------------+
|
||||
| | | | | | | |
|
||||
| AssignmentDynamics | | ObjectDynamics | | InterfaceDynamicsFactory | | AssignmentDynamicsFactory |
|
||||
|(assignment client) | | (physics) | | (interface) | | (assignment client) |
|
||||
+---------------------+ +----------------+ +--------------------------+ +---------------------------+
|
||||
| |
|
||||
| |
|
||||
+---------------------+ | |
|
||||
| | | |
|
||||
| btActionInterface | | |
|
||||
| (bullet) | | |
|
||||
+---------------------+ | |
|
||||
| | |
|
||||
+--------------+ +------------------+ +-------------------+
|
||||
| | | | | |
|
||||
| ObjectAction | | ObjectConstraint | | btTypedConstraint |
|
||||
| (physics) | | (physics) --|--->| (bullet) |
|
||||
+--------------+ +------------------+ +-------------------+
|
||||
| |
|
||||
| |
|
||||
+--------------------+ +-----------------------+
|
||||
| | | |
|
||||
| ObjectActionSpring | | ObjectConstraintHinge |
|
||||
| (physics) | | (physics) |
|
||||
+--------------------+ +-----------------------+
|
||||
|
||||
|
||||
|
||||
An dynamic is a callback which is registered with bullet. An dynamic is called-back every physics
|
||||
simulation step and can do whatever it wants with the various datastructures it has available. An
|
||||
dynamic, for example, can pull an EntityItem toward a point as if that EntityItem were connected to that
|
||||
point by a spring.
|
||||
|
||||
In this system, an dynamic is a property of an EntityItem (rather, an EntityItem has a property which
|
||||
encodes a list of dynamics). Each dynamic has a type and some arguments. Dynamics can be created by a
|
||||
script or when receiving information via an EntityTree data-stream (either over the network or from an
|
||||
svo file).
|
||||
|
||||
In the interface, if an EntityItem has dynamics, this EntityItem will have pointers to ObjectDynamic
|
||||
subclass (like ObjectDynamicSpring) instantiations. Code in the entities library affects an dynamic-object
|
||||
via the EntityDynamicInterface (which knows nothing about bullet). When the ObjectDynamic subclass
|
||||
instance is created, it is registered as an dynamic with bullet. Bullet will call into code in this
|
||||
instance with the btDynamicInterface every physics-simulation step.
|
||||
|
||||
Because the dynamic can exist next to the interface's EntityTree or the entity-server's EntityTree,
|
||||
parallel versions of the factories and dynamics are needed.
|
||||
|
||||
In an entity-server, any type of dynamic is instantiated as an AssignmentDynamic. This dynamic isn't called
|
||||
by bullet (which isn't part of an assignment-client). It does nothing but remember its type and its
|
||||
arguments. This may change as we try to make the entity-server's simple physics simulation better, but
|
||||
right now the AssignmentDynamic class is a place-holder.
|
||||
|
||||
The dynamic-objects are instantiated by singleton (dependecy) subclasses of EntityDynamicFactoryInterface.
|
||||
In the interface, the subclass is an InterfaceDynamicFactory and it will produce things like
|
||||
ObjectDynamicSpring. In an entity-server the subclass is an AssignmentDynamicFactory and it always
|
||||
produces AssignmentDynamics.
|
||||
|
||||
Depending on the dynamic's type, it will have various arguments. When a script changes an argument of an
|
||||
dynamic, the argument-holding member-variables of ObjectDynamicSpring (in this example) are updated and
|
||||
also serialized into _dynamicData in the EntityItem. Each subclass of ObjectDynamic knows how to serialize
|
||||
and deserialize its own arguments. _dynamicData is what gets sent over the wire or saved in an svo file.
|
||||
When a packet-reader receives data for _dynamicData, it will save it in the EntityItem; this causes the
|
||||
deserializer in the ObjectDynamic subclass to be called with the new data, thereby updating its argument
|
||||
variables. These argument variables are used by the code which is run when bullet does a callback.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#include "EntityItem.h"
|
||||
|
||||
#include "EntityDynamicInterface.h"
|
||||
|
||||
|
||||
EntityDynamicType EntityDynamicInterface::dynamicTypeFromString(QString dynamicTypeString) {
|
||||
QString normalizedDynamicTypeString = dynamicTypeString.toLower().remove('-').remove('_');
|
||||
if (normalizedDynamicTypeString == "none") {
|
||||
return DYNAMIC_TYPE_NONE;
|
||||
}
|
||||
if (normalizedDynamicTypeString == "offset") {
|
||||
return DYNAMIC_TYPE_OFFSET;
|
||||
}
|
||||
if (normalizedDynamicTypeString == "spring") {
|
||||
return DYNAMIC_TYPE_SPRING;
|
||||
}
|
||||
if (normalizedDynamicTypeString == "hold") {
|
||||
return DYNAMIC_TYPE_HOLD;
|
||||
}
|
||||
if (normalizedDynamicTypeString == "traveloriented") {
|
||||
return DYNAMIC_TYPE_TRAVEL_ORIENTED;
|
||||
}
|
||||
if (normalizedDynamicTypeString == "hinge") {
|
||||
return DYNAMIC_TYPE_HINGE;
|
||||
}
|
||||
if (normalizedDynamicTypeString == "fargrab") {
|
||||
return DYNAMIC_TYPE_FAR_GRAB;
|
||||
}
|
||||
|
||||
qCDebug(entities) << "Warning -- EntityDynamicInterface::dynamicTypeFromString got unknown dynamic-type name"
|
||||
<< dynamicTypeString;
|
||||
return DYNAMIC_TYPE_NONE;
|
||||
}
|
||||
|
||||
QString EntityDynamicInterface::dynamicTypeToString(EntityDynamicType dynamicType) {
|
||||
switch(dynamicType) {
|
||||
case DYNAMIC_TYPE_NONE:
|
||||
return "none";
|
||||
case DYNAMIC_TYPE_OFFSET:
|
||||
return "offset";
|
||||
case DYNAMIC_TYPE_SPRING:
|
||||
return "spring";
|
||||
case DYNAMIC_TYPE_HOLD:
|
||||
return "hold";
|
||||
case DYNAMIC_TYPE_TRAVEL_ORIENTED:
|
||||
return "travel-oriented";
|
||||
case DYNAMIC_TYPE_HINGE:
|
||||
return "hinge";
|
||||
case DYNAMIC_TYPE_FAR_GRAB:
|
||||
return "far-grab";
|
||||
}
|
||||
assert(false);
|
||||
return "none";
|
||||
}
|
||||
|
||||
glm::vec3 EntityDynamicInterface::extractVec3Argument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
QVariant resultV = arguments[argumentName];
|
||||
if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map";
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
QVariantMap resultVM = resultV.toMap();
|
||||
if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z";
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
QVariant xV = resultVM["x"];
|
||||
QVariant yV = resultVM["y"];
|
||||
QVariant zV = resultVM["z"];
|
||||
|
||||
bool xOk = true;
|
||||
bool yOk = true;
|
||||
bool zOk = true;
|
||||
float x = xV.toFloat(&xOk);
|
||||
float y = yV.toFloat(&yOk);
|
||||
float z = zV.toFloat(&zOk);
|
||||
if (!xOk || !yOk || !zOk) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, and z of type float.";
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
if (x != x || y != y || z != z) {
|
||||
// at least one of the values is NaN
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
return glm::vec3(x, y, z);
|
||||
}
|
||||
|
||||
glm::quat EntityDynamicInterface::extractQuatArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
QVariant resultV = arguments[argumentName];
|
||||
if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map, not" << resultV.typeName();
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
QVariantMap resultVM = resultV.toMap();
|
||||
if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z") || !resultVM.contains("w")) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z, and w";
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
QVariant xV = resultVM["x"];
|
||||
QVariant yV = resultVM["y"];
|
||||
QVariant zV = resultVM["z"];
|
||||
QVariant wV = resultVM["w"];
|
||||
|
||||
bool xOk = true;
|
||||
bool yOk = true;
|
||||
bool zOk = true;
|
||||
bool wOk = true;
|
||||
float x = xV.toFloat(&xOk);
|
||||
float y = yV.toFloat(&yOk);
|
||||
float z = zV.toFloat(&zOk);
|
||||
float w = wV.toFloat(&wOk);
|
||||
if (!xOk || !yOk || !zOk || !wOk) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName
|
||||
<< "must be a map with keys: x, y, z, and w of type float.";
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
if (x != x || y != y || z != z || w != w) {
|
||||
// at least one of the components is NaN!
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
return glm::normalize(glm::quat(w, x, y, z));
|
||||
}
|
||||
|
||||
float EntityDynamicInterface::extractFloatArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
QVariant variant = arguments[argumentName];
|
||||
bool variantOk = true;
|
||||
float value = variant.toFloat(&variantOk);
|
||||
|
||||
if (!variantOk || std::isnan(value)) {
|
||||
ok = false;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
int EntityDynamicInterface::extractIntegerArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
QVariant variant = arguments[argumentName];
|
||||
bool variantOk = true;
|
||||
int value = variant.toInt(&variantOk);
|
||||
|
||||
if (!variantOk) {
|
||||
ok = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
QString EntityDynamicInterface::extractStringArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return "";
|
||||
}
|
||||
return arguments[argumentName].toString();
|
||||
}
|
||||
|
||||
bool EntityDynamicInterface::extractBooleanArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return false;
|
||||
}
|
||||
return arguments[argumentName].toBool();
|
||||
}
|
||||
|
||||
QDataStream& operator<<(QDataStream& stream, const EntityDynamicType& entityDynamicType) {
|
||||
return stream << (quint16)entityDynamicType;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream& stream, EntityDynamicType& entityDynamicType) {
|
||||
quint16 dynamicTypeAsInt;
|
||||
stream >> dynamicTypeAsInt;
|
||||
entityDynamicType = (EntityDynamicType)dynamicTypeAsInt;
|
||||
return stream;
|
||||
}
|
||||
|
||||
QString serializedDynamicsToDebugString(QByteArray data) {
|
||||
if (data.size() == 0) {
|
||||
return QString();
|
||||
}
|
||||
QVector<QByteArray> serializedDynamics;
|
||||
QDataStream serializedDynamicsStream(data);
|
||||
serializedDynamicsStream >> serializedDynamics;
|
||||
|
||||
QString result;
|
||||
foreach(QByteArray serializedDynamic, serializedDynamics) {
|
||||
QDataStream serializedDynamicStream(serializedDynamic);
|
||||
EntityDynamicType dynamicType;
|
||||
QUuid dynamicID;
|
||||
serializedDynamicStream >> dynamicType;
|
||||
serializedDynamicStream >> dynamicID;
|
||||
result += EntityDynamicInterface::dynamicTypeToString(dynamicType) + "-" + dynamicID.toString() + " ";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// EntityActionInterface.h
|
||||
// EntityDynamicInterface.h
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-2
|
||||
|
@ -9,8 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_EntityActionInterface_h
|
||||
#define hifi_EntityActionInterface_h
|
||||
#ifndef hifi_EntityDynamicInterface_h
|
||||
#define hifi_EntityDynamicInterface_h
|
||||
|
||||
#include <memory>
|
||||
#include <QUuid>
|
||||
|
@ -23,22 +23,27 @@ using EntityItemWeakPointer = std::weak_ptr<EntityItem>;
|
|||
class EntitySimulation;
|
||||
using EntitySimulationPointer = std::shared_ptr<EntitySimulation>;
|
||||
|
||||
enum EntityActionType {
|
||||
// keep these synchronized with actionTypeFromString and actionTypeToString
|
||||
ACTION_TYPE_NONE = 0,
|
||||
ACTION_TYPE_OFFSET = 1000,
|
||||
ACTION_TYPE_SPRING = 2000,
|
||||
ACTION_TYPE_HOLD = 3000,
|
||||
ACTION_TYPE_TRAVEL_ORIENTED = 4000
|
||||
enum EntityDynamicType {
|
||||
// keep these synchronized with dynamicTypeFromString and dynamicTypeToString
|
||||
DYNAMIC_TYPE_NONE = 0,
|
||||
DYNAMIC_TYPE_OFFSET = 1000,
|
||||
DYNAMIC_TYPE_SPRING = 2000,
|
||||
DYNAMIC_TYPE_HOLD = 3000,
|
||||
DYNAMIC_TYPE_TRAVEL_ORIENTED = 4000,
|
||||
DYNAMIC_TYPE_HINGE = 5000,
|
||||
DYNAMIC_TYPE_FAR_GRAB = 6000
|
||||
};
|
||||
|
||||
|
||||
class EntityActionInterface {
|
||||
class EntityDynamicInterface {
|
||||
public:
|
||||
EntityActionInterface(EntityActionType type, const QUuid& id) : _id(id), _type(type) { }
|
||||
virtual ~EntityActionInterface() { }
|
||||
EntityDynamicInterface(EntityDynamicType type, const QUuid& id) : _id(id), _type(type) { }
|
||||
virtual ~EntityDynamicInterface() { }
|
||||
const QUuid& getID() const { return _id; }
|
||||
EntityActionType getType() const { return _type; }
|
||||
EntityDynamicType getType() const { return _type; }
|
||||
virtual bool isAction() const { return false; }
|
||||
virtual bool isConstraint() const { return false; }
|
||||
virtual bool isReadyForAdd() const { return true; }
|
||||
|
||||
bool isActive() { return _active; }
|
||||
|
||||
|
@ -51,8 +56,8 @@ public:
|
|||
virtual QByteArray serialize() const = 0;
|
||||
virtual void deserialize(QByteArray serializedArguments) = 0;
|
||||
|
||||
static EntityActionType actionTypeFromString(QString actionTypeString);
|
||||
static QString actionTypeToString(EntityActionType actionType);
|
||||
static EntityDynamicType dynamicTypeFromString(QString dynamicTypeString);
|
||||
static QString dynamicTypeToString(EntityDynamicType dynamicType);
|
||||
|
||||
virtual bool lifetimeIsOver() { return false; }
|
||||
virtual quint64 getExpires() { return 0; }
|
||||
|
@ -82,26 +87,26 @@ public:
|
|||
|
||||
protected:
|
||||
virtual glm::vec3 getPosition() = 0;
|
||||
virtual void setPosition(glm::vec3 position) = 0;
|
||||
// virtual void setPosition(glm::vec3 position) = 0;
|
||||
virtual glm::quat getRotation() = 0;
|
||||
virtual void setRotation(glm::quat rotation) = 0;
|
||||
// virtual void setRotation(glm::quat rotation) = 0;
|
||||
virtual glm::vec3 getLinearVelocity() = 0;
|
||||
virtual void setLinearVelocity(glm::vec3 linearVelocity) = 0;
|
||||
virtual glm::vec3 getAngularVelocity() = 0;
|
||||
virtual void setAngularVelocity(glm::vec3 angularVelocity) = 0;
|
||||
|
||||
QUuid _id;
|
||||
EntityActionType _type;
|
||||
EntityDynamicType _type;
|
||||
bool _active { false };
|
||||
bool _isMine { false }; // did this interface create / edit this action?
|
||||
bool _isMine { false }; // did this interface create / edit this dynamic?
|
||||
};
|
||||
|
||||
|
||||
typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
|
||||
typedef std::shared_ptr<EntityDynamicInterface> EntityDynamicPointer;
|
||||
|
||||
QDataStream& operator<<(QDataStream& stream, const EntityActionType& entityActionType);
|
||||
QDataStream& operator>>(QDataStream& stream, EntityActionType& entityActionType);
|
||||
QDataStream& operator<<(QDataStream& stream, const EntityDynamicType& entityDynamicType);
|
||||
QDataStream& operator>>(QDataStream& stream, EntityDynamicType& entityDynamicType);
|
||||
|
||||
QString serializedActionsToDebugString(QByteArray data);
|
||||
QString serializedDynamicsToDebugString(QByteArray data);
|
||||
|
||||
#endif // hifi_EntityActionInterface_h
|
||||
#endif // hifi_EntityDynamicInterface_h
|
|
@ -30,7 +30,7 @@
|
|||
#include "EntitiesLogging.h"
|
||||
#include "EntityTree.h"
|
||||
#include "EntitySimulation.h"
|
||||
#include "EntityActionFactoryInterface.h"
|
||||
#include "EntityDynamicFactoryInterface.h"
|
||||
|
||||
|
||||
int EntityItem::_maxActionsDataSize = 800;
|
||||
|
@ -280,7 +280,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_HREF, getHref());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, getActionData());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, getDynamicData());
|
||||
|
||||
// convert AVATAR_SELF_ID to actual sessionUUID.
|
||||
QUuid actualParentID = getParentID();
|
||||
|
@ -821,7 +821,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
|
||||
READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref);
|
||||
READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription);
|
||||
READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setActionData);
|
||||
READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setDynamicData);
|
||||
|
||||
{ // parentID and parentJointIndex are also protected by simulation ownership
|
||||
bool oldOverwrite = overwriteLocalData;
|
||||
|
@ -1251,7 +1251,7 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper
|
|||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(description, getDescription);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(actionData, getActionData);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(actionData, getDynamicData);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(parentID, getParentID);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(parentJointIndex, getParentJointIndex);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(queryAACube, getQueryAACube);
|
||||
|
@ -1358,7 +1358,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(actionData, setActionData);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(actionData, setDynamicData);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentID, updateParentID);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentJointIndex, setParentJointIndex);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(queryAACube, setQueryAACube);
|
||||
|
@ -1872,10 +1872,20 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask
|
|||
iAmHoldingThis = true;
|
||||
}
|
||||
// also, don't bootstrap our own avatar with a hold action
|
||||
QList<EntityActionPointer> holdActions = getActionsOfType(ACTION_TYPE_HOLD);
|
||||
QList<EntityActionPointer>::const_iterator i = holdActions.begin();
|
||||
QList<EntityDynamicPointer> holdActions = getActionsOfType(DYNAMIC_TYPE_HOLD);
|
||||
QList<EntityDynamicPointer>::const_iterator i = holdActions.begin();
|
||||
while (i != holdActions.end()) {
|
||||
EntityActionPointer action = *i;
|
||||
EntityDynamicPointer action = *i;
|
||||
if (action->isMine()) {
|
||||
iAmHoldingThis = true;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
QList<EntityDynamicPointer> farGrabActions = getActionsOfType(DYNAMIC_TYPE_FAR_GRAB);
|
||||
i = farGrabActions.begin();
|
||||
while (i != farGrabActions.end()) {
|
||||
EntityDynamicPointer action = *i;
|
||||
if (action->isMine()) {
|
||||
iAmHoldingThis = true;
|
||||
break;
|
||||
|
@ -1941,18 +1951,18 @@ void EntityItem::rememberHasSimulationOwnershipBid() const {
|
|||
QString EntityItem::actionsToDebugString() {
|
||||
QString result;
|
||||
QVector<QByteArray> serializedActions;
|
||||
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
|
||||
QHash<QUuid, EntityDynamicPointer>::const_iterator i = _objectActions.begin();
|
||||
while (i != _objectActions.end()) {
|
||||
const QUuid id = i.key();
|
||||
EntityActionPointer action = _objectActions[id];
|
||||
EntityActionType actionType = action->getType();
|
||||
EntityDynamicPointer action = _objectActions[id];
|
||||
EntityDynamicType actionType = action->getType();
|
||||
result += QString("") + actionType + ":" + action->getID().toString() + " ";
|
||||
i++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EntityItem::addAction(EntitySimulationPointer simulation, EntityActionPointer action) {
|
||||
bool EntityItem::addAction(EntitySimulationPointer simulation, EntityDynamicPointer action) {
|
||||
bool result;
|
||||
withWriteLock([&] {
|
||||
checkWaitingToRemove(simulation);
|
||||
|
@ -1960,7 +1970,7 @@ bool EntityItem::addAction(EntitySimulationPointer simulation, EntityActionPoint
|
|||
result = addActionInternal(simulation, action);
|
||||
if (result) {
|
||||
action->setIsMine(true);
|
||||
_actionDataDirty = true;
|
||||
_dynamicDataDirty = true;
|
||||
} else {
|
||||
removeActionInternal(action->getID());
|
||||
}
|
||||
|
@ -1969,7 +1979,7 @@ bool EntityItem::addAction(EntitySimulationPointer simulation, EntityActionPoint
|
|||
return result;
|
||||
}
|
||||
|
||||
bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityActionPointer action) {
|
||||
bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityDynamicPointer action) {
|
||||
assert(action);
|
||||
assert(simulation);
|
||||
auto actionOwnerEntity = action->getOwnerEntity().lock();
|
||||
|
@ -1979,7 +1989,7 @@ bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityAct
|
|||
const QUuid& actionID = action->getID();
|
||||
assert(!_objectActions.contains(actionID) || _objectActions[actionID] == action);
|
||||
_objectActions[actionID] = action;
|
||||
simulation->addAction(action);
|
||||
simulation->addDynamic(action);
|
||||
|
||||
bool success;
|
||||
QByteArray newDataCache;
|
||||
|
@ -2003,14 +2013,13 @@ bool EntityItem::updateAction(EntitySimulationPointer simulation, const QUuid& a
|
|||
return;
|
||||
}
|
||||
|
||||
EntityActionPointer action = _objectActions[actionID];
|
||||
EntityDynamicPointer action = _objectActions[actionID];
|
||||
|
||||
success = action->updateArguments(arguments);
|
||||
if (success) {
|
||||
action->setIsMine(true);
|
||||
serializeActions(success, _allActionsDataCache);
|
||||
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
|
||||
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
|
||||
} else {
|
||||
qCDebug(entities) << "EntityItem::updateAction failed";
|
||||
}
|
||||
|
@ -2035,7 +2044,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
|
|||
simulation = entityTree ? entityTree->getSimulation() : nullptr;
|
||||
}
|
||||
|
||||
EntityActionPointer action = _objectActions[actionID];
|
||||
EntityDynamicPointer action = _objectActions[actionID];
|
||||
|
||||
action->setOwnerEntity(nullptr);
|
||||
action->setIsMine(false);
|
||||
|
@ -2049,7 +2058,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
|
|||
serializeActions(success, _allActionsDataCache);
|
||||
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
|
||||
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
|
||||
setActionDataNeedsTransmit(true);
|
||||
setDynamicDataNeedsTransmit(true);
|
||||
return success;
|
||||
}
|
||||
return false;
|
||||
|
@ -2057,10 +2066,10 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
|
|||
|
||||
bool EntityItem::clearActions(EntitySimulationPointer simulation) {
|
||||
withWriteLock([&] {
|
||||
QHash<QUuid, EntityActionPointer>::iterator i = _objectActions.begin();
|
||||
QHash<QUuid, EntityDynamicPointer>::iterator i = _objectActions.begin();
|
||||
while (i != _objectActions.end()) {
|
||||
const QUuid id = i.key();
|
||||
EntityActionPointer action = _objectActions[id];
|
||||
EntityDynamicPointer action = _objectActions[id];
|
||||
i = _objectActions.erase(i);
|
||||
action->setOwnerEntity(nullptr);
|
||||
action->removeFromSimulation(simulation);
|
||||
|
@ -2101,12 +2110,12 @@ void EntityItem::deserializeActionsInternal() {
|
|||
serializedActionsStream >> serializedActions;
|
||||
}
|
||||
|
||||
// Keep track of which actions got added or updated by the new actionData
|
||||
// Keep track of which actions got added or updated by the new dynamicData
|
||||
QSet<QUuid> updated;
|
||||
|
||||
foreach(QByteArray serializedAction, serializedActions) {
|
||||
QDataStream serializedActionStream(serializedAction);
|
||||
EntityActionType actionType;
|
||||
EntityDynamicType actionType;
|
||||
QUuid actionID;
|
||||
serializedActionStream >> actionType;
|
||||
serializedActionStream >> actionID;
|
||||
|
@ -2115,7 +2124,7 @@ void EntityItem::deserializeActionsInternal() {
|
|||
}
|
||||
|
||||
if (_objectActions.contains(actionID)) {
|
||||
EntityActionPointer action = _objectActions[actionID];
|
||||
EntityDynamicPointer action = _objectActions[actionID];
|
||||
// TODO: make sure types match? there isn't currently a way to
|
||||
// change the type of an existing action.
|
||||
if (!action->isMine()) {
|
||||
|
@ -2123,9 +2132,9 @@ void EntityItem::deserializeActionsInternal() {
|
|||
}
|
||||
updated << actionID;
|
||||
} else {
|
||||
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
|
||||
auto actionFactory = DependencyManager::get<EntityDynamicFactoryInterface>();
|
||||
EntityItemPointer entity = getThisPointer();
|
||||
EntityActionPointer action = actionFactory->factoryBA(entity, serializedAction);
|
||||
EntityDynamicPointer action = actionFactory->factoryBA(entity, serializedAction);
|
||||
if (action) {
|
||||
entity->addActionInternal(simulation, action);
|
||||
updated << actionID;
|
||||
|
@ -2140,15 +2149,15 @@ void EntityItem::deserializeActionsInternal() {
|
|||
}
|
||||
|
||||
// remove any actions that weren't included in the new data.
|
||||
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
|
||||
QHash<QUuid, EntityDynamicPointer>::const_iterator i = _objectActions.begin();
|
||||
while (i != _objectActions.end()) {
|
||||
QUuid id = i.key();
|
||||
if (!updated.contains(id)) {
|
||||
EntityActionPointer action = i.value();
|
||||
EntityDynamicPointer action = i.value();
|
||||
|
||||
if (action->isMine()) {
|
||||
// we just received an update that didn't include one of our actions. tell the server about it (again).
|
||||
setActionDataNeedsTransmit(true);
|
||||
setDynamicDataNeedsTransmit(true);
|
||||
} else {
|
||||
// don't let someone else delete my action.
|
||||
_actionsToRemove << id;
|
||||
|
@ -2167,7 +2176,7 @@ void EntityItem::deserializeActionsInternal() {
|
|||
}
|
||||
}
|
||||
|
||||
_actionDataDirty = true;
|
||||
_dynamicDataDirty = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -2179,15 +2188,15 @@ void EntityItem::checkWaitingToRemove(EntitySimulationPointer simulation) {
|
|||
_actionsToRemove.clear();
|
||||
}
|
||||
|
||||
void EntityItem::setActionData(QByteArray actionData) {
|
||||
void EntityItem::setDynamicData(QByteArray dynamicData) {
|
||||
withWriteLock([&] {
|
||||
setActionDataInternal(actionData);
|
||||
setDynamicDataInternal(dynamicData);
|
||||
});
|
||||
}
|
||||
|
||||
void EntityItem::setActionDataInternal(QByteArray actionData) {
|
||||
if (_allActionsDataCache != actionData) {
|
||||
_allActionsDataCache = actionData;
|
||||
void EntityItem::setDynamicDataInternal(QByteArray dynamicData) {
|
||||
if (_allActionsDataCache != dynamicData) {
|
||||
_allActionsDataCache = dynamicData;
|
||||
deserializeActionsInternal();
|
||||
}
|
||||
checkWaitingToRemove();
|
||||
|
@ -2201,10 +2210,10 @@ void EntityItem::serializeActions(bool& success, QByteArray& result) const {
|
|||
}
|
||||
|
||||
QVector<QByteArray> serializedActions;
|
||||
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
|
||||
QHash<QUuid, EntityDynamicPointer>::const_iterator i = _objectActions.begin();
|
||||
while (i != _objectActions.end()) {
|
||||
const QUuid id = i.key();
|
||||
EntityActionPointer action = _objectActions[id];
|
||||
EntityDynamicPointer action = _objectActions[id];
|
||||
QByteArray bytesForAction = action->serialize();
|
||||
serializedActions << bytesForAction;
|
||||
i++;
|
||||
|
@ -2224,23 +2233,23 @@ void EntityItem::serializeActions(bool& success, QByteArray& result) const {
|
|||
return;
|
||||
}
|
||||
|
||||
const QByteArray EntityItem::getActionDataInternal() const {
|
||||
if (_actionDataDirty) {
|
||||
const QByteArray EntityItem::getDynamicDataInternal() const {
|
||||
if (_dynamicDataDirty) {
|
||||
bool success;
|
||||
serializeActions(success, _allActionsDataCache);
|
||||
if (success) {
|
||||
_actionDataDirty = false;
|
||||
_dynamicDataDirty = false;
|
||||
}
|
||||
}
|
||||
return _allActionsDataCache;
|
||||
}
|
||||
|
||||
const QByteArray EntityItem::getActionData() const {
|
||||
const QByteArray EntityItem::getDynamicData() const {
|
||||
QByteArray result;
|
||||
|
||||
if (_actionDataDirty) {
|
||||
if (_dynamicDataDirty) {
|
||||
withWriteLock([&] {
|
||||
getActionDataInternal();
|
||||
getDynamicDataInternal();
|
||||
result = _allActionsDataCache;
|
||||
});
|
||||
} else {
|
||||
|
@ -2255,9 +2264,9 @@ QVariantMap EntityItem::getActionArguments(const QUuid& actionID) const {
|
|||
QVariantMap result;
|
||||
withReadLock([&] {
|
||||
if (_objectActions.contains(actionID)) {
|
||||
EntityActionPointer action = _objectActions[actionID];
|
||||
EntityDynamicPointer action = _objectActions[actionID];
|
||||
result = action->getArguments();
|
||||
result["type"] = EntityActionInterface::actionTypeToString(action->getType());
|
||||
result["type"] = EntityDynamicInterface::dynamicTypeToString(action->getType());
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -2265,7 +2274,7 @@ QVariantMap EntityItem::getActionArguments(const QUuid& actionID) const {
|
|||
}
|
||||
|
||||
bool EntityItem::shouldSuppressLocationEdits() const {
|
||||
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
|
||||
QHash<QUuid, EntityDynamicPointer>::const_iterator i = _objectActions.begin();
|
||||
while (i != _objectActions.end()) {
|
||||
if (i.value()->shouldSuppressLocationEdits()) {
|
||||
return true;
|
||||
|
@ -2276,12 +2285,12 @@ bool EntityItem::shouldSuppressLocationEdits() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
QList<EntityActionPointer> EntityItem::getActionsOfType(EntityActionType typeToGet) const {
|
||||
QList<EntityActionPointer> result;
|
||||
QList<EntityDynamicPointer> EntityItem::getActionsOfType(EntityDynamicType typeToGet) const {
|
||||
QList<EntityDynamicPointer> result;
|
||||
|
||||
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
|
||||
QHash<QUuid, EntityDynamicPointer>::const_iterator i = _objectActions.begin();
|
||||
while (i != _objectActions.end()) {
|
||||
EntityActionPointer action = i.value();
|
||||
EntityDynamicPointer action = i.value();
|
||||
if (action->getType() == typeToGet && action->isActive()) {
|
||||
result += action;
|
||||
}
|
||||
|
|
|
@ -36,17 +36,17 @@
|
|||
#include "EntityTypes.h"
|
||||
#include "SimulationOwner.h"
|
||||
#include "SimulationFlags.h"
|
||||
#include "EntityActionInterface.h"
|
||||
#include "EntityDynamicInterface.h"
|
||||
|
||||
class EntitySimulation;
|
||||
class EntityTreeElement;
|
||||
class EntityTreeElementExtraEncodeData;
|
||||
class EntityActionInterface;
|
||||
class EntityDynamicInterface;
|
||||
class EntityItemProperties;
|
||||
class EntityTree;
|
||||
class btCollisionShape;
|
||||
typedef std::shared_ptr<EntityTree> EntityTreePointer;
|
||||
typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
|
||||
typedef std::shared_ptr<EntityDynamicInterface> EntityDynamicPointer;
|
||||
typedef std::shared_ptr<EntityTreeElement> EntityTreeElementPointer;
|
||||
using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr<EntityTreeElementExtraEncodeData>;
|
||||
|
||||
|
@ -398,22 +398,22 @@ public:
|
|||
void flagForMotionStateChange() { _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; }
|
||||
|
||||
QString actionsToDebugString();
|
||||
bool addAction(EntitySimulationPointer simulation, EntityActionPointer action);
|
||||
bool addAction(EntitySimulationPointer simulation, EntityDynamicPointer action);
|
||||
bool updateAction(EntitySimulationPointer simulation, const QUuid& actionID, const QVariantMap& arguments);
|
||||
bool removeAction(EntitySimulationPointer simulation, const QUuid& actionID);
|
||||
bool clearActions(EntitySimulationPointer simulation);
|
||||
void setActionData(QByteArray actionData);
|
||||
const QByteArray getActionData() const;
|
||||
void setDynamicData(QByteArray dynamicData);
|
||||
const QByteArray getDynamicData() const;
|
||||
bool hasActions() const { return !_objectActions.empty(); }
|
||||
QList<QUuid> getActionIDs() const { return _objectActions.keys(); }
|
||||
QVariantMap getActionArguments(const QUuid& actionID) const;
|
||||
void deserializeActions();
|
||||
|
||||
void setActionDataDirty(bool value) const { _actionDataDirty = value; }
|
||||
bool actionDataDirty() const { return _actionDataDirty; }
|
||||
void setDynamicDataDirty(bool value) const { _dynamicDataDirty = value; }
|
||||
bool dynamicDataDirty() const { return _dynamicDataDirty; }
|
||||
|
||||
void setActionDataNeedsTransmit(bool value) const { _actionDataNeedsTransmit = value; }
|
||||
bool actionDataNeedsTransmit() const { return _actionDataNeedsTransmit; }
|
||||
void setDynamicDataNeedsTransmit(bool value) const { _dynamicDataNeedsTransmit = value; }
|
||||
bool dynamicDataNeedsTransmit() const { return _dynamicDataNeedsTransmit; }
|
||||
|
||||
bool shouldSuppressLocationEdits() const;
|
||||
|
||||
|
@ -421,7 +421,7 @@ public:
|
|||
const QUuid& getSourceUUID() const { return _sourceUUID; }
|
||||
bool matchesSourceUUID(const QUuid& sourceUUID) const { return _sourceUUID == sourceUUID; }
|
||||
|
||||
QList<EntityActionPointer> getActionsOfType(EntityActionType typeToGet) const;
|
||||
QList<EntityDynamicPointer> getActionsOfType(EntityDynamicType typeToGet) const;
|
||||
|
||||
// these are in the frame of this object
|
||||
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override { return glm::quat(); }
|
||||
|
@ -479,8 +479,8 @@ protected:
|
|||
|
||||
void setSimulated(bool simulated) { _simulated = simulated; }
|
||||
|
||||
const QByteArray getActionDataInternal() const;
|
||||
void setActionDataInternal(QByteArray actionData);
|
||||
const QByteArray getDynamicDataInternal() const;
|
||||
void setDynamicDataInternal(QByteArray dynamicData);
|
||||
|
||||
virtual void locationChanged(bool tellPhysics = true) override;
|
||||
virtual void dimensionsChanged() override;
|
||||
|
@ -570,22 +570,22 @@ protected:
|
|||
void* _physicsInfo { nullptr }; // set by EntitySimulation
|
||||
bool _simulated; // set by EntitySimulation
|
||||
|
||||
bool addActionInternal(EntitySimulationPointer simulation, EntityActionPointer action);
|
||||
bool addActionInternal(EntitySimulationPointer simulation, EntityDynamicPointer action);
|
||||
bool removeActionInternal(const QUuid& actionID, EntitySimulationPointer simulation = nullptr);
|
||||
void deserializeActionsInternal();
|
||||
void serializeActions(bool& success, QByteArray& result) const;
|
||||
QHash<QUuid, EntityActionPointer> _objectActions;
|
||||
QHash<QUuid, EntityDynamicPointer> _objectActions;
|
||||
|
||||
static int _maxActionsDataSize;
|
||||
mutable QByteArray _allActionsDataCache;
|
||||
|
||||
// when an entity-server starts up, EntityItem::setActionData is called before the entity-tree is
|
||||
// when an entity-server starts up, EntityItem::setDynamicData is called before the entity-tree is
|
||||
// ready. This means we can't find our EntityItemPointer or add the action to the simulation. These
|
||||
// are used to keep track of and work around this situation.
|
||||
void checkWaitingToRemove(EntitySimulationPointer simulation = nullptr);
|
||||
mutable QSet<QUuid> _actionsToRemove;
|
||||
mutable bool _actionDataDirty { false };
|
||||
mutable bool _actionDataNeedsTransmit { false };
|
||||
mutable bool _dynamicDataDirty { false };
|
||||
mutable bool _dynamicDataNeedsTransmit { false };
|
||||
// _previouslyDeletedActions is used to avoid an action being re-added due to server round-trip lag
|
||||
static quint64 _rememberDeletedActionTime;
|
||||
mutable QHash<QUuid, quint64> _previouslyDeletedActions;
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
#include <model-networking/MeshProxy.h>
|
||||
|
||||
#include "EntitiesLogging.h"
|
||||
#include "EntityActionFactoryInterface.h"
|
||||
#include "EntityActionInterface.h"
|
||||
#include "EntityDynamicFactoryInterface.h"
|
||||
#include "EntityDynamicInterface.h"
|
||||
#include "EntitySimulation.h"
|
||||
#include "EntityTree.h"
|
||||
#include "LightEntityItem.h"
|
||||
|
@ -1133,7 +1133,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
|
|||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
QUuid actionID = QUuid::createUuid();
|
||||
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
|
||||
auto actionFactory = DependencyManager::get<EntityDynamicFactoryInterface>();
|
||||
bool success = false;
|
||||
actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
|
||||
// create this action even if the entity doesn't have physics info. it will often be the
|
||||
|
@ -1142,11 +1142,11 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
|
|||
// if (!entity->getPhysicsInfo()) {
|
||||
// return false;
|
||||
// }
|
||||
EntityActionType actionType = EntityActionInterface::actionTypeFromString(actionTypeString);
|
||||
if (actionType == ACTION_TYPE_NONE) {
|
||||
EntityDynamicType dynamicType = EntityDynamicInterface::dynamicTypeFromString(actionTypeString);
|
||||
if (dynamicType == DYNAMIC_TYPE_NONE) {
|
||||
return false;
|
||||
}
|
||||
EntityActionPointer action = actionFactory->factory(actionType, actionID, entity, arguments);
|
||||
EntityDynamicPointer action = actionFactory->factory(dynamicType, actionID, entity, arguments);
|
||||
if (!action) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -279,25 +279,25 @@ void EntitySimulation::moveSimpleKinematics(const quint64& now) {
|
|||
}
|
||||
}
|
||||
|
||||
void EntitySimulation::addAction(EntityActionPointer action) {
|
||||
void EntitySimulation::addDynamic(EntityDynamicPointer dynamic) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_actionsToAdd += action;
|
||||
_dynamicsToAdd += dynamic;
|
||||
}
|
||||
|
||||
void EntitySimulation::removeAction(const QUuid actionID) {
|
||||
void EntitySimulation::removeDynamic(const QUuid dynamicID) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_actionsToRemove += actionID;
|
||||
_dynamicsToRemove += dynamicID;
|
||||
}
|
||||
|
||||
void EntitySimulation::removeActions(QList<QUuid> actionIDsToRemove) {
|
||||
void EntitySimulation::removeDynamics(QList<QUuid> dynamicIDsToRemove) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
foreach(QUuid uuid, actionIDsToRemove) {
|
||||
_actionsToRemove.insert(uuid);
|
||||
foreach(QUuid uuid, dynamicIDsToRemove) {
|
||||
_dynamicsToRemove.insert(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
void EntitySimulation::applyActionChanges() {
|
||||
void EntitySimulation::applyDynamicChanges() {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_actionsToAdd.clear();
|
||||
_actionsToRemove.clear();
|
||||
_dynamicsToAdd.clear();
|
||||
_dynamicsToRemove.clear();
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "EntityActionInterface.h"
|
||||
#include "EntityDynamicInterface.h"
|
||||
#include "EntityItem.h"
|
||||
#include "EntityTree.h"
|
||||
|
||||
|
@ -59,10 +59,10 @@ public:
|
|||
|
||||
// friend class EntityTree;
|
||||
|
||||
virtual void addAction(EntityActionPointer action);
|
||||
virtual void removeAction(const QUuid actionID);
|
||||
virtual void removeActions(QList<QUuid> actionIDsToRemove);
|
||||
virtual void applyActionChanges();
|
||||
virtual void addDynamic(EntityDynamicPointer dynamic);
|
||||
virtual void removeDynamic(const QUuid dynamicID);
|
||||
virtual void removeDynamics(QList<QUuid> dynamicIDsToRemove);
|
||||
virtual void applyDynamicChanges();
|
||||
|
||||
/// \param entity pointer to EntityItem to be added
|
||||
/// \sideeffect sets relevant backpointers in entity, but maybe later when appropriate data structures are locked
|
||||
|
@ -103,8 +103,8 @@ protected:
|
|||
|
||||
SetOfEntities _entitiesToSort; // entities moved by simulation (and might need resort in EntityTree)
|
||||
SetOfEntities _simpleKinematicEntities; // entities undergoing non-colliding kinematic motion
|
||||
QList<EntityActionPointer> _actionsToAdd;
|
||||
QSet<QUuid> _actionsToRemove;
|
||||
QList<EntityDynamicPointer> _dynamicsToAdd;
|
||||
QSet<QUuid> _dynamicsToRemove;
|
||||
|
||||
protected:
|
||||
SetOfEntities _entitiesToDelete; // entities simulation decided needed to be deleted (EntityTree will actually delete)
|
||||
|
|
|
@ -808,7 +808,7 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList<Q
|
|||
int index = changedProperties.indexOf("actionData");
|
||||
if (index >= 0) {
|
||||
QByteArray value = properties.getActionData();
|
||||
QString changeHint = serializedActionsToDebugString(value);
|
||||
QString changeHint = serializedDynamicsToDebugString(value);
|
||||
changedProperties[index] = QString("actionData:") + changeHint;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,10 +76,6 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
|
|||
}
|
||||
break;
|
||||
|
||||
case gpu::COMPRESSED_R:
|
||||
result = GL_COMPRESSED_RED_RGTC1;
|
||||
break;
|
||||
|
||||
case gpu::R11G11B10:
|
||||
// the type should be float
|
||||
result = GL_R11F_G11F_B10F;
|
||||
|
@ -149,12 +145,6 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
|
|||
case gpu::SRGBA:
|
||||
result = GL_SRGB8; // standard 2.2 gamma correction color
|
||||
break;
|
||||
case gpu::COMPRESSED_RGB:
|
||||
result = GL_COMPRESSED_RGB;
|
||||
break;
|
||||
case gpu::COMPRESSED_SRGB:
|
||||
result = GL_COMPRESSED_SRGB;
|
||||
break;
|
||||
default:
|
||||
qCWarning(gpugllogging) << "Unknown combination of texel format";
|
||||
}
|
||||
|
@ -217,29 +207,22 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
|
|||
case gpu::SRGBA:
|
||||
result = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color
|
||||
break;
|
||||
case gpu::COMPRESSED_RGBA:
|
||||
result = GL_COMPRESSED_RGBA;
|
||||
break;
|
||||
case gpu::COMPRESSED_SRGBA:
|
||||
result = GL_COMPRESSED_SRGB_ALPHA;
|
||||
break;
|
||||
|
||||
// FIXME: WE will want to support this later
|
||||
/*
|
||||
case gpu::COMPRESSED_BC3_RGBA:
|
||||
result = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
case gpu::COMPRESSED_BC4_RED:
|
||||
result = GL_COMPRESSED_RED_RGTC1;
|
||||
break;
|
||||
case gpu::COMPRESSED_BC3_SRGBA:
|
||||
case gpu::COMPRESSED_BC1_SRGB:
|
||||
result = GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
|
||||
break;
|
||||
case gpu::COMPRESSED_BC1_SRGBA:
|
||||
result = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
|
||||
break;
|
||||
case gpu::COMPRESSED_BC3_SRGBA:
|
||||
result = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
|
||||
break;
|
||||
|
||||
case gpu::COMPRESSED_BC7_RGBA:
|
||||
result = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
|
||||
case gpu::COMPRESSED_BC5_XY:
|
||||
result = GL_COMPRESSED_RG_RGTC2;
|
||||
break;
|
||||
case gpu::COMPRESSED_BC7_SRGBA:
|
||||
result = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
|
||||
break;
|
||||
*/
|
||||
|
||||
default:
|
||||
qCWarning(gpugllogging) << "Unknown combination of texel format";
|
||||
|
@ -269,10 +252,6 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
|
|||
texel.internalFormat = GL_R8;
|
||||
break;
|
||||
|
||||
case gpu::COMPRESSED_R:
|
||||
texel.internalFormat = GL_COMPRESSED_RED_RGTC1;
|
||||
break;
|
||||
|
||||
case gpu::DEPTH:
|
||||
texel.format = GL_DEPTH_COMPONENT;
|
||||
texel.internalFormat = GL_DEPTH_COMPONENT32;
|
||||
|
@ -315,12 +294,6 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
|
|||
case gpu::RGBA:
|
||||
texel.internalFormat = GL_RGB8;
|
||||
break;
|
||||
case gpu::COMPRESSED_RGB:
|
||||
texel.internalFormat = GL_COMPRESSED_RGB;
|
||||
break;
|
||||
case gpu::COMPRESSED_SRGB:
|
||||
texel.internalFormat = GL_COMPRESSED_SRGB;
|
||||
break;
|
||||
default:
|
||||
qCWarning(gpugllogging) << "Unknown combination of texel format";
|
||||
}
|
||||
|
@ -359,30 +332,22 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
|
|||
texel.internalFormat = GL_SRGB8_ALPHA8;
|
||||
break;
|
||||
|
||||
case gpu::COMPRESSED_RGBA:
|
||||
texel.internalFormat = GL_COMPRESSED_RGBA;
|
||||
break;
|
||||
case gpu::COMPRESSED_SRGBA:
|
||||
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA;
|
||||
|
||||
case gpu::COMPRESSED_BC4_RED:
|
||||
texel.internalFormat = GL_COMPRESSED_RED_RGTC1;
|
||||
break;
|
||||
|
||||
// FIXME: WE will want to support this later
|
||||
/*
|
||||
case gpu::COMPRESSED_BC3_RGBA:
|
||||
texel.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
case gpu::COMPRESSED_BC1_SRGB:
|
||||
texel.internalFormat = GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
|
||||
break;
|
||||
case gpu::COMPRESSED_BC3_SRGBA:
|
||||
case gpu::COMPRESSED_BC1_SRGBA:
|
||||
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
|
||||
break;
|
||||
case gpu::COMPRESSED_BC3_SRGBA:
|
||||
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
|
||||
break;
|
||||
|
||||
case gpu::COMPRESSED_BC7_RGBA:
|
||||
texel.internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
|
||||
case gpu::COMPRESSED_BC5_XY:
|
||||
texel.internalFormat = GL_COMPRESSED_RG_RGTC2;
|
||||
break;
|
||||
case gpu::COMPRESSED_BC7_SRGBA:
|
||||
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
|
||||
break;
|
||||
*/
|
||||
|
||||
default:
|
||||
qCWarning(gpugllogging) << "Unknown combination of texel format";
|
||||
|
@ -403,10 +368,6 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
|
|||
texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()];
|
||||
|
||||
switch (dstFormat.getSemantic()) {
|
||||
case gpu::COMPRESSED_R: {
|
||||
texel.internalFormat = GL_COMPRESSED_RED_RGTC1;
|
||||
break;
|
||||
}
|
||||
case gpu::RED:
|
||||
case gpu::RGB:
|
||||
case gpu::RGBA:
|
||||
|
@ -564,12 +525,6 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
|
|||
case gpu::SRGBA:
|
||||
texel.internalFormat = GL_SRGB8; // standard 2.2 gamma correction color
|
||||
break;
|
||||
case gpu::COMPRESSED_RGB:
|
||||
texel.internalFormat = GL_COMPRESSED_RGB;
|
||||
break;
|
||||
case gpu::COMPRESSED_SRGB:
|
||||
texel.internalFormat = GL_COMPRESSED_SRGB;
|
||||
break;
|
||||
default:
|
||||
qCWarning(gpugllogging) << "Unknown combination of texel format";
|
||||
}
|
||||
|
@ -646,11 +601,21 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
|
|||
case gpu::SRGBA:
|
||||
texel.internalFormat = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color
|
||||
break;
|
||||
case gpu::COMPRESSED_RGBA:
|
||||
texel.internalFormat = GL_COMPRESSED_RGBA;
|
||||
|
||||
case gpu::COMPRESSED_BC4_RED:
|
||||
texel.internalFormat = GL_COMPRESSED_RED_RGTC1;
|
||||
break;
|
||||
case gpu::COMPRESSED_SRGBA:
|
||||
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA;
|
||||
case gpu::COMPRESSED_BC1_SRGB:
|
||||
texel.internalFormat = GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
|
||||
break;
|
||||
case gpu::COMPRESSED_BC1_SRGBA:
|
||||
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
|
||||
break;
|
||||
case gpu::COMPRESSED_BC3_SRGBA:
|
||||
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
|
||||
break;
|
||||
case gpu::COMPRESSED_BC5_XY:
|
||||
texel.internalFormat = GL_COMPRESSED_RG_RGTC2;
|
||||
break;
|
||||
default:
|
||||
qCWarning(gpugllogging) << "Unknown combination of texel format";
|
||||
|
|
|
@ -120,11 +120,12 @@ void GLTexture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, u
|
|||
}
|
||||
auto size = _gpuObject.evalMipDimensions(sourceMip);
|
||||
auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face);
|
||||
auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face);
|
||||
if (mipData) {
|
||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat());
|
||||
copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.format, texelFormat.type, mipData->readData());
|
||||
copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.internalFormat, texelFormat.format, texelFormat.type, mipSize, mipData->readData());
|
||||
} else {
|
||||
qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str();
|
||||
qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,9 +204,11 @@ TransferJob::TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t t
|
|||
|
||||
auto transferDimensions = _parent._gpuObject.evalMipDimensions(sourceMip);
|
||||
GLenum format;
|
||||
GLenum internalFormat;
|
||||
GLenum type;
|
||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), _parent._gpuObject.getStoredMipFormat());
|
||||
format = texelFormat.format;
|
||||
internalFormat = texelFormat.internalFormat;
|
||||
type = texelFormat.type;
|
||||
auto mipSize = _parent._gpuObject.getStoredMipFaceSize(sourceMip, face);
|
||||
|
||||
|
@ -236,7 +239,7 @@ TransferJob::TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t t
|
|||
Backend::updateTextureTransferPendingSize(0, _transferSize);
|
||||
|
||||
_transferLambda = [=] {
|
||||
_parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, format, type, _buffer.data());
|
||||
_parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, internalFormat, format, type, _buffer.size(), _buffer.data());
|
||||
std::vector<uint8_t> emptyVector;
|
||||
_buffer.swap(emptyVector);
|
||||
};
|
||||
|
|
|
@ -163,7 +163,7 @@ public:
|
|||
protected:
|
||||
virtual Size size() const = 0;
|
||||
virtual void generateMips() const = 0;
|
||||
virtual void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const = 0;
|
||||
virtual void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const = 0;
|
||||
virtual void copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const final;
|
||||
|
||||
GLTexture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GLuint id);
|
||||
|
@ -177,7 +177,7 @@ public:
|
|||
protected:
|
||||
GLExternalTexture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GLuint id);
|
||||
void generateMips() const override {}
|
||||
void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const override {}
|
||||
void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override {}
|
||||
|
||||
Size size() const override { return 0; }
|
||||
};
|
||||
|
|
|
@ -50,7 +50,7 @@ public:
|
|||
protected:
|
||||
GL41Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
|
||||
void generateMips() const override;
|
||||
void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const override;
|
||||
void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override;
|
||||
virtual void syncSampler() const;
|
||||
|
||||
void withPreservedTexture(std::function<void()> f) const;
|
||||
|
@ -105,7 +105,7 @@ public:
|
|||
void promote() override;
|
||||
void demote() override;
|
||||
void populateTransferQueue() override;
|
||||
void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const override;
|
||||
void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override;
|
||||
|
||||
Size size() const override { return _size; }
|
||||
Size _size { 0 };
|
||||
|
|
|
@ -69,9 +69,7 @@ GL41Texture::GL41Texture(const std::weak_ptr<GLBackend>& backend, const Texture&
|
|||
|
||||
GLuint GL41Texture::allocate(const Texture& texture) {
|
||||
GLuint result;
|
||||
// FIXME technically GL 4.2, but OSX includes the ARB_texture_storage extension
|
||||
glCreateTextures(getGLTextureType(texture), 1, &result);
|
||||
//glGenTextures(1, &result);
|
||||
glGenTextures(1, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -94,12 +92,37 @@ void GL41Texture::generateMips() const {
|
|||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
void GL41Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const {
|
||||
void GL41Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const {
|
||||
if (GL_TEXTURE_2D == _target) {
|
||||
glTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
|
||||
switch (internalFormat) {
|
||||
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
|
||||
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
|
||||
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
|
||||
case GL_COMPRESSED_RED_RGTC1:
|
||||
case GL_COMPRESSED_RG_RGTC2:
|
||||
glCompressedTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, internalFormat,
|
||||
static_cast<GLsizei>(sourceSize), sourcePointer);
|
||||
break;
|
||||
default:
|
||||
glTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
|
||||
break;
|
||||
}
|
||||
} else if (GL_TEXTURE_CUBE_MAP == _target) {
|
||||
auto target = GLTexture::CUBE_FACE_LAYOUT[face];
|
||||
glTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
|
||||
|
||||
switch (internalFormat) {
|
||||
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
|
||||
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
|
||||
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
|
||||
case GL_COMPRESSED_RED_RGTC1:
|
||||
case GL_COMPRESSED_RG_RGTC2:
|
||||
glCompressedTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, internalFormat,
|
||||
static_cast<GLsizei>(sourceSize), sourcePointer);
|
||||
break;
|
||||
default:
|
||||
glTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
@ -253,9 +276,9 @@ void GL41VariableAllocationTexture::allocateStorage(uint16 allocatedMip) {
|
|||
}
|
||||
|
||||
|
||||
void GL41VariableAllocationTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const {
|
||||
void GL41VariableAllocationTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const {
|
||||
withPreservedTexture([&] {
|
||||
Parent::copyMipFaceLinesFromTexture(mip, face, size, yOffset, format, type, sourcePointer);
|
||||
Parent::copyMipFaceLinesFromTexture(mip, face, size, yOffset, internalFormat, format, type, sourceSize, sourcePointer);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -280,7 +303,7 @@ void GL41VariableAllocationTexture::promote() {
|
|||
|
||||
withPreservedTexture([&] {
|
||||
GLuint fbo { 0 };
|
||||
glCreateFramebuffers(1, &fbo);
|
||||
glGenFramebuffers(1, &fbo);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
||||
|
||||
uint16_t mips = _gpuObject.getNumMips();
|
||||
|
|
|
@ -48,7 +48,7 @@ public:
|
|||
protected:
|
||||
GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
|
||||
void generateMips() const override;
|
||||
void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const override;
|
||||
void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override;
|
||||
virtual void syncSampler() const;
|
||||
};
|
||||
|
||||
|
|
|
@ -117,17 +117,47 @@ void GL45Texture::generateMips() const {
|
|||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
void GL45Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const {
|
||||
void GL45Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const {
|
||||
if (GL_TEXTURE_2D == _target) {
|
||||
glTextureSubImage2D(_id, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
|
||||
switch (internalFormat) {
|
||||
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
|
||||
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
|
||||
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
|
||||
case GL_COMPRESSED_RED_RGTC1:
|
||||
case GL_COMPRESSED_RG_RGTC2:
|
||||
glCompressedTextureSubImage2D(_id, mip, 0, yOffset, size.x, size.y, internalFormat,
|
||||
static_cast<GLsizei>(sourceSize), sourcePointer);
|
||||
break;
|
||||
default:
|
||||
glTextureSubImage2D(_id, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
|
||||
break;
|
||||
}
|
||||
} else if (GL_TEXTURE_CUBE_MAP == _target) {
|
||||
// DSA ARB does not work on AMD, so use EXT
|
||||
// unless EXT is not available on the driver
|
||||
if (glTextureSubImage2DEXT) {
|
||||
auto target = GLTexture::CUBE_FACE_LAYOUT[face];
|
||||
glTextureSubImage2DEXT(_id, target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
|
||||
} else {
|
||||
glTextureSubImage3D(_id, mip, 0, yOffset, face, size.x, size.y, 1, format, type, sourcePointer);
|
||||
switch (internalFormat) {
|
||||
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
|
||||
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
|
||||
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
|
||||
case GL_COMPRESSED_RED_RGTC1:
|
||||
case GL_COMPRESSED_RG_RGTC2:
|
||||
if (glCompressedTextureSubImage2DEXT) {
|
||||
auto target = GLTexture::CUBE_FACE_LAYOUT[face];
|
||||
glCompressedTextureSubImage2DEXT(_id, target, mip, 0, yOffset, size.x, size.y, internalFormat,
|
||||
static_cast<GLsizei>(sourceSize), sourcePointer);
|
||||
} else {
|
||||
glCompressedTextureSubImage3D(_id, mip, 0, yOffset, face, size.x, size.y, 1, internalFormat,
|
||||
static_cast<GLsizei>(sourceSize), sourcePointer);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// DSA ARB does not work on AMD, so use EXT
|
||||
// unless EXT is not available on the driver
|
||||
if (glTextureSubImage2DEXT) {
|
||||
auto target = GLTexture::CUBE_FACE_LAYOUT[face];
|
||||
glTextureSubImage2DEXT(_id, target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
|
||||
} else {
|
||||
glTextureSubImage3D(_id, mip, 0, yOffset, face, size.x, size.y, 1, format, type, sourcePointer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
Q_ASSERT(false);
|
||||
|
|
|
@ -19,6 +19,12 @@ const Element Element::COLOR_SRGBA_32{ VEC4, NUINT8, SRGBA };
|
|||
const Element Element::COLOR_BGRA_32{ VEC4, NUINT8, BGRA };
|
||||
const Element Element::COLOR_SBGRA_32{ VEC4, NUINT8, SBGRA };
|
||||
|
||||
const Element Element::COLOR_COMPRESSED_RED{ VEC4, NUINT8, COMPRESSED_BC4_RED };
|
||||
const Element Element::COLOR_COMPRESSED_SRGB{ VEC4, NUINT8, COMPRESSED_BC1_SRGB };
|
||||
const Element Element::COLOR_COMPRESSED_SRGBA_MASK{ VEC4, NUINT8, COMPRESSED_BC1_SRGBA };
|
||||
const Element Element::COLOR_COMPRESSED_SRGBA{ VEC4, NUINT8, COMPRESSED_BC3_SRGBA };
|
||||
const Element Element::COLOR_COMPRESSED_XY{ VEC4, NUINT8, COMPRESSED_BC5_XY };
|
||||
|
||||
const Element Element::COLOR_R11G11B10{ SCALAR, FLOAT, R11G11B10 };
|
||||
const Element Element::VEC4F_COLOR_RGBA{ VEC4, FLOAT, RGBA };
|
||||
const Element Element::VEC2F_UV{ VEC2, FLOAT, UV };
|
||||
|
|
|
@ -157,20 +157,12 @@ enum Semantic {
|
|||
|
||||
// These are generic compression format smeantic for images
|
||||
_FIRST_COMPRESSED,
|
||||
COMPRESSED_R,
|
||||
|
||||
COMPRESSED_RGB,
|
||||
COMPRESSED_RGBA,
|
||||
|
||||
COMPRESSED_SRGB,
|
||||
COMPRESSED_SRGBA,
|
||||
|
||||
// FIXME: Will have to be supported later:
|
||||
/*COMPRESSED_BC3_RGBA, // RGBA_S3TC_DXT5_EXT,
|
||||
COMPRESSED_BC3_SRGBA, // SRGB_ALPHA_S3TC_DXT5_EXT
|
||||
|
||||
COMPRESSED_BC7_RGBA,
|
||||
COMPRESSED_BC7_SRGBA, */
|
||||
COMPRESSED_BC1_SRGB,
|
||||
COMPRESSED_BC1_SRGBA,
|
||||
COMPRESSED_BC3_SRGBA,
|
||||
COMPRESSED_BC4_RED,
|
||||
COMPRESSED_BC5_XY,
|
||||
|
||||
_LAST_COMPRESSED,
|
||||
|
||||
|
@ -237,6 +229,11 @@ public:
|
|||
static const Element COLOR_BGRA_32;
|
||||
static const Element COLOR_SBGRA_32;
|
||||
static const Element COLOR_R11G11B10;
|
||||
static const Element COLOR_COMPRESSED_RED;
|
||||
static const Element COLOR_COMPRESSED_SRGB;
|
||||
static const Element COLOR_COMPRESSED_SRGBA_MASK;
|
||||
static const Element COLOR_COMPRESSED_SRGBA;
|
||||
static const Element COLOR_COMPRESSED_XY;
|
||||
static const Element VEC4F_COLOR_RGBA;
|
||||
static const Element VEC2F_UV;
|
||||
static const Element VEC2F_XY;
|
||||
|
|
|
@ -212,8 +212,8 @@ void Texture::MemoryStorage::assignMipFaceData(uint16 level, uint8 face, const s
|
|||
}
|
||||
}
|
||||
|
||||
Texture* Texture::createExternal(const ExternalRecycler& recycler, const Sampler& sampler) {
|
||||
Texture* tex = new Texture(TextureUsageType::EXTERNAL);
|
||||
TexturePointer Texture::createExternal(const ExternalRecycler& recycler, const Sampler& sampler) {
|
||||
TexturePointer tex = std::make_shared<Texture>(TextureUsageType::EXTERNAL);
|
||||
tex->_type = TEX_2D;
|
||||
tex->_maxMipLevel = 0;
|
||||
tex->_sampler = sampler;
|
||||
|
@ -221,36 +221,36 @@ Texture* Texture::createExternal(const ExternalRecycler& recycler, const Sampler
|
|||
return tex;
|
||||
}
|
||||
|
||||
Texture* Texture::createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips, const Sampler& sampler) {
|
||||
TexturePointer Texture::createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips, const Sampler& sampler) {
|
||||
return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, 1, 0, numMips, sampler);
|
||||
}
|
||||
|
||||
Texture* Texture::create1D(const Element& texelFormat, uint16 width, uint16 numMips, const Sampler& sampler) {
|
||||
TexturePointer Texture::create1D(const Element& texelFormat, uint16 width, uint16 numMips, const Sampler& sampler) {
|
||||
return create(TextureUsageType::RESOURCE, TEX_1D, texelFormat, width, 1, 1, 1, 0, numMips, sampler);
|
||||
}
|
||||
|
||||
Texture* Texture::create2D(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips, const Sampler& sampler) {
|
||||
TexturePointer Texture::create2D(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips, const Sampler& sampler) {
|
||||
return create(TextureUsageType::RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 0, numMips, sampler);
|
||||
}
|
||||
|
||||
Texture* Texture::createStrict(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips, const Sampler& sampler) {
|
||||
TexturePointer Texture::createStrict(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips, const Sampler& sampler) {
|
||||
return create(TextureUsageType::STRICT_RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 0, numMips, sampler);
|
||||
}
|
||||
|
||||
Texture* Texture::create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numMips, const Sampler& sampler) {
|
||||
TexturePointer Texture::create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numMips, const Sampler& sampler) {
|
||||
return create(TextureUsageType::RESOURCE, TEX_3D, texelFormat, width, height, depth, 1, 0, numMips, sampler);
|
||||
}
|
||||
|
||||
Texture* Texture::createCube(const Element& texelFormat, uint16 width, uint16 numMips, const Sampler& sampler) {
|
||||
TexturePointer Texture::createCube(const Element& texelFormat, uint16 width, uint16 numMips, const Sampler& sampler) {
|
||||
return create(TextureUsageType::RESOURCE, TEX_CUBE, texelFormat, width, width, 1, 1, 0, numMips, sampler);
|
||||
}
|
||||
|
||||
Texture* Texture::create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, uint16 numMips, const Sampler& sampler)
|
||||
TexturePointer Texture::create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, uint16 numMips, const Sampler& sampler)
|
||||
{
|
||||
Texture* tex = new Texture(usageType);
|
||||
TexturePointer tex = std::make_shared<Texture>(usageType);
|
||||
tex->_storage.reset(new MemoryStorage());
|
||||
tex->_type = type;
|
||||
tex->_storage->assignTexture(tex);
|
||||
tex->_storage->assignTexture(tex.get());
|
||||
tex->resize(type, texelFormat, width, height, depth, numSamples, numSlices, numMips);
|
||||
|
||||
tex->_sampler = sampler;
|
||||
|
@ -434,7 +434,7 @@ void Texture::assignStoredMip(uint16 level, storage::StoragePointer& storage) {
|
|||
// THen check that the mem texture passed make sense with its format
|
||||
Size expectedSize = evalStoredMipSize(level, getStoredMipFormat());
|
||||
auto size = storage->size();
|
||||
if (storage->size() == expectedSize) {
|
||||
if (storage->size() <= expectedSize) {
|
||||
_storage->assignMipData(level, storage);
|
||||
_stamp++;
|
||||
} else if (size > expectedSize) {
|
||||
|
@ -461,7 +461,7 @@ void Texture::assignStoredMipFace(uint16 level, uint8 face, storage::StoragePoin
|
|||
// THen check that the mem texture passed make sense with its format
|
||||
Size expectedSize = evalStoredMipFaceSize(level, getStoredMipFormat());
|
||||
auto size = storage->size();
|
||||
if (size == expectedSize) {
|
||||
if (size <= expectedSize) {
|
||||
_storage->assignMipFaceData(level, face, storage);
|
||||
_stamp++;
|
||||
} else if (size > expectedSize) {
|
||||
|
@ -752,7 +752,7 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
|
|||
boffset = 0;
|
||||
}
|
||||
|
||||
auto data = cubeTexture.accessStoredMipFace(0,face)->readData();
|
||||
auto data = cubeTexture.accessStoredMipFace(0, face)->readData();
|
||||
if (data == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -328,13 +328,13 @@ public:
|
|||
|
||||
static const uint16 MAX_NUM_MIPS = 0;
|
||||
static const uint16 SINGLE_MIP = 1;
|
||||
static Texture* create1D(const Element& texelFormat, uint16 width, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
|
||||
static Texture* create2D(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
|
||||
static Texture* create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
|
||||
static Texture* createCube(const Element& texelFormat, uint16 width, uint16 numMips = 1, const Sampler& sampler = Sampler());
|
||||
static Texture* createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
|
||||
static Texture* createStrict(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
|
||||
static Texture* createExternal(const ExternalRecycler& recycler, const Sampler& sampler = Sampler());
|
||||
static TexturePointer create1D(const Element& texelFormat, uint16 width, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
|
||||
static TexturePointer create2D(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
|
||||
static TexturePointer create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
|
||||
static TexturePointer createCube(const Element& texelFormat, uint16 width, uint16 numMips = 1, const Sampler& sampler = Sampler());
|
||||
static TexturePointer createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
|
||||
static TexturePointer createStrict(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
|
||||
static TexturePointer createExternal(const ExternalRecycler& recycler, const Sampler& sampler = Sampler());
|
||||
|
||||
// After the texture has been created, it should be defined
|
||||
bool isDefined() const { return _defined; }
|
||||
|
@ -435,6 +435,8 @@ public:
|
|||
// For convenience assign a source name
|
||||
const std::string& source() const { return _source; }
|
||||
void setSource(const std::string& source) { _source = source; }
|
||||
const std::string& sourceHash() const { return _sourceHash; }
|
||||
void setSourceHash(const std::string& sourceHash) { _sourceHash = sourceHash; }
|
||||
|
||||
// Potentially change the minimum mip (mostly for debugging purpose)
|
||||
bool setMinMip(uint16 newMinMip);
|
||||
|
@ -482,6 +484,7 @@ public:
|
|||
// For Cube Texture, it's possible to generate the irradiance spherical harmonics and make them availalbe with the texture
|
||||
bool generateIrradiance();
|
||||
const SHPointer& getIrradiance(uint16 slice = 0) const { return _irradiance; }
|
||||
void overrideIrradiance(SHPointer irradiance) { _irradiance = irradiance; }
|
||||
bool isIrradianceValid() const { return _isIrradianceValid; }
|
||||
|
||||
// Own sampler
|
||||
|
@ -502,7 +505,7 @@ public:
|
|||
|
||||
// Textures can be serialized directly to ktx data file, here is how
|
||||
static ktx::KTXUniquePointer serialize(const Texture& texture);
|
||||
static Texture* unserialize(const std::string& ktxFile, TextureUsageType usageType = TextureUsageType::RESOURCE, Usage usage = Usage(), const Sampler::Desc& sampler = Sampler::Desc());
|
||||
static TexturePointer unserialize(const std::string& ktxFile, TextureUsageType usageType = TextureUsageType::RESOURCE, Usage usage = Usage(), const Sampler::Desc& sampler = Sampler::Desc());
|
||||
static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header);
|
||||
static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat);
|
||||
|
||||
|
@ -518,6 +521,7 @@ protected:
|
|||
std::weak_ptr<Texture> _fallback;
|
||||
// Not strictly necessary, but incredibly useful for debugging
|
||||
std::string _source;
|
||||
std::string _sourceHash;
|
||||
std::unique_ptr< Storage > _storage;
|
||||
|
||||
Stamp _stamp { 0 };
|
||||
|
@ -552,7 +556,7 @@ protected:
|
|||
bool _isIrradianceValid = false;
|
||||
bool _defined = false;
|
||||
|
||||
static Texture* create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, uint16 numMips, const Sampler& sampler);
|
||||
static TexturePointer create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, uint16 numMips, const Sampler& sampler);
|
||||
|
||||
Size resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, uint16 numMips);
|
||||
};
|
||||
|
|
|
@ -23,6 +23,7 @@ struct GPUKTXPayload {
|
|||
Texture::Usage _usage;
|
||||
TextureUsageType _usageType;
|
||||
|
||||
|
||||
static std::string KEY;
|
||||
static bool isGPUKTX(const ktx::KeyValue& val) {
|
||||
return (val._key.compare(KEY) == 0);
|
||||
|
@ -161,7 +162,13 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
|
|||
keyval._usage = texture.getUsage();
|
||||
keyval._usageType = texture.getUsageType();
|
||||
ktx::KeyValues keyValues;
|
||||
keyValues.emplace_back(ktx::KeyValue(GPUKTXPayload::KEY, sizeof(GPUKTXPayload), (ktx::Byte*) &keyval));
|
||||
keyValues.emplace_back(ktx::KeyValue(GPUKTXPayload::KEY, sizeof(GPUKTXPayload), (ktx::Byte*) &keyval));
|
||||
|
||||
static const std::string SOURCE_HASH_KEY = "hifi.sourceHash";
|
||||
auto hash = texture.sourceHash();
|
||||
if (!hash.empty()) {
|
||||
keyValues.emplace_back(ktx::KeyValue(SOURCE_HASH_KEY, static_cast<uint32>(hash.size()), (ktx::Byte*) hash.c_str()));
|
||||
}
|
||||
|
||||
auto ktxBuffer = ktx::KTX::create(header, images, keyValues);
|
||||
#if 0
|
||||
|
@ -193,7 +200,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
|
|||
return ktxBuffer;
|
||||
}
|
||||
|
||||
Texture* Texture::unserialize(const std::string& ktxfile, TextureUsageType usageType, Usage usage, const Sampler::Desc& sampler) {
|
||||
TexturePointer Texture::unserialize(const std::string& ktxfile, TextureUsageType usageType, Usage usage, const Sampler::Desc& sampler) {
|
||||
std::unique_ptr<ktx::KTX> ktxPointer = ktx::KTX::create(ktx::StoragePointer { new storage::FileStorage(ktxfile.c_str()) });
|
||||
if (!ktxPointer) {
|
||||
return nullptr;
|
||||
|
@ -260,6 +267,16 @@ bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat
|
|||
header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA);
|
||||
} else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) {
|
||||
header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED);
|
||||
} else if (texelFormat == Format::COLOR_COMPRESSED_SRGB && mipFormat == Format::COLOR_COMPRESSED_SRGB) {
|
||||
header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT, ktx::GLBaseInternalFormat::RGB);
|
||||
} else if (texelFormat == Format::COLOR_COMPRESSED_SRGBA_MASK && mipFormat == Format::COLOR_COMPRESSED_SRGBA_MASK) {
|
||||
header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, ktx::GLBaseInternalFormat::RGBA);
|
||||
} else if (texelFormat == Format::COLOR_COMPRESSED_SRGBA && mipFormat == Format::COLOR_COMPRESSED_SRGBA) {
|
||||
header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, ktx::GLBaseInternalFormat::RGBA);
|
||||
} else if (texelFormat == Format::COLOR_COMPRESSED_RED && mipFormat == Format::COLOR_COMPRESSED_RED) {
|
||||
header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1, ktx::GLBaseInternalFormat::RED);
|
||||
} else if (texelFormat == Format::COLOR_COMPRESSED_XY && mipFormat == Format::COLOR_COMPRESSED_XY) {
|
||||
header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2, ktx::GLBaseInternalFormat::RG);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -295,6 +312,25 @@ bool Texture::evalTextureFormat(const ktx::Header& header, Element& mipFormat, E
|
|||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (header.getGLFormat() == ktx::GLFormat::COMPRESSED_FORMAT && header.getGLType() == ktx::GLType::COMPRESSED_TYPE) {
|
||||
if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT) {
|
||||
mipFormat = Format::COLOR_COMPRESSED_SRGB;
|
||||
texelFormat = Format::COLOR_COMPRESSED_SRGB;
|
||||
} else if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) {
|
||||
mipFormat = Format::COLOR_COMPRESSED_SRGBA_MASK;
|
||||
texelFormat = Format::COLOR_COMPRESSED_SRGBA_MASK;
|
||||
} else if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT) {
|
||||
mipFormat = Format::COLOR_COMPRESSED_SRGBA;
|
||||
texelFormat = Format::COLOR_COMPRESSED_SRGBA;
|
||||
} else if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1) {
|
||||
mipFormat = Format::COLOR_COMPRESSED_RED;
|
||||
texelFormat = Format::COLOR_COMPRESSED_RED;
|
||||
} else if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2) {
|
||||
mipFormat = Format::COLOR_COMPRESSED_XY;
|
||||
texelFormat = Format::COLOR_COMPRESSED_XY;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
11
libraries/image/CMakeLists.txt
Normal file
11
libraries/image/CMakeLists.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
set(TARGET_NAME image)
|
||||
setup_hifi_library()
|
||||
link_hifi_libraries(shared gpu)
|
||||
|
||||
target_glm()
|
||||
|
||||
add_dependency_external_projects(nvtt)
|
||||
find_package(NVTT REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${NVTT_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${NVTT_LIBRARIES})
|
||||
add_paths_to_fixup_libs(${NVTT_DLL_PATH})
|
934
libraries/image/src/image/Image.cpp
Normal file
934
libraries/image/src/image/Image.cpp
Normal file
|
@ -0,0 +1,934 @@
|
|||
//
|
||||
// Image.cpp
|
||||
// image/src/image
|
||||
//
|
||||
// Created by Clement Brisset on 4/5/2017.
|
||||
// Copyright 2017 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 "Image.h"
|
||||
|
||||
#include <nvtt/nvtt.h>
|
||||
|
||||
#include <QUrl>
|
||||
#include <QImage>
|
||||
#include <QBuffer>
|
||||
#include <QImageReader>
|
||||
|
||||
#include <Finally.h>
|
||||
#include <Profile.h>
|
||||
#include <StatTracker.h>
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
#include "ImageLogging.h"
|
||||
|
||||
using namespace gpu;
|
||||
|
||||
#define CPU_MIPMAPS 1
|
||||
#define COMPRESS_COLOR_TEXTURES 0
|
||||
#define COMPRESS_NORMALMAP_TEXTURES 0 // Disable Normalmap compression for now
|
||||
#define COMPRESS_GRAYSCALE_TEXTURES 0
|
||||
#define COMPRESS_CUBEMAP_TEXTURES 0 // Disable Cubemap compression for now
|
||||
|
||||
static const glm::uvec2 SPARSE_PAGE_SIZE(128);
|
||||
static const glm::uvec2 MAX_TEXTURE_SIZE(4096);
|
||||
bool DEV_DECIMATE_TEXTURES = false;
|
||||
std::atomic<size_t> DECIMATED_TEXTURE_COUNT{ 0 };
|
||||
std::atomic<size_t> RECTIFIED_TEXTURE_COUNT{ 0 };
|
||||
|
||||
bool needsSparseRectification(const glm::uvec2& size) {
|
||||
// Don't attempt to rectify small textures (textures less than the sparse page size in any dimension)
|
||||
if (glm::any(glm::lessThan(size, SPARSE_PAGE_SIZE))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't rectify textures that are already an exact multiple of sparse page size
|
||||
if (glm::uvec2(0) == (size % SPARSE_PAGE_SIZE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Texture is not sparse compatible, but is bigger than the sparse page size in both dimensions, rectify!
|
||||
return true;
|
||||
}
|
||||
|
||||
glm::uvec2 rectifyToSparseSize(const glm::uvec2& size) {
|
||||
glm::uvec2 pages = ((size / SPARSE_PAGE_SIZE) + glm::clamp(size % SPARSE_PAGE_SIZE, glm::uvec2(0), glm::uvec2(1)));
|
||||
glm::uvec2 result = pages * SPARSE_PAGE_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
namespace image {
|
||||
|
||||
TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, const QVariantMap& options) {
|
||||
switch (type) {
|
||||
case ALBEDO_TEXTURE:
|
||||
return image::TextureUsage::createAlbedoTextureFromImage;
|
||||
case EMISSIVE_TEXTURE:
|
||||
return image::TextureUsage::createEmissiveTextureFromImage;
|
||||
case LIGHTMAP_TEXTURE:
|
||||
return image::TextureUsage::createLightmapTextureFromImage;
|
||||
case CUBE_TEXTURE:
|
||||
if (options.value("generateIrradiance", true).toBool()) {
|
||||
return image::TextureUsage::createCubeTextureFromImage;
|
||||
} else {
|
||||
return image::TextureUsage::createCubeTextureFromImageWithoutIrradiance;
|
||||
}
|
||||
case BUMP_TEXTURE:
|
||||
return image::TextureUsage::createNormalTextureFromBumpImage;
|
||||
case NORMAL_TEXTURE:
|
||||
return image::TextureUsage::createNormalTextureFromNormalImage;
|
||||
case ROUGHNESS_TEXTURE:
|
||||
return image::TextureUsage::createRoughnessTextureFromImage;
|
||||
case GLOSS_TEXTURE:
|
||||
return image::TextureUsage::createRoughnessTextureFromGlossImage;
|
||||
case SPECULAR_TEXTURE:
|
||||
return image::TextureUsage::createMetallicTextureFromImage;
|
||||
case STRICT_TEXTURE:
|
||||
return image::TextureUsage::createStrict2DTextureFromImage;
|
||||
|
||||
case DEFAULT_TEXTURE:
|
||||
default:
|
||||
return image::TextureUsage::create2DTextureFromImage;
|
||||
}
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createStrict2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return process2DTextureColorFromImage(srcImage, srcImageName, true);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return process2DTextureColorFromImage(srcImage, srcImageName, false);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createAlbedoTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return process2DTextureColorFromImage(srcImage, srcImageName, false);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createEmissiveTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return process2DTextureColorFromImage(srcImage, srcImageName, false);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createLightmapTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return process2DTextureColorFromImage(srcImage, srcImageName, false);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createNormalTextureFromNormalImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return process2DTextureNormalMapFromImage(srcImage, srcImageName, false);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createNormalTextureFromBumpImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return process2DTextureNormalMapFromImage(srcImage, srcImageName, true);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createRoughnessTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return process2DTextureGrayscaleFromImage(srcImage, srcImageName, false);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createRoughnessTextureFromGlossImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return process2DTextureGrayscaleFromImage(srcImage, srcImageName, true);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createMetallicTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return process2DTextureGrayscaleFromImage(srcImage, srcImageName, false);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createCubeTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return processCubeTextureColorFromImage(srcImage, srcImageName, true);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createCubeTextureFromImageWithoutIrradiance(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return processCubeTextureColorFromImage(srcImage, srcImageName, false);
|
||||
}
|
||||
|
||||
gpu::TexturePointer processImage(const QByteArray& content, const std::string& filename, int maxNumPixels, TextureUsage::Type textureType) {
|
||||
// Help the QImage loader by extracting the image file format from the url filename ext.
|
||||
// Some tga are not created properly without it.
|
||||
auto filenameExtension = filename.substr(filename.find_last_of('.') + 1);
|
||||
QBuffer buffer;
|
||||
buffer.setData(content);
|
||||
QImageReader imageReader(&buffer, filenameExtension.c_str());
|
||||
QImage image;
|
||||
|
||||
if (imageReader.canRead()) {
|
||||
image = imageReader.read();
|
||||
} else {
|
||||
// Extension could be incorrect, try to detect the format from the content
|
||||
QImageReader newImageReader;
|
||||
newImageReader.setDecideFormatFromContent(true);
|
||||
buffer.setData(content);
|
||||
newImageReader.setDevice(&buffer);
|
||||
|
||||
if (newImageReader.canRead()) {
|
||||
qCWarning(imagelogging) << "Image file" << filename.c_str() << "has extension" << filenameExtension.c_str()
|
||||
<< "but is actually a" << qPrintable(newImageReader.format()) << "(recovering)";
|
||||
image = newImageReader.read();
|
||||
}
|
||||
}
|
||||
|
||||
int imageWidth = image.width();
|
||||
int imageHeight = image.height();
|
||||
|
||||
// Validate that the image loaded
|
||||
if (imageWidth == 0 || imageHeight == 0 || image.format() == QImage::Format_Invalid) {
|
||||
QString reason(image.format() == QImage::Format_Invalid ? "(Invalid Format)" : "(Size is invalid)");
|
||||
qCWarning(imagelogging) << "Failed to load" << filename.c_str() << qPrintable(reason);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Validate the image is less than _maxNumPixels, and downscale if necessary
|
||||
if (imageWidth * imageHeight > maxNumPixels) {
|
||||
float scaleFactor = sqrtf(maxNumPixels / (float)(imageWidth * imageHeight));
|
||||
int originalWidth = imageWidth;
|
||||
int originalHeight = imageHeight;
|
||||
imageWidth = (int)(scaleFactor * (float)imageWidth + 0.5f);
|
||||
imageHeight = (int)(scaleFactor * (float)imageHeight + 0.5f);
|
||||
QImage newImage = image.scaled(QSize(imageWidth, imageHeight), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
image.swap(newImage);
|
||||
qCDebug(imagelogging).nospace() << "Downscaled " << filename.c_str() << " (" <<
|
||||
QSize(originalWidth, originalHeight) << " to " <<
|
||||
QSize(imageWidth, imageHeight) << ")";
|
||||
}
|
||||
|
||||
auto loader = TextureUsage::getTextureLoaderForType(textureType);
|
||||
auto texture = loader(image, filename);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
|
||||
|
||||
QImage processSourceImage(const QImage& srcImage, bool cubemap) {
|
||||
PROFILE_RANGE(resource_parse, "processSourceImage");
|
||||
const glm::uvec2 srcImageSize = toGlm(srcImage.size());
|
||||
glm::uvec2 targetSize = srcImageSize;
|
||||
|
||||
while (glm::any(glm::greaterThan(targetSize, MAX_TEXTURE_SIZE))) {
|
||||
targetSize /= 2;
|
||||
}
|
||||
if (targetSize != srcImageSize) {
|
||||
++DECIMATED_TEXTURE_COUNT;
|
||||
}
|
||||
|
||||
if (!cubemap && needsSparseRectification(targetSize)) {
|
||||
++RECTIFIED_TEXTURE_COUNT;
|
||||
targetSize = rectifyToSparseSize(targetSize);
|
||||
}
|
||||
|
||||
if (DEV_DECIMATE_TEXTURES && glm::all(glm::greaterThanEqual(targetSize / SPARSE_PAGE_SIZE, glm::uvec2(2)))) {
|
||||
targetSize /= 2;
|
||||
}
|
||||
|
||||
if (targetSize != srcImageSize) {
|
||||
PROFILE_RANGE(resource_parse, "processSourceImage Rectify");
|
||||
qCDebug(imagelogging) << "Resizing texture from " << srcImageSize.x << "x" << srcImageSize.y << " to " << targetSize.x << "x" << targetSize.y;
|
||||
return srcImage.scaled(fromGlm(targetSize), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
return srcImage;
|
||||
}
|
||||
|
||||
struct MyOutputHandler : public nvtt::OutputHandler {
|
||||
MyOutputHandler(gpu::Texture* texture, int face) : _texture(texture), _face(face) {}
|
||||
|
||||
virtual void beginImage(int size, int width, int height, int depth, int face, int miplevel) override {
|
||||
_size = size;
|
||||
_miplevel = miplevel;
|
||||
|
||||
_data = static_cast<gpu::Byte*>(malloc(size));
|
||||
_current = _data;
|
||||
}
|
||||
virtual bool writeData(const void* data, int size) override {
|
||||
assert(_current + size <= _data + _size);
|
||||
memcpy(_current, data, size);
|
||||
_current += size;
|
||||
return true;
|
||||
}
|
||||
virtual void endImage() override {
|
||||
if (_face >= 0) {
|
||||
_texture->assignStoredMipFace(_miplevel, _face, _size, static_cast<const gpu::Byte*>(_data));
|
||||
} else {
|
||||
_texture->assignStoredMip(_miplevel, _size, static_cast<const gpu::Byte*>(_data));
|
||||
}
|
||||
free(_data);
|
||||
_data = nullptr;
|
||||
}
|
||||
|
||||
gpu::Byte* _data{ nullptr };
|
||||
gpu::Byte* _current{ nullptr };
|
||||
gpu::Texture* _texture{ nullptr };
|
||||
int _miplevel = 0;
|
||||
int _size = 0;
|
||||
int _face = -1;
|
||||
};
|
||||
struct MyErrorHandler : public nvtt::ErrorHandler {
|
||||
virtual void error(nvtt::Error e) override {
|
||||
qCWarning(imagelogging) << "Texture compression error:" << nvtt::errorString(e);
|
||||
}
|
||||
};
|
||||
|
||||
void generateMips(gpu::Texture* texture, QImage& image, int face = -1) {
|
||||
#if CPU_MIPMAPS
|
||||
PROFILE_RANGE(resource_parse, "generateMips");
|
||||
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
|
||||
const int width = image.width(), height = image.height();
|
||||
const void* data = static_cast<const void*>(image.constBits());
|
||||
|
||||
nvtt::TextureType textureType = nvtt::TextureType_2D;
|
||||
nvtt::InputFormat inputFormat = nvtt::InputFormat_BGRA_8UB;
|
||||
nvtt::WrapMode wrapMode = nvtt::WrapMode_Repeat;
|
||||
nvtt::RoundMode roundMode = nvtt::RoundMode_None;
|
||||
nvtt::AlphaMode alphaMode = nvtt::AlphaMode_None;
|
||||
|
||||
float inputGamma = 2.2f;
|
||||
float outputGamma = 2.2f;
|
||||
|
||||
nvtt::CompressionOptions compressionOptions;
|
||||
compressionOptions.setQuality(nvtt::Quality_Production);
|
||||
|
||||
auto mipFormat = texture->getStoredMipFormat();
|
||||
if (mipFormat == gpu::Element::COLOR_COMPRESSED_SRGB) {
|
||||
compressionOptions.setFormat(nvtt::Format_BC1);
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_SRGBA_MASK) {
|
||||
alphaMode = nvtt::AlphaMode_Transparency;
|
||||
compressionOptions.setFormat(nvtt::Format_BC1a);
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_SRGBA) {
|
||||
alphaMode = nvtt::AlphaMode_Transparency;
|
||||
compressionOptions.setFormat(nvtt::Format_BC3);
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_RED) {
|
||||
compressionOptions.setFormat(nvtt::Format_BC4);
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_XY) {
|
||||
compressionOptions.setFormat(nvtt::Format_BC5);
|
||||
} else if (mipFormat == gpu::Element::COLOR_RGBA_32) {
|
||||
compressionOptions.setFormat(nvtt::Format_RGBA);
|
||||
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
|
||||
compressionOptions.setPixelFormat(32,
|
||||
0x000000FF,
|
||||
0x0000FF00,
|
||||
0x00FF0000,
|
||||
0xFF000000);
|
||||
inputGamma = 1.0f;
|
||||
outputGamma = 1.0f;
|
||||
} else if (mipFormat == gpu::Element::COLOR_BGRA_32) {
|
||||
compressionOptions.setFormat(nvtt::Format_RGBA);
|
||||
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
|
||||
compressionOptions.setPixelFormat(32,
|
||||
0x00FF0000,
|
||||
0x0000FF00,
|
||||
0x000000FF,
|
||||
0xFF000000);
|
||||
inputGamma = 1.0f;
|
||||
outputGamma = 1.0f;
|
||||
} else if (mipFormat == gpu::Element::COLOR_SRGBA_32) {
|
||||
compressionOptions.setFormat(nvtt::Format_RGBA);
|
||||
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
|
||||
compressionOptions.setPixelFormat(32,
|
||||
0x000000FF,
|
||||
0x0000FF00,
|
||||
0x00FF0000,
|
||||
0xFF000000);
|
||||
} else if (mipFormat == gpu::Element::COLOR_SBGRA_32) {
|
||||
compressionOptions.setFormat(nvtt::Format_RGBA);
|
||||
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
|
||||
compressionOptions.setPixelFormat(32,
|
||||
0x00FF0000,
|
||||
0x0000FF00,
|
||||
0x000000FF,
|
||||
0xFF000000);
|
||||
} else if (mipFormat == gpu::Element::COLOR_R_8) {
|
||||
compressionOptions.setFormat(nvtt::Format_RGB);
|
||||
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
|
||||
compressionOptions.setPixelFormat(8, 0, 0, 0);
|
||||
} else {
|
||||
qCWarning(imagelogging) << "Unknown mip format";
|
||||
Q_UNREACHABLE();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
nvtt::InputOptions inputOptions;
|
||||
inputOptions.setTextureLayout(textureType, width, height);
|
||||
inputOptions.setMipmapData(data, width, height);
|
||||
|
||||
inputOptions.setFormat(inputFormat);
|
||||
inputOptions.setGamma(inputGamma, outputGamma);
|
||||
inputOptions.setAlphaMode(alphaMode);
|
||||
inputOptions.setWrapMode(wrapMode);
|
||||
inputOptions.setRoundMode(roundMode);
|
||||
|
||||
inputOptions.setMipmapGeneration(true);
|
||||
inputOptions.setMipmapFilter(nvtt::MipmapFilter_Box);
|
||||
|
||||
nvtt::OutputOptions outputOptions;
|
||||
outputOptions.setOutputHeader(false);
|
||||
MyOutputHandler outputHandler(texture, face);
|
||||
outputOptions.setOutputHandler(&outputHandler);
|
||||
MyErrorHandler errorHandler;
|
||||
outputOptions.setErrorHandler(&errorHandler);
|
||||
|
||||
nvtt::Compressor compressor;
|
||||
compressor.process(inputOptions, compressionOptions, outputOptions);
|
||||
#else
|
||||
texture->autoGenerateMips(-1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void processTextureAlpha(const QImage& srcImage, bool& validAlpha, bool& alphaAsMask) {
|
||||
PROFILE_RANGE(resource_parse, "processTextureAlpha");
|
||||
validAlpha = false;
|
||||
alphaAsMask = true;
|
||||
const uint8 OPAQUE_ALPHA = 255;
|
||||
const uint8 TRANSPARENT_ALPHA = 0;
|
||||
|
||||
// Figure out if we can use a mask for alpha or not
|
||||
int numOpaques = 0;
|
||||
int numTranslucents = 0;
|
||||
const int NUM_PIXELS = srcImage.width() * srcImage.height();
|
||||
const int MAX_TRANSLUCENT_PIXELS_FOR_ALPHAMASK = (int)(0.05f * (float)(NUM_PIXELS));
|
||||
const QRgb* data = reinterpret_cast<const QRgb*>(srcImage.constBits());
|
||||
for (int i = 0; i < NUM_PIXELS; ++i) {
|
||||
auto alpha = qAlpha(data[i]);
|
||||
if (alpha == OPAQUE_ALPHA) {
|
||||
numOpaques++;
|
||||
} else if (alpha != TRANSPARENT_ALPHA) {
|
||||
if (++numTranslucents > MAX_TRANSLUCENT_PIXELS_FOR_ALPHAMASK) {
|
||||
alphaAsMask = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
validAlpha = (numOpaques != NUM_PIXELS);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isStrict) {
|
||||
PROFILE_RANGE(resource_parse, "process2DTextureColorFromImage");
|
||||
QImage image = processSourceImage(srcImage, false);
|
||||
bool validAlpha = image.hasAlphaChannel();
|
||||
bool alphaAsMask = false;
|
||||
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
|
||||
if (validAlpha) {
|
||||
processTextureAlpha(image, validAlpha, alphaAsMask);
|
||||
}
|
||||
|
||||
gpu::TexturePointer theTexture = nullptr;
|
||||
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
#if CPU_MIPMAPS && COMPRESS_COLOR_TEXTURES
|
||||
gpu::Element formatGPU;
|
||||
if (validAlpha) {
|
||||
formatGPU = alphaAsMask ? gpu::Element::COLOR_COMPRESSED_SRGBA_MASK : gpu::Element::COLOR_COMPRESSED_SRGBA;
|
||||
} else {
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_SRGB;
|
||||
}
|
||||
gpu::Element formatMip = formatGPU;
|
||||
#else
|
||||
gpu::Element formatMip = gpu::Element::COLOR_SBGRA_32;
|
||||
gpu::Element formatGPU = gpu::Element::COLOR_SRGBA_32;
|
||||
#endif
|
||||
|
||||
if (isStrict) {
|
||||
theTexture = gpu::Texture::createStrict(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
|
||||
} else {
|
||||
theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
|
||||
}
|
||||
theTexture->setSource(srcImageName);
|
||||
auto usage = gpu::Texture::Usage::Builder().withColor();
|
||||
if (validAlpha) {
|
||||
usage.withAlpha();
|
||||
if (alphaAsMask) {
|
||||
usage.withAlphaMask();
|
||||
}
|
||||
}
|
||||
theTexture->setUsage(usage.build());
|
||||
theTexture->setStoredMipFormat(formatMip);
|
||||
generateMips(theTexture.get(), image);
|
||||
}
|
||||
|
||||
return theTexture;
|
||||
}
|
||||
|
||||
int clampPixelCoordinate(int coordinate, int maxCoordinate) {
|
||||
return coordinate - ((int)(coordinate < 0) * coordinate) + ((int)(coordinate > maxCoordinate) * (maxCoordinate - coordinate));
|
||||
}
|
||||
|
||||
const int RGBA_MAX = 255;
|
||||
|
||||
// transform -1 - 1 to 0 - 255 (from sobel value to rgb)
|
||||
double mapComponent(double sobelValue) {
|
||||
const double factor = RGBA_MAX / 2.0;
|
||||
return (sobelValue + 1.0) * factor;
|
||||
}
|
||||
|
||||
QImage processBumpMap(QImage& image) {
|
||||
if (image.format() != QImage::Format_Grayscale8) {
|
||||
image = image.convertToFormat(QImage::Format_Grayscale8);
|
||||
}
|
||||
|
||||
// PR 5540 by AlessandroSigna integrated here as a specialized TextureLoader for bumpmaps
|
||||
// The conversion is done using the Sobel Filter to calculate the derivatives from the grayscale image
|
||||
const double pStrength = 2.0;
|
||||
int width = image.width();
|
||||
int height = image.height();
|
||||
|
||||
QImage result(width, height, QImage::Format_ARGB32);
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
const int iNextClamped = clampPixelCoordinate(i + 1, width - 1);
|
||||
const int iPrevClamped = clampPixelCoordinate(i - 1, width - 1);
|
||||
|
||||
for (int j = 0; j < height; j++) {
|
||||
const int jNextClamped = clampPixelCoordinate(j + 1, height - 1);
|
||||
const int jPrevClamped = clampPixelCoordinate(j - 1, height - 1);
|
||||
|
||||
// surrounding pixels
|
||||
const QRgb topLeft = image.pixel(iPrevClamped, jPrevClamped);
|
||||
const QRgb top = image.pixel(iPrevClamped, j);
|
||||
const QRgb topRight = image.pixel(iPrevClamped, jNextClamped);
|
||||
const QRgb right = image.pixel(i, jNextClamped);
|
||||
const QRgb bottomRight = image.pixel(iNextClamped, jNextClamped);
|
||||
const QRgb bottom = image.pixel(iNextClamped, j);
|
||||
const QRgb bottomLeft = image.pixel(iNextClamped, jPrevClamped);
|
||||
const QRgb left = image.pixel(i, jPrevClamped);
|
||||
|
||||
// take their gray intensities
|
||||
// since it's a grayscale image, the value of each component RGB is the same
|
||||
const double tl = qRed(topLeft);
|
||||
const double t = qRed(top);
|
||||
const double tr = qRed(topRight);
|
||||
const double r = qRed(right);
|
||||
const double br = qRed(bottomRight);
|
||||
const double b = qRed(bottom);
|
||||
const double bl = qRed(bottomLeft);
|
||||
const double l = qRed(left);
|
||||
|
||||
// apply the sobel filter
|
||||
const double dX = (tr + pStrength * r + br) - (tl + pStrength * l + bl);
|
||||
const double dY = (bl + pStrength * b + br) - (tl + pStrength * t + tr);
|
||||
const double dZ = RGBA_MAX / pStrength;
|
||||
|
||||
glm::vec3 v(dX, dY, dZ);
|
||||
glm::normalize(v);
|
||||
|
||||
// convert to rgb from the value obtained computing the filter
|
||||
QRgb qRgbValue = qRgba(mapComponent(v.z), mapComponent(v.y), mapComponent(v.x), 1.0);
|
||||
result.setPixel(i, j, qRgbValue);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(const QImage& srcImage, const std::string& srcImageName, bool isBumpMap) {
|
||||
PROFILE_RANGE(resource_parse, "process2DTextureNormalMapFromImage");
|
||||
QImage image = processSourceImage(srcImage, false);
|
||||
|
||||
if (isBumpMap) {
|
||||
image = processBumpMap(image);
|
||||
}
|
||||
|
||||
// Make sure the normal map source image is ARGB32
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
|
||||
gpu::TexturePointer theTexture = nullptr;
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
|
||||
#if CPU_MIPMAPS && COMPRESS_NORMALMAP_TEXTURES
|
||||
gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_XY;
|
||||
gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_XY;
|
||||
#else
|
||||
gpu::Element formatMip = gpu::Element::COLOR_RGBA_32;
|
||||
gpu::Element formatGPU = gpu::Element::COLOR_RGBA_32;
|
||||
#endif
|
||||
|
||||
theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
|
||||
theTexture->setSource(srcImageName);
|
||||
theTexture->setStoredMipFormat(formatMip);
|
||||
generateMips(theTexture.get(), image);
|
||||
}
|
||||
|
||||
return theTexture;
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(const QImage& srcImage, const std::string& srcImageName, bool isInvertedPixels) {
|
||||
PROFILE_RANGE(resource_parse, "process2DTextureGrayscaleFromImage");
|
||||
QImage image = processSourceImage(srcImage, false);
|
||||
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
|
||||
if (isInvertedPixels) {
|
||||
// Gloss turned into Rough
|
||||
image.invertPixels(QImage::InvertRgba);
|
||||
}
|
||||
|
||||
gpu::TexturePointer theTexture = nullptr;
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
|
||||
#if CPU_MIPMAPS && COMPRESS_GRAYSCALE_TEXTURES
|
||||
gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_RED;
|
||||
gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_RED;
|
||||
#else
|
||||
gpu::Element formatMip = gpu::Element::COLOR_R_8;
|
||||
gpu::Element formatGPU = gpu::Element::COLOR_R_8;
|
||||
#endif
|
||||
|
||||
theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
|
||||
theTexture->setSource(srcImageName);
|
||||
theTexture->setStoredMipFormat(formatMip);
|
||||
generateMips(theTexture.get(), image);
|
||||
}
|
||||
|
||||
return theTexture;
|
||||
}
|
||||
|
||||
class CubeLayout {
|
||||
public:
|
||||
|
||||
enum SourceProjection {
|
||||
FLAT = 0,
|
||||
EQUIRECTANGULAR,
|
||||
};
|
||||
int _type = FLAT;
|
||||
int _widthRatio = 1;
|
||||
int _heightRatio = 1;
|
||||
|
||||
class Face {
|
||||
public:
|
||||
int _x = 0;
|
||||
int _y = 0;
|
||||
bool _horizontalMirror = false;
|
||||
bool _verticalMirror = false;
|
||||
|
||||
Face() {}
|
||||
Face(int x, int y, bool horizontalMirror, bool verticalMirror) : _x(x), _y(y), _horizontalMirror(horizontalMirror), _verticalMirror(verticalMirror) {}
|
||||
};
|
||||
|
||||
Face _faceXPos;
|
||||
Face _faceXNeg;
|
||||
Face _faceYPos;
|
||||
Face _faceYNeg;
|
||||
Face _faceZPos;
|
||||
Face _faceZNeg;
|
||||
|
||||
CubeLayout(int wr, int hr, Face fXP, Face fXN, Face fYP, Face fYN, Face fZP, Face fZN) :
|
||||
_type(FLAT),
|
||||
_widthRatio(wr),
|
||||
_heightRatio(hr),
|
||||
_faceXPos(fXP),
|
||||
_faceXNeg(fXN),
|
||||
_faceYPos(fYP),
|
||||
_faceYNeg(fYN),
|
||||
_faceZPos(fZP),
|
||||
_faceZNeg(fZN) {}
|
||||
|
||||
CubeLayout(int wr, int hr) :
|
||||
_type(EQUIRECTANGULAR),
|
||||
_widthRatio(wr),
|
||||
_heightRatio(hr) {}
|
||||
|
||||
|
||||
static const CubeLayout CUBEMAP_LAYOUTS[];
|
||||
static const int NUM_CUBEMAP_LAYOUTS;
|
||||
|
||||
static int findLayout(int width, int height) {
|
||||
// Find the layout of the cubemap in the 2D image
|
||||
int foundLayout = -1;
|
||||
for (int i = 0; i < NUM_CUBEMAP_LAYOUTS; i++) {
|
||||
if ((height * CUBEMAP_LAYOUTS[i]._widthRatio) == (width * CUBEMAP_LAYOUTS[i]._heightRatio)) {
|
||||
foundLayout = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return foundLayout;
|
||||
}
|
||||
|
||||
static QImage extractEquirectangularFace(const QImage& source, gpu::Texture::CubeFace face, int faceWidth) {
|
||||
QImage image(faceWidth, faceWidth, source.format());
|
||||
|
||||
glm::vec2 dstInvSize(1.0f / (float)image.width(), 1.0f / (float)image.height());
|
||||
|
||||
struct CubeToXYZ {
|
||||
gpu::Texture::CubeFace _face;
|
||||
CubeToXYZ(gpu::Texture::CubeFace face) : _face(face) {}
|
||||
|
||||
glm::vec3 xyzFrom(const glm::vec2& uv) {
|
||||
auto faceDir = glm::normalize(glm::vec3(-1.0f + 2.0f * uv.x, -1.0f + 2.0f * uv.y, 1.0f));
|
||||
|
||||
switch (_face) {
|
||||
case gpu::Texture::CubeFace::CUBE_FACE_BACK_POS_Z:
|
||||
return glm::vec3(-faceDir.x, faceDir.y, faceDir.z);
|
||||
case gpu::Texture::CubeFace::CUBE_FACE_FRONT_NEG_Z:
|
||||
return glm::vec3(faceDir.x, faceDir.y, -faceDir.z);
|
||||
case gpu::Texture::CubeFace::CUBE_FACE_LEFT_NEG_X:
|
||||
return glm::vec3(faceDir.z, faceDir.y, faceDir.x);
|
||||
case gpu::Texture::CubeFace::CUBE_FACE_RIGHT_POS_X:
|
||||
return glm::vec3(-faceDir.z, faceDir.y, -faceDir.x);
|
||||
case gpu::Texture::CubeFace::CUBE_FACE_BOTTOM_NEG_Y:
|
||||
return glm::vec3(-faceDir.x, -faceDir.z, faceDir.y);
|
||||
case gpu::Texture::CubeFace::CUBE_FACE_TOP_POS_Y:
|
||||
default:
|
||||
return glm::vec3(-faceDir.x, faceDir.z, -faceDir.y);
|
||||
}
|
||||
}
|
||||
};
|
||||
CubeToXYZ cubeToXYZ(face);
|
||||
|
||||
struct RectToXYZ {
|
||||
RectToXYZ() {}
|
||||
|
||||
glm::vec2 uvFrom(const glm::vec3& xyz) {
|
||||
auto flatDir = glm::normalize(glm::vec2(xyz.x, xyz.z));
|
||||
auto uvRad = glm::vec2(atan2(flatDir.x, flatDir.y), asin(xyz.y));
|
||||
|
||||
const float LON_TO_RECT_U = 1.0f / (glm::pi<float>());
|
||||
const float LAT_TO_RECT_V = 2.0f / glm::pi<float>();
|
||||
return glm::vec2(0.5f * uvRad.x * LON_TO_RECT_U + 0.5f, 0.5f * uvRad.y * LAT_TO_RECT_V + 0.5f);
|
||||
}
|
||||
};
|
||||
RectToXYZ rectToXYZ;
|
||||
|
||||
int srcFaceHeight = source.height();
|
||||
int srcFaceWidth = source.width();
|
||||
|
||||
glm::vec2 dstCoord;
|
||||
glm::ivec2 srcPixel;
|
||||
for (int y = 0; y < faceWidth; ++y) {
|
||||
dstCoord.y = 1.0f - (y + 0.5f) * dstInvSize.y; // Fill cube face images from top to bottom
|
||||
for (int x = 0; x < faceWidth; ++x) {
|
||||
dstCoord.x = (x + 0.5f) * dstInvSize.x;
|
||||
|
||||
auto xyzDir = cubeToXYZ.xyzFrom(dstCoord);
|
||||
auto srcCoord = rectToXYZ.uvFrom(xyzDir);
|
||||
|
||||
srcPixel.x = floor(srcCoord.x * srcFaceWidth);
|
||||
// Flip the vertical axis to QImage going top to bottom
|
||||
srcPixel.y = floor((1.0f - srcCoord.y) * srcFaceHeight);
|
||||
|
||||
if (((uint32)srcPixel.x < (uint32)source.width()) && ((uint32)srcPixel.y < (uint32)source.height())) {
|
||||
image.setPixel(x, y, source.pixel(QPoint(srcPixel.x, srcPixel.y)));
|
||||
|
||||
// Keep for debug, this is showing the dir as a color
|
||||
// glm::u8vec4 rgba((xyzDir.x + 1.0)*0.5 * 256, (xyzDir.y + 1.0)*0.5 * 256, (xyzDir.z + 1.0)*0.5 * 256, 256);
|
||||
// unsigned int val = 0xff000000 | (rgba.r) | (rgba.g << 8) | (rgba.b << 16);
|
||||
// image.setPixel(x, y, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
return image;
|
||||
}
|
||||
};
|
||||
|
||||
const CubeLayout CubeLayout::CUBEMAP_LAYOUTS[] = {
|
||||
|
||||
// Here is the expected layout for the faces in an image with the 2/1 aspect ratio:
|
||||
// THis is detected as an Equirectangular projection
|
||||
// WIDTH
|
||||
// <--------------------------->
|
||||
// ^ +------+------+------+------+
|
||||
// H | | | | |
|
||||
// E | | | | |
|
||||
// I | | | | |
|
||||
// G +------+------+------+------+
|
||||
// H | | | | |
|
||||
// T | | | | |
|
||||
// | | | | | |
|
||||
// v +------+------+------+------+
|
||||
//
|
||||
// FaceWidth = width = height / 6
|
||||
{ 2, 1 },
|
||||
|
||||
// Here is the expected layout for the faces in an image with the 1/6 aspect ratio:
|
||||
//
|
||||
// WIDTH
|
||||
// <------>
|
||||
// ^ +------+
|
||||
// | | |
|
||||
// | | +X |
|
||||
// | | |
|
||||
// H +------+
|
||||
// E | |
|
||||
// I | -X |
|
||||
// G | |
|
||||
// H +------+
|
||||
// T | |
|
||||
// | | +Y |
|
||||
// | | |
|
||||
// | +------+
|
||||
// | | |
|
||||
// | | -Y |
|
||||
// | | |
|
||||
// H +------+
|
||||
// E | |
|
||||
// I | +Z |
|
||||
// G | |
|
||||
// H +------+
|
||||
// T | |
|
||||
// | | -Z |
|
||||
// | | |
|
||||
// V +------+
|
||||
//
|
||||
// FaceWidth = width = height / 6
|
||||
{ 1, 6,
|
||||
{ 0, 0, true, false },
|
||||
{ 0, 1, true, false },
|
||||
{ 0, 2, false, true },
|
||||
{ 0, 3, false, true },
|
||||
{ 0, 4, true, false },
|
||||
{ 0, 5, true, false }
|
||||
},
|
||||
|
||||
// Here is the expected layout for the faces in an image with the 3/4 aspect ratio:
|
||||
//
|
||||
// <-----------WIDTH----------->
|
||||
// ^ +------+------+------+------+
|
||||
// | | | | | |
|
||||
// | | | +Y | | |
|
||||
// | | | | | |
|
||||
// H +------+------+------+------+
|
||||
// E | | | | |
|
||||
// I | -X | -Z | +X | +Z |
|
||||
// G | | | | |
|
||||
// H +------+------+------+------+
|
||||
// T | | | | |
|
||||
// | | | -Y | | |
|
||||
// | | | | | |
|
||||
// V +------+------+------+------+
|
||||
//
|
||||
// FaceWidth = width / 4 = height / 3
|
||||
{ 4, 3,
|
||||
{ 2, 1, true, false },
|
||||
{ 0, 1, true, false },
|
||||
{ 1, 0, false, true },
|
||||
{ 1, 2, false, true },
|
||||
{ 3, 1, true, false },
|
||||
{ 1, 1, true, false }
|
||||
},
|
||||
|
||||
// Here is the expected layout for the faces in an image with the 4/3 aspect ratio:
|
||||
//
|
||||
// <-------WIDTH-------->
|
||||
// ^ +------+------+------+
|
||||
// | | | | |
|
||||
// | | | +Y | |
|
||||
// | | | | |
|
||||
// H +------+------+------+
|
||||
// E | | | |
|
||||
// I | -X | -Z | +X |
|
||||
// G | | | |
|
||||
// H +------+------+------+
|
||||
// T | | | |
|
||||
// | | | -Y | |
|
||||
// | | | | |
|
||||
// | +------+------+------+
|
||||
// | | | | |
|
||||
// | | | +Z! | | <+Z is upside down!
|
||||
// | | | | |
|
||||
// V +------+------+------+
|
||||
//
|
||||
// FaceWidth = width / 3 = height / 4
|
||||
{ 3, 4,
|
||||
{ 2, 1, true, false },
|
||||
{ 0, 1, true, false },
|
||||
{ 1, 0, false, true },
|
||||
{ 1, 2, false, true },
|
||||
{ 1, 3, false, true },
|
||||
{ 1, 1, true, false }
|
||||
}
|
||||
};
|
||||
const int CubeLayout::NUM_CUBEMAP_LAYOUTS = sizeof(CubeLayout::CUBEMAP_LAYOUTS) / sizeof(CubeLayout);
|
||||
|
||||
gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool generateIrradiance) {
|
||||
PROFILE_RANGE(resource_parse, "processCubeTextureColorFromImage");
|
||||
|
||||
gpu::TexturePointer theTexture = nullptr;
|
||||
if ((srcImage.width() > 0) && (srcImage.height() > 0)) {
|
||||
QImage image = processSourceImage(srcImage, true);
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
|
||||
#if CPU_MIPMAPS && COMPRESS_CUBEMAP_TEXTURES
|
||||
gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_SRGBA;
|
||||
gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_SRGBA;
|
||||
#else
|
||||
gpu::Element formatMip = gpu::Element::COLOR_SRGBA_32;
|
||||
gpu::Element formatGPU = gpu::Element::COLOR_SRGBA_32;
|
||||
#endif
|
||||
|
||||
// Find the layout of the cubemap in the 2D image
|
||||
// Use the original image size since processSourceImage may have altered the size / aspect ratio
|
||||
int foundLayout = CubeLayout::findLayout(srcImage.width(), srcImage.height());
|
||||
|
||||
std::vector<QImage> faces;
|
||||
// If found, go extract the faces as separate images
|
||||
if (foundLayout >= 0) {
|
||||
auto& layout = CubeLayout::CUBEMAP_LAYOUTS[foundLayout];
|
||||
if (layout._type == CubeLayout::FLAT) {
|
||||
int faceWidth = image.width() / layout._widthRatio;
|
||||
|
||||
faces.push_back(image.copy(QRect(layout._faceXPos._x * faceWidth, layout._faceXPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceXPos._horizontalMirror, layout._faceXPos._verticalMirror));
|
||||
faces.push_back(image.copy(QRect(layout._faceXNeg._x * faceWidth, layout._faceXNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceXNeg._horizontalMirror, layout._faceXNeg._verticalMirror));
|
||||
faces.push_back(image.copy(QRect(layout._faceYPos._x * faceWidth, layout._faceYPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceYPos._horizontalMirror, layout._faceYPos._verticalMirror));
|
||||
faces.push_back(image.copy(QRect(layout._faceYNeg._x * faceWidth, layout._faceYNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceYNeg._horizontalMirror, layout._faceYNeg._verticalMirror));
|
||||
faces.push_back(image.copy(QRect(layout._faceZPos._x * faceWidth, layout._faceZPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZPos._horizontalMirror, layout._faceZPos._verticalMirror));
|
||||
faces.push_back(image.copy(QRect(layout._faceZNeg._x * faceWidth, layout._faceZNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZNeg._horizontalMirror, layout._faceZNeg._verticalMirror));
|
||||
} else if (layout._type == CubeLayout::EQUIRECTANGULAR) {
|
||||
// THe face width is estimated from the input image
|
||||
const int EQUIRECT_FACE_RATIO_TO_WIDTH = 4;
|
||||
const int EQUIRECT_MAX_FACE_WIDTH = 2048;
|
||||
int faceWidth = std::min(image.width() / EQUIRECT_FACE_RATIO_TO_WIDTH, EQUIRECT_MAX_FACE_WIDTH);
|
||||
for (int face = gpu::Texture::CUBE_FACE_RIGHT_POS_X; face < gpu::Texture::NUM_CUBE_FACES; face++) {
|
||||
QImage faceImage = CubeLayout::extractEquirectangularFace(image, (gpu::Texture::CubeFace) face, faceWidth);
|
||||
faces.push_back(faceImage);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qCDebug(imagelogging) << "Failed to find a known cube map layout from this image:" << QString(srcImageName.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If the 6 faces have been created go on and define the true Texture
|
||||
if (faces.size() == gpu::Texture::NUM_FACES_PER_TYPE[gpu::Texture::TEX_CUBE]) {
|
||||
theTexture = gpu::Texture::createCube(formatGPU, faces[0].width(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
|
||||
theTexture->setSource(srcImageName);
|
||||
theTexture->setStoredMipFormat(formatMip);
|
||||
|
||||
for (uint8 face = 0; face < faces.size(); ++face) {
|
||||
generateMips(theTexture.get(), faces[face], face);
|
||||
}
|
||||
|
||||
// Generate irradiance while we are at it
|
||||
if (generateIrradiance) {
|
||||
PROFILE_RANGE(resource_parse, "generateIrradiance");
|
||||
auto irradianceTexture = gpu::Texture::createCube(gpu::Element::COLOR_SRGBA_32, faces[0].width(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
|
||||
irradianceTexture->setSource(srcImageName);
|
||||
irradianceTexture->setStoredMipFormat(gpu::Element::COLOR_SBGRA_32);
|
||||
for (uint8 face = 0; face < faces.size(); ++face) {
|
||||
irradianceTexture->assignStoredMipFace(0, face, faces[face].byteCount(), faces[face].constBits());
|
||||
}
|
||||
|
||||
irradianceTexture->generateIrradiance();
|
||||
|
||||
auto irradiance = irradianceTexture->getIrradiance();
|
||||
theTexture->overrideIrradiance(irradiance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return theTexture;
|
||||
}
|
||||
|
||||
} // namespace image
|
70
libraries/image/src/image/Image.h
Normal file
70
libraries/image/src/image/Image.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// Image.h
|
||||
// image/src/image
|
||||
//
|
||||
// Created by Clement Brisset on 4/5/2017.
|
||||
// Copyright 2017 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_image_Image_h
|
||||
#define hifi_image_Image_h
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
#include <gpu/Texture.h>
|
||||
|
||||
class QByteArray;
|
||||
class QImage;
|
||||
|
||||
namespace image {
|
||||
|
||||
namespace TextureUsage {
|
||||
|
||||
enum Type {
|
||||
DEFAULT_TEXTURE,
|
||||
STRICT_TEXTURE,
|
||||
ALBEDO_TEXTURE,
|
||||
NORMAL_TEXTURE,
|
||||
BUMP_TEXTURE,
|
||||
SPECULAR_TEXTURE,
|
||||
METALLIC_TEXTURE = SPECULAR_TEXTURE, // for now spec and metallic texture are the same, converted to grey
|
||||
ROUGHNESS_TEXTURE,
|
||||
GLOSS_TEXTURE,
|
||||
EMISSIVE_TEXTURE,
|
||||
CUBE_TEXTURE,
|
||||
OCCLUSION_TEXTURE,
|
||||
SCATTERING_TEXTURE = OCCLUSION_TEXTURE,
|
||||
LIGHTMAP_TEXTURE
|
||||
};
|
||||
|
||||
using TextureLoader = std::function<gpu::TexturePointer(const QImage&, const std::string&)>;
|
||||
TextureLoader getTextureLoaderForType(Type type, const QVariantMap& options = QVariantMap());
|
||||
|
||||
gpu::TexturePointer create2DTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||
gpu::TexturePointer createStrict2DTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||
gpu::TexturePointer createAlbedoTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||
gpu::TexturePointer createEmissiveTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||
gpu::TexturePointer createNormalTextureFromNormalImage(const QImage& image, const std::string& srcImageName);
|
||||
gpu::TexturePointer createNormalTextureFromBumpImage(const QImage& image, const std::string& srcImageName);
|
||||
gpu::TexturePointer createRoughnessTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||
gpu::TexturePointer createRoughnessTextureFromGlossImage(const QImage& image, const std::string& srcImageName);
|
||||
gpu::TexturePointer createMetallicTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||
gpu::TexturePointer createCubeTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||
gpu::TexturePointer createCubeTextureFromImageWithoutIrradiance(const QImage& image, const std::string& srcImageName);
|
||||
gpu::TexturePointer createLightmapTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||
|
||||
gpu::TexturePointer process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isStrict);
|
||||
gpu::TexturePointer process2DTextureNormalMapFromImage(const QImage& srcImage, const std::string& srcImageName, bool isBumpMap);
|
||||
gpu::TexturePointer process2DTextureGrayscaleFromImage(const QImage& srcImage, const std::string& srcImageName, bool isInvertedPixels);
|
||||
gpu::TexturePointer processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool generateIrradiance);
|
||||
|
||||
} // namespace TextureUsage
|
||||
|
||||
gpu::TexturePointer processImage(const QByteArray& content, const std::string& url, int maxNumPixels, TextureUsage::Type textureType);
|
||||
|
||||
} // namespace image
|
||||
|
||||
#endif // hifi_image_Image_h
|
14
libraries/image/src/image/ImageLogging.cpp
Normal file
14
libraries/image/src/image/ImageLogging.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// ImageLogging.cpp
|
||||
// image/src/image
|
||||
//
|
||||
// Created by Clement Brisset on 4/5/2017.
|
||||
// Copyright 2017 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 "ImageLogging.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(imagelogging, "hifi.image")
|
14
libraries/image/src/image/ImageLogging.h
Normal file
14
libraries/image/src/image/ImageLogging.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// ImageLogging.h
|
||||
// image/src/image
|
||||
//
|
||||
// Created by Clement Brisset on 4/5/2017.
|
||||
// Copyright 2017 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 <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(imagelogging)
|
|
@ -101,8 +101,6 @@ namespace ktx {
|
|||
UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B,
|
||||
UNSIGNED_INT_5_9_9_9_REV = 0x8C3E,
|
||||
FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD,
|
||||
|
||||
NUM_GLTYPES = 25,
|
||||
};
|
||||
|
||||
enum class GLFormat : uint32_t {
|
||||
|
@ -130,8 +128,6 @@ namespace ktx {
|
|||
RGBA_INTEGER = 0x8D99,
|
||||
BGR_INTEGER = 0x8D9A,
|
||||
BGRA_INTEGER = 0x8D9B,
|
||||
|
||||
NUM_GLFORMATS = 20,
|
||||
};
|
||||
|
||||
enum class GLInternalFormat_Uncompressed : uint32_t {
|
||||
|
@ -232,8 +228,6 @@ namespace ktx {
|
|||
STENCIL_INDEX4 = 0x8D47,
|
||||
STENCIL_INDEX8 = 0x8D48,
|
||||
STENCIL_INDEX16 = 0x8D49,
|
||||
|
||||
NUM_UNCOMPRESSED_GLINTERNALFORMATS = 74,
|
||||
};
|
||||
|
||||
enum class GLInternalFormat_Compressed : uint32_t {
|
||||
|
@ -246,6 +240,11 @@ namespace ktx {
|
|||
COMPRESSED_SRGB = 0x8C48,
|
||||
COMPRESSED_SRGB_ALPHA = 0x8C49,
|
||||
|
||||
COMPRESSED_SRGB_S3TC_DXT1_EXT = 0x8C4C,
|
||||
COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT = 0x8C4D,
|
||||
COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT = 0x8C4E,
|
||||
COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT = 0x8C4F,
|
||||
|
||||
COMPRESSED_RED_RGTC1 = 0x8DBB,
|
||||
COMPRESSED_SIGNED_RED_RGTC1 = 0x8DBC,
|
||||
COMPRESSED_RG_RGTC2 = 0x8DBD,
|
||||
|
@ -267,8 +266,6 @@ namespace ktx {
|
|||
COMPRESSED_SIGNED_R11_EAC = 0x9271,
|
||||
COMPRESSED_RG11_EAC = 0x9272,
|
||||
COMPRESSED_SIGNED_RG11_EAC = 0x9273,
|
||||
|
||||
NUM_COMPRESSED_GLINTERNALFORMATS = 24,
|
||||
};
|
||||
|
||||
enum class GLBaseInternalFormat : uint32_t {
|
||||
|
@ -280,8 +277,6 @@ namespace ktx {
|
|||
RGB = 0x1907,
|
||||
RGBA = 0x1908,
|
||||
STENCIL_INDEX = 0x1901,
|
||||
|
||||
NUM_GLBASEINTERNALFORMATS = 7,
|
||||
};
|
||||
|
||||
enum CubeMapFace {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
set(TARGET_NAME model-networking)
|
||||
setup_hifi_library()
|
||||
link_hifi_libraries(shared networking model fbx ktx)
|
||||
link_hifi_libraries(shared networking model fbx ktx image)
|
||||
|
||||
|
|
|
@ -489,7 +489,7 @@ QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const FBXTexture& textu
|
|||
}
|
||||
|
||||
model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture,
|
||||
TextureType type, MapChannel channel) {
|
||||
image::TextureUsage::Type type, MapChannel channel) {
|
||||
const auto url = getTextureUrl(baseUrl, fbxTexture);
|
||||
const auto texture = DependencyManager::get<TextureCache>()->getTexture(url, type, fbxTexture.content, fbxTexture.maxNumPixels);
|
||||
_textures[channel] = Texture { fbxTexture.name, texture };
|
||||
|
@ -503,7 +503,7 @@ model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, c
|
|||
return map;
|
||||
}
|
||||
|
||||
model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, TextureType type, MapChannel channel) {
|
||||
model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel) {
|
||||
const auto texture = DependencyManager::get<TextureCache>()->getTexture(url, type);
|
||||
_textures[channel].texture = texture;
|
||||
|
||||
|
@ -518,7 +518,7 @@ NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textur
|
|||
{
|
||||
_textures = Textures(MapChannel::NUM_MAP_CHANNELS);
|
||||
if (!material.albedoTexture.filename.isEmpty()) {
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, NetworkTexture::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
|
||||
_albedoTransform = material.albedoTexture.transform;
|
||||
map->setTextureTransform(_albedoTransform);
|
||||
|
||||
|
@ -535,45 +535,45 @@ NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textur
|
|||
|
||||
|
||||
if (!material.normalTexture.filename.isEmpty()) {
|
||||
auto type = (material.normalTexture.isBumpmap ? NetworkTexture::BUMP_TEXTURE : NetworkTexture::NORMAL_TEXTURE);
|
||||
auto type = (material.normalTexture.isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE);
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.normalTexture, type, MapChannel::NORMAL_MAP);
|
||||
setTextureMap(MapChannel::NORMAL_MAP, map);
|
||||
}
|
||||
|
||||
if (!material.roughnessTexture.filename.isEmpty()) {
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.roughnessTexture, NetworkTexture::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.roughnessTexture, image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
|
||||
setTextureMap(MapChannel::ROUGHNESS_MAP, map);
|
||||
} else if (!material.glossTexture.filename.isEmpty()) {
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.glossTexture, NetworkTexture::GLOSS_TEXTURE, MapChannel::ROUGHNESS_MAP);
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.glossTexture, image::TextureUsage::GLOSS_TEXTURE, MapChannel::ROUGHNESS_MAP);
|
||||
setTextureMap(MapChannel::ROUGHNESS_MAP, map);
|
||||
}
|
||||
|
||||
if (!material.metallicTexture.filename.isEmpty()) {
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.metallicTexture, NetworkTexture::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.metallicTexture, image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
|
||||
setTextureMap(MapChannel::METALLIC_MAP, map);
|
||||
} else if (!material.specularTexture.filename.isEmpty()) {
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.specularTexture, NetworkTexture::SPECULAR_TEXTURE, MapChannel::METALLIC_MAP);
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.specularTexture, image::TextureUsage::SPECULAR_TEXTURE, MapChannel::METALLIC_MAP);
|
||||
setTextureMap(MapChannel::METALLIC_MAP, map);
|
||||
}
|
||||
|
||||
if (!material.occlusionTexture.filename.isEmpty()) {
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.occlusionTexture, NetworkTexture::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.occlusionTexture, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
|
||||
map->setTextureTransform(material.occlusionTexture.transform);
|
||||
setTextureMap(MapChannel::OCCLUSION_MAP, map);
|
||||
}
|
||||
|
||||
if (!material.emissiveTexture.filename.isEmpty()) {
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.emissiveTexture, NetworkTexture::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.emissiveTexture, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
|
||||
setTextureMap(MapChannel::EMISSIVE_MAP, map);
|
||||
}
|
||||
|
||||
if (!material.scatteringTexture.filename.isEmpty()) {
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.scatteringTexture, NetworkTexture::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.scatteringTexture, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
|
||||
setTextureMap(MapChannel::SCATTERING_MAP, map);
|
||||
}
|
||||
|
||||
if (!material.lightmapTexture.filename.isEmpty()) {
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.lightmapTexture, NetworkTexture::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
|
||||
auto map = fetchTextureMap(textureBaseUrl, material.lightmapTexture, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
|
||||
_lightmapTransform = material.lightmapTexture.transform;
|
||||
_lightmapParams = material.lightmapParams;
|
||||
map->setTextureTransform(_lightmapTransform);
|
||||
|
@ -596,7 +596,7 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) {
|
|||
|
||||
if (!albedoName.isEmpty()) {
|
||||
auto url = textureMap.contains(albedoName) ? textureMap[albedoName].toUrl() : QUrl();
|
||||
auto map = fetchTextureMap(url, NetworkTexture::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
|
||||
auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
|
||||
map->setTextureTransform(_albedoTransform);
|
||||
// when reassigning the albedo texture we also check for the alpha channel used as opacity
|
||||
map->setUseAlphaChannel(true);
|
||||
|
@ -605,45 +605,45 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) {
|
|||
|
||||
if (!normalName.isEmpty()) {
|
||||
auto url = textureMap.contains(normalName) ? textureMap[normalName].toUrl() : QUrl();
|
||||
auto map = fetchTextureMap(url, NetworkTexture::NORMAL_TEXTURE, MapChannel::NORMAL_MAP);
|
||||
auto map = fetchTextureMap(url, image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP);
|
||||
setTextureMap(MapChannel::NORMAL_MAP, map);
|
||||
}
|
||||
|
||||
if (!roughnessName.isEmpty()) {
|
||||
auto url = textureMap.contains(roughnessName) ? textureMap[roughnessName].toUrl() : QUrl();
|
||||
// FIXME: If passing a gloss map instead of a roughmap how do we know?
|
||||
auto map = fetchTextureMap(url, NetworkTexture::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
|
||||
auto map = fetchTextureMap(url, image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
|
||||
setTextureMap(MapChannel::ROUGHNESS_MAP, map);
|
||||
}
|
||||
|
||||
if (!metallicName.isEmpty()) {
|
||||
auto url = textureMap.contains(metallicName) ? textureMap[metallicName].toUrl() : QUrl();
|
||||
// FIXME: If passing a specular map instead of a metallic how do we know?
|
||||
auto map = fetchTextureMap(url, NetworkTexture::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
|
||||
auto map = fetchTextureMap(url, image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
|
||||
setTextureMap(MapChannel::METALLIC_MAP, map);
|
||||
}
|
||||
|
||||
if (!occlusionName.isEmpty()) {
|
||||
auto url = textureMap.contains(occlusionName) ? textureMap[occlusionName].toUrl() : QUrl();
|
||||
auto map = fetchTextureMap(url, NetworkTexture::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
|
||||
auto map = fetchTextureMap(url, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
|
||||
setTextureMap(MapChannel::OCCLUSION_MAP, map);
|
||||
}
|
||||
|
||||
if (!emissiveName.isEmpty()) {
|
||||
auto url = textureMap.contains(emissiveName) ? textureMap[emissiveName].toUrl() : QUrl();
|
||||
auto map = fetchTextureMap(url, NetworkTexture::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
|
||||
auto map = fetchTextureMap(url, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
|
||||
setTextureMap(MapChannel::EMISSIVE_MAP, map);
|
||||
}
|
||||
|
||||
if (!scatteringName.isEmpty()) {
|
||||
auto url = textureMap.contains(scatteringName) ? textureMap[scatteringName].toUrl() : QUrl();
|
||||
auto map = fetchTextureMap(url, NetworkTexture::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
|
||||
auto map = fetchTextureMap(url, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
|
||||
setTextureMap(MapChannel::SCATTERING_MAP, map);
|
||||
}
|
||||
|
||||
if (!lightmapName.isEmpty()) {
|
||||
auto url = textureMap.contains(lightmapName) ? textureMap[lightmapName].toUrl() : QUrl();
|
||||
auto map = fetchTextureMap(url, NetworkTexture::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
|
||||
auto map = fetchTextureMap(url, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
|
||||
map->setTextureTransform(_lightmapTransform);
|
||||
map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y);
|
||||
setTextureMap(MapChannel::LIGHTMAP_MAP, map);
|
||||
|
|
|
@ -180,13 +180,11 @@ protected:
|
|||
const bool& isOriginal() const { return _isOriginal; }
|
||||
|
||||
private:
|
||||
using TextureType = NetworkTexture::Type;
|
||||
|
||||
// Helpers for the ctors
|
||||
QUrl getTextureUrl(const QUrl& baseUrl, const FBXTexture& fbxTexture);
|
||||
model::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture,
|
||||
TextureType type, MapChannel channel);
|
||||
model::TextureMapPointer fetchTextureMap(const QUrl& url, TextureType type, MapChannel channel);
|
||||
image::TextureUsage::Type type, MapChannel channel);
|
||||
model::TextureMapPointer fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel);
|
||||
|
||||
Transform _albedoTransform;
|
||||
Transform _lightmapTransform;
|
||||
|
|
|
@ -13,11 +13,12 @@
|
|||
|
||||
#include <mutex>
|
||||
|
||||
#include <QNetworkReply>
|
||||
#include <QPainter>
|
||||
#include <QCryptographicHash>
|
||||
#include <QImageReader>
|
||||
#include <QRunnable>
|
||||
#include <QThreadPool>
|
||||
#include <QImageReader>
|
||||
#include <QNetworkReply>
|
||||
#include <QPainter>
|
||||
|
||||
#if DEBUG_DUMP_TEXTURE_LOADS
|
||||
#include <QtCore/QFile>
|
||||
|
@ -31,10 +32,13 @@
|
|||
|
||||
#include <ktx/KTX.h>
|
||||
|
||||
#include <image/Image.h>
|
||||
|
||||
#include <NumericalConstants.h>
|
||||
#include <shared/NsightHelpers.h>
|
||||
|
||||
#include <Finally.h>
|
||||
#include <Profile.h>
|
||||
|
||||
#include "ModelNetworkingLogging.h"
|
||||
#include <Trace.h>
|
||||
|
@ -51,16 +55,6 @@ TextureCache::TextureCache() :
|
|||
_ktxCache(KTX_DIRNAME, KTX_EXT) {
|
||||
setUnusedResourceCacheSize(0);
|
||||
setObjectName("TextureCache");
|
||||
|
||||
// Expose enum Type to JS/QML via properties
|
||||
// Despite being one-off, this should be fine, because TextureCache is a SINGLETON_DEPENDENCY
|
||||
QObject* type = new QObject(this);
|
||||
type->setObjectName("TextureType");
|
||||
setProperty("Type", QVariant::fromValue(type));
|
||||
auto metaEnum = QMetaEnum::fromType<Type>();
|
||||
for (int i = 0; i < metaEnum.keyCount(); ++i) {
|
||||
type->setProperty(metaEnum.key(i), metaEnum.value(i));
|
||||
}
|
||||
}
|
||||
|
||||
TextureCache::~TextureCache() {
|
||||
|
@ -117,7 +111,7 @@ const gpu::TexturePointer& TextureCache::getPermutationNormalTexture() {
|
|||
data[i + 2] = ((randvec.z + 1.0f) / 2.0f) * 255.0f;
|
||||
}
|
||||
|
||||
_permutationNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), 256, 2));
|
||||
_permutationNormalTexture = gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), 256, 2);
|
||||
_permutationNormalTexture->setStoredMipFormat(_permutationNormalTexture->getTexelFormat());
|
||||
_permutationNormalTexture->assignStoredMip(0, sizeof(data), data);
|
||||
}
|
||||
|
@ -131,7 +125,7 @@ const unsigned char OPAQUE_BLACK[] = { 0x00, 0x00, 0x00, 0xFF };
|
|||
|
||||
const gpu::TexturePointer& TextureCache::getWhiteTexture() {
|
||||
if (!_whiteTexture) {
|
||||
_whiteTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1));
|
||||
_whiteTexture = gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1);
|
||||
_whiteTexture->setSource("TextureCache::_whiteTexture");
|
||||
_whiteTexture->setStoredMipFormat(_whiteTexture->getTexelFormat());
|
||||
_whiteTexture->assignStoredMip(0, sizeof(OPAQUE_WHITE), OPAQUE_WHITE);
|
||||
|
@ -141,7 +135,7 @@ const gpu::TexturePointer& TextureCache::getWhiteTexture() {
|
|||
|
||||
const gpu::TexturePointer& TextureCache::getGrayTexture() {
|
||||
if (!_grayTexture) {
|
||||
_grayTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1));
|
||||
_grayTexture = gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1);
|
||||
_grayTexture->setSource("TextureCache::_grayTexture");
|
||||
_grayTexture->setStoredMipFormat(_grayTexture->getTexelFormat());
|
||||
_grayTexture->assignStoredMip(0, sizeof(OPAQUE_GRAY), OPAQUE_GRAY);
|
||||
|
@ -151,7 +145,7 @@ const gpu::TexturePointer& TextureCache::getGrayTexture() {
|
|||
|
||||
const gpu::TexturePointer& TextureCache::getBlueTexture() {
|
||||
if (!_blueTexture) {
|
||||
_blueTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1));
|
||||
_blueTexture = gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1);
|
||||
_blueTexture->setSource("TextureCache::_blueTexture");
|
||||
_blueTexture->setStoredMipFormat(_blueTexture->getTexelFormat());
|
||||
_blueTexture->assignStoredMip(0, sizeof(OPAQUE_BLUE), OPAQUE_BLUE);
|
||||
|
@ -161,7 +155,7 @@ const gpu::TexturePointer& TextureCache::getBlueTexture() {
|
|||
|
||||
const gpu::TexturePointer& TextureCache::getBlackTexture() {
|
||||
if (!_blackTexture) {
|
||||
_blackTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1));
|
||||
_blackTexture = gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1);
|
||||
_blackTexture->setSource("TextureCache::_blackTexture");
|
||||
_blackTexture->setStoredMipFormat(_blackTexture->getTexelFormat());
|
||||
_blackTexture->assignStoredMip(0, sizeof(OPAQUE_BLACK), OPAQUE_BLACK);
|
||||
|
@ -172,18 +166,18 @@ const gpu::TexturePointer& TextureCache::getBlackTexture() {
|
|||
/// Extra data for creating textures.
|
||||
class TextureExtra {
|
||||
public:
|
||||
NetworkTexture::Type type;
|
||||
image::TextureUsage::Type type;
|
||||
const QByteArray& content;
|
||||
int maxNumPixels;
|
||||
};
|
||||
|
||||
ScriptableResource* TextureCache::prefetch(const QUrl& url, int type, int maxNumPixels) {
|
||||
auto byteArray = QByteArray();
|
||||
TextureExtra extra = { (Type)type, byteArray, maxNumPixels };
|
||||
TextureExtra extra = { (image::TextureUsage::Type)type, byteArray, maxNumPixels };
|
||||
return ResourceCache::prefetch(url, &extra);
|
||||
}
|
||||
|
||||
NetworkTexturePointer TextureCache::getTexture(const QUrl& url, Type type, const QByteArray& content, int maxNumPixels) {
|
||||
NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) {
|
||||
TextureExtra extra = { type, content, maxNumPixels };
|
||||
return ResourceCache::getResource(url, QUrl(), &extra).staticCast<NetworkTexture>();
|
||||
}
|
||||
|
@ -216,8 +210,7 @@ gpu::TexturePointer TextureCache::cacheTextureByHash(const std::string& hash, co
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
gpu::TexturePointer getFallbackTextureForType(NetworkTexture::Type type) {
|
||||
gpu::TexturePointer getFallbackTextureForType(image::TextureUsage::Type type) {
|
||||
gpu::TexturePointer result;
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
// Since this can be called on a background thread, there's a chance that the cache
|
||||
|
@ -226,116 +219,51 @@ gpu::TexturePointer getFallbackTextureForType(NetworkTexture::Type type) {
|
|||
return result;
|
||||
}
|
||||
switch (type) {
|
||||
case NetworkTexture::DEFAULT_TEXTURE:
|
||||
case NetworkTexture::ALBEDO_TEXTURE:
|
||||
case NetworkTexture::ROUGHNESS_TEXTURE:
|
||||
case NetworkTexture::OCCLUSION_TEXTURE:
|
||||
case image::TextureUsage::DEFAULT_TEXTURE:
|
||||
case image::TextureUsage::ALBEDO_TEXTURE:
|
||||
case image::TextureUsage::ROUGHNESS_TEXTURE:
|
||||
case image::TextureUsage::OCCLUSION_TEXTURE:
|
||||
result = textureCache->getWhiteTexture();
|
||||
break;
|
||||
|
||||
case NetworkTexture::NORMAL_TEXTURE:
|
||||
case image::TextureUsage::NORMAL_TEXTURE:
|
||||
result = textureCache->getBlueTexture();
|
||||
break;
|
||||
|
||||
case NetworkTexture::EMISSIVE_TEXTURE:
|
||||
case NetworkTexture::LIGHTMAP_TEXTURE:
|
||||
case image::TextureUsage::EMISSIVE_TEXTURE:
|
||||
case image::TextureUsage::LIGHTMAP_TEXTURE:
|
||||
result = textureCache->getBlackTexture();
|
||||
break;
|
||||
|
||||
case NetworkTexture::BUMP_TEXTURE:
|
||||
case NetworkTexture::SPECULAR_TEXTURE:
|
||||
case NetworkTexture::GLOSS_TEXTURE:
|
||||
case NetworkTexture::CUBE_TEXTURE:
|
||||
case NetworkTexture::CUSTOM_TEXTURE:
|
||||
case NetworkTexture::STRICT_TEXTURE:
|
||||
case image::TextureUsage::BUMP_TEXTURE:
|
||||
case image::TextureUsage::SPECULAR_TEXTURE:
|
||||
case image::TextureUsage::GLOSS_TEXTURE:
|
||||
case image::TextureUsage::CUBE_TEXTURE:
|
||||
case image::TextureUsage::STRICT_TEXTURE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type type,
|
||||
const QVariantMap& options = QVariantMap()) {
|
||||
using Type = NetworkTexture;
|
||||
|
||||
switch (type) {
|
||||
case Type::ALBEDO_TEXTURE: {
|
||||
return model::TextureUsage::createAlbedoTextureFromImage;
|
||||
break;
|
||||
}
|
||||
case Type::EMISSIVE_TEXTURE: {
|
||||
return model::TextureUsage::createEmissiveTextureFromImage;
|
||||
break;
|
||||
}
|
||||
case Type::LIGHTMAP_TEXTURE: {
|
||||
return model::TextureUsage::createLightmapTextureFromImage;
|
||||
break;
|
||||
}
|
||||
case Type::CUBE_TEXTURE: {
|
||||
if (options.value("generateIrradiance", true).toBool()) {
|
||||
return model::TextureUsage::createCubeTextureFromImage;
|
||||
} else {
|
||||
return model::TextureUsage::createCubeTextureFromImageWithoutIrradiance;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Type::BUMP_TEXTURE: {
|
||||
return model::TextureUsage::createNormalTextureFromBumpImage;
|
||||
break;
|
||||
}
|
||||
case Type::NORMAL_TEXTURE: {
|
||||
return model::TextureUsage::createNormalTextureFromNormalImage;
|
||||
break;
|
||||
}
|
||||
case Type::ROUGHNESS_TEXTURE: {
|
||||
return model::TextureUsage::createRoughnessTextureFromImage;
|
||||
break;
|
||||
}
|
||||
case Type::GLOSS_TEXTURE: {
|
||||
return model::TextureUsage::createRoughnessTextureFromGlossImage;
|
||||
break;
|
||||
}
|
||||
case Type::SPECULAR_TEXTURE: {
|
||||
return model::TextureUsage::createMetallicTextureFromImage;
|
||||
break;
|
||||
}
|
||||
case Type::STRICT_TEXTURE: {
|
||||
return model::TextureUsage::createStrict2DTextureFromImage;
|
||||
break;
|
||||
}
|
||||
case Type::CUSTOM_TEXTURE: {
|
||||
Q_ASSERT(false);
|
||||
return NetworkTexture::TextureLoaderFunc();
|
||||
break;
|
||||
}
|
||||
|
||||
case Type::DEFAULT_TEXTURE:
|
||||
default: {
|
||||
return model::TextureUsage::create2DTextureFromImage;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a texture version of an image file
|
||||
gpu::TexturePointer TextureCache::getImageTexture(const QString& path, Type type, QVariantMap options) {
|
||||
gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::TextureUsage::Type type, QVariantMap options) {
|
||||
QImage image = QImage(path);
|
||||
auto loader = getTextureLoaderForType(type, options);
|
||||
auto loader = image::TextureUsage::getTextureLoaderForType(type, options);
|
||||
return gpu::TexturePointer(loader(image, QUrl::fromLocalFile(path).fileName().toStdString()));
|
||||
}
|
||||
|
||||
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
|
||||
const void* extra) {
|
||||
const TextureExtra* textureExtra = static_cast<const TextureExtra*>(extra);
|
||||
auto type = textureExtra ? textureExtra->type : Type::DEFAULT_TEXTURE;
|
||||
auto type = textureExtra ? textureExtra->type : image::TextureUsage::DEFAULT_TEXTURE;
|
||||
auto content = textureExtra ? textureExtra->content : QByteArray();
|
||||
auto maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS;
|
||||
NetworkTexture* texture = new NetworkTexture(url, type, content, maxNumPixels);
|
||||
return QSharedPointer<Resource>(texture, &Resource::deleter);
|
||||
}
|
||||
|
||||
NetworkTexture::NetworkTexture(const QUrl& url, Type type, const QByteArray& content, int maxNumPixels) :
|
||||
NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) :
|
||||
Resource(url),
|
||||
_type(type),
|
||||
_maxNumPixels(maxNumPixels)
|
||||
|
@ -353,13 +281,6 @@ NetworkTexture::NetworkTexture(const QUrl& url, Type type, const QByteArray& con
|
|||
}
|
||||
}
|
||||
|
||||
NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
|
||||
if (_type == CUSTOM_TEXTURE) {
|
||||
return _textureLoader;
|
||||
}
|
||||
return getTextureLoaderForType(_type);
|
||||
}
|
||||
|
||||
void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth,
|
||||
int originalHeight) {
|
||||
_originalWidth = originalWidth;
|
||||
|
@ -384,34 +305,22 @@ void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth,
|
|||
}
|
||||
|
||||
gpu::TexturePointer NetworkTexture::getFallbackTexture() const {
|
||||
if (_type == CUSTOM_TEXTURE) {
|
||||
return gpu::TexturePointer();
|
||||
}
|
||||
return getFallbackTextureForType(_type);
|
||||
}
|
||||
|
||||
class Reader : public QRunnable {
|
||||
public:
|
||||
Reader(const QWeakPointer<Resource>& resource, const QUrl& url);
|
||||
void run() override final;
|
||||
virtual void read() = 0;
|
||||
|
||||
protected:
|
||||
QWeakPointer<Resource> _resource;
|
||||
QUrl _url;
|
||||
};
|
||||
|
||||
class ImageReader : public Reader {
|
||||
class ImageReader : public QRunnable {
|
||||
public:
|
||||
ImageReader(const QWeakPointer<Resource>& resource, const QUrl& url,
|
||||
const QByteArray& data, const std::string& hash, int maxNumPixels);
|
||||
void read() override final;
|
||||
const QByteArray& data, int maxNumPixels);
|
||||
void run() override final;
|
||||
void read();
|
||||
|
||||
private:
|
||||
static void listSupportedImageFormats();
|
||||
|
||||
QWeakPointer<Resource> _resource;
|
||||
QUrl _url;
|
||||
QByteArray _content;
|
||||
std::string _hash;
|
||||
int _maxNumPixels;
|
||||
};
|
||||
|
||||
|
@ -420,71 +329,16 @@ void NetworkTexture::downloadFinished(const QByteArray& data) {
|
|||
}
|
||||
|
||||
void NetworkTexture::loadContent(const QByteArray& content) {
|
||||
// Hash the source image to for KTX caching
|
||||
std::string hash;
|
||||
{
|
||||
QCryptographicHash hasher(QCryptographicHash::Md5);
|
||||
hasher.addData(content);
|
||||
hash = hasher.result().toHex().toStdString();
|
||||
}
|
||||
|
||||
auto textureCache = static_cast<TextureCache*>(_cache.data());
|
||||
|
||||
if (textureCache != nullptr) {
|
||||
// If we already have a live texture with the same hash, use it
|
||||
auto texture = textureCache->getTextureByHash(hash);
|
||||
|
||||
// If there is no live texture, check if there's an existing KTX file
|
||||
if (!texture) {
|
||||
KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash);
|
||||
if (ktxFile) {
|
||||
texture.reset(gpu::Texture::unserialize(ktxFile->getFilepath()));
|
||||
if (texture) {
|
||||
texture = textureCache->cacheTextureByHash(hash, texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we found the texture either because it's in use or via KTX deserialization,
|
||||
// set the image and return immediately.
|
||||
if (texture) {
|
||||
setImage(texture, texture->getWidth(), texture->getHeight());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// We failed to find an existing live or KTX texture, so trigger an image reader
|
||||
QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, hash, _maxNumPixels));
|
||||
QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _maxNumPixels));
|
||||
}
|
||||
|
||||
Reader::Reader(const QWeakPointer<Resource>& resource, const QUrl& url) :
|
||||
_resource(resource), _url(url) {
|
||||
ImageReader::ImageReader(const QWeakPointer<Resource>& resource, const QUrl& url, const QByteArray& data, int maxNumPixels) :
|
||||
_resource(resource),
|
||||
_url(url),
|
||||
_content(data),
|
||||
_maxNumPixels(maxNumPixels)
|
||||
{
|
||||
DependencyManager::get<StatTracker>()->incrementStat("PendingProcessing");
|
||||
}
|
||||
|
||||
void Reader::run() {
|
||||
PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } });
|
||||
DependencyManager::get<StatTracker>()->decrementStat("PendingProcessing");
|
||||
CounterStat counter("Processing");
|
||||
|
||||
auto originalPriority = QThread::currentThread()->priority();
|
||||
if (originalPriority == QThread::InheritPriority) {
|
||||
originalPriority = QThread::NormalPriority;
|
||||
}
|
||||
QThread::currentThread()->setPriority(QThread::LowPriority);
|
||||
Finally restorePriority([originalPriority]{ QThread::currentThread()->setPriority(originalPriority); });
|
||||
|
||||
if (!_resource.data()) {
|
||||
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
||||
return;
|
||||
}
|
||||
|
||||
read();
|
||||
}
|
||||
|
||||
ImageReader::ImageReader(const QWeakPointer<Resource>& resource, const QUrl& url,
|
||||
const QByteArray& data, const std::string& hash, int maxNumPixels) :
|
||||
Reader(resource, url), _content(data), _hash(hash), _maxNumPixels(maxNumPixels) {
|
||||
listSupportedImageFormats();
|
||||
|
||||
#if DEBUG_DUMP_TEXTURE_LOADS
|
||||
|
@ -515,89 +369,110 @@ void ImageReader::listSupportedImageFormats() {
|
|||
});
|
||||
}
|
||||
|
||||
void ImageReader::read() {
|
||||
// Help the QImage loader by extracting the image file format from the url filename ext.
|
||||
// Some tga are not created properly without it.
|
||||
auto filename = _url.fileName().toStdString();
|
||||
auto filenameExtension = filename.substr(filename.find_last_of('.') + 1);
|
||||
QImage image = QImage::fromData(_content, filenameExtension.c_str());
|
||||
int imageWidth = image.width();
|
||||
int imageHeight = image.height();
|
||||
void ImageReader::run() {
|
||||
PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } });
|
||||
DependencyManager::get<StatTracker>()->decrementStat("PendingProcessing");
|
||||
CounterStat counter("Processing");
|
||||
|
||||
// Validate that the image loaded
|
||||
if (imageWidth == 0 || imageHeight == 0 || image.format() == QImage::Format_Invalid) {
|
||||
QString reason(filenameExtension.empty() ? "" : "(no file extension)");
|
||||
qCWarning(modelnetworking) << "Failed to load" << _url << reason;
|
||||
auto originalPriority = QThread::currentThread()->priority();
|
||||
if (originalPriority == QThread::InheritPriority) {
|
||||
originalPriority = QThread::NormalPriority;
|
||||
}
|
||||
QThread::currentThread()->setPriority(QThread::LowPriority);
|
||||
Finally restorePriority([originalPriority] { QThread::currentThread()->setPriority(originalPriority); });
|
||||
|
||||
read();
|
||||
}
|
||||
|
||||
void ImageReader::read() {
|
||||
auto resource = _resource.lock(); // to ensure the resource is still needed
|
||||
if (!resource) {
|
||||
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
||||
return;
|
||||
}
|
||||
auto networkTexture = resource.staticCast<NetworkTexture>();
|
||||
|
||||
// Validate the image is less than _maxNumPixels, and downscale if necessary
|
||||
if (imageWidth * imageHeight > _maxNumPixels) {
|
||||
float scaleFactor = sqrtf(_maxNumPixels / (float)(imageWidth * imageHeight));
|
||||
int originalWidth = imageWidth;
|
||||
int originalHeight = imageHeight;
|
||||
imageWidth = (int)(scaleFactor * (float)imageWidth + 0.5f);
|
||||
imageHeight = (int)(scaleFactor * (float)imageHeight + 0.5f);
|
||||
QImage newImage = image.scaled(QSize(imageWidth, imageHeight), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
image.swap(newImage);
|
||||
qCDebug(modelnetworking).nospace() << "Downscaled " << _url << " (" <<
|
||||
QSize(originalWidth, originalHeight) << " to " <<
|
||||
QSize(imageWidth, imageHeight) << ")";
|
||||
// Hash the source image to for KTX caching
|
||||
std::string hash;
|
||||
{
|
||||
QCryptographicHash hasher(QCryptographicHash::Md5);
|
||||
hasher.addData(_content);
|
||||
hash = hasher.result().toHex().toStdString();
|
||||
}
|
||||
|
||||
gpu::TexturePointer texture = nullptr;
|
||||
// Maybe load from cache
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
if (textureCache) {
|
||||
// If we already have a live texture with the same hash, use it
|
||||
auto texture = textureCache->getTextureByHash(hash);
|
||||
|
||||
// If there is no live texture, check if there's an existing KTX file
|
||||
if (!texture) {
|
||||
KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash);
|
||||
if (ktxFile) {
|
||||
texture = gpu::Texture::unserialize(ktxFile->getFilepath());
|
||||
if (texture) {
|
||||
texture = textureCache->cacheTextureByHash(hash, texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we found the texture either because it's in use or via KTX deserialization,
|
||||
// set the image and return immediately.
|
||||
if (texture) {
|
||||
QMetaObject::invokeMethod(resource.data(), "setImage",
|
||||
Q_ARG(gpu::TexturePointer, texture),
|
||||
Q_ARG(int, texture->getWidth()),
|
||||
Q_ARG(int, texture->getHeight()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Proccess new texture
|
||||
gpu::TexturePointer texture;
|
||||
{
|
||||
auto resource = _resource.lock(); // to ensure the resource is still needed
|
||||
if (!resource) {
|
||||
qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope";
|
||||
PROFILE_RANGE_EX(resource_parse_image_raw, __FUNCTION__, 0xffff0000, 0);
|
||||
texture = image::processImage(_content, _url.toString().toStdString(), _maxNumPixels, networkTexture->getTextureType());
|
||||
|
||||
if (!texture) {
|
||||
qCWarning(modelnetworking) << "Could not process:" << _url;
|
||||
QMetaObject::invokeMethod(resource.data(), "setImage",
|
||||
Q_ARG(gpu::TexturePointer, texture),
|
||||
Q_ARG(int, 0),
|
||||
Q_ARG(int, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
auto url = _url.toString().toStdString();
|
||||
texture->setSourceHash(hash);
|
||||
texture->setFallbackTexture(networkTexture->getFallbackTexture());
|
||||
}
|
||||
|
||||
PROFILE_RANGE_EX(resource_parse_image_raw, __FUNCTION__, 0xffff0000, 0);
|
||||
// Load the image into a gpu::Texture
|
||||
auto networkTexture = resource.staticCast<NetworkTexture>();
|
||||
texture.reset(networkTexture->getTextureLoader()(image, url));
|
||||
texture->setSource(url);
|
||||
if (texture) {
|
||||
texture->setFallbackTexture(networkTexture->getFallbackTexture());
|
||||
}
|
||||
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
// Save the image into a KTXFile
|
||||
// Save the image into a KTXFile
|
||||
if (texture && textureCache) {
|
||||
auto memKtx = gpu::Texture::serialize(*texture);
|
||||
if (!memKtx) {
|
||||
qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url;
|
||||
}
|
||||
|
||||
if (memKtx && textureCache) {
|
||||
if (memKtx) {
|
||||
const char* data = reinterpret_cast<const char*>(memKtx->_storage->data());
|
||||
size_t length = memKtx->_storage->size();
|
||||
KTXFilePointer file;
|
||||
auto& ktxCache = textureCache->_ktxCache;
|
||||
if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(_hash, length)))) {
|
||||
networkTexture->_file = ktxCache.writeFile(data, KTXCache::Metadata(hash, length));
|
||||
if (!networkTexture->_file) {
|
||||
qCWarning(modelnetworking) << _url << "file cache failed";
|
||||
} else {
|
||||
resource.staticCast<NetworkTexture>()->_file = file;
|
||||
texture->setKtxBacking(file->getFilepath());
|
||||
texture->setKtxBacking(networkTexture->_file->getFilepath());
|
||||
}
|
||||
} else {
|
||||
qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url;
|
||||
}
|
||||
|
||||
// We replace the texture with the one stored in the cache. This deals with the possible race condition of two different
|
||||
// images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will
|
||||
// be the winner
|
||||
if (textureCache) {
|
||||
texture = textureCache->cacheTextureByHash(_hash, texture);
|
||||
}
|
||||
texture = textureCache->cacheTextureByHash(hash, texture);
|
||||
}
|
||||
|
||||
auto resource = _resource.lock(); // to ensure the resource is still needed
|
||||
if (resource) {
|
||||
QMetaObject::invokeMethod(resource.data(), "setImage",
|
||||
Q_ARG(gpu::TexturePointer, texture),
|
||||
Q_ARG(int, imageWidth), Q_ARG(int, imageHeight));
|
||||
} else {
|
||||
qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope";
|
||||
}
|
||||
QMetaObject::invokeMethod(resource.data(), "setImage",
|
||||
Q_ARG(gpu::TexturePointer, texture),
|
||||
Q_ARG(int, texture->getWidth()),
|
||||
Q_ARG(int, texture->getHeight()));
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <DependencyManager.h>
|
||||
#include <ResourceCache.h>
|
||||
#include <model/TextureMap.h>
|
||||
#include <image/Image.h>
|
||||
|
||||
#include "KTXCache.h"
|
||||
|
||||
|
@ -43,29 +44,7 @@ class NetworkTexture : public Resource, public Texture {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Type {
|
||||
DEFAULT_TEXTURE,
|
||||
STRICT_TEXTURE,
|
||||
ALBEDO_TEXTURE,
|
||||
NORMAL_TEXTURE,
|
||||
BUMP_TEXTURE,
|
||||
SPECULAR_TEXTURE,
|
||||
METALLIC_TEXTURE = SPECULAR_TEXTURE, // for now spec and metallic texture are the same, converted to grey
|
||||
ROUGHNESS_TEXTURE,
|
||||
GLOSS_TEXTURE,
|
||||
EMISSIVE_TEXTURE,
|
||||
CUBE_TEXTURE,
|
||||
OCCLUSION_TEXTURE,
|
||||
SCATTERING_TEXTURE = OCCLUSION_TEXTURE,
|
||||
LIGHTMAP_TEXTURE,
|
||||
CUSTOM_TEXTURE
|
||||
};
|
||||
Q_ENUM(Type)
|
||||
|
||||
typedef gpu::Texture* TextureLoader(const QImage& image, const std::string& srcImageName);
|
||||
using TextureLoaderFunc = std::function<TextureLoader>;
|
||||
|
||||
NetworkTexture(const QUrl& url, Type type, const QByteArray& content, int maxNumPixels);
|
||||
NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels);
|
||||
|
||||
QString getType() const override { return "NetworkTexture"; }
|
||||
|
||||
|
@ -73,9 +52,8 @@ public:
|
|||
int getOriginalHeight() const { return _originalHeight; }
|
||||
int getWidth() const { return _width; }
|
||||
int getHeight() const { return _height; }
|
||||
Type getTextureType() const { return _type; }
|
||||
image::TextureUsage::Type getTextureType() const { return _type; }
|
||||
|
||||
TextureLoaderFunc getTextureLoader() const;
|
||||
gpu::TexturePointer getFallbackTexture() const;
|
||||
|
||||
signals:
|
||||
|
@ -93,8 +71,7 @@ private:
|
|||
friend class KTXReader;
|
||||
friend class ImageReader;
|
||||
|
||||
Type _type;
|
||||
TextureLoaderFunc _textureLoader { [](const QImage&, const std::string&){ return nullptr; } };
|
||||
image::TextureUsage::Type _type;
|
||||
KTXFilePointer _file;
|
||||
int _originalWidth { 0 };
|
||||
int _originalHeight { 0 };
|
||||
|
@ -110,8 +87,6 @@ class TextureCache : public ResourceCache, public Dependency {
|
|||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
using Type = NetworkTexture::Type;
|
||||
|
||||
public:
|
||||
/// Returns the ID of the permutation/normal texture used for Perlin noise shader programs. This texture
|
||||
/// has two lines: the first, a set of random numbers in [0, 255] to be used as permutation offsets, and
|
||||
|
@ -131,10 +106,10 @@ public:
|
|||
const gpu::TexturePointer& getBlackTexture();
|
||||
|
||||
/// Returns a texture version of an image file
|
||||
static gpu::TexturePointer getImageTexture(const QString& path, Type type = Type::DEFAULT_TEXTURE, QVariantMap options = QVariantMap());
|
||||
static gpu::TexturePointer getImageTexture(const QString& path, image::TextureUsage::Type type = image::TextureUsage::DEFAULT_TEXTURE, QVariantMap options = QVariantMap());
|
||||
|
||||
/// Loads a texture from the specified URL.
|
||||
NetworkTexturePointer getTexture(const QUrl& url, Type type = Type::DEFAULT_TEXTURE,
|
||||
NetworkTexturePointer getTexture(const QUrl& url, image::TextureUsage::Type type = image::TextureUsage::DEFAULT_TEXTURE,
|
||||
const QByteArray& content = QByteArray(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS);
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
set(TARGET_NAME model)
|
||||
AUTOSCRIBE_SHADER_LIB(gpu model)
|
||||
setup_hifi_library()
|
||||
link_hifi_libraries(shared ktx gpu)
|
||||
|
||||
link_hifi_libraries(shared ktx gpu image)
|
|
@ -10,81 +10,9 @@
|
|||
//
|
||||
#include "TextureMap.h"
|
||||
|
||||
#include <ktx/KTX.h>
|
||||
|
||||
#include <QImage>
|
||||
#include <QPainter>
|
||||
#include <QDebug>
|
||||
#include <QStandardPaths>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QCryptographicHash>
|
||||
#include <Profile.h>
|
||||
|
||||
#include "ModelLogging.h"
|
||||
using namespace model;
|
||||
using namespace gpu;
|
||||
|
||||
// FIXME: Declare this to enable compression
|
||||
//#define COMPRESS_TEXTURES
|
||||
static const uvec2 SPARSE_PAGE_SIZE(128);
|
||||
static const uvec2 MAX_TEXTURE_SIZE(4096);
|
||||
bool DEV_DECIMATE_TEXTURES = false;
|
||||
|
||||
bool needsSparseRectification(const uvec2& size) {
|
||||
// Don't attempt to rectify small textures (textures less than the sparse page size in any dimension)
|
||||
if (glm::any(glm::lessThan(size, SPARSE_PAGE_SIZE))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't rectify textures that are already an exact multiple of sparse page size
|
||||
if (uvec2(0) == (size % SPARSE_PAGE_SIZE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Texture is not sparse compatible, but is bigger than the sparse page size in both dimensions, rectify!
|
||||
return true;
|
||||
}
|
||||
|
||||
uvec2 rectifyToSparseSize(const uvec2& size) {
|
||||
uvec2 pages = ((size / SPARSE_PAGE_SIZE) + glm::clamp(size % SPARSE_PAGE_SIZE, uvec2(0), uvec2(1)));
|
||||
uvec2 result = pages * SPARSE_PAGE_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::atomic<size_t> DECIMATED_TEXTURE_COUNT { 0 };
|
||||
std::atomic<size_t> RECTIFIED_TEXTURE_COUNT { 0 };
|
||||
|
||||
QImage processSourceImage(const QImage& srcImage, bool cubemap) {
|
||||
PROFILE_RANGE(resource_parse, "processSourceImage");
|
||||
const uvec2 srcImageSize = toGlm(srcImage.size());
|
||||
uvec2 targetSize = srcImageSize;
|
||||
|
||||
while (glm::any(glm::greaterThan(targetSize, MAX_TEXTURE_SIZE))) {
|
||||
targetSize /= 2;
|
||||
}
|
||||
if (targetSize != srcImageSize) {
|
||||
++DECIMATED_TEXTURE_COUNT;
|
||||
}
|
||||
|
||||
if (!cubemap && needsSparseRectification(targetSize)) {
|
||||
++RECTIFIED_TEXTURE_COUNT;
|
||||
targetSize = rectifyToSparseSize(targetSize);
|
||||
}
|
||||
|
||||
if (DEV_DECIMATE_TEXTURES && glm::all(glm::greaterThanEqual(targetSize / SPARSE_PAGE_SIZE, uvec2(2)))) {
|
||||
targetSize /= 2;
|
||||
}
|
||||
|
||||
if (targetSize != srcImageSize) {
|
||||
PROFILE_RANGE(resource_parse, "processSourceImage Rectify");
|
||||
qCDebug(modelLog) << "Resizing texture from " << srcImageSize.x << "x" << srcImageSize.y << " to " << targetSize.x << "x" << targetSize.y;
|
||||
return srcImage.scaled(fromGlm(targetSize), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
return srcImage;
|
||||
}
|
||||
|
||||
void TextureMap::setTextureSource(TextureSourcePointer& textureSource) {
|
||||
_textureSource = textureSource;
|
||||
}
|
||||
|
@ -113,758 +41,3 @@ void TextureMap::setLightmapOffsetScale(float offset, float scale) {
|
|||
_lightmapOffsetScale.x = offset;
|
||||
_lightmapOffsetScale.y = scale;
|
||||
}
|
||||
|
||||
const QImage TextureUsage::process2DImageColor(const QImage& srcImage, bool& validAlpha, bool& alphaAsMask) {
|
||||
PROFILE_RANGE(resource_parse, "process2DImageColor");
|
||||
QImage image = processSourceImage(srcImage, false);
|
||||
validAlpha = false;
|
||||
alphaAsMask = true;
|
||||
const uint8 OPAQUE_ALPHA = 255;
|
||||
const uint8 TRANSPARENT_ALPHA = 0;
|
||||
if (image.hasAlphaChannel()) {
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
|
||||
// Figure out if we can use a mask for alpha or not
|
||||
int numOpaques = 0;
|
||||
int numTranslucents = 0;
|
||||
const int NUM_PIXELS = image.width() * image.height();
|
||||
const int MAX_TRANSLUCENT_PIXELS_FOR_ALPHAMASK = (int)(0.05f * (float)(NUM_PIXELS));
|
||||
const QRgb* data = reinterpret_cast<const QRgb*>(image.constBits());
|
||||
for (int i = 0; i < NUM_PIXELS; ++i) {
|
||||
auto alpha = qAlpha(data[i]);
|
||||
if (alpha == OPAQUE_ALPHA) {
|
||||
numOpaques++;
|
||||
} else if (alpha != TRANSPARENT_ALPHA) {
|
||||
if (++numTranslucents > MAX_TRANSLUCENT_PIXELS_FOR_ALPHAMASK) {
|
||||
alphaAsMask = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
validAlpha = (numOpaques != NUM_PIXELS);
|
||||
}
|
||||
|
||||
// Force all the color images to be rgba32bits
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void TextureUsage::defineColorTexelFormats(gpu::Element& formatGPU, gpu::Element& formatMip,
|
||||
const QImage& image, bool isLinear, bool doCompress) {
|
||||
|
||||
#ifdef COMPRESS_TEXTURES
|
||||
#else
|
||||
doCompress = false;
|
||||
#endif
|
||||
|
||||
if (image.hasAlphaChannel()) {
|
||||
gpu::Semantic gpuSemantic;
|
||||
gpu::Semantic mipSemantic;
|
||||
if (isLinear) {
|
||||
mipSemantic = gpu::BGRA;
|
||||
if (doCompress) {
|
||||
gpuSemantic = gpu::COMPRESSED_RGBA;
|
||||
} else {
|
||||
gpuSemantic = gpu::RGBA;
|
||||
}
|
||||
} else {
|
||||
mipSemantic = gpu::SBGRA;
|
||||
if (doCompress) {
|
||||
gpuSemantic = gpu::COMPRESSED_SRGBA;
|
||||
} else {
|
||||
gpuSemantic = gpu::SRGBA;
|
||||
}
|
||||
}
|
||||
formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpuSemantic);
|
||||
formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, mipSemantic);
|
||||
} else {
|
||||
gpu::Semantic gpuSemantic;
|
||||
gpu::Semantic mipSemantic;
|
||||
if (isLinear) {
|
||||
mipSemantic = gpu::RGB;
|
||||
if (doCompress) {
|
||||
gpuSemantic = gpu::COMPRESSED_RGB;
|
||||
} else {
|
||||
gpuSemantic = gpu::RGB;
|
||||
}
|
||||
} else {
|
||||
mipSemantic = gpu::SRGB;
|
||||
if (doCompress) {
|
||||
gpuSemantic = gpu::COMPRESSED_SRGB;
|
||||
} else {
|
||||
gpuSemantic = gpu::SRGB;
|
||||
}
|
||||
}
|
||||
formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpuSemantic);
|
||||
formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, mipSemantic);
|
||||
}
|
||||
}
|
||||
|
||||
#define CPU_MIPMAPS 1
|
||||
|
||||
void generateMips(gpu::Texture* texture, QImage& image, bool fastResize) {
|
||||
#if CPU_MIPMAPS
|
||||
PROFILE_RANGE(resource_parse, "generateMips");
|
||||
auto numMips = texture->getNumMips();
|
||||
for (uint16 level = 1; level < numMips; ++level) {
|
||||
QSize mipSize(texture->evalMipWidth(level), texture->evalMipHeight(level));
|
||||
if (fastResize) {
|
||||
image = image.scaled(mipSize);
|
||||
texture->assignStoredMip(level, image.byteCount(), image.constBits());
|
||||
} else {
|
||||
QImage mipImage = image.scaled(mipSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
texture->assignStoredMip(level, mipImage.byteCount(), mipImage.constBits());
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
texture->autoGenerateMips(-1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void generateFaceMips(gpu::Texture* texture, QImage& image, uint8 face) {
|
||||
#if CPU_MIPMAPS
|
||||
PROFILE_RANGE(resource_parse, "generateFaceMips");
|
||||
auto numMips = texture->getNumMips();
|
||||
for (uint16 level = 1; level < numMips; ++level) {
|
||||
QSize mipSize(texture->evalMipWidth(level), texture->evalMipHeight(level));
|
||||
QImage mipImage = image.scaled(mipSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
texture->assignStoredMipFace(level, face, mipImage.byteCount(), mipImage.constBits());
|
||||
}
|
||||
#else
|
||||
texture->autoGenerateMips(-1);
|
||||
#endif
|
||||
}
|
||||
|
||||
gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool isStrict) {
|
||||
PROFILE_RANGE(resource_parse, "process2DTextureColorFromImage");
|
||||
bool validAlpha = false;
|
||||
bool alphaAsMask = true;
|
||||
QImage image = process2DImageColor(srcImage, validAlpha, alphaAsMask);
|
||||
|
||||
gpu::Texture* theTexture = nullptr;
|
||||
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
gpu::Element formatGPU;
|
||||
gpu::Element formatMip;
|
||||
defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress);
|
||||
|
||||
if (isStrict) {
|
||||
theTexture = (gpu::Texture::createStrict(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
} else {
|
||||
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
}
|
||||
theTexture->setSource(srcImageName);
|
||||
auto usage = gpu::Texture::Usage::Builder().withColor();
|
||||
if (validAlpha) {
|
||||
usage.withAlpha();
|
||||
if (alphaAsMask) {
|
||||
usage.withAlphaMask();
|
||||
}
|
||||
}
|
||||
theTexture->setUsage(usage.build());
|
||||
theTexture->setStoredMipFormat(formatMip);
|
||||
theTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
|
||||
if (generateMips) {
|
||||
::generateMips(theTexture, image, false);
|
||||
}
|
||||
theTexture->setSource(srcImageName);
|
||||
}
|
||||
|
||||
return theTexture;
|
||||
}
|
||||
|
||||
gpu::Texture* TextureUsage::createStrict2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return process2DTextureColorFromImage(srcImage, srcImageName, false, false, true, true);
|
||||
}
|
||||
|
||||
gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return process2DTextureColorFromImage(srcImage, srcImageName, false, false, true);
|
||||
}
|
||||
|
||||
gpu::Texture* TextureUsage::createAlbedoTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return process2DTextureColorFromImage(srcImage, srcImageName, false, true, true);
|
||||
}
|
||||
|
||||
gpu::Texture* TextureUsage::createEmissiveTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return process2DTextureColorFromImage(srcImage, srcImageName, false, true, true);
|
||||
}
|
||||
|
||||
gpu::Texture* TextureUsage::createLightmapTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return process2DTextureColorFromImage(srcImage, srcImageName, false, true, true);
|
||||
}
|
||||
|
||||
|
||||
gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
PROFILE_RANGE(resource_parse, "createNormalTextureFromNormalImage");
|
||||
QImage image = processSourceImage(srcImage, false);
|
||||
|
||||
// Make sure the normal map source image is ARGB32
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
|
||||
|
||||
gpu::Texture* theTexture = nullptr;
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
|
||||
gpu::Element formatMip = gpu::Element::COLOR_BGRA_32;
|
||||
gpu::Element formatGPU = gpu::Element::COLOR_RGBA_32;
|
||||
|
||||
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
theTexture->setSource(srcImageName);
|
||||
theTexture->setStoredMipFormat(formatMip);
|
||||
theTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
generateMips(theTexture, image, true);
|
||||
|
||||
theTexture->setSource(srcImageName);
|
||||
}
|
||||
|
||||
return theTexture;
|
||||
}
|
||||
|
||||
int clampPixelCoordinate(int coordinate, int maxCoordinate) {
|
||||
return coordinate - ((int)(coordinate < 0) * coordinate) + ((int)(coordinate > maxCoordinate) * (maxCoordinate - coordinate));
|
||||
}
|
||||
|
||||
const int RGBA_MAX = 255;
|
||||
|
||||
// transform -1 - 1 to 0 - 255 (from sobel value to rgb)
|
||||
double mapComponent(double sobelValue) {
|
||||
const double factor = RGBA_MAX / 2.0;
|
||||
return (sobelValue + 1.0) * factor;
|
||||
}
|
||||
|
||||
gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
PROFILE_RANGE(resource_parse, "createNormalTextureFromBumpImage");
|
||||
QImage image = processSourceImage(srcImage, false);
|
||||
|
||||
if (image.format() != QImage::Format_Grayscale8) {
|
||||
image = image.convertToFormat(QImage::Format_Grayscale8);
|
||||
}
|
||||
|
||||
// PR 5540 by AlessandroSigna integrated here as a specialized TextureLoader for bumpmaps
|
||||
// The conversion is done using the Sobel Filter to calculate the derivatives from the grayscale image
|
||||
const double pStrength = 2.0;
|
||||
int width = image.width();
|
||||
int height = image.height();
|
||||
|
||||
QImage result(width, height, QImage::Format_ARGB32);
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
const int iNextClamped = clampPixelCoordinate(i + 1, width - 1);
|
||||
const int iPrevClamped = clampPixelCoordinate(i - 1, width - 1);
|
||||
|
||||
for (int j = 0; j < height; j++) {
|
||||
const int jNextClamped = clampPixelCoordinate(j + 1, height - 1);
|
||||
const int jPrevClamped = clampPixelCoordinate(j - 1, height - 1);
|
||||
|
||||
// surrounding pixels
|
||||
const QRgb topLeft = image.pixel(iPrevClamped, jPrevClamped);
|
||||
const QRgb top = image.pixel(iPrevClamped, j);
|
||||
const QRgb topRight = image.pixel(iPrevClamped, jNextClamped);
|
||||
const QRgb right = image.pixel(i, jNextClamped);
|
||||
const QRgb bottomRight = image.pixel(iNextClamped, jNextClamped);
|
||||
const QRgb bottom = image.pixel(iNextClamped, j);
|
||||
const QRgb bottomLeft = image.pixel(iNextClamped, jPrevClamped);
|
||||
const QRgb left = image.pixel(i, jPrevClamped);
|
||||
|
||||
// take their gray intensities
|
||||
// since it's a grayscale image, the value of each component RGB is the same
|
||||
const double tl = qRed(topLeft);
|
||||
const double t = qRed(top);
|
||||
const double tr = qRed(topRight);
|
||||
const double r = qRed(right);
|
||||
const double br = qRed(bottomRight);
|
||||
const double b = qRed(bottom);
|
||||
const double bl = qRed(bottomLeft);
|
||||
const double l = qRed(left);
|
||||
|
||||
// apply the sobel filter
|
||||
const double dX = (tr + pStrength * r + br) - (tl + pStrength * l + bl);
|
||||
const double dY = (bl + pStrength * b + br) - (tl + pStrength * t + tr);
|
||||
const double dZ = RGBA_MAX / pStrength;
|
||||
|
||||
glm::vec3 v(dX, dY, dZ);
|
||||
glm::normalize(v);
|
||||
|
||||
// convert to rgb from the value obtained computing the filter
|
||||
QRgb qRgbValue = qRgba(mapComponent(v.z), mapComponent(v.y), mapComponent(v.x), 1.0);
|
||||
result.setPixel(i, j, qRgbValue);
|
||||
}
|
||||
}
|
||||
|
||||
gpu::Texture* theTexture = nullptr;
|
||||
if ((result.width() > 0) && (result.height() > 0)) {
|
||||
|
||||
gpu::Element formatMip = gpu::Element::COLOR_BGRA_32;
|
||||
gpu::Element formatGPU = gpu::Element::COLOR_RGBA_32;
|
||||
|
||||
|
||||
theTexture = (gpu::Texture::create2D(formatGPU, result.width(), result.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
theTexture->setSource(srcImageName);
|
||||
theTexture->setStoredMipFormat(formatMip);
|
||||
theTexture->assignStoredMip(0, result.byteCount(), result.constBits());
|
||||
generateMips(theTexture, result, true);
|
||||
|
||||
theTexture->setSource(srcImageName);
|
||||
}
|
||||
|
||||
return theTexture;
|
||||
}
|
||||
|
||||
gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
PROFILE_RANGE(resource_parse, "createRoughnessTextureFromImage");
|
||||
QImage image = processSourceImage(srcImage, false);
|
||||
if (!image.hasAlphaChannel()) {
|
||||
if (image.format() != QImage::Format_RGB888) {
|
||||
image = image.convertToFormat(QImage::Format_RGB888);
|
||||
}
|
||||
} else {
|
||||
if (image.format() != QImage::Format_RGBA8888) {
|
||||
image = image.convertToFormat(QImage::Format_RGBA8888);
|
||||
}
|
||||
}
|
||||
|
||||
image = image.convertToFormat(QImage::Format_Grayscale8);
|
||||
|
||||
gpu::Texture* theTexture = nullptr;
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
#ifdef COMPRESS_TEXTURES
|
||||
gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R);
|
||||
#else
|
||||
gpu::Element formatGPU = gpu::Element::COLOR_R_8;
|
||||
#endif
|
||||
gpu::Element formatMip = gpu::Element::COLOR_R_8;
|
||||
|
||||
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
theTexture->setSource(srcImageName);
|
||||
theTexture->setStoredMipFormat(formatMip);
|
||||
theTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
generateMips(theTexture, image, true);
|
||||
|
||||
theTexture->setSource(srcImageName);
|
||||
}
|
||||
|
||||
return theTexture;
|
||||
}
|
||||
|
||||
gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
PROFILE_RANGE(resource_parse, "createRoughnessTextureFromGlossImage");
|
||||
QImage image = processSourceImage(srcImage, false);
|
||||
if (!image.hasAlphaChannel()) {
|
||||
if (image.format() != QImage::Format_RGB888) {
|
||||
image = image.convertToFormat(QImage::Format_RGB888);
|
||||
}
|
||||
} else {
|
||||
if (image.format() != QImage::Format_RGBA8888) {
|
||||
image = image.convertToFormat(QImage::Format_RGBA8888);
|
||||
}
|
||||
}
|
||||
|
||||
// Gloss turned into Rough
|
||||
image.invertPixels(QImage::InvertRgba);
|
||||
|
||||
image = image.convertToFormat(QImage::Format_Grayscale8);
|
||||
|
||||
gpu::Texture* theTexture = nullptr;
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
|
||||
#ifdef COMPRESS_TEXTURES
|
||||
gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R);
|
||||
#else
|
||||
gpu::Element formatGPU = gpu::Element::COLOR_R_8;
|
||||
#endif
|
||||
gpu::Element formatMip = gpu::Element::COLOR_R_8;
|
||||
|
||||
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
theTexture->setSource(srcImageName);
|
||||
theTexture->setStoredMipFormat(formatMip);
|
||||
theTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
generateMips(theTexture, image, true);
|
||||
|
||||
theTexture->setSource(srcImageName);
|
||||
}
|
||||
|
||||
return theTexture;
|
||||
}
|
||||
|
||||
gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
PROFILE_RANGE(resource_parse, "createMetallicTextureFromImage");
|
||||
QImage image = processSourceImage(srcImage, false);
|
||||
if (!image.hasAlphaChannel()) {
|
||||
if (image.format() != QImage::Format_RGB888) {
|
||||
image = image.convertToFormat(QImage::Format_RGB888);
|
||||
}
|
||||
} else {
|
||||
if (image.format() != QImage::Format_RGBA8888) {
|
||||
image = image.convertToFormat(QImage::Format_RGBA8888);
|
||||
}
|
||||
}
|
||||
|
||||
image = image.convertToFormat(QImage::Format_Grayscale8);
|
||||
|
||||
gpu::Texture* theTexture = nullptr;
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
|
||||
#ifdef COMPRESS_TEXTURES
|
||||
gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R);
|
||||
#else
|
||||
gpu::Element formatGPU = gpu::Element::COLOR_R_8;
|
||||
#endif
|
||||
gpu::Element formatMip = gpu::Element::COLOR_R_8;
|
||||
|
||||
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
theTexture->setSource(srcImageName);
|
||||
theTexture->setStoredMipFormat(formatMip);
|
||||
theTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
generateMips(theTexture, image, true);
|
||||
|
||||
theTexture->setSource(srcImageName);
|
||||
}
|
||||
|
||||
return theTexture;
|
||||
}
|
||||
|
||||
class CubeLayout {
|
||||
public:
|
||||
|
||||
enum SourceProjection {
|
||||
FLAT = 0,
|
||||
EQUIRECTANGULAR,
|
||||
};
|
||||
int _type = FLAT;
|
||||
int _widthRatio = 1;
|
||||
int _heightRatio = 1;
|
||||
|
||||
class Face {
|
||||
public:
|
||||
int _x = 0;
|
||||
int _y = 0;
|
||||
bool _horizontalMirror = false;
|
||||
bool _verticalMirror = false;
|
||||
|
||||
Face() {}
|
||||
Face(int x, int y, bool horizontalMirror, bool verticalMirror) : _x(x), _y(y), _horizontalMirror(horizontalMirror), _verticalMirror(verticalMirror) {}
|
||||
};
|
||||
|
||||
Face _faceXPos;
|
||||
Face _faceXNeg;
|
||||
Face _faceYPos;
|
||||
Face _faceYNeg;
|
||||
Face _faceZPos;
|
||||
Face _faceZNeg;
|
||||
|
||||
CubeLayout(int wr, int hr, Face fXP, Face fXN, Face fYP, Face fYN, Face fZP, Face fZN) :
|
||||
_type(FLAT),
|
||||
_widthRatio(wr),
|
||||
_heightRatio(hr),
|
||||
_faceXPos(fXP),
|
||||
_faceXNeg(fXN),
|
||||
_faceYPos(fYP),
|
||||
_faceYNeg(fYN),
|
||||
_faceZPos(fZP),
|
||||
_faceZNeg(fZN) {}
|
||||
|
||||
CubeLayout(int wr, int hr) :
|
||||
_type(EQUIRECTANGULAR),
|
||||
_widthRatio(wr),
|
||||
_heightRatio(hr) {}
|
||||
|
||||
|
||||
static const CubeLayout CUBEMAP_LAYOUTS[];
|
||||
static const int NUM_CUBEMAP_LAYOUTS;
|
||||
|
||||
static int findLayout(int width, int height) {
|
||||
// Find the layout of the cubemap in the 2D image
|
||||
int foundLayout = -1;
|
||||
for (int i = 0; i < NUM_CUBEMAP_LAYOUTS; i++) {
|
||||
if ((height * CUBEMAP_LAYOUTS[i]._widthRatio) == (width * CUBEMAP_LAYOUTS[i]._heightRatio)) {
|
||||
foundLayout = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return foundLayout;
|
||||
}
|
||||
|
||||
static QImage extractEquirectangularFace(const QImage& source, gpu::Texture::CubeFace face, int faceWidth) {
|
||||
QImage image(faceWidth, faceWidth, source.format());
|
||||
|
||||
glm::vec2 dstInvSize(1.0f / (float)image.width(), 1.0f / (float)image.height());
|
||||
|
||||
struct CubeToXYZ {
|
||||
gpu::Texture::CubeFace _face;
|
||||
CubeToXYZ(gpu::Texture::CubeFace face) : _face(face) {}
|
||||
|
||||
glm::vec3 xyzFrom(const glm::vec2& uv) {
|
||||
auto faceDir = glm::normalize(glm::vec3(-1.0f + 2.0f * uv.x, -1.0f + 2.0f * uv.y, 1.0f));
|
||||
|
||||
switch (_face) {
|
||||
case gpu::Texture::CubeFace::CUBE_FACE_BACK_POS_Z:
|
||||
return glm::vec3(-faceDir.x, faceDir.y, faceDir.z);
|
||||
case gpu::Texture::CubeFace::CUBE_FACE_FRONT_NEG_Z:
|
||||
return glm::vec3(faceDir.x, faceDir.y, -faceDir.z);
|
||||
case gpu::Texture::CubeFace::CUBE_FACE_LEFT_NEG_X:
|
||||
return glm::vec3(faceDir.z, faceDir.y, faceDir.x);
|
||||
case gpu::Texture::CubeFace::CUBE_FACE_RIGHT_POS_X:
|
||||
return glm::vec3(-faceDir.z, faceDir.y, -faceDir.x);
|
||||
case gpu::Texture::CubeFace::CUBE_FACE_BOTTOM_NEG_Y:
|
||||
return glm::vec3(-faceDir.x, -faceDir.z, faceDir.y);
|
||||
case gpu::Texture::CubeFace::CUBE_FACE_TOP_POS_Y:
|
||||
default:
|
||||
return glm::vec3(-faceDir.x, faceDir.z, -faceDir.y);
|
||||
}
|
||||
}
|
||||
};
|
||||
CubeToXYZ cubeToXYZ(face);
|
||||
|
||||
struct RectToXYZ {
|
||||
RectToXYZ() {}
|
||||
|
||||
glm::vec2 uvFrom(const glm::vec3& xyz) {
|
||||
auto flatDir = glm::normalize(glm::vec2(xyz.x, xyz.z));
|
||||
auto uvRad = glm::vec2(atan2(flatDir.x, flatDir.y), asin(xyz.y));
|
||||
|
||||
const float LON_TO_RECT_U = 1.0f / (glm::pi<float>());
|
||||
const float LAT_TO_RECT_V = 2.0f / glm::pi<float>();
|
||||
return glm::vec2(0.5f * uvRad.x * LON_TO_RECT_U + 0.5f, 0.5f * uvRad.y * LAT_TO_RECT_V + 0.5f);
|
||||
}
|
||||
};
|
||||
RectToXYZ rectToXYZ;
|
||||
|
||||
int srcFaceHeight = source.height();
|
||||
int srcFaceWidth = source.width();
|
||||
|
||||
glm::vec2 dstCoord;
|
||||
glm::ivec2 srcPixel;
|
||||
for (int y = 0; y < faceWidth; ++y) {
|
||||
dstCoord.y = 1.0f - (y + 0.5f) * dstInvSize.y; // Fill cube face images from top to bottom
|
||||
for (int x = 0; x < faceWidth; ++x) {
|
||||
dstCoord.x = (x + 0.5f) * dstInvSize.x;
|
||||
|
||||
auto xyzDir = cubeToXYZ.xyzFrom(dstCoord);
|
||||
auto srcCoord = rectToXYZ.uvFrom(xyzDir);
|
||||
|
||||
srcPixel.x = floor(srcCoord.x * srcFaceWidth);
|
||||
// Flip the vertical axis to QImage going top to bottom
|
||||
srcPixel.y = floor((1.0f - srcCoord.y) * srcFaceHeight);
|
||||
|
||||
if (((uint32) srcPixel.x < (uint32) source.width()) && ((uint32) srcPixel.y < (uint32) source.height())) {
|
||||
image.setPixel(x, y, source.pixel(QPoint(srcPixel.x, srcPixel.y)));
|
||||
|
||||
// Keep for debug, this is showing the dir as a color
|
||||
// glm::u8vec4 rgba((xyzDir.x + 1.0)*0.5 * 256, (xyzDir.y + 1.0)*0.5 * 256, (xyzDir.z + 1.0)*0.5 * 256, 256);
|
||||
// unsigned int val = 0xff000000 | (rgba.r) | (rgba.g << 8) | (rgba.b << 16);
|
||||
// image.setPixel(x, y, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
return image;
|
||||
}
|
||||
};
|
||||
|
||||
const CubeLayout CubeLayout::CUBEMAP_LAYOUTS[] = {
|
||||
|
||||
// Here is the expected layout for the faces in an image with the 2/1 aspect ratio:
|
||||
// THis is detected as an Equirectangular projection
|
||||
// WIDTH
|
||||
// <--------------------------->
|
||||
// ^ +------+------+------+------+
|
||||
// H | | | | |
|
||||
// E | | | | |
|
||||
// I | | | | |
|
||||
// G +------+------+------+------+
|
||||
// H | | | | |
|
||||
// T | | | | |
|
||||
// | | | | | |
|
||||
// v +------+------+------+------+
|
||||
//
|
||||
// FaceWidth = width = height / 6
|
||||
{ 2, 1 },
|
||||
|
||||
// Here is the expected layout for the faces in an image with the 1/6 aspect ratio:
|
||||
//
|
||||
// WIDTH
|
||||
// <------>
|
||||
// ^ +------+
|
||||
// | | |
|
||||
// | | +X |
|
||||
// | | |
|
||||
// H +------+
|
||||
// E | |
|
||||
// I | -X |
|
||||
// G | |
|
||||
// H +------+
|
||||
// T | |
|
||||
// | | +Y |
|
||||
// | | |
|
||||
// | +------+
|
||||
// | | |
|
||||
// | | -Y |
|
||||
// | | |
|
||||
// H +------+
|
||||
// E | |
|
||||
// I | +Z |
|
||||
// G | |
|
||||
// H +------+
|
||||
// T | |
|
||||
// | | -Z |
|
||||
// | | |
|
||||
// V +------+
|
||||
//
|
||||
// FaceWidth = width = height / 6
|
||||
{ 1, 6,
|
||||
{ 0, 0, true, false },
|
||||
{ 0, 1, true, false },
|
||||
{ 0, 2, false, true },
|
||||
{ 0, 3, false, true },
|
||||
{ 0, 4, true, false },
|
||||
{ 0, 5, true, false }
|
||||
},
|
||||
|
||||
// Here is the expected layout for the faces in an image with the 3/4 aspect ratio:
|
||||
//
|
||||
// <-----------WIDTH----------->
|
||||
// ^ +------+------+------+------+
|
||||
// | | | | | |
|
||||
// | | | +Y | | |
|
||||
// | | | | | |
|
||||
// H +------+------+------+------+
|
||||
// E | | | | |
|
||||
// I | -X | -Z | +X | +Z |
|
||||
// G | | | | |
|
||||
// H +------+------+------+------+
|
||||
// T | | | | |
|
||||
// | | | -Y | | |
|
||||
// | | | | | |
|
||||
// V +------+------+------+------+
|
||||
//
|
||||
// FaceWidth = width / 4 = height / 3
|
||||
{ 4, 3,
|
||||
{ 2, 1, true, false },
|
||||
{ 0, 1, true, false },
|
||||
{ 1, 0, false, true },
|
||||
{ 1, 2, false, true },
|
||||
{ 3, 1, true, false },
|
||||
{ 1, 1, true, false }
|
||||
},
|
||||
|
||||
// Here is the expected layout for the faces in an image with the 4/3 aspect ratio:
|
||||
//
|
||||
// <-------WIDTH-------->
|
||||
// ^ +------+------+------+
|
||||
// | | | | |
|
||||
// | | | +Y | |
|
||||
// | | | | |
|
||||
// H +------+------+------+
|
||||
// E | | | |
|
||||
// I | -X | -Z | +X |
|
||||
// G | | | |
|
||||
// H +------+------+------+
|
||||
// T | | | |
|
||||
// | | | -Y | |
|
||||
// | | | | |
|
||||
// | +------+------+------+
|
||||
// | | | | |
|
||||
// | | | +Z! | | <+Z is upside down!
|
||||
// | | | | |
|
||||
// V +------+------+------+
|
||||
//
|
||||
// FaceWidth = width / 3 = height / 4
|
||||
{ 3, 4,
|
||||
{ 2, 1, true, false },
|
||||
{ 0, 1, true, false },
|
||||
{ 1, 0, false, true },
|
||||
{ 1, 2, false, true },
|
||||
{ 1, 3, false, true },
|
||||
{ 1, 1, true, false }
|
||||
}
|
||||
};
|
||||
const int CubeLayout::NUM_CUBEMAP_LAYOUTS = sizeof(CubeLayout::CUBEMAP_LAYOUTS) / sizeof(CubeLayout);
|
||||
|
||||
gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance) {
|
||||
PROFILE_RANGE(resource_parse, "processCubeTextureColorFromImage");
|
||||
|
||||
gpu::Texture* theTexture = nullptr;
|
||||
if ((srcImage.width() > 0) && (srcImage.height() > 0)) {
|
||||
QImage image = processSourceImage(srcImage, true);
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
|
||||
gpu::Element formatGPU;
|
||||
gpu::Element formatMip;
|
||||
defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress);
|
||||
|
||||
// Find the layout of the cubemap in the 2D image
|
||||
// Use the original image size since processSourceImage may have altered the size / aspect ratio
|
||||
int foundLayout = CubeLayout::findLayout(srcImage.width(), srcImage.height());
|
||||
|
||||
std::vector<QImage> faces;
|
||||
// If found, go extract the faces as separate images
|
||||
if (foundLayout >= 0) {
|
||||
auto& layout = CubeLayout::CUBEMAP_LAYOUTS[foundLayout];
|
||||
if (layout._type == CubeLayout::FLAT) {
|
||||
int faceWidth = image.width() / layout._widthRatio;
|
||||
|
||||
faces.push_back(image.copy(QRect(layout._faceXPos._x * faceWidth, layout._faceXPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceXPos._horizontalMirror, layout._faceXPos._verticalMirror));
|
||||
faces.push_back(image.copy(QRect(layout._faceXNeg._x * faceWidth, layout._faceXNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceXNeg._horizontalMirror, layout._faceXNeg._verticalMirror));
|
||||
faces.push_back(image.copy(QRect(layout._faceYPos._x * faceWidth, layout._faceYPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceYPos._horizontalMirror, layout._faceYPos._verticalMirror));
|
||||
faces.push_back(image.copy(QRect(layout._faceYNeg._x * faceWidth, layout._faceYNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceYNeg._horizontalMirror, layout._faceYNeg._verticalMirror));
|
||||
faces.push_back(image.copy(QRect(layout._faceZPos._x * faceWidth, layout._faceZPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZPos._horizontalMirror, layout._faceZPos._verticalMirror));
|
||||
faces.push_back(image.copy(QRect(layout._faceZNeg._x * faceWidth, layout._faceZNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZNeg._horizontalMirror, layout._faceZNeg._verticalMirror));
|
||||
} else if (layout._type == CubeLayout::EQUIRECTANGULAR) {
|
||||
// THe face width is estimated from the input image
|
||||
const int EQUIRECT_FACE_RATIO_TO_WIDTH = 4;
|
||||
const int EQUIRECT_MAX_FACE_WIDTH = 2048;
|
||||
int faceWidth = std::min(image.width() / EQUIRECT_FACE_RATIO_TO_WIDTH, EQUIRECT_MAX_FACE_WIDTH);
|
||||
for (int face = gpu::Texture::CUBE_FACE_RIGHT_POS_X; face < gpu::Texture::NUM_CUBE_FACES; face++) {
|
||||
QImage faceImage = CubeLayout::extractEquirectangularFace(image, (gpu::Texture::CubeFace) face, faceWidth);
|
||||
faces.push_back(faceImage);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qCDebug(modelLog) << "Failed to find a known cube map layout from this image:" << QString(srcImageName.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If the 6 faces have been created go on and define the true Texture
|
||||
if (faces.size() == gpu::Texture::NUM_FACES_PER_TYPE[gpu::Texture::TEX_CUBE]) {
|
||||
theTexture = gpu::Texture::createCube(formatGPU, faces[0].width(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
|
||||
theTexture->setSource(srcImageName);
|
||||
theTexture->setStoredMipFormat(formatMip);
|
||||
int f = 0;
|
||||
for (auto& face : faces) {
|
||||
theTexture->assignStoredMipFace(0, f, face.byteCount(), face.constBits());
|
||||
if (generateMips) {
|
||||
generateFaceMips(theTexture, face, f);
|
||||
}
|
||||
f++;
|
||||
}
|
||||
|
||||
// Generate irradiance while we are at it
|
||||
if (generateIrradiance) {
|
||||
PROFILE_RANGE(resource_parse, "generateIrradiance");
|
||||
theTexture->generateIrradiance();
|
||||
}
|
||||
|
||||
theTexture->setSource(srcImageName);
|
||||
}
|
||||
}
|
||||
|
||||
return theTexture;
|
||||
}
|
||||
|
||||
gpu::Texture* TextureUsage::createCubeTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return processCubeTextureColorFromImage(srcImage, srcImageName, false, true, true, true);
|
||||
}
|
||||
|
||||
gpu::Texture* TextureUsage::createCubeTextureFromImageWithoutIrradiance(const QImage& srcImage, const std::string& srcImageName) {
|
||||
return processCubeTextureColorFromImage(srcImage, srcImageName, false, true, true, false);
|
||||
}
|
||||
|
|
|
@ -13,48 +13,10 @@
|
|||
|
||||
#include "gpu/Texture.h"
|
||||
|
||||
#include "Material.h"
|
||||
#include "Transform.h"
|
||||
|
||||
#include <qurl.h>
|
||||
|
||||
class QImage;
|
||||
|
||||
namespace model {
|
||||
|
||||
typedef glm::vec3 Color;
|
||||
|
||||
class TextureUsage {
|
||||
public:
|
||||
gpu::Texture::Type _type{ gpu::Texture::TEX_2D };
|
||||
Material::MapFlags _materialUsage{ MaterialKey::ALBEDO_MAP };
|
||||
|
||||
int _environmentUsage = 0;
|
||||
|
||||
static gpu::Texture* create2DTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||
static gpu::Texture* createStrict2DTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||
static gpu::Texture* createAlbedoTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||
static gpu::Texture* createEmissiveTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||
static gpu::Texture* createNormalTextureFromNormalImage(const QImage& image, const std::string& srcImageName);
|
||||
static gpu::Texture* createNormalTextureFromBumpImage(const QImage& image, const std::string& srcImageName);
|
||||
static gpu::Texture* createRoughnessTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||
static gpu::Texture* createRoughnessTextureFromGlossImage(const QImage& image, const std::string& srcImageName);
|
||||
static gpu::Texture* createMetallicTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||
static gpu::Texture* createCubeTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||
static gpu::Texture* createCubeTextureFromImageWithoutIrradiance(const QImage& image, const std::string& srcImageName);
|
||||
static gpu::Texture* createLightmapTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||
|
||||
|
||||
static const QImage process2DImageColor(const QImage& srcImage, bool& validAlpha, bool& alphaAsMask);
|
||||
static void defineColorTexelFormats(gpu::Element& formatGPU, gpu::Element& formatMip,
|
||||
const QImage& srcImage, bool isLinear, bool doCompress);
|
||||
static gpu::Texture* process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool isStrict = false);
|
||||
static gpu::Texture* processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class TextureMap {
|
||||
public:
|
||||
TextureMap() {}
|
||||
|
|
|
@ -644,8 +644,6 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::unique_ptr<NLPacket> LimitedNodeList::constructPingPacket(PingType_t pingType) {
|
||||
int packetSize = sizeof(PingType_t) + sizeof(quint64);
|
||||
|
||||
|
|
|
@ -257,14 +257,14 @@ void NodeList::reset() {
|
|||
_avatarGainMap.clear();
|
||||
_avatarGainMapLock.unlock();
|
||||
|
||||
// refresh the owner UUID to the NULL UUID
|
||||
setSessionUUID(QUuid());
|
||||
|
||||
if (sender() != &_domainHandler) {
|
||||
// clear the domain connection information, unless they're the ones that asked us to reset
|
||||
_domainHandler.softReset();
|
||||
}
|
||||
|
||||
// refresh the owner UUID to the NULL UUID
|
||||
setSessionUUID(QUuid());
|
||||
|
||||
// if we setup the DTLS socket, also disconnect from the DTLS socket readyRead() so it can handle handshaking
|
||||
if (_dtlsSocket) {
|
||||
disconnect(_dtlsSocket, 0, this, 0);
|
||||
|
|
|
@ -49,7 +49,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::EntityEdit:
|
||||
case PacketType::EntityData:
|
||||
case PacketType::EntityPhysics:
|
||||
return VERSION_ENTITIES_ZONE_FILTERS;
|
||||
return VERSION_ENTITIES_HINGE_CONSTRAINT;
|
||||
case PacketType::EntityQuery:
|
||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::JSONFilterWithFamilyTree);
|
||||
case PacketType::AvatarIdentity:
|
||||
|
|
|
@ -206,6 +206,7 @@ const PacketVersion VERSION_ENTITIES_LAST_EDITED_BY = 65;
|
|||
const PacketVersion VERSION_ENTITIES_SERVER_SCRIPTS = 66;
|
||||
const PacketVersion VERSION_ENTITIES_PHYSICS_PACKET = 67;
|
||||
const PacketVersion VERSION_ENTITIES_ZONE_FILTERS = 68;
|
||||
const PacketVersion VERSION_ENTITIES_HINGE_CONSTRAINT = 69;
|
||||
|
||||
enum class EntityQueryPacketVersion: PacketVersion {
|
||||
JSONFilter = 18,
|
||||
|
|
|
@ -94,7 +94,7 @@ void EntityMotionState::updateServerPhysicsVariables() {
|
|||
_serverPosition = localTransform.getTranslation();
|
||||
_serverRotation = localTransform.getRotation();
|
||||
_serverAcceleration = _entity->getAcceleration();
|
||||
_serverActionData = _entity->getActionData();
|
||||
_serverActionData = _entity->getDynamicData();
|
||||
}
|
||||
|
||||
void EntityMotionState::handleDeactivation() {
|
||||
|
@ -309,7 +309,7 @@ bool EntityMotionState::isCandidateForOwnership() const {
|
|||
assert(entityTreeIsLocked());
|
||||
return _outgoingPriority != 0
|
||||
|| Physics::getSessionUUID() == _entity->getSimulatorID()
|
||||
|| _entity->actionDataNeedsTransmit();
|
||||
|| _entity->dynamicDataNeedsTransmit();
|
||||
}
|
||||
|
||||
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||
|
@ -335,7 +335,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
_serverAcceleration = Vectors::ZERO;
|
||||
_serverAngularVelocity = worldVelocityToLocal.transform(bulletToGLM(_body->getAngularVelocity()));
|
||||
_lastStep = simulationStep;
|
||||
_serverActionData = _entity->getActionData();
|
||||
_serverActionData = _entity->getDynamicData();
|
||||
_numInactiveUpdates = 1;
|
||||
return false;
|
||||
}
|
||||
|
@ -387,7 +387,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
}
|
||||
}
|
||||
|
||||
if (_entity->actionDataNeedsTransmit()) {
|
||||
if (_entity->dynamicDataNeedsTransmit()) {
|
||||
_outgoingPriority = _entity->hasActions() ? SCRIPT_GRAB_SIMULATION_PRIORITY : SCRIPT_POKE_SIMULATION_PRIORITY;
|
||||
return true;
|
||||
}
|
||||
|
@ -474,7 +474,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (_entity->actionDataNeedsTransmit()) {
|
||||
if (_entity->dynamicDataNeedsTransmit()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -551,7 +551,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
|||
_serverPosition = localTransform.getTranslation();
|
||||
_serverRotation = localTransform.getRotation();
|
||||
_serverAcceleration = _entity->getAcceleration();
|
||||
_serverActionData = _entity->getActionData();
|
||||
_serverActionData = _entity->getDynamicData();
|
||||
|
||||
EntityItemProperties properties;
|
||||
|
||||
|
@ -562,8 +562,8 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
|||
properties.setVelocity(_serverVelocity);
|
||||
properties.setAcceleration(_serverAcceleration);
|
||||
properties.setAngularVelocity(_serverAngularVelocity);
|
||||
if (_entity->actionDataNeedsTransmit()) {
|
||||
_entity->setActionDataNeedsTransmit(false);
|
||||
if (_entity->dynamicDataNeedsTransmit()) {
|
||||
_entity->setDynamicDataNeedsTransmit(false);
|
||||
properties.setActionData(_serverActionData);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,13 +16,10 @@
|
|||
#include "PhysicsLogging.h"
|
||||
|
||||
|
||||
ObjectAction::ObjectAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectAction::ObjectAction(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
btActionInterface(),
|
||||
EntityActionInterface(type, id),
|
||||
_ownerEntity(ownerEntity) {
|
||||
}
|
||||
|
||||
ObjectAction::~ObjectAction() {
|
||||
ObjectDynamic(type, id, ownerEntity)
|
||||
{
|
||||
}
|
||||
|
||||
void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) {
|
||||
|
@ -35,7 +32,7 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta
|
|||
});
|
||||
|
||||
if (!ownerEntity) {
|
||||
qCDebug(physics) << "warning -- action with no entity removing self from btCollisionWorld.";
|
||||
qCDebug(physics) << "warning -- action [" << _tag << "] with no entity removing self from btCollisionWorld.";
|
||||
btDynamicsWorld* dynamicsWorld = static_cast<btDynamicsWorld*>(collisionWorld);
|
||||
if (dynamicsWorld) {
|
||||
dynamicsWorld->removeAction(this);
|
||||
|
@ -64,240 +61,5 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta
|
|||
updateActionWorker(deltaTimeStep);
|
||||
}
|
||||
|
||||
qint64 ObjectAction::getEntityServerClockSkew() const {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (!ownerEntity) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const QUuid& entityServerNodeID = ownerEntity->getSourceUUID();
|
||||
auto entityServerNode = nodeList->nodeWithUUID(entityServerNodeID);
|
||||
if (entityServerNode) {
|
||||
return entityServerNode->getClockSkewUsec();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ObjectAction::updateArguments(QVariantMap arguments) {
|
||||
bool somethingChanged = false;
|
||||
|
||||
withWriteLock([&]{
|
||||
quint64 previousExpires = _expires;
|
||||
QString previousTag = _tag;
|
||||
|
||||
bool ttlSet = true;
|
||||
float ttl = EntityActionInterface::extractFloatArgument("action", arguments, "ttl", ttlSet, false);
|
||||
if (ttlSet) {
|
||||
quint64 now = usecTimestampNow();
|
||||
_expires = now + (quint64)(ttl * USECS_PER_SECOND);
|
||||
} else {
|
||||
_expires = 0;
|
||||
}
|
||||
|
||||
bool tagSet = true;
|
||||
QString tag = EntityActionInterface::extractStringArgument("action", arguments, "tag", tagSet, false);
|
||||
if (tagSet) {
|
||||
_tag = tag;
|
||||
} else {
|
||||
tag = "";
|
||||
}
|
||||
|
||||
if (previousExpires != _expires || previousTag != _tag) {
|
||||
somethingChanged = true;
|
||||
}
|
||||
});
|
||||
|
||||
return somethingChanged;
|
||||
}
|
||||
|
||||
QVariantMap ObjectAction::getArguments() {
|
||||
QVariantMap arguments;
|
||||
withReadLock([&]{
|
||||
if (_expires == 0) {
|
||||
arguments["ttl"] = 0.0f;
|
||||
} else {
|
||||
quint64 now = usecTimestampNow();
|
||||
arguments["ttl"] = (float)(_expires - now) / (float)USECS_PER_SECOND;
|
||||
}
|
||||
arguments["tag"] = _tag;
|
||||
|
||||
EntityItemPointer entity = _ownerEntity.lock();
|
||||
if (entity) {
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(entity->getPhysicsInfo());
|
||||
if (motionState) {
|
||||
arguments["::active"] = motionState->isActive();
|
||||
arguments["::motion-type"] = motionTypeToString(motionState->getMotionType());
|
||||
} else {
|
||||
arguments["::no-motion-state"] = true;
|
||||
}
|
||||
}
|
||||
arguments["isMine"] = isMine();
|
||||
});
|
||||
return arguments;
|
||||
}
|
||||
|
||||
|
||||
void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) {
|
||||
}
|
||||
|
||||
void ObjectAction::removeFromSimulation(EntitySimulationPointer simulation) const {
|
||||
QUuid myID;
|
||||
withReadLock([&]{
|
||||
myID = _id;
|
||||
});
|
||||
simulation->removeAction(myID);
|
||||
}
|
||||
|
||||
btRigidBody* ObjectAction::getRigidBody() {
|
||||
ObjectMotionState* motionState = nullptr;
|
||||
withReadLock([&]{
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (!ownerEntity) {
|
||||
return;
|
||||
}
|
||||
void* physicsInfo = ownerEntity->getPhysicsInfo();
|
||||
if (!physicsInfo) {
|
||||
return;
|
||||
}
|
||||
motionState = static_cast<ObjectMotionState*>(physicsInfo);
|
||||
});
|
||||
if (motionState) {
|
||||
return motionState->getRigidBody();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
glm::vec3 ObjectAction::getPosition() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getCenterOfMassPosition());
|
||||
}
|
||||
|
||||
void ObjectAction::setPosition(glm::vec3 position) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return;
|
||||
}
|
||||
// XXX
|
||||
// void setWorldTransform (const btTransform &worldTrans)
|
||||
assert(false);
|
||||
rigidBody->activate();
|
||||
}
|
||||
|
||||
glm::quat ObjectAction::getRotation() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::quat(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getOrientation());
|
||||
}
|
||||
|
||||
void ObjectAction::setRotation(glm::quat rotation) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return;
|
||||
}
|
||||
// XXX
|
||||
// void setWorldTransform (const btTransform &worldTrans)
|
||||
assert(false);
|
||||
rigidBody->activate();
|
||||
}
|
||||
|
||||
glm::vec3 ObjectAction::getLinearVelocity() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getLinearVelocity());
|
||||
}
|
||||
|
||||
void ObjectAction::setLinearVelocity(glm::vec3 linearVelocity) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return;
|
||||
}
|
||||
rigidBody->setLinearVelocity(glmToBullet(glm::vec3(0.0f)));
|
||||
rigidBody->activate();
|
||||
}
|
||||
|
||||
glm::vec3 ObjectAction::getAngularVelocity() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getAngularVelocity());
|
||||
}
|
||||
|
||||
void ObjectAction::setAngularVelocity(glm::vec3 angularVelocity) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return;
|
||||
}
|
||||
rigidBody->setAngularVelocity(glmToBullet(angularVelocity));
|
||||
rigidBody->activate();
|
||||
}
|
||||
|
||||
void ObjectAction::activateBody(bool forceActivation) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (rigidBody) {
|
||||
rigidBody->activate(forceActivation);
|
||||
} else {
|
||||
qCDebug(physics) << "ObjectAction::activateBody -- no rigid body" << (void*)rigidBody;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectAction::forceBodyNonStatic() {
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (!ownerEntity) {
|
||||
return;
|
||||
}
|
||||
void* physicsInfo = ownerEntity->getPhysicsInfo();
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
|
||||
if (motionState && motionState->getMotionType() == MOTION_TYPE_STATIC) {
|
||||
ownerEntity->flagForMotionStateChange();
|
||||
}
|
||||
}
|
||||
|
||||
bool ObjectAction::lifetimeIsOver() {
|
||||
if (_expires == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
if (now >= _expires) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
quint64 ObjectAction::localTimeToServerTime(quint64 timeValue) const {
|
||||
// 0 indicates no expiration
|
||||
if (timeValue == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 serverClockSkew = getEntityServerClockSkew();
|
||||
if (serverClockSkew < 0 && timeValue <= (quint64)(-serverClockSkew)) {
|
||||
return 1; // non-zero but long-expired value to avoid negative roll-over
|
||||
}
|
||||
|
||||
return timeValue + serverClockSkew;
|
||||
}
|
||||
|
||||
quint64 ObjectAction::serverTimeToLocalTime(quint64 timeValue) const {
|
||||
// 0 indicates no expiration
|
||||
if (timeValue == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 serverClockSkew = getEntityServerClockSkew();
|
||||
if (serverClockSkew > 0 && timeValue <= (quint64)serverClockSkew) {
|
||||
return 1; // non-zero but long-expired value to avoid negative roll-over
|
||||
}
|
||||
|
||||
return timeValue - serverClockSkew;
|
||||
}
|
||||
|
|
|
@ -14,27 +14,15 @@
|
|||
#define hifi_ObjectAction_h
|
||||
|
||||
#include <QUuid>
|
||||
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
#include "ObjectDynamic.h"
|
||||
|
||||
#include <shared/ReadWriteLockable.h>
|
||||
|
||||
#include "ObjectMotionState.h"
|
||||
#include "BulletUtil.h"
|
||||
#include "EntityActionInterface.h"
|
||||
|
||||
|
||||
class ObjectAction : public btActionInterface, public EntityActionInterface, public ReadWriteLockable {
|
||||
class ObjectAction : public btActionInterface, public ObjectDynamic {
|
||||
public:
|
||||
ObjectAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~ObjectAction();
|
||||
ObjectAction(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~ObjectAction() {}
|
||||
|
||||
virtual void removeFromSimulation(EntitySimulationPointer simulation) const override;
|
||||
virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; }
|
||||
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) override { _ownerEntity = ownerEntity; }
|
||||
|
||||
virtual bool updateArguments(QVariantMap arguments) override;
|
||||
virtual QVariantMap getArguments() override;
|
||||
virtual bool isAction() const override { return true; }
|
||||
|
||||
// this is called from updateAction and should be overridden by subclasses
|
||||
virtual void updateActionWorker(float deltaTimeStep) = 0;
|
||||
|
@ -42,35 +30,6 @@ public:
|
|||
// these are from btActionInterface
|
||||
virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) override;
|
||||
virtual void debugDraw(btIDebugDraw* debugDrawer) override;
|
||||
|
||||
virtual QByteArray serialize() const override = 0;
|
||||
virtual void deserialize(QByteArray serializedArguments) override = 0;
|
||||
|
||||
virtual bool lifetimeIsOver() override;
|
||||
virtual quint64 getExpires() override { return _expires; }
|
||||
|
||||
protected:
|
||||
quint64 localTimeToServerTime(quint64 timeValue) const;
|
||||
quint64 serverTimeToLocalTime(quint64 timeValue) const;
|
||||
|
||||
virtual btRigidBody* getRigidBody();
|
||||
virtual glm::vec3 getPosition() override;
|
||||
virtual void setPosition(glm::vec3 position) override;
|
||||
virtual glm::quat getRotation() override;
|
||||
virtual void setRotation(glm::quat rotation) override;
|
||||
virtual glm::vec3 getLinearVelocity() override;
|
||||
virtual void setLinearVelocity(glm::vec3 linearVelocity) override;
|
||||
virtual glm::vec3 getAngularVelocity() override;
|
||||
virtual void setAngularVelocity(glm::vec3 angularVelocity) override;
|
||||
virtual void activateBody(bool forceActivation = false);
|
||||
virtual void forceBodyNonStatic();
|
||||
|
||||
EntityItemWeakPointer _ownerEntity;
|
||||
QString _tag;
|
||||
quint64 _expires { 0 }; // in seconds since epoch
|
||||
|
||||
private:
|
||||
qint64 getEntityServerClockSkew() const;
|
||||
};
|
||||
|
||||
#endif // hifi_ObjectAction_h
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
const uint16_t ObjectActionOffset::offsetVersion = 1;
|
||||
|
||||
ObjectActionOffset::ObjectActionOffset(const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectAction(ACTION_TYPE_OFFSET, id, ownerEntity),
|
||||
ObjectAction(DYNAMIC_TYPE_OFFSET, id, ownerEntity),
|
||||
_pointToOffsetFrom(0.0f),
|
||||
_linearDistance(0.0f),
|
||||
_linearTimeScale(FLT_MAX),
|
||||
|
@ -88,26 +88,26 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) {
|
|||
float linearDistance;
|
||||
|
||||
bool needUpdate = false;
|
||||
bool somethingChanged = ObjectAction::updateArguments(arguments);
|
||||
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
|
||||
|
||||
withReadLock([&]{
|
||||
bool ok = true;
|
||||
pointToOffsetFrom =
|
||||
EntityActionInterface::extractVec3Argument("offset action", arguments, "pointToOffsetFrom", ok, true);
|
||||
EntityDynamicInterface::extractVec3Argument("offset action", arguments, "pointToOffsetFrom", ok, true);
|
||||
if (!ok) {
|
||||
pointToOffsetFrom = _pointToOffsetFrom;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
linearTimeScale =
|
||||
EntityActionInterface::extractFloatArgument("offset action", arguments, "linearTimeScale", ok, false);
|
||||
EntityDynamicInterface::extractFloatArgument("offset action", arguments, "linearTimeScale", ok, false);
|
||||
if (!ok) {
|
||||
linearTimeScale = _linearTimeScale;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
linearDistance =
|
||||
EntityActionInterface::extractFloatArgument("offset action", arguments, "linearDistance", ok, false);
|
||||
EntityDynamicInterface::extractFloatArgument("offset action", arguments, "linearDistance", ok, false);
|
||||
if (!ok) {
|
||||
linearDistance = _linearDistance;
|
||||
}
|
||||
|
@ -132,8 +132,8 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) {
|
|||
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setActionDataDirty(true);
|
||||
ownerEntity->setActionDataNeedsTransmit(true);
|
||||
ownerEntity->setDynamicDataDirty(true);
|
||||
ownerEntity->setDynamicDataNeedsTransmit(true);
|
||||
}
|
||||
});
|
||||
activateBody();
|
||||
|
@ -143,7 +143,7 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) {
|
|||
}
|
||||
|
||||
QVariantMap ObjectActionOffset::getArguments() {
|
||||
QVariantMap arguments = ObjectAction::getArguments();
|
||||
QVariantMap arguments = ObjectDynamic::getArguments();
|
||||
withReadLock([&] {
|
||||
arguments["pointToOffsetFrom"] = glmToQMap(_pointToOffsetFrom);
|
||||
arguments["linearTimeScale"] = _linearTimeScale;
|
||||
|
@ -155,7 +155,7 @@ QVariantMap ObjectActionOffset::getArguments() {
|
|||
QByteArray ObjectActionOffset::serialize() const {
|
||||
QByteArray ba;
|
||||
QDataStream dataStream(&ba, QIODevice::WriteOnly);
|
||||
dataStream << ACTION_TYPE_OFFSET;
|
||||
dataStream << DYNAMIC_TYPE_OFFSET;
|
||||
dataStream << getID();
|
||||
dataStream << ObjectActionOffset::offsetVersion;
|
||||
|
||||
|
@ -174,7 +174,7 @@ QByteArray ObjectActionOffset::serialize() const {
|
|||
void ObjectActionOffset::deserialize(QByteArray serializedArguments) {
|
||||
QDataStream dataStream(serializedArguments);
|
||||
|
||||
EntityActionType type;
|
||||
EntityDynamicType type;
|
||||
dataStream >> type;
|
||||
assert(type == getType());
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ const uint16_t ObjectActionSpring::springVersion = 1;
|
|||
|
||||
|
||||
ObjectActionSpring::ObjectActionSpring(const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectAction(ACTION_TYPE_SPRING, id, ownerEntity),
|
||||
ObjectAction(DYNAMIC_TYPE_SPRING, id, ownerEntity),
|
||||
_positionalTarget(glm::vec3(0.0f)),
|
||||
_desiredPositionalTarget(glm::vec3(0.0f)),
|
||||
_linearTimeScale(FLT_MAX),
|
||||
|
@ -64,11 +64,12 @@ bool ObjectActionSpring::prepareForSpringUpdate(btScalar deltaTimeStep) {
|
|||
bool valid = false;
|
||||
int springCount = 0;
|
||||
|
||||
QList<EntityActionPointer> springDerivedActions;
|
||||
springDerivedActions.append(ownerEntity->getActionsOfType(ACTION_TYPE_SPRING));
|
||||
springDerivedActions.append(ownerEntity->getActionsOfType(ACTION_TYPE_HOLD));
|
||||
QList<EntityDynamicPointer> springDerivedActions;
|
||||
springDerivedActions.append(ownerEntity->getActionsOfType(DYNAMIC_TYPE_SPRING));
|
||||
springDerivedActions.append(ownerEntity->getActionsOfType(DYNAMIC_TYPE_FAR_GRAB));
|
||||
springDerivedActions.append(ownerEntity->getActionsOfType(DYNAMIC_TYPE_HOLD));
|
||||
|
||||
foreach (EntityActionPointer action, springDerivedActions) {
|
||||
foreach (EntityDynamicPointer action, springDerivedActions) {
|
||||
std::shared_ptr<ObjectActionSpring> springAction = std::static_pointer_cast<ObjectActionSpring>(action);
|
||||
glm::quat rotationForAction;
|
||||
glm::vec3 positionForAction;
|
||||
|
@ -190,29 +191,29 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
|
|||
float angularTimeScale;
|
||||
|
||||
bool needUpdate = false;
|
||||
bool somethingChanged = ObjectAction::updateArguments(arguments);
|
||||
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
|
||||
withReadLock([&]{
|
||||
// targets are required, spring-constants are optional
|
||||
bool ok = true;
|
||||
positionalTarget = EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ok, false);
|
||||
positionalTarget = EntityDynamicInterface::extractVec3Argument("spring action", arguments, "targetPosition", ok, false);
|
||||
if (!ok) {
|
||||
positionalTarget = _desiredPositionalTarget;
|
||||
}
|
||||
ok = true;
|
||||
linearTimeScale = EntityActionInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", ok, false);
|
||||
linearTimeScale = EntityDynamicInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", ok, false);
|
||||
if (!ok || linearTimeScale <= 0.0f) {
|
||||
linearTimeScale = _linearTimeScale;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
rotationalTarget = EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", ok, false);
|
||||
rotationalTarget = EntityDynamicInterface::extractQuatArgument("spring action", arguments, "targetRotation", ok, false);
|
||||
if (!ok) {
|
||||
rotationalTarget = _desiredRotationalTarget;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
angularTimeScale =
|
||||
EntityActionInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", ok, false);
|
||||
EntityDynamicInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", ok, false);
|
||||
if (!ok) {
|
||||
angularTimeScale = _angularTimeScale;
|
||||
}
|
||||
|
@ -237,8 +238,8 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
|
|||
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setActionDataDirty(true);
|
||||
ownerEntity->setActionDataNeedsTransmit(true);
|
||||
ownerEntity->setDynamicDataDirty(true);
|
||||
ownerEntity->setDynamicDataNeedsTransmit(true);
|
||||
}
|
||||
});
|
||||
activateBody();
|
||||
|
@ -248,7 +249,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
|
|||
}
|
||||
|
||||
QVariantMap ObjectActionSpring::getArguments() {
|
||||
QVariantMap arguments = ObjectAction::getArguments();
|
||||
QVariantMap arguments = ObjectDynamic::getArguments();
|
||||
withReadLock([&] {
|
||||
arguments["linearTimeScale"] = _linearTimeScale;
|
||||
arguments["targetPosition"] = glmToQMap(_desiredPositionalTarget);
|
||||
|
@ -259,14 +260,7 @@ QVariantMap ObjectActionSpring::getArguments() {
|
|||
return arguments;
|
||||
}
|
||||
|
||||
QByteArray ObjectActionSpring::serialize() const {
|
||||
QByteArray serializedActionArguments;
|
||||
QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly);
|
||||
|
||||
dataStream << ACTION_TYPE_SPRING;
|
||||
dataStream << getID();
|
||||
dataStream << ObjectActionSpring::springVersion;
|
||||
|
||||
void ObjectActionSpring::serializeParameters(QDataStream& dataStream) const {
|
||||
withReadLock([&] {
|
||||
dataStream << _desiredPositionalTarget;
|
||||
dataStream << _linearTimeScale;
|
||||
|
@ -277,28 +271,22 @@ QByteArray ObjectActionSpring::serialize() const {
|
|||
dataStream << localTimeToServerTime(_expires);
|
||||
dataStream << _tag;
|
||||
});
|
||||
}
|
||||
|
||||
QByteArray ObjectActionSpring::serialize() const {
|
||||
QByteArray serializedActionArguments;
|
||||
QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly);
|
||||
|
||||
dataStream << DYNAMIC_TYPE_SPRING;
|
||||
dataStream << getID();
|
||||
dataStream << ObjectActionSpring::springVersion;
|
||||
|
||||
serializeParameters(dataStream);
|
||||
|
||||
return serializedActionArguments;
|
||||
}
|
||||
|
||||
void ObjectActionSpring::deserialize(QByteArray serializedArguments) {
|
||||
QDataStream dataStream(serializedArguments);
|
||||
|
||||
EntityActionType type;
|
||||
dataStream >> type;
|
||||
assert(type == getType());
|
||||
|
||||
QUuid id;
|
||||
dataStream >> id;
|
||||
assert(id == getID());
|
||||
|
||||
uint16_t serializationVersion;
|
||||
dataStream >> serializationVersion;
|
||||
if (serializationVersion != ObjectActionSpring::springVersion) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
void ObjectActionSpring::deserializeParameters(QByteArray serializedArguments, QDataStream& dataStream) {
|
||||
withWriteLock([&] {
|
||||
dataStream >> _desiredPositionalTarget;
|
||||
dataStream >> _linearTimeScale;
|
||||
|
@ -317,3 +305,24 @@ void ObjectActionSpring::deserialize(QByteArray serializedArguments) {
|
|||
_active = true;
|
||||
});
|
||||
}
|
||||
|
||||
void ObjectActionSpring::deserialize(QByteArray serializedArguments) {
|
||||
QDataStream dataStream(serializedArguments);
|
||||
|
||||
EntityDynamicType type;
|
||||
dataStream >> type;
|
||||
assert(type == getType());
|
||||
|
||||
QUuid id;
|
||||
dataStream >> id;
|
||||
assert(id == getID());
|
||||
|
||||
uint16_t serializationVersion;
|
||||
dataStream >> serializationVersion;
|
||||
if (serializationVersion != ObjectActionSpring::springVersion) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
deserializeParameters(serializedArguments, dataStream);
|
||||
}
|
||||
|
|
|
@ -47,6 +47,9 @@ protected:
|
|||
glm::vec3 _angularVelocityTarget;
|
||||
|
||||
virtual bool prepareForSpringUpdate(btScalar deltaTimeStep);
|
||||
|
||||
void serializeParameters(QDataStream& dataStream) const;
|
||||
void deserializeParameters(QByteArray serializedArguments, QDataStream& dataStream);
|
||||
};
|
||||
|
||||
#endif // hifi_ObjectActionSpring_h
|
||||
|
|
|
@ -19,7 +19,7 @@ const uint16_t ObjectActionTravelOriented::actionVersion = 1;
|
|||
|
||||
|
||||
ObjectActionTravelOriented::ObjectActionTravelOriented(const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectAction(ACTION_TYPE_TRAVEL_ORIENTED, id, ownerEntity) {
|
||||
ObjectAction(DYNAMIC_TYPE_TRAVEL_ORIENTED, id, ownerEntity) {
|
||||
#if WANT_DEBUG
|
||||
qCDebug(physics) << "ObjectActionTravelOriented::ObjectActionTravelOriented";
|
||||
#endif
|
||||
|
@ -106,16 +106,16 @@ bool ObjectActionTravelOriented::updateArguments(QVariantMap arguments) {
|
|||
float angularTimeScale;
|
||||
|
||||
bool needUpdate = false;
|
||||
bool somethingChanged = ObjectAction::updateArguments(arguments);
|
||||
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
|
||||
withReadLock([&]{
|
||||
bool ok = true;
|
||||
forward = EntityActionInterface::extractVec3Argument("travel oriented action", arguments, "forward", ok, true);
|
||||
forward = EntityDynamicInterface::extractVec3Argument("travel oriented action", arguments, "forward", ok, true);
|
||||
if (!ok) {
|
||||
forward = _forward;
|
||||
}
|
||||
ok = true;
|
||||
angularTimeScale =
|
||||
EntityActionInterface::extractFloatArgument("travel oriented action", arguments, "angularTimeScale", ok, false);
|
||||
EntityDynamicInterface::extractFloatArgument("travel oriented action", arguments, "angularTimeScale", ok, false);
|
||||
if (!ok) {
|
||||
angularTimeScale = _angularTimeScale;
|
||||
}
|
||||
|
@ -136,8 +136,8 @@ bool ObjectActionTravelOriented::updateArguments(QVariantMap arguments) {
|
|||
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setActionDataDirty(true);
|
||||
ownerEntity->setActionDataNeedsTransmit(true);
|
||||
ownerEntity->setDynamicDataDirty(true);
|
||||
ownerEntity->setDynamicDataNeedsTransmit(true);
|
||||
}
|
||||
});
|
||||
activateBody();
|
||||
|
@ -147,7 +147,7 @@ bool ObjectActionTravelOriented::updateArguments(QVariantMap arguments) {
|
|||
}
|
||||
|
||||
QVariantMap ObjectActionTravelOriented::getArguments() {
|
||||
QVariantMap arguments = ObjectAction::getArguments();
|
||||
QVariantMap arguments = ObjectDynamic::getArguments();
|
||||
withReadLock([&] {
|
||||
arguments["forward"] = glmToQMap(_forward);
|
||||
arguments["angularTimeScale"] = _angularTimeScale;
|
||||
|
@ -159,7 +159,7 @@ QByteArray ObjectActionTravelOriented::serialize() const {
|
|||
QByteArray serializedActionArguments;
|
||||
QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly);
|
||||
|
||||
dataStream << ACTION_TYPE_TRAVEL_ORIENTED;
|
||||
dataStream << DYNAMIC_TYPE_TRAVEL_ORIENTED;
|
||||
dataStream << getID();
|
||||
dataStream << ObjectActionTravelOriented::actionVersion;
|
||||
|
||||
|
@ -177,7 +177,7 @@ QByteArray ObjectActionTravelOriented::serialize() const {
|
|||
void ObjectActionTravelOriented::deserialize(QByteArray serializedArguments) {
|
||||
QDataStream dataStream(serializedArguments);
|
||||
|
||||
EntityActionType type;
|
||||
EntityDynamicType type;
|
||||
dataStream >> type;
|
||||
assert(type == getType());
|
||||
|
||||
|
|
25
libraries/physics/src/ObjectConstraint.cpp
Normal file
25
libraries/physics/src/ObjectConstraint.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// ObjectConstraint.cpp
|
||||
// libraries/physcis/src
|
||||
//
|
||||
// Created by Seth Alves 2015-6-2
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "EntitySimulation.h"
|
||||
|
||||
#include "ObjectConstraint.h"
|
||||
|
||||
#include "PhysicsLogging.h"
|
||||
|
||||
ObjectConstraint::ObjectConstraint(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectDynamic(type, id, ownerEntity)
|
||||
{
|
||||
}
|
||||
|
||||
void ObjectConstraint::invalidate() {
|
||||
_constraint = nullptr;
|
||||
}
|
35
libraries/physics/src/ObjectConstraint.h
Normal file
35
libraries/physics/src/ObjectConstraint.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// ObjectConstraint.h
|
||||
// libraries/physcis/src
|
||||
//
|
||||
// Created by Seth Alves 2017-4-11
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// http://bulletphysics.org/Bullet/BulletFull/classbtConstraintInterface.html
|
||||
|
||||
#ifndef hifi_ObjectConstraint_h
|
||||
#define hifi_ObjectConstraint_h
|
||||
|
||||
#include <QUuid>
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
#include "ObjectDynamic.h"
|
||||
|
||||
class ObjectConstraint : public ObjectDynamic
|
||||
{
|
||||
public:
|
||||
ObjectConstraint(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~ObjectConstraint() {}
|
||||
|
||||
virtual btTypedConstraint* getConstraint() = 0;
|
||||
|
||||
virtual bool isConstraint() const override { return true; }
|
||||
virtual void invalidate() override;
|
||||
|
||||
protected:
|
||||
btTypedConstraint* _constraint { nullptr };
|
||||
};
|
||||
|
||||
#endif // hifi_ObjectConstraint_h
|
363
libraries/physics/src/ObjectConstraintHinge.cpp
Normal file
363
libraries/physics/src/ObjectConstraintHinge.cpp
Normal file
|
@ -0,0 +1,363 @@
|
|||
//
|
||||
// ObjectConstraintHinge.cpp
|
||||
// libraries/physics/src
|
||||
//
|
||||
// Created by Seth Alves 2017-4-11
|
||||
// Copyright 2017 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 "QVariantGLM.h"
|
||||
|
||||
#include "EntityTree.h"
|
||||
#include "ObjectConstraintHinge.h"
|
||||
#include "PhysicsLogging.h"
|
||||
|
||||
|
||||
const uint16_t ObjectConstraintHinge::constraintVersion = 1;
|
||||
|
||||
|
||||
ObjectConstraintHinge::ObjectConstraintHinge(const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectConstraint(DYNAMIC_TYPE_HINGE, id, ownerEntity),
|
||||
_pivotInA(glm::vec3(0.0f)),
|
||||
_axisInA(glm::vec3(0.0f))
|
||||
{
|
||||
#if WANT_DEBUG
|
||||
qCDebug(physics) << "ObjectConstraintHinge::ObjectConstraintHinge";
|
||||
#endif
|
||||
}
|
||||
|
||||
ObjectConstraintHinge::~ObjectConstraintHinge() {
|
||||
#if WANT_DEBUG
|
||||
qCDebug(physics) << "ObjectConstraintHinge::~ObjectConstraintHinge";
|
||||
#endif
|
||||
}
|
||||
|
||||
QList<btRigidBody*> ObjectConstraintHinge::getRigidBodies() {
|
||||
QList<btRigidBody*> result;
|
||||
result += getRigidBody();
|
||||
QUuid otherEntityID;
|
||||
withReadLock([&]{
|
||||
otherEntityID = _otherEntityID;
|
||||
});
|
||||
if (!otherEntityID.isNull()) {
|
||||
result += getOtherRigidBody(otherEntityID);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ObjectConstraintHinge::updateHinge() {
|
||||
btHingeConstraint* constraint { nullptr };
|
||||
float low;
|
||||
float high;
|
||||
float softness;
|
||||
float biasFactor;
|
||||
float relaxationFactor;
|
||||
float motorVelocity;
|
||||
|
||||
withReadLock([&]{
|
||||
constraint = static_cast<btHingeConstraint*>(_constraint);
|
||||
low = _low;
|
||||
high = _high;
|
||||
softness = _softness;
|
||||
biasFactor = _biasFactor;
|
||||
relaxationFactor = _relaxationFactor;
|
||||
motorVelocity = _motorVelocity;
|
||||
});
|
||||
|
||||
if (!constraint) {
|
||||
return;
|
||||
}
|
||||
|
||||
constraint->setLimit(low, high, softness, biasFactor, relaxationFactor);
|
||||
if (motorVelocity != 0.0f) {
|
||||
constraint->setMotorTargetVelocity(motorVelocity);
|
||||
constraint->enableMotor(true);
|
||||
} else {
|
||||
constraint->enableMotor(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
btTypedConstraint* ObjectConstraintHinge::getConstraint() {
|
||||
btHingeConstraint* constraint { nullptr };
|
||||
QUuid otherEntityID;
|
||||
glm::vec3 pivotInA;
|
||||
glm::vec3 axisInA;
|
||||
glm::vec3 pivotInB;
|
||||
glm::vec3 axisInB;
|
||||
|
||||
withReadLock([&]{
|
||||
constraint = static_cast<btHingeConstraint*>(_constraint);
|
||||
pivotInA = _pivotInA;
|
||||
axisInA = _axisInA;
|
||||
otherEntityID = _otherEntityID;
|
||||
pivotInB = _pivotInB;
|
||||
axisInB = _axisInB;
|
||||
});
|
||||
if (constraint) {
|
||||
return constraint;
|
||||
}
|
||||
|
||||
btRigidBody* rigidBodyA = getRigidBody();
|
||||
if (!rigidBodyA) {
|
||||
qCDebug(physics) << "ObjectConstraintHinge::getConstraint -- no rigidBodyA";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!otherEntityID.isNull()) {
|
||||
// This hinge is between two entities... find the other rigid body.
|
||||
btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID);
|
||||
if (!rigidBodyB) {
|
||||
return nullptr;
|
||||
}
|
||||
constraint = new btHingeConstraint(*rigidBodyA, *rigidBodyB,
|
||||
glmToBullet(pivotInA), glmToBullet(pivotInB),
|
||||
glmToBullet(axisInA), glmToBullet(axisInB),
|
||||
true); // useReferenceFrameA
|
||||
|
||||
} else {
|
||||
// This hinge is between an entity and the world-frame.
|
||||
constraint = new btHingeConstraint(*rigidBodyA,
|
||||
glmToBullet(pivotInA), glmToBullet(axisInA),
|
||||
true); // useReferenceFrameA
|
||||
}
|
||||
|
||||
withWriteLock([&]{
|
||||
_constraint = constraint;
|
||||
});
|
||||
|
||||
// if we don't wake up rigidBodyA, we may not send the dynamicData property over the network
|
||||
forceBodyNonStatic();
|
||||
activateBody();
|
||||
|
||||
updateHinge();
|
||||
|
||||
return constraint;
|
||||
}
|
||||
|
||||
|
||||
bool ObjectConstraintHinge::updateArguments(QVariantMap arguments) {
|
||||
glm::vec3 pivotInA;
|
||||
glm::vec3 axisInA;
|
||||
QUuid otherEntityID;
|
||||
glm::vec3 pivotInB;
|
||||
glm::vec3 axisInB;
|
||||
float low;
|
||||
float high;
|
||||
float softness;
|
||||
float biasFactor;
|
||||
float relaxationFactor;
|
||||
float motorVelocity;
|
||||
|
||||
bool needUpdate = false;
|
||||
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
|
||||
withReadLock([&]{
|
||||
bool ok = true;
|
||||
pivotInA = EntityDynamicInterface::extractVec3Argument("hinge constraint", arguments, "pivot", ok, false);
|
||||
if (!ok) {
|
||||
pivotInA = _pivotInA;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
axisInA = EntityDynamicInterface::extractVec3Argument("hinge constraint", arguments, "axis", ok, false);
|
||||
if (!ok) {
|
||||
axisInA = _axisInA;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
otherEntityID = QUuid(EntityDynamicInterface::extractStringArgument("hinge constraint",
|
||||
arguments, "otherEntityID", ok, false));
|
||||
if (!ok) {
|
||||
otherEntityID = _otherEntityID;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
pivotInB = EntityDynamicInterface::extractVec3Argument("hinge constraint", arguments, "otherPivot", ok, false);
|
||||
if (!ok) {
|
||||
pivotInB = _pivotInB;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
axisInB = EntityDynamicInterface::extractVec3Argument("hinge constraint", arguments, "otherAxis", ok, false);
|
||||
if (!ok) {
|
||||
axisInB = _axisInB;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
low = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, "low", ok, false);
|
||||
if (!ok) {
|
||||
low = _low;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
high = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, "high", ok, false);
|
||||
if (!ok) {
|
||||
high = _high;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
softness = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, "softness", ok, false);
|
||||
if (!ok) {
|
||||
softness = _softness;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
biasFactor = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, "biasFactor", ok, false);
|
||||
if (!ok) {
|
||||
biasFactor = _biasFactor;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
relaxationFactor = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments,
|
||||
"relaxationFactor", ok, false);
|
||||
if (!ok) {
|
||||
relaxationFactor = _relaxationFactor;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
motorVelocity = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments,
|
||||
"motorVelocity", ok, false);
|
||||
if (!ok) {
|
||||
motorVelocity = _motorVelocity;
|
||||
}
|
||||
|
||||
if (somethingChanged ||
|
||||
pivotInA != _pivotInA ||
|
||||
axisInA != _axisInA ||
|
||||
otherEntityID != _otherEntityID ||
|
||||
pivotInB != _pivotInB ||
|
||||
axisInB != _axisInB ||
|
||||
low != _low ||
|
||||
high != _high ||
|
||||
softness != _softness ||
|
||||
biasFactor != _biasFactor ||
|
||||
relaxationFactor != _relaxationFactor ||
|
||||
motorVelocity != _motorVelocity) {
|
||||
// something changed
|
||||
needUpdate = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (needUpdate) {
|
||||
withWriteLock([&] {
|
||||
_pivotInA = pivotInA;
|
||||
_axisInA = axisInA;
|
||||
_otherEntityID = otherEntityID;
|
||||
_pivotInB = pivotInB;
|
||||
_axisInB = axisInB;
|
||||
_low = low;
|
||||
_high = high;
|
||||
_softness = softness;
|
||||
_biasFactor = biasFactor;
|
||||
_relaxationFactor = relaxationFactor;
|
||||
_motorVelocity = motorVelocity;
|
||||
|
||||
_active = true;
|
||||
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setDynamicDataDirty(true);
|
||||
ownerEntity->setDynamicDataNeedsTransmit(true);
|
||||
}
|
||||
});
|
||||
|
||||
updateHinge();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariantMap ObjectConstraintHinge::getArguments() {
|
||||
QVariantMap arguments = ObjectDynamic::getArguments();
|
||||
withReadLock([&] {
|
||||
if (_constraint) {
|
||||
arguments["pivot"] = glmToQMap(_pivotInA);
|
||||
arguments["axis"] = glmToQMap(_axisInA);
|
||||
arguments["otherEntityID"] = _otherEntityID;
|
||||
arguments["otherPivot"] = glmToQMap(_pivotInB);
|
||||
arguments["otherAxis"] = glmToQMap(_axisInB);
|
||||
arguments["low"] = _low;
|
||||
arguments["high"] = _high;
|
||||
arguments["softness"] = _softness;
|
||||
arguments["biasFactor"] = _biasFactor;
|
||||
arguments["relaxationFactor"] = _relaxationFactor;
|
||||
arguments["motorVelocity"] = _motorVelocity;
|
||||
arguments["angle"] = static_cast<btHingeConstraint*>(_constraint)->getHingeAngle(); // [-PI,PI]
|
||||
}
|
||||
});
|
||||
return arguments;
|
||||
}
|
||||
|
||||
QByteArray ObjectConstraintHinge::serialize() const {
|
||||
QByteArray serializedConstraintArguments;
|
||||
QDataStream dataStream(&serializedConstraintArguments, QIODevice::WriteOnly);
|
||||
|
||||
dataStream << DYNAMIC_TYPE_HINGE;
|
||||
dataStream << getID();
|
||||
dataStream << ObjectConstraintHinge::constraintVersion;
|
||||
|
||||
withReadLock([&] {
|
||||
dataStream << _pivotInA;
|
||||
dataStream << _axisInA;
|
||||
dataStream << _otherEntityID;
|
||||
dataStream << _pivotInB;
|
||||
dataStream << _axisInB;
|
||||
dataStream << _low;
|
||||
dataStream << _high;
|
||||
dataStream << _softness;
|
||||
dataStream << _biasFactor;
|
||||
dataStream << _relaxationFactor;
|
||||
|
||||
dataStream << localTimeToServerTime(_expires);
|
||||
dataStream << _tag;
|
||||
|
||||
dataStream << _motorVelocity;
|
||||
});
|
||||
|
||||
return serializedConstraintArguments;
|
||||
}
|
||||
|
||||
void ObjectConstraintHinge::deserialize(QByteArray serializedArguments) {
|
||||
QDataStream dataStream(serializedArguments);
|
||||
|
||||
EntityDynamicType type;
|
||||
dataStream >> type;
|
||||
assert(type == getType());
|
||||
|
||||
QUuid id;
|
||||
dataStream >> id;
|
||||
assert(id == getID());
|
||||
|
||||
uint16_t serializationVersion;
|
||||
dataStream >> serializationVersion;
|
||||
if (serializationVersion != ObjectConstraintHinge::constraintVersion) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
withWriteLock([&] {
|
||||
dataStream >> _pivotInA;
|
||||
dataStream >> _axisInA;
|
||||
dataStream >> _otherEntityID;
|
||||
dataStream >> _pivotInB;
|
||||
dataStream >> _axisInB;
|
||||
dataStream >> _low;
|
||||
dataStream >> _high;
|
||||
dataStream >> _softness;
|
||||
dataStream >> _biasFactor;
|
||||
dataStream >> _relaxationFactor;
|
||||
|
||||
quint64 serverExpires;
|
||||
dataStream >> serverExpires;
|
||||
_expires = serverTimeToLocalTime(serverExpires);
|
||||
|
||||
dataStream >> _tag;
|
||||
|
||||
dataStream >> _motorVelocity;
|
||||
|
||||
_active = true;
|
||||
});
|
||||
}
|
53
libraries/physics/src/ObjectConstraintHinge.h
Normal file
53
libraries/physics/src/ObjectConstraintHinge.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// ObjectConstraintHinge.h
|
||||
// libraries/physics/src
|
||||
//
|
||||
// Created by Seth Alves 2017-4-11
|
||||
// Copyright 2017 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_ObjectConstraintHinge_h
|
||||
#define hifi_ObjectConstraintHinge_h
|
||||
|
||||
#include "ObjectConstraint.h"
|
||||
|
||||
// http://bulletphysics.org/Bullet/BulletFull/classbtHingeConstraint.html
|
||||
|
||||
class ObjectConstraintHinge : public ObjectConstraint {
|
||||
public:
|
||||
ObjectConstraintHinge(const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~ObjectConstraintHinge();
|
||||
|
||||
virtual bool updateArguments(QVariantMap arguments) override;
|
||||
virtual QVariantMap getArguments() override;
|
||||
|
||||
virtual QByteArray serialize() const override;
|
||||
virtual void deserialize(QByteArray serializedArguments) override;
|
||||
|
||||
virtual QList<btRigidBody*> getRigidBodies() override;
|
||||
virtual btTypedConstraint* getConstraint() override;
|
||||
|
||||
protected:
|
||||
static const uint16_t constraintVersion;
|
||||
|
||||
void updateHinge();
|
||||
|
||||
glm::vec3 _pivotInA;
|
||||
glm::vec3 _axisInA;
|
||||
|
||||
EntityItemID _otherEntityID;
|
||||
glm::vec3 _pivotInB;
|
||||
glm::vec3 _axisInB;
|
||||
|
||||
float _low { -2.0f * PI };
|
||||
float _high { 2.0f * PI };
|
||||
float _softness { 0.9f };
|
||||
float _biasFactor { 0.3f };
|
||||
float _relaxationFactor { 1.0f };
|
||||
float _motorVelocity { 0.0f };
|
||||
};
|
||||
|
||||
#endif // hifi_ObjectConstraintHinge_h
|
276
libraries/physics/src/ObjectDynamic.cpp
Normal file
276
libraries/physics/src/ObjectDynamic.cpp
Normal file
|
@ -0,0 +1,276 @@
|
|||
//
|
||||
// ObjectDynamic.cpp
|
||||
// libraries/physcis/src
|
||||
//
|
||||
// Created by Seth Alves 2015-6-2
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "EntitySimulation.h"
|
||||
|
||||
#include "ObjectDynamic.h"
|
||||
|
||||
#include "PhysicsLogging.h"
|
||||
|
||||
|
||||
ObjectDynamic::ObjectDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
EntityDynamicInterface(type, id),
|
||||
_ownerEntity(ownerEntity) {
|
||||
}
|
||||
|
||||
ObjectDynamic::~ObjectDynamic() {
|
||||
}
|
||||
|
||||
qint64 ObjectDynamic::getEntityServerClockSkew() const {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (!ownerEntity) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const QUuid& entityServerNodeID = ownerEntity->getSourceUUID();
|
||||
auto entityServerNode = nodeList->nodeWithUUID(entityServerNodeID);
|
||||
if (entityServerNode) {
|
||||
return entityServerNode->getClockSkewUsec();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ObjectDynamic::updateArguments(QVariantMap arguments) {
|
||||
bool somethingChanged = false;
|
||||
|
||||
withWriteLock([&]{
|
||||
quint64 previousExpires = _expires;
|
||||
QString previousTag = _tag;
|
||||
|
||||
bool ttlSet = true;
|
||||
float ttl = EntityDynamicInterface::extractFloatArgument("dynamic", arguments, "ttl", ttlSet, false);
|
||||
if (ttlSet) {
|
||||
quint64 now = usecTimestampNow();
|
||||
_expires = now + (quint64)(ttl * USECS_PER_SECOND);
|
||||
} else {
|
||||
_expires = 0;
|
||||
}
|
||||
|
||||
bool tagSet = true;
|
||||
QString tag = EntityDynamicInterface::extractStringArgument("dynamic", arguments, "tag", tagSet, false);
|
||||
if (tagSet) {
|
||||
_tag = tag;
|
||||
} else {
|
||||
tag = "";
|
||||
}
|
||||
|
||||
if (previousExpires != _expires || previousTag != _tag) {
|
||||
somethingChanged = true;
|
||||
}
|
||||
});
|
||||
|
||||
return somethingChanged;
|
||||
}
|
||||
|
||||
QVariantMap ObjectDynamic::getArguments() {
|
||||
QVariantMap arguments;
|
||||
withReadLock([&]{
|
||||
if (_expires == 0) {
|
||||
arguments["ttl"] = 0.0f;
|
||||
} else {
|
||||
quint64 now = usecTimestampNow();
|
||||
arguments["ttl"] = (float)(_expires - now) / (float)USECS_PER_SECOND;
|
||||
}
|
||||
arguments["tag"] = _tag;
|
||||
|
||||
EntityItemPointer entity = _ownerEntity.lock();
|
||||
if (entity) {
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(entity->getPhysicsInfo());
|
||||
if (motionState) {
|
||||
arguments["::active"] = motionState->isActive();
|
||||
arguments["::motion-type"] = motionTypeToString(motionState->getMotionType());
|
||||
} else {
|
||||
arguments["::no-motion-state"] = true;
|
||||
}
|
||||
}
|
||||
arguments["isMine"] = isMine();
|
||||
});
|
||||
return arguments;
|
||||
}
|
||||
|
||||
void ObjectDynamic::removeFromSimulation(EntitySimulationPointer simulation) const {
|
||||
QUuid myID;
|
||||
withReadLock([&]{
|
||||
myID = _id;
|
||||
});
|
||||
simulation->removeDynamic(myID);
|
||||
}
|
||||
|
||||
EntityItemPointer ObjectDynamic::getEntityByID(EntityItemID entityID) const {
|
||||
EntityItemPointer ownerEntity;
|
||||
withReadLock([&]{
|
||||
ownerEntity = _ownerEntity.lock();
|
||||
});
|
||||
EntityTreeElementPointer element = ownerEntity ? ownerEntity->getElement() : nullptr;
|
||||
EntityTreePointer tree = element ? element->getTree() : nullptr;
|
||||
if (!tree) {
|
||||
return nullptr;
|
||||
}
|
||||
return tree->findEntityByID(entityID);
|
||||
}
|
||||
|
||||
|
||||
btRigidBody* ObjectDynamic::getRigidBody() {
|
||||
ObjectMotionState* motionState = nullptr;
|
||||
withReadLock([&]{
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (!ownerEntity) {
|
||||
return;
|
||||
}
|
||||
void* physicsInfo = ownerEntity->getPhysicsInfo();
|
||||
if (!physicsInfo) {
|
||||
return;
|
||||
}
|
||||
motionState = static_cast<ObjectMotionState*>(physicsInfo);
|
||||
});
|
||||
if (motionState) {
|
||||
return motionState->getRigidBody();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
glm::vec3 ObjectDynamic::getPosition() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getCenterOfMassPosition());
|
||||
}
|
||||
|
||||
glm::quat ObjectDynamic::getRotation() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::quat(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getOrientation());
|
||||
}
|
||||
|
||||
glm::vec3 ObjectDynamic::getLinearVelocity() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getLinearVelocity());
|
||||
}
|
||||
|
||||
void ObjectDynamic::setLinearVelocity(glm::vec3 linearVelocity) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return;
|
||||
}
|
||||
rigidBody->setLinearVelocity(glmToBullet(glm::vec3(0.0f)));
|
||||
rigidBody->activate();
|
||||
}
|
||||
|
||||
glm::vec3 ObjectDynamic::getAngularVelocity() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getAngularVelocity());
|
||||
}
|
||||
|
||||
void ObjectDynamic::setAngularVelocity(glm::vec3 angularVelocity) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return;
|
||||
}
|
||||
rigidBody->setAngularVelocity(glmToBullet(angularVelocity));
|
||||
rigidBody->activate();
|
||||
}
|
||||
|
||||
void ObjectDynamic::activateBody(bool forceActivation) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (rigidBody) {
|
||||
rigidBody->activate(forceActivation);
|
||||
} else {
|
||||
qCDebug(physics) << "ObjectDynamic::activateBody -- no rigid body" << (void*)rigidBody;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectDynamic::forceBodyNonStatic() {
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (!ownerEntity) {
|
||||
return;
|
||||
}
|
||||
void* physicsInfo = ownerEntity->getPhysicsInfo();
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
|
||||
if (motionState && motionState->getMotionType() == MOTION_TYPE_STATIC) {
|
||||
ownerEntity->flagForMotionStateChange();
|
||||
}
|
||||
}
|
||||
|
||||
bool ObjectDynamic::lifetimeIsOver() {
|
||||
if (_expires == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
if (now >= _expires) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
quint64 ObjectDynamic::localTimeToServerTime(quint64 timeValue) const {
|
||||
// 0 indicates no expiration
|
||||
if (timeValue == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 serverClockSkew = getEntityServerClockSkew();
|
||||
if (serverClockSkew < 0 && timeValue <= (quint64)(-serverClockSkew)) {
|
||||
return 1; // non-zero but long-expired value to avoid negative roll-over
|
||||
}
|
||||
|
||||
return timeValue + serverClockSkew;
|
||||
}
|
||||
|
||||
quint64 ObjectDynamic::serverTimeToLocalTime(quint64 timeValue) const {
|
||||
// 0 indicates no expiration
|
||||
if (timeValue == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 serverClockSkew = getEntityServerClockSkew();
|
||||
if (serverClockSkew > 0 && timeValue <= (quint64)serverClockSkew) {
|
||||
return 1; // non-zero but long-expired value to avoid negative roll-over
|
||||
}
|
||||
|
||||
return timeValue - serverClockSkew;
|
||||
}
|
||||
|
||||
btRigidBody* ObjectDynamic::getOtherRigidBody(EntityItemID otherEntityID) {
|
||||
EntityItemPointer otherEntity = getEntityByID(otherEntityID);
|
||||
if (!otherEntity) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* otherPhysicsInfo = otherEntity->getPhysicsInfo();
|
||||
if (!otherPhysicsInfo) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ObjectMotionState* otherMotionState = static_cast<ObjectMotionState*>(otherPhysicsInfo);
|
||||
if (!otherMotionState) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return otherMotionState->getRigidBody();
|
||||
}
|
||||
|
||||
QList<btRigidBody*> ObjectDynamic::getRigidBodies() {
|
||||
QList<btRigidBody*> result;
|
||||
result += getRigidBody();
|
||||
return result;
|
||||
}
|
76
libraries/physics/src/ObjectDynamic.h
Normal file
76
libraries/physics/src/ObjectDynamic.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// ObjectDynamic.h
|
||||
// libraries/physcis/src
|
||||
//
|
||||
// Created by Seth Alves 2015-6-2
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// http://bulletphysics.org/Bullet/BulletFull/classbtDynamicInterface.html
|
||||
|
||||
#ifndef hifi_ObjectDynamic_h
|
||||
#define hifi_ObjectDynamic_h
|
||||
|
||||
#include <QUuid>
|
||||
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
|
||||
#include <shared/ReadWriteLockable.h>
|
||||
|
||||
#include "ObjectMotionState.h"
|
||||
#include "BulletUtil.h"
|
||||
#include "EntityDynamicInterface.h"
|
||||
|
||||
|
||||
class ObjectDynamic : public EntityDynamicInterface, public ReadWriteLockable {
|
||||
public:
|
||||
ObjectDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~ObjectDynamic();
|
||||
|
||||
virtual void removeFromSimulation(EntitySimulationPointer simulation) const override;
|
||||
virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; }
|
||||
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) override { _ownerEntity = ownerEntity; }
|
||||
|
||||
virtual void invalidate() {};
|
||||
|
||||
virtual bool updateArguments(QVariantMap arguments) override;
|
||||
virtual QVariantMap getArguments() override;
|
||||
|
||||
|
||||
virtual QByteArray serialize() const override = 0;
|
||||
virtual void deserialize(QByteArray serializedArguments) override = 0;
|
||||
|
||||
virtual bool lifetimeIsOver() override;
|
||||
virtual quint64 getExpires() override { return _expires; }
|
||||
|
||||
virtual QList<btRigidBody*> getRigidBodies();
|
||||
|
||||
protected:
|
||||
quint64 localTimeToServerTime(quint64 timeValue) const;
|
||||
quint64 serverTimeToLocalTime(quint64 timeValue) const;
|
||||
|
||||
btRigidBody* getOtherRigidBody(EntityItemID otherEntityID);
|
||||
EntityItemPointer getEntityByID(EntityItemID entityID) const;
|
||||
virtual btRigidBody* getRigidBody();
|
||||
virtual glm::vec3 getPosition() override;
|
||||
virtual glm::quat getRotation() override;
|
||||
virtual glm::vec3 getLinearVelocity() override;
|
||||
virtual void setLinearVelocity(glm::vec3 linearVelocity) override;
|
||||
virtual glm::vec3 getAngularVelocity() override;
|
||||
virtual void setAngularVelocity(glm::vec3 angularVelocity) override;
|
||||
virtual void activateBody(bool forceActivation = false);
|
||||
virtual void forceBodyNonStatic();
|
||||
|
||||
EntityItemWeakPointer _ownerEntity;
|
||||
QString _tag;
|
||||
quint64 _expires { 0 }; // in seconds since epoch
|
||||
|
||||
private:
|
||||
qint64 getEntityServerClockSkew() const;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<ObjectDynamic> ObjectDynamicPointer;
|
||||
|
||||
#endif // hifi_ObjectDynamic_h
|
|
@ -330,33 +330,41 @@ void PhysicalEntitySimulation::handleCollisionEvents(const CollisionEvents& coll
|
|||
}
|
||||
|
||||
|
||||
void PhysicalEntitySimulation::addAction(EntityActionPointer action) {
|
||||
void PhysicalEntitySimulation::addDynamic(EntityDynamicPointer dynamic) {
|
||||
if (_physicsEngine) {
|
||||
// FIXME put fine grain locking into _physicsEngine
|
||||
{
|
||||
QMutexLocker lock(&_mutex);
|
||||
const QUuid& actionID = action->getID();
|
||||
if (_physicsEngine->getActionByID(actionID)) {
|
||||
qCDebug(physics) << "warning -- PhysicalEntitySimulation::addAction -- adding an "
|
||||
"action that was already in _physicsEngine";
|
||||
const QUuid& dynamicID = dynamic->getID();
|
||||
if (_physicsEngine->getDynamicByID(dynamicID)) {
|
||||
qCDebug(physics) << "warning -- PhysicalEntitySimulation::addDynamic -- adding an "
|
||||
"dynamic that was already in _physicsEngine";
|
||||
}
|
||||
}
|
||||
EntitySimulation::addAction(action);
|
||||
EntitySimulation::addDynamic(dynamic);
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::applyActionChanges() {
|
||||
void PhysicalEntitySimulation::applyDynamicChanges() {
|
||||
QList<EntityDynamicPointer> dynamicsFailedToAdd;
|
||||
if (_physicsEngine) {
|
||||
// FIXME put fine grain locking into _physicsEngine
|
||||
QMutexLocker lock(&_mutex);
|
||||
foreach(QUuid actionToRemove, _actionsToRemove) {
|
||||
_physicsEngine->removeAction(actionToRemove);
|
||||
foreach(QUuid dynamicToRemove, _dynamicsToRemove) {
|
||||
_physicsEngine->removeDynamic(dynamicToRemove);
|
||||
}
|
||||
foreach (EntityActionPointer actionToAdd, _actionsToAdd) {
|
||||
if (!_actionsToRemove.contains(actionToAdd->getID())) {
|
||||
_physicsEngine->addAction(actionToAdd);
|
||||
foreach (EntityDynamicPointer dynamicToAdd, _dynamicsToAdd) {
|
||||
if (!_dynamicsToRemove.contains(dynamicToAdd->getID())) {
|
||||
if (!_physicsEngine->addDynamic(dynamicToAdd)) {
|
||||
dynamicsFailedToAdd += dynamicToAdd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EntitySimulation::applyActionChanges();
|
||||
// applyDynamicChanges will clear _dynamicsToRemove and _dynamicsToAdd
|
||||
EntitySimulation::applyDynamicChanges();
|
||||
// put back the ones that couldn't yet be added
|
||||
foreach (EntityDynamicPointer dynamicFailedToAdd, dynamicsFailedToAdd) {
|
||||
addDynamic(dynamicFailedToAdd);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,8 +34,8 @@ public:
|
|||
|
||||
void init(EntityTreePointer tree, PhysicsEnginePointer engine, EntityEditPacketSender* packetSender);
|
||||
|
||||
virtual void addAction(EntityActionPointer action) override;
|
||||
virtual void applyActionChanges() override;
|
||||
virtual void addDynamic(EntityDynamicPointer dynamic) override;
|
||||
virtual void applyDynamicChanges() override;
|
||||
|
||||
virtual void takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) override;
|
||||
|
||||
|
|
|
@ -142,6 +142,26 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
|
|||
motionState->clearIncomingDirtyFlags();
|
||||
}
|
||||
|
||||
QList<EntityDynamicPointer> PhysicsEngine::removeDynamicsForBody(btRigidBody* body) {
|
||||
// remove dynamics that are attached to this body
|
||||
QList<EntityDynamicPointer> removedDynamics;
|
||||
QMutableSetIterator<QUuid> i(_objectDynamicsByBody[body]);
|
||||
|
||||
while (i.hasNext()) {
|
||||
QUuid dynamicID = i.next();
|
||||
if (dynamicID.isNull()) {
|
||||
continue;
|
||||
}
|
||||
EntityDynamicPointer dynamic = _objectDynamics[dynamicID];
|
||||
if (!dynamic) {
|
||||
continue;
|
||||
}
|
||||
removeDynamic(dynamicID);
|
||||
removedDynamics += dynamic;
|
||||
}
|
||||
return removedDynamics;
|
||||
}
|
||||
|
||||
void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) {
|
||||
// bump and prune contacts for all objects in the list
|
||||
for (auto object : objects) {
|
||||
|
@ -175,6 +195,7 @@ void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) {
|
|||
for (auto object : objects) {
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
if (body) {
|
||||
removeDynamicsForBody(body);
|
||||
_dynamicsWorld->removeRigidBody(body);
|
||||
|
||||
// NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it.
|
||||
|
@ -191,6 +212,7 @@ void PhysicsEngine::removeObjects(const SetOfMotionStates& objects) {
|
|||
for (auto object : objects) {
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
if (body) {
|
||||
removeDynamicsForBody(body);
|
||||
_dynamicsWorld->removeRigidBody(body);
|
||||
|
||||
// NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it.
|
||||
|
@ -240,7 +262,6 @@ void PhysicsEngine::reinsertObject(ObjectMotionState* object) {
|
|||
btRigidBody* body = object->getRigidBody();
|
||||
if (body) {
|
||||
_dynamicsWorld->removeRigidBody(body);
|
||||
|
||||
// add it back
|
||||
addObjectToDynamicsWorld(object);
|
||||
}
|
||||
|
@ -548,44 +569,84 @@ void PhysicsEngine::setCharacterController(CharacterController* character) {
|
|||
}
|
||||
}
|
||||
|
||||
EntityActionPointer PhysicsEngine::getActionByID(const QUuid& actionID) const {
|
||||
if (_objectActions.contains(actionID)) {
|
||||
return _objectActions[actionID];
|
||||
EntityDynamicPointer PhysicsEngine::getDynamicByID(const QUuid& dynamicID) const {
|
||||
if (_objectDynamics.contains(dynamicID)) {
|
||||
return _objectDynamics[dynamicID];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PhysicsEngine::addAction(EntityActionPointer action) {
|
||||
assert(action);
|
||||
const QUuid& actionID = action->getID();
|
||||
if (_objectActions.contains(actionID)) {
|
||||
if (_objectActions[actionID] == action) {
|
||||
bool PhysicsEngine::addDynamic(EntityDynamicPointer dynamic) {
|
||||
assert(dynamic);
|
||||
|
||||
if (!dynamic->isReadyForAdd()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const QUuid& dynamicID = dynamic->getID();
|
||||
if (_objectDynamics.contains(dynamicID)) {
|
||||
if (_objectDynamics[dynamicID] == dynamic) {
|
||||
return true;
|
||||
}
|
||||
removeDynamic(dynamic->getID());
|
||||
}
|
||||
|
||||
bool success { false };
|
||||
if (dynamic->isAction()) {
|
||||
ObjectAction* objectAction = static_cast<ObjectAction*>(dynamic.get());
|
||||
_dynamicsWorld->addAction(objectAction);
|
||||
success = true;
|
||||
} else if (dynamic->isConstraint()) {
|
||||
ObjectConstraint* objectConstraint = static_cast<ObjectConstraint*>(dynamic.get());
|
||||
btTypedConstraint* constraint = objectConstraint->getConstraint();
|
||||
if (constraint) {
|
||||
_dynamicsWorld->addConstraint(constraint);
|
||||
success = true;
|
||||
} // else perhaps not all the rigid bodies are available, yet
|
||||
}
|
||||
|
||||
if (success) {
|
||||
_objectDynamics[dynamicID] = dynamic;
|
||||
foreach(btRigidBody* rigidBody, std::static_pointer_cast<ObjectDynamic>(dynamic)->getRigidBodies()) {
|
||||
_objectDynamicsByBody[rigidBody] += dynamic->getID();
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void PhysicsEngine::removeDynamic(const QUuid dynamicID) {
|
||||
if (_objectDynamics.contains(dynamicID)) {
|
||||
ObjectDynamicPointer dynamic = std::static_pointer_cast<ObjectDynamic>(_objectDynamics[dynamicID]);
|
||||
if (!dynamic) {
|
||||
return;
|
||||
}
|
||||
removeAction(action->getID());
|
||||
}
|
||||
|
||||
_objectActions[actionID] = action;
|
||||
|
||||
// bullet needs a pointer to the action, but it doesn't use shared pointers.
|
||||
// is there a way to bump the reference count?
|
||||
ObjectAction* objectAction = static_cast<ObjectAction*>(action.get());
|
||||
_dynamicsWorld->addAction(objectAction);
|
||||
}
|
||||
|
||||
void PhysicsEngine::removeAction(const QUuid actionID) {
|
||||
if (_objectActions.contains(actionID)) {
|
||||
EntityActionPointer action = _objectActions[actionID];
|
||||
ObjectAction* objectAction = static_cast<ObjectAction*>(action.get());
|
||||
_dynamicsWorld->removeAction(objectAction);
|
||||
_objectActions.remove(actionID);
|
||||
QList<btRigidBody*> rigidBodies = dynamic->getRigidBodies();
|
||||
if (dynamic->isAction()) {
|
||||
ObjectAction* objectAction = static_cast<ObjectAction*>(dynamic.get());
|
||||
_dynamicsWorld->removeAction(objectAction);
|
||||
} else {
|
||||
ObjectConstraint* objectConstraint = static_cast<ObjectConstraint*>(dynamic.get());
|
||||
btTypedConstraint* constraint = objectConstraint->getConstraint();
|
||||
if (constraint) {
|
||||
_dynamicsWorld->removeConstraint(constraint);
|
||||
} else {
|
||||
qCDebug(physics) << "PhysicsEngine::removeDynamic of constraint failed";
|
||||
}
|
||||
}
|
||||
_objectDynamics.remove(dynamicID);
|
||||
foreach(btRigidBody* rigidBody, rigidBodies) {
|
||||
_objectDynamicsByBody[rigidBody].remove(dynamic->getID());
|
||||
}
|
||||
dynamic->invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsEngine::forEachAction(std::function<void(EntityActionPointer)> actor) {
|
||||
QHashIterator<QUuid, EntityActionPointer> iter(_objectActions);
|
||||
void PhysicsEngine::forEachDynamic(std::function<void(EntityDynamicPointer)> actor) {
|
||||
QMutableHashIterator<QUuid, EntityDynamicPointer> iter(_objectDynamics);
|
||||
while (iter.hasNext()) {
|
||||
iter.next();
|
||||
actor(iter.value());
|
||||
if (iter.value()) {
|
||||
actor(iter.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "ObjectMotionState.h"
|
||||
#include "ThreadSafeDynamicsWorld.h"
|
||||
#include "ObjectAction.h"
|
||||
#include "ObjectConstraint.h"
|
||||
|
||||
const float HALF_SIMULATION_EXTENT = 512.0f; // meters
|
||||
|
||||
|
@ -84,12 +85,13 @@ public:
|
|||
|
||||
void dumpNextStats() { _dumpNextStats = true; }
|
||||
|
||||
EntityActionPointer getActionByID(const QUuid& actionID) const;
|
||||
void addAction(EntityActionPointer action);
|
||||
void removeAction(const QUuid actionID);
|
||||
void forEachAction(std::function<void(EntityActionPointer)> actor);
|
||||
EntityDynamicPointer getDynamicByID(const QUuid& dynamicID) const;
|
||||
bool addDynamic(EntityDynamicPointer dynamic);
|
||||
void removeDynamic(const QUuid dynamicID);
|
||||
void forEachDynamic(std::function<void(EntityDynamicPointer)> actor);
|
||||
|
||||
private:
|
||||
QList<EntityDynamicPointer> removeDynamicsForBody(btRigidBody* body);
|
||||
void addObjectToDynamicsWorld(ObjectMotionState* motionState);
|
||||
void recursivelyHarvestPerformanceStats(CProfileIterator* profileIterator, QString contextName);
|
||||
|
||||
|
@ -110,7 +112,8 @@ private:
|
|||
|
||||
ContactMap _contactMap;
|
||||
CollisionEvents _collisionEvents;
|
||||
QHash<QUuid, EntityActionPointer> _objectActions;
|
||||
QHash<QUuid, EntityDynamicPointer> _objectDynamics;
|
||||
QHash<btRigidBody*, QSet<QUuid>> _objectDynamicsByBody;
|
||||
std::vector<btRigidBody*> _activeStaticBodies;
|
||||
|
||||
glm::vec3 _originOffset;
|
||||
|
@ -122,6 +125,7 @@ private:
|
|||
|
||||
bool _dumpNextStats = false;
|
||||
bool _hasOutgoingChanges = false;
|
||||
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<PhysicsEngine> PhysicsEnginePointer;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
set(TARGET_NAME procedural)
|
||||
AUTOSCRIBE_SHADER_LIB(gpu model)
|
||||
setup_hifi_library()
|
||||
link_hifi_libraries(shared gpu gpu-gl networking model model-networking)
|
||||
link_hifi_libraries(shared gpu gpu-gl networking model model-networking image)
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ AUTOSCRIBE_SHADER_LIB(gpu model render)
|
|||
# pull in the resources.qrc file
|
||||
qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc")
|
||||
setup_hifi_library(Widgets OpenGL Network Qml Quick Script)
|
||||
link_hifi_libraries(shared ktx gpu model model-networking render animation fbx entities)
|
||||
link_hifi_libraries(shared ktx gpu model model-networking render animation fbx entities image)
|
||||
|
||||
if (NOT ANDROID)
|
||||
target_nsight()
|
||||
|
|
|
@ -74,11 +74,11 @@ void AmbientOcclusionFramebuffer::allocate() {
|
|||
auto width = _frameSize.x;
|
||||
auto height = _frameSize.y;
|
||||
|
||||
_occlusionTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
||||
_occlusionTexture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT));
|
||||
_occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion"));
|
||||
_occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture);
|
||||
|
||||
_occlusionBlurredTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
||||
_occlusionBlurredTexture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT));
|
||||
_occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusionBlurred"));
|
||||
_occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture);
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() {
|
|||
_antialiasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("antialiasing"));
|
||||
auto format = gpu::Element::COLOR_SRGBA_32; // DependencyManager::get<FramebufferCache>()->getLightingTexture()->getTexelFormat();
|
||||
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
|
||||
_antialiasingTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, defaultSampler));
|
||||
_antialiasingTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
_antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture);
|
||||
}
|
||||
|
||||
|
|
|
@ -53,9 +53,9 @@ void DeferredFramebuffer::allocate() {
|
|||
|
||||
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
|
||||
|
||||
_deferredColorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, width, height, gpu::Texture::SINGLE_MIP, defaultSampler));
|
||||
_deferredNormalTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(linearFormat, width, height, gpu::Texture::SINGLE_MIP, defaultSampler));
|
||||
_deferredSpecularTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(linearFormat, width, height, gpu::Texture::SINGLE_MIP, defaultSampler));
|
||||
_deferredColorTexture = gpu::Texture::createRenderBuffer(colorFormat, width, height, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
_deferredNormalTexture = gpu::Texture::createRenderBuffer(linearFormat, width, height, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
_deferredSpecularTexture = gpu::Texture::createRenderBuffer(linearFormat, width, height, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
|
||||
_deferredFramebuffer->setRenderBuffer(0, _deferredColorTexture);
|
||||
_deferredFramebuffer->setRenderBuffer(1, _deferredNormalTexture);
|
||||
|
@ -65,7 +65,7 @@ void DeferredFramebuffer::allocate() {
|
|||
|
||||
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format
|
||||
if (!_primaryDepthTexture) {
|
||||
_primaryDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, width, height, gpu::Texture::SINGLE_MIP, defaultSampler));
|
||||
_primaryDepthTexture = gpu::Texture::createRenderBuffer(depthFormat, width, height, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
}
|
||||
|
||||
_deferredFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
|
||||
|
@ -75,7 +75,7 @@ void DeferredFramebuffer::allocate() {
|
|||
|
||||
auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR);
|
||||
|
||||
_lightingTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, gpu::Texture::SINGLE_MIP, defaultSampler));
|
||||
_lightingTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
_lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("lighting"));
|
||||
_lightingFramebuffer->setRenderBuffer(0, _lightingTexture);
|
||||
_lightingFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
|
||||
|
|
|
@ -496,14 +496,14 @@ void PreparePrimaryFramebuffer::run(const RenderContextPointer& renderContext, g
|
|||
auto colorFormat = gpu::Element::COLOR_SRGBA_32;
|
||||
|
||||
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
|
||||
auto primaryColorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler));
|
||||
auto primaryColorTexture = gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
|
||||
|
||||
_primaryFramebuffer->setRenderBuffer(0, primaryColorTexture);
|
||||
|
||||
|
||||
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format
|
||||
auto primaryDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler));
|
||||
auto primaryDepthTexture = gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
|
||||
_primaryFramebuffer->setDepthStencilBuffer(primaryDepthTexture, depthFormat);
|
||||
}
|
||||
|
|
|
@ -199,7 +199,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
|||
{
|
||||
// Grab a texture map representing the different status icons and assign that to the drawStatsuJob
|
||||
auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg";
|
||||
auto statusIconMap = DependencyManager::get<TextureCache>()->getImageTexture(iconMapPath, NetworkTexture::STRICT_TEXTURE);
|
||||
auto statusIconMap = DependencyManager::get<TextureCache>()->getImageTexture(iconMapPath, image::TextureUsage::STRICT_TEXTURE);
|
||||
task.addJob<DrawStatus>("DrawStatus", opaques, DrawStatus(statusIconMap));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,11 +75,11 @@ void PrepareFramebuffer::run(const RenderContextPointer& renderContext,
|
|||
|
||||
auto colorFormat = gpu::Element::COLOR_SRGBA_32;
|
||||
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
|
||||
auto colorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler));
|
||||
auto colorTexture = gpu::Texture::create2D(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
_framebuffer->setRenderBuffer(0, colorTexture);
|
||||
|
||||
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format
|
||||
auto depthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler));
|
||||
auto depthTexture = gpu::Texture::create2D(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
_framebuffer->setDepthStencilBuffer(depthTexture, depthFormat);
|
||||
}
|
||||
|
||||
|
|
|
@ -414,7 +414,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generateScatteringProfile(Rend
|
|||
const int PROFILE_RESOLUTION = 512;
|
||||
// const auto pixelFormat = gpu::Element::COLOR_SRGBA_32;
|
||||
const auto pixelFormat = gpu::Element::COLOR_R11G11B10;
|
||||
auto profileMap = gpu::TexturePointer(gpu::Texture::createRenderBuffer(pixelFormat, PROFILE_RESOLUTION, 1, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)));
|
||||
auto profileMap = gpu::Texture::createRenderBuffer(pixelFormat, PROFILE_RESOLUTION, 1, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
|
||||
profileMap->setSource("Generated Scattering Profile");
|
||||
diffuseProfileGPU(profileMap, args);
|
||||
return profileMap;
|
||||
|
@ -425,7 +425,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generatePreIntegratedScatterin
|
|||
const int TABLE_RESOLUTION = 512;
|
||||
// const auto pixelFormat = gpu::Element::COLOR_SRGBA_32;
|
||||
const auto pixelFormat = gpu::Element::COLOR_R11G11B10;
|
||||
auto scatteringLUT = gpu::TexturePointer(gpu::Texture::createRenderBuffer(pixelFormat, TABLE_RESOLUTION, TABLE_RESOLUTION, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)));
|
||||
auto scatteringLUT = gpu::Texture::createRenderBuffer(pixelFormat, TABLE_RESOLUTION, TABLE_RESOLUTION, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
|
||||
//diffuseScatter(scatteringLUT);
|
||||
scatteringLUT->setSource("Generated pre-integrated scattering");
|
||||
diffuseScatterGPU(profile, scatteringLUT, args);
|
||||
|
@ -434,7 +434,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generatePreIntegratedScatterin
|
|||
|
||||
gpu::TexturePointer SubsurfaceScatteringResource::generateScatteringSpecularBeckmann(RenderArgs* args) {
|
||||
const int SPECULAR_RESOLUTION = 256;
|
||||
auto beckmannMap = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, SPECULAR_RESOLUTION, SPECULAR_RESOLUTION, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)));
|
||||
auto beckmannMap = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, SPECULAR_RESOLUTION, SPECULAR_RESOLUTION, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
|
||||
beckmannMap->setSource("Generated beckmannMap");
|
||||
computeSpecularBeckmannGPU(beckmannMap, args);
|
||||
return beckmannMap;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue