Initial commit

This commit is contained in:
2026-04-29 07:19:21 +03:00
commit 9a8cdfa08a
5964 changed files with 1194660 additions and 0 deletions
@@ -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"
}
}
@@ -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)
}
}
}
}
}
}
@@ -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