From a0b77ead41d53e28f87e19a07dfb0b1a1f904662 Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Wed, 22 Feb 2023 00:23:45 +0100 Subject: [PATCH] Add devices 3d view --- CMakeLists.txt | 3 + Devices3dView.qml | 295 ++++++++++++++++++++++++++++ DevicesSettingsPage.qml | 5 + main.cpp | 3 +- models/cube.obj | 415 +++++++++++++++++++++++++++++++++++++++ textures/cubetexture.png | Bin 0 -> 10429 bytes 6 files changed, 720 insertions(+), 1 deletion(-) create mode 100644 Devices3dView.qml create mode 100644 models/cube.obj create mode 100644 textures/cubetexture.png diff --git a/CMakeLists.txt b/CMakeLists.txt index d0ad2c0..ecc5221 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,8 @@ qt_add_qml_module(appscheincommander icons/nebelmaschine.png icons/rgbstrahler.png ui-icons/MaterialIcons-Regular.ttf + models/cube.obj + textures/cubetexture.png QML_FILES main.qml HomePage.qml @@ -60,6 +62,7 @@ qt_add_qml_module(appscheincommander LampRegistersPanel.qml AnimatedInputPanel.qml AnimatedStackView.qml + Devices3dView.qml ) add_definitions(-DCMAKE_PROJECT_VERSION=${CMAKE_PROJECT_VERSION}) diff --git a/Devices3dView.qml b/Devices3dView.qml new file mode 100644 index 0000000..6121aa3 --- /dev/null +++ b/Devices3dView.qml @@ -0,0 +1,295 @@ +import QtQuick +import QtDataVisualization +import QtQuick.Controls + +Item { + + property int selectedAxisLabel: -1 + property real dragSpeedModifier: 100.0 + property int currentMouseX: -1 + property int currentMouseY: -1 + property int previousMouseX: -1 + property int previousMouseY: -1 + + ListModel { + id: graphModel + ListElement{ xPos: 0.0; yPos: 0.0; zPos: 0.0; rotation: "@0,0,0,0" } + ListElement{ xPos: 1.0; yPos: 1.0; zPos: 1.0; rotation: "@45,1,1,1" } + } + + Timer { + id: dataTimer + interval: 1 + running: true + repeat: true + property bool isIncreasing: true + property real rotationAngle: 0 + + function generateQuaternion() { + return "@" + Math.random() * 360 + "," + Math.random() + "," + + Math.random() + "," + Math.random() + } + + function appendRow() { + graphModel.append({"xPos": Math.random(), + "yPos": Math.random(), + "zPos": Math.random(), + "rotation": generateQuaternion() + }); + } + + //! [10] + onTriggered: { + rotationAngle = rotationAngle + 1 + qtCube.setRotationAxisAndAngle(Qt.vector3d(1,0,1), rotationAngle) + //! [10] + scatterSeries.setMeshAxisAndAngle(Qt.vector3d(1,1,1), rotationAngle) + if (isIncreasing) { + for (var i = 0; i < 10; i++) + appendRow() + if (graphModel.count > 2002) { + scatterGraph.theme = isabelleTheme + isIncreasing = false + } + } else { + graphModel.remove(2, 10); + if (graphModel.count == 2) { + scatterGraph.theme = dynamicColorTheme + isIncreasing = true + } + } + } + } + + ThemeColor { + id: dynamicColor + ColorAnimation on color { + from: "red" + to: "yellow" + duration: 2000 + loops: Animation.Infinite + } + } + + Theme3D { + id: dynamicColorTheme + type: Theme3D.ThemeEbony + baseColors: [dynamicColor] + font.pointSize: 50 + labelBorderEnabled: true + labelBackgroundColor: "gold" + labelTextColor: "black" + } + + Theme3D { + id: isabelleTheme + type: Theme3D.ThemeIsabelle + font.pointSize: 50 + labelBorderEnabled: true + labelBackgroundColor: "gold" + labelTextColor: "black" + } + + Item { + id: dataView + anchors.bottom: parent.bottom + width: parent.width + height: parent.height + + //! [0] + Scatter3D { + id: scatterGraph + inputHandler: null + //! [0] + width: dataView.width + height: dataView.height + theme: dynamicColorTheme + shadowQuality: AbstractGraph3D.ShadowQualityLow + scene.activeCamera.yRotation: 45.0 + scene.activeCamera.xRotation: 45.0 + scene.activeCamera.zoomLevel: 75.0 + + Scatter3DSeries { + id: scatterSeries + itemLabelFormat: "X:@xLabel Y:@yLabel Z:@zLabel" + mesh: Abstract3DSeries.MeshCube + + ItemModelScatterDataProxy { + itemModel: graphModel + xPosRole: "xPos" + yPosRole: "yPos" + zPosRole: "zPos" + rotationRole: "rotation" + } + } + //! [9] + customItemList: [ + Custom3DItem { + id: qtCube + meshFile: ":/scheincommander/models/cube.obj" + textureFile: ":/scheincommander/textures/cubetexture.png" + position: Qt.vector3d(0.65,0.35,0.65) + scaling: Qt.vector3d(0.3,0.3,0.3) + } + ] + //! [9] + //! [5] + onSelectedElementChanged: { + if (selectedElement >= AbstractGraph3D.ElementAxisXLabel + && selectedElement <= AbstractGraph3D.ElementAxisZLabel) + selectedAxisLabel = selectedElement + else + selectedAxisLabel = -1 + } + //! [5] + } + + //! [1] + MouseArea { + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.LeftButton + //! [1] + + //! [3] + onPositionChanged: (mouse)=> { + currentMouseX = mouse.x; + currentMouseY = mouse.y; + //! [3] + //! [6] + if (pressed && selectedAxisLabel != -1) + dragAxis(); + //! [6] + //! [4] + previousMouseX = currentMouseX; + previousMouseY = currentMouseY; + } + //! [4] + + //! [2] + onPressed: (mouse)=> { + scatterGraph.scene.selectionQueryPosition = Qt.point(mouse.x, mouse.y); + } + //! [2] + + onReleased: { + // We need to clear mouse positions and selected axis, because touch devices cannot + // track position all the time + selectedAxisLabel = -1 + currentMouseX = -1 + currentMouseY = -1 + previousMouseX = -1 + previousMouseY = -1 + } + } + } + + //! [7] + function dragAxis() { + // Do nothing if previous mouse position is uninitialized + if (previousMouseX === -1) + return + + // Directional drag multipliers based on rotation. Camera is locked to 45 degrees, so we + // can use one precalculated value instead of calculating xx, xy, zx and zy individually + var cameraMultiplier = 0.70710678 + + // Calculate the mouse move amount + var moveX = currentMouseX - previousMouseX + var moveY = currentMouseY - previousMouseY + + // Adjust axes + switch (selectedAxisLabel) { + case AbstractGraph3D.ElementAxisXLabel: + var distance = ((moveX - moveY) * cameraMultiplier) / dragSpeedModifier + // Check if we need to change min or max first to avoid invalid ranges + if (distance > 0) { + scatterGraph.axisX.min -= distance + scatterGraph.axisX.max -= distance + } else { + scatterGraph.axisX.max -= distance + scatterGraph.axisX.min -= distance + } + break + case AbstractGraph3D.ElementAxisYLabel: + distance = moveY / dragSpeedModifier + // Check if we need to change min or max first to avoid invalid ranges + if (distance > 0) { + scatterGraph.axisY.max += distance + scatterGraph.axisY.min += distance + } else { + scatterGraph.axisY.min += distance + scatterGraph.axisY.max += distance + } + break + case AbstractGraph3D.ElementAxisZLabel: + distance = ((moveX + moveY) * cameraMultiplier) / dragSpeedModifier + // Check if we need to change min or max first to avoid invalid ranges + if (distance > 0) { + scatterGraph.axisZ.max += distance + scatterGraph.axisZ.min += distance + } else { + scatterGraph.axisZ.min += distance + scatterGraph.axisZ.max += distance + } + break + } + } + //! [7] + + Button { + id: rangeToggle + width: parent.width / 3 // We're adding 3 buttons and want to divide them equally + text: "Use Preset Range" + anchors.left: parent.left + property bool autoRange: true + onClicked: { + if (autoRange) { + text = "Use Automatic Range" + scatterGraph.axisX.min = 0.3 + scatterGraph.axisX.max = 0.7 + scatterGraph.axisY.min = 0.3 + scatterGraph.axisY.max = 0.7 + scatterGraph.axisZ.min = 0.3 + scatterGraph.axisZ.max = 0.7 + autoRange = false + dragSpeedModifier = 200.0 + } else { + text = "Use Preset Range" + autoRange = true + dragSpeedModifier = 100.0 + } + scatterGraph.axisX.autoAdjustRange = autoRange + scatterGraph.axisY.autoAdjustRange = autoRange + scatterGraph.axisZ.autoAdjustRange = autoRange + } + } + + //! [8] + Button { + id: orthoToggle + width: parent.width / 3 + text: "Display Orthographic" + anchors.left: rangeToggle.right + onClicked: { + if (scatterGraph.orthoProjection) { + text = "Display Orthographic"; + scatterGraph.orthoProjection = false + // Orthographic projection disables shadows, so we need to switch them back on + scatterGraph.shadowQuality = AbstractGraph3D.ShadowQualityLow + } else { + text = "Display Perspective"; + scatterGraph.orthoProjection = true + } + } + } + //! [8] + + Button { + id: exitButton + width: parent.width / 3 + text: "Quit" + anchors.left: orthoToggle.right + onClicked: Qt.quit(); + } +} diff --git a/DevicesSettingsPage.qml b/DevicesSettingsPage.qml index c536dcc..b246d7a 100644 --- a/DevicesSettingsPage.qml +++ b/DevicesSettingsPage.qml @@ -115,5 +115,10 @@ ColumnLayout { Layout.fillHeight: true } } + + Devices3dView { + Layout.fillWidth: true + Layout.fillHeight: true + } } } diff --git a/main.cpp b/main.cpp index aa87cf4..58b5a61 100644 --- a/main.cpp +++ b/main.cpp @@ -22,7 +22,8 @@ int main(int argc, char *argv[]) "%{function}(): " "%{message}")); - qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard")); + qputenv("QT_IM_MODULE", QByteArrayLiteral("qtvirtualkeyboard")); + qputenv("QSG_RHI_BACKEND", QByteArrayLiteral("opengl")); QCoreApplication::setOrganizationDomain("com.büro"); QCoreApplication::setOrganizationName("büro"); diff --git a/models/cube.obj b/models/cube.obj new file mode 100644 index 0000000..0197618 --- /dev/null +++ b/models/cube.obj @@ -0,0 +1,415 @@ +# Blender v2.66 (sub 0) OBJ File: 'beveled_cube.blend' +# www.blender.org +v -1.000000 -0.878027 0.878027 +v -0.978771 -0.929277 0.878027 +v -0.950975 -0.932562 0.932562 +v -0.978771 -0.878027 0.929277 +v -0.932562 -0.950975 0.932562 +v -0.929277 -0.978771 0.878027 +v -0.878027 -1.000000 0.878027 +v -0.878027 -0.978771 0.929277 +v -0.932562 -0.932562 0.950975 +v -0.878027 -0.929277 0.978771 +v -0.878027 -0.878027 1.000000 +v -0.929277 -0.878027 0.978771 +v -1.000000 -0.878027 -0.878027 +v -0.978771 -0.878027 -0.929277 +v -0.950975 -0.932562 -0.932562 +v -0.978771 -0.929277 -0.878027 +v -0.932562 -0.932562 -0.950975 +v -0.929277 -0.878027 -0.978771 +v -0.878027 -0.878027 -1.000000 +v -0.878027 -0.929277 -0.978771 +v -0.932562 -0.950975 -0.932562 +v -0.878027 -0.978771 -0.929277 +v -0.878027 -1.000000 -0.878027 +v -0.929277 -0.978771 -0.878027 +v 0.878027 -0.878027 -1.000000 +v 0.929277 -0.878027 -0.978771 +v 0.932562 -0.932562 -0.950975 +v 0.878027 -0.929277 -0.978771 +v 0.950975 -0.932562 -0.932562 +v 0.978771 -0.878027 -0.929277 +v 1.000000 -0.878027 -0.878027 +v 0.978771 -0.929277 -0.878027 +v 0.932562 -0.950975 -0.932562 +v 0.929277 -0.978771 -0.878027 +v 0.878027 -1.000000 -0.878027 +v 0.878027 -0.978771 -0.929277 +v 1.000000 -0.878027 0.878027 +v 0.978771 -0.878027 0.929277 +v 0.950975 -0.932562 0.932562 +v 0.978771 -0.929277 0.878027 +v 0.932562 -0.932562 0.950975 +v 0.929277 -0.878027 0.978771 +v 0.878027 -0.878027 1.000000 +v 0.878027 -0.929277 0.978771 +v 0.932562 -0.950975 0.932562 +v 0.878027 -0.978771 0.929277 +v 0.878027 -1.000000 0.878027 +v 0.929277 -0.978771 0.878027 +v -0.878027 0.878027 1.000000 +v -0.878027 0.929277 0.978771 +v -0.932562 0.932562 0.950975 +v -0.929277 0.878027 0.978771 +v -0.932562 0.950975 0.932562 +v -0.878027 0.978771 0.929277 +v -0.878027 1.000000 0.878027 +v -0.929277 0.978771 0.878027 +v -0.950975 0.932562 0.932562 +v -0.978771 0.929277 0.878027 +v -1.000000 0.878027 0.878027 +v -0.978771 0.878027 0.929277 +v -1.000000 0.878027 -0.878027 +v -0.978771 0.929277 -0.878027 +v -0.950975 0.932562 -0.932562 +v -0.978771 0.878027 -0.929277 +v -0.932562 0.950975 -0.932562 +v -0.929277 0.978771 -0.878027 +v -0.878027 1.000000 -0.878027 +v -0.878027 0.978771 -0.929277 +v -0.932562 0.932562 -0.950975 +v -0.878027 0.929277 -0.978771 +v -0.878027 0.878027 -1.000000 +v -0.929277 0.878027 -0.978771 +v 0.878027 0.878027 -1.000000 +v 0.878027 0.929277 -0.978771 +v 0.932562 0.932562 -0.950975 +v 0.929277 0.878027 -0.978771 +v 0.932562 0.950975 -0.932562 +v 0.878027 0.978771 -0.929277 +v 0.878027 1.000000 -0.878027 +v 0.929277 0.978771 -0.878027 +v 0.950975 0.932562 -0.932562 +v 0.978771 0.929277 -0.878027 +v 1.000000 0.878027 -0.878027 +v 0.978771 0.878027 -0.929277 +v 1.000000 0.878027 0.878027 +v 0.978771 0.929277 0.878027 +v 0.950975 0.932562 0.932562 +v 0.978771 0.878027 0.929277 +v 0.932562 0.950975 0.932562 +v 0.929277 0.978771 0.878027 +v 0.878027 1.000000 0.878027 +v 0.878027 0.978771 0.929277 +v 0.932562 0.932562 0.950975 +v 0.878027 0.929277 0.978771 +v 0.878027 0.878027 1.000000 +v 0.929277 0.878027 0.978771 +vt 0.024513 0.966281 +vt 0.033719 0.975487 +vt 0.033719 0.966281 +vt 0.964639 0.060986 +vt 0.964639 0.939014 +vt 0.989386 0.939014 +vt 0.939014 0.964639 +vt 0.060986 0.964639 +vt 0.939014 0.989386 +vt 0.060986 0.060986 +vt 0.060986 0.939014 +vt 0.939014 0.939014 +vt 0.035361 0.060986 +vt 0.035361 0.939014 +vt 0.010614 0.060986 +vt 0.010614 0.939014 +vt 0.989386 0.060986 +vt 0.939014 0.035361 +vt 0.060986 0.035361 +vt 0.060986 0.010614 +vt 0.060986 0.989386 +vt 0.939014 0.060986 +vt 0.966281 0.975487 +vt 0.966281 0.966281 +vt 0.975487 0.966281 +vt 0.975487 0.033719 +vt 0.966281 0.033719 +vt 0.966281 0.024513 +vt 0.033719 0.024513 +vt 0.033719 0.033719 +vt 0.024513 0.033719 +vt 0.939014 0.010614 +vn -0.713187 -0.495651 -0.495651 +vn -0.495651 -0.495651 -0.713187 +vn -0.495651 -0.713187 -0.495651 +vn 0.539384 -0.823450 0.175909 +vn 0.539384 -0.823450 -0.175909 +vn 0.823450 -0.539384 -0.175909 +vn -0.713187 0.495651 -0.495651 +vn -0.495651 0.713187 -0.495651 +vn -0.495651 0.495651 -0.713187 +vn 0.175909 -0.823450 -0.539384 +vn -0.185644 -0.825892 -0.532365 +vn 0.175909 -0.539384 -0.823450 +vn -0.193426 -0.961852 0.193426 +vn -0.193426 -0.961852 -0.193426 +vn 0.187689 -0.964110 -0.187689 +vn -0.532365 -0.185644 0.825892 +vn -0.532365 0.185644 0.825892 +vn -0.825892 -0.185644 0.532365 +vn -0.532365 0.185644 -0.825892 +vn -0.532365 -0.185644 -0.825892 +vn -0.825892 0.185644 -0.532365 +vn 0.823450 0.175909 -0.539384 +vn 0.823450 -0.175909 -0.539384 +vn 0.539384 -0.175909 -0.823450 +vn 0.539384 0.175909 0.823450 +vn 0.539384 -0.175909 0.823450 +vn 0.823450 -0.175909 0.539384 +vn 0.175909 0.823450 0.539384 +vn -0.185644 0.825892 0.532365 +vn -0.185644 0.532365 0.825892 +vn -0.185644 0.825892 -0.532365 +vn 0.175909 0.823450 -0.539384 +vn -0.185644 0.532365 -0.825892 +vn 0.539384 0.823450 -0.175909 +vn 0.539384 0.823450 0.175909 +vn 0.823450 0.539384 0.175909 +vn 0.187689 0.964110 0.187689 +vn 0.187689 0.964110 -0.187689 +vn -0.193426 0.961852 -0.193426 +vn -0.193426 0.193426 -0.961852 +vn 0.187689 0.187689 -0.964110 +vn -0.193426 -0.193426 -0.961852 +vn -0.961852 0.193426 0.193426 +vn -0.961852 0.193426 -0.193426 +vn -0.961852 -0.193426 0.193426 +vn -0.532365 -0.825892 -0.185644 +vn -0.532365 -0.825892 0.185644 +vn -0.825892 -0.532365 -0.185644 +vn 0.498856 0.498856 -0.708701 +vn 0.498856 0.708701 -0.498856 +vn 0.708701 0.498856 -0.498856 +vn 0.964110 0.187689 -0.187689 +vn 0.964110 0.187689 0.187689 +vn 0.964110 -0.187689 0.187689 +vn 0.498856 -0.498856 -0.708701 +vn 0.708701 -0.498856 -0.498856 +vn 0.498856 -0.708701 -0.498856 +vn 0.708701 0.498856 0.498856 +vn 0.498856 0.708701 0.498856 +vn 0.498856 0.498856 0.708701 +vn -0.495651 0.495651 0.713187 +vn -0.495651 0.713187 0.495651 +vn -0.713187 0.495651 0.495651 +vn 0.708701 -0.498856 0.498856 +vn 0.498856 -0.498856 0.708701 +vn 0.498856 -0.708701 0.498856 +vn -0.532365 0.825892 0.185644 +vn -0.532365 0.825892 -0.185644 +vn -0.825892 0.532365 0.185644 +vn 0.187689 0.187689 0.964110 +vn -0.193426 0.193426 0.961852 +vn -0.193426 -0.193426 0.961852 +vn -0.185644 -0.825892 0.532365 +vn 0.175909 -0.823450 0.539384 +vn 0.175909 -0.539384 0.823450 +vn -0.825892 -0.532365 0.185644 +vn -0.713187 -0.495651 0.495651 +vn -0.495651 -0.713187 0.495651 +vn -0.495651 -0.495651 0.713187 +vn -0.185644 -0.532365 0.825892 +vn -0.961852 -0.193426 -0.193426 +vn -0.825892 -0.185644 -0.532365 +vn 0.187689 -0.187689 -0.964110 +vn 0.823450 -0.539384 0.175909 +vn -0.193426 0.961852 0.193426 +vn -0.825892 0.532365 -0.185644 +vn 0.175909 0.539384 -0.823450 +vn 0.539384 0.175909 -0.823450 +vn 0.823450 0.539384 -0.175909 +vn 0.823450 0.175909 0.539384 +vn 0.175909 0.539384 0.823450 +vn -0.185644 -0.532365 -0.825892 +vn -0.825892 0.185644 0.532365 +vn 0.964110 -0.187689 -0.187689 +vn 0.187689 -0.187689 0.964110 +vn 0.187689 -0.964110 0.187689 +s 1 +f 15/1/1 17/2/2 21/3/3 +f 48/4/4 34/5/5 32/6/6 +f 63/1/7 65/3/8 69/2/9 +f 36/7/10 22/8/11 28/9/12 +f 7/10/13 23/11/14 35/12/15 +f 12/13/16 52/14/17 4/15/18 +f 72/14/19 18/13/20 64/16/21 +f 84/6/22 30/17/23 26/4/24 +f 96/5/25 42/4/26 38/17/27 +f 92/18/28 54/19/29 50/20/30 +f 68/8/31 78/7/32 70/21/33 +f 80/5/34 90/4/35 86/17/36 +f 91/22/37 79/12/38 67/11/39 +f 71/11/40 73/12/41 19/10/42 +f 59/11/43 61/12/44 1/10/45 +f 24/14/46 6/13/47 16/16/48 +f 75/23/49 77/24/50 81/25/51 +f 83/12/52 85/11/53 37/10/54 +f 27/23/55 29/25/56 33/24/57 +f 87/26/58 89/27/59 93/28/60 +f 51/29/61 53/30/62 57/31/63 +f 39/26/64 41/28/65 45/27/66 +f 56/13/67 66/14/68 58/15/69 +f 95/12/70 49/11/71 11/10/72 +f 8/19/73 46/18/74 44/32/75 +f 1/10/45 2/19/76 3/30/77 +f 5/30/78 6/13/47 7/10/13 +f 9/30/79 10/19/80 11/10/72 +f 13/22/81 14/4/82 15/27/1 +f 17/30/2 18/13/20 19/10/42 +f 21/3/3 22/8/11 23/11/14 +f 25/22/83 26/4/24 28/18/12 +f 29/27/56 30/4/23 32/18/6 +f 33/24/57 34/5/5 36/7/10 +f 37/10/54 38/13/27 40/19/84 +f 41/27/65 42/4/26 44/18/75 +f 45/27/66 46/18/74 48/4/4 +f 49/11/71 50/8/30 51/3/61 +f 53/30/62 54/19/29 55/10/85 +f 57/3/63 58/8/69 59/11/43 +f 61/12/44 62/7/86 63/24/7 +f 65/3/8 66/14/68 67/11/39 +f 69/3/9 70/8/33 71/11/40 +f 73/12/41 74/7/87 76/5/88 +f 77/24/50 78/7/32 80/5/34 +f 81/24/51 82/7/89 84/5/22 +f 85/11/53 86/8/36 88/14/90 +f 89/27/59 90/4/35 92/18/28 +f 93/24/60 94/7/91 96/5/25 +f 2/15/76 6/13/47 3/31/77 +f 8/19/73 10/20/80 5/30/78 +f 12/13/16 4/15/18 9/30/79 +f 14/15/82 18/13/20 15/31/1 +f 20/21/92 22/8/11 17/2/2 +f 24/14/46 16/16/48 21/3/3 +f 26/4/24 30/17/23 29/26/56 +f 32/6/6 34/5/5 33/24/57 +f 36/7/10 28/9/12 27/23/55 +f 38/17/27 42/4/26 41/27/65 +f 44/32/75 46/18/74 45/27/66 +f 48/4/4 40/17/84 39/26/64 +f 50/20/30 54/19/29 51/29/61 +f 56/13/67 58/15/69 53/30/62 +f 60/16/93 52/14/17 57/1/63 +f 62/16/86 66/14/68 63/1/7 +f 68/8/31 70/21/33 65/3/8 +f 72/14/19 64/16/21 69/3/9 +f 74/9/87 78/7/32 77/24/50 +f 80/5/34 82/6/89 81/25/51 +f 84/6/22 76/5/88 75/24/49 +f 86/17/36 90/4/35 89/27/59 +f 92/18/28 94/32/91 93/28/60 +f 96/5/25 88/6/90 87/25/58 +f 55/10/85 67/11/39 56/13/67 +f 61/12/44 59/11/43 62/7/86 +f 71/11/40 19/10/42 72/14/19 +f 13/22/81 61/12/44 14/4/82 +f 23/11/14 7/10/13 24/14/46 +f 1/10/45 13/22/81 2/19/76 +f 11/10/72 49/11/71 12/13/16 +f 59/11/43 1/10/45 60/14/93 +f 67/11/39 79/12/38 68/8/31 +f 73/12/41 71/11/40 74/7/87 +f 83/12/52 31/22/94 30/4/23 +f 25/22/83 73/12/41 76/5/88 +f 35/12/15 23/11/14 36/7/10 +f 19/10/42 25/22/83 20/19/92 +f 79/12/38 91/22/37 90/4/35 +f 85/11/53 83/12/52 82/7/89 +f 95/12/70 43/22/95 42/4/26 +f 37/10/54 85/11/53 88/14/90 +f 47/22/96 35/12/15 34/5/5 +f 31/22/94 37/10/54 40/19/84 +f 91/22/37 55/10/85 54/19/29 +f 49/11/71 95/12/70 94/7/91 +f 7/10/13 47/22/96 46/18/74 +f 43/22/95 11/10/72 10/19/80 +f 3/31/77 5/30/78 9/29/79 +f 40/17/84 48/4/4 32/6/6 +f 22/8/11 20/21/92 28/9/12 +f 47/22/96 7/10/13 35/12/15 +f 52/14/17 60/16/93 4/15/18 +f 18/13/20 14/15/82 64/16/21 +f 76/5/88 84/6/22 26/4/24 +f 88/6/90 96/5/25 38/17/27 +f 94/32/91 92/18/28 50/20/30 +f 78/7/32 74/9/87 70/21/33 +f 82/6/89 80/5/34 86/17/36 +f 55/10/85 91/22/37 67/11/39 +f 73/12/41 25/22/83 19/10/42 +f 61/12/44 13/22/81 1/10/45 +f 6/13/47 2/15/76 16/16/48 +f 31/22/94 83/12/52 37/10/54 +f 66/14/68 62/16/86 58/15/69 +f 43/22/95 95/12/70 11/10/72 +f 10/20/80 8/19/73 44/32/75 +f 4/13/18 1/10/45 3/30/77 +f 8/19/73 5/30/78 7/10/13 +f 12/13/16 9/30/79 11/10/72 +f 16/18/48 13/22/81 15/27/1 +f 20/19/92 17/30/2 19/10/42 +f 24/14/46 21/3/3 23/11/14 +f 26/4/24 27/27/55 28/18/12 +f 30/4/23 31/22/94 32/18/6 +f 34/5/5 35/12/15 36/7/10 +f 38/13/27 39/30/64 40/19/84 +f 42/4/26 43/22/95 44/18/75 +f 46/18/74 47/22/96 48/4/4 +f 52/14/17 49/11/71 51/3/61 +f 56/13/67 53/30/62 55/10/85 +f 60/14/93 57/3/63 59/11/43 +f 64/5/21 61/12/44 63/24/7 +f 68/8/31 65/3/8 67/11/39 +f 72/14/19 69/3/9 71/11/40 +f 74/7/87 75/24/49 76/5/88 +f 78/7/32 79/12/38 80/5/34 +f 82/7/89 83/12/52 84/5/22 +f 86/8/36 87/3/58 88/14/90 +f 90/4/35 91/22/37 92/18/28 +f 94/7/91 95/12/70 96/5/25 +f 6/13/47 5/30/78 3/31/77 +f 10/20/80 9/29/79 5/30/78 +f 4/15/18 3/31/77 9/30/79 +f 18/13/20 17/30/2 15/31/1 +f 22/8/11 21/3/3 17/2/2 +f 16/16/48 15/1/1 21/3/3 +f 27/27/55 26/4/24 29/26/56 +f 29/25/56 32/6/6 33/24/57 +f 33/24/57 36/7/10 27/23/55 +f 39/26/64 38/17/27 41/27/65 +f 41/28/65 44/32/75 45/27/66 +f 45/27/66 48/4/4 39/26/64 +f 54/19/29 53/30/62 51/29/61 +f 58/15/69 57/31/63 53/30/62 +f 52/14/17 51/3/61 57/1/63 +f 66/14/68 65/3/8 63/1/7 +f 70/21/33 69/2/9 65/3/8 +f 64/16/21 63/1/7 69/3/9 +f 75/23/49 74/9/87 77/24/50 +f 77/24/50 80/5/34 81/25/51 +f 81/25/51 84/6/22 75/24/49 +f 87/26/58 86/17/36 89/27/59 +f 89/27/59 92/18/28 93/28/60 +f 93/24/60 96/5/25 87/25/58 +f 67/11/39 66/14/68 56/13/67 +f 59/11/43 58/8/69 62/7/86 +f 19/10/42 18/13/20 72/14/19 +f 61/12/44 64/5/21 14/4/82 +f 7/10/13 6/13/47 24/14/46 +f 13/22/81 16/18/48 2/19/76 +f 49/11/71 52/14/17 12/13/16 +f 1/10/45 4/13/18 60/14/93 +f 79/12/38 78/7/32 68/8/31 +f 71/11/40 70/8/33 74/7/87 +f 84/5/22 83/12/52 30/4/23 +f 26/4/24 25/22/83 76/5/88 +f 23/11/14 22/8/11 36/7/10 +f 25/22/83 28/18/12 20/19/92 +f 80/5/34 79/12/38 90/4/35 +f 86/8/36 85/11/53 82/7/89 +f 96/5/25 95/12/70 42/4/26 +f 38/13/27 37/10/54 88/14/90 +f 48/4/4 47/22/96 34/5/5 +f 32/18/6 31/22/94 40/19/84 +f 92/18/28 91/22/37 54/19/29 +f 50/8/30 49/11/71 94/7/91 +f 8/19/73 7/10/13 46/18/74 +f 44/18/75 43/22/95 10/19/80 diff --git a/textures/cubetexture.png b/textures/cubetexture.png new file mode 100644 index 0000000000000000000000000000000000000000..3cea6863c53024bd4a9c7df24df35ada8c47e33a GIT binary patch literal 10429 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

GUyDrnR=Rh-;&Zk!M)0fLiL(3dH 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 F0RV3Jo>>3@ literal 0 HcmV?d00001