mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-07 20:22:27 +02:00
Merge branch 'master' into CreateAppDimensionsCopyPaste
This commit is contained in:
commit
cc8133bf08
36 changed files with 1095 additions and 986 deletions
|
@ -184,8 +184,13 @@ if(OVERTE_WARNINGS_WHITELIST)
|
|||
endif()
|
||||
|
||||
if(OVERTE_WARNINGS_AS_ERRORS)
|
||||
set(ENV{CXXFLAGS} "$ENV{CXXFLAGS} -Werror")
|
||||
set(ENV{CFLAGS} "$ENV{CXXFLAGS} -Werror")
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC" OR (CMAKE_CXX_COMPILER_ID MATCHES "" AND WIN32))
|
||||
set(ENV{CXXFLAGS} "$ENV{CXXFLAGS} /WX")
|
||||
set(ENV{CFLAGS} "$ENV{CXXFLAGS} /WX")
|
||||
else()
|
||||
set(ENV{CXXFLAGS} "$ENV{CXXFLAGS} -Werror")
|
||||
set(ENV{CFLAGS} "$ENV{CXXFLAGS} -Werror")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
|
@ -252,9 +257,6 @@ else()
|
|||
endif()
|
||||
|
||||
set(SCREENSHARE 0)
|
||||
if (WIN32)
|
||||
set(SCREENSHARE 1)
|
||||
endif()
|
||||
if (APPLE AND NOT CLIENT_ONLY)
|
||||
# Don't include Screenshare in OSX client-only builds.
|
||||
set(SCREENSHARE 1)
|
||||
|
|
|
@ -32,6 +32,10 @@
|
|||
|
||||
ScriptableAvatar::ScriptableAvatar(): _scriptEngine(newScriptEngine()) {
|
||||
_clientTraitsHandler.reset(new ClientTraitsHandler(this));
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [] {
|
||||
qRegisterMetaType<HFMModel::Pointer>("HFMModel::Pointer");
|
||||
});
|
||||
}
|
||||
|
||||
QByteArray ScriptableAvatar::toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking) {
|
||||
|
@ -52,6 +56,7 @@ void ScriptableAvatar::startAnimation(const QString& url, float fps, float prior
|
|||
_animation = DependencyManager::get<AnimationCache>()->getAnimation(url);
|
||||
_animationDetails = AnimationDetails("", QUrl(url), fps, 0, loop, hold, false, firstFrame, lastFrame, true, firstFrame, false);
|
||||
_maskedJoints = maskedJoints;
|
||||
_isAnimationRigValid = false;
|
||||
}
|
||||
|
||||
void ScriptableAvatar::stopAnimation() {
|
||||
|
@ -89,11 +94,12 @@ QStringList ScriptableAvatar::getJointNames() const {
|
|||
}
|
||||
|
||||
void ScriptableAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
||||
_bind.reset();
|
||||
_animSkeleton.reset();
|
||||
_avatarAnimSkeleton.reset();
|
||||
_geometryResource.reset();
|
||||
|
||||
AvatarData::setSkeletonModelURL(skeletonModelURL);
|
||||
updateJointMappings();
|
||||
_isRigValid = false;
|
||||
}
|
||||
|
||||
int ScriptableAvatar::sendAvatarDataPacket(bool sendAll) {
|
||||
|
@ -137,65 +143,87 @@ static AnimPose composeAnimPose(const HFMJoint& joint, const glm::quat rotation,
|
|||
}
|
||||
|
||||
void ScriptableAvatar::update(float deltatime) {
|
||||
if (!_geometryResource && !_skeletonModelFilenameURL.isEmpty()) { // AvatarData will parse the .fst, but not get the .fbx skeleton.
|
||||
_geometryResource = DependencyManager::get<ModelCache>()->getGeometryResource(_skeletonModelFilenameURL);
|
||||
}
|
||||
|
||||
// Run animation
|
||||
if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && !_bind.isNull() && _bind->isLoaded()) {
|
||||
if (!_animSkeleton) {
|
||||
_animSkeleton = std::make_shared<AnimSkeleton>(_bind->getHFMModel());
|
||||
}
|
||||
float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps;
|
||||
if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) {
|
||||
while (currentFrame >= _animationDetails.lastFrame) {
|
||||
currentFrame -= (_animationDetails.lastFrame - _animationDetails.firstFrame);
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
if (_animation && _animation->isLoaded()) {
|
||||
Q_ASSERT(thread() == _animation->thread());
|
||||
auto frames = _animation->getFramesReference();
|
||||
if (frames.size() > 0 && _geometryResource && _geometryResource->isHFMModelLoaded()) {
|
||||
if (!_isRigValid) {
|
||||
_rig.reset(_geometryResource->getHFMModel());
|
||||
_isRigValid = true;
|
||||
}
|
||||
_animationDetails.currentFrame = currentFrame;
|
||||
|
||||
const QVector<HFMJoint>& modelJoints = _bind->getHFMModel().joints;
|
||||
QStringList animationJointNames = _animation->getJointNames();
|
||||
|
||||
const int nJoints = modelJoints.size();
|
||||
if (_jointData.size() != nJoints) {
|
||||
_jointData.resize(nJoints);
|
||||
if (!_isAnimationRigValid) {
|
||||
_animationRig.reset(_animation->getHFMModel());
|
||||
_isAnimationRigValid = true;
|
||||
}
|
||||
|
||||
const int frameCount = _animation->getFrames().size();
|
||||
const HFMAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(currentFrame) % frameCount);
|
||||
const HFMAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount);
|
||||
const float frameFraction = glm::fract(currentFrame);
|
||||
std::vector<AnimPose> poses = _animSkeleton->getRelativeDefaultPoses();
|
||||
|
||||
const float UNIT_SCALE = 0.01f;
|
||||
|
||||
for (int i = 0; i < animationJointNames.size(); i++) {
|
||||
const QString& name = animationJointNames[i];
|
||||
// As long as we need the model preRotations anyway, let's get the jointIndex from the bind skeleton rather than
|
||||
// trusting the .fst (which is sometimes not updated to match changes to .fbx).
|
||||
int mapping = _bind->getHFMModel().getJointIndex(name);
|
||||
if (mapping != -1 && !_maskedJoints.contains(name)) {
|
||||
|
||||
AnimPose floorPose = composeAnimPose(modelJoints[mapping], floorFrame.rotations[i], floorFrame.translations[i] * UNIT_SCALE);
|
||||
AnimPose ceilPose = composeAnimPose(modelJoints[mapping], ceilFrame.rotations[i], floorFrame.translations[i] * UNIT_SCALE);
|
||||
blend(1, &floorPose, &ceilPose, frameFraction, &poses[mapping]);
|
||||
}
|
||||
if (!_avatarAnimSkeleton) {
|
||||
_avatarAnimSkeleton = std::make_shared<AnimSkeleton>(_geometryResource->getHFMModel());
|
||||
}
|
||||
|
||||
std::vector<AnimPose> absPoses = poses;
|
||||
_animSkeleton->convertRelativePosesToAbsolute(absPoses);
|
||||
for (int i = 0; i < nJoints; i++) {
|
||||
JointData& data = _jointData[i];
|
||||
AnimPose& absPose = absPoses[i];
|
||||
if (data.rotation != absPose.rot()) {
|
||||
data.rotation = absPose.rot();
|
||||
data.rotationIsDefaultPose = false;
|
||||
float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps;
|
||||
if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) {
|
||||
while (currentFrame >= _animationDetails.lastFrame) {
|
||||
currentFrame -= (_animationDetails.lastFrame - _animationDetails.firstFrame);
|
||||
}
|
||||
AnimPose& relPose = poses[i];
|
||||
if (data.translation != relPose.trans()) {
|
||||
data.translation = relPose.trans();
|
||||
data.translationIsDefaultPose = false;
|
||||
}
|
||||
}
|
||||
_animationDetails.currentFrame = currentFrame;
|
||||
|
||||
} else {
|
||||
_animation.clear();
|
||||
const QVector<HFMJoint>& modelJoints = _geometryResource->getHFMModel().joints;
|
||||
QStringList animationJointNames = _animation->getJointNames();
|
||||
|
||||
const int nJoints = modelJoints.size();
|
||||
if (_jointData.size() != nJoints) {
|
||||
_jointData.resize(nJoints);
|
||||
}
|
||||
|
||||
const int frameCount = frames.size();
|
||||
const HFMAnimationFrame& floorFrame = frames.at((int)glm::floor(currentFrame) % frameCount);
|
||||
const HFMAnimationFrame& ceilFrame = frames.at((int)glm::ceil(currentFrame) % frameCount);
|
||||
const float frameFraction = glm::fract(currentFrame);
|
||||
std::vector<AnimPose> poses = _avatarAnimSkeleton->getRelativeDefaultPoses();
|
||||
|
||||
// TODO: this needs more testing, it's possible that we need not only scale but also rotation and translation
|
||||
// According to tests with unmatching avatar and animation armatures, sometimes bones are not rotated correctly.
|
||||
// Matching armatures already work very well now.
|
||||
const float UNIT_SCALE = _animationRig.GetScaleFactorGeometryToUnscaledRig() / _rig.GetScaleFactorGeometryToUnscaledRig();
|
||||
|
||||
for (int i = 0; i < animationJointNames.size(); i++) {
|
||||
const QString& name = animationJointNames[i];
|
||||
// As long as we need the model preRotations anyway, let's get the jointIndex from the bind skeleton rather than
|
||||
// trusting the .fst (which is sometimes not updated to match changes to .fbx).
|
||||
int mapping = _geometryResource->getHFMModel().getJointIndex(name);
|
||||
if (mapping != -1 && !_maskedJoints.contains(name)) {
|
||||
AnimPose floorPose = composeAnimPose(modelJoints[mapping], floorFrame.rotations[i],
|
||||
floorFrame.translations[i] * UNIT_SCALE);
|
||||
AnimPose ceilPose = composeAnimPose(modelJoints[mapping], ceilFrame.rotations[i],
|
||||
ceilFrame.translations[i] * UNIT_SCALE);
|
||||
blend(1, &floorPose, &ceilPose, frameFraction, &poses[mapping]);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<AnimPose> absPoses = poses;
|
||||
Q_ASSERT(_avatarAnimSkeleton != nullptr);
|
||||
_avatarAnimSkeleton->convertRelativePosesToAbsolute(absPoses);
|
||||
for (int i = 0; i < nJoints; i++) {
|
||||
JointData& data = _jointData[i];
|
||||
AnimPose& absPose = absPoses[i];
|
||||
if (data.rotation != absPose.rot()) {
|
||||
data.rotation = absPose.rot();
|
||||
data.rotationIsDefaultPose = false;
|
||||
}
|
||||
AnimPose& relPose = poses[i];
|
||||
if (data.translation != relPose.trans()) {
|
||||
data.translation = relPose.trans();
|
||||
data.translationIsDefaultPose = false;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
_animation.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -245,6 +273,7 @@ void ScriptableAvatar::setJointMappingsFromNetworkReply() {
|
|||
networkReply->deleteLater();
|
||||
return;
|
||||
}
|
||||
// TODO: this works only with .fst files currently, not directly with FBX and GLB models
|
||||
{
|
||||
QWriteLocker writeLock(&_jointDataLock);
|
||||
QByteArray line;
|
||||
|
@ -253,7 +282,7 @@ void ScriptableAvatar::setJointMappingsFromNetworkReply() {
|
|||
if (line.startsWith("filename")) {
|
||||
int filenameIndex = line.indexOf('=') + 1;
|
||||
if (filenameIndex > 0) {
|
||||
_skeletonFBXURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed()));
|
||||
_skeletonModelFilenameURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed()));
|
||||
}
|
||||
}
|
||||
if (!line.startsWith("jointIndex")) {
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include <AvatarData.h>
|
||||
#include <ScriptEngine.h>
|
||||
#include <EntityItem.h>
|
||||
#include "model-networking/ModelCache.h"
|
||||
#include "Rig.h"
|
||||
|
||||
/*@jsdoc
|
||||
* The <code>Avatar</code> API is used to manipulate scriptable avatars on the domain. This API is a subset of the
|
||||
|
@ -217,11 +219,15 @@ private:
|
|||
AnimationPointer _animation;
|
||||
AnimationDetails _animationDetails;
|
||||
QStringList _maskedJoints;
|
||||
AnimationPointer _bind; // a sleazy way to get the skeleton, given the various library/cmake dependencies
|
||||
std::shared_ptr<AnimSkeleton> _animSkeleton;
|
||||
GeometryResource::Pointer _geometryResource;
|
||||
Rig _rig;
|
||||
bool _isRigValid{false};
|
||||
Rig _animationRig;
|
||||
bool _isAnimationRigValid{false};
|
||||
std::shared_ptr<AnimSkeleton> _avatarAnimSkeleton;
|
||||
QHash<QString, int> _fstJointIndices; ///< 1-based, since zero is returned for missing keys
|
||||
QStringList _fstJointNames; ///< in order of depth-first traversal
|
||||
QUrl _skeletonFBXURL;
|
||||
QUrl _skeletonModelFilenameURL; // This contains URL from filename field in fst file
|
||||
mutable ScriptEnginePointer _scriptEngine;
|
||||
std::map<QUuid, EntityItemPointer> _entities;
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ Item {
|
|||
width: parent.width
|
||||
|
||||
property string title: "Controls"
|
||||
property var openVRDevices: ["HTC Vive", "Valve Index", "Valve HMD", "Valve", "WindowsMR"]
|
||||
property var openVRDevices: ["HTC Vive", "Valve Index", "Valve HMD", "Valve", "WindowsMR", "Oculus"]
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
|
|
|
@ -281,7 +281,7 @@ if (APPLE)
|
|||
|
||||
set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME})
|
||||
|
||||
set(DMG_SUBFOLDER_NAME "Vircadia")
|
||||
set(DMG_SUBFOLDER_NAME "Overte")
|
||||
set(ESCAPED_DMG_SUBFOLDER_NAME "")
|
||||
set(DMG_SUBFOLDER_ICON "${CMAKE_SOURCE_DIR}/cmake/installer/install-folder.rsrc")
|
||||
|
||||
|
|
|
@ -337,21 +337,26 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) {
|
|||
}
|
||||
|
||||
// Draw!
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(batch);
|
||||
const uint32_t compactColor = 0xFFFFFFFF;
|
||||
_colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor);
|
||||
DependencyManager::get<GeometryCache>()->renderShape(batch, GeometryCache::Shape::Sphere, _colorBuffer);
|
||||
} else {
|
||||
auto proceduralDrawMaterial = std::static_pointer_cast<graphics::ProceduralMaterial>(drawMaterial);
|
||||
glm::vec4 outColor = glm::vec4(drawMaterial->getAlbedo(), drawMaterial->getOpacity());
|
||||
outColor = proceduralDrawMaterial->getColor(outColor);
|
||||
proceduralDrawMaterial->prepare(batch, transform.getTranslation(), transform.getScale(),
|
||||
transform.getRotation(), _created, ProceduralProgramKey(outColor.a < 1.0f));
|
||||
|
||||
const uint32_t compactColor = GeometryCache::toCompactColor(glm::vec4(outColor));
|
||||
_colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor);
|
||||
if (render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES) {
|
||||
DependencyManager::get<GeometryCache>()->renderWireSphere(batch, outColor);
|
||||
DependencyManager::get<GeometryCache>()->renderWireShape(batch, GeometryCache::Shape::Sphere, _colorBuffer);
|
||||
} else {
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(batch, outColor);
|
||||
DependencyManager::get<GeometryCache>()->renderShape(batch, GeometryCache::Shape::Sphere, _colorBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
args->_details._trianglesRendered += (int)DependencyManager::get<GeometryCache>()->getSphereTriangleCount();
|
||||
args->_details._trianglesRendered += (int)DependencyManager::get<GeometryCache>()->getShapeTriangleCount(GeometryCache::Shape::Sphere);
|
||||
}
|
||||
|
||||
void MaterialEntityRenderer::setCurrentMaterialName(const std::string& currentMaterialName) {
|
||||
|
|
|
@ -67,6 +67,7 @@ private:
|
|||
std::shared_ptr<NetworkMaterial> _appliedMaterial;
|
||||
std::string _currentMaterialName;
|
||||
|
||||
gpu::BufferPointer _colorBuffer { std::make_shared<gpu::Buffer>() };
|
||||
};
|
||||
|
||||
} }
|
||||
|
|
|
@ -131,10 +131,12 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
|||
procedural->prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(outColor.a < 1.0f));
|
||||
});
|
||||
|
||||
const uint32_t compactColor = GeometryCache::toCompactColor(glm::vec4(outColor));
|
||||
_colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor);
|
||||
if (wireframe) {
|
||||
geometryCache->renderWireShape(batch, geometryShape, outColor);
|
||||
geometryCache->renderWireShape(batch, geometryShape, _colorBuffer);
|
||||
} else {
|
||||
geometryCache->renderShape(batch, geometryShape, outColor);
|
||||
geometryCache->renderShape(batch, geometryShape, _colorBuffer);
|
||||
}
|
||||
} else if (pipelineType == Pipeline::SIMPLE) {
|
||||
// FIXME, support instanced multi-shape rendering using multidraw indirect
|
||||
|
@ -149,10 +151,12 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
|||
geometryCache->renderSolidShapeInstance(args, batch, geometryShape, outColor, pipeline);
|
||||
}
|
||||
} else {
|
||||
const uint32_t compactColor = GeometryCache::toCompactColor(glm::vec4(outColor));
|
||||
_colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor);
|
||||
if (wireframe) {
|
||||
geometryCache->renderWireShape(batch, geometryShape, outColor);
|
||||
geometryCache->renderWireShape(batch, geometryShape, _colorBuffer);
|
||||
} else {
|
||||
geometryCache->renderShape(batch, geometryShape, outColor);
|
||||
geometryCache->renderShape(batch, geometryShape, _colorBuffer);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -160,7 +164,9 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
|||
args->_details._materialSwitches++;
|
||||
}
|
||||
|
||||
geometryCache->renderShape(batch, geometryShape);
|
||||
const uint32_t compactColor = GeometryCache::toCompactColor(glm::vec4(outColor));
|
||||
_colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor);
|
||||
geometryCache->renderShape(batch, geometryShape, _colorBuffer);
|
||||
}
|
||||
|
||||
const auto triCount = geometryCache->getShapeTriangleCount(geometryShape);
|
||||
|
|
|
@ -42,6 +42,8 @@ private:
|
|||
std::shared_ptr<graphics::ProceduralMaterial> _material { std::make_shared<graphics::ProceduralMaterial>() };
|
||||
glm::vec3 _color { NAN };
|
||||
float _alpha { NAN };
|
||||
|
||||
gpu::BufferPointer _colorBuffer { std::make_shared<gpu::Buffer>() };
|
||||
};
|
||||
|
||||
} }
|
||||
|
|
|
@ -100,8 +100,6 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
|
|||
(&::gpu::gl::GLBackend::do_glUniformMatrix3fv),
|
||||
(&::gpu::gl::GLBackend::do_glUniformMatrix4fv),
|
||||
|
||||
(&::gpu::gl::GLBackend::do_glColor4f),
|
||||
|
||||
(&::gpu::gl::GLBackend::do_pushProfileRange),
|
||||
(&::gpu::gl::GLBackend::do_popProfileRange),
|
||||
};
|
||||
|
@ -838,22 +836,6 @@ void GLBackend::do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) {
|
|||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
void GLBackend::do_glColor4f(const Batch& batch, size_t paramOffset) {
|
||||
|
||||
glm::vec4 newColor(
|
||||
batch._params[paramOffset + 3]._float,
|
||||
batch._params[paramOffset + 2]._float,
|
||||
batch._params[paramOffset + 1]._float,
|
||||
batch._params[paramOffset + 0]._float);
|
||||
|
||||
if (_input._colorAttribute != newColor) {
|
||||
_input._colorAttribute = newColor;
|
||||
glVertexAttrib4fv(gpu::Stream::COLOR, &_input._colorAttribute.r);
|
||||
_input._hasColorAttribute = true;
|
||||
}
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
void GLBackend::releaseBuffer(GLuint id, Size size) const {
|
||||
Lock lock(_trashMutex);
|
||||
_currentFrameTrash.buffersTrash.push_back({ id, size });
|
||||
|
|
|
@ -241,8 +241,6 @@ public:
|
|||
virtual void do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) final;
|
||||
virtual void do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) final;
|
||||
|
||||
virtual void do_glColor4f(const Batch& batch, size_t paramOffset) final;
|
||||
|
||||
// The State setters called by the GLState::Commands when a new state is assigned
|
||||
virtual void do_setStateFillMode(int32 mode) final;
|
||||
virtual void do_setStateCullMode(int32 mode) final;
|
||||
|
@ -350,8 +348,6 @@ protected:
|
|||
struct InputStageState {
|
||||
bool _invalidFormat { true };
|
||||
bool _lastUpdateStereoState { false };
|
||||
bool _hasColorAttribute { false };
|
||||
bool _hadColorAttribute { false };
|
||||
FormatReference _format { GPU_REFERENCE_INIT_VALUE };
|
||||
std::string _formatKey;
|
||||
|
||||
|
@ -368,8 +364,6 @@ protected:
|
|||
std::array<Offset, MAX_NUM_INPUT_BUFFERS> _bufferStrides;
|
||||
std::array<GLuint, MAX_NUM_INPUT_BUFFERS> _bufferVBOs;
|
||||
|
||||
glm::vec4 _colorAttribute { 1.0f };
|
||||
|
||||
BufferReference _indexBuffer;
|
||||
Offset _indexBufferOffset { 0 };
|
||||
Type _indexBufferType { UINT32 };
|
||||
|
|
|
@ -103,9 +103,6 @@ void GLBackend::resetInputStage() {
|
|||
reset(_input._format);
|
||||
_input._formatKey.clear();
|
||||
_input._invalidFormat = false;
|
||||
_input._hasColorAttribute = false;
|
||||
_input._hadColorAttribute = false;
|
||||
_input._colorAttribute = vec4(1.0f);
|
||||
_input._attributeActivation.reset();
|
||||
|
||||
for (uint32_t i = 0; i < _input._buffers.size(); i++) {
|
||||
|
@ -163,8 +160,6 @@ void GLBackend::updateInput() {
|
|||
#endif
|
||||
_input._lastUpdateStereoState = isStereoNow;
|
||||
|
||||
bool hasColorAttribute = _input._hasColorAttribute;
|
||||
|
||||
if (_input._invalidFormat) {
|
||||
InputStageState::ActivationCache newActivation;
|
||||
|
||||
|
@ -194,8 +189,6 @@ void GLBackend::updateInput() {
|
|||
|
||||
GLenum perLocationSize = attrib._element.getLocationSize();
|
||||
|
||||
hasColorAttribute |= slot == Stream::COLOR;
|
||||
|
||||
for (GLuint locNum = 0; locNum < locationCount; ++locNum) {
|
||||
GLuint attriNum = (GLuint)(slot + locNum);
|
||||
newActivation.set(attriNum);
|
||||
|
@ -226,12 +219,6 @@ void GLBackend::updateInput() {
|
|||
glVertexBindingDivisor(bufferChannelNum, frequency);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!hasColorAttribute && _input._hadColorAttribute) {
|
||||
// The previous input stage had a color attribute but this one doesn't, so reset the color to pure white.
|
||||
_input._colorAttribute = glm::vec4(1.0f);
|
||||
glVertexAttrib4fv(Stream::COLOR, &_input._colorAttribute.r);
|
||||
}
|
||||
}
|
||||
|
||||
// Manage Activation what was and what is expected now
|
||||
|
@ -253,9 +240,6 @@ void GLBackend::updateInput() {
|
|||
_stats._ISNumFormatChanges++;
|
||||
}
|
||||
|
||||
_input._hadColorAttribute = hasColorAttribute;
|
||||
_input._hasColorAttribute = false;
|
||||
|
||||
if (_input._invalidBuffers.any()) {
|
||||
auto vbo = _input._bufferVBOs.data();
|
||||
auto offset = _input._bufferOffsets.data();
|
||||
|
|
|
@ -33,8 +33,6 @@ void GL41Backend::updateInput() {
|
|||
#endif
|
||||
_input._lastUpdateStereoState = isStereoNow;
|
||||
|
||||
bool hasColorAttribute = _input._hasColorAttribute;
|
||||
|
||||
if (_input._invalidFormat || _input._invalidBuffers.any()) {
|
||||
|
||||
auto format = acquire(_input._format);
|
||||
|
@ -110,8 +108,6 @@ void GL41Backend::updateInput() {
|
|||
uintptr_t pointer = (uintptr_t)(attrib._offset + offsets[bufferNum]);
|
||||
GLboolean isNormalized = attrib._element.isNormalized();
|
||||
|
||||
hasColorAttribute |= slot == Stream::COLOR;
|
||||
|
||||
for (size_t locNum = 0; locNum < locationCount; ++locNum) {
|
||||
if (attrib._element.isInteger()) {
|
||||
glVertexAttribIPointer(slot + (GLuint)locNum, count, type, stride,
|
||||
|
@ -131,17 +127,8 @@ void GL41Backend::updateInput() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasColorAttribute && _input._hadColorAttribute) {
|
||||
// The previous input stage had a color attribute but this one doesn't, so reset the color to pure white.
|
||||
_input._colorAttribute = glm::vec4(1.0f);
|
||||
glVertexAttrib4fv(Stream::COLOR, &_input._colorAttribute.r);
|
||||
}
|
||||
}
|
||||
// everything format related should be in sync now
|
||||
_input._invalidFormat = false;
|
||||
}
|
||||
|
||||
_input._hadColorAttribute = hasColorAttribute;
|
||||
_input._hasColorAttribute = false;
|
||||
}
|
||||
|
|
|
@ -35,8 +35,6 @@ void GL45Backend::updateInput() {
|
|||
#endif
|
||||
_input._lastUpdateStereoState = isStereoNow;
|
||||
|
||||
bool hasColorAttribute = _input._hasColorAttribute;
|
||||
|
||||
if (_input._invalidFormat) {
|
||||
InputStageState::ActivationCache newActivation;
|
||||
|
||||
|
@ -66,8 +64,6 @@ void GL45Backend::updateInput() {
|
|||
|
||||
GLenum perLocationSize = attrib._element.getLocationSize();
|
||||
|
||||
hasColorAttribute |= slot == Stream::COLOR;
|
||||
|
||||
for (GLuint locNum = 0; locNum < locationCount; ++locNum) {
|
||||
GLuint attriNum = (GLuint)(slot + locNum);
|
||||
newActivation.set(attriNum);
|
||||
|
@ -98,12 +94,6 @@ void GL45Backend::updateInput() {
|
|||
glVertexBindingDivisor(bufferChannelNum, frequency);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!hasColorAttribute && _input._hadColorAttribute) {
|
||||
// The previous input stage had a color attribute but this one doesn't, so reset the color to pure white.
|
||||
_input._colorAttribute = glm::vec4(1.0f);
|
||||
glVertexAttrib4fv(Stream::COLOR, &_input._colorAttribute.r);
|
||||
}
|
||||
}
|
||||
|
||||
// Manage Activation what was and what is expected now
|
||||
|
@ -125,9 +115,6 @@ void GL45Backend::updateInput() {
|
|||
_stats._ISNumFormatChanges++;
|
||||
}
|
||||
|
||||
_input._hadColorAttribute = hasColorAttribute;
|
||||
_input._hasColorAttribute = false;
|
||||
|
||||
if (_input._invalidBuffers.any()) {
|
||||
auto vbo = _input._bufferVBOs.data();
|
||||
auto offset = _input._bufferOffsets.data();
|
||||
|
|
|
@ -687,15 +687,6 @@ void Batch::_glUniformMatrix4fv(int32 location, int count, uint8 transpose, cons
|
|||
_params.emplace_back(location);
|
||||
}
|
||||
|
||||
void Batch::_glColor4f(float red, float green, float blue, float alpha) {
|
||||
ADD_COMMAND(glColor4f);
|
||||
|
||||
_params.emplace_back(alpha);
|
||||
_params.emplace_back(blue);
|
||||
_params.emplace_back(green);
|
||||
_params.emplace_back(red);
|
||||
}
|
||||
|
||||
void Batch::finishFrame(BufferUpdates& updates) {
|
||||
PROFILE_RANGE(render_gpu, __FUNCTION__);
|
||||
|
||||
|
|
|
@ -287,8 +287,6 @@ public:
|
|||
_glUniformMatrix3fv(location, 1, false, glm::value_ptr(v));
|
||||
}
|
||||
|
||||
void _glColor4f(float red, float green, float blue, float alpha);
|
||||
|
||||
// Maybe useful but shoudln't be public. Please convince me otherwise
|
||||
// Well porting to gles i need it...
|
||||
void runLambda(std::function<void()> f);
|
||||
|
@ -363,8 +361,6 @@ public:
|
|||
COMMAND_glUniformMatrix3fv,
|
||||
COMMAND_glUniformMatrix4fv,
|
||||
|
||||
COMMAND_glColor4f,
|
||||
|
||||
COMMAND_pushProfileRange,
|
||||
COMMAND_popProfileRange,
|
||||
|
||||
|
|
|
@ -201,8 +201,6 @@ constexpr const char* COMMAND_NAMES[] = {
|
|||
"glUniformMatrix3fv",
|
||||
"glUniformMatrix4fv",
|
||||
|
||||
"glColor4f",
|
||||
|
||||
"pushProfileRange",
|
||||
"popProfileRange",
|
||||
};
|
||||
|
|
|
@ -19,6 +19,8 @@ Mesh::Mesh() :
|
|||
_vertexBuffer(gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)),
|
||||
_indexBuffer(gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::INDEX)),
|
||||
_partBuffer(gpu::Element(gpu::VEC4, gpu::UINT32, gpu::PART)) {
|
||||
const uint32_t compactColor = 0xFFFFFFFF;
|
||||
_colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor);
|
||||
}
|
||||
|
||||
Mesh::Mesh(const Mesh& mesh) :
|
||||
|
@ -26,7 +28,8 @@ Mesh::Mesh(const Mesh& mesh) :
|
|||
_vertexBuffer(mesh._vertexBuffer),
|
||||
_attributeBuffers(mesh._attributeBuffers),
|
||||
_indexBuffer(mesh._indexBuffer),
|
||||
_partBuffer(mesh._partBuffer) {
|
||||
_partBuffer(mesh._partBuffer),
|
||||
_colorBuffer(mesh._colorBuffer) {
|
||||
}
|
||||
|
||||
Mesh::~Mesh() {
|
||||
|
@ -39,6 +42,13 @@ void Mesh::setVertexFormatAndStream(const gpu::Stream::FormatPointer& vf, const
|
|||
auto attrib = _vertexFormat->getAttribute(gpu::Stream::POSITION);
|
||||
_vertexBuffer = BufferView(vbs->getBuffers()[attrib._channel], vbs->getOffsets()[attrib._channel], vbs->getBuffers()[attrib._channel]->getSize(),
|
||||
(gpu::uint16) vbs->getStrides()[attrib._channel], attrib._element);
|
||||
|
||||
// We require meshes to have a color attribute. If they don't, we default to white.
|
||||
if (!_vertexFormat->hasAttribute(gpu::Stream::COLOR)) {
|
||||
int channelNum = _vertexStream.getNumBuffers();
|
||||
_vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE);
|
||||
_vertexStream.addBuffer(_colorBuffer, 0, _vertexFormat->getChannels().at(channelNum)._stride);
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::setVertexBuffer(const BufferView& buffer) {
|
||||
|
@ -98,6 +108,12 @@ void Mesh::evalVertexStream() {
|
|||
_vertexStream.addBuffer(view._buffer, view._offset, stride);
|
||||
channelNum++;
|
||||
}
|
||||
|
||||
// We require meshes to have a color attribute. If they don't, we default to white.
|
||||
if (!_vertexFormat->hasAttribute(gpu::Stream::COLOR)) {
|
||||
_vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE);
|
||||
_vertexStream.addBuffer(_colorBuffer, 0, _vertexFormat->getChannels().at(channelNum)._stride);
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::setIndexBuffer(const BufferView& buffer) {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include <AABox.h>
|
||||
|
||||
#include <gpu/Forward.h>
|
||||
#include <gpu/Resource.h>
|
||||
#include <gpu/Stream.h>
|
||||
|
||||
|
@ -28,7 +29,6 @@ typedef glm::vec3 Vec3;
|
|||
class Mesh;
|
||||
using MeshPointer = std::shared_ptr< Mesh >;
|
||||
|
||||
|
||||
class Mesh {
|
||||
public:
|
||||
const static Index PRIMITIVE_RESTART_INDEX = -1;
|
||||
|
@ -142,6 +142,8 @@ public:
|
|||
std::string modelName;
|
||||
std::string displayName;
|
||||
|
||||
gpu::BufferPointer getColorBuffer() const { return _colorBuffer; }
|
||||
|
||||
protected:
|
||||
|
||||
gpu::Stream::FormatPointer _vertexFormat;
|
||||
|
@ -154,6 +156,8 @@ protected:
|
|||
|
||||
BufferView _partBuffer;
|
||||
|
||||
gpu::BufferPointer _colorBuffer { std::make_shared<gpu::Buffer>() };
|
||||
|
||||
void evalVertexFormat();
|
||||
void evalVertexStream();
|
||||
|
||||
|
|
|
@ -355,8 +355,12 @@ void GeometryResource::deleter() {
|
|||
|
||||
void GeometryResource::setTextures() {
|
||||
if (_hfmModel) {
|
||||
for (const HFMMaterial& material : _hfmModel->materials) {
|
||||
_materials.push_back(std::make_shared<NetworkMaterial>(material, _textureBaseURL));
|
||||
if (DependencyManager::get<TextureCache>()) {
|
||||
for (const HFMMaterial& material : _hfmModel->materials) {
|
||||
_materials.push_back(std::make_shared<NetworkMaterial>(material, _textureBaseURL));
|
||||
}
|
||||
} else {
|
||||
qDebug() << "GeometryResource::setTextures: TextureCache dependency not available, skipping textures";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include <qfile.h>
|
||||
#include <qfileinfo.h>
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include <shared/NsightHelpers.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <ResourceManager.h>
|
||||
|
@ -460,6 +462,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
|
|||
// Increment the triangle indices by the current mesh vertex count so each mesh part can all reference the same buffers within the mesh
|
||||
int prevMeshVerticesCount = mesh.vertices.count();
|
||||
|
||||
// For each vertex (stride is WEIGHTS_PER_VERTEX), it contains index of the cluster that given weight belongs to.
|
||||
QVector<uint16_t> clusterJoints;
|
||||
QVector<float> clusterWeights;
|
||||
|
||||
|
@ -940,18 +943,17 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
|
|||
} else {
|
||||
mesh.clusterWeights[prevMeshClusterWeightCount + j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF);
|
||||
}
|
||||
for (int clusterIndex = 0; clusterIndex < mesh.clusters.size() - 1; ++clusterIndex) {
|
||||
for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {
|
||||
int clusterIndex = mesh.clusterIndices[prevMeshClusterIndexCount + k];
|
||||
ShapeVertices& points = hfmModel.shapeVertices.at(clusterIndex);
|
||||
glm::vec3 globalMeshScale = extractScale(globalTransforms[nodeIndex]);
|
||||
const glm::mat4 meshToJoint = glm::scale(glm::mat4(), globalMeshScale) * jointInverseBindTransforms[clusterIndex];
|
||||
|
||||
// TODO: The entire clustering is probably broken and detailed collision shapes fail to generate due to it.
|
||||
const uint16_t EXPANSION_WEIGHT_THRESHOLD = UINT16_MAX/4; // Equivalent of 0.25f?
|
||||
if (mesh.clusterWeights[j] >= EXPANSION_WEIGHT_THRESHOLD) {
|
||||
// TODO: fix transformed vertices being pushed back
|
||||
auto& vertex = mesh.vertices[i];
|
||||
const glm::mat4 vertexTransform = meshToJoint * (glm::translate(glm::mat4(), vertex));
|
||||
glm::vec3 transformedVertex = hfmModel.joints[clusterIndex].translation * (extractTranslation(vertexTransform));
|
||||
if (mesh.clusterWeights[prevMeshClusterWeightCount + k] >= EXPANSION_WEIGHT_THRESHOLD) {
|
||||
auto& vertex = mesh.vertices[prevMeshVerticesCount + i];
|
||||
const glm::mat4 vertexTransform = meshToJoint * glm::translate(vertex);
|
||||
glm::vec3 transformedVertex = extractTranslation(vertexTransform);
|
||||
points.push_back(transformedVertex);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ static const int HASH_ITERATIONS = 65535;
|
|||
|
||||
// Salt string for the hardware ID, makes our IDs unique to our project.
|
||||
// Changing this results in different hardware IDs being computed.
|
||||
static const QByteArray HASH_SALT{"Vircadia"};
|
||||
static const QByteArray HASH_SALT{"Overte"};
|
||||
|
||||
static const QString FALLBACK_FINGERPRINT_KEY = "fallbackFingerprint";
|
||||
|
||||
|
|
|
@ -587,7 +587,7 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const
|
|||
}
|
||||
|
||||
// it's ok to change offset immediately -- there are no thread safety issues here
|
||||
_shapeLocalOffset = minCorner + 0.5f * scale;
|
||||
_shapeLocalOffset = glm::vec3((minCorner + 0.5f * scale).x, (minCorner + 0.5f * scale).y, -(minCorner + 0.5f * scale).z);
|
||||
|
||||
if (_rigidBody) {
|
||||
// update CCD with new _radius
|
||||
|
|
|
@ -865,7 +865,13 @@ graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl
|
|||
}
|
||||
|
||||
const auto url = getTextureUrl(baseUrl, hfmTexture);
|
||||
const auto texture = DependencyManager::get<TextureCache>()->getTexture(url, type, hfmTexture.content, hfmTexture.maxNumPixels, hfmTexture.sourceChannel);
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
NetworkTexturePointer texture;
|
||||
if (textureCache) {
|
||||
texture = textureCache->getTexture(url, type, hfmTexture.content, hfmTexture.maxNumPixels, hfmTexture.sourceChannel);
|
||||
} else {
|
||||
qDebug() << "GeometryResource::setTextures: TextureCache dependency not available, skipping textures";
|
||||
}
|
||||
_textures[channel] = Texture { hfmTexture.name, texture };
|
||||
|
||||
auto map = std::make_shared<graphics::TextureMap>();
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -180,31 +180,10 @@ public:
|
|||
void renderShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer);
|
||||
void renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer);
|
||||
|
||||
void renderFadeShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer,
|
||||
gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3);
|
||||
void renderWireFadeShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer,
|
||||
gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3);
|
||||
|
||||
void renderSolidShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color,
|
||||
const render::ShapePipelinePointer& pipeline);
|
||||
void renderSolidShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec3& color,
|
||||
const render::ShapePipelinePointer& pipeline) {
|
||||
renderSolidShapeInstance(args, batch, shape, glm::vec4(color, 1.0f), pipeline);
|
||||
}
|
||||
|
||||
const render::ShapePipelinePointer& pipeline);
|
||||
void renderWireShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color,
|
||||
const render::ShapePipelinePointer& pipeline);
|
||||
void renderWireShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec3& color,
|
||||
const render::ShapePipelinePointer& pipeline) {
|
||||
renderWireShapeInstance(args, batch, shape, glm::vec4(color, 1.0f), pipeline);
|
||||
}
|
||||
|
||||
void renderSolidFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color, int fadeCategory, float fadeThreshold,
|
||||
const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize,
|
||||
const render::ShapePipelinePointer& pipeline);
|
||||
void renderWireFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color, int fadeCategory, float fadeThreshold,
|
||||
const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize,
|
||||
const render::ShapePipelinePointer& pipeline);
|
||||
|
||||
void renderSolidSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color,
|
||||
const render::ShapePipelinePointer& pipeline);
|
||||
|
@ -213,20 +192,6 @@ public:
|
|||
renderSolidSphereInstance(args, batch, glm::vec4(color, 1.0f), pipeline);
|
||||
}
|
||||
|
||||
void renderWireSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color,
|
||||
const render::ShapePipelinePointer& pipeline);
|
||||
void renderWireSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec3& color,
|
||||
const render::ShapePipelinePointer& pipeline) {
|
||||
renderWireSphereInstance(args, batch, glm::vec4(color, 1.0f), pipeline);
|
||||
}
|
||||
|
||||
void renderSolidCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color,
|
||||
const render::ShapePipelinePointer& pipeline);
|
||||
void renderSolidCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec3& color,
|
||||
const render::ShapePipelinePointer& pipeline) {
|
||||
renderSolidCubeInstance(args, batch, glm::vec4(color, 1.0f), pipeline);
|
||||
}
|
||||
|
||||
void renderWireCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color,
|
||||
const render::ShapePipelinePointer& pipeline);
|
||||
void renderWireCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec3& color,
|
||||
|
@ -235,24 +200,10 @@ public:
|
|||
}
|
||||
|
||||
// Dynamic geometry
|
||||
void renderShape(gpu::Batch& batch, Shape shape);
|
||||
void renderWireShape(gpu::Batch& batch, Shape shape);
|
||||
void renderShape(gpu::Batch& batch, Shape shape, const glm::vec4& color);
|
||||
void renderWireShape(gpu::Batch& batch, Shape shape, const glm::vec4& color);
|
||||
void renderShape(gpu::Batch& batch, Shape shape, gpu::BufferPointer& colorBuffer);
|
||||
void renderWireShape(gpu::Batch& batch, Shape shape, gpu::BufferPointer& colorBuffer);
|
||||
size_t getShapeTriangleCount(Shape shape);
|
||||
|
||||
void renderCube(gpu::Batch& batch);
|
||||
void renderWireCube(gpu::Batch& batch);
|
||||
void renderCube(gpu::Batch& batch, const glm::vec4& color);
|
||||
void renderWireCube(gpu::Batch& batch, const glm::vec4& color);
|
||||
size_t getCubeTriangleCount();
|
||||
|
||||
void renderSphere(gpu::Batch& batch);
|
||||
void renderWireSphere(gpu::Batch& batch);
|
||||
void renderSphere(gpu::Batch& batch, const glm::vec4& color);
|
||||
void renderWireSphere(gpu::Batch& batch, const glm::vec4& color);
|
||||
size_t getSphereTriangleCount();
|
||||
|
||||
void renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner,
|
||||
int majorRows, int majorCols, float majorEdge,
|
||||
int minorRows, int minorCols, float minorEdge,
|
||||
|
@ -262,10 +213,6 @@ public:
|
|||
|
||||
void renderUnitQuad(gpu::Batch& batch, const glm::vec4& color, int id);
|
||||
|
||||
void renderUnitQuad(gpu::Batch& batch, int id) {
|
||||
renderUnitQuad(batch, glm::vec4(1), id);
|
||||
}
|
||||
|
||||
void renderQuad(gpu::Batch& batch, int x, int y, int width, int height, const glm::vec4& color, int id)
|
||||
{ renderQuad(batch, glm::vec2(x,y), glm::vec2(x + width, y + height), color, id); }
|
||||
|
||||
|
@ -307,19 +254,6 @@ public:
|
|||
void renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color,
|
||||
const float dash_length, const float gap_length, int id);
|
||||
|
||||
void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec3& color, int id)
|
||||
{ renderLine(batch, p1, p2, glm::vec4(color, 1.0f), id); }
|
||||
|
||||
void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec4& color, int id)
|
||||
{ renderLine(batch, p1, p2, color, color, id); }
|
||||
|
||||
void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2,
|
||||
const glm::vec3& color1, const glm::vec3& color2, int id)
|
||||
{ renderLine(batch, p1, p2, glm::vec4(color1, 1.0f), glm::vec4(color2, 1.0f), id); }
|
||||
|
||||
void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2,
|
||||
const glm::vec4& color1, const glm::vec4& color2, int id);
|
||||
|
||||
void updateVertices(int id, const QVector<glm::vec2>& points, const glm::vec4& color);
|
||||
void updateVertices(int id, const QVector<glm::vec2>& points, const QVector<glm::vec4>& colors);
|
||||
void updateVertices(int id, const QVector<glm::vec3>& points, const glm::vec4& color);
|
||||
|
@ -364,6 +298,8 @@ public:
|
|||
|
||||
graphics::MeshPointer meshFromShape(Shape geometryShape, glm::vec3 color);
|
||||
|
||||
static uint32_t toCompactColor(const glm::vec4& color);
|
||||
|
||||
private:
|
||||
|
||||
GeometryCache();
|
||||
|
@ -397,6 +333,7 @@ private:
|
|||
public:
|
||||
static int population;
|
||||
gpu::BufferPointer verticesBuffer;
|
||||
gpu::BufferPointer normalBuffer;
|
||||
gpu::BufferPointer colorBuffer;
|
||||
gpu::BufferPointer uniformBuffer;
|
||||
gpu::Stream::FormatPointer streamFormat;
|
||||
|
@ -445,18 +382,9 @@ private:
|
|||
QHash<int, Vec2FloatPairPair> _lastRegisteredGridBuffer;
|
||||
QHash<int, GridBuffer> _registeredGridBuffers;
|
||||
|
||||
// FIXME: clean these up
|
||||
static gpu::ShaderPointer _simpleShader;
|
||||
static gpu::ShaderPointer _transparentShader;
|
||||
static gpu::ShaderPointer _unlitShader;
|
||||
static gpu::ShaderPointer _simpleFadeShader;
|
||||
static gpu::ShaderPointer _unlitFadeShader;
|
||||
static gpu::ShaderPointer _forwardSimpleShader;
|
||||
static gpu::ShaderPointer _forwardTransparentShader;
|
||||
static gpu::ShaderPointer _forwardUnlitShader;
|
||||
static gpu::ShaderPointer _forwardSimpleFadeShader;
|
||||
static gpu::ShaderPointer _forwardUnlitFadeShader;
|
||||
|
||||
// transparent, unlit, forward, fade
|
||||
static std::map<std::tuple<bool, bool, bool, bool>, gpu::ShaderPointer> _shapeShaders;
|
||||
// transparent, unlit, forward
|
||||
static std::map<std::tuple<bool, bool, bool, graphics::MaterialKey::CullFaceMode>, render::ShapePipelinePointer> _shapePipelines;
|
||||
static QHash<SimpleProgramKey, gpu::PipelinePointer> _simplePrograms;
|
||||
|
||||
|
|
|
@ -354,12 +354,17 @@ void ModelMeshPartPayload::render(RenderArgs* args) {
|
|||
outColor = procedural->getColor(outColor);
|
||||
procedural->prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created,
|
||||
ProceduralProgramKey(outColor.a < 1.0f, _shapeKey.isDeformed(), _shapeKey.isDualQuatSkinned()));
|
||||
batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a);
|
||||
|
||||
const uint32_t compactColor = GeometryCache::toCompactColor(glm::vec4(outColor));
|
||||
_drawMesh->getColorBuffer()->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor);
|
||||
} else {
|
||||
// apply material properties
|
||||
if (RenderPipelines::bindMaterials(_drawMaterials, batch, args->_renderMode, args->_enableTexturing)) {
|
||||
args->_details._materialSwitches++;
|
||||
}
|
||||
|
||||
const uint32_t compactColor = 0xFFFFFFFF;
|
||||
_drawMesh->getColorBuffer()->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor);
|
||||
}
|
||||
|
||||
// Draw!
|
||||
|
|
|
@ -279,6 +279,7 @@ bool QmlWindowClass::isVisible() {
|
|||
return quickItem->isVisible();
|
||||
} else {
|
||||
qDebug() << "QmlWindowClass::isVisible: asQuickItem() returned NULL";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -123,6 +123,17 @@
|
|||
var copiedRotation;
|
||||
var copiedDimensions;
|
||||
|
||||
var importUiPersistedData = {
|
||||
"elJsonUrl": "",
|
||||
"elImportAtAvatar": true,
|
||||
"elImportAtSpecificPosition": false,
|
||||
"elPositionX": 0,
|
||||
"elPositionY": 0,
|
||||
"elPositionZ": 0,
|
||||
"elEntityHostTypeDomain": true,
|
||||
"elEntityHostTypeAvatar": false
|
||||
};
|
||||
|
||||
var cameraManager = new CameraManager();
|
||||
|
||||
var grid = new Grid();
|
||||
|
@ -2010,7 +2021,8 @@
|
|||
return position;
|
||||
}
|
||||
|
||||
function importSVO(importURL) {
|
||||
function importSVO(importURL, importEntityHostType) {
|
||||
importEntityHostType = importEntityHostType || "domain";
|
||||
if (!Entities.canRez() && !Entities.canRezTmp()) {
|
||||
Window.notifyEditError(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG);
|
||||
return;
|
||||
|
@ -2033,7 +2045,7 @@
|
|||
position = createApp.getPositionToCreateEntity(Clipboard.getClipboardContentsLargestDimension() / 2);
|
||||
}
|
||||
if (position !== null && position !== undefined) {
|
||||
var pastedEntityIDs = Clipboard.pasteEntities(position);
|
||||
var pastedEntityIDs = Clipboard.pasteEntities(position, importEntityHostType);
|
||||
if (!isLargeImport) {
|
||||
// The first entity in Clipboard gets the specified position with the rest being relative to it. Therefore, move
|
||||
// entities after they're imported so that they're all the correct distance in front of and with geometric mean
|
||||
|
@ -2813,6 +2825,72 @@
|
|||
type: 'zoneListRequest',
|
||||
zones: getExistingZoneList()
|
||||
});
|
||||
} else if (data.type === "importUiBrowse") {
|
||||
let fileToImport = Window.browse("Select .json to Import", "", "*.json");
|
||||
if (fileToImport !== null) {
|
||||
emitScriptEvent({
|
||||
type: 'importUi_SELECTED_FILE',
|
||||
file: fileToImport
|
||||
});
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
}
|
||||
} else if (data.type === "importUiImport") {
|
||||
if ((data.entityHostType === "domain" && Entities.canAdjustLocks() && Entities.canRez()) ||
|
||||
(data.entityHostType === "avatar" && Entities.canRezAvatarEntities())) {
|
||||
if (data.positioningMode === "avatar") {
|
||||
importSVO(data.jsonURL, data.entityHostType);
|
||||
} else {
|
||||
if (Clipboard.importEntities(data.jsonURL)) {
|
||||
let importedPastedEntities = Clipboard.pasteEntities(data.position, data.entityHostType);
|
||||
if (importedPastedEntities.length === 0) {
|
||||
emitScriptEvent({
|
||||
type: 'importUi_IMPORT_ERROR',
|
||||
reason: "No Entity has been imported."
|
||||
});
|
||||
} else {
|
||||
if (isActive) {
|
||||
selectionManager.setSelections(importedPastedEntities, this);
|
||||
}
|
||||
emitScriptEvent({type: 'importUi_IMPORT_CONFIRMATION'});
|
||||
}
|
||||
} else {
|
||||
emitScriptEvent({
|
||||
type: 'importUi_IMPORT_ERROR',
|
||||
reason: "Import Entities has failed."
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
emitScriptEvent({
|
||||
type: 'importUi_IMPORT_ERROR',
|
||||
reason: "You don't have permission to create in this domain."
|
||||
});
|
||||
}
|
||||
} else if (data.type === "importUiGoBack") {
|
||||
if (location.canGoBack()) {
|
||||
location.goBack();
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
}
|
||||
} else if (data.type === "importUiGoTutorial") {
|
||||
Window.location = "file:///~/serverless/tutorial.json";
|
||||
} else if (data.type === "importUiGetCopiedPosition") {
|
||||
if (copiedPosition !== undefined) {
|
||||
emitScriptEvent({
|
||||
type: 'importUi_POSITION_TO_PASTE',
|
||||
position: copiedPosition
|
||||
});
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
}
|
||||
} else if (data.type === "importUiPersistData") {
|
||||
importUiPersistedData = data.importUiPersistedData;
|
||||
} else if (data.type === "importUiGetPersistData") {
|
||||
emitScriptEvent({
|
||||
type: 'importUi_LOAD_DATA',
|
||||
importUiPersistedData: importUiPersistedData
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
160
scripts/system/create/importEntities/html/css/importEntities.css
Normal file
160
scripts/system/create/importEntities/html/css/importEntities.css
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
// importEntities.css
|
||||
//
|
||||
// Created by Alezia Kurdis on March 13th, 2024
|
||||
// Copyright 2024 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
*/
|
||||
|
||||
@font-face {
|
||||
font-family: FiraSans-SemiBold;
|
||||
src: url(../../../../../../resources/fonts/FiraSans-SemiBold.ttf), /* Windows production */
|
||||
url(../../../../../../fonts/FiraSans-SemiBold.ttf); /* OSX production */
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: FiraSans-Regular;
|
||||
src: url(../../../../../../resources/fonts/FiraSans-Regular.ttf), /* Windows production */
|
||||
url(../../../../../../fonts/FiraSans-Regular.ttf); /* OSX production */
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Raleway-Bold;
|
||||
src: url(../../../../../../resources/fonts/Raleway-Bold.ttf), /* Windows production */
|
||||
url(../../../../../../fonts/Raleway-Bold.ttf); /* OSX production */
|
||||
}
|
||||
|
||||
html {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
input[type="text"] {
|
||||
font-family: FiraSans-SemiBold;
|
||||
color: #BBBBBB;
|
||||
background-color: #222222;
|
||||
border: 0;
|
||||
padding: 4px;
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
font-family: FiraSans-SemiBold;
|
||||
color: #BBBBBB;
|
||||
background-color: #222222;
|
||||
border: 0;
|
||||
padding: 4px;
|
||||
margin: 1px;
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 18px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
body {
|
||||
background: #404040;
|
||||
font-family: FiraSans-Regular;
|
||||
font-size: 14px;
|
||||
color: #BBBBBB;
|
||||
text-decoration: none;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
#importAtSpecificPositionContainer {
|
||||
display: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#jsonUrl {
|
||||
width:90%;
|
||||
}
|
||||
#browseBtn {
|
||||
font-family: FiraSans-SemiBold;
|
||||
}
|
||||
#browseBtn:hover {
|
||||
|
||||
}
|
||||
|
||||
label {
|
||||
font-family: FiraSans-SemiBold;
|
||||
color: #DDDDDD;
|
||||
}
|
||||
font.red {
|
||||
font-family: FiraSans-SemiBold;
|
||||
color: #e83333;
|
||||
}
|
||||
font.green {
|
||||
font-family: FiraSans-SemiBold;
|
||||
color: #0db518;
|
||||
}
|
||||
font.blue {
|
||||
font-family: FiraSans-SemiBold;
|
||||
color: #447ef2;
|
||||
}
|
||||
#importBtn {
|
||||
color: #ffffff;
|
||||
background-color: #1080b8;
|
||||
background: linear-gradient(#00b4ef 20%, #1080b8 100%);
|
||||
font-family: Raleway-Bold;
|
||||
font-size: 13px;
|
||||
text-transform: uppercase;
|
||||
vertical-align: top;
|
||||
height: 28px;
|
||||
min-width: 70px;
|
||||
padding: 0 18px;
|
||||
margin: 3px 3px 12px 3px;
|
||||
border-radius: 5px;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
#importBtn:hover {
|
||||
background: linear-gradient(#00b4ef, #00b4ef);
|
||||
border: none;
|
||||
}
|
||||
input:focus {
|
||||
outline: none;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
button:focus {
|
||||
outline: none;
|
||||
}
|
||||
div.explicative {
|
||||
width: 96%;
|
||||
padding: 7px;
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 12px;
|
||||
text-decoration: none;
|
||||
color: #BBBBBB;
|
||||
}
|
||||
button.black {
|
||||
font-family: Raleway-Bold;
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
vertical-align: top;
|
||||
height: 18px;
|
||||
min-width: 60px;
|
||||
padding: 0 14px;
|
||||
margin: 5px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
background: linear-gradient(#343434 20%, #000 100%);
|
||||
cursor: pointer;
|
||||
}
|
||||
button.black:hover {
|
||||
background: linear-gradient(#000, #000);
|
||||
border: none;
|
||||
}
|
||||
#messageContainer {
|
||||
font-family: FiraSans-SemiBold;
|
||||
width: 100%;
|
||||
}
|
||||
#testContainer {
|
||||
border: 1px solid #AAAAAA;
|
||||
padding: 0px;
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
<!--
|
||||
// importEntities.html
|
||||
//
|
||||
// Created by Alezia Kurdis on March 13th, 2024.
|
||||
// Copyright 2024 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Import Entities</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
<link rel="stylesheet" type="text/css" href="css/importEntities.css">
|
||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||
<script type="text/javascript" src="js/importEntitiesUi.js"></script>
|
||||
</head>
|
||||
<body onload="loaded();" >
|
||||
<h2>Import Entities (.json)</h2>
|
||||
<font class="red">* </font>URL/File (.json):<br>
|
||||
<input type="text" id = "jsonUrl"> <button id="browseBtn">...</button><br>
|
||||
<br>
|
||||
<table style="width: 96%;">
|
||||
<tr style="vertical-align: top;">
|
||||
<td style="width: 40%;">
|
||||
Position:<br>
|
||||
<input type="radio" name="importAtPosition" id="importAtAvatar" value="avatar" checked><label for="importAtAvatar"> In front of your avatar</label><br>
|
||||
<input type="radio" name="importAtPosition" id="importAtSpecificPosition" value="position"><label for="importAtSpecificPosition"> At a specified Position</label><br>
|
||||
</td>
|
||||
<td style="width: 60%;">
|
||||
<div id="importAtSpecificPositionContainer">
|
||||
<font class="red">X</font> <input type="number" size="6" id = "positionX" value = "0">
|
||||
<font class="green">Y</font> <input type="number" size="6" id = "positionY" value = "0">
|
||||
<font class="blue">Z</font> <input type="number" size="6" id = "positionZ" value = "0"><br>
|
||||
<button id="pastePositionBtn" class="black">Paste Position</button><br>
|
||||
<div class="explicative">
|
||||
Note: If you import a "serverless" json file, such data include positions.
|
||||
It this case, the "Position" will act as an offset.
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br>
|
||||
<table style="width: 96%;">
|
||||
<tr style="vertical-align: top;">
|
||||
<td style="width: 30%;">
|
||||
Entity Host Type:<br>
|
||||
<input type="radio" name="entityHostType" id="entityHostTypeDomain" value="domain" checked><label for="entityHostTypeDomain"> Domain Entities</label><br>
|
||||
<input type="radio" name="entityHostType" id="entityHostTypeAvatar" value="avatar"><label for="entityHostTypeAvatar"> Avatar Entities</label><br>
|
||||
</td>
|
||||
<td style="width: 70%;">
|
||||
<div id="messageContainer"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="text-align: right; width:96%;"><button id="importBtn">IMPORT</button></div>
|
||||
<div id="testContainer">
|
||||
<table style="width: 96%;">
|
||||
<tr style="vertical-align: top;">
|
||||
<td style="width: 60%;">
|
||||
<div class="explicative">
|
||||
For large import, it can be wise to test it in a serverless environment before doing it in your real domain.
|
||||
</div>
|
||||
</td>
|
||||
<td style="width: 40%;">
|
||||
<div style="text-align: center; width:96%;">
|
||||
<button id="backBtn" class="black">⮜ Back</button>
|
||||
|
||||
<button id="tpTutorialBtn" class="black">Go test ⮞</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
217
scripts/system/create/importEntities/html/js/importEntitiesUi.js
Normal file
217
scripts/system/create/importEntities/html/js/importEntitiesUi.js
Normal file
|
@ -0,0 +1,217 @@
|
|||
// importEntitiesUi.js
|
||||
//
|
||||
// Created by Alezia Kurdis on March 13th, 2024
|
||||
// Copyright 2024 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
let elJsonUrl;
|
||||
let elBrowseBtn;
|
||||
let elImportAtAvatar;
|
||||
let elImportAtSpecificPosition;
|
||||
let elImportAtSpecificPositionContainer;
|
||||
let elPositionX;
|
||||
let elPositionY;
|
||||
let elPositionZ;
|
||||
let elEntityHostTypeDomain;
|
||||
let elEntityHostTypeAvatar;
|
||||
let elMessageContainer;
|
||||
let elImportBtn;
|
||||
let elBackBtn;
|
||||
let elTpTutorialBtn;
|
||||
let elPastePositionBtn;
|
||||
|
||||
let lockUntil;
|
||||
|
||||
const LOCK_BTN_DELAY = 2000; //2 sec
|
||||
|
||||
function loaded() {
|
||||
lockUntil = 0;
|
||||
|
||||
elJsonUrl = document.getElementById("jsonUrl");
|
||||
elBrowseBtn = document.getElementById("browseBtn");
|
||||
elImportAtAvatar = document.getElementById("importAtAvatar");
|
||||
elImportAtSpecificPosition = document.getElementById("importAtSpecificPosition");
|
||||
elImportAtSpecificPositionContainer = document.getElementById("importAtSpecificPositionContainer");
|
||||
elPositionX = document.getElementById("positionX");
|
||||
elPositionY = document.getElementById("positionY");
|
||||
elPositionZ = document.getElementById("positionZ");
|
||||
elEntityHostTypeDomain = document.getElementById("entityHostTypeDomain");
|
||||
elEntityHostTypeAvatar = document.getElementById("entityHostTypeAvatar");
|
||||
elMessageContainer = document.getElementById("messageContainer");
|
||||
elImportBtn = document.getElementById("importBtn");
|
||||
elBackBtn = document.getElementById("backBtn");
|
||||
elTpTutorialBtn = document.getElementById("tpTutorialBtn");
|
||||
elPastePositionBtn = document.getElementById("pastePositionBtn");
|
||||
|
||||
elJsonUrl.oninput = function() {
|
||||
persistData();
|
||||
}
|
||||
|
||||
elPositionX.oninput = function() {
|
||||
persistData();
|
||||
}
|
||||
|
||||
elPositionY.oninput = function() {
|
||||
persistData();
|
||||
}
|
||||
|
||||
elPositionZ.oninput = function() {
|
||||
persistData();
|
||||
}
|
||||
|
||||
elEntityHostTypeDomain.onclick = function() {
|
||||
persistData();
|
||||
}
|
||||
|
||||
elEntityHostTypeAvatar.onclick = function() {
|
||||
persistData();
|
||||
}
|
||||
|
||||
elBrowseBtn.onclick = function() {
|
||||
const d = new Date();
|
||||
let time = d.getTime();
|
||||
if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiBrowse" }));
|
||||
lockUntil = d.getTime() + LOCK_BTN_DELAY;
|
||||
}
|
||||
};
|
||||
|
||||
elImportAtAvatar.onclick = function() {
|
||||
elImportAtSpecificPositionContainer.style.display = "None";
|
||||
persistData();
|
||||
};
|
||||
|
||||
elImportAtSpecificPosition.onclick = function() {
|
||||
elImportAtSpecificPositionContainer.style.display = "Block";
|
||||
persistData();
|
||||
};
|
||||
|
||||
elImportBtn.onclick = function() {
|
||||
const d = new Date();
|
||||
let time = d.getTime();
|
||||
if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) {
|
||||
importJsonToWorld();
|
||||
lockUntil = d.getTime() + LOCK_BTN_DELAY;
|
||||
}
|
||||
};
|
||||
|
||||
elBackBtn.onclick = function() {
|
||||
const d = new Date();
|
||||
let time = d.getTime();
|
||||
if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiGoBack" }));
|
||||
lockUntil = d.getTime() + LOCK_BTN_DELAY;
|
||||
}
|
||||
};
|
||||
|
||||
elTpTutorialBtn.onclick = function() {
|
||||
const d = new Date();
|
||||
let time = d.getTime();
|
||||
if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiGoTutorial" }));
|
||||
lockUntil = d.getTime() + LOCK_BTN_DELAY;
|
||||
}
|
||||
};
|
||||
|
||||
elPastePositionBtn.onclick = function() {
|
||||
const d = new Date();
|
||||
let time = d.getTime();
|
||||
if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiGetCopiedPosition" }));
|
||||
lockUntil = d.getTime() + LOCK_BTN_DELAY;
|
||||
}
|
||||
};
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiGetPersistData" }));
|
||||
}
|
||||
|
||||
function persistData() {
|
||||
let message = {
|
||||
"type": "importUiPersistData",
|
||||
"importUiPersistedData": {
|
||||
"elJsonUrl": elJsonUrl.value,
|
||||
"elImportAtAvatar": elImportAtAvatar.checked,
|
||||
"elImportAtSpecificPosition": elImportAtSpecificPosition.checked,
|
||||
"elPositionX": elPositionX.value,
|
||||
"elPositionY": elPositionY.value,
|
||||
"elPositionZ": elPositionZ.value,
|
||||
"elEntityHostTypeDomain": elEntityHostTypeDomain.checked,
|
||||
"elEntityHostTypeAvatar": elEntityHostTypeAvatar.checked
|
||||
}
|
||||
};
|
||||
EventBridge.emitWebEvent(JSON.stringify(message));
|
||||
}
|
||||
|
||||
function loadDataInUi(importUiPersistedData) {
|
||||
elJsonUrl.value = importUiPersistedData.elJsonUrl;
|
||||
elImportAtAvatar.checked = importUiPersistedData.elImportAtAvatar;
|
||||
elImportAtSpecificPosition.checked = importUiPersistedData.elImportAtSpecificPosition;
|
||||
elPositionX.value = importUiPersistedData.elPositionX;
|
||||
elPositionY.value = importUiPersistedData.elPositionY;
|
||||
elPositionZ.value = importUiPersistedData.elPositionZ;
|
||||
elEntityHostTypeDomain.checked = importUiPersistedData.elEntityHostTypeDomain;
|
||||
elEntityHostTypeAvatar.checked = importUiPersistedData.elEntityHostTypeAvatar;
|
||||
if (elImportAtSpecificPosition.checked) {
|
||||
elImportAtSpecificPositionContainer.style.display = "Block";
|
||||
}
|
||||
}
|
||||
|
||||
function importJsonToWorld() {
|
||||
elMessageContainer.innerHTML = "";
|
||||
|
||||
if (elJsonUrl.value === "") {
|
||||
elMessageContainer.innerHTML = "<div style = 'padding: 10px; color: #000000; background-color: #ff7700;'>ERROR: 'URL/File (.json)' is required.</div>";
|
||||
return;
|
||||
}
|
||||
|
||||
let positioningMode = getRadioValue("importAtPosition");
|
||||
let entityHostType = getRadioValue("entityHostType");
|
||||
|
||||
if (positioningMode === "position" && (elPositionX.value === "" || elPositionY.value === "" || elPositionZ.value === "")) {
|
||||
elMessageContainer.innerHTML = "<div style = 'padding: 10px; color: #000000; background-color: #ff7700;'>ERROR: 'Position' is required.</div>";
|
||||
return;
|
||||
}
|
||||
let position = {"x": parseFloat(elPositionX.value), "y": parseFloat(elPositionY.value), "z": parseFloat(elPositionZ.value)};
|
||||
let message = {
|
||||
"type": "importUiImport",
|
||||
"jsonURL": elJsonUrl.value,
|
||||
"positioningMode": positioningMode,
|
||||
"position": position,
|
||||
"entityHostType": entityHostType
|
||||
};
|
||||
EventBridge.emitWebEvent(JSON.stringify(message));
|
||||
}
|
||||
|
||||
function getRadioValue(objectName) {
|
||||
let radios = document.getElementsByName(objectName);
|
||||
let i;
|
||||
let selectedValue = "";
|
||||
for (i = 0; i < radios.length; i++) {
|
||||
if (radios[i].checked) {
|
||||
selectedValue = radios[i].value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return selectedValue;
|
||||
}
|
||||
|
||||
EventBridge.scriptEventReceived.connect(function(message){
|
||||
let messageObj = JSON.parse(message);
|
||||
if (messageObj.type === "importUi_IMPORT_CONFIRMATION") {
|
||||
elMessageContainer.innerHTML = "<div style = 'padding: 10px; color: #000000; background-color: #00ff00;'>IMPORT SUCCESSFUL.</div>";
|
||||
} else if (messageObj.type === "importUi_IMPORT_ERROR") {
|
||||
elMessageContainer.innerHTML = "<div style = 'padding: 10px; color: #FFFFFF; background-color: #ff0000;'>IMPORT ERROR: " + messageObj.reason + "</div>";
|
||||
} else if (messageObj.type === "importUi_SELECTED_FILE") {
|
||||
elJsonUrl.value = messageObj.file;
|
||||
persistData();
|
||||
} else if (messageObj.type === "importUi_POSITION_TO_PASTE") {
|
||||
elPositionX.value = messageObj.position.x;
|
||||
elPositionY.value = messageObj.position.y;
|
||||
elPositionZ.value = messageObj.position.z;
|
||||
persistData();
|
||||
} else if (messageObj.type === "importUi_LOAD_DATA") {
|
||||
loadDataInUi(messageObj.importUiPersistedData);
|
||||
}
|
||||
});
|
|
@ -301,6 +301,22 @@ TabBar {
|
|||
}
|
||||
}
|
||||
|
||||
EditTabButton {
|
||||
title: "IMPORT"
|
||||
active: true
|
||||
enabled: true
|
||||
property string originalUrl: ""
|
||||
|
||||
property Component visualItem: Component {
|
||||
WebView {
|
||||
id: advancedImportWebView
|
||||
url: Qt.resolvedUrl("../importEntities/html/importEntities.html")
|
||||
enabled: true
|
||||
blurOnCtrlShift: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function fromScript(message) {
|
||||
switch (message.method) {
|
||||
case 'selectTab':
|
||||
|
@ -333,6 +349,9 @@ TabBar {
|
|||
case 'grid':
|
||||
editTabView.currentIndex = 3;
|
||||
break;
|
||||
case 'import':
|
||||
editTabView.currentIndex = 4;
|
||||
break;
|
||||
default:
|
||||
console.warn('Attempt to switch to invalid tab:', id);
|
||||
}
|
||||
|
|
|
@ -291,6 +291,22 @@ TabBar {
|
|||
}
|
||||
}
|
||||
|
||||
EditTabButton {
|
||||
title: "IMPORT"
|
||||
active: true
|
||||
enabled: true
|
||||
property string originalUrl: ""
|
||||
|
||||
property Component visualItem: Component {
|
||||
WebView {
|
||||
id: advancedImportWebView
|
||||
url: Qt.resolvedUrl("../importEntities/html/importEntities.html")
|
||||
enabled: true
|
||||
blurOnCtrlShift: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function fromScript(message) {
|
||||
switch (message.method) {
|
||||
case 'selectTab':
|
||||
|
@ -304,7 +320,7 @@ TabBar {
|
|||
// Changes the current tab based on tab index or title as input
|
||||
function selectTab(id) {
|
||||
if (typeof id === 'number') {
|
||||
if (id >= tabIndex.create && id <= tabIndex.grid) {
|
||||
if (id >= tabIndex.create && id <= tabIndex.import) {
|
||||
editTabView.currentIndex = id;
|
||||
} else {
|
||||
console.warn('Attempt to switch to invalid tab:', id);
|
||||
|
@ -320,6 +336,9 @@ TabBar {
|
|||
case 'grid':
|
||||
editTabView.currentIndex = tabIndex.grid;
|
||||
break;
|
||||
case 'import':
|
||||
editTabView.currentIndex = tabIndex.import;
|
||||
break;
|
||||
default:
|
||||
console.warn('Attempt to switch to invalid tab:', id);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
// edit-style.css
|
||||
//
|
||||
// Created by Ryan Huffman on 13 Nov 2014
|
||||
// Created by Ryan Huffman on November 13th, 2014
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
// Copyright 2022 Overte e.V.
|
||||
// Copyright 2022-2024 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -701,6 +701,17 @@ div.section-header, hr {
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
div.simpleSeparator {
|
||||
width: 97%;
|
||||
height: 2px;
|
||||
border: 0px;
|
||||
margin-top: 4px;
|
||||
margin-right: 8px;
|
||||
margin-left: 8px;
|
||||
margin-bottom: 4px;
|
||||
background-color: #777777;
|
||||
}
|
||||
|
||||
.section.minor {
|
||||
margin: 0 21px;
|
||||
box-shadow: 1px -1px 0 rgb(37,37,37);
|
||||
|
@ -1304,6 +1315,27 @@ div#grid-section, body#entity-list-body {
|
|||
margin: 0px 8px 8px 8px;
|
||||
}
|
||||
|
||||
#voxels-section {
|
||||
padding-bottom: 0px;
|
||||
margin: 8px 8px 8px 8px;
|
||||
}
|
||||
|
||||
#mode-section {
|
||||
padding: 3px;
|
||||
height: 28px;
|
||||
margin: 8px 8px 8px 8px;
|
||||
color: #000000;
|
||||
background-color: #999999;
|
||||
font-family: Raleway-Bold;
|
||||
font-size: 16px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
#creationModeLabel {
|
||||
padding-top: 4px;
|
||||
width: 156px;
|
||||
}
|
||||
|
||||
|
||||
#entity-list-header {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
@ -1433,6 +1465,11 @@ input[type=button].entity-list-menutitle {
|
|||
background: linear-gradient(#343434 30%, #000 100%);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#voxel-edit-mode {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
input[type=button].entity-list-menutitle:enabled:hover {
|
||||
background: linear-gradient(#000, #000);
|
||||
border: none;
|
||||
|
@ -2082,10 +2119,10 @@ div.entity-list-menu {
|
|||
div.tools-select-menu {
|
||||
position: relative;
|
||||
display: none;
|
||||
width: 370px;
|
||||
width: 200px;
|
||||
height: 0px;
|
||||
top: 0px;
|
||||
left: 8px;
|
||||
top: -16px;
|
||||
left: 124px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-style: solid;
|
||||
|
@ -2097,20 +2134,19 @@ div.tools-select-menu {
|
|||
}
|
||||
|
||||
div.tools-help-popup {
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 15px;
|
||||
font-family: Raleway-SemiBold;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
display: none;
|
||||
width: 690px;
|
||||
width: 96%;
|
||||
padding: 5px;
|
||||
height: auto;
|
||||
top: 0px;
|
||||
top: -16px;
|
||||
left: 8px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-style: solid;
|
||||
border-color: #505050;
|
||||
border-width: 1px;
|
||||
background-color: #404040;
|
||||
border: 2px solid #c0c0c0;
|
||||
background-color: #333333;
|
||||
z-index: 2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<!--
|
||||
// gridControls.html
|
||||
//
|
||||
// Created by Ryan Huffman on 6 Nov 2014
|
||||
// Created by Ryan Huffman on November 6th, 2014
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
// Copyright 2022 Overte e.V.
|
||||
// Copyright 2022-2024 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -23,26 +23,28 @@
|
|||
<body onload='loaded();'>
|
||||
<div id="mode-section" class="section">
|
||||
<div class="property container">
|
||||
<label for="create-app-mode">Create app mode </label>
|
||||
<div id="creationModeLabel">
|
||||
CREATION MODE
|
||||
</div>
|
||||
<div class="property container">
|
||||
<input name="create-app-mode" type="button" class="entity-list-menutitle" id="create-app-mode" value="Create app mode▾" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="tools-select-menu" id="edit-mode-menu" >
|
||||
<button class="menu-button" id="edit-mode-object" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Object mode</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="edit-mode-voxel" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Voxel edit mode</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tools-select-menu" id="edit-mode-menu" >
|
||||
<button class="menu-button" id="edit-mode-object" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Object mode</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="edit-mode-voxel" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Voxel edit mode</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="voxels-section" class="section">
|
||||
<h2>Voxel edit settings</h2>
|
||||
<div class="property container">
|
||||
|
@ -76,9 +78,9 @@
|
|||
</button>
|
||||
</div>
|
||||
<div class="property container">
|
||||
<label for="voxel-remove"> Remove voxels</label>
|
||||
<div style="width: 100%">
|
||||
<input type='checkbox' id="voxel-remove" style="width: 100%">
|
||||
<label for="voxel-remove">Remove voxels</label>
|
||||
<div style="width: 100%;">
|
||||
<input type='checkbox' id="voxel-remove" style="width: 100%;">
|
||||
<label for="voxel-remove"> </label>
|
||||
</div>
|
||||
<div class="property container">
|
||||
|
@ -86,13 +88,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="tools-help-popup" id="voxel-help-popup" >
|
||||
<p>To edit voxels, Voxel Edit Mode needs to be selected.</p>
|
||||
<p>Desktop mode:</p>
|
||||
<p>Click the left mouse button to add a voxel. Click the middle mouse button to remove a voxel. Hold the mouse button and move the mouse to add or remove voxels in a single plane. The plane is determined by the direction in which you are looking when first voxel is added or removed (for example, look downwards to draw in horizontal plane).</p>
|
||||
<p>VR mode:</p>
|
||||
<p>Press the trigger to add a voxel. Press the trigger while holding the grip to remove a voxel. Hold the trigger and move the controller to add or remove voxels in a single plane. The plane is determined by the direction in which the controller's ray points when the first voxel is added or removed (for example point downwards to draw in the horizontal plane). Hold both grips and move your hands together or apart to change the size of the edit sphere.</p>
|
||||
To edit voxels, "<b>Voxel Edit Mode</b>" needs to be selected.<br><br>
|
||||
<b>Desktop:</b><br>
|
||||
Click the left mouse button to add a voxel. Click the middle mouse button to remove a voxel. Hold the mouse button and move the mouse to add or remove voxels in a single plane. The plane is determined by the direction in which you are looking when first voxel is added or removed (for example, look downwards to draw in horizontal plane).<br><br>
|
||||
<b>VR:</b><br>
|
||||
Press the trigger to add a voxel. Press the trigger while holding the grip to remove a voxel. Hold the trigger and move the controller to add or remove voxels in a single plane. The plane is determined by the direction in which the controller's ray points when the first voxel is added or removed (for example point downwards to draw in the horizontal plane). Hold both grips and move your hands together or apart to change the size of the edit sphere.
|
||||
</div>
|
||||
</div>
|
||||
<div class="simpleSeparator"></div>
|
||||
<div id="grid-section" class="section">
|
||||
<h2>Grid settings</h2>
|
||||
<div class="property container">
|
||||
|
|
Loading…
Reference in a new issue