mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 17:14:59 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into yellow
This commit is contained in:
commit
7951284e9c
36 changed files with 962 additions and 393 deletions
11
examples/tests/playaPerformanceTest.js
Normal file
11
examples/tests/playaPerformanceTest.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
var qml = Script.resolvePath('playaPerformanceTest.qml');
|
||||
qmlWindow = new OverlayWindow({
|
||||
title: 'Test Qml',
|
||||
source: qml,
|
||||
height: 320,
|
||||
width: 640,
|
||||
toolWindow: false,
|
||||
visible: true
|
||||
});
|
||||
|
||||
|
193
examples/tests/playaPerformanceTest.qml
Normal file
193
examples/tests/playaPerformanceTest.qml
Normal file
|
@ -0,0 +1,193 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
width: parent ? parent.width : 100
|
||||
height: parent ? parent.height : 100
|
||||
|
||||
signal sendToScript(var message);
|
||||
property var values: [];
|
||||
property var host: AddressManager.hostname
|
||||
|
||||
|
||||
Component.onCompleted: {
|
||||
Window.domainChanged.connect(function(newDomain){
|
||||
if (newDomain !== root.host) {
|
||||
root.host = AddressManager.hostname;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onHostChanged: {
|
||||
if (root.running) {
|
||||
if (host !== "Dreaming" && host !== "Playa") {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("PERF new domain " + host)
|
||||
if (host === "Dreaming") {
|
||||
AddressManager.handleLookupString("Playa");
|
||||
return;
|
||||
}
|
||||
|
||||
if (host === "Playa") {
|
||||
console.log("PERF starting timers and frame timing");
|
||||
// If we've arrived, start running the test
|
||||
FrameTimings.start();
|
||||
rotationTimer.start();
|
||||
stopTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
console.log("PERF startTest()");
|
||||
root.running = true
|
||||
console.log("PERF current host: " + AddressManager.hostname)
|
||||
// If we're already in playa, we need to go somewhere else...
|
||||
if ("Playa" === AddressManager.hostname) {
|
||||
console.log("PERF Navigating to dreaming")
|
||||
AddressManager.handleLookupString("Dreaming/0,0,0");
|
||||
} else {
|
||||
console.log("PERF Navigating to playa")
|
||||
AddressManager.handleLookupString("Playa");
|
||||
}
|
||||
}
|
||||
|
||||
function stopTest() {
|
||||
console.log("PERF stopTest()");
|
||||
root.running = false;
|
||||
stopTimer.stop();
|
||||
rotationTimer.stop();
|
||||
FrameTimings.finish();
|
||||
root.values = FrameTimings.getValues();
|
||||
AddressManager.handleLookupString("Dreaming/0,0,0");
|
||||
resultGraph.requestPaint();
|
||||
console.log("PERF Value Count: " + root.values.length);
|
||||
console.log("PERF Max: " + FrameTimings.max);
|
||||
console.log("PERF Min: " + FrameTimings.min);
|
||||
console.log("PERF Avg: " + FrameTimings.mean);
|
||||
console.log("PERF StdDev: " + FrameTimings.standardDeviation);
|
||||
}
|
||||
|
||||
function yaw(a) {
|
||||
var y = -Math.sin( a / 2.0 );
|
||||
var w = Math.cos( a / 2.0 );
|
||||
var l = Math.sqrt((y * y) + (w * w));
|
||||
return Qt.quaternion(w / l, 0, y / l, 0);
|
||||
}
|
||||
|
||||
function rotate() {
|
||||
MyAvatar.setOrientationVar(yaw(Date.now() / 1000));
|
||||
}
|
||||
|
||||
property bool running: false
|
||||
|
||||
Timer {
|
||||
id: stopTimer
|
||||
interval: 30 * 1000
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: stopTest();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: rotationTimer
|
||||
interval: 100
|
||||
repeat: true
|
||||
running: false
|
||||
onTriggered: rotate();
|
||||
}
|
||||
|
||||
Row {
|
||||
id: row
|
||||
anchors { left: parent.left; right: parent.right; }
|
||||
spacing: 8
|
||||
Button {
|
||||
text: root.running ? "Stop" : "Run"
|
||||
onClicked: root.running ? stopTest() : startTest();
|
||||
}
|
||||
}
|
||||
|
||||
// Rectangle {
|
||||
// anchors { left: parent.left; right: parent.right; top: row.bottom; topMargin: 8; bottom: parent.bottom; }
|
||||
// //anchors.fill: parent
|
||||
// color: "#7fff0000"
|
||||
// }
|
||||
|
||||
// Return the maximum value from a set of values
|
||||
function vv(i, max) {
|
||||
var perValue = values.length / max;
|
||||
var start = Math.floor(perValue * i);
|
||||
var end = Math.min(values.length, Math.floor(start + perValue));
|
||||
var result = 0;
|
||||
for (var j = start; j <= end; ++j) {
|
||||
result = Math.max(result, values[j]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Canvas {
|
||||
id: resultGraph
|
||||
anchors { left: parent.left; right: parent.right; top: row.bottom; margins: 16; bottom: parent.bottom; }
|
||||
property real maxValue: 200;
|
||||
property real perFrame: 10000;
|
||||
property real k1: (5 / maxValue) * height;
|
||||
property real k2: (10 / maxValue) * height;
|
||||
property real k3: (100 / maxValue) * height;
|
||||
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
if (values.length === 0) {
|
||||
ctx.fillStyle = Qt.rgba(1, 0, 0, 1);
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//ctx.setTransform(1, 0, 0, -1, 0, 0);
|
||||
ctx.fillStyle = Qt.rgba(0, 0, 0, 1);
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
ctx.strokeStyle= "gray";
|
||||
ctx.lineWidth="1";
|
||||
ctx.beginPath();
|
||||
for (var i = 0; i < width; ++i) {
|
||||
var value = vv(i, width); //values[Math.min(i, values.length - 1)];
|
||||
value /= 10000;
|
||||
value /= maxValue;
|
||||
ctx.moveTo(i, height);
|
||||
ctx.lineTo(i, height - (height * value));
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
ctx.strokeStyle= "green";
|
||||
ctx.lineWidth="2";
|
||||
ctx.beginPath();
|
||||
var lineHeight = height - k1;
|
||||
ctx.moveTo(0, lineHeight);
|
||||
ctx.lineTo(width, lineHeight);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.strokeStyle= "yellow";
|
||||
ctx.lineWidth="2";
|
||||
ctx.beginPath();
|
||||
lineHeight = height - k2;
|
||||
ctx.moveTo(0, lineHeight);
|
||||
ctx.lineTo(width, lineHeight);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.strokeStyle= "red";
|
||||
ctx.lineWidth="2";
|
||||
ctx.beginPath();
|
||||
lineHeight = height - k3;
|
||||
ctx.moveTo(0, lineHeight);
|
||||
ctx.lineTo(width, lineHeight);
|
||||
ctx.stroke();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ import "windows"
|
|||
Window {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
anchors.centerIn: parent
|
||||
|
||||
objectName: "AddressBarDialog"
|
||||
frame: HiddenFrame {}
|
||||
|
||||
|
@ -29,6 +29,19 @@ Window {
|
|||
width: addressBarDialog.implicitWidth
|
||||
height: addressBarDialog.implicitHeight
|
||||
|
||||
Component.onCompleted: {
|
||||
root.parentChanged.connect(center);
|
||||
center();
|
||||
}
|
||||
Component.onDestruction: {
|
||||
root.parentChanged.disconnect(center);
|
||||
}
|
||||
|
||||
function center() {
|
||||
// Explicitly center in order to avoid warnings at shutdown
|
||||
anchors.centerIn = parent;
|
||||
}
|
||||
|
||||
AddressBarDialog {
|
||||
id: addressBarDialog
|
||||
implicitWidth: backgroundImage.width
|
||||
|
|
|
@ -3,10 +3,24 @@ import QtQuick 2.3
|
|||
import QtQuick.Controls 1.2
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
id: stats
|
||||
|
||||
anchors.leftMargin: 300
|
||||
objectName: "StatsItem"
|
||||
|
||||
Component.onCompleted: {
|
||||
stats.parentChanged.connect(fill);
|
||||
fill();
|
||||
}
|
||||
Component.onDestruction: {
|
||||
stats.parentChanged.disconnect(fill);
|
||||
}
|
||||
|
||||
function fill() {
|
||||
// Explicitly fill in order to avoid warnings at shutdown
|
||||
anchors.fill = parent;
|
||||
}
|
||||
|
||||
Hifi.Stats {
|
||||
id: root
|
||||
objectName: "Stats"
|
||||
|
|
|
@ -248,9 +248,15 @@ Fadable {
|
|||
|
||||
children: [ swallower, frame, pane, activator ]
|
||||
|
||||
Component.onCompleted: { raise(); setDefaultFocus(); }
|
||||
Component.onDestruction: windowDestroyed();
|
||||
onParentChanged: raise();
|
||||
Component.onCompleted: {
|
||||
window.parentChanged.connect(raise);
|
||||
raise();
|
||||
setDefaultFocus();
|
||||
}
|
||||
Component.onDestruction: {
|
||||
window.parentChanged.disconnect(raise); // Prevent warning on shutdown
|
||||
windowDestroyed();
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible && destroyOnInvisible) {
|
||||
|
|
|
@ -114,9 +114,14 @@ Fadable {
|
|||
|
||||
children: [ swallower, frame, content, activator ]
|
||||
|
||||
Component.onCompleted: raise();
|
||||
Component.onDestruction: windowDestroyed();
|
||||
onParentChanged: raise();
|
||||
Component.onCompleted: {
|
||||
window.parentChanged.connect(raise);
|
||||
raise();
|
||||
}
|
||||
Component.onDestruction: {
|
||||
window.parentChanged.disconnect(raise); // Prevent warning on shutdown
|
||||
windowDestroyed();
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible && destroyOnInvisible) {
|
||||
|
|
|
@ -147,6 +147,8 @@
|
|||
#include "Util.h"
|
||||
#include "InterfaceParentFinder.h"
|
||||
|
||||
#include "FrameTimingsScriptingInterface.h"
|
||||
|
||||
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||
// FIXME seems to be broken.
|
||||
#if defined(Q_OS_WIN)
|
||||
|
@ -1150,6 +1152,9 @@ void Application::aboutToQuit() {
|
|||
|
||||
getActiveDisplayPlugin()->deactivate();
|
||||
|
||||
// Hide Running Scripts dialog so that it gets destroyed in an orderly manner; prevents warnings at shutdown.
|
||||
DependencyManager::get<OffscreenUi>()->hide("RunningScripts");
|
||||
|
||||
_aboutToQuit = true;
|
||||
|
||||
cleanupBeforeQuit();
|
||||
|
@ -1230,6 +1235,9 @@ void Application::cleanupBeforeQuit() {
|
|||
}
|
||||
|
||||
Application::~Application() {
|
||||
_entityClipboard->eraseAllOctreeElements();
|
||||
_entityClipboard.reset();
|
||||
|
||||
EntityTreePointer tree = getEntities()->getTree();
|
||||
tree->setSimulation(nullptr);
|
||||
|
||||
|
@ -1239,7 +1247,7 @@ Application::~Application() {
|
|||
_physicsEngine->setCharacterController(nullptr);
|
||||
|
||||
// remove avatars from physics engine
|
||||
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
|
||||
DependencyManager::get<AvatarManager>()->clearAllAvatars();
|
||||
VectorOfMotionStates motionStates;
|
||||
DependencyManager::get<AvatarManager>()->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
|
@ -1334,6 +1342,8 @@ void Application::initializeGL() {
|
|||
InfoView::show(INFO_HELP_PATH, true);
|
||||
}
|
||||
|
||||
FrameTimingsScriptingInterface _frameTimingsScriptingInterface;
|
||||
|
||||
extern void setupPreferences();
|
||||
|
||||
void Application::initializeUi() {
|
||||
|
@ -1378,6 +1388,8 @@ void Application::initializeUi() {
|
|||
rootContext->setContextProperty("Messages", DependencyManager::get<MessagesClient>().data());
|
||||
rootContext->setContextProperty("Recording", DependencyManager::get<RecordingScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Preferences", DependencyManager::get<Preferences>().data());
|
||||
rootContext->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
|
||||
rootContext->setContextProperty("FrameTimings", &_frameTimingsScriptingInterface);
|
||||
|
||||
rootContext->setContextProperty("TREE_SCALE", TREE_SCALE);
|
||||
rootContext->setContextProperty("Quat", new Quat());
|
||||
|
@ -1421,6 +1433,7 @@ void Application::initializeUi() {
|
|||
rootContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface());
|
||||
|
||||
rootContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor());
|
||||
|
||||
|
||||
_glWidget->installEventFilter(offscreenUi.data());
|
||||
offscreenUi->setMouseTranslator([=](const QPointF& pt) {
|
||||
|
@ -1463,9 +1476,9 @@ void Application::initializeUi() {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
void Application::paintGL() {
|
||||
updateHeartbeat();
|
||||
|
||||
// Some plugins process message events, potentially leading to
|
||||
// re-entering a paint event. don't allow further processing if this
|
||||
// happens
|
||||
|
@ -1483,6 +1496,7 @@ void Application::paintGL() {
|
|||
_frameCount++;
|
||||
_frameCounter.increment();
|
||||
|
||||
auto lastPaintBegin = usecTimestampNow();
|
||||
PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount);
|
||||
PerformanceTimer perfTimer("paintGL");
|
||||
|
||||
|
@ -1735,6 +1749,9 @@ void Application::paintGL() {
|
|||
batch.resetStages();
|
||||
});
|
||||
}
|
||||
|
||||
uint64_t lastPaintDuration = usecTimestampNow() - lastPaintBegin;
|
||||
_frameTimingsScriptingInterface.addValue(lastPaintDuration);
|
||||
}
|
||||
|
||||
void Application::runTests() {
|
||||
|
|
53
interface/src/FrameTimingsScriptingInterface.cpp
Normal file
53
interface/src/FrameTimingsScriptingInterface.cpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/04/04
|
||||
// Copyright 2013-2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "FrameTimingsScriptingInterface.h"
|
||||
|
||||
#include <TextureCache.h>
|
||||
|
||||
void FrameTimingsScriptingInterface::start() {
|
||||
_values.clear();
|
||||
DependencyManager::get<TextureCache>()->setUnusedResourceCacheSize(0);
|
||||
_values.reserve(8192);
|
||||
_active = true;
|
||||
}
|
||||
|
||||
void FrameTimingsScriptingInterface::addValue(uint64_t value) {
|
||||
if (_active) {
|
||||
_values.push_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
void FrameTimingsScriptingInterface::finish() {
|
||||
_active = false;
|
||||
uint64_t total = 0;
|
||||
_min = std::numeric_limits<uint64_t>::max();
|
||||
_max = std::numeric_limits<uint64_t>::lowest();
|
||||
size_t count = _values.size();
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
const uint64_t& value = _values[i];
|
||||
_max = std::max(_max, value);
|
||||
_min = std::min(_min, value);
|
||||
total += value;
|
||||
}
|
||||
_mean = (float)total / (float)count;
|
||||
float deviationTotal = 0;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
float deviation = _values[i] - _mean;
|
||||
deviationTotal += deviation*deviation;
|
||||
}
|
||||
_stdDev = sqrt(deviationTotal / (float)count);
|
||||
}
|
||||
|
||||
QVariantList FrameTimingsScriptingInterface::getValues() const {
|
||||
QVariantList result;
|
||||
for (quint64 v : _values) {
|
||||
result << QVariant(v);
|
||||
}
|
||||
return result;
|
||||
}
|
38
interface/src/FrameTimingsScriptingInterface.h
Normal file
38
interface/src/FrameTimingsScriptingInterface.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/04/04
|
||||
// Copyright 2013-2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <QtCore/QObject>
|
||||
|
||||
class FrameTimingsScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float mean READ getMean CONSTANT)
|
||||
Q_PROPERTY(float max READ getMax CONSTANT)
|
||||
Q_PROPERTY(float min READ getMin CONSTANT)
|
||||
Q_PROPERTY(float standardDeviation READ getStandardDeviation CONSTANT)
|
||||
public:
|
||||
Q_INVOKABLE void start();
|
||||
Q_INVOKABLE void addValue(uint64_t value);
|
||||
Q_INVOKABLE void finish();
|
||||
Q_INVOKABLE QVariantList getValues() const;
|
||||
|
||||
|
||||
uint64_t getMax() const { return _max; }
|
||||
uint64_t getMin() const { return _min; }
|
||||
float getStandardDeviation() const { return _stdDev; }
|
||||
float getMean() const { return _mean; }
|
||||
|
||||
protected:
|
||||
std::vector<uint64_t> _values;
|
||||
bool _active { false };
|
||||
uint64_t _max { 0 };
|
||||
uint64_t _min { 0 };
|
||||
float _stdDev { 0 };
|
||||
float _mean { 0 };
|
||||
};
|
|
@ -77,7 +77,6 @@ AvatarManager::AvatarManager(QObject* parent) :
|
|||
}
|
||||
|
||||
AvatarManager::~AvatarManager() {
|
||||
_myAvatar->die();
|
||||
}
|
||||
|
||||
void AvatarManager::init() {
|
||||
|
@ -250,6 +249,17 @@ void AvatarManager::clearOtherAvatars() {
|
|||
_myAvatar->clearLookAtTargetAvatar();
|
||||
}
|
||||
|
||||
void AvatarManager::clearAllAvatars() {
|
||||
clearOtherAvatars();
|
||||
|
||||
QWriteLocker locker(&_hashLock);
|
||||
|
||||
_myAvatar->die();
|
||||
_myAvatar.reset();
|
||||
|
||||
_avatarHash.clear();
|
||||
}
|
||||
|
||||
void AvatarManager::setLocalLights(const QVector<AvatarManager::LocalLight>& localLights) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setLocalLights", Q_ARG(const QVector<AvatarManager::LocalLight>&, localLights));
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
void updateOtherAvatars(float deltaTime);
|
||||
|
||||
void clearOtherAvatars();
|
||||
void clearAllAvatars();
|
||||
|
||||
bool shouldShowReceiveStats() const { return _shouldShowReceiveStats; }
|
||||
|
||||
|
|
|
@ -204,6 +204,15 @@ MyAvatar::~MyAvatar() {
|
|||
_lookAtTargetAvatar.reset();
|
||||
}
|
||||
|
||||
void MyAvatar::setOrientationVar(const QVariant& newOrientationVar) {
|
||||
Avatar::setOrientation(quatFromVariant(newOrientationVar));
|
||||
}
|
||||
|
||||
QVariant MyAvatar::getOrientationVar() const {
|
||||
return quatToVariant(Avatar::getOrientation());
|
||||
}
|
||||
|
||||
|
||||
// virtual
|
||||
void MyAvatar::simulateAttachments(float deltaTime) {
|
||||
// don't update attachments here, do it in harvestResultsFromPhysicsSimulation()
|
||||
|
|
|
@ -105,6 +105,10 @@ public:
|
|||
// thread safe
|
||||
Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const;
|
||||
|
||||
Q_INVOKABLE void setOrientationVar(const QVariant& newOrientationVar);
|
||||
Q_INVOKABLE QVariant getOrientationVar() const;
|
||||
|
||||
|
||||
// Pass a recent sample of the HMD to the avatar.
|
||||
// This can also update the avatar's position to follow the HMD
|
||||
// as it moves through the world.
|
||||
|
|
|
@ -926,7 +926,8 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) {
|
|||
glm::quat dQ = computeBulletRotationStep(localAngularVelocity, dt);
|
||||
rotation = glm::normalize(dQ * rotation);
|
||||
|
||||
setRotation(rotation);
|
||||
bool success;
|
||||
setOrientation(rotation, success, false);
|
||||
}
|
||||
|
||||
setLocalAngularVelocity(localAngularVelocity);
|
||||
|
@ -1983,10 +1984,10 @@ void EntityItem::locationChanged(bool tellPhysics) {
|
|||
requiresRecalcBoxes();
|
||||
if (tellPhysics) {
|
||||
_dirtyFlags |= Simulation::DIRTY_TRANSFORM;
|
||||
}
|
||||
EntityTreePointer tree = getTree();
|
||||
if (tree) {
|
||||
tree->entityChanged(getThisPointer());
|
||||
EntityTreePointer tree = getTree();
|
||||
if (tree) {
|
||||
tree->entityChanged(getThisPointer());
|
||||
}
|
||||
}
|
||||
SpatiallyNestable::locationChanged(tellPhysics); // tell all the children, also
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ void EntitySimulation::updateEntities() {
|
|||
callUpdateOnEntitiesThatNeedIt(now);
|
||||
moveSimpleKinematics(now);
|
||||
updateEntitiesInternal(now);
|
||||
PerformanceTimer perfTimer("sortingEntities");
|
||||
sortEntitiesThatMoved();
|
||||
}
|
||||
|
||||
|
@ -133,10 +134,8 @@ void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) {
|
|||
|
||||
// protected
|
||||
void EntitySimulation::sortEntitiesThatMoved() {
|
||||
QMutexLocker lock(&_mutex);
|
||||
// NOTE: this is only for entities that have been moved by THIS EntitySimulation.
|
||||
// External changes to entity position/shape are expected to be sorted outside of the EntitySimulation.
|
||||
PerformanceTimer perfTimer("sortingEntities");
|
||||
MovingEntitiesOperator moveOperator(_entityTree);
|
||||
AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE), (float)TREE_SCALE);
|
||||
SetOfEntities::iterator itemItr = _entitiesToSort.begin();
|
||||
|
|
|
@ -92,7 +92,7 @@ protected:
|
|||
|
||||
void expireMortalEntities(const quint64& now);
|
||||
void callUpdateOnEntitiesThatNeedIt(const quint64& now);
|
||||
void sortEntitiesThatMoved();
|
||||
virtual void sortEntitiesThatMoved();
|
||||
|
||||
QMutex _mutex{ QMutex::Recursive };
|
||||
|
||||
|
|
|
@ -132,3 +132,12 @@ void SimpleEntitySimulation::clearEntitiesInternal() {
|
|||
_entitiesThatNeedSimulationOwner.clear();
|
||||
}
|
||||
|
||||
void SimpleEntitySimulation::sortEntitiesThatMoved() {
|
||||
SetOfEntities::iterator itemItr = _entitiesToSort.begin();
|
||||
while (itemItr != _entitiesToSort.end()) {
|
||||
EntityItemPointer entity = *itemItr;
|
||||
entity->computePuffedQueryAACube();
|
||||
++itemItr;
|
||||
}
|
||||
EntitySimulation::sortEntitiesThatMoved();
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@ protected:
|
|||
virtual void changeEntityInternal(EntityItemPointer entity) override;
|
||||
virtual void clearEntitiesInternal() override;
|
||||
|
||||
virtual void sortEntitiesThatMoved() override;
|
||||
|
||||
SetOfEntities _entitiesWithSimulationOwner;
|
||||
SetOfEntities _entitiesThatNeedSimulationOwner;
|
||||
quint64 _nextOwnerlessExpiry { 0 };
|
||||
|
|
|
@ -126,7 +126,7 @@ ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect) {
|
|||
}
|
||||
|
||||
ShapeWrapperPtr loadSkybox(ProgramPtr program) {
|
||||
return ShapeWrapperPtr(new shapes::ShapeWrapper({ { "Position" } }, shapes::SkyBox(), *program));
|
||||
return ShapeWrapperPtr(new shapes::ShapeWrapper(std::initializer_list<std::string>{ "Position" }, shapes::SkyBox(), *program));
|
||||
}
|
||||
|
||||
// Return a point's cartesian coordinates on a sphere from pitch and yaw
|
||||
|
|
|
@ -139,7 +139,7 @@ public:
|
|||
GLuint _virtualSize; // theorical size as expected
|
||||
GLuint _numLevels{ 0 };
|
||||
|
||||
void transferMip(GLenum target, const Texture::PixelsPointer& mip) const;
|
||||
void transferMip(uint16_t mipLevel, uint8_t face = 0) const;
|
||||
|
||||
// The owning texture
|
||||
const Texture& _gpuTexture;
|
||||
|
|
|
@ -399,11 +399,9 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
|
|||
}
|
||||
break;
|
||||
case gpu::SRGB:
|
||||
texel.format = GL_RGB;
|
||||
texel.internalFormat = GL_SRGB8;
|
||||
break;
|
||||
case gpu::SRGBA:
|
||||
texel.format = GL_RGBA;
|
||||
texel.internalFormat = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color
|
||||
break;
|
||||
case gpu::COMPRESSED_RGBA:
|
||||
|
|
|
@ -93,25 +93,22 @@ void GLBackend::GLTexture::createTexture() {
|
|||
(void)CHECK_GL_ERROR();
|
||||
// Fixme: this usage of TexStorage doesn;t work wtih compressed texture, altuogh it should.
|
||||
// GO through the process of allocating the correct storage
|
||||
/* if (GLEW_VERSION_4_2 && !texture.getTexelFormat().isCompressed()) {
|
||||
glTexStorage2D(_target, _numLevels, texelFormat.internalFormat, width, height);
|
||||
(void)CHECK_GL_ERROR();
|
||||
} else*/
|
||||
{
|
||||
if (GLEW_VERSION_4_2 && !_gpuTexture.getTexelFormat().isCompressed()) {
|
||||
glTexStorage2D(_target, _numLevels, texelFormat.internalFormat, width, height);
|
||||
(void)CHECK_GL_ERROR();
|
||||
} else {
|
||||
glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, _numLevels - 1);
|
||||
|
||||
// for (int l = 0; l < _numLevels; l++) {
|
||||
{ int l = 0;
|
||||
if (_gpuTexture.getType() == gpu::Texture::TEX_CUBE) {
|
||||
for (size_t face = 0; face < CUBE_NUM_FACES; face++) {
|
||||
glTexImage2D(CUBE_FACE_LAYOUT[face], l, texelFormat.internalFormat, width, height, 0, texelFormat.format, texelFormat.type, NULL);
|
||||
for (uint16_t l = 0; l < _numLevels; l++) {
|
||||
if (_gpuTexture.getType() == gpu::Texture::TEX_CUBE) {
|
||||
for (size_t face = 0; face < CUBE_NUM_FACES; face++) {
|
||||
glTexImage2D(CUBE_FACE_LAYOUT[face], l, texelFormat.internalFormat, width, height, 0, texelFormat.format, texelFormat.type, NULL);
|
||||
}
|
||||
} else {
|
||||
glTexImage2D(_target, l, texelFormat.internalFormat, width, height, 0, texelFormat.format, texelFormat.type, NULL);
|
||||
}
|
||||
} else {
|
||||
glTexImage2D(_target, l, texelFormat.internalFormat, width, height, 0, texelFormat.format, texelFormat.type, NULL);
|
||||
}
|
||||
width = std::max(1, (width / 2));
|
||||
height = std::max(1, (height / 2));
|
||||
width = std::max(1, (width / 2));
|
||||
height = std::max(1, (height / 2));
|
||||
}
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
|
@ -213,9 +210,13 @@ bool GLBackend::GLTexture::isReady() const {
|
|||
}
|
||||
|
||||
// Move content bits from the CPU to the GPU for a given mip / face
|
||||
void GLBackend::GLTexture::transferMip(GLenum target, const Texture::PixelsPointer& mip) const {
|
||||
void GLBackend::GLTexture::transferMip(uint16_t mipLevel, uint8_t face) const {
|
||||
auto mip = _gpuTexture.accessStoredMipFace(mipLevel, face);
|
||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuTexture.getTexelFormat(), mip->getFormat());
|
||||
glTexSubImage2D(target, 0, 0, 0, _gpuTexture.getWidth(), _gpuTexture.getHeight(), texelFormat.format, texelFormat.type, mip->readData()); glTexSubImage2D(target, 0, 0, 0, _gpuTexture.getWidth(), _gpuTexture.getHeight(), texelFormat.format, texelFormat.type, mip->readData());
|
||||
GLenum target = _target == GL_TEXTURE_2D ? GL_TEXTURE_2D : CUBE_FACE_LAYOUT[face];
|
||||
uvec2 size = uvec2(_gpuTexture.getWidth(), _gpuTexture.getHeight());
|
||||
size >>= mipLevel;
|
||||
glTexSubImage2D(target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData());
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
|
@ -234,16 +235,20 @@ void GLBackend::GLTexture::transfer() const {
|
|||
// GO through the process of allocating the correct storage and/or update the content
|
||||
switch (_gpuTexture.getType()) {
|
||||
case Texture::TEX_2D:
|
||||
if (_gpuTexture.isStoredMipFaceAvailable(0)) {
|
||||
transferMip(GL_TEXTURE_2D, _gpuTexture.accessStoredMipFace(0));
|
||||
for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) {
|
||||
if (_gpuTexture.isStoredMipFaceAvailable(i)) {
|
||||
transferMip(i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Texture::TEX_CUBE:
|
||||
// transfer pixels from each faces
|
||||
for (uint8_t f = 0; f < CUBE_NUM_FACES; f++) {
|
||||
if (_gpuTexture.isStoredMipFaceAvailable(0, f)) {
|
||||
transferMip(CUBE_FACE_LAYOUT[f], _gpuTexture.accessStoredMipFace(0, f));
|
||||
for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) {
|
||||
if (_gpuTexture.isStoredMipFaceAvailable(i, f)) {
|
||||
transferMip(i, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -269,12 +274,21 @@ void GLBackend::GLTexture::postTransfer() {
|
|||
// At this point the mip pixels have been loaded, we can notify the gpu texture to abandon it's memory
|
||||
switch (_gpuTexture.getType()) {
|
||||
case Texture::TEX_2D:
|
||||
_gpuTexture.notifyMipFaceGPULoaded(0, 0);
|
||||
for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) {
|
||||
if (_gpuTexture.isStoredMipFaceAvailable(i)) {
|
||||
_gpuTexture.notifyMipFaceGPULoaded(i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Texture::TEX_CUBE:
|
||||
for (uint8_t f = 0; f < CUBE_NUM_FACES; ++f) {
|
||||
_gpuTexture.notifyMipFaceGPULoaded(0, f);
|
||||
// transfer pixels from each faces
|
||||
for (uint8_t f = 0; f < CUBE_NUM_FACES; f++) {
|
||||
for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) {
|
||||
if (_gpuTexture.isStoredMipFaceAvailable(i, f)) {
|
||||
_gpuTexture.notifyMipFaceGPULoaded(i, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -345,7 +359,7 @@ GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) {
|
|||
} else {
|
||||
object = Backend::getGPUObject<GLBackend::GLTexture>(*texture);
|
||||
}
|
||||
if (object) {
|
||||
if (object && object->getSyncState() == GLTexture::Idle) {
|
||||
return object->_texture;
|
||||
} else {
|
||||
return 0;
|
||||
|
|
|
@ -221,13 +221,6 @@ Texture* Texture::create(Type type, const Element& texelFormat, uint16 width, ui
|
|||
return tex;
|
||||
}
|
||||
|
||||
Texture* Texture::createFromStorage(Storage* storage) {
|
||||
Texture* tex = new Texture();
|
||||
tex->_storage.reset(storage);
|
||||
storage->assignTexture(tex);
|
||||
return tex;
|
||||
}
|
||||
|
||||
Texture::Texture():
|
||||
Resource()
|
||||
{
|
||||
|
|
|
@ -260,7 +260,7 @@ public:
|
|||
Stamp bumpStamp() { return ++_stamp; }
|
||||
protected:
|
||||
Stamp _stamp = 0;
|
||||
Texture* _texture = nullptr;
|
||||
Texture* _texture = nullptr; // Points to the parent texture (not owned)
|
||||
Texture::Type _type = Texture::TEX_2D; // The type of texture is needed to know the number of faces to expect
|
||||
std::vector<std::vector<PixelsPointer>> _mips; // an array of mips, each mip is an array of faces
|
||||
|
||||
|
@ -280,8 +280,6 @@ public:
|
|||
static Texture* create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler = Sampler());
|
||||
static Texture* createCube(const Element& texelFormat, uint16 width, const Sampler& sampler = Sampler());
|
||||
|
||||
static Texture* createFromStorage(Storage* storage);
|
||||
|
||||
Texture();
|
||||
Texture(const Texture& buf); // deep copy of the sysmem texture
|
||||
Texture& operator=(const Texture& buf); // deep copy of the sysmem texture
|
||||
|
@ -430,7 +428,7 @@ public:
|
|||
Stamp getSamplerStamp() const { return _samplerStamp; }
|
||||
|
||||
// Only callable by the Backend
|
||||
void notifyMipFaceGPULoaded(uint16 level, uint8 face) const { return _storage->notifyMipFaceGPULoaded(level, face); }
|
||||
void notifyMipFaceGPULoaded(uint16 level, uint8 face = 0) const { return _storage->notifyMipFaceGPULoaded(level, face); }
|
||||
|
||||
const GPUObjectPointer gpuObject {};
|
||||
|
||||
|
|
|
@ -154,21 +154,63 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, TextureType type
|
|||
return ResourceCache::getResource(url, QUrl(), content.isEmpty(), &extra).staticCast<NetworkTexture>();
|
||||
}
|
||||
|
||||
/// Returns a texture version of an image file
|
||||
gpu::TexturePointer TextureCache::getImageTexture(const QString& path) {
|
||||
QImage image = QImage(path).mirrored(false, true);
|
||||
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
|
||||
gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
|
||||
if (image.hasAlphaChannel()) {
|
||||
formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
|
||||
formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::BGRA);
|
||||
|
||||
TextureCache::TextureLoaderFunc getTextureLoaderForType(TextureType type) {
|
||||
switch (type) {
|
||||
case ALBEDO_TEXTURE: {
|
||||
return model::TextureUsage::createAlbedoTextureFromImage;
|
||||
break;
|
||||
}
|
||||
case EMISSIVE_TEXTURE: {
|
||||
return model::TextureUsage::createEmissiveTextureFromImage;
|
||||
break;
|
||||
}
|
||||
case LIGHTMAP_TEXTURE: {
|
||||
return model::TextureUsage::createLightmapTextureFromImage;
|
||||
break;
|
||||
}
|
||||
case CUBE_TEXTURE: {
|
||||
return model::TextureUsage::createCubeTextureFromImage;
|
||||
break;
|
||||
}
|
||||
case BUMP_TEXTURE: {
|
||||
return model::TextureUsage::createNormalTextureFromBumpImage;
|
||||
break;
|
||||
}
|
||||
case NORMAL_TEXTURE: {
|
||||
return model::TextureUsage::createNormalTextureFromNormalImage;
|
||||
break;
|
||||
}
|
||||
case ROUGHNESS_TEXTURE: {
|
||||
return model::TextureUsage::createRoughnessTextureFromImage;
|
||||
break;
|
||||
}
|
||||
case GLOSS_TEXTURE: {
|
||||
return model::TextureUsage::createRoughnessTextureFromGlossImage;
|
||||
break;
|
||||
}
|
||||
case SPECULAR_TEXTURE: {
|
||||
return model::TextureUsage::createMetallicTextureFromImage;
|
||||
break;
|
||||
}
|
||||
case CUSTOM_TEXTURE: {
|
||||
Q_ASSERT(false);
|
||||
return TextureCache::TextureLoaderFunc();
|
||||
break;
|
||||
}
|
||||
case DEFAULT_TEXTURE:
|
||||
default: {
|
||||
return model::TextureUsage::create2DTextureFromImage;
|
||||
break;
|
||||
}
|
||||
}
|
||||
gpu::TexturePointer texture = gpu::TexturePointer(
|
||||
gpu::Texture::create2D(formatGPU, image.width(), image.height(),
|
||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
texture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits());
|
||||
texture->autoGenerateMips(-1);
|
||||
return texture;
|
||||
}
|
||||
|
||||
/// Returns a texture version of an image file
|
||||
gpu::TexturePointer TextureCache::getImageTexture(const QString& path, TextureType type) {
|
||||
QImage image = QImage(path);
|
||||
auto loader = getTextureLoaderForType(type);
|
||||
return gpu::TexturePointer(loader(image, QUrl::fromLocalFile(path).fileName().toStdString()));
|
||||
}
|
||||
|
||||
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url,
|
||||
|
@ -203,53 +245,10 @@ NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& texture
|
|||
}
|
||||
|
||||
NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
|
||||
switch (_type) {
|
||||
case ALBEDO_TEXTURE: {
|
||||
return TextureLoaderFunc(model::TextureUsage::createAlbedoTextureFromImage);
|
||||
break;
|
||||
}
|
||||
case EMISSIVE_TEXTURE: {
|
||||
return TextureLoaderFunc(model::TextureUsage::createEmissiveTextureFromImage);
|
||||
break;
|
||||
}
|
||||
case LIGHTMAP_TEXTURE: {
|
||||
return TextureLoaderFunc(model::TextureUsage::createLightmapTextureFromImage);
|
||||
break;
|
||||
}
|
||||
case CUBE_TEXTURE: {
|
||||
return TextureLoaderFunc(model::TextureUsage::createCubeTextureFromImage);
|
||||
break;
|
||||
}
|
||||
case BUMP_TEXTURE: {
|
||||
return TextureLoaderFunc(model::TextureUsage::createNormalTextureFromBumpImage);
|
||||
break;
|
||||
}
|
||||
case NORMAL_TEXTURE: {
|
||||
return TextureLoaderFunc(model::TextureUsage::createNormalTextureFromNormalImage);
|
||||
break;
|
||||
}
|
||||
case ROUGHNESS_TEXTURE: {
|
||||
return TextureLoaderFunc(model::TextureUsage::createRoughnessTextureFromImage);
|
||||
break;
|
||||
}
|
||||
case GLOSS_TEXTURE: {
|
||||
return TextureLoaderFunc(model::TextureUsage::createRoughnessTextureFromGlossImage);
|
||||
break;
|
||||
}
|
||||
case SPECULAR_TEXTURE: {
|
||||
return TextureLoaderFunc(model::TextureUsage::createMetallicTextureFromImage);
|
||||
break;
|
||||
}
|
||||
case CUSTOM_TEXTURE: {
|
||||
return _textureLoader;
|
||||
break;
|
||||
}
|
||||
case DEFAULT_TEXTURE:
|
||||
default: {
|
||||
return TextureLoaderFunc(model::TextureUsage::create2DTextureFromImage);
|
||||
break;
|
||||
}
|
||||
if (_type == CUSTOM_TEXTURE) {
|
||||
return _textureLoader;
|
||||
}
|
||||
return getTextureLoaderForType(_type);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ public:
|
|||
const gpu::TexturePointer& getNormalFittingTexture();
|
||||
|
||||
/// Returns a texture version of an image file
|
||||
static gpu::TexturePointer getImageTexture(const QString& path);
|
||||
static gpu::TexturePointer getImageTexture(const QString& path, TextureType type = DEFAULT_TEXTURE);
|
||||
|
||||
/// Loads a texture from the specified URL.
|
||||
NetworkTexturePointer getTexture(const QUrl& url, TextureType type = DEFAULT_TEXTURE,
|
||||
|
|
|
@ -150,8 +150,8 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag
|
|||
QImage image = process2DImageColor(srcImage, validAlpha, alphaAsMask);
|
||||
|
||||
gpu::Texture* theTexture = nullptr;
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
gpu::Element formatGPU;
|
||||
gpu::Element formatMip;
|
||||
defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress);
|
||||
|
@ -171,6 +171,14 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag
|
|||
|
||||
if (generateMips) {
|
||||
theTexture->autoGenerateMips(-1);
|
||||
auto levels = theTexture->maxMip();
|
||||
uvec2 size(image.width(), image.height());
|
||||
for (uint8_t i = 1; i <= levels; ++i) {
|
||||
size >>= 1;
|
||||
size = glm::max(size, uvec2(1));
|
||||
image = image.scaled(size.x, size.y, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
theTexture->assignStoredMip(i, formatMip, image.byteCount(), image.constBits());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,7 +216,6 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src
|
|||
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
|
||||
gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
|
||||
|
||||
|
||||
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits());
|
||||
theTexture->autoGenerateMips(-1);
|
||||
|
@ -232,10 +239,11 @@ double mapComponent(double sobelValue) {
|
|||
gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||
QImage image = srcImage;
|
||||
|
||||
if (image.format() != QImage::Format_RGB888) {
|
||||
image = image.convertToFormat(QImage::Format_RGB888);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// PR 5540 by AlessandroSigna
|
||||
// integrated here as a specialized TextureLoader for bumpmaps
|
||||
// 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();
|
||||
|
@ -284,15 +292,13 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm
|
|||
result.setPixel(i, j, qRgbValue);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
gpu::Texture* theTexture = nullptr;
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
|
||||
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
|
||||
gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
|
||||
|
||||
|
||||
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits());
|
||||
theTexture->autoGenerateMips(-1);
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "AssetUtils.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QtCore/QCryptographicHash>
|
||||
#include <QtNetwork/QAbstractNetworkCache>
|
||||
|
||||
|
@ -29,12 +31,15 @@ QByteArray hashData(const QByteArray& data) {
|
|||
|
||||
QByteArray loadFromCache(const QUrl& url) {
|
||||
if (auto cache = NetworkAccessManager::getInstance().cache()) {
|
||||
if (auto ioDevice = cache->data(url)) {
|
||||
|
||||
// caller is responsible for the deletion of the ioDevice, hence the unique_ptr
|
||||
if (auto ioDevice = std::unique_ptr<QIODevice>(cache->data(url))) {
|
||||
qCDebug(asset_client) << url.toDisplayString() << "loaded from disk cache.";
|
||||
return ioDevice->readAll();
|
||||
} else {
|
||||
qCDebug(asset_client) << url.toDisplayString() << "not in disk cache";
|
||||
}
|
||||
|
||||
} else {
|
||||
qCWarning(asset_client) << "No disk cache to load assets from.";
|
||||
}
|
||||
|
@ -49,7 +54,8 @@ bool saveToCache(const QUrl& url, const QByteArray& file) {
|
|||
metaData.setSaveToDisk(true);
|
||||
metaData.setLastModified(QDateTime::currentDateTime());
|
||||
metaData.setExpirationDate(QDateTime()); // Never expires
|
||||
|
||||
|
||||
// ioDevice is managed by the cache and should either be passed back to insert or remove!
|
||||
if (auto ioDevice = cache->prepare(metaData)) {
|
||||
ioDevice->write(file);
|
||||
cache->insert(ioDevice);
|
||||
|
|
|
@ -28,6 +28,97 @@
|
|||
(((x) > (max)) ? (max) :\
|
||||
(x)))
|
||||
|
||||
void ResourceCacheSharedItems::appendActiveRequest(QWeakPointer<Resource> resource) {
|
||||
Lock lock(_mutex);
|
||||
_loadingRequests.append(resource);
|
||||
}
|
||||
|
||||
void ResourceCacheSharedItems::appendPendingRequest(QWeakPointer<Resource> resource) {
|
||||
Lock lock(_mutex);
|
||||
_pendingRequests.append(resource);
|
||||
}
|
||||
|
||||
QList<QSharedPointer<Resource>> ResourceCacheSharedItems::getPendingRequests() {
|
||||
QList<QSharedPointer<Resource>> result;
|
||||
|
||||
{
|
||||
Lock lock(_mutex);
|
||||
foreach(QSharedPointer<Resource> resource, _pendingRequests) {
|
||||
if (resource) {
|
||||
result.append(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t ResourceCacheSharedItems::getPendingRequestsCount() const {
|
||||
Lock lock(_mutex);
|
||||
return _pendingRequests.size();
|
||||
}
|
||||
|
||||
QList<QSharedPointer<Resource>> ResourceCacheSharedItems::getLoadingRequests() {
|
||||
QList<QSharedPointer<Resource>> result;
|
||||
|
||||
{
|
||||
Lock lock(_mutex);
|
||||
foreach(QSharedPointer<Resource> resource, _loadingRequests) {
|
||||
if (resource) {
|
||||
result.append(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ResourceCacheSharedItems::removeRequest(QWeakPointer<Resource> resource) {
|
||||
Lock lock(_mutex);
|
||||
// resource can only be removed if it still has a ref-count, as
|
||||
// QWeakPointer has no operator== implementation for two weak ptrs, so
|
||||
// manually loop in case resource has been freed.
|
||||
for (int i = 0; i < _loadingRequests.size();) {
|
||||
auto request = _loadingRequests.at(i);
|
||||
// Clear our resource and any freed resources
|
||||
if (!request || request.data() == resource.data()) {
|
||||
_loadingRequests.removeAt(i);
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
QSharedPointer<Resource> ResourceCacheSharedItems::getHighestPendingRequest() {
|
||||
Lock lock(_mutex);
|
||||
// look for the highest priority pending request
|
||||
int highestIndex = -1;
|
||||
float highestPriority = -FLT_MAX;
|
||||
QSharedPointer<Resource> highestResource;
|
||||
|
||||
for (int i = 0; i < _pendingRequests.size();) {
|
||||
// Clear any freed resources
|
||||
auto resource = _pendingRequests.at(i).lock();
|
||||
if (!resource) {
|
||||
_pendingRequests.removeAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check load priority
|
||||
float priority = resource->getLoadPriority();
|
||||
if (priority >= highestPriority) {
|
||||
highestPriority = priority;
|
||||
highestIndex = i;
|
||||
highestResource = resource;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (highestIndex >= 0) {
|
||||
_pendingRequests.takeAt(highestIndex);
|
||||
}
|
||||
|
||||
return highestResource;
|
||||
}
|
||||
|
||||
ResourceCache::ResourceCache(QObject* parent) : QObject(parent) {
|
||||
auto& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
|
||||
connect(&domainHandler, &DomainHandler::disconnectedFromDomain,
|
||||
|
@ -101,8 +192,7 @@ void ResourceCache::refresh(const QUrl& url) {
|
|||
if (resource) {
|
||||
resource->refresh();
|
||||
} else {
|
||||
QWriteLocker locker(&_resourcesLock);
|
||||
_resources.remove(url);
|
||||
removeResource(url);
|
||||
resetResourceCounters();
|
||||
}
|
||||
}
|
||||
|
@ -196,11 +286,8 @@ void ResourceCache::addUnusedResource(const QSharedPointer<Resource>& resource)
|
|||
// If it doesn't fit or its size is unknown, remove it from the cache.
|
||||
if (resource->getBytes() == 0 || resource->getBytes() > _unusedResourcesMaxSize) {
|
||||
resource->setCache(nullptr);
|
||||
|
||||
_totalResourcesSize -= resource->getBytes();
|
||||
_resources.remove(resource->getURL());
|
||||
removeResource(resource->getURL(), resource->getBytes());
|
||||
resetResourceCounters();
|
||||
|
||||
return;
|
||||
}
|
||||
reserveUnusedResource(resource->getBytes());
|
||||
|
@ -233,8 +320,7 @@ void ResourceCache::reserveUnusedResource(qint64 resourceSize) {
|
|||
it.value()->setCache(nullptr);
|
||||
auto size = it.value()->getBytes();
|
||||
|
||||
_totalResourcesSize -= size;
|
||||
_resources.remove(it.value()->getURL());
|
||||
removeResource(it.value()->getURL(), size);
|
||||
|
||||
_unusedResourcesSize -= size;
|
||||
_unusedResources.erase(it);
|
||||
|
@ -259,85 +345,17 @@ void ResourceCache::resetResourceCounters() {
|
|||
emit dirty();
|
||||
}
|
||||
|
||||
void ResourceCache::removeResource(const QUrl& url, qint64 size) {
|
||||
QWriteLocker locker(&_resourcesLock);
|
||||
_resources.remove(url);
|
||||
_totalResourcesSize -= size;
|
||||
}
|
||||
|
||||
void ResourceCache::updateTotalSize(const qint64& oldSize, const qint64& newSize) {
|
||||
_totalResourcesSize += (newSize - oldSize);
|
||||
emit dirty();
|
||||
}
|
||||
|
||||
void ResourceCacheSharedItems::appendActiveRequest(QWeakPointer<Resource> resource) {
|
||||
Lock lock(_mutex);
|
||||
_loadingRequests.append(resource);
|
||||
}
|
||||
|
||||
void ResourceCacheSharedItems::appendPendingRequest(QWeakPointer<Resource> resource) {
|
||||
Lock lock(_mutex);
|
||||
_pendingRequests.append(resource);
|
||||
}
|
||||
|
||||
QList<QSharedPointer<Resource>> ResourceCacheSharedItems::getPendingRequests() {
|
||||
QList<QSharedPointer<Resource>> result;
|
||||
|
||||
{
|
||||
Lock lock(_mutex);
|
||||
foreach(QSharedPointer<Resource> resource, _pendingRequests) {
|
||||
if (resource) {
|
||||
result.append(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t ResourceCacheSharedItems::getPendingRequestsCount() const {
|
||||
Lock lock(_mutex);
|
||||
return _pendingRequests.size();
|
||||
}
|
||||
|
||||
QList<QSharedPointer<Resource>> ResourceCacheSharedItems::getLoadingRequests() {
|
||||
QList<QSharedPointer<Resource>> result;
|
||||
|
||||
{
|
||||
Lock lock(_mutex);
|
||||
foreach(QSharedPointer<Resource> resource, _loadingRequests) {
|
||||
if (resource) {
|
||||
result.append(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ResourceCacheSharedItems::removeRequest(QWeakPointer<Resource> resource) {
|
||||
Lock lock(_mutex);
|
||||
_loadingRequests.removeAll(resource);
|
||||
}
|
||||
|
||||
QSharedPointer<Resource> ResourceCacheSharedItems::getHighestPendingRequest() {
|
||||
Lock lock(_mutex);
|
||||
// look for the highest priority pending request
|
||||
int highestIndex = -1;
|
||||
float highestPriority = -FLT_MAX;
|
||||
QSharedPointer<Resource> highestResource;
|
||||
for (int i = 0; i < _pendingRequests.size();) {
|
||||
auto resource = _pendingRequests.at(i).lock();
|
||||
if (!resource) {
|
||||
_pendingRequests.removeAt(i);
|
||||
continue;
|
||||
}
|
||||
float priority = resource->getLoadPriority();
|
||||
if (priority >= highestPriority) {
|
||||
highestPriority = priority;
|
||||
highestIndex = i;
|
||||
highestResource = resource;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (highestIndex >= 0) {
|
||||
_pendingRequests.takeAt(highestIndex);
|
||||
}
|
||||
return highestResource;
|
||||
}
|
||||
|
||||
|
||||
QList<QSharedPointer<Resource>> ResourceCache::getLoadingRequests() {
|
||||
return DependencyManager::get<ResourceCacheSharedItems>()->getLoadingRequests();
|
||||
}
|
||||
|
@ -454,12 +472,12 @@ void Resource::refresh() {
|
|||
}
|
||||
|
||||
void Resource::allReferencesCleared() {
|
||||
if (_cache && isCacheable()) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "allReferencesCleared");
|
||||
return;
|
||||
}
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "allReferencesCleared");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_cache && isCacheable()) {
|
||||
// create and reinsert new shared pointer
|
||||
QSharedPointer<Resource> self(this, &Resource::allReferencesCleared);
|
||||
setSelf(self);
|
||||
|
@ -467,8 +485,13 @@ void Resource::allReferencesCleared() {
|
|||
|
||||
// add to the unused list
|
||||
_cache->addUnusedResource(self);
|
||||
|
||||
} else {
|
||||
if (_cache) {
|
||||
// remove from the cache
|
||||
_cache->removeResource(getURL(), getBytes());
|
||||
_cache->resetResourceCounters();
|
||||
}
|
||||
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -153,6 +153,7 @@ private:
|
|||
void reserveUnusedResource(qint64 resourceSize);
|
||||
void clearUnusedResource();
|
||||
void resetResourceCounters();
|
||||
void removeResource(const QUrl& url, qint64 size = 0);
|
||||
|
||||
QReadWriteLock _resourcesLock { QReadWriteLock::Recursive };
|
||||
QHash<QUrl, QWeakPointer<Resource>> _resources;
|
||||
|
|
|
@ -620,7 +620,7 @@ glm::vec3 SpatiallyNestable::getLocalPosition() const {
|
|||
return result;
|
||||
}
|
||||
|
||||
void SpatiallyNestable::setLocalPosition(const glm::vec3& position) {
|
||||
void SpatiallyNestable::setLocalPosition(const glm::vec3& position, bool tellPhysics) {
|
||||
// guard against introducing NaN into the transform
|
||||
if (isNaN(position)) {
|
||||
qDebug() << "SpatiallyNestable::setLocalPosition -- position contains NaN";
|
||||
|
@ -629,7 +629,7 @@ void SpatiallyNestable::setLocalPosition(const glm::vec3& position) {
|
|||
_transformLock.withWriteLock([&] {
|
||||
_transform.setTranslation(position);
|
||||
});
|
||||
locationChanged();
|
||||
locationChanged(tellPhysics);
|
||||
}
|
||||
|
||||
glm::quat SpatiallyNestable::getLocalOrientation() const {
|
||||
|
|
|
@ -102,7 +102,7 @@ public:
|
|||
virtual void setLocalTransform(const Transform& transform);
|
||||
|
||||
virtual glm::vec3 getLocalPosition() const;
|
||||
virtual void setLocalPosition(const glm::vec3& position);
|
||||
virtual void setLocalPosition(const glm::vec3& position, bool tellPhysics = true);
|
||||
|
||||
virtual glm::quat getLocalOrientation() const;
|
||||
virtual void setLocalOrientation(const glm::quat& orientation);
|
||||
|
|
|
@ -22,10 +22,10 @@ class RateCounter {
|
|||
public:
|
||||
void increment(size_t count = 1) {
|
||||
auto now = usecTimestampNow();
|
||||
auto currentIntervalMs = (uint32_t)((now - _start) / USECS_PER_MSEC);
|
||||
if (currentIntervalMs > INTERVAL) {
|
||||
float currentIntervalMs = (now - _start) / (float) USECS_PER_MSEC;
|
||||
if (currentIntervalMs > (float) INTERVAL) {
|
||||
float currentCount = _count;
|
||||
float intervalSeconds = (float)currentIntervalMs / (float)MSECS_PER_SECOND;
|
||||
float intervalSeconds = currentIntervalMs / (float) MSECS_PER_SECOND;
|
||||
_rate = roundf(currentCount / intervalSeconds * _scale) / _scale;
|
||||
_start = now;
|
||||
_count = 0;
|
||||
|
|
|
@ -143,6 +143,13 @@ void OffscreenUi::toggle(const QUrl& url, const QString& name, std::function<voi
|
|||
}
|
||||
}
|
||||
|
||||
void OffscreenUi::hide(const QString& name) {
|
||||
QQuickItem* item = getRootItem()->findChild<QQuickItem*>(name);
|
||||
if (item) {
|
||||
item->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
class ModalDialogListener : public QObject {
|
||||
Q_OBJECT
|
||||
friend class OffscreenUi;
|
||||
|
|
|
@ -37,6 +37,7 @@ public:
|
|||
virtual void create(QOpenGLContext* context) override;
|
||||
void createDesktop(const QUrl& url);
|
||||
void show(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
|
||||
void hide(const QString& name);
|
||||
void toggle(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
|
||||
bool shouldSwallowShortcut(QEvent* event);
|
||||
bool navigationFocused();
|
||||
|
|
|
@ -38,9 +38,11 @@
|
|||
|
||||
#include <GLMHelpers.h>
|
||||
#include <PathUtils.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
#include <GeometryCache.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <TextureCache.h>
|
||||
|
||||
#include "unlit_frag.h"
|
||||
#include "unlit_vert.h"
|
||||
|
@ -83,6 +85,93 @@ public:
|
|||
|
||||
uint32_t toCompactColor(const glm::vec4& color);
|
||||
|
||||
|
||||
const char* VERTEX_SHADER = R"SHADER(
|
||||
#version 450 core
|
||||
|
||||
layout(location = 0) in vec4 inPosition;
|
||||
layout(location = 3) in vec2 inTexCoord0;
|
||||
|
||||
struct TransformObject {
|
||||
mat4 _model;
|
||||
mat4 _modelInverse;
|
||||
};
|
||||
|
||||
layout(location=15) in ivec2 _drawCallInfo;
|
||||
|
||||
uniform samplerBuffer transformObjectBuffer;
|
||||
|
||||
TransformObject getTransformObject() {
|
||||
int offset = 8 * _drawCallInfo.x;
|
||||
TransformObject object;
|
||||
object._model[0] = texelFetch(transformObjectBuffer, offset);
|
||||
object._model[1] = texelFetch(transformObjectBuffer, offset + 1);
|
||||
object._model[2] = texelFetch(transformObjectBuffer, offset + 2);
|
||||
object._model[3] = texelFetch(transformObjectBuffer, offset + 3);
|
||||
|
||||
object._modelInverse[0] = texelFetch(transformObjectBuffer, offset + 4);
|
||||
object._modelInverse[1] = texelFetch(transformObjectBuffer, offset + 5);
|
||||
object._modelInverse[2] = texelFetch(transformObjectBuffer, offset + 6);
|
||||
object._modelInverse[3] = texelFetch(transformObjectBuffer, offset + 7);
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
struct TransformCamera {
|
||||
mat4 _view;
|
||||
mat4 _viewInverse;
|
||||
mat4 _projectionViewUntranslated;
|
||||
mat4 _projection;
|
||||
mat4 _projectionInverse;
|
||||
vec4 _viewport;
|
||||
};
|
||||
|
||||
layout(std140) uniform transformCameraBuffer {
|
||||
TransformCamera _camera;
|
||||
};
|
||||
|
||||
TransformCamera getTransformCamera() {
|
||||
return _camera;
|
||||
}
|
||||
|
||||
// the interpolated normal
|
||||
out vec2 _texCoord0;
|
||||
|
||||
void main(void) {
|
||||
_texCoord0 = inTexCoord0.st;
|
||||
|
||||
// standard transform
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
{ // transformModelToClipPos
|
||||
vec4 eyeWAPos;
|
||||
{ // _transformModelToEyeWorldAlignedPos
|
||||
highp mat4 _mv = obj._model;
|
||||
_mv[3].xyz -= cam._viewInverse[3].xyz;
|
||||
highp vec4 _eyeWApos = (_mv * inPosition);
|
||||
eyeWAPos = _eyeWApos;
|
||||
}
|
||||
gl_Position = cam._projectionViewUntranslated * eyeWAPos;
|
||||
}
|
||||
|
||||
})SHADER";
|
||||
|
||||
const char* FRAGMENT_SHADER = R"SHADER(
|
||||
#version 450 core
|
||||
|
||||
uniform sampler2D originalTexture;
|
||||
|
||||
in vec2 _texCoord0;
|
||||
|
||||
layout(location = 0) out vec4 _fragColor0;
|
||||
|
||||
void main(void) {
|
||||
//_fragColor0 = vec4(_texCoord0, 0.0, 1.0);
|
||||
_fragColor0 = texture(originalTexture, _texCoord0);
|
||||
}
|
||||
)SHADER";
|
||||
|
||||
|
||||
gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings) {
|
||||
auto vs = gpu::Shader::createVertex(vertexShaderSrc);
|
||||
auto fs = gpu::Shader::createPixel(fragmentShaderSrc);
|
||||
|
@ -125,6 +214,7 @@ class QTestWindow : public QWindow {
|
|||
glm::mat4 _projectionMatrix;
|
||||
RateCounter fps;
|
||||
QTime _time;
|
||||
glm::mat4 _camera;
|
||||
|
||||
protected:
|
||||
void renderText();
|
||||
|
@ -145,7 +235,7 @@ public:
|
|||
setGLFormatVersion(format);
|
||||
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
|
||||
format.setOption(QSurfaceFormat::DebugContext);
|
||||
format.setSwapInterval(0);
|
||||
//format.setSwapInterval(0);
|
||||
|
||||
setFormat(format);
|
||||
|
||||
|
@ -158,19 +248,22 @@ public:
|
|||
|
||||
gpu::Context::init<gpu::GLBackend>();
|
||||
_context = std::make_shared<gpu::Context>();
|
||||
|
||||
makeCurrent();
|
||||
auto shader = makeShader(unlit_vert, unlit_frag, gpu::Shader::BindingSet{});
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setMultisampleEnable(true);
|
||||
state->setDepthTest(gpu::State::DepthTest { true });
|
||||
_pipeline = gpu::Pipeline::create(shader, state);
|
||||
|
||||
|
||||
|
||||
// Clear screen
|
||||
gpu::Batch batch;
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 1.0, 0.0, 0.5, 1.0 });
|
||||
_context->render(batch);
|
||||
|
||||
DependencyManager::set<GeometryCache>();
|
||||
DependencyManager::set<TextureCache>();
|
||||
DependencyManager::set<DeferredLightingEffect>();
|
||||
|
||||
resize(QSize(800, 600));
|
||||
|
@ -181,182 +274,227 @@ public:
|
|||
virtual ~QTestWindow() {
|
||||
}
|
||||
|
||||
void updateCamera() {
|
||||
float t = _time.elapsed() * 1e-4f;
|
||||
glm::vec3 unitscale { 1.0f };
|
||||
glm::vec3 up { 0.0f, 1.0f, 0.0f };
|
||||
|
||||
float distance = 3.0f;
|
||||
glm::vec3 camera_position { distance * sinf(t), 0.5f, distance * cosf(t) };
|
||||
|
||||
static const vec3 camera_focus(0);
|
||||
static const vec3 camera_up(0, 1, 0);
|
||||
_camera = glm::inverse(glm::lookAt(camera_position, camera_focus, up));
|
||||
}
|
||||
|
||||
|
||||
void drawFloorGrid(gpu::Batch& batch) {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
// Render grid on xz plane (not the optimal way to do things, but w/e)
|
||||
// Note: GeometryCache::renderGrid will *not* work, as it is apparenly unaffected by batch rotations and renders xy only
|
||||
static const std::string GRID_INSTANCE = "Grid";
|
||||
static auto compactColor1 = toCompactColor(vec4 { 0.35f, 0.25f, 0.15f, 1.0f });
|
||||
static auto compactColor2 = toCompactColor(vec4 { 0.15f, 0.25f, 0.35f, 1.0f });
|
||||
static std::vector<glm::mat4> transforms;
|
||||
static gpu::BufferPointer colorBuffer;
|
||||
if (!transforms.empty()) {
|
||||
transforms.reserve(200);
|
||||
colorBuffer = std::make_shared<gpu::Buffer>();
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
{
|
||||
glm::mat4 transform = glm::translate(mat4(), vec3(0, -1, -50 + i));
|
||||
transform = glm::scale(transform, vec3(100, 1, 1));
|
||||
transforms.push_back(transform);
|
||||
colorBuffer->append(compactColor1);
|
||||
}
|
||||
|
||||
{
|
||||
glm::mat4 transform = glm::mat4_cast(quat(vec3(0, PI / 2.0f, 0)));
|
||||
transform = glm::translate(transform, vec3(0, -1, -50 + i));
|
||||
transform = glm::scale(transform, vec3(100, 1, 1));
|
||||
transforms.push_back(transform);
|
||||
colorBuffer->append(compactColor2);
|
||||
}
|
||||
}
|
||||
}
|
||||
auto pipeline = geometryCache->getSimplePipeline();
|
||||
for (auto& transform : transforms) {
|
||||
batch.setModelTransform(transform);
|
||||
batch.setupNamedCalls(GRID_INSTANCE, [=](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) {
|
||||
batch.setViewTransform(_camera);
|
||||
batch.setPipeline(_pipeline);
|
||||
geometryCache->renderWireShapeInstances(batch, GeometryCache::Line, data.count(), colorBuffer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void drawSimpleShapes(gpu::Batch& batch) {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
static const size_t ITEM_COUNT = 1000;
|
||||
static const float SHAPE_INTERVAL = (PI * 2.0f) / ITEM_COUNT;
|
||||
static const float ITEM_INTERVAL = SHAPE_INTERVAL / TYPE_COUNT;
|
||||
|
||||
static const gpu::Element POSITION_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };
|
||||
static const gpu::Element NORMAL_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };
|
||||
static const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA };
|
||||
|
||||
static std::vector<Transform> transforms;
|
||||
static std::vector<vec4> colors;
|
||||
static gpu::BufferPointer colorBuffer;
|
||||
static gpu::BufferView colorView;
|
||||
static gpu::BufferView instanceXfmView;
|
||||
if (!colorBuffer) {
|
||||
colorBuffer = std::make_shared<gpu::Buffer>();
|
||||
|
||||
static const float ITEM_RADIUS = 20;
|
||||
static const vec3 ITEM_TRANSLATION { 0, 0, -ITEM_RADIUS };
|
||||
for (size_t i = 0; i < TYPE_COUNT; ++i) {
|
||||
GeometryCache::Shape shape = SHAPE[i];
|
||||
GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape];
|
||||
//indirectCommand._count
|
||||
float startingInterval = ITEM_INTERVAL * i;
|
||||
for (size_t j = 0; j < ITEM_COUNT; ++j) {
|
||||
float theta = j * SHAPE_INTERVAL + startingInterval;
|
||||
auto transform = glm::rotate(mat4(), theta, Vectors::UP);
|
||||
transform = glm::rotate(transform, (randFloat() - 0.5f) * PI / 4.0f, Vectors::UNIT_X);
|
||||
transform = glm::translate(transform, ITEM_TRANSLATION);
|
||||
transform = glm::scale(transform, vec3(randFloat() / 2.0f + 0.5f));
|
||||
transforms.push_back(transform);
|
||||
auto color = vec4 { randomColorValue(64), randomColorValue(64), randomColorValue(64), 255 };
|
||||
color /= 255.0f;
|
||||
colors.push_back(color);
|
||||
colorBuffer->append(toCompactColor(color));
|
||||
}
|
||||
}
|
||||
colorView = gpu::BufferView(colorBuffer, COLOR_ELEMENT);
|
||||
}
|
||||
|
||||
batch.setViewTransform(_camera);
|
||||
batch.setPipeline(_pipeline);
|
||||
batch.setInputFormat(getInstancedSolidStreamFormat());
|
||||
for (size_t i = 0; i < TYPE_COUNT; ++i) {
|
||||
GeometryCache::Shape shape = SHAPE[i];
|
||||
GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape];
|
||||
batch.setInputBuffer(gpu::Stream::COLOR, colorView);
|
||||
for (size_t j = 0; j < ITEM_COUNT; ++j) {
|
||||
batch.setModelTransform(transforms[j]);
|
||||
shapeData.draw(batch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawCenterShape(gpu::Batch& batch) {
|
||||
// Render unlit cube + sphere
|
||||
static auto startUsecs = usecTimestampNow();
|
||||
float seconds = getSeconds(startUsecs);
|
||||
seconds /= 4.0f;
|
||||
batch.setModelTransform(Transform());
|
||||
batch._glColor4f(0.8f, 0.25f, 0.25f, 1.0f);
|
||||
|
||||
bool wire = (seconds - floorf(seconds) > 0.5f);
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
int shapeIndex = ((int)seconds) % TYPE_COUNT;
|
||||
if (wire) {
|
||||
geometryCache->renderWireShape(batch, SHAPE[shapeIndex]);
|
||||
} else {
|
||||
geometryCache->renderShape(batch, SHAPE[shapeIndex]);
|
||||
}
|
||||
|
||||
batch.setModelTransform(Transform().setScale(2.05f));
|
||||
batch._glColor4f(1, 1, 1, 1);
|
||||
geometryCache->renderWireCube(batch);
|
||||
}
|
||||
|
||||
void drawTerrain(gpu::Batch& batch) {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
static std::once_flag once;
|
||||
static gpu::BufferPointer vertexBuffer { std::make_shared<gpu::Buffer>() };
|
||||
static gpu::BufferPointer indexBuffer { std::make_shared<gpu::Buffer>() };
|
||||
|
||||
static gpu::BufferView positionView;
|
||||
static gpu::BufferView textureView;
|
||||
static gpu::Stream::FormatPointer vertexFormat { std::make_shared<gpu::Stream::Format>() };
|
||||
|
||||
static gpu::TexturePointer texture;
|
||||
static gpu::PipelinePointer pipeline;
|
||||
std::call_once(once, [&] {
|
||||
static const uint SHAPE_VERTEX_STRIDE = sizeof(glm::vec4) * 2; // position, normals, textures
|
||||
static const uint SHAPE_TEXTURES_OFFSET = sizeof(glm::vec4);
|
||||
static const gpu::Element POSITION_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };
|
||||
static const gpu::Element TEXTURE_ELEMENT { gpu::VEC2, gpu::FLOAT, gpu::UV };
|
||||
std::vector<vec4> vertices;
|
||||
const int MINX = -1000;
|
||||
const int MAXX = 1000;
|
||||
|
||||
// top
|
||||
vertices.push_back(vec4(MAXX, 0, MAXX, 1));
|
||||
vertices.push_back(vec4(MAXX, MAXX, 0, 0));
|
||||
|
||||
vertices.push_back(vec4(MAXX, 0, MINX, 1));
|
||||
vertices.push_back(vec4(MAXX, 0, 0, 0));
|
||||
|
||||
vertices.push_back(vec4(MINX, 0, MINX, 1));
|
||||
vertices.push_back(vec4(0, 0, 0, 0));
|
||||
|
||||
vertices.push_back(vec4(MINX, 0, MAXX, 1));
|
||||
vertices.push_back(vec4(0, MAXX, 0, 0));
|
||||
|
||||
vertexBuffer->append(vertices);
|
||||
indexBuffer->append(std::vector<uint16_t>({ 0, 1, 2, 2, 3, 0 }));
|
||||
|
||||
positionView = gpu::BufferView(vertexBuffer, 0, vertexBuffer->getSize(), SHAPE_VERTEX_STRIDE, POSITION_ELEMENT);
|
||||
textureView = gpu::BufferView(vertexBuffer, SHAPE_TEXTURES_OFFSET, vertexBuffer->getSize(), SHAPE_VERTEX_STRIDE, TEXTURE_ELEMENT);
|
||||
texture = DependencyManager::get<TextureCache>()->getImageTexture("C:/Users/bdavis/Git/openvr/samples/bin/cube_texture.png");
|
||||
//texture = DependencyManager::get<TextureCache>()->getImageTexture("H:/test.png");
|
||||
//texture = DependencyManager::get<TextureCache>()->getImageTexture("H:/crate_blue.fbm/lambert8SG_Normal_OpenGL.png");
|
||||
|
||||
auto shader = makeShader(VERTEX_SHADER, FRAGMENT_SHADER, gpu::Shader::BindingSet {});
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setMultisampleEnable(false);
|
||||
state->setDepthTest(gpu::State::DepthTest { true });
|
||||
pipeline = gpu::Pipeline::create(shader, state);
|
||||
vertexFormat->setAttribute(gpu::Stream::POSITION);
|
||||
vertexFormat->setAttribute(gpu::Stream::TEXCOORD);
|
||||
});
|
||||
batch.setPipeline(pipeline);
|
||||
batch.setInputBuffer(gpu::Stream::POSITION, positionView);
|
||||
batch.setInputBuffer(gpu::Stream::TEXCOORD, textureView);
|
||||
batch.setIndexBuffer(gpu::UINT16, indexBuffer, 0);
|
||||
batch.setInputFormat(vertexFormat);
|
||||
|
||||
batch.setResourceTexture(0, texture);
|
||||
batch.setModelTransform(glm::translate(glm::mat4(), vec3(0, -0.1, 0)));
|
||||
batch.drawIndexed(gpu::TRIANGLES, 6, 0);
|
||||
|
||||
batch.setResourceTexture(0, DependencyManager::get<TextureCache>()->getBlueTexture());
|
||||
batch.setModelTransform(glm::translate(glm::mat4(), vec3(0, -0.2, 0)));
|
||||
batch.drawIndexed(gpu::TRIANGLES, 6, 0);
|
||||
}
|
||||
|
||||
void draw() {
|
||||
// Attempting to draw before we're visible and have a valid size will
|
||||
// produce GL errors.
|
||||
if (!isVisible() || _size.width() <= 0 || _size.height() <= 0) {
|
||||
return;
|
||||
}
|
||||
updateCamera();
|
||||
makeCurrent();
|
||||
|
||||
gpu::Batch batch;
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 0.0f, 0.0f, 0.0f, 1.0f });
|
||||
batch.resetStages();
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 0.0f, 0.1f, 0.2f, 1.0f });
|
||||
batch.clearDepthFramebuffer(1e4);
|
||||
batch.setViewportTransform({ 0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio() });
|
||||
batch.setProjectionTransform(_projectionMatrix);
|
||||
|
||||
float t = _time.elapsed() * 1e-3f;
|
||||
glm::vec3 unitscale { 1.0f };
|
||||
glm::vec3 up { 0.0f, 1.0f, 0.0f };
|
||||
|
||||
float distance = 3.0f;
|
||||
glm::vec3 camera_position{ distance * sinf(t), 0.0f, distance * cosf(t) };
|
||||
|
||||
static const vec3 camera_focus(0);
|
||||
static const vec3 camera_up(0, 1, 0);
|
||||
glm::mat4 camera = glm::inverse(glm::lookAt(camera_position, camera_focus, up));
|
||||
batch.setViewTransform(camera);
|
||||
batch.setViewTransform(_camera);
|
||||
batch.setPipeline(_pipeline);
|
||||
batch.setModelTransform(Transform());
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
// Render grid on xz plane (not the optimal way to do things, but w/e)
|
||||
// Note: GeometryCache::renderGrid will *not* work, as it is apparenly unaffected by batch rotations and renders xy only
|
||||
{
|
||||
static const std::string GRID_INSTANCE = "Grid";
|
||||
static auto compactColor1 = toCompactColor(vec4{ 0.35f, 0.25f, 0.15f, 1.0f });
|
||||
static auto compactColor2 = toCompactColor(vec4{ 0.15f, 0.25f, 0.35f, 1.0f });
|
||||
static std::vector<glm::mat4> transforms;
|
||||
static gpu::BufferPointer colorBuffer;
|
||||
if (!transforms.empty()) {
|
||||
transforms.reserve(200);
|
||||
colorBuffer = std::make_shared<gpu::Buffer>();
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
{
|
||||
glm::mat4 transform = glm::translate(mat4(), vec3(0, -1, -50 + i));
|
||||
transform = glm::scale(transform, vec3(100, 1, 1));
|
||||
transforms.push_back(transform);
|
||||
colorBuffer->append(compactColor1);
|
||||
}
|
||||
|
||||
{
|
||||
glm::mat4 transform = glm::mat4_cast(quat(vec3(0, PI / 2.0f, 0)));
|
||||
transform = glm::translate(transform, vec3(0, -1, -50 + i));
|
||||
transform = glm::scale(transform, vec3(100, 1, 1));
|
||||
transforms.push_back(transform);
|
||||
colorBuffer->append(compactColor2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto pipeline = geometryCache->getSimplePipeline();
|
||||
for (auto& transform : transforms) {
|
||||
batch.setModelTransform(transform);
|
||||
batch.setupNamedCalls(GRID_INSTANCE, [=](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) {
|
||||
batch.setViewTransform(camera);
|
||||
batch.setPipeline(_pipeline);
|
||||
geometryCache->renderWireShapeInstances(batch, GeometryCache::Line, data.count(), colorBuffer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
static const size_t ITEM_COUNT = 1000;
|
||||
static const float SHAPE_INTERVAL = (PI * 2.0f) / ITEM_COUNT;
|
||||
static const float ITEM_INTERVAL = SHAPE_INTERVAL / TYPE_COUNT;
|
||||
|
||||
static const gpu::Element POSITION_ELEMENT{ gpu::VEC3, gpu::FLOAT, gpu::XYZ };
|
||||
static const gpu::Element NORMAL_ELEMENT{ gpu::VEC3, gpu::FLOAT, gpu::XYZ };
|
||||
static const gpu::Element COLOR_ELEMENT{ gpu::VEC4, gpu::NUINT8, gpu::RGBA };
|
||||
static const gpu::Element TRANSFORM_ELEMENT{ gpu::MAT4, gpu::FLOAT, gpu::XYZW };
|
||||
|
||||
|
||||
static std::vector<Transform> transforms;
|
||||
static std::vector<vec4> colors;
|
||||
static gpu::BufferPointer indirectBuffer;
|
||||
static gpu::BufferPointer transformBuffer;
|
||||
static gpu::BufferPointer colorBuffer;
|
||||
static gpu::BufferView colorView;
|
||||
static gpu::BufferView instanceXfmView;
|
||||
|
||||
if (!transformBuffer) {
|
||||
transformBuffer = std::make_shared<gpu::Buffer>();
|
||||
colorBuffer = std::make_shared<gpu::Buffer>();
|
||||
indirectBuffer = std::make_shared<gpu::Buffer>();
|
||||
|
||||
static const float ITEM_RADIUS = 20;
|
||||
static const vec3 ITEM_TRANSLATION{ 0, 0, -ITEM_RADIUS };
|
||||
for (size_t i = 0; i < TYPE_COUNT; ++i) {
|
||||
GeometryCache::Shape shape = SHAPE[i];
|
||||
GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape];
|
||||
{
|
||||
gpu::Batch::DrawIndexedIndirectCommand indirectCommand;
|
||||
indirectCommand._count = (uint)shapeData._indexCount;
|
||||
indirectCommand._instanceCount = ITEM_COUNT;
|
||||
indirectCommand._baseInstance = (uint)(i * ITEM_COUNT);
|
||||
indirectCommand._firstIndex = (uint)shapeData._indexOffset / 2;
|
||||
indirectCommand._baseVertex = 0;
|
||||
indirectBuffer->append(indirectCommand);
|
||||
}
|
||||
|
||||
//indirectCommand._count
|
||||
float startingInterval = ITEM_INTERVAL * i;
|
||||
for (size_t j = 0; j < ITEM_COUNT; ++j) {
|
||||
float theta = j * SHAPE_INTERVAL + startingInterval;
|
||||
auto transform = glm::rotate(mat4(), theta, Vectors::UP);
|
||||
transform = glm::rotate(transform, (randFloat() - 0.5f) * PI / 4.0f, Vectors::UNIT_X);
|
||||
transform = glm::translate(transform, ITEM_TRANSLATION);
|
||||
transform = glm::scale(transform, vec3(randFloat() / 2.0f + 0.5f));
|
||||
transformBuffer->append(transform);
|
||||
transforms.push_back(transform);
|
||||
auto color = vec4{ randomColorValue(64), randomColorValue(64), randomColorValue(64), 255 };
|
||||
color /= 255.0f;
|
||||
colors.push_back(color);
|
||||
colorBuffer->append(toCompactColor(color));
|
||||
}
|
||||
}
|
||||
colorView = gpu::BufferView(colorBuffer, COLOR_ELEMENT);
|
||||
instanceXfmView = gpu::BufferView(transformBuffer, TRANSFORM_ELEMENT);
|
||||
}
|
||||
|
||||
#if 1
|
||||
GeometryCache::ShapeData shapeData = geometryCache->_shapes[GeometryCache::Icosahedron];
|
||||
{
|
||||
batch.setViewTransform(camera);
|
||||
batch.setModelTransform(Transform());
|
||||
batch.setPipeline(_pipeline);
|
||||
batch.setInputFormat(getInstancedSolidStreamFormat());
|
||||
batch.setInputBuffer(gpu::Stream::COLOR, colorView);
|
||||
batch.setIndirectBuffer(indirectBuffer);
|
||||
shapeData.setupBatch(batch);
|
||||
batch.multiDrawIndexedIndirect(TYPE_COUNT, gpu::TRIANGLES);
|
||||
}
|
||||
#else
|
||||
batch.setViewTransform(camera);
|
||||
batch.setPipeline(_pipeline);
|
||||
for (size_t i = 0; i < TYPE_COUNT; ++i) {
|
||||
GeometryCache::Shape shape = SHAPE[i];
|
||||
for (size_t j = 0; j < ITEM_COUNT; ++j) {
|
||||
int index = i * ITEM_COUNT + j;
|
||||
batch.setModelTransform(transforms[index]);
|
||||
const vec4& color = colors[index];
|
||||
batch._glColor4f(color.r, color.g, color.b, 1.0);
|
||||
geometryCache->renderShape(batch, shape);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Render unlit cube + sphere
|
||||
static auto startUsecs = usecTimestampNow();
|
||||
float seconds = getSeconds(startUsecs);
|
||||
|
||||
seconds /= 4.0f;
|
||||
int shapeIndex = ((int)seconds) % TYPE_COUNT;
|
||||
bool wire = (seconds - floorf(seconds) > 0.5f);
|
||||
batch.setModelTransform(Transform());
|
||||
batch._glColor4f(0.8f, 0.25f, 0.25f, 1.0f);
|
||||
|
||||
if (wire) {
|
||||
geometryCache->renderWireShape(batch, SHAPE[shapeIndex]);
|
||||
} else {
|
||||
geometryCache->renderShape(batch, SHAPE[shapeIndex]);
|
||||
}
|
||||
|
||||
batch.setModelTransform(Transform().setScale(2.05f));
|
||||
batch._glColor4f(1, 1, 1, 1);
|
||||
geometryCache->renderWireCube(batch);
|
||||
//drawFloorGrid(batch);
|
||||
//drawSimpleShapes(batch);
|
||||
drawCenterShape(batch);
|
||||
drawTerrain(batch);
|
||||
|
||||
_context->render(batch);
|
||||
_qGlContext.swapBuffers(this);
|
||||
|
@ -387,12 +525,12 @@ protected:
|
|||
int main(int argc, char** argv) {
|
||||
QGuiApplication app(argc, argv);
|
||||
QTestWindow window;
|
||||
QTimer timer;
|
||||
timer.setInterval(0);
|
||||
app.connect(&timer, &QTimer::timeout, &app, [&] {
|
||||
auto timer = new QTimer(&app);
|
||||
timer->setInterval(0);
|
||||
app.connect(timer, &QTimer::timeout, &app, [&] {
|
||||
window.draw();
|
||||
});
|
||||
timer.start();
|
||||
timer->start();
|
||||
app.exec();
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue