Initial commit
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Window
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
title: qsTr("About Material Editor")
|
||||
modal: true
|
||||
dim: false
|
||||
focus: true
|
||||
standardButtons: Dialog.Ok
|
||||
width: Math.max(implicitWidth, 340)
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 12
|
||||
|
||||
Label {
|
||||
text: qsTr("Material Editor %1").arg(Qt.application.version)
|
||||
font.bold: true
|
||||
font.pixelSize: Application.font.pixelSize * 1.1
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("Copyright (C) 2023 The Qt Company Ltd.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick3D.MaterialEditor
|
||||
import QtQuick3D
|
||||
|
||||
SplitView {
|
||||
id: editorView
|
||||
orientation: Qt.Vertical
|
||||
property alias vertexEditor: vertEdit
|
||||
property alias fragmentEditor: fragEdit
|
||||
property alias outputTextItem: outputTextItem
|
||||
property alias outputView: outputView
|
||||
property alias vertexTabText: vertexTabText.text
|
||||
property alias fragmentTabText: fragTabtext.text
|
||||
property alias infoStack: infoStack
|
||||
property alias tabBarInfoView: tabBarInfoView
|
||||
property alias tabButtonShaderOutput: tabButtonShaderOutput
|
||||
property alias uniformModel: uniformManagerPane.uniformModel
|
||||
required property MaterialAdapter materialAdapter
|
||||
required property InstanceListEntry instanceEntry
|
||||
required property Model targetModel
|
||||
|
||||
ColumnLayout {
|
||||
SplitView.preferredHeight: parent.height * .8
|
||||
TabBar {
|
||||
id: tabBarEditors
|
||||
Layout.fillWidth: true
|
||||
readonly property string defVertText: qsTr("Vertex")
|
||||
readonly property string defFragText: qsTr("Fragment")
|
||||
TabButton {
|
||||
id: vertexTabText
|
||||
onTextChanged: {
|
||||
if (text === "")
|
||||
text = tabBarEditors.defVertText
|
||||
}
|
||||
}
|
||||
TabButton {
|
||||
id: fragTabtext
|
||||
onTextChanged: {
|
||||
if (text === "")
|
||||
text = tabBarEditors.defFragText
|
||||
}
|
||||
}
|
||||
TabButton {
|
||||
id: matPropTabText
|
||||
text: qsTr("Material Properties")
|
||||
}
|
||||
TabButton {
|
||||
id: instPropTabText
|
||||
text: qsTr("Instancing Properties")
|
||||
}
|
||||
}
|
||||
|
||||
// Editors
|
||||
StackLayout {
|
||||
id: editorStack
|
||||
currentIndex: tabBarEditors.currentIndex
|
||||
Layout.fillWidth: true
|
||||
|
||||
ShaderEditor {
|
||||
id: vertEdit
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
ShaderEditor {
|
||||
id: fragEdit
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
MaterialPropertiesPane {
|
||||
id: matPropPane
|
||||
targetMaterial: editorView.materialAdapter
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
InstancingPropertiesPane {
|
||||
id: instPropPane
|
||||
instanceEntry: editorView.instanceEntry
|
||||
targetModel: editorView.targetModel
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
TabBar {
|
||||
id: tabBarInfoView
|
||||
Layout.fillWidth: true
|
||||
TabButton {
|
||||
id: tabButtonUniforms
|
||||
text: qsTr("Uniforms")
|
||||
}
|
||||
TabButton {
|
||||
id: tabButtonShaderOutput
|
||||
text: qsTr("Shader Output")
|
||||
}
|
||||
}
|
||||
|
||||
// Uniform, compile output etc.
|
||||
StackLayout {
|
||||
id: infoStack
|
||||
currentIndex: tabBarInfoView.currentIndex
|
||||
// Layout.preferredHeight: parent.height * .2
|
||||
Layout.fillWidth: true
|
||||
UniformManagerPane {
|
||||
id: uniformManagerPane
|
||||
materialAdapter: editorView.materialAdapter
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Rectangle {
|
||||
id: outputView
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
color: palette.base
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
|
||||
TextArea {
|
||||
id: outputTextItem
|
||||
width: outputView.width
|
||||
padding: 2
|
||||
color: palette.text
|
||||
wrapMode: Text.WordWrap
|
||||
readOnly: true
|
||||
text: " "
|
||||
}
|
||||
}
|
||||
Button {
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 25
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 5
|
||||
text: qsTr("Clear")
|
||||
onClicked: {
|
||||
outputTextItem.text = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
required property Item backgroundItem
|
||||
property alias range: glassEffect.range
|
||||
property alias blur: glassEffect.blur
|
||||
property alias color: glassEffect.color
|
||||
property alias backgroundRect: backgroundSourceImage.sourceRect
|
||||
|
||||
ShaderEffectSource {
|
||||
anchors.fill: parent
|
||||
id: backgroundSourceImage
|
||||
sourceRect: Qt.rect(0, 0, width, height)
|
||||
sourceItem: root.backgroundItem
|
||||
visible: false
|
||||
}
|
||||
|
||||
|
||||
ShaderEffectSource {
|
||||
anchors.fill: parent
|
||||
id: noiseImageSource
|
||||
sourceRect: Qt.rect(0, 0, width, height)
|
||||
sourceItem: noiseImage
|
||||
visible: false
|
||||
}
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
id: noiseImage
|
||||
fillMode: Image.Tile
|
||||
horizontalAlignment: Image.AlignLeft
|
||||
verticalAlignment: Image.AlignTop
|
||||
visible: false
|
||||
source: "assets/images/noise.png"
|
||||
}
|
||||
|
||||
ShaderEffect {
|
||||
id: glassEffect
|
||||
property variant sourceTex: backgroundSourceImage
|
||||
property variant noiseTex: noiseImageSource
|
||||
property real range: 0.25;
|
||||
property real blur: 0.05;
|
||||
property color color: "white"
|
||||
anchors.fill: parent
|
||||
fragmentShader: "assets/shaders/frostedGlass.frag.qsb"
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick3D
|
||||
import Qt.labs.platform
|
||||
|
||||
Pane {
|
||||
id: root
|
||||
required property InstanceListEntry instanceEntry
|
||||
required property Model targetModel
|
||||
|
||||
ColumnLayout {
|
||||
CheckBox {
|
||||
id: enableInstCheckBox
|
||||
text: qsTr("Enable Instancing")
|
||||
onCheckStateChanged: {
|
||||
targetModel.enableInstancing = checkState == Qt.Checked
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
visible: targetModel.enableInstancing
|
||||
RowLayout {
|
||||
Label {
|
||||
text: qsTr("Color")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Button {
|
||||
id: colorButton
|
||||
text: qsTr("Instancing Color")
|
||||
Layout.fillWidth: true
|
||||
background: Rectangle {
|
||||
radius: 10
|
||||
color: root.instanceEntry.color
|
||||
}
|
||||
onClicked: {
|
||||
colorDialog.open()
|
||||
}
|
||||
}
|
||||
ColorDialog {
|
||||
id: colorDialog
|
||||
currentColor: root.instanceEntry.color
|
||||
onAccepted: root.instanceEntry.color = color
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Label {
|
||||
text: qsTr("CustomData.x")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
TextField {
|
||||
id: customXInput
|
||||
Layout.fillWidth: true
|
||||
validator: DoubleValidator { locale: "C" }
|
||||
onEditingFinished: {
|
||||
if (acceptableInput)
|
||||
root.instanceEntry.customData.x = parseFloat(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Label {
|
||||
text: qsTr("CustomData.y")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
TextField {
|
||||
id: customYInput
|
||||
Layout.fillWidth: true
|
||||
validator: DoubleValidator { locale: "C" }
|
||||
onEditingFinished: {
|
||||
if (acceptableInput)
|
||||
root.instanceEntry.customData.y = parseFloat(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Label {
|
||||
text: qsTr("CustomData.z")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
TextField {
|
||||
id: customZInput
|
||||
Layout.fillWidth: true
|
||||
validator: DoubleValidator { locale: "C" }
|
||||
onEditingFinished: {
|
||||
if (acceptableInput)
|
||||
root.instanceEntry.customData.z = parseFloat(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Label {
|
||||
text: qsTr("CustomData.w")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
TextField {
|
||||
id: customWInput
|
||||
Layout.fillWidth: true
|
||||
validator: DoubleValidator { locale: "C" }
|
||||
onEditingFinished: {
|
||||
if (acceptableInput)
|
||||
root.instanceEntry.customData.w = parseFloat(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick3D
|
||||
import QtQuick3D.MaterialEditor
|
||||
|
||||
Pane {
|
||||
id: root
|
||||
required property MaterialAdapter targetMaterial
|
||||
|
||||
ColumnLayout {
|
||||
RowLayout {
|
||||
Label {
|
||||
text: qsTr("Source Blend")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
ComboBox {
|
||||
id: sourceBlendComboBox
|
||||
textRole: "text"
|
||||
valueRole: "value"
|
||||
implicitContentWidthPolicy: ComboBox.WidestText
|
||||
onActivated: root.targetMaterial.sourceBlend = currentValue
|
||||
Component.onCompleted: currentIndex = indexOfValue(root.targetMaterial.sourceBlend)
|
||||
model: [
|
||||
{ value: CustomMaterial.NoBlend, text: qsTr("No Blend") },
|
||||
{ value: CustomMaterial.Zero, text: qsTr("Zero") },
|
||||
{ value: CustomMaterial.One, text: qsTr("One") },
|
||||
{ value: CustomMaterial.SrcColor, text: qsTr("Source Color") },
|
||||
{ value: CustomMaterial.OneMinusSrcColor, text: qsTr("1 - Source Color") },
|
||||
{ value: CustomMaterial.DstColor, text: qsTr("Destination Color") },
|
||||
{ value: CustomMaterial.OneMinusDstColor, text: qsTr("1 - Destination Color") },
|
||||
{ value: CustomMaterial.SrcAlpha, text: qsTr("Source Alpha") },
|
||||
{ value: CustomMaterial.OneMinusSrcAlpha, text: qsTr("1 - Source Alpha") },
|
||||
{ value: CustomMaterial.DstAlpha, text: qsTr("Destination Alpha") },
|
||||
{ value: CustomMaterial.OneMinusDstAlpha, text: qsTr("1 - Destination Alpha") },
|
||||
{ value: CustomMaterial.ConstantColor, text: qsTr("Constant Color") },
|
||||
{ value: CustomMaterial.OneMinusConstantColor, text: qsTr("1 - Constant Color") },
|
||||
{ value: CustomMaterial.ConstantAlpha, text: qsTr("Constant Alpha") },
|
||||
{ value: CustomMaterial.OneMinusConstantAlpha, text: qsTr("1 - Constant Alpha") },
|
||||
{ value: CustomMaterial.SrcAlphaSaturate, text: qsTr("Source Alpha Saturate") }
|
||||
]
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Label {
|
||||
text: qsTr("Destination Blend")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
ComboBox {
|
||||
id: destinationBlendComboBox
|
||||
textRole: "text"
|
||||
valueRole: "value"
|
||||
implicitContentWidthPolicy: ComboBox.WidestText
|
||||
onActivated: root.targetMaterial.destinationBlend = currentValue
|
||||
Component.onCompleted: currentIndex = indexOfValue(root.targetMaterial.destinationBlend)
|
||||
|
||||
model: [
|
||||
{ value: CustomMaterial.NoBlend, text: qsTr("No Blend") },
|
||||
{ value: CustomMaterial.Zero, text: qsTr("Zero") },
|
||||
{ value: CustomMaterial.One, text: qsTr("One") },
|
||||
{ value: CustomMaterial.SrcColor, text: qsTr("Source Color") },
|
||||
{ value: CustomMaterial.OneMinusSrcColor, text: qsTr("1 - Source Color") },
|
||||
{ value: CustomMaterial.DstColor, text: qsTr("Destination Color") },
|
||||
{ value: CustomMaterial.OneMinusDstColor, text: qsTr("1 - Destination Color") },
|
||||
{ value: CustomMaterial.SrcAlpha, text: qsTr("Source Alpha") },
|
||||
{ value: CustomMaterial.OneMinusSrcAlpha, text: qsTr("1 - Source Alpha") },
|
||||
{ value: CustomMaterial.DstAlpha, text: qsTr("Destination Alpha") },
|
||||
{ value: CustomMaterial.OneMinusDstAlpha, text: qsTr("1 - Destination Alpha") },
|
||||
{ value: CustomMaterial.ConstantColor, text: qsTr("Constant Color") },
|
||||
{ value: CustomMaterial.OneMinusConstantColor, text: qsTr("1 - Constant Color") },
|
||||
{ value: CustomMaterial.ConstantAlpha, text: qsTr("Constant Alpha") },
|
||||
{ value: CustomMaterial.OneMinusConstantAlpha, text: qsTr("1 - Constant Alpha") },
|
||||
{ value: CustomMaterial.SrcAlphaSaturate, text: qsTr("Source Alpha Saturate") }
|
||||
]
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Label {
|
||||
text: qsTr("Cull Mode")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
ComboBox {
|
||||
id: cullModeComboBox
|
||||
textRole: "text"
|
||||
valueRole: "value"
|
||||
implicitContentWidthPolicy: ComboBox.WidestText
|
||||
onActivated: root.targetMaterial.cullMode = currentValue
|
||||
Component.onCompleted: currentIndex = indexOfValue(root.targetMaterial.cullMode)
|
||||
model: [
|
||||
{ value: CustomMaterial.BackFaceCulling, text: qsTr("Back Face Culling") },
|
||||
{ value: CustomMaterial.FrontFaceCulling, text: qsTr("Front Face Culling") },
|
||||
{ value: CustomMaterial.NoCulling, text: qsTr("No Culling") }
|
||||
]
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Label {
|
||||
text: qsTr("Depth Draw Mode")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
ComboBox {
|
||||
id: depthDrawModeComboBox
|
||||
textRole: "text"
|
||||
valueRole: "value"
|
||||
implicitContentWidthPolicy: ComboBox.WidestText
|
||||
onActivated: root.targetMaterial.depthDrawMode = currentValue
|
||||
Component.onCompleted: currentIndex = indexOfValue(root.targetMaterial.depthDrawMode)
|
||||
model: [
|
||||
{ value: CustomMaterial.OpaqueOnlyDepthDraw, text: qsTr("Opaque Only") },
|
||||
{ value: CustomMaterial.AlwaysDepthDraw, text: qsTr("Always") },
|
||||
{ value: CustomMaterial.NeverDepthDraw, text: qsTr("Never") },
|
||||
{ value: CustomMaterial.OpaquePrePassDepthDraw, text: qsTr("Opaque Pre-pass") }
|
||||
]
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Label {
|
||||
text: qsTr("Shading Mode")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
ComboBox {
|
||||
id: shadingModeComboBox
|
||||
textRole: "text"
|
||||
valueRole: "value"
|
||||
implicitContentWidthPolicy: ComboBox.WidestText
|
||||
onActivated: root.targetMaterial.shadingMode = currentValue
|
||||
Component.onCompleted: currentIndex = indexOfValue(root.targetMaterial.shadingMode)
|
||||
model: [
|
||||
{ value: CustomMaterial.Shaded, text: qsTr("Shaded") },
|
||||
{ value: CustomMaterial.Unshaded, text: qsTr("Unshaded") }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtCore
|
||||
import QtQuick3D
|
||||
import QtQuick3D.Helpers
|
||||
|
||||
Item {
|
||||
id: previewRoot
|
||||
|
||||
property url skyBoxTexturePath: "assets/skybox/OpenfootageNET_lowerAustria01-1024.hdr"
|
||||
property CustomMaterial currentMaterial: CustomMaterial {
|
||||
|
||||
}
|
||||
|
||||
property PrincipledMaterial fallbackMaterial: PrincipledMaterial {
|
||||
baseColor: "magenta"
|
||||
}
|
||||
|
||||
property alias modelInstance: model
|
||||
property alias rootNode: resourceRoot
|
||||
property alias instanceEntry: instEntry
|
||||
|
||||
Settings {
|
||||
property alias cameraOriginRotation: originNode.rotation
|
||||
property alias cameraRotation: sceneCamera.rotation
|
||||
property alias cameraPosition: sceneCamera.position
|
||||
}
|
||||
|
||||
View3D {
|
||||
id: view
|
||||
anchors.fill: parent
|
||||
environment: SceneEnvironment {
|
||||
id: sceneEnvironment
|
||||
backgroundMode: previewControls.enableIBL ? SceneEnvironment.SkyBox : SceneEnvironment.Transparent
|
||||
lightProbe: previewControls.enableIBL ? skyboxTexture : null
|
||||
}
|
||||
|
||||
Texture {
|
||||
id: skyboxTexture
|
||||
source: previewRoot.skyBoxTexturePath
|
||||
}
|
||||
|
||||
Node {
|
||||
id: resourceRoot
|
||||
}
|
||||
|
||||
property alias cameraOrigin: originNode
|
||||
|
||||
Node {
|
||||
id: originNode
|
||||
PerspectiveCamera {
|
||||
id: sceneCamera
|
||||
z: 300
|
||||
}
|
||||
}
|
||||
|
||||
camera: sceneCamera
|
||||
|
||||
DirectionalLight {
|
||||
id: light
|
||||
z: 600
|
||||
eulerRotation: Qt.vector3d(30, 0, 0)
|
||||
visible: previewControls.enableDirectionalLight
|
||||
}
|
||||
|
||||
Model {
|
||||
id: model
|
||||
source: previewControls.modelSource
|
||||
materials: [ previewRoot.currentMaterial, previewRoot.fallbackMaterial ]
|
||||
property bool enableInstancing: false
|
||||
instancing: enableInstancing ? manualInstancing : null
|
||||
}
|
||||
|
||||
InstanceList {
|
||||
id: manualInstancing
|
||||
instances: [instEntry, instEntry1, instEntry2, instEntry3, instEntry4]
|
||||
}
|
||||
InstanceListEntry {
|
||||
id: instEntry
|
||||
}
|
||||
InstanceListEntry {
|
||||
id: instEntry1
|
||||
position: Qt.vector3d(120, 150, 150);
|
||||
}
|
||||
InstanceListEntry {
|
||||
id: instEntry2
|
||||
position: Qt.vector3d(-70, 70, -100);
|
||||
}
|
||||
InstanceListEntry {
|
||||
id: instEntry3
|
||||
position: Qt.vector3d(-100, -120, -70);
|
||||
}
|
||||
InstanceListEntry {
|
||||
id: instEntry4
|
||||
position: Qt.vector3d(120, -50, 100);
|
||||
}
|
||||
|
||||
OrbitCameraController {
|
||||
id: cameraController
|
||||
origin: originNode
|
||||
camera: sceneCamera
|
||||
panEnabled: false
|
||||
}
|
||||
}
|
||||
|
||||
PreviewControls {
|
||||
id: previewControls
|
||||
width: parent.width
|
||||
targetView: view
|
||||
orbitCamera: cameraController
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtCore
|
||||
import QtQuick3D
|
||||
import QtQuick3D.Helpers
|
||||
|
||||
Item {
|
||||
id: previewControls
|
||||
required property View3D targetView
|
||||
required property OrbitCameraController orbitCamera
|
||||
property alias modelSource: modelComboBox.currentValue
|
||||
property alias enableIBL: iblEnableButton.checked
|
||||
property alias enableDirectionalLight: directionalLightEnabledButton.checked
|
||||
|
||||
|
||||
Settings {
|
||||
property alias enableIbl: previewControls.enableIBL
|
||||
property alias enableDirectionalLight: previewControls.enableDirectionalLight
|
||||
property alias environmentOrientationSliderValue: environmentOrientationSlider.value
|
||||
}
|
||||
|
||||
FrostedGlass {
|
||||
width: parent.width
|
||||
height: layout.implicitHeight
|
||||
backgroundItem: previewControls.targetView
|
||||
backgroundRect: Qt.rect(0, 0, width, height)
|
||||
// range: 0.05
|
||||
// blur: 0.005
|
||||
range: 0.05
|
||||
blur: 0.05
|
||||
//color: "pink"
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: layout
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 10
|
||||
Label {
|
||||
text: "Model"
|
||||
}
|
||||
ComboBox {
|
||||
id: modelComboBox
|
||||
textRole: "text"
|
||||
valueRole: "value"
|
||||
model: ListModel {
|
||||
ListElement {
|
||||
text: "Sphere"
|
||||
value: "#Sphere"
|
||||
}
|
||||
ListElement {
|
||||
text: "Cube"
|
||||
value: "#Cube"
|
||||
}
|
||||
ListElement {
|
||||
text: "Plane"
|
||||
value: "#Rectangle"
|
||||
}
|
||||
ListElement {
|
||||
text: "Suzanne"
|
||||
value: "assets/meshes/suzanne.mesh"
|
||||
}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
text: "Reset View"
|
||||
onClicked: {
|
||||
previewControls.orbitCamera.origin.rotation = Qt.quaternion(1, 0, 0, 0)
|
||||
previewControls.orbitCamera.camera.rotation = Qt.quaternion(1, 0, 0, 0)
|
||||
previewControls.orbitCamera.camera.position = Qt.vector3d(0, 0, 300)
|
||||
environmentOrientationSlider.value = 0
|
||||
}
|
||||
}
|
||||
ToolButton {
|
||||
id: iblEnableButton
|
||||
icon.source: "assets/icons/texture.png"
|
||||
checkable: true
|
||||
checked: true
|
||||
hoverEnabled: true
|
||||
ToolTip.delay: 1000
|
||||
ToolTip.timeout: 5000
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Toggle the use of IBL")
|
||||
}
|
||||
|
||||
Label {
|
||||
visible: previewControls.enableIBL
|
||||
text: "Environment Orientation"
|
||||
}
|
||||
Slider {
|
||||
visible: previewControls.enableIBL
|
||||
id: environmentOrientationSlider
|
||||
Layout.fillWidth: true
|
||||
from: -180
|
||||
to: 180
|
||||
value: 0
|
||||
onValueChanged: {
|
||||
previewControls.targetView.environment.probeOrientation = Qt.vector3d(0, value, 0)
|
||||
}
|
||||
}
|
||||
ToolButton {
|
||||
id: directionalLightEnabledButton
|
||||
icon.source: "assets/icons/lightdirectional.png"
|
||||
checkable: true
|
||||
checked: true
|
||||
hoverEnabled: true
|
||||
ToolTip.delay: 1000
|
||||
ToolTip.timeout: 5000
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Toggle a Directional Light")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick3D.MaterialEditor
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
title: qsTr("Unsaved changes")
|
||||
modal: true
|
||||
|
||||
required property MaterialAdapter materialAdapter
|
||||
required property var saveAsDialog
|
||||
|
||||
function doIfChangesSavedOrDiscarded(actionFunction) {
|
||||
if (!materialAdapter.unsavedChanges) {
|
||||
actionFunction()
|
||||
return
|
||||
}
|
||||
|
||||
// There are unsaved changes, so we need to prompt.
|
||||
|
||||
function disconnectSaveChangesSignals() {
|
||||
root.accepted.disconnect(saveChanges)
|
||||
root.discarded.disconnect(discardChanges)
|
||||
root.rejected.disconnect(cancel)
|
||||
}
|
||||
|
||||
function saveChanges() {
|
||||
if (materialAdapter.materialSaveFile.toString().length > 0) {
|
||||
// Existing project; can save without a dialog.
|
||||
if (materialAdapter.save()) {
|
||||
// Saved successfully, so now we can perform the action.
|
||||
performAction()
|
||||
} else {
|
||||
// Failed to save; cancel.
|
||||
cancel()
|
||||
}
|
||||
} else {
|
||||
// New project; need to save as.
|
||||
function disconnectSaveAsSignals() {
|
||||
materialAdapter.errorOccurred.disconnect(saveAsFailed)
|
||||
materialAdapter.postMaterialSaved.disconnect(saveAsSucceeded)
|
||||
saveAsDialog.rejected.disconnect(saveAsDialogRejected)
|
||||
}
|
||||
|
||||
function saveAsSucceeded() {
|
||||
disconnectSaveAsSignals()
|
||||
performAction()
|
||||
}
|
||||
|
||||
function saveAsFailed() {
|
||||
disconnectSaveAsSignals()
|
||||
disconnectSaveChangesSignals()
|
||||
}
|
||||
|
||||
function saveAsDialogRejected() {
|
||||
disconnectSaveAsSignals()
|
||||
cancel()
|
||||
}
|
||||
|
||||
materialAdapter.errorOccurred.connect(saveAsFailed)
|
||||
materialAdapter.postMaterialSaved.connect(saveAsSucceeded)
|
||||
saveAsDialog.rejected.connect(saveAsDialogRejected)
|
||||
|
||||
saveAsDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
function discardChanges() {
|
||||
performAction()
|
||||
root.close()
|
||||
}
|
||||
|
||||
function performAction() {
|
||||
disconnectSaveChangesSignals()
|
||||
actionFunction()
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
disconnectSaveChangesSignals()
|
||||
}
|
||||
|
||||
root.accepted.connect(saveChanges)
|
||||
root.discarded.connect(discardChanges)
|
||||
root.rejected.connect(cancel)
|
||||
root.open()
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("Save changes to the material before closing?")
|
||||
}
|
||||
|
||||
// Using a DialogButtonBox allows us to assign objectNames to the buttons,
|
||||
// which makes it possible to test them.
|
||||
footer: DialogButtonBox {
|
||||
Button {
|
||||
objectName: "cancelDialogButton"
|
||||
text: qsTr("Cancel")
|
||||
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
|
||||
}
|
||||
Button {
|
||||
objectName: "saveChangesDialogButton"
|
||||
text: qsTr("Save")
|
||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||
}
|
||||
Button {
|
||||
objectName: "discardChangesDialogButton"
|
||||
text: qsTr("Don't save")
|
||||
DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick3D.MaterialEditor
|
||||
|
||||
Flickable {
|
||||
id: flickable
|
||||
property alias font: textArea.font
|
||||
property alias text: textArea.text
|
||||
property alias textDocument: textArea.textDocument
|
||||
property alias lineColumn: lineNumbers
|
||||
property alias textArea: textArea
|
||||
|
||||
property int rowHeight: Math.ceil(fontMetrics.lineSpacing)
|
||||
property int marginsTop: 10
|
||||
property int marginsLeft: 4
|
||||
property int lineCountWidth: 40
|
||||
clip: true
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
width: 15
|
||||
active: true
|
||||
}
|
||||
ScrollBar.horizontal: ScrollBar {
|
||||
width: 15
|
||||
active: true
|
||||
}
|
||||
|
||||
FontMetrics {
|
||||
id: fontMetrics
|
||||
font: textArea.font
|
||||
}
|
||||
|
||||
|
||||
Column {
|
||||
id: lineNumbers
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: flickable.marginsLeft
|
||||
anchors.topMargin: flickable.marginsTop
|
||||
y: flickable.marginsTop
|
||||
width: flickable.lineCountWidth
|
||||
|
||||
function labelAt(lineNr) {
|
||||
if (lineNr > 0) {
|
||||
if (lineNr > repeater.count)
|
||||
lineNr = repeater.count; // Best guess at this point...
|
||||
return repeater.itemAt(lineNr - 1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function range(start, end) {
|
||||
var rangeArray = new Array(end-start);
|
||||
for (var i = 0; i < rangeArray.length; i++)
|
||||
rangeArray[i] = start+i;
|
||||
return rangeArray;
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: textArea.lineCount
|
||||
delegate: Label {
|
||||
required property int index
|
||||
font: textArea.font
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
height: flickable.rowHeight
|
||||
renderType: Text.NativeRendering
|
||||
text: index+1
|
||||
}
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
id: lineNumbersSeperator
|
||||
y: 4
|
||||
height: parent.height
|
||||
anchors.left: lineNumbers.right
|
||||
anchors.leftMargin: flickable.marginsLeft
|
||||
width: 1
|
||||
color: "#ddd"
|
||||
}
|
||||
|
||||
SyntaxHighlighter {
|
||||
id: syntaxHighlighter
|
||||
document: textArea.textDocument
|
||||
}
|
||||
|
||||
TextArea.flickable: TextArea {
|
||||
id: textArea
|
||||
textFormat: Qt.PlainText
|
||||
focus: false
|
||||
selectByMouse: true
|
||||
leftPadding: flickable.marginsLeft
|
||||
rightPadding: flickable.marginsLeft
|
||||
topPadding: flickable.marginsTop
|
||||
bottomPadding: flickable.marginsTop
|
||||
tabStopDistance: fontMetrics.averageCharacterWidth * 4;
|
||||
anchors.left: lineNumbersSeperator.right
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,737 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
import QtQuick3D.MaterialEditor
|
||||
|
||||
Pane {
|
||||
id: uniformManagerPane
|
||||
property alias uniformModel: uniformModel
|
||||
required property MaterialAdapter materialAdapter
|
||||
SplitView {
|
||||
anchors.fill: parent
|
||||
ColumnLayout {
|
||||
clip: true
|
||||
RowLayout {
|
||||
id: tableControls
|
||||
|
||||
function insertUniform() {
|
||||
let rowCount = uniformManagerPane.materialAdapter.uniformModel.rowCount;
|
||||
if (uniformManagerPane.materialAdapter.uniformModel.insertRow(rowCount, typeComboBox.currentIndex, uniformNameTextInput.text))
|
||||
uniformNameTextInput.text = ""
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Type:"
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: typeComboBox
|
||||
textRole: "text"
|
||||
valueRole: "value"
|
||||
model: [
|
||||
{ value: UniformModel.Bool, text: "bool" },
|
||||
{ value: UniformModel.Int, text: "int" },
|
||||
{ value: UniformModel.Float, text: "float" },
|
||||
{ value: UniformModel.Vec2, text: "vec2" },
|
||||
{ value: UniformModel.Vec3, text: "vec3" },
|
||||
{ value: UniformModel.Vec4, text: "vec4" },
|
||||
{ value: UniformModel.Mat44, text: "mat44" },
|
||||
{ value: UniformModel.Sampler, text: "sampler" }
|
||||
]
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: uniformNameTextInput
|
||||
validator: RegularExpressionValidator {
|
||||
regularExpression: /[a-zA-Z_][a-zA-Z0-9_]+/
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "Uniform Name"
|
||||
onAccepted: tableControls.insertUniform()
|
||||
}
|
||||
Button {
|
||||
id: addButton
|
||||
text: "Add"
|
||||
enabled: uniformNameTextInput.text != ""
|
||||
onClicked: tableControls.insertUniform()
|
||||
}
|
||||
}
|
||||
|
||||
// Column Header
|
||||
Row {
|
||||
id: columnsHeader
|
||||
Layout.fillWidth: true
|
||||
Label {
|
||||
width: uniformTable.columnWidth(0)
|
||||
text: "Type"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
Label {
|
||||
width: uniformTable.columnWidth(1)
|
||||
text: "Name"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
Label {
|
||||
width: uniformTable.columnWidth(2)
|
||||
text: "Value"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
ListView {
|
||||
id: uniformTable
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
model: UniformModel {
|
||||
id: uniformModel
|
||||
}
|
||||
clip: true
|
||||
ScrollBar.vertical: ScrollBar { }
|
||||
highlight: Rectangle {
|
||||
color: palette.highlight
|
||||
}
|
||||
|
||||
property var typeStrings: [
|
||||
"bool",
|
||||
"int",
|
||||
"float",
|
||||
"vec2",
|
||||
"vec3",
|
||||
"vec4",
|
||||
"mat44",
|
||||
"sampler"
|
||||
]
|
||||
|
||||
function convertValueToString(value, type)
|
||||
{
|
||||
if (type === 0) {
|
||||
// bool
|
||||
return String(value);
|
||||
} if (type === 1) {
|
||||
// int
|
||||
return String(value);
|
||||
} if (type === 2) {
|
||||
// float
|
||||
return String(value);
|
||||
} if (type === 3) {
|
||||
// vec2
|
||||
return "(" + value.x + ", " + value.y + ")"
|
||||
} if (type === 4) {
|
||||
// vec3
|
||||
return "(" + value.x + ", " + value.y + ", " + value.z + ")"
|
||||
} if (type === 5) {
|
||||
// vec4
|
||||
return "(" + value.x + ", " + value.y + ", " + value.z + ", " + value.w + ")"
|
||||
} if (type === 6) {
|
||||
// mat44
|
||||
return value.toString()
|
||||
} if (type === 7) {
|
||||
// sampler
|
||||
return "[Texture]"
|
||||
}
|
||||
}
|
||||
|
||||
function columnWidth(column) {
|
||||
if (column === 0)
|
||||
return 50;
|
||||
if (column === 1)
|
||||
return 100;
|
||||
return 100;
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
id: delegateRoot
|
||||
required property int type
|
||||
required property string name
|
||||
required property var value
|
||||
required property int index
|
||||
|
||||
|
||||
width: ListView.view.width
|
||||
height: typeLabel.implicitHeight
|
||||
Row {
|
||||
Label {
|
||||
id: typeLabel
|
||||
width: uniformTable.columnWidth(0)
|
||||
text: uniformTable.typeStrings[delegateRoot.type]
|
||||
}
|
||||
Label {
|
||||
width: uniformTable.columnWidth(1)
|
||||
text: delegateRoot.name
|
||||
}
|
||||
Label {
|
||||
width: uniformTable.columnWidth(2)
|
||||
Layout.fillWidth: true
|
||||
text: uniformTable.convertValueToString(delegateRoot.value, delegateRoot.type)
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
uniformTable.currentIndex = delegateRoot.index
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Item {
|
||||
id: uniformValueEditor
|
||||
width: parent.width * 0.5
|
||||
clip: true
|
||||
|
||||
Label {
|
||||
id: emptyLabel
|
||||
visible: uniformTable.currentIndex == -1
|
||||
anchors.centerIn: parent
|
||||
text: "Select a uniform to edit"
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: delegateRepeater
|
||||
anchors.fill: parent
|
||||
model: uniformModel
|
||||
Item {
|
||||
id: editorRoot
|
||||
|
||||
required property int index
|
||||
required property int type
|
||||
required property string name
|
||||
required property var model
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
visible: index === uniformTable.currentIndex
|
||||
|
||||
Item {
|
||||
id: header
|
||||
width: parent.width
|
||||
anchors.top: parent.top
|
||||
height: removeButton.implicitHeight
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
id: headerLayout
|
||||
Label {
|
||||
text: "Uniform: " + editorRoot.name
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
Button {
|
||||
id: removeButton
|
||||
text: "Remove"
|
||||
Layout.alignment: Qt.AlignRight
|
||||
onClicked: {
|
||||
uniformManagerPane.materialAdapter.uniformModel.removeRow(uniformTable.currentIndex, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: editorLoader
|
||||
anchors.top: header.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
sourceComponent: editors[editorRoot.type]
|
||||
|
||||
|
||||
readonly property list<Component> editors: [
|
||||
boolEditor,
|
||||
intEditor,
|
||||
floatEditor,
|
||||
vec2Editor,
|
||||
vec3Editor,
|
||||
vec4Editor,
|
||||
mat44Editor,
|
||||
samplerEditor
|
||||
]
|
||||
|
||||
Component {
|
||||
id: boolEditor
|
||||
CheckBox {
|
||||
text: "value"
|
||||
checked: editorRoot.model.value
|
||||
onCheckedChanged: editorRoot.model.value = checked
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: intEditor
|
||||
TextField {
|
||||
text: editorRoot.model.value
|
||||
validator: IntValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = parseInt(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: floatEditor
|
||||
ColumnLayout {
|
||||
TextField {
|
||||
Layout.fillWidth: true
|
||||
text: editorRoot.model.value
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput) {
|
||||
var floatValue = parseFloat(text);
|
||||
floatSlider.updateMinMax(floatValue);
|
||||
editorRoot.value = floatValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
Slider {
|
||||
id: floatSlider
|
||||
// Grow slider min & max based on given values
|
||||
function updateMinMax(newValue) {
|
||||
if (from > newValue)
|
||||
from = newValue;
|
||||
if (to < newValue)
|
||||
to = newValue;
|
||||
value = newValue;
|
||||
}
|
||||
from: 0.0
|
||||
to: 1.0
|
||||
onValueChanged: {
|
||||
editorRoot.model.value = value;
|
||||
}
|
||||
Component.onCompleted: {
|
||||
updateMinMax(editorRoot.model.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: vec2Editor
|
||||
ColumnLayout {
|
||||
RowLayout {
|
||||
Label {
|
||||
text: "X:"
|
||||
}
|
||||
TextField {
|
||||
id: xField
|
||||
text: editorRoot.model.value.x
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished: {
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.vector2d(parseFloat(text), editorRoot.model.value.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Label {
|
||||
text: "Y:"
|
||||
}
|
||||
TextField {
|
||||
id: yField
|
||||
text: editorRoot.model.value.y
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished: {
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.vector2d(editorRoot.model.value.x, parseFloat(text))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: vec3Editor
|
||||
ColumnLayout {
|
||||
RowLayout {
|
||||
Label {
|
||||
text: "X:"
|
||||
}
|
||||
TextField {
|
||||
id: xField
|
||||
text: editorRoot.model.value.x
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.vector3d(parseFloat(text), editorRoot.model.value.y, editorRoot.model.value.z)
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Label {
|
||||
text: "Y:"
|
||||
}
|
||||
TextField {
|
||||
id: yField
|
||||
text: editorRoot.model.value.y
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.vector3d(editorRoot.model.value.x, parseFloat(text), editorRoot.model.value.z)
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Label {
|
||||
text: "Z:"
|
||||
}
|
||||
TextField {
|
||||
id: zField
|
||||
text: editorRoot.model.value.z
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.vector3d(editorRoot.model.value.x, editorRoot.model.value.y, parseFloat(text))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: vec4Editor
|
||||
ColumnLayout {
|
||||
RowLayout {
|
||||
Label {
|
||||
text: "X:"
|
||||
}
|
||||
TextField {
|
||||
id: xField
|
||||
text: editorRoot.model.value.x
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.vector4d(parseFloat(text), editorRoot.model.value.y, editorRoot.model.value.z, editorRoot.model.value.w)
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Label {
|
||||
text: "Y:"
|
||||
}
|
||||
TextField {
|
||||
id: yField
|
||||
text: editorRoot.model.value.y
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.vector4d(editorRoot.model.value.x, parseFloat(text), editorRoot.model.value.z, editorRoot.model.value.w)
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Label {
|
||||
text: "Z:"
|
||||
}
|
||||
TextField {
|
||||
id: zField
|
||||
text: editorRoot.model.value.z
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.vector4d(editorRoot.model.value.x, editorRoot.model.value.y, parseFloat(text), editorRoot.model.value.w)
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Label {
|
||||
text: "W:"
|
||||
}
|
||||
TextField {
|
||||
id: wField
|
||||
text: editorRoot.model.value.w
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.vector4d(editorRoot.model.value.x, editorRoot.model.value.y, editorRoot.model.value.z, parseFloat(text))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: mat44Editor
|
||||
ColumnLayout {
|
||||
RowLayout {
|
||||
TextField {
|
||||
text: editorRoot.model.value.m11
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.matrix4x4(parseFloat(text), editorRoot.model.value.m12, editorRoot.model.value.m13 , editorRoot.model.value.m14,
|
||||
editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24,
|
||||
editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34,
|
||||
editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44)
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
text: editorRoot.model.value.m12
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, parseFloat(text), editorRoot.model.value.m13 , editorRoot.model.value.m14,
|
||||
editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24,
|
||||
editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34,
|
||||
editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44)
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
text: editorRoot.model.value.m13
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, parseFloat(text), editorRoot.model.value.m14,
|
||||
editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24,
|
||||
editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34,
|
||||
editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44)
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
text: editorRoot.model.value.m14
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, parseFloat(text),
|
||||
editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24,
|
||||
editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34,
|
||||
editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44)
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
TextField {
|
||||
text: editorRoot.model.value.m21
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14,
|
||||
parseFloat(text), editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24,
|
||||
editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34,
|
||||
editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44)
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
text: editorRoot.model.value.m22
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14,
|
||||
editorRoot.model.value.m21, parseFloat(text), editorRoot.model.value.m23, editorRoot.model.value.m24,
|
||||
editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34,
|
||||
editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44)
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
text: editorRoot.model.value.m23
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14,
|
||||
editorRoot.model.value.m21, editorRoot.model.value.m22, parseFloat(text), editorRoot.model.value.m24,
|
||||
editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34,
|
||||
editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44)
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
text: editorRoot.model.value.m24
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14,
|
||||
editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, parseFloat(text),
|
||||
editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34,
|
||||
editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44)
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
TextField {
|
||||
text: editorRoot.model.value.m31
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14,
|
||||
editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24,
|
||||
parseFloat(text), editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34,
|
||||
editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44)
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
text: editorRoot.model.value.m32
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14,
|
||||
editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24,
|
||||
editorRoot.model.value.m31, parseFloat(text), editorRoot.model.value.m33, editorRoot.model.value.m34,
|
||||
editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44)
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
text: editorRoot.model.value.m33
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14,
|
||||
editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24,
|
||||
editorRoot.model.value.m31, editorRoot.model.value.m32, parseFloat(text), editorRoot.model.value.m34,
|
||||
editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44)
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
text: editorRoot.model.value.m34
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14,
|
||||
editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24,
|
||||
editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, parseFloat(text),
|
||||
editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44)
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
TextField {
|
||||
text: editorRoot.model.value.m41
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14,
|
||||
editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24,
|
||||
editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34,
|
||||
parseFloat(text), editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44)
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
text: editorRoot.model.value.m42
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14,
|
||||
editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24,
|
||||
editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34,
|
||||
editorRoot.model.value.m41, parseFloat(text), editorRoot.model.value.m43, editorRoot.model.value.m44)
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
text: editorRoot.model.value.m43
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14,
|
||||
editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24,
|
||||
editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34,
|
||||
editorRoot.model.value.m41, editorRoot.model.value.m42, parseFloat(text), editorRoot.model.value.m44)
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
text: editorRoot.model.value.m44
|
||||
validator: DoubleValidator {
|
||||
locale: "C"
|
||||
}
|
||||
onEditingFinished:{
|
||||
if (acceptableInput)
|
||||
editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14,
|
||||
editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24,
|
||||
editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34,
|
||||
editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, parseFloat(text))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: samplerEditor
|
||||
ColumnLayout {
|
||||
Image {
|
||||
id: previewImage
|
||||
sourceSize.width: 128
|
||||
sourceSize.height: 128
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
Button {
|
||||
text: "Choose Image"
|
||||
onClicked: {
|
||||
textureSourceDialog.open()
|
||||
}
|
||||
}
|
||||
FileDialog {
|
||||
id: textureSourceDialog
|
||||
title: "Open an Image File"
|
||||
nameFilters: [ uniformManagerPane.materialAdapter.getSupportedImageFormatsFilter()]
|
||||
onAccepted: {
|
||||
if (textureSourceDialog.selectedFile !== null) {
|
||||
editorRoot.model.value = textureSourceDialog.selectedFile
|
||||
previewImage.source = textureSourceDialog.selectedFile
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
import QtCore
|
||||
|
||||
import QtQuick3D.MaterialEditor
|
||||
|
||||
ApplicationWindow {
|
||||
id: window
|
||||
height: 720
|
||||
width: 1024
|
||||
visible: true
|
||||
title: qsTr("Custom Material Editor")
|
||||
|
||||
// Context property (see main.cpp)
|
||||
property url projectFolder: _qtProjectDir // qmllint disable unqualified
|
||||
|
||||
Settings {
|
||||
id: settings
|
||||
property alias windowX: window.x
|
||||
property alias windowY: window.y
|
||||
property alias windowWidth: window.width
|
||||
property alias windowHeight: window.height
|
||||
property alias windowVisibility: window.visibility
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
mainSplitView.restoreState(settings.value("ui/mainSplitView"))
|
||||
editorView.restoreState(settings.value("ui/editorView"))
|
||||
}
|
||||
Component.onDestruction: {
|
||||
settings.setValue("ui/mainSplitView", mainSplitView.saveState())
|
||||
settings.setValue("ui/editorView", editorView.saveState())
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: resourceStore
|
||||
objectName: "QtQuick3DResourceStorePrivate"
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: openMaterialDialog
|
||||
title: "Open a Material Project File"
|
||||
nameFilters: [ "Material Editor Project (*.qmp)"]
|
||||
currentFolder: window.projectFolder
|
||||
onAccepted: {
|
||||
if (openMaterialDialog.selectedFile !== null)
|
||||
materialAdapter.loadMaterial(openMaterialDialog.selectedFile);
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: saveAsDialog
|
||||
fileMode: FileDialog.SaveFile
|
||||
currentFolder: window.projectFolder
|
||||
nameFilters: [ "Material Editor Project (*.qmp)"]
|
||||
onAccepted: materialAdapter.saveMaterial(selectedFile)
|
||||
|
||||
}
|
||||
|
||||
|
||||
FileDialog {
|
||||
id: fragmentShaderImportDialog
|
||||
title: "Fragment Shader to import"
|
||||
nameFilters: [ "Fragment Shader (*.frag *.fs *.glsl)" ]
|
||||
currentFolder: window.projectFolder
|
||||
onAccepted: {
|
||||
if (fragmentShaderImportDialog.selectedFile !== null) {
|
||||
materialAdapter.importFragmentShader(fragmentShaderImportDialog.selectedFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: vertexShaderImportDialog
|
||||
title: "Vertex Shader to import"
|
||||
nameFilters: [ "Vertex Shader (*.vert *.vs *.glsl)" ]
|
||||
currentFolder: window.projectFolder
|
||||
onAccepted: {
|
||||
if (vertexShaderImportDialog.selectedFile !== null) {
|
||||
materialAdapter.importVertexShader(vertexShaderImportDialog.selectedFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: saveCompFileDialog
|
||||
title: "Choose file"
|
||||
nameFilters: [ "QML Componen (*.qml)" ]
|
||||
fileMode: FileDialog.SaveFile
|
||||
currentFolder: window.projectFolder
|
||||
onAccepted: {
|
||||
if (selectedFile !== null)
|
||||
componentFilePath.text = selectedFile
|
||||
}
|
||||
}
|
||||
|
||||
RegularExpressionValidator {
|
||||
id: nameValidator
|
||||
regularExpression: /[a-zA-Z0-9_-]*/
|
||||
}
|
||||
|
||||
Dialog {
|
||||
id: exportMaterialDialog
|
||||
title: "Export material"
|
||||
anchors.centerIn: parent
|
||||
|
||||
ColumnLayout {
|
||||
id: exportFiles
|
||||
anchors.fill: parent
|
||||
spacing: 1
|
||||
RowLayout {
|
||||
Text {
|
||||
text: qsTr("Component")
|
||||
color: palette.text
|
||||
}
|
||||
TextField {
|
||||
id: componentFilePath
|
||||
readOnly: true
|
||||
}
|
||||
Button {
|
||||
text: qsTr("Choose...")
|
||||
onClicked: {
|
||||
saveCompFileDialog.open()
|
||||
exportMaterialDialog.aboutToHide()
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Text {
|
||||
text: qsTr("Vertex:")
|
||||
color: palette.text
|
||||
}
|
||||
TextField {
|
||||
id: vertexFilename
|
||||
enabled: (editorView.vertexEditor.text !== "")
|
||||
validator: nameValidator
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Text {
|
||||
text: qsTr("Fragment:")
|
||||
color: palette.text
|
||||
}
|
||||
TextField {
|
||||
id: fragmentFilename
|
||||
enabled: (editorView.fragmentEditor.text !== "")
|
||||
validator: nameValidator
|
||||
}
|
||||
}
|
||||
|
||||
DialogButtonBox {
|
||||
Button {
|
||||
text: qsTr("Export")
|
||||
enabled: (componentFilePath.text !== "" && (!vertexFilename.enabled || (vertexFilename.enabled && vertexFilename.text !== "")) && (!fragmentFilename.enabled || (fragmentFilename.enabled && fragmentFilename.text !== "")))
|
||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||
onClicked: exportMaterialDialog.accept()
|
||||
}
|
||||
Button {
|
||||
text: qsTr("Cancel")
|
||||
DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole
|
||||
onClicked: exportMaterialDialog.reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
materialAdapter.exportQmlComponent(componentFilePath.text, vertexFilename.text, fragmentFilename.text)
|
||||
}
|
||||
}
|
||||
|
||||
SaveChangesDialog {
|
||||
id: saveChangesDialog
|
||||
materialAdapter: materialAdapter
|
||||
saveAsDialog: saveAsDialog
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
AboutDialog {
|
||||
id: aboutDialog
|
||||
parent: Overlay.overlay
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
function saveAction() {
|
||||
// 1. No file name(s) given (call saveAs)
|
||||
let materialSaveFileUrl = new URL(materialAdapter.materialSaveFile)
|
||||
if (materialSaveFileUrl.toString().length > 0)
|
||||
materialAdapter.save()
|
||||
else
|
||||
saveAsAction()
|
||||
}
|
||||
function openAction() {
|
||||
openMaterialDialog.open()
|
||||
}
|
||||
function newAction() {
|
||||
saveChangesDialog.doIfChangesSavedOrDiscarded(() => { materialAdapter.reset() });
|
||||
materialAdapter.reset()
|
||||
}
|
||||
function saveAsAction() {
|
||||
saveAsDialog.open()
|
||||
}
|
||||
function quitAction() {
|
||||
Qt.quit()
|
||||
}
|
||||
function aboutAction() {
|
||||
aboutDialog.open()
|
||||
}
|
||||
|
||||
function importFragmentShader() {
|
||||
fragmentShaderImportDialog.open()
|
||||
}
|
||||
|
||||
function importVertexShader() {
|
||||
vertexShaderImportDialog.open()
|
||||
}
|
||||
|
||||
function exportMaterial() {
|
||||
exportMaterialDialog.open()
|
||||
}
|
||||
|
||||
menuBar: MenuBar {
|
||||
Menu {
|
||||
title: qsTr("&File")
|
||||
Action { text: qsTr("&New..."); onTriggered: window.newAction(); }
|
||||
Action { text: qsTr("&Open..."); onTriggered: window.openAction(); }
|
||||
Action { text: qsTr("&Save"); onTriggered: window.saveAction(); }
|
||||
Action { text: qsTr("Save &As..."); onTriggered: window.saveAsAction(); }
|
||||
MenuSeparator { }
|
||||
Menu {
|
||||
title: qsTr("Import")
|
||||
Action { text: qsTr("Fragment Shader"); onTriggered: window.importFragmentShader(); }
|
||||
Action { text: qsTr("Vertex Shader"); onTriggered: window.importVertexShader(); }
|
||||
}
|
||||
Action { text: qsTr("Export"); onTriggered: window.exportMaterial(); }
|
||||
|
||||
MenuSeparator { }
|
||||
Action { text: qsTr("&Quit"); onTriggered: window.quitAction(); }
|
||||
}
|
||||
Menu {
|
||||
title: qsTr("&Help")
|
||||
Action { text: qsTr("&About"); onTriggered: window.aboutAction(); }
|
||||
}
|
||||
}
|
||||
|
||||
SplitView {
|
||||
id: mainSplitView
|
||||
anchors.fill: parent
|
||||
orientation: Qt.Horizontal
|
||||
EditorView {
|
||||
id: editorView
|
||||
vertexTabText: "Vertex Shader"
|
||||
fragmentTabText: "Fragment Shader"
|
||||
SplitView.preferredWidth: window.width * 0.5
|
||||
SplitView.fillWidth: true
|
||||
materialAdapter: materialAdapter
|
||||
instanceEntry: preview.instanceEntry
|
||||
targetModel: preview.modelInstance
|
||||
}
|
||||
Preview {
|
||||
id: preview
|
||||
implicitWidth: parent.width * 0.5
|
||||
currentMaterial: materialAdapter.material
|
||||
}
|
||||
}
|
||||
|
||||
function outputLine(lineText) {
|
||||
// Prepend
|
||||
editorView.outputTextItem.text = lineText + "\n" + editorView.outputTextItem.text;
|
||||
}
|
||||
|
||||
function printShaderStatusError(stage, msg) {
|
||||
let outputString = ""
|
||||
outputString += msg.filename + " => " + msg.message
|
||||
if (msg.identifier !== null && msg.identifier !== "")
|
||||
outputString += " '" + msg.identifier + "'";
|
||||
if (msg.line >= 0)
|
||||
outputString += ", on line: " + msg.line
|
||||
outputLine(outputString)
|
||||
}
|
||||
|
||||
MaterialAdapter {
|
||||
id: materialAdapter
|
||||
vertexShader: editorView.vertexEditor.text
|
||||
fragmentShader: editorView.fragmentEditor.text
|
||||
rootNode: preview.rootNode
|
||||
uniformModel: editorView.uniformModel
|
||||
onVertexStatusChanged: {
|
||||
if (vertexStatus.status !== ShaderConstants.Success) {
|
||||
editorView.tabBarInfoView.currentIndex = 1
|
||||
window.printShaderStatusError(ShaderConstants.Vertex, vertexStatus)
|
||||
} else if (fragmentStatus.status === ShaderConstants.Success){
|
||||
// both work, clear
|
||||
editorView.outputTextItem.text = "";
|
||||
}
|
||||
}
|
||||
onFragmentStatusChanged: {
|
||||
if (fragmentStatus.status !== ShaderConstants.Success) {
|
||||
editorView.tabBarInfoView.currentIndex = 1
|
||||
window.printShaderStatusError(ShaderConstants.Fragment, fragmentStatus)
|
||||
} else if (vertexStatus.status === ShaderConstants.Success) {
|
||||
// both work, clear
|
||||
editorView.outputTextItem.text = "";
|
||||
}
|
||||
}
|
||||
|
||||
onVertexShaderChanged: {
|
||||
editorView.vertexEditor.text = materialAdapter.vertexShader
|
||||
}
|
||||
onFragmentShaderChanged: {
|
||||
editorView.fragmentEditor.text = materialAdapter.fragmentShader
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
import QtQuick.tooling 1.2
|
||||
|
||||
// This file describes the plugin-supplied types contained in the library.
|
||||
// It is used for QML tooling purposes only.
|
||||
//
|
||||
// This file was auto-generated by qmltyperegistrar.
|
||||
|
||||
Module {
|
||||
Component {
|
||||
file: "materialadapter.h"
|
||||
lineNumber: 31
|
||||
name: "MaterialAdapter"
|
||||
accessSemantics: "reference"
|
||||
prototype: "QObject"
|
||||
exports: ["QtQuick3D.MaterialEditor/MaterialAdapter 1.0"]
|
||||
exportMetaObjectRevisions: [256]
|
||||
Property {
|
||||
name: "material"
|
||||
type: "QQuick3DCustomMaterial"
|
||||
isPointer: true
|
||||
read: "material"
|
||||
notify: "materialChanged"
|
||||
index: 0
|
||||
lineNumber: 40
|
||||
isReadonly: true
|
||||
}
|
||||
Property {
|
||||
name: "rootNode"
|
||||
type: "QQuick3DNode"
|
||||
isPointer: true
|
||||
read: "rootNode"
|
||||
write: "setRootNode"
|
||||
notify: "rootNodeChanged"
|
||||
index: 1
|
||||
lineNumber: 41
|
||||
}
|
||||
Property {
|
||||
name: "fragmentShader"
|
||||
type: "QString"
|
||||
read: "fragmentShader"
|
||||
write: "setFragmentShader"
|
||||
notify: "fragmentShaderChanged"
|
||||
index: 2
|
||||
lineNumber: 42
|
||||
}
|
||||
Property {
|
||||
name: "vertexShader"
|
||||
type: "QString"
|
||||
read: "vertexShader"
|
||||
write: "setVertexShader"
|
||||
notify: "vertexShaderChanged"
|
||||
index: 3
|
||||
lineNumber: 43
|
||||
}
|
||||
Property {
|
||||
name: "vertexStatus"
|
||||
type: "ShaderBuildMessage"
|
||||
read: "vertexStatus"
|
||||
notify: "vertexStatusChanged"
|
||||
index: 4
|
||||
lineNumber: 44
|
||||
isReadonly: true
|
||||
}
|
||||
Property {
|
||||
name: "fragmentStatus"
|
||||
type: "ShaderBuildMessage"
|
||||
read: "fragmentStatus"
|
||||
notify: "fragmentStatusChanged"
|
||||
index: 5
|
||||
lineNumber: 45
|
||||
isReadonly: true
|
||||
}
|
||||
Property {
|
||||
name: "uniformModel"
|
||||
type: "UniformModel"
|
||||
isPointer: true
|
||||
read: "uniformModel"
|
||||
write: "setUniformModel"
|
||||
notify: "uniformModelChanged"
|
||||
index: 6
|
||||
lineNumber: 46
|
||||
}
|
||||
Property {
|
||||
name: "unsavedChanges"
|
||||
type: "bool"
|
||||
read: "unsavedChanges"
|
||||
write: "setUnsavedChanges"
|
||||
notify: "unsavedChangesChanged"
|
||||
index: 7
|
||||
lineNumber: 47
|
||||
}
|
||||
Property {
|
||||
name: "materialSaveFile"
|
||||
type: "QUrl"
|
||||
read: "materialSaveFile"
|
||||
write: "setMaterialSaveFile"
|
||||
notify: "materialSaveFileChanged"
|
||||
index: 8
|
||||
lineNumber: 48
|
||||
}
|
||||
Property {
|
||||
name: "cullMode"
|
||||
type: "QQuick3DMaterial::CullMode"
|
||||
read: "cullMode"
|
||||
write: "setCullMode"
|
||||
notify: "cullModeChanged"
|
||||
index: 9
|
||||
lineNumber: 50
|
||||
}
|
||||
Property {
|
||||
name: "depthDrawMode"
|
||||
type: "QQuick3DMaterial::DepthDrawMode"
|
||||
read: "depthDrawMode"
|
||||
write: "setDepthDrawMode"
|
||||
notify: "depthDrawModeChanged"
|
||||
index: 10
|
||||
lineNumber: 51
|
||||
}
|
||||
Property {
|
||||
name: "shadingMode"
|
||||
type: "QQuick3DCustomMaterial::ShadingMode"
|
||||
read: "shadingMode"
|
||||
write: "setShadingMode"
|
||||
notify: "shadingModeChanged"
|
||||
index: 11
|
||||
lineNumber: 52
|
||||
}
|
||||
Property {
|
||||
name: "sourceBlend"
|
||||
type: "QQuick3DCustomMaterial::BlendMode"
|
||||
read: "srcBlend"
|
||||
write: "setSrcBlend"
|
||||
notify: "srcBlendChanged"
|
||||
index: 12
|
||||
lineNumber: 53
|
||||
}
|
||||
Property {
|
||||
name: "destinationBlend"
|
||||
type: "QQuick3DCustomMaterial::BlendMode"
|
||||
read: "dstBlend"
|
||||
write: "setDstBlend"
|
||||
notify: "dstBlendChanged"
|
||||
index: 13
|
||||
lineNumber: 54
|
||||
}
|
||||
Signal { name: "materialChanged"; lineNumber: 110 }
|
||||
Signal { name: "fragmentShaderChanged"; lineNumber: 111 }
|
||||
Signal { name: "vertexShaderChanged"; lineNumber: 112 }
|
||||
Signal { name: "vertexStatusChanged"; lineNumber: 113 }
|
||||
Signal { name: "uniformModelChanged"; lineNumber: 114 }
|
||||
Signal { name: "fragmentStatusChanged"; lineNumber: 115 }
|
||||
Signal { name: "unsavedChangesChanged"; lineNumber: 116 }
|
||||
Signal { name: "materialSaveFileChanged"; lineNumber: 117 }
|
||||
Signal { name: "errorOccurred"; lineNumber: 119 }
|
||||
Signal { name: "postMaterialSaved"; lineNumber: 120 }
|
||||
Signal { name: "rootNodeChanged"; lineNumber: 121 }
|
||||
Signal { name: "cullModeChanged"; lineNumber: 122 }
|
||||
Signal { name: "depthDrawModeChanged"; lineNumber: 123 }
|
||||
Signal { name: "shadingModeChanged"; lineNumber: 124 }
|
||||
Signal { name: "srcBlendChanged"; lineNumber: 125 }
|
||||
Signal { name: "dstBlendChanged"; lineNumber: 126 }
|
||||
Method {
|
||||
name: "importFragmentShader"
|
||||
lineNumber: 101
|
||||
Parameter { name: "shaderFile"; type: "QUrl" }
|
||||
}
|
||||
Method {
|
||||
name: "importVertexShader"
|
||||
lineNumber: 102
|
||||
Parameter { name: "shaderFile"; type: "QUrl" }
|
||||
}
|
||||
Method { name: "save"; type: "bool"; lineNumber: 103 }
|
||||
Method {
|
||||
name: "saveMaterial"
|
||||
type: "bool"
|
||||
lineNumber: 104
|
||||
Parameter { name: "materialFile"; type: "QUrl" }
|
||||
}
|
||||
Method {
|
||||
name: "loadMaterial"
|
||||
type: "bool"
|
||||
lineNumber: 105
|
||||
Parameter { name: "materialFile"; type: "QUrl" }
|
||||
}
|
||||
Method {
|
||||
name: "exportQmlComponent"
|
||||
type: "bool"
|
||||
lineNumber: 106
|
||||
Parameter { name: "componentFile"; type: "QUrl" }
|
||||
Parameter { name: "vertName"; type: "QString" }
|
||||
Parameter { name: "fragName"; type: "QString" }
|
||||
}
|
||||
Method { name: "reset"; lineNumber: 107 }
|
||||
Method {
|
||||
name: "getSupportedImageFormatsFilter"
|
||||
type: "QString"
|
||||
isMethodConstant: true
|
||||
lineNumber: 98
|
||||
}
|
||||
}
|
||||
Component {
|
||||
file: "qsyntaxhighlighter.h"
|
||||
lineNumber: 24
|
||||
name: "QSyntaxHighlighter"
|
||||
accessSemantics: "reference"
|
||||
prototype: "QObject"
|
||||
Method { name: "rehighlight"; lineNumber: 37 }
|
||||
Method {
|
||||
name: "rehighlightBlock"
|
||||
lineNumber: 38
|
||||
Parameter { name: "block"; type: "QTextBlock" }
|
||||
}
|
||||
Method {
|
||||
name: "_q_reformatBlocks"
|
||||
lineNumber: 59
|
||||
Parameter { name: "from"; type: "int" }
|
||||
Parameter { name: "charsRemoved"; type: "int" }
|
||||
Parameter { name: "charsAdded"; type: "int" }
|
||||
}
|
||||
Method { name: "_q_delayedRehighlight"; lineNumber: 60 }
|
||||
}
|
||||
Component {
|
||||
file: "buildmessage.h"
|
||||
lineNumber: 32
|
||||
name: "ShaderBuildMessage"
|
||||
accessSemantics: "value"
|
||||
exports: ["QtQuick3D.MaterialEditor/shaderStatus 1.0"]
|
||||
isCreatable: false
|
||||
exportMetaObjectRevisions: [256]
|
||||
Enum {
|
||||
name: "Status"
|
||||
isScoped: true
|
||||
lineNumber: 43
|
||||
values: ["Success", "Error"]
|
||||
}
|
||||
Enum {
|
||||
name: "Stage"
|
||||
isScoped: true
|
||||
lineNumber: 49
|
||||
values: ["Vertex", "Fragment"]
|
||||
}
|
||||
Property {
|
||||
name: "filename"
|
||||
type: "QString"
|
||||
read: "filename"
|
||||
index: 0
|
||||
lineNumber: 35
|
||||
isReadonly: true
|
||||
}
|
||||
Property {
|
||||
name: "message"
|
||||
type: "QString"
|
||||
read: "message"
|
||||
index: 1
|
||||
lineNumber: 36
|
||||
isReadonly: true
|
||||
}
|
||||
Property {
|
||||
name: "identifier"
|
||||
type: "QString"
|
||||
read: "identifier"
|
||||
index: 2
|
||||
lineNumber: 37
|
||||
isReadonly: true
|
||||
}
|
||||
Property { name: "line"; type: "qlonglong"; read: "line"; index: 3; lineNumber: 38; isReadonly: true }
|
||||
Property {
|
||||
name: "status"
|
||||
type: "Status"
|
||||
read: "status"
|
||||
index: 4
|
||||
lineNumber: 39
|
||||
isReadonly: true
|
||||
}
|
||||
Property { name: "stage"; type: "Stage"; read: "stage"; index: 5; lineNumber: 40; isReadonly: true }
|
||||
}
|
||||
Component {
|
||||
file: "buildmessage.h"
|
||||
lineNumber: 78
|
||||
name: "ShaderBuildMessageDerived"
|
||||
accessSemantics: "none"
|
||||
prototype: "ShaderBuildMessage"
|
||||
exports: ["QtQuick3D.MaterialEditor/ShaderConstants 1.0"]
|
||||
isCreatable: false
|
||||
exportMetaObjectRevisions: [256]
|
||||
}
|
||||
Component {
|
||||
file: "syntaxhighlighter.h"
|
||||
lineNumber: 16
|
||||
name: "SyntaxHighlighter"
|
||||
accessSemantics: "reference"
|
||||
prototype: "QSyntaxHighlighter"
|
||||
exports: ["QtQuick3D.MaterialEditor/SyntaxHighlighter 1.0"]
|
||||
exportMetaObjectRevisions: [256]
|
||||
Property {
|
||||
name: "document"
|
||||
type: "QQuickTextDocument"
|
||||
isPointer: true
|
||||
read: "document"
|
||||
write: "setDocument"
|
||||
notify: "documentChanged"
|
||||
index: 0
|
||||
lineNumber: 19
|
||||
}
|
||||
Signal { name: "documentChanged"; lineNumber: 35 }
|
||||
}
|
||||
Component {
|
||||
file: "uniformmodel.h"
|
||||
lineNumber: 13
|
||||
name: "UniformModel"
|
||||
accessSemantics: "reference"
|
||||
prototype: "QAbstractListModel"
|
||||
exports: ["QtQuick3D.MaterialEditor/UniformModel 1.0"]
|
||||
exportMetaObjectRevisions: [256]
|
||||
Enum {
|
||||
name: "UniformType"
|
||||
lineNumber: 21
|
||||
values: [
|
||||
"Bool",
|
||||
"Int",
|
||||
"Float",
|
||||
"Vec2",
|
||||
"Vec3",
|
||||
"Vec4",
|
||||
"Mat44",
|
||||
"Sampler"
|
||||
]
|
||||
}
|
||||
Method {
|
||||
name: "insertRow"
|
||||
type: "bool"
|
||||
lineNumber: 47
|
||||
Parameter { name: "rowIndex"; type: "int" }
|
||||
Parameter { name: "type"; type: "int" }
|
||||
Parameter { name: "id"; type: "QString" }
|
||||
}
|
||||
Method {
|
||||
name: "removeRow"
|
||||
lineNumber: 48
|
||||
Parameter { name: "rowIndex"; type: "int" }
|
||||
Parameter { name: "rows"; type: "int" }
|
||||
}
|
||||
Method {
|
||||
name: "removeRow"
|
||||
isCloned: true
|
||||
lineNumber: 48
|
||||
Parameter { name: "rowIndex"; type: "int" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
module QtQuick3D.MaterialEditor
|
||||
typeinfo plugins.qmltypes
|
||||
import QtQuick3D
|
||||
prefer :/qt-project.org/imports/QtQuick3D/MaterialEditor/
|
||||
ShaderEditor 1.0 ShaderEditor.qml
|
||||
EditorView 1.0 EditorView.qml
|
||||
Preview 1.0 Preview.qml
|
||||
PreviewControls 1.0 PreviewControls.qml
|
||||
FrostedGlass 1.0 FrostedGlass.qml
|
||||
AboutDialog 1.0 AboutDialog.qml
|
||||
MaterialPropertiesPane 1.0 MaterialPropertiesPane.qml
|
||||
InstancingPropertiesPane 1.0 InstancingPropertiesPane.qml
|
||||
SaveChangesDialog 1.0 SaveChangesDialog.qml
|
||||
UniformManagerPane 1.0 UniformManagerPane.qml
|
||||
depends QtQuick
|
||||
|
||||
Reference in New Issue
Block a user