Revision control

Copy as Markdown

// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.15
import QtQuick.Layouts 1.15
import HelperWidgets 2.0
import StudioTheme 1.0 as StudioTheme
Column {
width: parent.width
Section {
width: parent.width
caption: qsTr("Dynamic Rigid Body")
SectionLayout {
id: baseSectionLayout
property bool isDefaultDensityMode: massModeComboBox.currentIndex === 0
property bool isCustomDensityMode: massModeComboBox.currentIndex === 1
property bool isMassMode: massModeComboBox.currentIndex === 2
property bool isMassAndInertiaTensorMode: massModeComboBox.currentIndex === 3
property bool isMassAndInertiaMatrixMode: massModeComboBox.currentIndex === 4
PropertyLabel {
text: "Mass Mode"
tooltip: "Describes how mass and inertia are calculated for this body."
}
SecondColumnLayout {
ComboBox {
id: massModeComboBox
scope: "DynamicRigidBody"
model: ["DefaultDensity", "CustomDensity", "Mass", "MassAndInertiaTensor", "MassAndInertiaMatrix"]
backendValue: backendValues.massMode
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
}
ExpandingSpacer {}
}
PropertyLabel {
visible: baseSectionLayout.isMassAndInertiaMatrixMode || baseSectionLayout.isMassAndInertiaTensorMode
}
SecondColumnLayout {
visible: baseSectionLayout.isMassAndInertiaMatrixMode || baseSectionLayout.isMassAndInertiaTensorMode
Item {
// spacer for the always hiden action indicator
width: StudioTheme.Values.actionIndicatorWidth
}
Label {
text: qsTr("Tensor and Matrix modes require QML code.")
Layout.fillWidth: true
Layout.preferredWidth: StudioTheme.Values.singleControlColumnWidth
Layout.minimumWidth: StudioTheme.Values.singleControlColumnWidth
Layout.maximumWidth: StudioTheme.Values.singleControlColumnWidth
}
}
PropertyLabel {
visible: !baseSectionLayout.isDefaultDensityMode && !baseSectionLayout.isCustomDensityMode
text: "Mass"
tooltip: "The mass of the body."
}
SecondColumnLayout {
visible: !baseSectionLayout.isDefaultDensityMode && !baseSectionLayout.isCustomDensityMode
SpinBox {
minimumValue: 0
maximumValue: 9999999
decimals: 2
stepSize: 0.01
backendValue: backendValues.mass
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
}
ExpandingSpacer {}
}
PropertyLabel {
visible: baseSectionLayout.isCustomDensityMode
text: "Density"
tooltip: "The density of the body."
}
SecondColumnLayout {
visible: baseSectionLayout.isCustomDensityMode
SpinBox {
minimumValue: -1
maximumValue: 9999999
decimals: 2
stepSize: 0.01
backendValue: backendValues.density
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
}
ExpandingSpacer {}
}
PropertyLabel {
text: "Enable Gravity"
tooltip: "Sets if the body affected by gravity."
}
SecondColumnLayout {
CheckBox {
text: backendValues.gravityEnabled.valueToString
backendValue: backendValues.gravityEnabled
implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
}
ExpandingSpacer {}
}
PropertyLabel {
text: "CCD Mode"
tooltip: "Continuous collision detection mode for this body (None, SpeculativeCCD, SweepBasedCCD)."
}
SecondColumnLayout {
ComboBox {
backendValue: backendValues.ccd
model: ["None", "SpeculativeCCD", "SweepBasedCCD"]
scope: "DynamicRigidBody"
implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
}
ExpandingSpacer {}
}
PropertyLabel {
text: "Linear Axis Lock"
tooltip: "Lock the linear axis of the body."
}
SecondColumnLayout {
ActionIndicator {
id: linearAxisLockController
icon.color: extFuncLogic.color
icon.text: extFuncLogic.glyph
onClicked: extFuncLogic.show()
forceVisible: extFuncLogic.menuVisible
visible: true
property var enableLockX: { "value": false, "isInModel": false}
property var enableLockY: { "value": false, "isInModel": false}
property var enableLockZ: { "value": false, "isInModel": false}
property variant backendValue: backendValues.linearAxisLock
property variant valueFromBackend: backendValue === undefined ? 0 : backendValue.value
property bool blockLocks: false
onBackendValueChanged: evaluateLocks()
onValueFromBackendChanged: evaluateLocks()
Connections {
target: modelNodeBackend
function onSelectionChanged() {
evaluateLevels()
}
}
Component.onCompleted: evaluateLocks()
function evaluateLocks() {
blockLocks = true
enableLockX = { "value": valueFromBackend & 1, "isInModel": false}
enableLockY = { "value": valueFromBackend & 2, "isInModel": false}
enableLockZ = { "value": valueFromBackend & 4, "isInModel": false}
blockLocks = false
}
function composeExpressionString() {
if (blockLocks)
return
let expressionStr = "";
if (enableLockX.value || enableLockY.value || enableLockY.value) {
if (enableLockX.value)
expressionStr += " | DynamicRigidBody.LockX";
if (enableLockY.value)
expressionStr += " | DynamicRigidBody.LockY";
if (enableLockZ.value)
expressionStr += " | DynamicRigidBody.LockZ";
expressionStr = expressionStr.substring(3);
backendValue.expression = expressionStr
} else {
expressionStr = "DynamicRigidBody.None";
backendValue.expression = expressionStr
}
}
ExtendedFunctionLogic {
id: extFuncLogic
backendValue: backendValues.linearAxisLock
onReseted: {
linearAxisLockController.enableLockX = { "value": false, "isInModel": false}
linearAxisLockController.enableLockY = { "value": false, "isInModel": false}
linearAxisLockController.enableLockZ = { "value": false, "isInModel": false}
linearAxisLockController.evaluateLocks()
}
}
}
}
PropertyLabel {
// spacer
}
SecondColumnLayout {
Item {
// spacer for the always hiden action indicator
width: StudioTheme.Values.actionIndicatorWidth
}
CheckBox {
text: qsTr("Lock X")
backendValue: linearAxisLockController.enableLockX
actionIndicatorVisible: false
onCheckedChanged: linearAxisLockController.composeExpressionString()
implicitWidth: StudioTheme.Values.twoControlColumnWidth
}
ExpandingSpacer {}
}
PropertyLabel {
// spacer
}
SecondColumnLayout {
Item {
// spacer for the always hiden action indicator
width: StudioTheme.Values.actionIndicatorWidth
}
CheckBox {
text: qsTr("Lock Y")
backendValue: linearAxisLockController.enableLockY
actionIndicatorVisible: false
onCheckedChanged: linearAxisLockController.composeExpressionString()
implicitWidth: StudioTheme.Values.twoControlColumnWidth
}
ExpandingSpacer {}
}
PropertyLabel {
// spacer
}
SecondColumnLayout {
Item {
// spacer for the always hiden action indicator
width: StudioTheme.Values.actionIndicatorWidth
}
CheckBox {
text: qsTr("Lock Z")
backendValue: linearAxisLockController.enableLockZ
actionIndicatorVisible: false
onCheckedChanged: linearAxisLockController.composeExpressionString()
implicitWidth: StudioTheme.Values.twoControlColumnWidth
}
ExpandingSpacer {}
}
PropertyLabel {
text: "Angular Axis Lock"
tooltip: "Lock the angular axis of the body."
}
SecondColumnLayout {
ActionIndicator {
id: angularAxisLockController
icon.color: extFuncLogicAngular.color
icon.text: extFuncLogicAngular.glyph
onClicked: extFuncLogicAngular.show()
forceVisible: extFuncLogic.menuVisible
visible: true
property var enableLockX: { "value": false, "isInModel": false}
property var enableLockY: { "value": false, "isInModel": false}
property var enableLockZ: { "value": false, "isInModel": false}
property variant backendValue: backendValues.angularAxisLock
property variant valueFromBackend: backendValue === undefined ? 0 : backendValue.value
property bool blockLocks: false
onBackendValueChanged: evaluateLocks()
onValueFromBackendChanged: evaluateLocks()
Connections {
target: modelNodeBackend
function onSelectionChanged() {
evaluateLevels()
}
}
Component.onCompleted: evaluateLocks()
function evaluateLocks() {
blockLocks = true
enableLockX = { "value": valueFromBackend & 1, "isInModel": false}
enableLockY = { "value": valueFromBackend & 2, "isInModel": false}
enableLockZ = { "value": valueFromBackend & 4, "isInModel": false}
blockLocks = false
}
function composeExpressionString() {
if (blockLocks)
return
let expressionStr = "";
if (enableLockX.value || enableLockY.value || enableLockY.value) {
if (enableLockX.value)
expressionStr += " | DynamicRigidBody.LockX";
if (enableLockY.value)
expressionStr += " | DynamicRigidBody.LockY";
if (enableLockZ.value)
expressionStr += " | DynamicRigidBody.LockZ";
expressionStr = expressionStr.substring(3);
backendValue.expression = expressionStr
} else {
expressionStr = "DynamicRigidBody.None";
backendValue.expression = expressionStr
}
}
ExtendedFunctionLogic {
id: extFuncLogicAngular
backendValue: backendValues.angularAxisLock
onReseted: {
angularAxisLockController.enableLockX = { "value": false, "isInModel": false}
angularAxisLockController.enableLockY = { "value": false, "isInModel": false}
angularAxisLockController.enableLockZ = { "value": false, "isInModel": false}
angularAxisLockController.evaluateLocks()
}
}
}
}
PropertyLabel {
// spacer
}
SecondColumnLayout {
Item {
// spacer for the always hiden action indicator
width: StudioTheme.Values.actionIndicatorWidth
}
CheckBox {
text: qsTr("Lock X")
backendValue: angularAxisLockController.enableLockX
actionIndicatorVisible: false
onCheckedChanged: angularAxisLockController.composeExpressionString()
implicitWidth: StudioTheme.Values.twoControlColumnWidth
}
ExpandingSpacer {}
}
PropertyLabel {
// spacer
}
SecondColumnLayout {
Item {
// spacer for the always hiden action indicator
width: StudioTheme.Values.actionIndicatorWidth
}
CheckBox {
text: qsTr("Lock Y")
backendValue: angularAxisLockController.enableLockY
actionIndicatorVisible: false
onCheckedChanged: angularAxisLockController.composeExpressionString()
implicitWidth: StudioTheme.Values.twoControlColumnWidth
}
ExpandingSpacer {}
}
PropertyLabel {
// spacer
}
SecondColumnLayout {
Item {
// spacer for the always hiden action indicator
width: StudioTheme.Values.actionIndicatorWidth
}
CheckBox {
text: qsTr("Lock Z")
backendValue: angularAxisLockController.enableLockZ
actionIndicatorVisible: false
onCheckedChanged: angularAxisLockController.composeExpressionString()
implicitWidth: StudioTheme.Values.twoControlColumnWidth
}
ExpandingSpacer {}
}
PropertyLabel {
text: "Is Kinematic"
tooltip: "Kinematic objects are not influenced by external forces and can be seen as an object of infinite mass."
}
SecondColumnLayout {
CheckBox {
id: isKinematicCheckBox
text: backendValues.isKinematic.valueToString
backendValue: backendValues.isKinematic
implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
}
ExpandingSpacer {}
}
PropertyLabel {
visible: isKinematicCheckBox.checked
text: "Kinematic Position"
tooltip: "The position of the kinematic object."
}
SecondColumnLayout {
visible: isKinematicCheckBox.checked
SpinBox {
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.kinematicPosition_x
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "X"
color: StudioTheme.Values.theme3DAxisXColor
}
ExpandingSpacer {}
}
PropertyLabel {
visible: isKinematicCheckBox.checked
}
SecondColumnLayout {
visible: isKinematicCheckBox.checked
SpinBox {
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.kinematicPosition_y
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "Y"
color: StudioTheme.Values.theme3DAxisYColor
}
ExpandingSpacer {}
}
PropertyLabel {
visible: isKinematicCheckBox.checked
}
SecondColumnLayout {
visible: isKinematicCheckBox.checked
SpinBox {
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.kinematicPosition_z
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "Z"
color: StudioTheme.Values.theme3DAxisZColor
}
ExpandingSpacer {}
}
PropertyLabel {
visible: isKinematicCheckBox.checked
text: "Kinematic Rotation"
tooltip: "The rotation of the kinematic object."
}
SecondColumnLayout {
visible: isKinematicCheckBox.checked
SpinBox {
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.kinematicEulerRotation_x
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "X"
color: StudioTheme.Values.theme3DAxisXColor
}
ExpandingSpacer {}
}
PropertyLabel {
visible: isKinematicCheckBox.checked
}
SecondColumnLayout {
visible: isKinematicCheckBox.checked
SpinBox {
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.kinematicEulerRotation_y
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "Y"
color: StudioTheme.Values.theme3DAxisYColor
}
ExpandingSpacer {}
}
PropertyLabel {
visible: isKinematicCheckBox.checked
}
SecondColumnLayout {
visible: isKinematicCheckBox.checked
SpinBox {
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.kinematicEulerRotation_z
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "Z"
color: StudioTheme.Values.theme3DAxisZColor
}
ExpandingSpacer {}
}
PropertyLabel {
visible: isKinematicCheckBox.checked
text: "Kinematic Pivot"
tooltip: "The pivot point of the kinematic object."
}
SecondColumnLayout {
visible: isKinematicCheckBox.checked
SpinBox {
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.kinematicPivot_x
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "X"
color: StudioTheme.Values.theme3DAxisXColor
}
ExpandingSpacer {}
}
PropertyLabel {
visible: isKinematicCheckBox.checked
}
SecondColumnLayout {
visible: isKinematicCheckBox.checked
SpinBox {
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.kinematicPivot_y
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "Y"
color: StudioTheme.Values.theme3DAxisYColor
}
ExpandingSpacer {}
}
PropertyLabel {
visible: isKinematicCheckBox.checked
}
SecondColumnLayout {
visible: isKinematicCheckBox.checked
SpinBox {
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.kinematicPivot_z
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "Z"
color: StudioTheme.Values.theme3DAxisZColor
}
ExpandingSpacer {}
}
}
}
}
// Other Properties Not covered by the UI
// QVector3D inertiaTensor
// QVector3D centerOfMassPosition
// QQuaternion centerOfMassRotation
// List<float> inertiaMatrix (9 floats for a Mat3x3)