/**************************************************************************** ** ** Copyright (C) 2006 NVIDIA Corporation. ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt 3D Studio. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "Qt3DSCommonPrecompile.h" #include "StudioRendererImpl.h" #include "StudioRendererTranslation.h" #include "Qt3DSRenderEffectSystem.h" #include "foundation/StrConvertUTF.h" #include "Qt3DSFileTools.h" #include "Qt3DSRenderUIPLoader.h" #include "Qt3DSRenderWidgets.h" #include "foundation/Qt3DSBounds3.h" #include "Qt3DSRenderResourceManager.h" #include "render/Qt3DSRenderFrameBuffer.h" #include "Qt3DSRenderCamera.h" #include "foundation/Qt3DSPlane.h" #include "Qt3DSRenderRotationHelper.h" #include "Qt3DSRenderPluginGraphObject.h" #include "Qt3DSRenderPlugin.h" #include "StudioCoreSystem.h" #include "Qt3DSDMDataCore.h" #include "Qt3DSRenderPluginPropertyValue.h" #include "Qt3DSRenderEffectSystem.h" #include "render/Qt3DSRenderShaderProgram.h" #include "Qt3DSRenderMaterialHelpers.h" #include "Qt3DSRenderDynamicObjectSystem.h" #include "Qt3DSRenderCustomMaterialSystem.h" #include "Qt3DSRenderReferencedMaterial.h" #include "Qt3DSRenderPixelGraphicsTypes.h" #include "Qt3DSRenderPixelGraphicsRenderer.h" #include "Qt3DSRenderPathManager.h" #include "PathWidget.h" #include "Qt3DSRenderLightmaps.h" #include "StudioPreferences.h" #include "HotKeys.h" #include "Qt3DSRenderCamera.h" #include "Qt3DSRenderLight.h" #pragma warning(disable : 4100) // unreferenced formal parameter using namespace qt3ds::studio; QT3DSU32 qt3ds::studio::g_GraphObjectTranslatorTag; using qt3ds::render::SPGGraphObject; using qt3ds::render::SPGRect; using qt3ds::render::SPGVertLine; using qt3ds::render::SPGHorzLine; using qt3ds::render::NVRenderRectF; using qt3ds::render::NVRenderRect; namespace { using namespace qt3dsdm; struct STranslatorDataModelParser { STranslation &m_Context; Qt3DSDMInstanceHandle m_InstanceHandle; STranslatorDataModelParser(STranslation &inContext, Qt3DSDMInstanceHandle inInstance) : m_Context(inContext) , m_InstanceHandle(inInstance) { } Qt3DSDMInstanceHandle GetInstanceHandle() { return m_InstanceHandle; } template inline Option GetPropertyValue(qt3dsdm::Qt3DSDMPropertyHandle inProperty) { Option theValue = m_Context.m_Reader.GetRawInstancePropertyValue(GetInstanceHandle(), inProperty); if (theValue.hasValue()) return qt3dsdm::get(*theValue); return Empty(); } bool ParseProperty(Qt3DSDMPropertyHandle inProperty, QT3DSF32 &outValue) { Option theValue = GetPropertyValue(inProperty); if (theValue.hasValue()) { outValue = theValue.getValue(); return true; } return false; } bool ParseProperty(Qt3DSDMPropertyHandle inProperty, QT3DSU32 &outValue) { auto theValue = GetPropertyValue(inProperty); if (theValue.hasValue()) { outValue = qMax(theValue.getValue(), 0); return true; } return false; } bool ParseProperty(Qt3DSDMPropertyHandle inProperty, QT3DSI32 &outValue) { auto theValue = GetPropertyValue(inProperty); if (theValue.hasValue()) { outValue = *theValue; return true; } return false; } bool ParseRadianProperty(Qt3DSDMPropertyHandle inProperty, QT3DSF32 &outValue) { if (ParseProperty(inProperty, outValue)) { TORAD(outValue); return true; } return false; } bool ParseRadianProperty(Qt3DSDMPropertyHandle inProperty, QT3DSVec3 &outValue) { if (ParseProperty(inProperty, outValue)) { TORAD(outValue.x); TORAD(outValue.y); TORAD(outValue.z); return true; } return false; } bool ParseOpacityProperty(Qt3DSDMPropertyHandle inProperty, QT3DSF32 &outValue) { if (ParseProperty(inProperty, outValue)) { outValue = (1.0f / 100.0f) * outValue; return true; } return false; } bool ParseRotationOrder(Qt3DSDMPropertyHandle inProperty, QT3DSU32 &outValue) { qt3ds::render::CRegisteredString temp; if (ParseProperty(inProperty, temp)) { outValue = qt3ds::render::MapRotationOrder(temp); return true; } return false; } bool ParseOrientation(Qt3DSDMPropertyHandle inProperty, qt3ds::render::NodeFlags &outValue) { qt3ds::render::CRegisteredString temp; if (ParseProperty(inProperty, temp)) { bool isLeftHanded = strcmp(temp.c_str(), "Left Handed") == 0; outValue.SetLeftHanded(isLeftHanded); return true; } return false; } bool ParseProperty(Qt3DSDMPropertyHandle inProperty, bool &outValue) { Option theValue = GetPropertyValue(inProperty); if (theValue.hasValue()) { outValue = theValue.getValue(); return true; } return false; } bool ParseProperty(Qt3DSDMPropertyHandle inProperty, QT3DSVec2 &outValue) { Option theValue = GetPropertyValue(inProperty); if (theValue.hasValue()) { outValue = QT3DSVec2(theValue->m_Floats[0], theValue->m_Floats[1]); return true; } return false; } bool ParseProperty(Qt3DSDMPropertyHandle inProperty, QT3DSVec3 &outValue) { Option theValue = GetPropertyValue(inProperty); if (theValue.hasValue()) { outValue = QT3DSVec3(theValue->m_Floats[0], theValue->m_Floats[1], theValue->m_Floats[2]); return true; } return false; } bool ParseProperty(Qt3DSDMPropertyHandle inProperty, QT3DSVec4 &outValue) { Option theValue = GetPropertyValue(inProperty); if (theValue.hasValue()) { outValue = QT3DSVec4(theValue->m_Floats[0], theValue->m_Floats[1], theValue->m_Floats[2], theValue->m_Floats[3]); return true; } return false; } bool ParseProperty(Qt3DSDMPropertyHandle inProperty, qt3ds::render::CRegisteredString &outValue) { Option theValue = GetPropertyValue(inProperty); if (theValue.hasValue() && *theValue) { qt3ds::render::IStringTable &theStrTable(m_Context.m_Context.GetStringTable()); outValue = theStrTable.RegisterStr((*theValue)->GetData()); return true; } return false; } bool ParseAndResolveSourcePath(qt3dsdm::Qt3DSDMPropertyHandle inProperty, qt3ds::render::CRegisteredString &outValue) { if (ParseProperty(inProperty, outValue)) { if (outValue.IsValid() && outValue.c_str()[0] != '#') { Q3DStudio::CFilePath theDirectory = m_Context.m_Doc.GetDocumentDirectory(); Q3DStudio::CFilePath theResolvedPath = Q3DStudio::CFilePath::CombineBaseAndRelative(theDirectory, outValue.c_str()); if (theResolvedPath.exists()) { outValue = m_Context.m_Context.GetStringTable() .RegisterStr(theResolvedPath.toCString()); } } return true; } return false; } template bool ParseEnumProperty(qt3dsdm::Qt3DSDMPropertyHandle inProperty, TEnumType &ioValue) { qt3ds::render::CRegisteredString temp; if (ParseProperty(inProperty, temp)) { qt3ds::render::SEnumNameMap *theNameMap(qt3ds::render::SEnumParseMap::GetMap()); for (qt3ds::render::SEnumNameMap *theIter = theNameMap; theIter->m_Name && *theIter->m_Name; ++theIter) { // hack to match advanced overlay types, whose name start with a '*' const char8_t *p = temp; if (*p == '*') ++p; if (strcmp(p, theIter->m_Name) == 0) { ioValue = (TEnumType)theIter->m_Enum; return true; } } } return false; } bool ParseNodeFlagsProperty(qt3dsdm::Qt3DSDMPropertyHandle inProperty, qt3ds::render::NodeFlags &outValue, qt3ds::render::NodeFlagValues::Enum theFlag) { bool temp = false; if (ParseProperty(inProperty, temp)) { outValue.ClearOrSet(temp, theFlag); return true; } return false; } bool ParseNodeFlagsInverseProperty(qt3dsdm::Qt3DSDMPropertyHandle inProperty, qt3ds::render::NodeFlags &outValue, qt3ds::render::NodeFlagValues::Enum theFlag) { bool temp = false; if (ParseProperty(inProperty, temp)) { outValue.ClearOrSet(!temp, theFlag); return true; } return false; } bool ParseProperty(Qt3DSDMPropertyHandle inProperty, qt3ds::render::SImage *&ioImage) { Option theData = GetPropertyValue(inProperty); if (theData.hasValue()) { qt3dsdm::Qt3DSDMInstanceHandle theInstance( m_Context.m_Reader.GetInstanceForGuid(*theData)); SGraphObjectTranslator *imageTranslator = m_Context.GetOrCreateTranslator(theInstance); if (imageTranslator && imageTranslator->GetGraphObject().m_Type == qt3ds::render::GraphObjectTypes::Image) { SImage *theNewImage = static_cast(&imageTranslator->GetGraphObject()); ioImage = theNewImage; } else ioImage = nullptr; return true; } return false; } bool ParseProperty(Qt3DSDMPropertyHandle inProperty, qt3ds::render::SGraphObject *&ioObjRef) { Option theData = GetPropertyValue(inProperty); if (theData.hasValue()) { qt3dsdm::Qt3DSDMInstanceHandle theInstance( m_Context.m_Reader.GetInstanceForObjectRef(m_InstanceHandle, *theData)); SGraphObjectTranslator *theItemTranslator = m_Context.GetOrCreateTranslator(theInstance); if (theItemTranslator) ioObjRef = &theItemTranslator->GetGraphObject(); } return true; } bool ParseProperty(Qt3DSDMPropertyHandle inProperty, qt3ds::render::SNode *&ioNodePtr) { Option theData = GetPropertyValue(inProperty); SNode *theNewNodePtr = nullptr; if (theData.hasValue()) { qt3dsdm::Qt3DSDMInstanceHandle theInstance( m_Context.m_Reader.GetInstanceForObjectRef(m_InstanceHandle, *theData)); SGraphObjectTranslator *theItemTranslator = m_Context.GetOrCreateTranslator(theInstance); if (theItemTranslator) { SGraphObject &theObject = theItemTranslator->GetGraphObject(); if (GraphObjectTypes::IsNodeType(theObject.m_Type)) theNewNodePtr = &static_cast(theObject); } } ioNodePtr = theNewNodePtr; return true; } }; // Define parse tables #define Scene_ClearColor m_Scene.m_BackgroundColor #define Scene_UseClearColor m_Scene.m_BgColorEnable #define Node_Rotation m_Node.m_Rotation #define Node_Position m_Node.m_Position #define Node_Scale m_Node.m_Scale #define Node_Pivot m_Node.m_Pivot #define Node_LocalOpacity m_Node.m_Opacity #define Node_RotationOrder m_Node.m_RotationOrder #define Node_LeftHanded m_Node.m_Orientation #define Layer_TemporalAAEnabled m_Layer.m_TemporalAA #define Layer_LayerEnableDepthTest m_Layer.m_DisableDepthTest #define Layer_LayerEnableDepthPrePass m_Layer.m_DisableDepthPrepass #define Layer_ClearColor m_Layer.m_BackgroundColor #define Layer_Background m_Layer.m_Background #define Layer_BlendType m_Layer.m_BlendType #define Layer_Size m_Layer.m_Size #define Layer_Location m_Layer.m_Location #define Layer_ProgressiveAAMode m_Layer.m_ProgressiveAA #define Layer_MultisampleAAMode m_Layer.m_MultisampleAA #define Layer_HorizontalFieldValues m_Layer.m_HorizontalFieldValues #define Layer_Left m_Layer.m_Left #define Layer_LeftUnits m_Layer.m_LeftUnits #define Layer_Width m_Layer.m_Width #define Layer_WidthUnits m_Layer.m_WidthUnits #define Layer_Right m_Layer.m_Right #define Layer_RightUnits m_Layer.m_RightUnits #define Layer_VerticalFieldValues m_Layer.m_VerticalFieldValues #define Layer_Top m_Layer.m_Top #define Layer_TopUnits m_Layer.m_TopUnits #define Layer_Height m_Layer.m_Height #define Layer_HeightUnits m_Layer.m_HeightUnits #define Layer_Bottom m_Layer.m_Bottom #define Layer_BottomUnits m_Layer.m_BottomUnits #define Layer_AoStrength m_Layer.m_AoStrength #define Layer_AoDistance m_Layer.m_AoDistance #define Layer_AoSoftness m_Layer.m_AoSoftness #define Layer_AoBias m_Layer.m_AoBias #define Layer_AoSamplerate m_Layer.m_AoSamplerate #define Layer_AoDither m_Layer.m_AoDither #define Layer_ShadowStrength m_Layer.m_ShadowStrength #define Layer_ShadowDist m_Layer.m_ShadowDist #define Layer_ShadowSoftness m_Layer.m_ShadowSoftness #define Layer_ShadowBias m_Layer.m_ShadowBias #define Layer_LightProbe m_Layer.m_LightProbe #define Layer_ProbeBright m_Layer.m_ProbeBright #define Layer_FastIbl m_Layer.m_FastIbl #define Layer_ProbeHorizon m_Layer.m_ProbeHorizon #define Layer_ProbeFov m_Layer.m_ProbeFov #define Layer_LightProbe2 m_Layer.m_LightProbe2 #define Layer_Probe2Fade m_Layer.m_Probe2Fade #define Layer_Probe2Window m_Layer.m_Probe2Window #define Layer_Probe2Pos m_Layer.m_Probe2Pos #define Layer_TexturePath m_Asset.m_SourcePath #define Camera_ClipNear m_Camera.m_ClipNear #define Camera_ClipFar m_Camera.m_ClipFar #define Camera_FOV m_Camera.m_Fov #define Camera_FOVHorizontal m_Camera.m_FovHorizontal #define Camera_Orthographic m_Camera.m_Orthographic #define Camera_ScaleMode m_Camera.m_ScaleMode #define Camera_ScaleAnchor m_Camera.m_ScaleAnchor #define Light_LightType m_Light.m_LightType #define Light_Scope m_Light.m_Scope #define Light_DiffuseColor m_Light.m_LightColor #define Light_SpecularColor m_Light.m_SpecularColor #define Light_AmbientColor m_Light.m_AmbientColor #define Light_Brightness m_Light.m_Brightness #define Light_LinearFade m_Light.m_LinearFade #define Light_ExponentialFade m_Light.m_ExpFade #define Light_AreaWidth m_Light.m_AreaWidth #define Light_AreaHeight m_Light.m_AreaHeight #define Light_CastShadow m_Light.m_CastShadow #define Light_ShadowBias m_Light.m_ShadowBias #define Light_ShadowFactor m_Light.m_ShadowFactor #define Light_ShadowMapRes m_Light.m_ShadowMapRes #define Light_ShadowMapFar m_Light.m_ShadowMapFar #define Light_ShadowMapFov m_Light.m_ShadowMapFov #define Light_ShadowFilter m_Light.m_ShadowFilter #define Model_MeshPath m_Asset.m_SourcePath #define Model_ShadowCaster m_Model.m_ShadowCaster #define Model_TessellationMode m_Model.m_Tessellation #define Model_EdgeTess m_Model.m_EdgeTess #define Model_InnerTess m_Model.m_InnerTess #define Lightmaps_LightmapIndirect m_Lightmaps.m_LightmapIndirect #define Lightmaps_LightmapRadiosity m_Lightmaps.m_LightmapRadiosity #define Lightmaps_LightmapShadow m_Lightmaps.m_LightmapShadow #define MaterialBase_IblProbe m_MaterialBase.m_IblProbe #define Material_Lighting m_Material.m_ShaderLighting #define Material_BlendMode m_Material.m_BlendMode #define Material_DiffuseColor m_Material.m_DiffuseColor #define Material_DiffuseMaps_0 m_Material.m_DiffuseMap1 #define Material_DiffuseMaps_1 m_Material.m_DiffuseMap2 #define Material_DiffuseMaps_2 m_Material.m_DiffuseMap3 #define Material_EmissivePower m_Material.m_EmissivePower #define Material_EmissiveColor m_Material.m_EmissiveColor #define Material_EmissiveMap m_Material.m_EmissiveMap #define Material_EmissiveMap2 m_Material.m_EmissiveMap2 #define Material_SpecularReflection m_Material.m_SpecularReflection #define Material_SpecularMap m_Material.m_SpecularMap #define Material_SpecularModel m_Material.m_SpecularModel #define Material_SpecularTint m_Material.m_SpecularTint #define Material_IOR m_Material.m_IOR #define Material_FresnelPower m_Material.m_FresnelPower #define Material_SpecularAmount m_Material.m_SpecularAmount #define Material_SpecularRoughness m_Material.m_SpecularRoughness #define Material_RoughnessMap m_Material.m_RoughnessMap #define Material_Opacity m_Material.m_Opacity #define Material_OpacityMap m_Material.m_OpacityMap #define Material_BumpMap m_Material.m_BumpMap #define Material_BumpAmount m_Material.m_BumpAmount #define Material_NormalMap m_Material.m_NormalMap #define Material_DisplacementMap m_Material.m_DisplacementMap #define Material_DisplaceAmount m_Material.m_DisplaceAmount #define Material_TranslucencyMap m_Material.m_TranslucencyMap #define Material_TranslucentFalloff m_Material.m_TranslucentFalloff #define Material_DiffuseLightWrap m_Material.m_DiffuseLightWrap #define Material_ReferencedMaterial m_ReferencedMaterial.m_ReferencedMaterial #define Material_VertexColors m_Material.m_VertexColors #define Image_ImagePath m_Asset.m_SourcePath #define Image_OffscreenRendererId m_Image.m_SubPresentation #define Image_Scale_X m_Image.m_RepeatU #define Image_Scale_Y m_Image.m_RepeatV #define Image_Pivot_X m_Image.m_PivotU #define Image_Pivot_Y m_Image.m_PivotV #define Image_Rotation m_Image.m_RotationUV #define Image_Position_X m_Image.m_PositionU #define Image_Position_Y m_Image.m_PositionV #define Image_MappingMode m_Image.m_TextureMapping #define Image_HorizontalTilingMode m_Image.m_TilingU #define Image_VerticalTilingMode m_Image.m_TilingV #define Text_Text m_Text.m_TextString #define Text_Font m_Text.m_Font #define Text_FontSize m_Text.m_Size #define Text_HorizontalAlignment m_Text.m_HorzAlign #define Text_VerticalAlignment m_Text.m_VertAlign #define Text_Leading m_Text.m_Leading #define Text_Tracking m_Text.m_Tracking #define Text_DropShadow m_Text.m_DropShadow #define Text_DropShadowStrength m_Text.m_DropShadowStrength #define Text_DropShadowOffset m_Text.m_DropShadowOffset // To be removed in 2.x (when UIP version is next updated) #define Text_DropShadowOffsetX m_Text.m_DropShadowOffsetX #define Text_DropShadowOffsetY m_Text.m_DropShadowOffsetY #define Text_DropShadowHorizontalAlignment m_Text.m_DropShadowHorizontalAlignment // To be removed in 2.x (when UIP version is next updated) #define Text_DropShadowVerticalAlignment m_Text.m_DropShadowVerticalAlignment // To be removed in 2.x (when UIP version is next updated) #define Text_WordWrap m_Text.m_WordWrap #define Text_BoundingBox m_Text.m_BoundingBox #define Text_Elide m_Text.m_Elide #define Text_TextColor m_Text.m_TextColor #define Text_EnableAcceleratedFont m_Text.m_EnableAcceleratedFont #define Path_Width m_Path.m_Width #define Path_LinearError m_Path.m_LinearError #define Path_InnerTessAmount m_Path.m_InnerTessAmount #define Path_EdgeTessAmount m_Path.m_EdgeTessAmount #define Path_Opacity m_Path.m_Opacity #define Path_BeginCapping m_Path.m_BeginCap #define Path_BeginCapOffset m_Path.m_BeginCapOffset #define Path_BeginCapOpacity m_Path.m_BeginCapOpacity #define Path_BeginCapWidth m_Path.m_BeginCapWidth #define Path_EndCapping m_Path.m_EndCap #define Path_EndCapOffset m_Path.m_EndCapOffset #define Path_EndCapOpacity m_Path.m_EndCapOpacity #define Path_EndCapWidth m_Path.m_EndCapWidth #define Path_PathType m_Path.m_PathType #define Path_PaintStyle m_Path.m_PaintStyle #define Path_PathBuffer m_Asset.m_SourcePath #define SubPath_Closed m_SubPath.m_Closed // Fill in implementations for the actual parse tables. #define HANDLE_QT3DS_RENDER_PROPERTY(type, name, dirty) \ theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); #define HANDLE_QT3DS_RENDER_VEC3_PROPERTY(type, name, dirty) \ theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); #define HANDLE_QT3DS_RENDER_REAL_VEC2_PROPERTY(type, name, dirty) \ theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); #define HANDLE_QT3DS_RENDER_COLOR_PROPERTY(type, name, dirty) \ theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); #define HANDLE_QT3DS_RENDER_RADIAN_PROPERTY(type, name, dirty) \ theParser.ParseRadianProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); #define HANDLE_QT3DS_RENDER_VEC3_RADIAN_PROPERTY(type, name, dirty) \ theParser.ParseRadianProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); #define HANDLE_QT3DS_RENDER_OPACITY_PROPERTY(type, name, dirty) \ theParser.ParseOpacityProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); #define HANDLE_QT3DS_ROTATION_ORDER_PROPERTY(type, name, dirty) \ theParser.ParseRotationOrder(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); #define HANDLE_QT3DS_NODE_ORIENTATION_PROPERTY(type, name, dirty) \ theParser.ParseOrientation(inContext.m_ObjectDefinitions.type##_##name, theItem.m_Flags); #define HANDLE_QT3DS_RENDER_DEPTH_TEST_PROPERTY(type, name, dirty) \ if (theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name)) \ theItem.m_##name = !theItem.m_##name; #define HANDLE_QT3DS_NODE_FLAGS_PROPERTY(type, name, dirty) \ theParser.ParseNodeFlagsProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_Flags, \ qt3ds::render::NodeFlagValues::name); #define HANDLE_QT3DS_NODE_FLAGS_INVERSE_PROPERTY(type, name, dirty) \ theParser.ParseNodeFlagsInverseProperty(inContext.m_ObjectDefinitions.type##_##name, \ theItem.m_Flags, qt3ds::render::NodeFlagValues::name); #define HANDLE_QT3DS_RENDER_ENUM_PROPERTY(type, name, dirty) \ theParser.ParseEnumProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); #define HANDLE_QT3DS_RENDER_SOURCEPATH_PROPERTY(type, name, dirty) \ theParser.ParseAndResolveSourcePath(inContext.m_ObjectDefinitions.type##_##name, \ theItem.m_##name); #define HANDLE_QT3DS_RENDER_ARRAY_PROPERTY(type, name, index, dirty) \ theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name##_##index, \ theItem.m_##name[index]); #define HANDLE_QT3DS_RENDER_VEC2_PROPERTY(type, name, dirty) \ theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name##_##X, \ theItem.m_##name.x); \ theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name##_##Y, theItem.m_##name.y); #define HANDLE_QT3DS_RENDER_COLOR_VEC3_PROPERTY( \ type, name, dirty) // noop by intention already handled by HANDLE_QT3DS_RENDER_COLOR_PROPERTY #define HANDLE_QT3DS_RENDER_TRANSFORM_VEC3_PROPERTY( \ type, name, dirty) // noop by intention already handled by HANDLE_QT3DS_RENDER_VEC3_PROPERTY struct SSceneTranslator : public SGraphObjectTranslator { SSceneTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SScene)()) { } void PushTranslation(STranslation &inContext) override { SScene &theItem = static_cast(GetGraphObject()); STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); ITERATE_QT3DS_RENDER_SCENE_PROPERTIES SLayer *theCurrentLayer = nullptr; theItem.m_FirstChild = nullptr; for (long idx = 0, end = inContext.m_AssetGraph.GetChildCount(GetInstanceHandle()); idx < end; ++idx) { SGraphObjectTranslator::PushTranslation(inContext); qt3dsdm::Qt3DSDMInstanceHandle theLayer = inContext.m_AssetGraph.GetChild(GetInstanceHandle(), idx); SGraphObjectTranslator *theTranslator = inContext.GetOrCreateTranslator(theLayer); if (theTranslator && theTranslator->GetGraphObject().m_Type == qt3ds::render::GraphObjectTypes::Layer) { SLayer *theLayerObj = static_cast(&theTranslator->GetGraphObject()); theLayerObj->m_NextSibling = nullptr; if (theItem.m_FirstChild == nullptr) theItem.m_FirstChild = theLayerObj; else theCurrentLayer->m_NextSibling = theLayerObj; theCurrentLayer = theLayerObj; } } } void ClearChildren() override { SScene &theItem = static_cast(GetGraphObject()); SLayer *theLastChild = nullptr; for (SLayer *theChild = theItem.m_FirstChild; theChild; theChild = static_cast(theChild->m_NextSibling)) { if (theLastChild) theLastChild->m_NextSibling = nullptr; theChild->m_Parent = nullptr; theLastChild = theChild; } theItem.m_FirstChild = nullptr; } void AppendChild(SGraphObject &inChild) override { if (inChild.m_Type != GraphObjectTypes::Layer) return; SScene &theItem = static_cast(GetGraphObject()); SLayer &theLayer = static_cast(inChild); theItem.AddChild(theLayer); } void SetActive(bool /*inActive*/) override { // How could we not be active? } }; struct SNodeTranslator : public SGraphObjectTranslator { SNodeTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SNode)()) { Initialize(); } SNodeTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, SNode &inNode) : SGraphObjectTranslator(inInstance, inNode) { Initialize(); } void Initialize() { SNode &theNode = static_cast(GetGraphObject()); // Ensure the global transform is valid because we use this before we render sometimes. theNode.m_GlobalTransform = QT3DSMat44::createIdentity(); } static inline bool IsNodeType(qt3ds::render::GraphObjectTypes::Enum inType) { return qt3ds::render::GraphObjectTypes::IsNodeType(inType); } void PushTranslation(STranslation &inContext) override { SGraphObjectTranslator::PushTranslation(inContext); SNode &theItem = static_cast(GetGraphObject()); STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); ITERATE_QT3DS_RENDER_NODE_PROPERTIES theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Node.m_BoneId, theItem.m_SkeletonId); bool ignoresParent = false; if (theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Node.m_IgnoresParent, ignoresParent)) theItem.m_Flags.SetIgnoreParentTransform(ignoresParent); } void AppendChild(SGraphObject &inChild) override { if (GraphObjectTypes::IsNodeType(inChild.m_Type) == false) { QT3DS_ASSERT(false); return; } SNode &theItem = static_cast(GetGraphObject()); SNode &theChild = static_cast(inChild); theItem.AddChild(theChild); theItem.MarkDirty(qt3ds::render::NodeTransformDirtyFlag::TransformIsDirty); theChild.MarkDirty(qt3ds::render::NodeTransformDirtyFlag::TransformIsDirty); } void ClearChildren() override { SNode &theItem = static_cast(GetGraphObject()); SNode *theLastChild = nullptr; for (SNode *theChild = theItem.m_FirstChild; theChild; theChild = theChild->m_NextSibling) { if (theLastChild) theLastChild->m_NextSibling = nullptr; theLastChild = theChild; theChild->m_Parent = nullptr; } theItem.m_FirstChild = nullptr; } void SetActive(bool inActive) override { SNode &theNode = static_cast(GetGraphObject()); if (inActive != theNode.m_Flags.IsActive()) { theNode.m_Flags.SetActive(inActive); theNode.MarkDirty(qt3ds::render::NodeTransformDirtyFlag::TransformIsDirty); } } }; struct SLayerTranslator : public SNodeTranslator { SLayerTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SLayer)()) { } void PushTranslation(STranslation &inContext) override { SNodeTranslator::PushTranslation(inContext); SLayer &theItem = static_cast(GetGraphObject()); STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); ITERATE_QT3DS_RENDER_LAYER_PROPERTIES theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Layer.m_AoSamplerate, theItem.m_AoSamplerate); } void AppendChild(SGraphObject &inChild) override { if (GraphObjectTypes::IsNodeType(inChild.m_Type)) { SNodeTranslator::AppendChild(inChild); } else if (inChild.m_Type == GraphObjectTypes::Effect) { SLayer &theItem = static_cast(GetGraphObject()); theItem.AddEffect(static_cast(inChild)); } else if (inChild.m_Type == GraphObjectTypes::RenderPlugin) { SLayer &theItem = static_cast(GetGraphObject()); theItem.m_RenderPlugin = &static_cast(inChild); } } void ClearChildren() override { SNodeTranslator::ClearChildren(); SLayer &theItem = static_cast(GetGraphObject()); SEffect *theLastChild = nullptr; for (SEffect *theChild = theItem.m_FirstEffect; theChild; theChild = theChild->m_NextEffect) { if (theLastChild) theLastChild->m_NextEffect = nullptr; theLastChild = theChild; theChild->m_Layer = nullptr; } theItem.m_FirstEffect = nullptr; theItem.m_RenderPlugin = nullptr; // Don't clear the light probe properties because those are added/removed as part of the // normal // property scan, they aren't added as generic children. } }; struct SLightTranslator : public SNodeTranslator { SLightTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SLight)()) { } void PushTranslation(STranslation &inContext) override { SNodeTranslator::PushTranslation(inContext); SLight &theItem = static_cast(GetGraphObject()); STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); ITERATE_QT3DS_RENDER_LIGHT_PROPERTIES theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Light.m_ShadowMapRes, theItem.m_ShadowMapRes); } void AppendChild(SGraphObject &inChild) override { SNodeTranslator::AppendChild(inChild); } void ClearChildren() override { SNodeTranslator::ClearChildren(); } }; struct SCameraTranslator : public SNodeTranslator { SCameraTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SCamera)()) { } void PushTranslation(STranslation &inContext) override { SNodeTranslator::PushTranslation(inContext); SCamera &theItem = static_cast(GetGraphObject()); STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); ITERATE_QT3DS_RENDER_CAMERA_PROPERTIES } }; struct SModelTranslator : public SNodeTranslator { SModelTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SModel)()) { } void PushTranslation(STranslation &inContext) override { SNodeTranslator::PushTranslation(inContext); SModel &theItem = static_cast(GetGraphObject()); STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); ITERATE_QT3DS_RENDER_MODEL_PROPERTIES theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Model.m_PoseRoot, theItem.m_SkeletonRoot); theItem.m_FirstMaterial = nullptr; for (long idx = 0, end = inContext.m_AssetGraph.GetChildCount(GetInstanceHandle()); idx < end; ++idx) { qt3dsdm::Qt3DSDMInstanceHandle theItemHandle = inContext.m_AssetGraph.GetChild(GetInstanceHandle(), idx); SGraphObjectTranslator *theTranslator = inContext.GetOrCreateTranslator(theItemHandle); if (theTranslator && IsMaterial(theTranslator->GetGraphObject())) { SGraphObject *theMaterial = &theTranslator->GetGraphObject(); SetNextMaterialSibling(*theMaterial, nullptr); theItem.AddMaterial(*theMaterial); } } } void AppendChild(SGraphObject &inChild) override { if (GraphObjectTypes::IsNodeType(inChild.m_Type)) { SNodeTranslator::AppendChild(inChild); } else if (IsMaterial(inChild)) { SModel &theItem = static_cast(GetGraphObject()); theItem.AddMaterial(inChild); } else { QT3DS_ASSERT(false); } } void ClearChildren() override { SModel &theItem = static_cast(GetGraphObject()); SNodeTranslator::ClearChildren(); SGraphObject *theLastMaterial = nullptr; for (SGraphObject *theMaterial = theItem.m_FirstMaterial; theMaterial; theMaterial = qt3ds::render::GetNextMaterialSibling(theMaterial)) { if (theLastMaterial) qt3ds::render::SetNextMaterialSibling(*theLastMaterial, nullptr); theLastMaterial = theMaterial; } theItem.m_FirstMaterial = nullptr; } }; static SFloat2 ToFloat2(const Option &inValue) { if (inValue.hasValue()) return inValue->getData(); return SFloat2(); } static float ToFloat(const Option &inValue) { if (inValue.hasValue()) return inValue->getData(); return 0.0f; } struct SPathSubPathTranslator : public SGraphObjectTranslator { eastl::vector m_PathBuffer; SPathSubPathTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SPathSubPath)()) { } void PushTranslation(STranslation &inContext) override { SPathSubPath &theItem = static_cast(GetGraphObject()); STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); ITERATE_QT3DS_RENDER_PATH_SUBPATH_PROPERTIES m_PathBuffer.clear(); Q3DStudio::IDocumentReader &theReader(inContext.m_Doc.GetDocumentReader()); QT3DSU32 anchorCount = 0; for (QT3DSI32 idx = 0, end = inContext.m_AssetGraph.GetChildCount(GetInstanceHandle()); idx < end; ++idx) { qt3dsdm::Qt3DSDMInstanceHandle theAnchor = inContext.m_AssetGraph.GetChild(GetInstanceHandle(), idx); if (theReader.GetObjectTypeName(theAnchor) == L"PathAnchorPoint") ++anchorCount; } QT3DSU32 anchorIdx = 0; for (QT3DSI32 idx = 0, end = inContext.m_AssetGraph.GetChildCount(GetInstanceHandle()); idx < end; ++idx) { qt3dsdm::Qt3DSDMInstanceHandle theAnchor = inContext.m_AssetGraph.GetChild(GetInstanceHandle(), idx); if (theReader.GetObjectTypeName(theAnchor) == L"PathAnchorPoint") { SFloat2 theAnchorPos = ToFloat2(theReader.GetInstancePropertyValue( theAnchor, inContext.m_ObjectDefinitions.m_PathAnchorPoint.m_Position.m_Property)); float theIncomingAngle = ToFloat(theReader.GetInstancePropertyValue( theAnchor, inContext.m_ObjectDefinitions.m_PathAnchorPoint.m_IncomingAngle.m_Property)); float theIncomingDistance = ToFloat(theReader.GetInstancePropertyValue( theAnchor, inContext.m_ObjectDefinitions.m_PathAnchorPoint.m_IncomingDistance.m_Property)); float theOutgoingDistance = ToFloat(theReader.GetInstancePropertyValue( theAnchor, inContext.m_ObjectDefinitions.m_PathAnchorPoint.m_OutgoingDistance.m_Property)); qt3ds::render::SPathAnchorPoint thePoint(QT3DSVec2(theAnchorPos[0], theAnchorPos[1]), theIncomingAngle, theIncomingAngle + 180.0f, theIncomingDistance, theOutgoingDistance); m_PathBuffer.push_back(thePoint); ++anchorIdx; } } inContext.m_Context.GetPathManager().SetPathSubPathData( theItem, qt3ds::foundation::toConstDataRef(m_PathBuffer.begin(), (QT3DSU32)m_PathBuffer.size())); } void AppendChild(SGraphObject &) override {} void ClearChildren() override {} void SetActive(bool /*inActive*/) override {} }; struct SPathTranslator : public SNodeTranslator { SPathTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SPath)()) { } void AppendChild(SGraphObject &inChild) override { if (GraphObjectTypes::IsMaterialType(inChild.m_Type)) { SPath &theItem = static_cast(GetGraphObject()); theItem.AddMaterial(&inChild); theItem.m_Flags.SetDirty(true); } else if (inChild.m_Type == GraphObjectTypes::PathSubPath) { SPath &theItem = static_cast(GetGraphObject()); theItem.AddSubPath(static_cast(inChild)); theItem.m_Flags.SetDirty(true); } else { SNodeTranslator::AppendChild(inChild); } } void ClearChildren() override { SNodeTranslator::ClearChildren(); SPath &theItem = static_cast(GetGraphObject()); theItem.ClearMaterials(); theItem.ClearSubPaths(); } void PushTranslation(STranslation &inContext) override { SNodeTranslator::PushTranslation(inContext); SPath &theItem = static_cast(GetGraphObject()); STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); ITERATE_QT3DS_RENDER_PATH_PROPERTIES } }; struct SDefaultMaterialTranslator : public SGraphObjectTranslator { SDefaultMaterialTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SDefaultMaterial)()) { } void PushTranslation(STranslation &inContext) override { SGraphObjectTranslator::PushTranslation(inContext); SDefaultMaterial &theItem = static_cast(GetGraphObject()); STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); ITERATE_QT3DS_RENDER_MATERIAL_PROPERTIES // qt3dsdm::Qt3DSDMInstanceHandle parent = inContext.m_AssetGraph.GetParent( // GetInstanceHandle() ); theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapIndirect, theItem.m_Lightmaps.m_LightmapIndirect); theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapRadiosity, theItem.m_Lightmaps.m_LightmapRadiosity); theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapShadow, theItem.m_Lightmaps.m_LightmapShadow); } void AppendChild(SGraphObject &) override {} void ClearChildren() override {} void SetActive(bool /*inActive*/) override {} }; struct SImageTranslator : public SGraphObjectTranslator { SImageTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SImage)()) { } void PushTranslation(STranslation &inContext) override { SGraphObjectTranslator::PushTranslation(inContext); SImage &theItem = static_cast(GetGraphObject()); STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); ITERATE_QT3DS_RENDER_IMAGE_PROPERTIES theItem.m_Flags.SetDirty(true); theItem.m_Flags.SetTransformDirty(true); } void AppendChild(SGraphObject &child) override { SImage &theItem = static_cast(GetGraphObject()); if (child.m_Type == GraphObjectTypes::RenderPlugin) theItem.m_RenderPlugin = &static_cast(child); } void ClearChildren() override { SImage &theItem = static_cast(GetGraphObject()); theItem.m_RenderPlugin = nullptr; } void SetActive(bool /*inActive*/) override {} }; struct STextTranslator : public SNodeTranslator { STextTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SText)()) { } void PushTranslation(STranslation &inContext) override { SNodeTranslator::PushTranslation(inContext); STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); SText &theItem = static_cast(GetGraphObject()); ITERATE_QT3DS_RENDER_TEXT_PROPERTIES theItem.m_Flags.SetTextDirty(true); } }; inline qt3ds::QT3DSVec2 ToRenderType(const qt3dsdm::SFloat2 &inType) { return qt3ds::QT3DSVec2(inType.m_Floats[0], inType.m_Floats[1]); } inline qt3ds::QT3DSVec3 ToRenderType(const qt3dsdm::SFloat3 &inType) { return qt3ds::QT3DSVec3(inType.m_Floats[0], inType.m_Floats[1], inType.m_Floats[2]); } inline qt3ds::QT3DSVec4 ToRenderType(const qt3dsdm::SFloat4 &inType) { return qt3ds::QT3DSVec4(inType.m_Floats[0], inType.m_Floats[1], inType.m_Floats[2], inType.m_Floats[3]); } struct SDynamicObjectTranslator : public SGraphObjectTranslator { typedef eastl::vector> TIdxToPropertyMap; eastl::basic_string m_ConvertStr; TIdxToPropertyMap m_PropertyMap; SDynamicObjectTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &, SDynamicObject &inObject) : SGraphObjectTranslator(inInstance, inObject) { } void PushTranslation(STranslation &inContext) override { SDynamicObject &theItem = static_cast(GetGraphObject()); IDynamicObjectSystem &theSystem = inContext.m_Context.GetDynamicObjectSystem(); using namespace qt3ds::render::dynamic; using qt3ds::foundation::NVConstDataRef; NVConstDataRef theProperties = theSystem.GetProperties(theItem.m_ClassName); if (m_PropertyMap.size() == 0) { for (QT3DSU32 idx = 0, end = theProperties.size(); idx < end; ++idx) { const SPropertyDefinition &theDefinition(theProperties[idx]); qt3ds::foundation::ConvertUTF(theDefinition.m_Name.c_str(), 0, m_ConvertStr); const wchar_t *thePropName = reinterpret_cast(m_ConvertStr.c_str()); qt3dsdm::Qt3DSDMPropertyHandle theProperty = inContext.m_Reader.FindProperty(GetInstanceHandle(), thePropName); if (theProperty.Valid()) m_PropertyMap.push_back(eastl::make_pair(idx, theProperty.GetHandleValue())); } } std::shared_ptr theDataCore = inContext.m_StudioSystem.GetFullSystem()->GetCoreSystem()->GetDataCore(); for (TIdxToPropertyMap::iterator theIter = m_PropertyMap.begin(), end = m_PropertyMap.end(); theIter != end; ++theIter) { const SPropertyDefinition &theDefinition(theProperties[theIter->first]); qt3dsdm::Qt3DSDMPropertyHandle theProperty = theIter->second; // Sometimes it is possible to have dirty properties that no longer exist, e.g. // when undoing standard material -> custom material change. We just ignore changes // to such properties. if (theDataCore->IsProperty(theProperty)) { Option theValueOpt = inContext.m_Reader.GetInstancePropertyValue(GetInstanceHandle(), theProperty); if (theValueOpt.hasValue()) { qt3dsdm::SValue &theValue(*theValueOpt); switch (qt3dsdm::GetValueType(theValue)) { case qt3dsdm::DataModelDataType::Long: if (theDefinition.m_DataType == qt3ds::render::NVRenderShaderDataTypes::QT3DSI32) { theItem.SetPropertyValue(theDefinition, qt3dsdm::get(theValue)); } else { QT3DS_ASSERT(false); } break; case qt3dsdm::DataModelDataType::Bool: if (theDefinition.m_DataType == qt3ds::render::NVRenderShaderDataTypes::QT3DSRenderBool) { theItem.SetPropertyValue(theDefinition, qt3dsdm::get(theValue)); } else { QT3DS_ASSERT(false); } break; case qt3dsdm::DataModelDataType::Float: if (theDefinition.m_DataType == qt3ds::render::NVRenderShaderDataTypes::QT3DSF32) { theItem.SetPropertyValue(theDefinition, qt3dsdm::get(theValue)); } else { QT3DS_ASSERT(false); } break; case qt3dsdm::DataModelDataType::Float2: if (theDefinition.m_DataType == qt3ds::render::NVRenderShaderDataTypes::QT3DSVec2) { theItem.SetPropertyValue( theDefinition, ToRenderType(qt3dsdm::get(theValue))); } else { QT3DS_ASSERT(false); } break; case qt3dsdm::DataModelDataType::Float3: if (theDefinition.m_DataType == qt3ds::render::NVRenderShaderDataTypes::QT3DSVec3) { theItem.SetPropertyValue( theDefinition, ToRenderType(qt3dsdm::get(theValue))); } else { QT3DS_ASSERT(false); } break; case qt3dsdm::DataModelDataType::Float4: if (theDefinition.m_DataType == qt3ds::render::NVRenderShaderDataTypes::QT3DSVec4) { theItem.SetPropertyValue( theDefinition, ToRenderType(qt3dsdm::get(theValue))); } else { QT3DS_ASSERT(false); } break; // Could be either an enum or a texture. case qt3dsdm::DataModelDataType::String: { qt3dsdm::TDataStrPtr theData = qt3dsdm::get(theValue); if (theData) { eastl::string theStr; qt3ds::render::ConvertWideUTF(theData->GetData(), 0, theStr); eastl::string theWorkspace; theItem.SetPropertyValue( theDefinition, theStr.c_str(), inContext.m_Doc.GetCore()->getProjectFile() .getProjectPath().toLocal8Bit().constData(), theWorkspace, inContext.m_Context.GetStringTable()); } } break; default: QT3DS_ASSERT(false); } } } } } void AppendChild(SGraphObject &) override {} void ClearChildren() override {} }; struct SEffectTranslator : public SDynamicObjectTranslator { // TODO - move this map to inContext and have it looked up by name. IEffectSystem *m_EffectSystem; SEffectTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc, SEffect &inEffect) : SDynamicObjectTranslator(inInstance, inAlloc, inEffect) , m_EffectSystem(nullptr) { } void PushTranslation(STranslation &inContext) override { m_EffectSystem = &inContext.m_Context.GetEffectSystem(); SDynamicObjectTranslator::PushTranslation(inContext); } void SetActive(bool inActive) override { SEffect &theItem = static_cast(GetGraphObject()); if (m_EffectSystem) theItem.SetActive(inActive, *m_EffectSystem); else theItem.m_Flags.SetActive(inActive); } void ResetEffect() override { SEffect &theItem = static_cast(GetGraphObject()); if (m_EffectSystem) theItem.Reset(*m_EffectSystem); } }; struct SCustomMaterialTranslator : public SDynamicObjectTranslator { ICustomMaterialSystem *m_MaterialSystem; SCustomMaterialTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc, SCustomMaterial &inMaterial) : SDynamicObjectTranslator(inInstance, inAlloc, inMaterial) , m_MaterialSystem(nullptr) { } void PushTranslation(STranslation &inContext) override { SDynamicObjectTranslator::PushTranslation(inContext); SCustomMaterial &theItem = static_cast(GetGraphObject()); STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); ITERATE_QT3DS_RENDER_CUSTOM_MATERIAL_PROPERTIES theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapIndirect, theItem.m_Lightmaps.m_LightmapIndirect); theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapRadiosity, theItem.m_Lightmaps.m_LightmapRadiosity); theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapShadow, theItem.m_Lightmaps.m_LightmapShadow); } void SetActive(bool inActive) override { if (m_MaterialSystem) { SCustomMaterial &theItem = static_cast(GetGraphObject()); if (inActive != theItem.m_Flags.IsActive()) { theItem.m_Flags.SetActive(inActive); m_MaterialSystem->OnMaterialActivationChange(theItem, inActive); } } } }; struct SReferencedMaterialTranslator : public SGraphObjectTranslator { SReferencedMaterialTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SReferencedMaterial)()) { } void PushTranslation(STranslation &inContext) override { SGraphObjectTranslator::PushTranslation(inContext); SReferencedMaterial &theItem = static_cast(GetGraphObject()); STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); ITERATE_QT3DS_RENDER_REFERENCED_MATERIAL_PROPERTIES theItem.m_Dirty.SetDirty(); if (theItem.m_ReferencedMaterial == &theItem) { qCCritical(qt3ds::INVALID_OPERATION, "Referenced material is referencing itself."); } else if (theItem.m_ReferencedMaterial && theItem.m_ReferencedMaterial->m_Type == GraphObjectTypes::DefaultMaterial) { SDefaultMaterial *theDefaultItem = static_cast(theItem.m_ReferencedMaterial); theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapIndirect, theDefaultItem->m_Lightmaps.m_LightmapIndirect); theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapRadiosity, theDefaultItem->m_Lightmaps.m_LightmapRadiosity); theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapShadow, theDefaultItem->m_Lightmaps.m_LightmapShadow); } else if (theItem.m_ReferencedMaterial && theItem.m_ReferencedMaterial->m_Type == GraphObjectTypes::CustomMaterial) { SCustomMaterial *theDefaultItem = static_cast(theItem.m_ReferencedMaterial); theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapIndirect, theDefaultItem->m_Lightmaps.m_LightmapIndirect); theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapRadiosity, theDefaultItem->m_Lightmaps.m_LightmapRadiosity); theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapShadow, theDefaultItem->m_Lightmaps.m_LightmapShadow); } } void AppendChild(SGraphObject &) override {} void ClearChildren() override {} void SetActive(bool /*inActive*/) override {} }; using qt3ds::render::SRenderPlugin; using qt3ds::render::SRenderPropertyValueUpdate; using qt3ds::render::IRenderPluginClass; using qt3ds::render::SRenderPluginPropertyDeclaration; struct SRenderPluginPropertyUpdateFactory { static void Add(eastl::vector &ioUpdates, float value, const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &) { ioUpdates.push_back(SRenderPropertyValueUpdate(theDec.m_Name, value)); } static void Add(eastl::vector &ioUpdates, qt3ds::QT3DSI32 value, const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &) { ioUpdates.push_back(SRenderPropertyValueUpdate(theDec.m_Name, value)); } static void Add(eastl::vector &ioUpdates, bool value, const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &) { ioUpdates.push_back(SRenderPropertyValueUpdate(theDec.m_Name, value)); } static void Add(eastl::vector &ioUpdates, qt3dsdm::TDataStrPtr value, const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &, qt3ds::foundation::IStringTable &strTable) { if (value) { ioUpdates.push_back( SRenderPropertyValueUpdate(theDec.m_Name, strTable.RegisterStr(value->GetData()))); } } static void Add(eastl::vector &ioUpdates, qt3dsdm::SStringRef value, const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &, qt3ds::foundation::IStringTable &strTable) { ioUpdates.push_back( SRenderPropertyValueUpdate(theDec.m_Name, strTable.RegisterStr(value.m_Id))); } static void Add(eastl::vector &ioUpdates, const qt3dsdm::SFloat2 &value, const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &inClass) { ioUpdates.push_back(SRenderPropertyValueUpdate( inClass.GetPropertyValueInfo(theDec.m_StartOffset).first, value.m_Floats[0])); ioUpdates.push_back(SRenderPropertyValueUpdate( inClass.GetPropertyValueInfo(theDec.m_StartOffset + 1).first, value.m_Floats[1])); } static void Add(eastl::vector &ioUpdates, const qt3dsdm::SFloat3 &value, const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &inClass) { ioUpdates.push_back(SRenderPropertyValueUpdate( inClass.GetPropertyValueInfo(theDec.m_StartOffset).first, value.m_Floats[0])); ioUpdates.push_back(SRenderPropertyValueUpdate( inClass.GetPropertyValueInfo(theDec.m_StartOffset + 1).first, value.m_Floats[1])); ioUpdates.push_back(SRenderPropertyValueUpdate( inClass.GetPropertyValueInfo(theDec.m_StartOffset + 2).first, value.m_Floats[2])); } static void Add(eastl::vector &ioUpdates, const qt3dsdm::SFloat4 &value, const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &inClass) { ioUpdates.push_back(SRenderPropertyValueUpdate( inClass.GetPropertyValueInfo(theDec.m_StartOffset).first, value.m_Floats[0])); ioUpdates.push_back(SRenderPropertyValueUpdate( inClass.GetPropertyValueInfo(theDec.m_StartOffset + 1).first, value.m_Floats[1])); ioUpdates.push_back(SRenderPropertyValueUpdate( inClass.GetPropertyValueInfo(theDec.m_StartOffset + 2).first, value.m_Floats[2])); ioUpdates.push_back(SRenderPropertyValueUpdate( inClass.GetPropertyValueInfo(theDec.m_StartOffset + 3).first, value.m_Floats[3])); } }; struct SRenderPluginTranslator : public SGraphObjectTranslator { eastl::vector m_PropertyUpdates; SRenderPluginTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SRenderPlugin)()) { } void PushTranslation(STranslation &inContext) override { SRenderPlugin &theItem = static_cast(GetGraphObject()); // First, get the instance via resolving the source path. Qt3DSDMPropertyHandle sourcepath = inContext.m_Reader.FindProperty(GetInstanceHandle(), L"sourcepath"); Option theSourcePath = inContext.m_Reader.GetInstancePropertyValue(GetInstanceHandle(), sourcepath); qt3dsdm::TDataStrPtr theData = theSourcePath->getData(); if (!theData) return; Q3DStudio::CFilePath theFullPath = inContext.m_Doc.GetResolvedPathToDoc(theData->GetData()); qt3ds::foundation::IStringTable &theStrTable = inContext.m_Context.GetStringTable(); theItem.m_PluginPath = theStrTable.RegisterStr(theFullPath.toCString()); qt3ds::render::IRenderPluginInstance *theInstance = inContext.m_Context.GetRenderPluginManager().GetOrCreateRenderPluginInstance( theItem.m_PluginPath, &theItem); // Couldn't load the instance, so we can't render the instance. if (theInstance == nullptr) return; // Grab the instance's parent and get the properties that are specific to just that // instance. TInstanceHandleList derivationParents; std::shared_ptr theDataCore = inContext.m_StudioSystem.GetFullSystem()->GetCoreSystem()->GetDataCore(); theDataCore->GetInstanceParents(GetInstanceHandle(), derivationParents); if (derivationParents.size() == 0) return; TPropertyHandleList theSpecificProperties; theDataCore->GetInstanceProperties(derivationParents[0], theSpecificProperties); eastl::string propStem; eastl::string propname; m_PropertyUpdates.clear(); qt3ds::render::IRenderPluginClass &theClass = theInstance->GetPluginClass(); using qt3ds::render::SRenderPluginPropertyDeclaration; if (theClass.GetRegisteredProperties().size() == 0) { for (size_t idx = 0, end = theSpecificProperties.size(); idx < end; ++idx) { Qt3DSDMPropertyDefinition theProperty = theDataCore->GetProperty(theSpecificProperties[idx]); qt3dsdm::AdditionalMetaDataType::Value theMetaType = inContext.m_StudioSystem.GetActionMetaData()->GetAdditionalMetaDataType( GetInstanceHandle(), theSpecificProperties[idx]); CRegisteredString thePropName(theStrTable.RegisterStr(theProperty.m_Name.c_str())); switch (theProperty.m_Type) { case DataModelDataType::Float: theClass.RegisterProperty(SRenderPluginPropertyDeclaration( thePropName, qt3ds::render::SRenderPluginPropertyTypes::Float)); break; case DataModelDataType::Float2: theClass.RegisterProperty(SRenderPluginPropertyDeclaration( thePropName, qt3ds::render::SRenderPluginPropertyTypes::Vector2)); break; case DataModelDataType::Float3: if (theMetaType != AdditionalMetaDataType::Color) { theClass.RegisterProperty(SRenderPluginPropertyDeclaration( thePropName, qt3ds::render::SRenderPluginPropertyTypes::Vector3)); } else { theClass.RegisterProperty(SRenderPluginPropertyDeclaration( thePropName, qt3ds::render::SRenderPluginPropertyTypes::Color)); } break; case DataModelDataType::Float4: if (theMetaType == AdditionalMetaDataType::Color) { theClass.RegisterProperty(SRenderPluginPropertyDeclaration( thePropName, qt3ds::render::SRenderPluginPropertyTypes::Color)); } break; case DataModelDataType::Long: theClass.RegisterProperty(SRenderPluginPropertyDeclaration( thePropName, qt3ds::render::SRenderPluginPropertyTypes::Long)); break; case DataModelDataType::String: case DataModelDataType::StringRef: theClass.RegisterProperty(SRenderPluginPropertyDeclaration( thePropName, qt3ds::render::SRenderPluginPropertyTypes::String)); break; case DataModelDataType::Bool: theClass.RegisterProperty(SRenderPluginPropertyDeclaration( thePropName, qt3ds::render::SRenderPluginPropertyTypes::Boolean)); break; default: // Unsupported plugin property. QT3DS_ASSERT(false); break; } } } for (size_t idx = 0, end = theSpecificProperties.size(); idx < end; ++idx) { Option thePropValueOpt = inContext.m_Reader.GetInstancePropertyValue( GetInstanceHandle(), theSpecificProperties[idx]); if (thePropValueOpt.hasValue()) { SValue &thePropValue = thePropValueOpt.getValue(); Qt3DSDMPropertyDefinition theProperty = theDataCore->GetProperty(theSpecificProperties[idx]); SRenderPluginPropertyDeclaration theDeclaration(theClass.GetPropertyDeclaration( theStrTable.RegisterStr(theProperty.m_Name.c_str()))); switch (thePropValue.getType()) { case DataModelDataType::None: QT3DS_ASSERT(false); break; case DataModelDataType::Float: SRenderPluginPropertyUpdateFactory::Add( m_PropertyUpdates, thePropValue.getData(), theDeclaration, theClass); break; case DataModelDataType::Float2: SRenderPluginPropertyUpdateFactory::Add(m_PropertyUpdates, thePropValue.getData(), theDeclaration, theClass); break; case DataModelDataType::Float3: SRenderPluginPropertyUpdateFactory::Add(m_PropertyUpdates, thePropValue.getData(), theDeclaration, theClass); break; case DataModelDataType::Float4: SRenderPluginPropertyUpdateFactory::Add(m_PropertyUpdates, thePropValue.getData(), theDeclaration, theClass); break; case DataModelDataType::Long: SRenderPluginPropertyUpdateFactory::Add( m_PropertyUpdates, thePropValue.getData(), theDeclaration, theClass); break; case DataModelDataType::String: SRenderPluginPropertyUpdateFactory::Add(m_PropertyUpdates, thePropValue.getData(), theDeclaration, theClass, theStrTable); break; case DataModelDataType::Bool: SRenderPluginPropertyUpdateFactory::Add( m_PropertyUpdates, thePropValue.getData(), theDeclaration, theClass); break; case DataModelDataType::StringRef: SRenderPluginPropertyUpdateFactory::Add(m_PropertyUpdates, thePropValue.getData(), theDeclaration, theClass, theStrTable); break; default: QT3DS_ASSERT(false); break; } } } theInstance->Update(NVConstDataRef(m_PropertyUpdates.data(), m_PropertyUpdates.size())); } void AppendChild(SGraphObject &) override {} void ClearChildren() override {} void SetActive(bool inActive) override { SRenderPlugin &theItem = static_cast(GetGraphObject()); theItem.m_Flags.SetActive(inActive); } }; struct SAliasTranslator : public SGraphObjectTranslator { SGraphObjectTranslator *m_ReferenceTree; Qt3DSDMInstanceHandle m_ReferencedInstance; SAliasTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SNode)()) , m_ReferenceTree(nullptr) { } void RecurseAndCreateTranslators(STranslation &inContext, qt3dsdm::Qt3DSDMInstanceHandle inInstance) { for (QT3DSI32 idx = 0, end = inContext.m_AssetGraph.GetChildCount(inInstance); idx < end; ++idx) { qt3dsdm::Qt3DSDMInstanceHandle theChild = inContext.m_AssetGraph.GetChild(inInstance, idx); inContext.GetOrCreateTranslator(theChild, m_InstanceHandle); RecurseAndCreateTranslators(inContext, theChild); } } void PushTranslation(STranslation &inContext) override { STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); Option theData = theParser.GetPropertyValue( inContext.m_ObjectDefinitions.m_Alias.m_ReferencedNode); m_ReferencedInstance = Qt3DSDMInstanceHandle(); m_ReferenceTree = nullptr; ((SNode *)m_GraphObject)->m_Flags.SetDirty(true); if (theData.hasValue()) { m_ReferencedInstance = inContext.m_Reader.GetInstanceForObjectRef(GetInstanceHandle(), *theData); if (inContext.m_Reader.IsInstance(m_ReferencedInstance)) { m_ReferenceTree = inContext.GetOrCreateTranslator(m_ReferencedInstance, m_InstanceHandle); if (m_ReferenceTree && !GraphObjectTypes::IsNodeType(m_ReferenceTree->GetGraphObject().m_Type)) { QT3DS_ASSERT(false); m_ReferenceTree = nullptr; m_ReferencedInstance = Qt3DSDMInstanceHandle(); } else { RecurseAndCreateTranslators(inContext, m_ReferencedInstance); } } } } void AfterRenderGraphIsBuilt(STranslation &inContext) override { SNode &theItem = static_cast(GetGraphObject()); STranslatorDataModelParser theParser(inContext, m_InstanceHandle); ITERATE_QT3DS_RENDER_NODE_PROPERTIES theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Node.m_BoneId, theItem.m_SkeletonId); bool ignoresParent = false; if (theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Node.m_IgnoresParent, ignoresParent)) theItem.m_Flags.SetIgnoreParentTransform(ignoresParent); theItem.m_Flags.SetDirty(true); } void AppendChild(SGraphObject &inObject) override { if (m_ReferenceTree) m_ReferenceTree->AppendChild(inObject); } void ClearChildren() override { if (m_ReferenceTree) m_ReferenceTree->ClearChildren(); } void SetActive(bool inActive) override { SNode &theItem = static_cast(GetGraphObject()); theItem.m_Flags.SetActive(inActive); } SGraphObject &GetGraphObject() override { if (m_ReferenceTree) return *m_ReferenceTree->m_GraphObject; return *m_GraphObject; } qt3dsdm::Qt3DSDMInstanceHandle GetSceneGraphInstanceHandle() override { if (m_ReferencedInstance.Valid()) return m_ReferencedInstance; return m_InstanceHandle; } Qt3DSDMInstanceHandle GetInstanceHandle() override { return m_InstanceHandle; } SGraphObject &GetNonAliasedGraphObject() override { return *m_GraphObject; } }; } void SGraphObjectTranslator::PushTranslation(STranslation &inTranslatorContext) { Q3DStudio::CString theId = inTranslatorContext.m_Reader.GetFileId(GetInstanceHandle()); if (theId.size()) GetGraphObject().m_Id = inTranslatorContext.m_Context.GetStringTable().RegisterStr(theId.c_str()); } bool STranslation::IncludeNode(const SNode &inNode) { SGraphObjectTranslator *theTranslator = inNode.m_UserData.DynamicCast(); if (theTranslator && m_Doc.GetDocumentReader().IsCurrentlyActive(theTranslator->GetInstanceHandle())) return true; return false; } void STranslation::ReleaseEffect(qt3dsdm::Qt3DSDMInstanceHandle inInstance) { if (m_Reader.IsInstance(inInstance) == false) return; qt3dsdm::ComposerObjectTypes::Enum theType = m_ObjectDefinitions.GetType(inInstance); qt3dsdm::Qt3DSDMInstanceHandle theParentClass = m_Reader.GetFirstBaseClass(inInstance); if (theType == NULL && theParentClass.Valid()) theType = m_ObjectDefinitions.GetType(theParentClass); if (theType == qt3dsdm::ComposerObjectTypes::Effect) { IEffectSystem &theSystem = m_Context.GetEffectSystem(); if (theParentClass.Valid()) { Q3DStudio::CString theInstanceName = m_Reader.GetName(theParentClass); CRegisteredString theNameStr = m_Context.GetStringTable().RegisterStr(theInstanceName); if (theSystem.IsEffectRegistered(theNameStr)) { TInstanceToTranslatorMap::iterator theTranslatorList = m_TranslatorMap.find(inInstance); if (theTranslatorList != m_TranslatorMap.end()) m_TranslatorMap.erase(theTranslatorList); theSystem.SetEffectRequiresCompilation(theNameStr, true); } } } } SGraphObjectTranslator *STranslation::CreateTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance) { SGraphObjectTranslator *theNewTranslator = nullptr; qt3dsdm::ComposerObjectTypes::Enum theType = m_ObjectDefinitions.GetType(inInstance); qt3dsdm::Qt3DSDMInstanceHandle theParentClass = m_Reader.GetFirstBaseClass(inInstance); if (theType == NULL && theParentClass.Valid()) theType = m_ObjectDefinitions.GetType(theParentClass); // For the subset of possible instances, pick out the valid translators. switch (theType) { case qt3dsdm::ComposerObjectTypes::Group: case qt3dsdm::ComposerObjectTypes::Component: case qt3dsdm::ComposerObjectTypes::Node: theNewTranslator = QT3DS_NEW(m_Allocator, SNodeTranslator)(inInstance, m_Allocator); break; case qt3dsdm::ComposerObjectTypes::Scene: theNewTranslator = QT3DS_NEW(m_Allocator, SSceneTranslator)(inInstance, m_Allocator); m_Scene = static_cast(&theNewTranslator->GetGraphObject()); m_Scene->m_Presentation = &m_Presentation; break; case qt3dsdm::ComposerObjectTypes::Layer: theNewTranslator = QT3DS_NEW(m_Allocator, SLayerTranslator)(inInstance, m_Allocator); break; case qt3dsdm::ComposerObjectTypes::Light: theNewTranslator = QT3DS_NEW(m_Allocator, SLightTranslator)(inInstance, m_Allocator); break; case qt3dsdm::ComposerObjectTypes::Camera: theNewTranslator = QT3DS_NEW(m_Allocator, SCameraTranslator)(inInstance, m_Allocator); break; case qt3dsdm::ComposerObjectTypes::Model: theNewTranslator = QT3DS_NEW(m_Allocator, SModelTranslator)(inInstance, m_Allocator); break; case qt3dsdm::ComposerObjectTypes::Image: theNewTranslator = QT3DS_NEW(m_Allocator, SImageTranslator)(inInstance, m_Allocator); break; case qt3dsdm::ComposerObjectTypes::Text: theNewTranslator = QT3DS_NEW(m_Allocator, STextTranslator)(inInstance, m_Allocator); break; case qt3dsdm::ComposerObjectTypes::Material: theNewTranslator = QT3DS_NEW(m_Allocator, SDefaultMaterialTranslator)(inInstance, m_Allocator); break; case qt3dsdm::ComposerObjectTypes::ReferencedMaterial: theNewTranslator = QT3DS_NEW(m_Allocator, SReferencedMaterialTranslator)(inInstance, m_Allocator); break; case qt3dsdm::ComposerObjectTypes::Alias: theNewTranslator = QT3DS_NEW(m_Allocator, SAliasTranslator)(inInstance, m_Allocator); break; case qt3dsdm::ComposerObjectTypes::Path: theNewTranslator = QT3DS_NEW(m_Allocator, SPathTranslator)(inInstance, m_Allocator); break; case qt3dsdm::ComposerObjectTypes::SubPath: theNewTranslator = QT3DS_NEW(m_Allocator, SPathSubPathTranslator)(inInstance, m_Allocator); break; case qt3dsdm::ComposerObjectTypes::Effect: { IEffectSystem &theSystem = m_Context.GetEffectSystem(); if (theParentClass.Valid()) { Q3DStudio::CString theInstanceName = m_Reader.GetName(theParentClass); CRegisteredString theNameStr = m_Context.GetStringTable().RegisterStr(theInstanceName); if (theSystem.IsEffectRegistered(theNameStr) && theSystem.DoesEffectRequireCompilation(theNameStr)) { theSystem.UnregisterEffect(theNameStr); } if (!theSystem.IsEffectRegistered(theNameStr)) { // We assume the effect has already been registered and such. qt3dsdm::IMetaData &theMetaData(*m_StudioSystem.GetActionMetaData()); Q3DStudio::CString theInstancePath = m_Reader.GetSourcePath(theParentClass); Option theMetaEffect = theMetaData.GetEffectBySourcePath( m_Context.GetStringTable().GetNarrowStr(theInstancePath)); if (theMetaEffect.hasValue()) { qt3ds::render::IUIPLoader::CreateEffectClassFromMetaEffect( theNameStr, m_Context.GetFoundation(), theSystem, theMetaEffect, m_Context.GetStringTable()); theSystem.SetEffectRequiresCompilation(theNameStr, true); } } if (theSystem.IsEffectRegistered(theNameStr)) { theNewTranslator = QT3DS_NEW(m_Allocator, SEffectTranslator)( inInstance, m_Allocator, *theSystem.CreateEffectInstance(theNameStr, m_Allocator)); } } } break; case qt3dsdm::ComposerObjectTypes::CustomMaterial: { ICustomMaterialSystem &theSystem = m_Context.GetCustomMaterialSystem(); if (theParentClass.Valid()) { Q3DStudio::CString theInstanceName = m_Reader.GetName(theParentClass); CRegisteredString theNameStr = m_Context.GetStringTable().RegisterStr(theInstanceName); if (!theSystem.IsMaterialRegistered(theNameStr)) { // We assume the effect has already been registered and such. qt3dsdm::IMetaData &theMetaData(*m_StudioSystem.GetActionMetaData()); Q3DStudio::CString theInstancePath = m_Reader.GetSourcePath(theParentClass); Option theMaterialData = theMetaData.GetMaterialBySourcePath( m_Context.GetStringTable().GetNarrowStr(theInstancePath)); if (theMaterialData.hasValue()) { qt3ds::render::IUIPLoader::CreateMaterialClassFromMetaMaterial( theNameStr, m_Context.GetFoundation(), theSystem, theMaterialData, m_Context.GetStringTable()); } } if (theSystem.IsMaterialRegistered(theNameStr)) { theNewTranslator = QT3DS_NEW(m_Allocator, SCustomMaterialTranslator)( inInstance, m_Allocator, *theSystem.CreateCustomMaterial(theNameStr, m_Allocator)); static_cast(theNewTranslator)->m_MaterialSystem = &theSystem; } } } break; case qt3dsdm::ComposerObjectTypes::RenderPlugin: { theNewTranslator = QT3DS_NEW(m_Allocator, SRenderPluginTranslator)(inInstance, m_Allocator); } break; default: break; } return theNewTranslator; } bool CompareTranslator(const STranslation::THandleTranslatorPair &first, const STranslation::THandleTranslatorPair &second) { return first.first == second.first; } struct STranslatorPredicate { Qt3DSDMInstanceHandle m_Instance; STranslatorPredicate(Qt3DSDMInstanceHandle &ins) : m_Instance(ins) { } bool operator()(const STranslation::THandleTranslatorPair &first) const { return first.first == m_Instance; } }; Option FindTranslator(STranslation::THandleTranslatorPairList &inList, Qt3DSDMInstanceHandle inInstance = Qt3DSDMInstanceHandle()) { STranslation::THandleTranslatorPairList::iterator iter = eastl::find_if(inList.begin(), inList.end(), STranslatorPredicate(inInstance)); if (iter != inList.end()) return *iter; return Empty(); } SGraphObjectTranslator *STranslation::GetOrCreateTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance) { return GetOrCreateTranslator(inInstance, Qt3DSDMInstanceHandle()); } SGraphObjectTranslator * STranslation::GetOrCreateTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3dsdm::Qt3DSDMInstanceHandle inAliasInstance) { TInstanceToTranslatorMap::iterator theTranslatorList = m_TranslatorMap.insert(eastl::make_pair(inInstance, THandleTranslatorPairList())).first; THandleTranslatorPairList &theList = theTranslatorList->second; Option theExistingTranslator = FindTranslator(theList, inAliasInstance); if (theExistingTranslator.hasValue()) { return theExistingTranslator->second; } if (m_Reader.IsInstance(inInstance) == false) return nullptr; SGraphObjectTranslator *theNewTranslator = CreateTranslator(inInstance); if (theNewTranslator != nullptr) { theNewTranslator->m_AliasInstanceHandle = inAliasInstance; m_DirtySet.insert(*theNewTranslator); theList.push_back(THandleTranslatorPair(inAliasInstance, theNewTranslator)); } return theNewTranslator; } STranslation::THandleTranslatorPairList & STranslation::GetTranslatorsForInstance(qt3dsdm::Qt3DSDMInstanceHandle inInstance) { return m_TranslatorMap.insert(eastl::make_pair(inInstance, THandleTranslatorPairList())) .first->second; } qt3dsdm::Qt3DSDMInstanceHandle STranslation::GetAnchorPoint(QT3DSU32 inAnchorIndex) { SGraphObjectTranslator *thePathTranslator = static_cast(m_PathWidget->GetNode().m_UserData.m_UserData); if (thePathTranslator == nullptr) return qt3dsdm::Qt3DSDMInstanceHandle(); qt3dsdm::Qt3DSDMInstanceHandle thePathHandle = thePathTranslator->GetInstanceHandle(); QT3DSU32 theAnchorIndex = 0; for (QT3DSI32 idx = 0, end = m_AssetGraph.GetChildCount(thePathHandle); idx < end; ++idx) { qt3dsdm::Qt3DSDMInstanceHandle theChildInstance = m_AssetGraph.GetChild(thePathHandle, idx); if (m_Doc.GetDocumentReader().GetObjectTypeName(theChildInstance) == L"SubPath") { QT3DSI32 numAnchors = m_AssetGraph.GetChildCount(theChildInstance); QT3DSU32 endIndex = theAnchorIndex + (QT3DSU32)numAnchors; if (endIndex > inAnchorIndex) { return m_AssetGraph.GetChild(theChildInstance, inAnchorIndex - theAnchorIndex); } else theAnchorIndex = endIndex; } } return qt3dsdm::Qt3DSDMInstanceHandle(); } qt3dsdm::Qt3DSDMInstanceHandle STranslation::GetAnchorPoint(SPathPick &inPick) { return GetAnchorPoint(inPick.m_AnchorIndex); } namespace qt3ds { namespace studio { struct SEditCameraLayerTranslator : public SLayerTranslator { SEditCameraLayerTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) : SLayerTranslator(inInstance, inAlloc) { } void PushTranslation(STranslation &) override { SLayer &theItem = static_cast(GetGraphObject()); theItem.m_Flags.SetActive(true); } void AppendChild(SGraphObject &inChild) override { if (GraphObjectTypes::IsNodeType(inChild.m_Type)) { SNodeTranslator::AppendChild(inChild); } } }; } } STranslation::STranslation(IStudioRenderer &inRenderer, IQt3DSRenderContext &inContext) : m_Renderer(inRenderer) , m_Context(inContext) , m_Doc(*g_StudioApp.GetCore()->GetDoc()) , m_Reader(m_Doc.GetDocumentReader()) , m_ObjectDefinitions( m_Doc.GetStudioSystem()->GetClientDataModelBridge()->GetObjectDefinitions()) , m_StudioSystem(*m_Doc.GetStudioSystem()) , m_FullSystem(*m_Doc.GetStudioSystem()->GetFullSystem()) , m_AssetGraph(*m_Doc.GetAssetGraph()) , m_Allocator(inContext.GetRenderContext().GetFoundation()) , m_TranslatorMap(inContext.GetAllocator(), "STranslation::m_TranslatorMap") , m_DirtySet(inContext.GetAllocator(), "STranslation::m_DirtySet") , m_Scene(nullptr) , m_SignalConnections(inContext.GetAllocator(), "STranslation::m_SignalConnections") , m_ComponentSecondsDepth(0) , m_KeyRepeat(0) , m_EditCameraEnabled(false) , m_EditLightEnabled(false) , m_Viewport(0, 0) , m_EditCameraLayerTranslator(nullptr) , m_PixelBuffer(inContext.GetAllocator(), "STranslation::m_PixelBuffer") , m_editModeCamerasAndLights(inContext.GetAllocator(), "STranslation::m_editModeCamerasAndLights") , m_GuideAllocator(inContext.GetAllocator(), "STranslation::m_GuideAllocator") { m_EditCamera.m_Flags.SetActive(true); m_EditLight.m_Flags.SetActive(true); qt3dsdm::Qt3DSDMInstanceHandle theScene = m_AssetGraph.GetRoot(0); m_GraphIterator.ClearResults(); m_AssetGraph.GetDepthFirst(m_GraphIterator, theScene); for (; !m_GraphIterator.IsDone(); ++m_GraphIterator) { qt3dsdm::Qt3DSDMInstanceHandle theInstance(m_GraphIterator.GetCurrent()); GetOrCreateTranslator(theInstance); } qt3dsdm::IStudioFullSystemSignalProvider *theProvider = m_FullSystem.GetSignalProvider(); m_SignalConnections.push_back( theProvider->ConnectInstanceCreated(std::bind(&STranslation::DoMarkDirty, this, std::placeholders::_1))); m_SignalConnections.push_back(theProvider->ConnectInstanceDeleted( std::bind(&STranslation::ReleaseTranslation, this, std::placeholders::_1))); m_SignalConnections.push_back( theProvider->ConnectInstancePropertyValue(std::bind(&STranslation::DoMarkDirty, this, std::placeholders::_1))); m_SignalConnections.push_back(m_AssetGraph.ConnectChildAdded( std::bind(&STranslation::MarkGraphInstanceDirty, this, std::placeholders::_1, std::placeholders::_2))); m_SignalConnections.push_back(m_AssetGraph.ConnectChildMoved( std::bind(&STranslation::MarkGraphInstanceDirty, this, std::placeholders::_1, std::placeholders::_2))); m_SignalConnections.push_back(m_AssetGraph.ConnectChildRemoved( std::bind(&STranslation::MarkGraphInstanceDirty, this, std::placeholders::_1, std::placeholders::_2))); m_SignalConnections.push_back(theProvider->ConnectBeginComponentSeconds( std::bind(&STranslation::MarkBeginComponentSeconds, this, std::placeholders::_1))); m_SignalConnections.push_back(theProvider->ConnectComponentSeconds( std::bind(&STranslation::MarkComponentSeconds, this, std::placeholders::_1))); ::CColor color = CStudioPreferences::GetRulerBackgroundColor(); // Rectangles under tick marks m_rectColor = QT3DSVec4(color.GetRed() / 255.f, color.GetGreen() / 255.f, color.GetBlue() / 255.f, 1.f); color = CStudioPreferences::GetRulerTickColor(); // Tick marks m_lineColor = QT3DSVec4(color.GetRed() / 255.f, color.GetGreen() / 255.f, color.GetBlue() / 255.f, 1.f); color = CStudioPreferences::GetGuideColor(); m_guideColor = QT3DSVec4(color.GetRed() / 255.f, color.GetGreen() / 255.f, color.GetBlue() / 255.f, 1.f); color = CStudioPreferences::GetGuideSelectedColor(); m_selectedGuideColor = QT3DSVec4(color.GetRed() / 255.f, color.GetGreen() / 255.f, color.GetBlue() / 255.f, 1.f); color = CStudioPreferences::GetGuideFillColor(); // Not sure what this is used for m_guideFillColor = QT3DSVec4(color.GetRed() / 255.f, color.GetGreen() / 255.f, color.GetBlue() / 255.f, 1.f); color = CStudioPreferences::GetGuideFillSelectedColor(); // Not sure what this is used for m_selectedGuideFillColor = QT3DSVec4(color.GetRed() / 255.f, color.GetGreen() / 255.f, color.GetBlue() / 255.f, 1.f); } void STranslation::BuildRenderGraph(SGraphObjectTranslator &inParent, bool scenePreviewPass, Qt3DSDMInstanceHandle inAliasHandle) { SGraphObjectTranslator &theParentTranslator(inParent); theParentTranslator.ClearChildren(); if (m_EditCameraEnabled && !scenePreviewPass) { const auto objectType = theParentTranslator.GetGraphObject().m_Type; if (objectType == GraphObjectTypes::Layer) { theParentTranslator.AppendChild(m_EditCamera); if (m_EditLightEnabled) { m_EditLight.m_Parent = &m_EditCamera; m_EditCamera.m_FirstChild = &m_EditLight; } else { m_EditCamera.m_FirstChild = nullptr; m_EditLight.m_Parent = nullptr; } } else if (objectType == GraphObjectTypes::Light) { m_editModeCamerasAndLights.push_back(&inParent); } else if (objectType == GraphObjectTypes::Camera) { m_editModeCamerasAndLights.push_back(&inParent); } } // Alias handles propagate down the scene graph. if (inParent.GetInstanceHandle() != inParent.GetSceneGraphInstanceHandle()) inAliasHandle = inParent.GetInstanceHandle(); for (long idx = 0, end = m_AssetGraph.GetChildCount(inParent.GetSceneGraphInstanceHandle()); idx < end; ++idx) { qt3dsdm::Qt3DSDMInstanceHandle theChild( m_AssetGraph.GetChild(inParent.GetSceneGraphInstanceHandle(), idx)); SGraphObjectTranslator *theTranslator = GetOrCreateTranslator(theChild, inAliasHandle); if (theTranslator == nullptr) continue; // We we have edit cameras active, we only render the active layer and we remove any cameras // in the active layer. Furthermore if our edit light is active, then we also remove any // active lights in the layer. if (m_EditCameraEnabled && !scenePreviewPass) { if (theTranslator->GetGraphObject().m_Type == GraphObjectTypes::Layer) { if (theChild == m_Doc.GetActiveLayer()) { if (m_EditCameraLayerTranslator != nullptr && m_EditCameraLayerTranslator->GetInstanceHandle() != theChild) { QT3DS_FREE(m_Allocator, m_EditCameraLayerTranslator); m_EditCameraLayerTranslator = nullptr; } if (!m_EditCameraLayerTranslator) { m_EditCameraLayerTranslator = QT3DS_NEW(m_Allocator, SEditCameraLayerTranslator)(theChild, m_Allocator); } theTranslator = m_EditCameraLayerTranslator; theParentTranslator.AppendChild(theTranslator->GetGraphObject()); BuildRenderGraph(*m_EditCameraLayerTranslator, scenePreviewPass); } } else { theParentTranslator.AppendChild(theTranslator->GetGraphObject()); BuildRenderGraph(theChild, scenePreviewPass, inAliasHandle); if (theTranslator->GetGraphObject().m_Type == GraphObjectTypes::Effect) theTranslator->SetActive(false); else if (theTranslator->GetGraphObject().m_Type == GraphObjectTypes::Camera) theTranslator->SetActive(false); else if (theTranslator->GetGraphObject().m_Type == GraphObjectTypes::Light && m_EditLightEnabled == true) theTranslator->SetActive(false); else theTranslator->SetActive(m_Reader.IsCurrentlyActive(theChild)); } } else // Else build the graph and it will be an exact copy of the asset graph. { theParentTranslator.AppendChild(theTranslator->GetGraphObject()); if (m_Reader.IsCurrentlyActive(theChild)) { BuildRenderGraph(theChild, scenePreviewPass, inAliasHandle); theTranslator->SetActive(true); } else { theTranslator->SetActive(false); DeactivateScan(*theTranslator, inAliasHandle); } } } if (GraphObjectTypes::Layer == theParentTranslator.GetGraphObject().m_Type) m_Context.GetRenderer().ChildrenUpdated( static_cast(theParentTranslator.GetGraphObject())); // Allow certain nodes to override their children. theParentTranslator.AfterRenderGraphIsBuilt(*this); } void STranslation::DeactivateScan(SGraphObjectTranslator &inParent, Qt3DSDMInstanceHandle inAliasHandle) { // Alias handles propagate down the scene graph. if (inParent.GetInstanceHandle() != inParent.GetSceneGraphInstanceHandle()) inAliasHandle = inParent.GetInstanceHandle(); for (long idx = 0, end = m_AssetGraph.GetChildCount(inParent.GetSceneGraphInstanceHandle()); idx < end; ++idx) { qt3dsdm::Qt3DSDMInstanceHandle theChild( m_AssetGraph.GetChild(inParent.GetSceneGraphInstanceHandle(), idx)); SGraphObjectTranslator *theTranslator = GetOrCreateTranslator(theChild, inAliasHandle); if (theTranslator == nullptr) continue; theTranslator->SetActive(false); DeactivateScan(*theTranslator, inAliasHandle); } } // We build the render graph every time we render. This may seem wasteful void STranslation::BuildRenderGraph(qt3dsdm::Qt3DSDMInstanceHandle inParent, bool scenePreviewPass, Qt3DSDMInstanceHandle inAliasHandle) { SGraphObjectTranslator *theParentTranslator = GetOrCreateTranslator(inParent, inAliasHandle); if (theParentTranslator == nullptr) return; if (m_Reader.IsCurrentlyActive(inParent) == false) { theParentTranslator->SetActive(false); return; } BuildRenderGraph(*theParentTranslator, scenePreviewPass, inAliasHandle); } void STranslation::ReleaseTranslation(Q3DStudio::TIdentifier inInstance) { m_TranslatorMap.erase(inInstance); } void STranslation::MarkDirty(qt3dsdm::Qt3DSDMInstanceHandle inInstance) { // Anchor points are not handled individually. if (m_Reader.GetObjectTypeName(inInstance) == L"PathAnchorPoint") inInstance = m_AssetGraph.GetParent(inInstance); GetOrCreateTranslator(inInstance); THandleTranslatorPairList &theTranslators = GetTranslatorsForInstance(inInstance); for (size_t idx = 0, end = theTranslators.size(); idx < end; ++idx) { m_DirtySet.insert(*theTranslators[(eastl::allocator::size_type)idx].second); // Reset effect when effect parameters change, as with certain corner cases // some effects would accumulate ~infinitely and result would not reflect // actual parameter setting unless reset (f.ex corona, other blur types) m_DirtySet.back()->ResetEffect(); } RequestRender(); } QT3DSVec2 STranslation::GetPreviewViewportDimensions() const { CStudioProjectSettings *theSettings = m_Doc.GetCore()->GetStudioProjectSettings(); QSize thePresSize = theSettings->getPresentationSize(); return QT3DSVec2(thePresSize.width(), thePresSize.height()); } qt3ds::QT3DSVec2 STranslation::GetOverlayPreviewDimensions() const { QT3DSVec2 ret(0.0f); if (hasRoomForOverlayPreview()) { CStudioProjectSettings *theSettings = m_Doc.GetCore()->GetStudioProjectSettings(); QSize thePresSize = theSettings->getPresentationSize(); ret = QT3DSVec2(thePresSize.width(), thePresSize.height()); const float aspect = ret.x / ret.y; if (aspect > 1.0) { ret.x = m_overlayPreviewSize > ret.x ? ret.x : m_overlayPreviewSize; ret.y = ret.x / aspect; } else { ret.y = m_overlayPreviewSize > ret.y ? ret.y : m_overlayPreviewSize; ret.x = ret.y * aspect; } } return ret; } void STranslation::PreRender(bool scenePreviewPass) { // Run through the entire asset graph and mark active or inactive if we have an // associated render representation. // If we cache all the components and some of their state then we don't have to do this // but for now it is more stable to run through the graph. // There is always one root, the scene. TIdentifier theRoot = m_AssetGraph.GetRoot(0); if (!scenePreviewPass) m_editModeCamerasAndLights.clear(); ClearDirtySet(); m_EditLightEnabled = CStudioPreferences::editModeLightingEnabled(); BuildRenderGraph(theRoot, scenePreviewPass); QT3DSVec2 theViewportDims(GetViewportDimensions()); if (scenePreviewPass) { m_Context.SetScaleMode(qt3ds::render::ScaleModes::FitSelected); theViewportDims = GetPreviewViewportDimensions(); } else { m_Context.SetScaleMode(qt3ds::render::ScaleModes::ExactSize); } static const QT3DSVec4 matteColor(CStudioPreferences::matteColor().redF(), CStudioPreferences::matteColor().greenF(), CStudioPreferences::matteColor().blueF(), 1.0f); m_Context.SetMatteColor(matteColor); // Ensure the camera points where it should if (m_EditCameraEnabled && !scenePreviewPass) { m_EditCameraInfo.ApplyToCamera(m_EditCamera, theViewportDims); m_EditLight.MarkDirty(qt3ds::render::NodeTransformDirtyFlag::TransformIsDirty); } if (m_Scene) { CStudioProjectSettings *theSettings = m_Doc.GetCore()->GetStudioProjectSettings(); QSize thePresSize = theSettings->getPresentationSize(); // The presentation sizes are used for when we have to render a layer offscreen. // If their width and height isn't set, then they use the presentation dimensions. m_Presentation.m_PresentationDimensions = QT3DSVec2((QT3DSF32)thePresSize.width(), (QT3DSF32)thePresSize.height()); m_Context.SetWindowDimensions( QSize((QT3DSU32)theViewportDims.x, (QT3DSU32)theViewportDims.y)); m_Context.SetPresentationDimensions( QSize((QT3DSU32)m_Presentation.m_PresentationDimensions.x, (QT3DSU32)m_Presentation.m_PresentationDimensions.y)); // set if we draw geometry in wireframe mode m_Context.SetWireframeMode(CStudioPreferences::IsWireframeModeOn()); if (m_EditCameraEnabled && !scenePreviewPass) { m_Presentation.m_PresentationDimensions = theViewportDims; m_Context.SetPresentationDimensions( QSize((QT3DSU32)theViewportDims.x, (QT3DSU32)theViewportDims.y)); m_Context.SetSceneColor(QT3DSVec4(0.0f, 0.0f, 0.0f, 1.0f)); } else { TIdentifier theRoot = m_AssetGraph.GetRoot(0); SGraphObjectTranslator *theSceneTranslator = GetOrCreateTranslator(theRoot); if (theSceneTranslator) { SScene &theScene = static_cast(theSceneTranslator->GetGraphObject()); if (scenePreviewPass) { if (theScene.m_UseClearColor) m_Context.SetMatteColor(theScene.m_ClearColor); else m_Context.SetMatteColor(QT3DSVec4(0.0f, 0.0f, 0.0f, 1.0f)); } else { if (theScene.m_UseClearColor) m_Context.SetSceneColor(theScene.m_ClearColor); else m_Context.SetSceneColor(QT3DSVec4(QT3DSVec3(0.0f), 1.0f)); } } } } if (m_EditCameraEnabled == false && g_StudioApp.IsAuthorZoom()) { if (m_Presentation.m_PresentationDimensions.x > theViewportDims.x || m_Presentation.m_PresentationDimensions.y > theViewportDims.y) { m_Context.SetScaleMode(qt3ds::render::ScaleModes::FitSelected); } } } static void CreatePixelRect(STranslation &inTranslation, QT3DSF32 left, QT3DSF32 right, QT3DSF32 bottom, QT3DSF32 top, QT3DSVec4 color) { SPGRect *theRect = QT3DS_NEW(inTranslation.m_GuideAllocator, SPGRect)(); theRect->m_Left = left; theRect->m_Right = right; theRect->m_Top = top; theRect->m_Bottom = bottom; theRect->m_FillColor = color; inTranslation.m_GuideContainer.push_back(theRect); } static void CreatePixelVertLine(STranslation &inTranslation, QT3DSF32 inXPos, QT3DSF32 inBottom, QT3DSF32 inTop, QT3DSVec4 color) { SPGVertLine *theLine = QT3DS_NEW(inTranslation.m_GuideAllocator, SPGVertLine)(); theLine->m_X = inXPos; theLine->m_Bottom = inBottom; theLine->m_Top = inTop; theLine->m_LineColor = color; inTranslation.m_GuideContainer.push_back(theLine); } static void CreatePixelHorzLine(STranslation &inTranslation, QT3DSF32 inYPos, QT3DSF32 inLeft, QT3DSF32 inRight, QT3DSVec4 color) { SPGHorzLine *theLine = QT3DS_NEW(inTranslation.m_GuideAllocator, SPGHorzLine)(); theLine->m_Y = inYPos; theLine->m_Left = inLeft; theLine->m_Right = inRight; theLine->m_LineColor = color; inTranslation.m_GuideContainer.push_back(theLine); } static void CreateTopBottomTickMarks(STranslation &inTranslation, QT3DSF32 posX, QT3DSF32 innerBottom, QT3DSF32 innerTop, QT3DSF32 outerBottom, QT3DSF32 outerTop, QT3DSF32 lineHeight, QT3DSVec4 lineColor) { CreatePixelVertLine(inTranslation, posX, innerBottom - lineHeight, innerBottom, lineColor); CreatePixelVertLine(inTranslation, posX, innerTop, innerTop + lineHeight, lineColor); } static void DrawTickMarksOnHorizontalRects(STranslation &inTranslation, QT3DSF32 innerLeft, QT3DSF32 innerRight, QT3DSF32 innerBottom, QT3DSF32 innerTop, QT3DSF32 outerBottom, QT3DSF32 outerTop, QT3DSVec4 lineColor) { QT3DSF32 centerPosX = floor(innerLeft + (innerRight - innerLeft) / 2.0f + .5f); CreateTopBottomTickMarks(inTranslation, centerPosX, innerBottom, innerTop, outerBottom, outerTop, 15, lineColor); for (QT3DSU32 incrementor = 10; (centerPosX + incrementor) < innerRight && (centerPosX - incrementor) > innerLeft; incrementor += 10) { QT3DSF32 rightEdge = centerPosX + incrementor; QT3DSF32 leftEdge = centerPosX - incrementor; QT3DSF32 lineHeight = 0; if (incrementor % 100 == 0) lineHeight = 11; else if (incrementor % 20) lineHeight = 4; else lineHeight = 2; if (rightEdge < innerRight) CreateTopBottomTickMarks(inTranslation, rightEdge, innerBottom, innerTop, outerBottom, outerTop, lineHeight, lineColor); if (leftEdge > innerLeft) CreateTopBottomTickMarks(inTranslation, leftEdge, innerBottom, innerTop, outerBottom, outerTop, lineHeight, lineColor); } } static void CreateLeftRightTickMarks(STranslation &inTranslation, QT3DSF32 inYPos, QT3DSF32 innerLeft, QT3DSF32 innerRight, QT3DSF32 outerLeft, QT3DSF32 outerRight, QT3DSF32 lineLength, QT3DSVec4 lineColor) { CreatePixelHorzLine(inTranslation, inYPos, innerLeft - lineLength, innerLeft, lineColor); CreatePixelHorzLine(inTranslation, inYPos, innerRight, innerRight + lineLength, lineColor); } static void DrawTickMarksOnVerticalRects(STranslation &inTranslation, QT3DSF32 innerLeft, QT3DSF32 innerRight, QT3DSF32 innerBottom, QT3DSF32 innerTop, QT3DSF32 outerLeft, QT3DSF32 outerRight, QT3DSVec4 lineColor) { QT3DSF32 centerPosY = floor(innerBottom + (innerTop - innerBottom) / 2.0f + .5f); CreateLeftRightTickMarks(inTranslation, centerPosY, innerLeft, innerRight, outerLeft, outerRight, 15, lineColor); for (QT3DSU32 incrementor = 10; (centerPosY + incrementor) < innerTop && (centerPosY - incrementor) > innerBottom; incrementor += 10) { QT3DSF32 topEdge = centerPosY + incrementor; QT3DSF32 bottomEdge = centerPosY - incrementor; QT3DSF32 lineHeight = 0; if (incrementor % 100 == 0) lineHeight = 11; else if (incrementor % 20) lineHeight = 4; else lineHeight = 2; if (topEdge < innerTop) CreateLeftRightTickMarks(inTranslation, topEdge, innerLeft, innerRight, outerLeft, outerRight, lineHeight, lineColor); if (bottomEdge > innerBottom) CreateLeftRightTickMarks(inTranslation, bottomEdge, innerLeft, innerRight, outerLeft, outerRight, lineHeight, lineColor); } } class IGuideElementFactory { protected: virtual ~IGuideElementFactory() {} public: virtual void CreateLine(QT3DSF32 inPos) = 0; virtual void CreateRect(QT3DSF32 inPosMin, QT3DSF32 inPosMax) = 0; }; static void CreateGuide(IGuideElementFactory &inFactory, QT3DSF32 inPos, QT3DSF32 inWidth) { QT3DSF32 halfWidth = inWidth / 2.0f; QT3DSF32 leftLine = floor(inPos + 1.0f - halfWidth); inFactory.CreateLine(leftLine); // Then we are done if not enough width if (inWidth < 2.0f) return; QT3DSF32 rightLine = leftLine + inWidth - 1; inFactory.CreateLine(rightLine); if (inWidth < 3.0f) return; QT3DSF32 rectStart = leftLine + 1; QT3DSF32 rectStop = rectStart + inWidth - 2.0f; inFactory.CreateRect(rectStart, rectStop); } struct SHorizontalGuideFactory : public IGuideElementFactory { STranslation &m_Translation; QT3DSF32 m_Start; QT3DSF32 m_Stop; QT3DSVec4 m_LineColor; QT3DSVec4 m_FillColor; SHorizontalGuideFactory(STranslation &trans, QT3DSF32 start, QT3DSF32 stop, QT3DSVec4 lineColor, QT3DSVec4 fillColor) : m_Translation(trans) , m_Start(start) , m_Stop(stop) , m_LineColor(lineColor) , m_FillColor(fillColor) { } void CreateLine(QT3DSF32 inPos) override { CreatePixelHorzLine(m_Translation, inPos, m_Start, m_Stop, m_LineColor); } void CreateRect(QT3DSF32 inPosMin, QT3DSF32 inPosMax) override { CreatePixelRect(m_Translation, m_Start, m_Stop, inPosMin, inPosMax, m_FillColor); } }; struct SVerticalGuideFactory : public IGuideElementFactory { STranslation &m_Translation; QT3DSF32 m_Start; QT3DSF32 m_Stop; QT3DSVec4 m_LineColor; QT3DSVec4 m_FillColor; SVerticalGuideFactory(STranslation &trans, QT3DSF32 start, QT3DSF32 stop, QT3DSVec4 lineColor, QT3DSVec4 fillColor) : m_Translation(trans) , m_Start(start) , m_Stop(stop) , m_LineColor(lineColor) , m_FillColor(fillColor) { } void CreateLine(QT3DSF32 inPos) override { CreatePixelVertLine(m_Translation, inPos, m_Start, m_Stop, m_LineColor); } void CreateRect(QT3DSF32 inPosMin, QT3DSF32 inPosMax) override { CreatePixelRect(m_Translation, inPosMin, inPosMax, m_Start, m_Stop, m_FillColor); } }; qt3ds::render::NVRenderRect STranslation::GetPreviewViewport() const { QT3DSVec2 vp = GetPreviewViewportDimensions(); return qt3ds::render::NVRenderRect(0, 0, vp.x, vp.y); } qt3ds::render::NVRenderRect STranslation::GetOverlayPreviewViewport() const { QT3DSVec2 vp = GetOverlayPreviewDimensions(); return qt3ds::render::NVRenderRect(0, 0, vp.x, vp.y); } bool STranslation::hasRoomForOverlayPreview() const { QT3DSVec2 vp(GetViewportDimensions()); return vp.x > m_overlayPreviewSize && vp.y > m_overlayPreviewSize; } void STranslation::Render(int inWidgetId, bool inDrawGuides, bool scenePreviewPass, bool overlayPreview) { // For now, we just render. // Next step will be to get the bounding boxes and such setup. // but we will want a custom renderer to do that. if (m_Scene) { // Note that begin frame is called before we allocate the bounding box and axis widgets so // that we can take advantage of the renderer's per-frame-allocator. m_Context.BeginFrame(); qt3dsdm::TInstanceHandleList theHandles = m_Doc.GetSelectedValue().GetSelectedInstances(); if (scenePreviewPass) { qt3ds::render::NVRenderContext &renderContext(m_Context.GetRenderContext()); QT3DSVec2 previewDims(GetPreviewViewportDimensions()); if (m_previewFboDimensions != previewDims) { m_previewFboDimensions = previewDims; if (m_previewFbo) m_Context.GetResourceManager().Release(*m_previewFbo); if (m_previewRenderBuffer) m_Context.GetResourceManager().Release(*m_previewRenderBuffer); if (m_previewTexture) m_Context.GetResourceManager().Release(*m_previewTexture); m_previewFbo = nullptr; m_previewRenderBuffer = nullptr; m_previewTexture = nullptr; } if (!m_previewFbo) m_previewFbo = m_Context.GetResourceManager().AllocateFrameBuffer(); if (!m_previewTexture) { m_previewTexture = renderContext.CreateTexture2D(); m_previewTexture->SetTextureData(qt3ds::foundation::NVDataRef(), 0, previewDims.x, previewDims.y, qt3ds::render::NVRenderTextureFormats::RGBA8); m_previewFbo->Attach( qt3ds::render::NVRenderFrameBufferAttachments::Color0, qt3ds::render::NVRenderTextureOrRenderBuffer(*m_previewTexture)); } if (!m_previewRenderBuffer) { m_previewRenderBuffer = m_Context.GetResourceManager().AllocateRenderBuffer( previewDims.x, previewDims.y, qt3ds::render::NVRenderRenderBufferFormats::Depth24); m_previewFbo->Attach( qt3ds::render::NVRenderFrameBufferAttachments::Depth, qt3ds::render::NVRenderTextureOrRenderBuffer(*m_previewRenderBuffer)); } renderContext.SetRenderTarget(m_previewFbo); } else { // Render the bounding boxes and extra widgets. // This is called *before* the render because these sort of appendages need to be added // to the layer renderables. // Don't show the bounding box or pivot for the component we are *in* the component SGraphObjectTranslator *theTranslator = nullptr; long theToolMode = g_StudioApp.GetToolMode(); int theCameraToolMode = m_EditCameraEnabled ? (theToolMode & STUDIO_CAMERATOOL_MASK) : 0; bool shouldDisplayWidget = false; if (theCameraToolMode == 0) { switch (theToolMode) { default: break; case STUDIO_TOOLMODE_MOVE: case STUDIO_TOOLMODE_ROTATE: case STUDIO_TOOLMODE_SCALE: shouldDisplayWidget = true; break; }; } bool selectedPath = false; for (size_t selectedIdx = 0, selectedEnd = theHandles.size(); selectedIdx < selectedEnd; ++selectedIdx) { qt3dsdm::Qt3DSDMInstanceHandle theInstance = theHandles[selectedIdx]; if (theInstance != m_Doc.GetDocumentReader().GetComponentForSlide(m_Doc.GetActiveSlide())) { if (m_Doc.GetDocumentReader().GetObjectTypeName(theInstance) == L"PathAnchorPoint") { theInstance = m_AssetGraph.GetParent(m_AssetGraph.GetParent(theInstance)); shouldDisplayWidget = false; } theTranslator = GetOrCreateTranslator(theInstance); // Get the tool mode right now. if (theTranslator) { GraphObjectTypes::Enum theType(theTranslator->GetGraphObject().m_Type); if (CStudioPreferences::IsBoundingBoxesOn()) { switch (theType) { case GraphObjectTypes::Node: DrawGroupBoundingBoxes(*theTranslator); break; case GraphObjectTypes::Text: case GraphObjectTypes::Model: case GraphObjectTypes::Layer: case GraphObjectTypes::Light: case GraphObjectTypes::Path: DrawNonGroupBoundingBoxes(*theTranslator); break; default: break; } } // Don't draw the axis if there is a widget. if (CStudioPreferences::ShouldDisplayPivotPoint()) { switch (theTranslator->GetGraphObject().m_Type) { case GraphObjectTypes::Node: case GraphObjectTypes::Text: case GraphObjectTypes::Model: drawPivot(*theTranslator); break; default: break; } } if (theType == GraphObjectTypes::Path && selectedPath == false) { selectedPath = true; if (!m_PathWidget) { m_PathWidget = qt3ds::widgets::IPathWidget::CreatePathWidget( m_Context.GetAllocator(), m_Context); } m_PathWidget->SetNode( static_cast(theTranslator->GetGraphObject())); m_Context.GetRenderer().AddRenderWidget(*m_PathWidget); } } } } if (theHandles.size() > 1) theTranslator = nullptr; qt3ds::widgets::IStudioWidget *theNextWidget(nullptr); if (theTranslator && GraphObjectTypes::IsNodeType(theTranslator->GetGraphObject().m_Type) && theTranslator->GetGraphObject().m_Type != GraphObjectTypes::Layer) { qt3ds::render::SNode &theNode( static_cast(theTranslator->GetGraphObject())); const GraphObjectTypes::Enum type = theTranslator->GetGraphObject().m_Type; // Don't draw widgets for non-visible nodes bool isActive = theNode.m_Flags.IsActive(); // Light and camera nodes are never active, so check from doc if (type == GraphObjectTypes::Camera || type == GraphObjectTypes::Light) isActive = m_Reader.IsCurrentlyActive(theHandles[0]); shouldDisplayWidget = shouldDisplayWidget && isActive; SCamera *theRenderCamera = m_Context.GetRenderer().GetCameraForNode(theNode); bool isActiveCamera = theRenderCamera == (static_cast(&theNode)); if (shouldDisplayWidget && !isActiveCamera && ((type == GraphObjectTypes::Camera && m_EditCameraEnabled) || type != GraphObjectTypes::Camera)) { switch (theToolMode) { default: QT3DS_ASSERT(false); break; case STUDIO_TOOLMODE_MOVE: // Render translation widget if (!m_TranslationWidget) { m_TranslationWidget = qt3ds::widgets::IStudioWidget::CreateTranslationWidget( m_Context.GetAllocator()); } theNextWidget = m_TranslationWidget.mPtr; break; case STUDIO_TOOLMODE_ROTATE: if (!m_RotationWidget) { m_RotationWidget = qt3ds::widgets::IStudioWidget::CreateRotationWidget( m_Context.GetAllocator()); } theNextWidget = m_RotationWidget.mPtr; break; case STUDIO_TOOLMODE_SCALE: if (!m_ScaleWidget) { m_ScaleWidget = qt3ds::widgets::IStudioWidget::CreateScaleWidget( m_Context.GetAllocator()); } theNextWidget = m_ScaleWidget.mPtr; break; } if (theNextWidget) { SNode &node = static_cast(theTranslator->GetGraphObject()); theNextWidget->SetNode(node); m_Context.GetRenderer().AddRenderWidget(*theNextWidget); } } } if (m_LastRenderedWidget.mPtr && m_LastRenderedWidget.mPtr != theNextWidget) ResetWidgets(); m_LastRenderedWidget = theNextWidget; if (m_LastRenderedWidget) { m_LastRenderedWidget->SetSubComponentId(inWidgetId); switch (g_StudioApp.GetManipulationMode()) { case StudioManipulationModes::Local: m_LastRenderedWidget->SetRenderWidgetMode( qt3ds::render::RenderWidgetModes::Local); break; case StudioManipulationModes::Global: m_LastRenderedWidget->SetRenderWidgetMode( qt3ds::render::RenderWidgetModes::Global); break; default: QT3DS_ASSERT(false); break; } } } Option viewport = m_Context.GetRenderContext().GetViewport(); if (scenePreviewPass) { m_Context.GetRenderContext().SetViewport(GetPreviewViewport()); m_Context.SetSceneColor(Option()); } m_Scene->PrepareForRender(scenePreviewPass ? GetPreviewViewportDimensions() : GetViewportDimensions(), m_Context); m_Context.RunRenderTasks(); if (!scenePreviewPass && m_EditCameraEnabled) { if (m_GradientWidget == nullptr) m_GradientWidget = qt3ds::widgets::SGradientWidget ::CreateGradientWidget(m_Context.GetAllocator()); // render gradient background SNode *node = GetEditCameraLayer(); m_GradientWidget->SetNode(*node); m_GradientWidget->Render(m_Context.GetRenderWidgetContext(), m_Context.GetRenderContext(), m_EditCameraInfo.IsOrthographic()); } m_Scene->Render(scenePreviewPass ? GetPreviewViewportDimensions() : GetViewportDimensions(), m_Context, SScene::DoNotClear); if (!scenePreviewPass && m_editModeCamerasAndLights.size() > 0) { if (!m_VisualAidWidget) { m_VisualAidWidget = qt3ds::widgets::SVisualAidWidget ::CreateVisualAidWidget(m_Context.GetAllocator()); } for (SGraphObjectTranslator *translator : m_editModeCamerasAndLights) { SGraphObject &object = translator->GetGraphObject(); qt3dsdm::Qt3DSDMInstanceHandle handle = translator->GetInstanceHandle(); m_VisualAidWidget->setSelected(false); for (int j = 0; j < theHandles.size(); ++j) { if (handle == theHandles[j]) { m_VisualAidWidget->setSelected(true); break; } } if (object.m_Type == GraphObjectTypes::Camera) { if (m_InnerRect.isNull()) { // this happens when the initial view is not the camera view NVRenderRect thePresentationViewport = m_Context.GetPresentationViewport(); m_InnerRect.m_Left = thePresentationViewport.m_X; m_InnerRect.m_Right = thePresentationViewport.m_X + thePresentationViewport.m_Width; m_InnerRect.m_Bottom = thePresentationViewport.m_Y; m_InnerRect.m_Top = thePresentationViewport.m_Y + thePresentationViewport.m_Height; } QT3DSVec2 dim = QT3DSVec2(m_InnerRect.m_Right - m_InnerRect.m_Left, m_InnerRect.m_Top - m_InnerRect.m_Bottom); NVRenderRectF theViewport(0, 0, dim.x, dim.y); static_cast(&object)->CalculateGlobalVariables(theViewport, dim); } m_VisualAidWidget->SetNode(static_cast(&object)); m_VisualAidWidget->Render(m_Context.GetRenderWidgetContext(), m_Context.GetRenderContext()); } } if (inDrawGuides && !m_EditCameraEnabled && !g_StudioApp.IsAuthorZoom()) { m_GuideContainer.clear(); // Figure out the matte area. NVRenderRect theContextViewport = m_Context.GetContextViewport(); NVRenderRect thePresentationViewport = m_Context.GetPresentationViewport(); m_Context.GetRenderContext().SetViewport(theContextViewport); QT3DSI32 innerLeft = thePresentationViewport.m_X; QT3DSI32 innerRight = thePresentationViewport.m_X + thePresentationViewport.m_Width; QT3DSI32 innerBottom = thePresentationViewport.m_Y; QT3DSI32 innerTop = thePresentationViewport.m_Y + thePresentationViewport.m_Height; QT3DSI32 outerLeft = innerLeft - 16; QT3DSI32 outerRight = innerRight + 16; QT3DSI32 outerBottom = innerBottom - 16; QT3DSI32 outerTop = innerTop + 16; // Retain the rects for picking purposes. m_InnerRect = SRulerRect(innerLeft, innerTop, innerRight, innerBottom); m_OuterRect = SRulerRect(outerLeft, outerTop, outerRight, outerBottom); // Draw tick marks around the presentation CreatePixelRect(*this, (QT3DSF32)outerLeft, (QT3DSF32)innerLeft, (QT3DSF32)innerBottom, (QT3DSF32)innerTop, m_rectColor); CreatePixelRect(*this, (QT3DSF32)innerRight, (QT3DSF32)outerRight, (QT3DSF32)innerBottom, (QT3DSF32)innerTop, m_rectColor); CreatePixelRect(*this, (QT3DSF32)innerLeft, (QT3DSF32)innerRight, (QT3DSF32)outerBottom, (QT3DSF32)innerBottom, m_rectColor); CreatePixelRect(*this, (QT3DSF32)innerLeft, (QT3DSF32)innerRight, (QT3DSF32)innerTop, (QT3DSF32)outerTop, m_rectColor); DrawTickMarksOnHorizontalRects(*this, (QT3DSF32)innerLeft, (QT3DSF32)innerRight, (QT3DSF32)innerBottom, (QT3DSF32)innerTop, (QT3DSF32)outerBottom, (QT3DSF32)outerTop, m_lineColor); DrawTickMarksOnVerticalRects(*this, (QT3DSF32)innerLeft, (QT3DSF32)innerRight, (QT3DSF32)innerBottom, (QT3DSF32)innerTop, (QT3DSF32)outerLeft, (QT3DSF32)outerRight, m_lineColor); qt3dsdm::TGuideHandleList theGuides = m_Doc.GetDocumentReader().GetGuides(); qt3dsdm::Qt3DSDMGuideHandle theSelectedGuide; Q3DStudio::SSelectedValue theSelection = m_Doc.GetSelectedValue(); if (theSelection.getType() == Q3DStudio::SelectedValueTypes::Guide) theSelectedGuide = theSelection.getData(); // Draw guides for (size_t guideIdx = 0, guideEnd = theGuides.size(); guideIdx < guideEnd; ++guideIdx) { qt3dsdm::SGuideInfo theInfo = m_Doc.GetDocumentReader().GetGuideInfo(theGuides[guideIdx]); bool isGuideSelected = theGuides[guideIdx] == theSelectedGuide; QT3DSVec4 theColor = isGuideSelected ? m_selectedGuideColor : m_guideColor; QT3DSVec4 theFillColor = isGuideSelected ? m_selectedGuideFillColor : m_guideFillColor; switch (theInfo.m_Direction) { case qt3dsdm::GuideDirections::Horizontal: { SHorizontalGuideFactory theFactory(*this, (QT3DSF32)innerLeft, (QT3DSF32)innerRight, theColor, theFillColor); CreateGuide(theFactory, (QT3DSF32)m_InnerRect.m_Bottom + theInfo.m_Position, (QT3DSF32)theInfo.m_Width); } break; case qt3dsdm::GuideDirections::Vertical: { SVerticalGuideFactory theFactory(*this, (QT3DSF32)innerBottom, (QT3DSF32)innerTop, theColor, theFillColor); CreateGuide(theFactory, (QT3DSF32)m_InnerRect.m_Left + theInfo.m_Position, (QT3DSF32)theInfo.m_Width); } break; default: QT3DS_ASSERT(false); break; } } m_Context.GetPixelGraphicsRenderer().Render( qt3ds::foundation::toDataRef(m_GuideContainer.data(), m_GuideContainer.size())); m_GuideContainer.clear(); m_GuideAllocator.reset(); } if (!scenePreviewPass && m_previewTexture) { if (overlayPreview) { // Draw the overlay framebuffer qt3ds::render::NVRenderContext &renderContext(m_Context.GetRenderContext()); renderContext.SetViewport(GetOverlayPreviewViewport()); qt3ds::render::SCamera camera; camera.MarkDirty(qt3ds::render::NodeTransformDirtyFlag::TransformIsDirty); camera.m_Flags.SetOrthographic(true); QT3DSVec2 previewDims(GetOverlayPreviewDimensions()); camera.CalculateGlobalVariables( render::NVRenderRectF(0, 0, previewDims.x, previewDims.y), previewDims); QT3DSMat44 theVP; camera.CalculateViewProjectionMatrix(theVP); renderContext.SetCullingEnabled(false); renderContext.SetBlendingEnabled(false); renderContext.SetDepthTestEnabled(false); renderContext.SetDepthWriteEnabled(false); m_Context.GetRenderer().RenderQuad(previewDims, theVP, *m_previewTexture); } else { // Hack: For some reason, the m_previewTexture is only valid later if it is // actually drawn somewhere during the main render pass, so draw a dummy quad m_Context.GetRenderContext().SetViewport(NVRenderRect(0, 0, 0, 0)); m_Context.GetRenderer().RenderQuad(QT3DSVec2(0.0f), QT3DSMat44(), *m_previewTexture); } } if (scenePreviewPass) m_Context.GetRenderContext().SetRenderTarget(nullptr); m_Context.EndFrame(); m_Context.GetRenderContext().SetViewport(viewport); QT3DSVec2 theViewportDims(GetViewportDimensions()); m_Context.SetWindowDimensions(QSize((QT3DSU32)theViewportDims.x, (QT3DSU32)theViewportDims.y)); CStudioProjectSettings *theSettings = m_Doc.GetCore()->GetStudioProjectSettings(); QSize thePresSize = theSettings->getPresentationSize(); m_Presentation.m_PresentationDimensions = QT3DSVec2((QT3DSF32)thePresSize.width(), (QT3DSF32)thePresSize.height()); if (m_ZoomRender.hasValue()) { RenderZoomRender(*m_ZoomRender); m_ZoomRender = Empty(); } // Render the pick buffer, useful for debugging why a widget wasn't hit. /* if ( m_PickBuffer ) { qt3ds::render::NVRenderContext& theRenderContext( m_Context.GetRenderContext() ); qt3ds::render::STextureDetails thePickDetails = m_PickBuffer->GetTextureDetails(); theRenderContext.SetViewport( qt3ds::render::NVRenderRect( 0, 0, thePickDetails.m_Width, thePickDetails.m_Height ) ); qt3ds::render::SCamera theCamera; theCamera.MarkDirty( qt3ds::render::NodeTransformDirtyFlag::TransformIsDirty ); theCamera.m_Flags.SetOrthographic( true ); QT3DSVec2 theDimensions( (QT3DSF32)thePickDetails.m_Width, (QT3DSF32)thePickDetails.m_Height ); theCamera.CalculateGlobalVariables( render::NVRenderRectF( 0, 0, theDimensions.x, theDimensions.y ), theDimensions ); QT3DSMat44 theVP; theCamera.CalculateViewProjectionMatrix( theVP ); theRenderContext.SetCullingEnabled( false ); theRenderContext.SetBlendingEnabled( false ); theRenderContext.SetDepthTestEnabled( false ); theRenderContext.SetDepthWriteEnabled( false ); m_Context.GetRenderer().RenderQuad( theDimensions, theVP, *m_PickBuffer ); }*/ } } void STranslation::ResetWidgets() { if (m_ScaleWidget) m_ScaleWidget->SetAxisScale(QT3DSVec3(1, 1, 1)); if (m_RotationWidget) m_RotationWidget->ClearRotationEdges(); m_CumulativeRotation = 0.0f; } void STranslation::DoPrepareForDrag(SNode *inSelectedNode) { if (inSelectedNode == nullptr) return; m_MouseDownNode = *inSelectedNode; m_MouseDownParentGlobalTransformInverse = Empty(); m_MouseDownParentRotationInverse = Empty(); m_MouseDownGlobalRotation = Empty(); // Orphan this node manually since it is a straight copy m_MouseDownNode.m_Parent = nullptr; m_MouseDownNode.m_FirstChild = nullptr; m_MouseDownNode.m_NextSibling = nullptr; SCamera *theCamera = m_Context.GetRenderer().GetCameraForNode(*inSelectedNode); m_CumulativeRotation = 0.0f; if (theCamera == nullptr) return; m_MouseDownCamera = *theCamera; m_LastPathDragValue = Empty(); } void STranslation::EndDrag() { ResetWidgets(); } bool STranslation::IsPathWidgetActive() { qt3dsdm::TInstanceHandleList theHandles = m_Doc.GetSelectedValue().GetSelectedInstances(); for (size_t selectedIdx = 0, selectedEnd = theHandles.size(); selectedIdx < selectedEnd; ++selectedIdx) { qt3dsdm::Qt3DSDMInstanceHandle theInstance(theHandles[selectedIdx]); if (m_Doc.GetDocumentReader().GetObjectTypeName(theInstance) == L"PathAnchorPoint") theInstance = m_AssetGraph.GetParent(m_AssetGraph.GetParent(theInstance)); SGraphObjectTranslator *theTranslator = GetOrCreateTranslator(theInstance); if (theTranslator && theTranslator->GetGraphObject().m_Type == GraphObjectTypes::Path) return true; } return false; } inline qt3ds::render::SLayer *GetLayerForNode(const qt3ds::render::SNode &inNode) { SNode *theNode; // empty loop intentional for (theNode = const_cast(&inNode); theNode && theNode->m_Type != GraphObjectTypes::Layer; theNode = theNode->m_Parent) { } if (theNode && theNode->m_Type == GraphObjectTypes::Layer) return static_cast(theNode); return nullptr; } void STranslation::RenderZoomRender(SZoomRender &inRender) { SLayer *theLayer(inRender.m_Layer); CPt thePoint(inRender.m_Point); if (theLayer) { qt3ds::render::IQt3DSRenderer &theRenderer(m_Context.GetRenderer()); Option thePickSetup( theRenderer.GetLayerPickSetup(*theLayer, QT3DSVec2((QT3DSF32)thePoint.x, (QT3DSF32)thePoint.y), QSize(16, 16))); if (thePickSetup.hasValue()) { qt3ds::render::NVRenderContext &theRenderContext(m_Context.GetRenderContext()); theRenderContext.SetViewport(qt3ds::render::NVRenderRect(0, 0, 100, 100)); theRenderContext.SetScissorRect(qt3ds::render::NVRenderRect(0, 0, 100, 100)); theRenderContext.SetDepthWriteEnabled(true); theRenderContext.SetScissorTestEnabled(true); theRenderContext.SetClearColor(QT3DSVec4(.2f, .2f, .2f, 0.0f)); theRenderContext.Clear(qt3ds::render::NVRenderClearFlags( qt3ds::render::NVRenderClearValues::Color | qt3ds::render::NVRenderClearValues::Depth)); theRenderer.RunLayerRender(*theLayer, thePickSetup->m_ViewProjection); theRenderContext.SetScissorTestEnabled(false); } } } void STranslation::DrawBoundingBox(SNode &inNode, QT3DSVec3 inColor) { qt3ds::NVBounds3 theBounds = inNode.GetBounds(m_Context.GetBufferManager(), m_Context.GetPathManager(), true, this); qt3ds::render::IRenderWidget &theBBoxWidget = qt3ds::render::IRenderWidget::CreateBoundingBoxWidget( inNode, theBounds, inColor, m_Context.GetRenderer().GetPerFrameAllocator()); m_Context.GetRenderer().AddRenderWidget(theBBoxWidget); } void STranslation::drawPivot(SGraphObjectTranslator &inTranslator) { if (GraphObjectTypes::IsNodeType(inTranslator.GetGraphObject().m_Type)) { qt3ds::render::IRenderWidget &theAxisWidget = qt3ds::render::IRenderWidget::CreateAxisWidget( static_cast(inTranslator.GetGraphObject()), m_Context.GetRenderer().GetPerFrameAllocator()); m_Context.GetRenderer().AddRenderWidget(theAxisWidget); } else { QT3DS_ASSERT(false); } } void STranslation::SetViewport(qt3ds::QT3DSF32 inWidth, qt3ds::QT3DSF32 inHeight) { m_Viewport = QT3DSVec2(inWidth, inHeight); if (m_EditCameraEnabled) { // Update inner rect as it is used to calculate camera frustrum for visual aid widget QSize theSize = g_StudioApp.GetCore()->GetStudioProjectSettings()->getPresentationSize(); m_InnerRect.m_Top = 0; m_InnerRect.m_Bottom = theSize.height(); m_InnerRect.m_Left = 0; m_InnerRect.m_Right = theSize.width(); } } Option STranslation::PickWidget(CPt inMouseCoords, TranslationSelectMode::Enum, qt3ds::widgets::IStudioWidgetBase &inWidget) { SNode &theNode = inWidget.GetNode(); SGraphObjectTranslator *theWidgetTranslator = theNode.m_UserData.DynamicCast(); SLayer *theLayer = GetLayerForNode(theNode); if (theLayer && theWidgetTranslator) { Option thePickSetup( m_Context.GetRenderer().GetLayerPickSetup( *theLayer, QT3DSVec2((QT3DSF32)inMouseCoords.x, (QT3DSF32)inMouseCoords.y), QSize(4, 4))); if (thePickSetup.hasValue()) { qt3ds::render::NVRenderContext &theContext(m_Context.GetRenderContext()); qt3ds::render::NVRenderContextScopedProperty __currentrt(theContext, &qt3ds::render::NVRenderContext::GetRenderTarget, &qt3ds::render::NVRenderContext::SetRenderTarget); qt3ds::render::NVRenderFrameBuffer *theFBO = m_Context.GetResourceManager().AllocateFrameBuffer(); const QT3DSU32 fboDims = 8; if (!m_PickBuffer) { m_PickBuffer = theContext.CreateTexture2D(); m_PickBuffer->SetTextureData(qt3ds::foundation::NVDataRef(), 0, fboDims, fboDims, qt3ds::render::NVRenderTextureFormats::LuminanceAlpha8); } qt3ds::render::NVRenderRenderBuffer *theRenderBuffer = m_Context.GetResourceManager().AllocateRenderBuffer( fboDims, fboDims, qt3ds::render::NVRenderRenderBufferFormats::Depth16); theFBO->Attach(qt3ds::render::NVRenderFrameBufferAttachments::Color0, qt3ds::render::NVRenderTextureOrRenderBuffer(*m_PickBuffer)); theFBO->Attach(qt3ds::render::NVRenderFrameBufferAttachments::Depth, qt3ds::render::NVRenderTextureOrRenderBuffer(*theRenderBuffer)); qt3ds::render::NVRenderRect theViewport(0, 0, fboDims, fboDims); theContext.SetViewport(theViewport); theContext.SetDepthWriteEnabled(true); theContext.SetDepthTestEnabled(true); theContext.SetScissorTestEnabled(false); theContext.SetBlendingEnabled(false); theContext.SetClearColor(QT3DSVec4(0, 0, 0, 0)); theContext.Clear(qt3ds::render::NVRenderClearFlags( qt3ds::render::NVRenderClearValues::Color | qt3ds::render::NVRenderClearValues::Depth)); inWidget.RenderPick(thePickSetup->m_ProjectionPreMultiply, theContext, QSize(4, 4)); // Now read the pixels back. m_PixelBuffer.resize(fboDims * fboDims * 3); theContext.ReadPixels(theViewport, qt3ds::render::NVRenderReadPixelFormats::RGB8, m_PixelBuffer); m_Context.GetResourceManager().Release(*theFBO); m_Context.GetResourceManager().Release(*theRenderBuffer); eastl::hash_map tallies; QT3DSU32 numPixels = fboDims * fboDims; for (QT3DSU32 idx = 0; idx < numPixels; ++idx) { qt3ds::QT3DSU16 theChannelAmount = m_PixelBuffer[idx * 3] + (m_PixelBuffer[idx * 3 + 1] << 8); if (theChannelAmount) tallies.insert(eastl::make_pair(theChannelAmount, (QT3DSU32)0)).first->second += 1; } QT3DSU32 tallyMaxTally = 0; QT3DSU32 tallyMaxIdx = 0; for (eastl::hash_map::iterator iter = tallies.begin(), end = tallies.end(); iter != end; ++iter) { if (iter->second > tallyMaxTally) { tallyMaxTally = iter->second; tallyMaxIdx = iter->first; } } if (tallyMaxIdx > 0) { return tallyMaxIdx; } } } return Empty(); } SStudioPickValue STranslation::Pick(CPt inMouseCoords, TranslationSelectMode::Enum inSelectMode, bool ignoreWidgets) { bool requestRender = false; if (!ignoreWidgets) { if (m_Doc.GetDocumentReader().AreGuidesEditable()) { qt3dsdm::TGuideHandleList theGuides = m_Doc.GetDocumentReader().GetGuides(); CPt renderSpacePt(inMouseCoords.x - (long)m_InnerRect.m_Left, (long)GetViewportDimensions().y - inMouseCoords.y - (long)m_InnerRect.m_Bottom); for (size_t guideIdx = 0, guideEnd = theGuides.size(); guideIdx < guideEnd; ++guideIdx) { qt3dsdm::SGuideInfo theGuideInfo = m_Doc.GetDocumentReader().GetGuideInfo(theGuides[guideIdx]); float width = (theGuideInfo.m_Width / 2.0f) + 2.0f; switch (theGuideInfo.m_Direction) { case qt3dsdm::GuideDirections::Horizontal: if (fabs((float)renderSpacePt.y - theGuideInfo.m_Position) <= width) return theGuides[guideIdx]; break; case qt3dsdm::GuideDirections::Vertical: if (fabs((float)renderSpacePt.x - theGuideInfo.m_Position) <= width) return theGuides[guideIdx]; break; default: break; } } } if (IsPathWidgetActive()) { Option picked = PickWidget(inMouseCoords, inSelectMode, *m_PathWidget); if (picked.hasValue()) { RequestRender(); DoPrepareForDrag(&m_PathWidget->GetNode()); return m_PathWidget->PickIndexToPickValue(*picked); } } // Pick against the widget first if possible. if (m_LastRenderedWidget && (m_LastRenderedWidget->GetNode().m_Flags.IsActive() || m_LastRenderedWidget->GetNode().m_Type == GraphObjectTypes::Light || m_LastRenderedWidget->GetNode().m_Type == GraphObjectTypes::Camera)) { Option picked = PickWidget(inMouseCoords, inSelectMode, *m_LastRenderedWidget); if (picked.hasValue()) { RequestRender(); DoPrepareForDrag(&m_LastRenderedWidget->GetNode()); return m_LastRenderedWidget->PickIndexToPickValue(*picked); } } } // Pick against Lights and Cameras // This doesn't use the color picker or renderer pick float lastDist = 99999999999999.0f; int lastIndex = -1; for (int i = 0; i < int(m_editModeCamerasAndLights.size()); ++i) { const QT3DSVec2 mouseCoords((QT3DSF32)inMouseCoords.x, (QT3DSF32)inMouseCoords.y); float dist; SGraphObject &object = m_editModeCamerasAndLights[i]->GetGraphObject(); m_VisualAidWidget->SetNode(static_cast(&object)); if (m_VisualAidWidget->pick(m_Context.GetRenderer().GetRenderWidgetContext(), dist, GetViewportDimensions(), mouseCoords)) { if (dist < lastDist) { lastDist = dist; lastIndex = i; } } } if (m_Scene && m_Scene->m_FirstChild) { qt3ds::render::Qt3DSRenderPickResult thePickResult = m_Context.GetRenderer().Pick(*m_Scene->m_FirstChild, GetViewportDimensions(), QT3DSVec2((QT3DSF32)inMouseCoords.x, (QT3DSF32)inMouseCoords.y)); if (thePickResult.m_HitObject) { const SGraphObject &theObject = *thePickResult.m_HitObject; // check hit distance to cameras and lights if (lastIndex != -1 && thePickResult.m_CameraDistanceSq > lastDist * lastDist) { DoPrepareForDrag(static_cast( &(m_editModeCamerasAndLights[lastIndex]->GetGraphObject()))); return m_editModeCamerasAndLights[lastIndex]->GetInstanceHandle(); } if (theObject.m_Type == GraphObjectTypes::Model || theObject.m_Type == GraphObjectTypes::Text || theObject.m_Type == GraphObjectTypes::Path) { const SNode &theTranslatorModel(static_cast(theObject)); SGraphObjectTranslator *theTranslator = theTranslatorModel.m_UserData.DynamicCast(); const SNode *theModelPtr = &theTranslatorModel; if (theTranslator->GetPossiblyAliasedInstanceHandle() != theTranslator->GetInstanceHandle()) { theTranslator = GetOrCreateTranslator(theTranslator->GetPossiblyAliasedInstanceHandle()); theModelPtr = static_cast(&theTranslator->GetNonAliasedGraphObject()); } Qt3DSDMInstanceHandle theActiveComponent = m_Reader.GetComponentForSlide(m_Doc.GetActiveSlide()); if (inSelectMode == TranslationSelectMode::Group) { // Bounce up the hierarchy till one of two conditions are met // the parent is a layer or the our component is the active component // but the parent's is not. while (theTranslator && GraphObjectTypes::IsNodeType( theTranslator->GetGraphObject().m_Type)) { SNode *myNode = static_cast(&theTranslator->GetGraphObject()); if (myNode->m_Parent == nullptr) { theTranslator = nullptr; break; } SNode *parentNode = myNode->m_Parent; SGraphObjectTranslator *theParentTranslator = parentNode->m_UserData.DynamicCast(); Qt3DSDMInstanceHandle myComponent = m_Reader.GetAssociatedComponent(theTranslator->GetInstanceHandle()); Qt3DSDMInstanceHandle myParentComponent = m_Reader.GetAssociatedComponent( theParentTranslator->GetInstanceHandle()); if (parentNode->m_Type == GraphObjectTypes::Layer) { if (myParentComponent != theActiveComponent) theTranslator = nullptr; break; } if (myComponent == theActiveComponent && myParentComponent != theActiveComponent) break; theTranslator = theParentTranslator; } } else { // Bounce up until we get into the active component and then stop. while (inSelectMode == TranslationSelectMode::Single && theTranslator && GraphObjectTypes::IsNodeType(theTranslator->GetGraphObject().m_Type) && m_Reader.GetAssociatedComponent(theTranslator->GetInstanceHandle()) != theActiveComponent) { SNode *theNode = static_cast(&theTranslator->GetGraphObject()); theNode = theNode->m_Parent; if (theNode && theNode->m_Type != GraphObjectTypes::Layer) theTranslator = theNode->m_UserData.DynamicCast(); else theTranslator = nullptr; } } if (theTranslator) { QT3DS_ASSERT(GraphObjectTypes::IsNodeType(theTranslator->GetGraphObject().m_Type)); DoPrepareForDrag(static_cast(&theTranslator->GetGraphObject())); return theTranslator->GetInstanceHandle(); } } } if (requestRender) RequestRender(); } if (lastIndex != -1) { DoPrepareForDrag(static_cast( &(m_editModeCamerasAndLights[lastIndex]->GetGraphObject()))); return m_editModeCamerasAndLights[lastIndex]->GetInstanceHandle(); } return SStudioPickValue(); } qt3ds::foundation::Option STranslation::PickRulers(CPt inMouseCoords) { CPt renderSpacePt(inMouseCoords.x, (long)GetViewportDimensions().y - inMouseCoords.y); // If mouse is inside outer rect but outside inner rect. if (m_OuterRect.Contains(renderSpacePt.x, renderSpacePt.y) && !m_InnerRect.Contains(renderSpacePt.x, renderSpacePt.y)) { std::shared_ptr theGuideSystem = m_StudioSystem.GetFullSystem()->GetCoreSystem()->GetGuideSystem(); if (renderSpacePt.x >= m_InnerRect.m_Left && renderSpacePt.x <= m_InnerRect.m_Right) { return qt3dsdm::SGuideInfo((QT3DSF32)renderSpacePt.y - (QT3DSF32)m_InnerRect.m_Bottom, qt3dsdm::GuideDirections::Horizontal); } else if (renderSpacePt.y >= m_InnerRect.m_Bottom && renderSpacePt.y <= m_InnerRect.m_Top) { return qt3dsdm::SGuideInfo((QT3DSF32)renderSpacePt.x - (QT3DSF32)m_InnerRect.m_Left, qt3dsdm::GuideDirections::Vertical); } } return qt3ds::foundation::Option(); } QT3DSVec3 STranslation::GetIntendedPosition(qt3dsdm::Qt3DSDMInstanceHandle inInstance, CPt inPos) { ClearDirtySet(); SGraphObjectTranslator *theTranslator = GetOrCreateTranslator(inInstance); if (theTranslator == nullptr) return QT3DSVec3(0, 0, 0); if (GraphObjectTypes::IsNodeType(theTranslator->GetGraphObject().m_Type) == false) return QT3DSVec3(0, 0, 0); SNode *theNode = static_cast(&theTranslator->GetGraphObject()); SCamera *theCamera = m_Context.GetRenderer().GetCameraForNode(*theNode); { // Get the node's parent Qt3DSDMInstanceHandle theParent = m_AssetGraph.GetParent(inInstance); SGraphObjectTranslator *theParentTranslator = GetOrCreateTranslator(theParent); if (theParentTranslator && GraphObjectTypes::IsNodeType(theParentTranslator->GetGraphObject().m_Type)) theCamera = m_Context.GetRenderer().GetCameraForNode( *static_cast(&theParentTranslator->GetGraphObject())); } if (theCamera == nullptr) return QT3DSVec3(0, 0, 0); QT3DSVec3 theGlobalPos(theNode->GetGlobalPos()); return m_Context.GetRenderer().UnprojectToPosition(*theCamera, theGlobalPos, QT3DSVec2((QT3DSF32)inPos.x, (QT3DSF32)inPos.y)); } static void CheckLockToAxis(QT3DSF32 &inXDistance, QT3DSF32 &inYDistance, bool inLockToAxis) { if (inLockToAxis) { if (fabs(inXDistance) > fabs(inYDistance)) inYDistance = 0; else inXDistance = 0; } } void STranslation::ApplyPositionalChange(QT3DSVec3 inDiff, SNode &inNode, CUpdateableDocumentEditor &inEditor) { if (m_MouseDownParentGlobalTransformInverse.isEmpty()) { if (inNode.m_Parent) m_MouseDownParentGlobalTransformInverse = inNode.m_Parent->m_GlobalTransform.getInverse(); else m_MouseDownParentGlobalTransformInverse = QT3DSMat44::createIdentity(); } QT3DSMat44 theGlobalTransform = m_MouseDownNode.m_GlobalTransform; QT3DSMat44 theNewLocalTransform = m_MouseDownParentGlobalTransformInverse.getValue() * theGlobalTransform; QT3DSVec3 theOldPos = theNewLocalTransform.column3.getXYZ(); theOldPos.z *= -1; theGlobalTransform.column3 += QT3DSVec4(inDiff, 0.0f); theNewLocalTransform = m_MouseDownParentGlobalTransformInverse.getValue() * theGlobalTransform; QT3DSVec3 thePos = theNewLocalTransform.column3.getXYZ(); thePos.z *= -1; QT3DSVec3 theDiff = thePos - theOldPos; SetPosition(m_MouseDownNode.m_Position + theDiff, inEditor); } void STranslation::TranslateSelectedInstanceAlongCameraDirection( CPt inOriginalCoords, CPt inMouseCoords, CUpdateableDocumentEditor &inEditor) { SNode *theNode = GetSelectedNode(); if (theNode == nullptr) return; SCamera *theCamera = m_Context.GetRenderer().GetCameraForNode(*theNode); if (theCamera == nullptr) return; QT3DSF32 theYDistance = QT3DSF32(inMouseCoords.y - inOriginalCoords.y); if (fabs(theYDistance) == 0) return; QT3DSF32 theMouseMultiplier = 1.0f / 2.0f; QT3DSF32 theDistanceMultiplier = 1.0f + theYDistance * theMouseMultiplier; QT3DSVec3 theCameraDir = m_MouseDownCamera.GetDirection(); QT3DSVec3 theDiff = theCameraDir * theDistanceMultiplier; ApplyPositionalChange(theDiff, *theNode, inEditor); } void STranslation::TranslateSelectedInstance(CPt inOriginalCoords, CPt inMouseCoords, CUpdateableDocumentEditor &inEditor, bool inLockToAxis) { SNode *theNode = GetSelectedNode(); if (theNode == nullptr) return; qt3ds::render::IQt3DSRenderer &theRenderer(m_Context.GetRenderer()); QT3DSF32 theXDistance = QT3DSF32(inMouseCoords.x - inOriginalCoords.x); QT3DSF32 theYDistance = QT3DSF32(inMouseCoords.y - inOriginalCoords.y); if (fabs(theXDistance) == 0 && fabs(theYDistance) == 0) return; CheckLockToAxis(theXDistance, theYDistance, inLockToAxis); inMouseCoords.x = inOriginalCoords.x + (long)theXDistance; inMouseCoords.y = inOriginalCoords.y + (long)theYDistance; QT3DSVec3 theNodeGlobal = m_MouseDownNode.GetGlobalPos(); QT3DSVec3 theOriginalPos = theRenderer.UnprojectToPosition( *theNode, theNodeGlobal, QT3DSVec2((QT3DSF32)inOriginalCoords.x, (QT3DSF32)inOriginalCoords.y)); QT3DSVec3 theNewPos = theRenderer.UnprojectToPosition( *theNode, theNodeGlobal, QT3DSVec2((QT3DSF32)inMouseCoords.x, (QT3DSF32)inMouseCoords.y)); QT3DSVec3 theDiff = theNewPos - theOriginalPos; ApplyPositionalChange(theDiff, *theNode, inEditor); } void STranslation::ScaleSelectedInstanceZ(CPt inOriginalCoords, CPt inMouseCoords, CUpdateableDocumentEditor &inEditor) { SNode *theNode = GetSelectedNode(); if (theNode == nullptr) return; // Scale scales uniformly and responds to mouse Y only. QT3DSF32 theYDistance = (QT3DSF32)inMouseCoords.y - (QT3DSF32)inOriginalCoords.y; if (fabs(theYDistance) == 0) return; QT3DSF32 theMouseMultiplier = 1.0f / 40.0f; QT3DSF32 theScaleMultiplier = 1.0f + theYDistance * theMouseMultiplier; SetScale(QT3DSVec3(m_MouseDownNode.m_Scale.x, m_MouseDownNode.m_Scale.y, m_MouseDownNode.m_Scale.z * theScaleMultiplier), inEditor); } void STranslation::ScaleSelectedInstance(CPt inOriginalCoords, CPt inMouseCoords, CUpdateableDocumentEditor &inEditor) { SNode *theNode = GetSelectedNode(); if (theNode == nullptr) return; // Scale scales uniformly and responds to mouse Y only. QT3DSF32 theYDistance = (QT3DSF32)inMouseCoords.y - (QT3DSF32)inOriginalCoords.y; if (fabs(theYDistance) == 0) return; QT3DSF32 theMouseMultiplier = 1.0f / 40.0f; QT3DSF32 theScaleMultiplier = 1.0f + theYDistance * theMouseMultiplier; SetScale(m_MouseDownNode.m_Scale * theScaleMultiplier, inEditor); } void STranslation::CalculateNodeGlobalRotation(SNode &inNode) { if (inNode.m_Parent) CalculateNodeGlobalRotation(*inNode.m_Parent); if (m_MouseDownParentRotationInverse.isEmpty()) { m_MouseDownParentRotationInverse = QT3DSMat33::createIdentity(); m_MouseDownGlobalRotation = QT3DSMat33::createIdentity(); } QT3DSMat44 localRotation; inNode.CalculateRotationMatrix(localRotation); if (inNode.m_Flags.IsLeftHanded()) SNode::FlipCoordinateSystem(localRotation); QT3DSMat33 theRotation; SNode::GetMatrixUpper3x3(theRotation, localRotation); m_MouseDownParentRotationInverse = m_MouseDownGlobalRotation; m_MouseDownGlobalRotation = m_MouseDownGlobalRotation.getValue() * theRotation; } void STranslation::ApplyRotationToSelectedInstance(const QT3DSQuat &inFinalRotation, SNode &inNode, CUpdateableDocumentEditor &inEditor, bool inIsMouseRelative) { if (m_MouseDownParentRotationInverse.isEmpty()) { CalculateNodeGlobalRotation(inNode); m_MouseDownParentRotationInverse = m_MouseDownParentRotationInverse->getInverse(); } QT3DSMat33 theRotationMatrix(inFinalRotation); QT3DSMat33 theFinalGlobal = theRotationMatrix * m_MouseDownGlobalRotation.getValue(); QT3DSMat33 theLocalGlobal = m_MouseDownParentRotationInverse.getValue() * theFinalGlobal; QT3DSVec3 theRotations = inNode.GetRotationVectorFromRotationMatrix(theLocalGlobal); theRotations = qt3ds::render::SRotationHelper::ToNearestAngle(inNode.m_Rotation, theRotations, inNode.m_RotationOrder); SetRotation(theRotations, inEditor); // Trackball rotation is relative to the previous mouse position. // Rotation manipulator rotation is relative to only the original mouse down position // so inIsMouseRelative is false for rotations that are relative to the original mouse down // position. if (inIsMouseRelative) m_MouseDownGlobalRotation = theFinalGlobal; } void STranslation::RotateSelectedInstanceAboutCameraDirectionVector( CPt inPreviousMouseCoords, CPt inMouseCoords, CUpdateableDocumentEditor &inEditor) { SNode *theNode = GetSelectedNode(); if (theNode == nullptr) return; SCamera *theCamera = m_Context.GetRenderer().GetCameraForNode(*theNode); if (theCamera == nullptr) return; QT3DSVec3 theDirection = m_MouseDownCamera.GetDirection(); QT3DSF32 theYDistance = (QT3DSF32)inMouseCoords.y - (QT3DSF32)inPreviousMouseCoords.y; QT3DSQuat theYRotation(-1.0f * theYDistance * g_RotationScaleFactor, theDirection); ApplyRotationToSelectedInstance(theYRotation, *theNode, inEditor); } // This method never feels right to me. It is difficult to apply it to a single axis (of course for // that you can use the inspector palette). void STranslation::RotateSelectedInstance(CPt inOriginalCoords, CPt inPreviousCoords, CPt inMouseCoords, CUpdateableDocumentEditor &inEditor, bool inLockToAxis) { SNode *theNode = GetSelectedNode(); if (theNode == nullptr) return; SCamera *theCamera = m_Context.GetRenderer().GetCameraForNode(*theNode); if (theCamera == nullptr) return; // We want to do a similar translation to what we did below but we need to calculate the // parent's global rotation without scale included. QT3DSF32 theXDistance = (QT3DSF32)inMouseCoords.x - (QT3DSF32)inPreviousCoords.x; QT3DSF32 theYDistance = (QT3DSF32)inMouseCoords.y - (QT3DSF32)inPreviousCoords.y; bool xIsZero = fabs(theXDistance) < .001f; bool yIsZero = fabs(theYDistance) < .001f; if (xIsZero && yIsZero) return; if (inLockToAxis) { QT3DSF32 originalDistX = (QT3DSF32)inMouseCoords.x - (QT3DSF32)inOriginalCoords.x; QT3DSF32 originalDistY = (QT3DSF32)inMouseCoords.y - (QT3DSF32)inOriginalCoords.y; if (inLockToAxis) { if (fabs(originalDistX) > fabs(originalDistY)) theYDistance = 0; else theXDistance = 0; } } QT3DSVec3 theXAxis = m_MouseDownCamera.m_GlobalTransform.column0.getXYZ(); QT3DSVec3 theYAxis = m_MouseDownCamera.m_GlobalTransform.column1.getXYZ(); QT3DSVec3 theFinalAxis = theXDistance * theYAxis + theYDistance * theXAxis; QT3DSF32 theTotalDistance = theFinalAxis.normalize(); QT3DSQuat theRotation(theTotalDistance * g_RotationScaleFactor / 2.0f, theFinalAxis); ApplyRotationToSelectedInstance(theRotation, *theNode, inEditor); } inline void NiceAdd(QT3DSF32 &ioValue, QT3DSF32 inIncrement) { QT3DSF32 temp = ioValue + inIncrement; // Round to nearest .5 QT3DSF32 sign = temp >= 0 ? 1.0f : -1.0f; QT3DSU32 largerValue = (QT3DSU32)(fabs(temp * 10.0f)); QT3DSU32 leftover = largerValue % 10; // Round down to zero largerValue -= leftover; if (leftover < 2) leftover = 0; else if (leftover > 7) { leftover = 0; largerValue += 10; } else leftover = 5; largerValue += leftover; ioValue = sign * (QT3DSF32)largerValue / 10.0f; } static inline QT3DSVec3 GetAxis(QT3DSU32 inIndex, QT3DSMat33 &inMatrix) { QT3DSVec3 retval(0, 0, 0); switch (inIndex) { case 0: retval = inMatrix.column0; break; case 1: retval = inMatrix.column1; break; case 2: retval = inMatrix.column2; break; default: QT3DS_ASSERT(false); break; } retval.normalize(); return retval; } inline Option GetScaleAlongAxis(const QT3DSVec3 &inAxis, const QT3DSVec3 &inObjToOriginal, const QT3DSVec3 &inObjToCurrent) { QT3DSF32 lhs = inAxis.dot(inObjToCurrent); QT3DSF32 rhs = inAxis.dot(inObjToOriginal); if (fabs(rhs) > .001f) return lhs / rhs; return Empty(); } // Make a nice rotation from the incoming rotation static inline QT3DSF32 MakeNiceRotation(QT3DSF32 inAngle) { TODEG(inAngle); inAngle *= 10.0f; QT3DSF32 sign = inAngle > 0.0f ? 1.0f : -1.0f; // Attempt to ensure angle is prtty clean QT3DSU32 clampedAngle = (QT3DSU32)(fabs(inAngle) + .5f); QT3DSU32 leftover = clampedAngle % 10; clampedAngle -= leftover; if (leftover <= 2) leftover = 0; else if (leftover <= 7) leftover = 5; else leftover = 10; clampedAngle += leftover; QT3DSF32 retval = (QT3DSF32)clampedAngle; retval = (retval * sign) / 10.0f; TORAD(retval); return retval; } static inline QT3DSF32 ShortestAngleDifference(QT3DSF32 inCumulative, QT3DSF32 inNewTotal) { QT3DSF32 diff = qt3ds::render::SRotationHelper::ToMinimalAngle(inNewTotal - inCumulative); return inCumulative + diff; } Option STranslation::PrepareWidgetDrag(qt3ds::widgets::StudioWidgetComponentIds::Enum inComponentId, qt3ds::widgets::StudioWidgetTypes::Enum inWidgetId, qt3ds::render::RenderWidgetModes::Enum inWidgetMode, SNode &inNode, CPt inOriginalCoords, CPt inPreviousMouseCoords, CPt inMouseCoords) { SDragPreparationResult retval; retval.m_ComponentId = inComponentId; retval.m_WidgetType = inWidgetId; retval.m_WidgetMode = inWidgetMode; qt3ds::render::IQt3DSRenderer &theRenderer(m_Context.GetRenderer()); retval.m_Renderer = &theRenderer; retval.m_Node = &inNode; retval.m_Layer = GetLayerForNode(inNode); retval.m_Camera = theRenderer.GetCameraForNode(inNode); QSize theUnsignedDimensions(m_Context.GetWindowDimensions()); QT3DSVec2 theWindowDimensions((QT3DSF32)theUnsignedDimensions.width(), (QT3DSF32)theUnsignedDimensions.height()); if (retval.m_Camera == nullptr || retval.m_Layer == nullptr) return Empty(); SCamera &theCamera(*retval.m_Camera); SLayer &theLayer(*retval.m_Layer); QT3DSVec2 theLayerOriginalCoords = m_Context.GetRenderer().GetLayerMouseCoords( *retval.m_Layer, QT3DSVec2((QT3DSF32)inOriginalCoords.x, (QT3DSF32)inOriginalCoords.y), theWindowDimensions, true); QT3DSVec2 theLayerMouseCoords = m_Context.GetRenderer().GetLayerMouseCoords( *retval.m_Layer, QT3DSVec2((QT3DSF32)inMouseCoords.x, (QT3DSF32)inMouseCoords.y), theWindowDimensions, true); QT3DSVec2 thePreviousLayerMouseCoords = m_Context.GetRenderer().GetLayerMouseCoords( *retval.m_Layer, QT3DSVec2((QT3DSF32)inPreviousMouseCoords.x, (QT3DSF32)inPreviousMouseCoords.y), theWindowDimensions, true); QT3DSMat44 theGlobalTransform(QT3DSMat44::createIdentity()); if (inWidgetMode == qt3ds::render::RenderWidgetModes::Local) { theGlobalTransform = m_MouseDownNode.m_GlobalTransform; } retval.m_GlobalTransform = theGlobalTransform; QT3DSMat33 theNormalMat(theGlobalTransform.column0.getXYZ(), theGlobalTransform.column1.getXYZ(), theGlobalTransform.column2.getXYZ()); theNormalMat = theNormalMat.getInverse().getTranspose(); retval.m_NormalMatrix = theNormalMat; qt3ds::render::NVRenderRectF theLayerRect(theRenderer.GetLayerRect(theLayer)); SRay theOriginalRay = theCamera.Unproject(theLayerOriginalCoords, theLayerRect, theWindowDimensions); SRay theCurrentRay = theCamera.Unproject(theLayerMouseCoords, theLayerRect, theWindowDimensions); SRay thePreviousRay = theCamera.Unproject(thePreviousLayerMouseCoords, theLayerRect, theWindowDimensions); retval.m_OriginalRay = theOriginalRay; retval.m_CurrentRay = theCurrentRay; retval.m_PreviousRay = thePreviousRay; QT3DSVec3 theAxis; QT3DSVec3 thePlaneNormal; bool isPlane = false; QT3DSVec3 globalPos = inNode.GetGlobalPivot(); QT3DSVec3 camGlobalPos = theCamera.GetGlobalPos(); QT3DSVec3 theCamDirection; retval.m_GlobalPos = globalPos; retval.m_CameraGlobalPos = camGlobalPos; if (theCamera.m_Flags.IsOrthographic()) theCamDirection = theCamera.GetDirection(); else { theCamDirection = globalPos - camGlobalPos; // The normal will be normalized below. } theCamDirection.normalize(); retval.m_CameraDirection = theCamDirection; retval.m_AxisIndex = 0; switch (inComponentId) { default: QT3DS_ASSERT(false); break; case qt3ds::widgets::StudioWidgetComponentIds::XAxis: theAxis = QT3DSVec3(1, 0, 0); break; case qt3ds::widgets::StudioWidgetComponentIds::YAxis: theAxis = QT3DSVec3(0, 1, 0); retval.m_AxisIndex = 1; break; case qt3ds::widgets::StudioWidgetComponentIds::ZAxis: theAxis = QT3DSVec3(0, 0, -1); retval.m_AxisIndex = 2; break; case qt3ds::widgets::StudioWidgetComponentIds::XPlane: thePlaneNormal = QT3DSVec3(1, 0, 0); isPlane = true; break; case qt3ds::widgets::StudioWidgetComponentIds::YPlane: thePlaneNormal = QT3DSVec3(0, 1, 0); isPlane = true; break; case qt3ds::widgets::StudioWidgetComponentIds::ZPlane: thePlaneNormal = QT3DSVec3(0, 0, -1); isPlane = true; break; case qt3ds::widgets::StudioWidgetComponentIds::CameraPlane: { isPlane = true; thePlaneNormal = theCamDirection; } break; } retval.m_IsPlane = isPlane; if (inWidgetId == qt3ds::widgets::StudioWidgetTypes::Rotation) { if (isPlane == false) { theAxis = theNormalMat.transform(theAxis); theAxis.normalize(); thePlaneNormal = theAxis; retval.m_Plane = qt3ds::NVPlane(thePlaneNormal, -1.0f * thePlaneNormal.dot(globalPos)); } else { if (inComponentId != qt3ds::widgets::StudioWidgetComponentIds::CameraPlane) { thePlaneNormal = theNormalMat.transform(thePlaneNormal); } thePlaneNormal.normalize(); retval.m_Plane = qt3ds::NVPlane(thePlaneNormal, -1.0f * (thePlaneNormal.dot(globalPos))); } } else { if (isPlane == false) { theAxis = theNormalMat.transform(theAxis); theAxis.normalize(); QT3DSVec3 theCameraToObj = globalPos - camGlobalPos; QT3DSVec3 theTemp = theAxis.cross(theOriginalRay.m_Direction); // When the axis is parallel to the camera, we can't drag meaningfully if (theTemp.magnitudeSquared() < .05f) { // Attempt to find a better axis by moving the object back towards the camera. QT3DSF32 theSign = theCameraToObj.dot(theCamDirection) > 0.0 ? -1.0f : 1.0f; QT3DSF32 theDistance = theCameraToObj.dot(theCamDirection); QT3DSVec3 thePoint = globalPos + (theDistance * theSign) * theAxis; // Check if we actually moved to right direction QT3DSVec3 theNewCameraToObj = thePoint - camGlobalPos; QT3DSF32 theNewDistance = theNewCameraToObj.dot(theCamDirection); if (theNewDistance > theDistance) thePoint = globalPos - (theDistance * theSign) * theAxis; QT3DSVec3 theNewDir = thePoint - camGlobalPos; theNewDir.normalize(); theTemp = theAxis.cross(theNewDir); // Attempt again to find a better cross if (theTemp.magnitudeSquared() < .05f) return Empty(); } thePlaneNormal = theTemp.cross(theAxis); thePlaneNormal.normalize(); retval.m_Plane = qt3ds::NVPlane(thePlaneNormal, -1.0f * thePlaneNormal.dot(globalPos)); } else { thePlaneNormal = theNormalMat.transform(thePlaneNormal); thePlaneNormal.normalize(); retval.m_Plane = qt3ds::NVPlane(thePlaneNormal, -1.0f * (thePlaneNormal.dot(globalPos))); } } retval.m_Axis = theAxis; retval.m_OriginalPlaneCoords = theOriginalRay.Intersect(retval.m_Plane); retval.m_CurrentPlaneCoords = theCurrentRay.Intersect(retval.m_Plane); retval.m_PreviousPlaneCoords = thePreviousRay.Intersect(retval.m_Plane); return retval; } void STranslation::PerformWidgetDrag(int inWidgetSubComponent, CPt inOriginalCoords, CPt inPreviousMouseCoords, CPt inMouseCoords, CUpdateableDocumentEditor &inEditor) { if (inWidgetSubComponent == 0 || m_LastRenderedWidget == nullptr) { QT3DS_ASSERT(false); return; } Option thePrepResult(PrepareWidgetDrag( static_cast(inWidgetSubComponent), m_LastRenderedWidget->GetWidgetType(), m_LastRenderedWidget->GetRenderWidgetMode(), m_LastRenderedWidget->GetNode(), inOriginalCoords, inPreviousMouseCoords, inMouseCoords)); if (!thePrepResult.hasValue()) return; Option theOriginalPlaneCoords(thePrepResult->m_OriginalPlaneCoords); Option theCurrentPlaneCoords(thePrepResult->m_CurrentPlaneCoords); QT3DSVec3 globalPos(thePrepResult->m_GlobalPos); bool isPlane(thePrepResult->m_IsPlane); QT3DSVec3 theAxis(thePrepResult->m_Axis); QT3DSU32 axisIndex(thePrepResult->m_AxisIndex); SNode *theNode(thePrepResult->m_Node); SRay theCurrentRay(thePrepResult->m_CurrentRay); SRay theOriginalRay(thePrepResult->m_OriginalRay); QT3DSVec3 thePlaneNormal(thePrepResult->m_Plane.n); QT3DSVec3 theCamDirection(thePrepResult->m_CameraDirection); QT3DSVec3 camGlobalPos(thePrepResult->m_CameraGlobalPos); switch (m_LastRenderedWidget->GetWidgetType()) { default: QT3DS_ASSERT(false); return; case qt3ds::widgets::StudioWidgetTypes::Scale: { if (theOriginalPlaneCoords.hasValue() && theCurrentPlaneCoords.hasValue()) { QT3DSVec3 objToOriginal = globalPos - *theOriginalPlaneCoords; QT3DSVec3 objToCurrent = globalPos - *theCurrentPlaneCoords; QT3DSVec3 theScaleMultiplier(1, 1, 1); if (!isPlane) { // Ensure that we only have a scale vector in the direction of the axis. objToOriginal = theAxis * (theAxis.dot(objToOriginal)); objToCurrent = theAxis * (theAxis.dot(objToCurrent)); QT3DSF32 objToOriginalDot = theAxis.dot(objToOriginal); if (fabs(objToOriginalDot) > .001f) theScaleMultiplier[axisIndex] = theAxis.dot(objToCurrent) / theAxis.dot(objToOriginal); else theScaleMultiplier[axisIndex] = 0.0f; } QT3DSMat33 theNodeAxisMatrix(theNode->m_GlobalTransform.column0.getXYZ(), theNode->m_GlobalTransform.column1.getXYZ(), theNode->m_GlobalTransform.column2.getXYZ()); theNodeAxisMatrix = theNodeAxisMatrix.getInverse().getTranspose(); QT3DSVec3 &theLocalXAxis(theNodeAxisMatrix.column0); QT3DSVec3 &theLocalYAxis(theNodeAxisMatrix.column1); QT3DSVec3 &theLocalZAxis(theNodeAxisMatrix.column2); theLocalXAxis.normalize(); theLocalYAxis.normalize(); theLocalZAxis.normalize(); Option theXScale = GetScaleAlongAxis(theLocalXAxis, objToOriginal, objToCurrent); Option theYScale = GetScaleAlongAxis(theLocalYAxis, objToOriginal, objToCurrent); Option theZScale = GetScaleAlongAxis(theLocalZAxis, objToOriginal, objToCurrent); QT3DSVec3 theScale = m_MouseDownNode.m_Scale; if (theXScale.isEmpty() && theYScale.isEmpty() && theZScale.isEmpty()) { theScale = QT3DSVec3(0, 0, 0); } else { if (theXScale.hasValue()) theScale.x *= *theXScale; if (theYScale.hasValue()) theScale.y *= *theYScale; if (theZScale.hasValue()) theScale.z *= *theZScale; } m_LastRenderedWidget->SetAxisScale(theScaleMultiplier); SetScale(theScale, inEditor); } } break; case qt3ds::widgets::StudioWidgetTypes::Rotation: { QT3DSF32 theIntersectionCosine = theOriginalRay.m_Direction.dot(thePlaneNormal); QT3DSVec3 objToPrevious; QT3DSVec3 objToCurrent; if (fabs(theIntersectionCosine) > .08f) { if (!theOriginalPlaneCoords.hasValue() || !theCurrentPlaneCoords.hasValue()) return; objToPrevious = globalPos - *theOriginalPlaneCoords; objToCurrent = globalPos - *theCurrentPlaneCoords; objToPrevious.normalize(); QT3DSF32 lineLen = objToCurrent.normalize(); if (!thePrepResult->m_Camera->m_Flags.IsOrthographic()) { // Flip object vector if coords are behind camera to get the correct angle QT3DSVec3 camToCurrent = camGlobalPos - *theCurrentPlaneCoords; if (camToCurrent.dot(theCamDirection) >= 0.0f) { objToCurrent = -objToCurrent; // Negative line length seems counterintuitive, but since the end point is // behind the camera, it results in correct line when rendered lineLen = -lineLen; } } QT3DSF32 cosAngle = objToPrevious.dot(objToCurrent); QT3DSVec3 theCrossProd = objToPrevious.cross(objToCurrent); QT3DSF32 theCrossPlaneDot = theCrossProd.dot(thePlaneNormal); QT3DSF32 angleSign = theCrossPlaneDot >= 0.0f ? 1.0f : -1.0f; QT3DSF32 angleRad = acos(cosAngle) * angleSign; angleRad = MakeNiceRotation(angleRad); QT3DSQuat theRotation(angleRad, thePlaneNormal); m_CumulativeRotation = ShortestAngleDifference(m_CumulativeRotation, angleRad); m_LastRenderedWidget->SetRotationEdges(-1.0f * objToPrevious, thePlaneNormal, m_CumulativeRotation, lineLen); ApplyRotationToSelectedInstance(theRotation, *theNode, inEditor, false); } // In this case we are viewing the plane of rotation pretty much dead on, so we need to // assume the camera and the object are both in the plane of rotation. In this case we // *sort* of need to do trackball rotation but force it to one plane of rotation. else { // Setup a plane 600 units away from the camera and have the gadget run from there. // This keeps rotation consistent. QT3DSVec3 thePlaneSpot = theCamDirection * -600.0f + camGlobalPos; qt3ds::NVPlane theCameraPlaneAtObject(theCamDirection, -1.0f * (theCamDirection.dot(thePlaneSpot))); theCurrentPlaneCoords = theCurrentRay.Intersect(theCameraPlaneAtObject); theOriginalPlaneCoords = theOriginalRay.Intersect(theCameraPlaneAtObject); QT3DSVec3 theChangeVector = *theOriginalPlaneCoords - *theCurrentPlaneCoords; // Remove any component of the change vector that doesn't lie in the plane. theChangeVector = theChangeVector - theChangeVector.dot(thePlaneNormal) * thePlaneNormal; QT3DSF32 theDistance = theChangeVector.normalize(); // We want about 90* per 200 units in imaginary 600-units-from-camera space. QT3DSF32 theScaleFactor = 1.0f / 200.0f; if (thePrepResult->m_Camera->m_Flags.IsOrthographic()) theScaleFactor = 1.0f / 100.0f; QT3DSF32 theDeg = 90.0f * theDistance * theScaleFactor; // Check the sign of the angle. QT3DSVec3 theCurrentIsectDir = camGlobalPos - *theCurrentPlaneCoords; QT3DSVec3 thePreviousIsectDir = camGlobalPos - *theOriginalPlaneCoords; QT3DSVec3 theCrossProd = theCurrentIsectDir.cross(thePreviousIsectDir); QT3DSF32 theAngleSign = theCrossProd.dot(thePlaneNormal) > 0.0f ? 1.0f : -1.0f; theDeg *= theAngleSign; QT3DSF32 theRad(theDeg); TORAD(theRad); theRad = MakeNiceRotation(theRad); QT3DSQuat theRotation(theRad, thePlaneNormal); ApplyRotationToSelectedInstance(theRotation, *theNode, inEditor, false); } } break; case qt3ds::widgets::StudioWidgetTypes::Translation: { if (theOriginalPlaneCoords.hasValue() && theCurrentPlaneCoords.hasValue()) { QT3DSVec3 theDiff = *theCurrentPlaneCoords - *theOriginalPlaneCoords; if (isPlane) { ApplyPositionalChange(theDiff, *theNode, inEditor); } else { QT3DSVec3 theMovement = theAxis * theAxis.dot(theDiff); ApplyPositionalChange(theMovement, *theNode, inEditor); } } } break; } } static float RoundToNearest(float inValue, float inMin, float inMax, float inRound) { float half = (inMin + inMax) / 2.0f; inValue -= half; inValue = inRound * floor(inValue / inRound + .5f); inValue += half; inValue -= inMin; return inValue; } void STranslation::PerformGuideDrag(Qt3DSDMGuideHandle inGuide, CPt inPoint, CUpdateableDocumentEditor &inEditor) { qt3dsdm::SGuideInfo theInfo = m_Doc.GetDocumentReader().GetGuideInfo(inGuide); CPt renderSpacePt(inPoint.x, (long)GetViewportDimensions().y - inPoint.y); switch (theInfo.m_Direction) { case qt3dsdm::GuideDirections::Horizontal: theInfo.m_Position = RoundToNearest((float)renderSpacePt.y, (float)m_InnerRect.m_Bottom, (float)m_InnerRect.m_Top, 10.0f); break; case qt3dsdm::GuideDirections::Vertical: theInfo.m_Position = RoundToNearest((float)renderSpacePt.x, (float)m_InnerRect.m_Left, (float)m_InnerRect.m_Right, 10.0f); break; default: QT3DS_ASSERT(FALSE); break; break; } inEditor.EnsureEditor(QObject::tr("Drag Guide"), __FILE__, __LINE__).UpdateGuide(inGuide, theInfo); inEditor.FireImmediateRefresh(qt3dsdm::Qt3DSDMInstanceHandle()); } void STranslation::CheckGuideInPresentationRect(Qt3DSDMGuideHandle inGuide, CUpdateableDocumentEditor &inEditor) { qt3dsdm::SGuideInfo theInfo = m_Doc.GetDocumentReader().GetGuideInfo(inGuide); bool inPresentation = false; QT3DSF32 presHeight = (QT3DSF32)m_InnerRect.m_Top - (QT3DSF32)m_InnerRect.m_Bottom; QT3DSF32 presWidth = (QT3DSF32)m_InnerRect.m_Right - (QT3DSF32)m_InnerRect.m_Left; switch (theInfo.m_Direction) { case qt3dsdm::GuideDirections::Horizontal: inPresentation = 0.0f <= theInfo.m_Position && presHeight >= theInfo.m_Position; break; case qt3dsdm::GuideDirections::Vertical: inPresentation = 0.0f <= theInfo.m_Position && presWidth >= theInfo.m_Position; break; default: break; } if (!inPresentation) inEditor.EnsureEditor(QObject::tr("Delete Guide"), __FILE__, __LINE__).DeleteGuide(inGuide); } void STranslation::PerformPathDrag(qt3ds::studio::SPathPick &inPathPick, CPt inOriginalCoords, CPt inPreviousMouseCoords, CPt inMouseCoords, CUpdateableDocumentEditor &inEditor) { Option thePrepResult(PrepareWidgetDrag( qt3ds::widgets::StudioWidgetComponentIds::ZPlane, qt3ds::widgets::StudioWidgetTypes::Translation, qt3ds::render::RenderWidgetModes::Local, m_PathWidget->GetNode(), inOriginalCoords, inPreviousMouseCoords, inMouseCoords)); if (!thePrepResult.hasValue()) return; Option theOriginalPlaneCoords(thePrepResult->m_OriginalPlaneCoords); Option theCurrentPlaneCoords(thePrepResult->m_CurrentPlaneCoords); Option thePreviousPlaneCoords(thePrepResult->m_PreviousPlaneCoords); if (theOriginalPlaneCoords.hasValue() && theCurrentPlaneCoords.hasValue()) { QT3DSVec3 theGlobalDiff = *theCurrentPlaneCoords - *theOriginalPlaneCoords; QT3DSMat44 theGlobalInverse = thePrepResult->m_GlobalTransform.getInverse(); QT3DSVec3 theCurrentPos = theGlobalInverse.transform(*theCurrentPlaneCoords); QT3DSVec3 theOldPos = theGlobalInverse.transform(*theOriginalPlaneCoords); QT3DSVec3 theDiff = theCurrentPos - theOldPos; // Now find the anchor point; nontrivial. SPathTranslator *theTranslator = reinterpret_cast(thePrepResult->m_Node->m_UserData.m_UserData); qt3dsdm::Qt3DSDMInstanceHandle thePathHandle = theTranslator->GetInstanceHandle(); qt3dsdm::Qt3DSDMInstanceHandle theAnchorHandle = GetAnchorPoint(inPathPick); if (theAnchorHandle.Valid()) { qt3dsdm::Qt3DSDMPropertyHandle thePosProperty = m_ObjectDefinitions.m_PathAnchorPoint.m_Position.m_Property; qt3dsdm::Qt3DSDMPropertyHandle theAngleProperty = m_ObjectDefinitions.m_PathAnchorPoint.m_IncomingAngle.m_Property; qt3dsdm::Qt3DSDMPropertyHandle theIncomingDistanceProperty = m_ObjectDefinitions.m_PathAnchorPoint.m_IncomingDistance.m_Property; qt3dsdm::Qt3DSDMPropertyHandle theOutgoingDistanceProperty = m_ObjectDefinitions.m_PathAnchorPoint.m_OutgoingDistance.m_Property; IDocumentReader &theReader(m_Doc.GetDocumentReader()); SFloat2 anchorPos = *theReader.GetTypedInstancePropertyValue(theAnchorHandle, thePosProperty); QT3DSVec2 anchorPosVec = QT3DSVec2(anchorPos[0], anchorPos[1]); if (m_LastPathDragValue.hasValue() == false) { SPathAnchorDragInitialValue initialValue; initialValue.m_Position = anchorPosVec; initialValue.m_IncomingAngle = theReader.GetTypedInstancePropertyValue( theAnchorHandle, theAngleProperty); initialValue.m_IncomingDistance = theReader.GetTypedInstancePropertyValue( theAnchorHandle, theIncomingDistanceProperty); initialValue.m_OutgoingDistance = theReader.GetTypedInstancePropertyValue( theAnchorHandle, theOutgoingDistanceProperty); m_LastPathDragValue = initialValue; } SPathAnchorDragInitialValue &lastValue(*m_LastPathDragValue); QT3DSVec2 theCurrentValue; switch (inPathPick.m_Property) { case SPathPick::Anchor: theCurrentValue = lastValue.m_Position; break; case SPathPick::IncomingControl: theCurrentValue = qt3ds::render::IPathManagerCore::GetControlPointFromAngleDistance( lastValue.m_Position, lastValue.m_IncomingAngle, lastValue.m_IncomingDistance); break; case SPathPick::OutgoingControl: theCurrentValue = qt3ds::render::IPathManagerCore::GetControlPointFromAngleDistance( lastValue.m_Position, lastValue.m_IncomingAngle + 180.0f, lastValue.m_OutgoingDistance); break; default: QT3DS_ASSERT(false); break; } theCurrentValue[0] += theDiff.x; theCurrentValue[1] += theDiff.y; Q3DStudio::IDocumentEditor &theEditor = inEditor.EnsureEditor( QObject::tr("Anchor Point Drag"), __FILE__, __LINE__); switch (inPathPick.m_Property) { case SPathPick::Anchor: theEditor.SetInstancePropertyValue(theAnchorHandle, thePosProperty, SFloat2(theCurrentValue.x, theCurrentValue.y)); break; case SPathPick::IncomingControl: { QT3DSVec2 angleDistance = qt3ds::render::IPathManagerCore::GetAngleDistanceFromControlPoint( anchorPosVec, theCurrentValue); float angleDiff = angleDistance.x - lastValue.m_IncomingAngle; float minimalDiff = qRadiansToDegrees(qt3ds::render::SRotationHelper::ToMinimalAngle( qDegreesToRadians(angleDiff))); float newAngle = lastValue.m_IncomingAngle + minimalDiff; theEditor.SetInstancePropertyValue(theAnchorHandle, theAngleProperty, newAngle); theEditor.SetInstancePropertyValue(theAnchorHandle, theIncomingDistanceProperty, angleDistance.y); } break; case SPathPick::OutgoingControl: { QT3DSVec2 angleDistance = qt3ds::render::IPathManagerCore::GetAngleDistanceFromControlPoint( anchorPosVec, theCurrentValue); angleDistance.x += 180.0f; float angleDiff = angleDistance.x - lastValue.m_IncomingAngle; float minimalDiff = qRadiansToDegrees(qt3ds::render::SRotationHelper::ToMinimalAngle( qDegreesToRadians(angleDiff))); float newAngle = lastValue.m_IncomingAngle + minimalDiff; theEditor.SetInstancePropertyValue(theAnchorHandle, theAngleProperty, newAngle); theEditor.SetInstancePropertyValue(theAnchorHandle, theOutgoingDistanceProperty, angleDistance.y); } break; } inEditor.FireImmediateRefresh(m_AssetGraph.GetParent(theAnchorHandle)); } } } SNode *STranslation::GetEditCameraLayer() { if (m_EditCameraLayerTranslator) return static_cast(&m_EditCameraLayerTranslator->GetGraphObject()); return nullptr; } PickTargetAreas::Enum STranslation::GetPickArea(CPt inPoint) { qt3ds::render::NVRenderRectF displayViewport = m_Context.GetDisplayViewport(); QT3DSVec2 thePickPoint((QT3DSF32)inPoint.x, m_Context.GetWindowDimensions().height() - (QT3DSF32)inPoint.y); QT3DSF32 left = displayViewport.m_X; QT3DSF32 right = displayViewport.m_X + displayViewport.m_Width; QT3DSF32 top = displayViewport.m_Y + displayViewport.m_Height; QT3DSF32 bottom = displayViewport.m_Y; if (thePickPoint.x < left || thePickPoint.x > right || thePickPoint.y < bottom || thePickPoint.y > top) return PickTargetAreas::Matte; return PickTargetAreas::Presentation; }