SDK Problem with UI not refreshing.
Sighman
Posts: 56
I don't know if anyone has run into this issue or not...
I have a shader that has a button. The idea is that if you press the button then a bunch of presets get applied to the shader. One of the presets is a sub-shader. I create a new sub-shader and assign it to the PMap for the main shader. The problem is that the UI does not update with the new sub-shader assignment. If I leave the Material Room and come back then sure enough the new sub-shader is shown. Or if I press the 'Edit' button for the sub-shader then it shows the new shader.
Is there any way to tell the Shader Window to refresh itself?
Comments
Actually the problem is more insidious than originally presented. You cannot change a component this way (by just updating the PMap) because Carrara will crash if a shader is already assigned. It seems to maintain a cache of shaders that it will swap in and out as you make changes via the pick list. Somehow I need to tell Carrara to pick a new shader on my behalf. This seems to only be the case when the component you want to swap out is currently being displayed in the Material Editor.
Does anyone have any ideas how to solve this? Do I need to navigate the 'IMCPart' tree to find the shader picker?
Sighman,
Here you might be better off trying to do your changes through a scene command. That part of the SDK is geared towards making changes to the model and then the view picking up the changes.
I do a similar thing with the Set Cel Lighting scene command in Toon! Pro. It takes the selected object, looks at the master shaders for each of the shading domains, and then rearranges the shading tree as needed to make Cel the lighting model. This can include grabbing the original topmost shader and making it a child of a new Cel lighting model, and then setting the master shader again. At then end of that I do a PostChange on the ChangeManager, and everything is reflected appropriately in the UI.
You might be able to do it in your plug-in by calling the change manager there, but not sure if that's safe inside a UI handler.
Set Cel's code is below.
Regards,
#if CP_PRAGMA_ONCE
#pragma once
#endif
#include "Basic3DCOMImplementations.h"
#include "SetCellLightingModelDef.h"
#include "BitField.h"
#include "I3DShFacetMesh.h"
#include "I3DShScene.h"
#include "I3DExModifier.h"
#include "ISceneSelection.h"
#include "IChangeManagement.h"
#include "PublicUtilities.h"
#include "ISceneDocument.h"
#include "copyright.h"
#include "cell.h"
// define the SceneOp CLSID
struct SetCellLightingModelPublicData{
TVector2 vec2ShadowHighlight;
int32 lLevels;
real32 fShadowBrightness;
};
extern const MCGUID CLSID_SetCellLightingModel;
#if (VERSIONNUMBER >= 0x030000)
class SetCellLightingModelMenuPreparer : public TBasicMenuCallBack
{
public:
virtual boolean MCCOMAPI SelfPrepareMenu(ISceneDocument* sceneDocument);
};
#endif
// SceneOp Object :
class SetCellLightingModel : public TBasicSceneCommand
{
public :
SetCellLightingModel();
STANDARD_RELEASE;
virtual int32 MCCOMAPI GetParamsBufferSize ()
const { return 0; }
// IExDataExchanger methods :
virtual int16 MCCOMAPI GetResID ();
virtual MCCOMErr MCCOMAPI HandleEvent(MessageID message, IMFResponder* source, void* data);
virtual void* MCCOMAPI GetExtensionDataBuffer();
#if (VERSIONNUMBER >= 0x030000)
virtual void MCCOMAPI GetMenuCallBack(ISelfPrepareMenuCallBack** callBack);
#else
virtual boolean MCCOMAPI SelfPrepareMenus (ISceneDocument* sceneDocument);
#endif
virtual MCCOMErr MCCOMAPI Init (ISceneDocument* sceneDocument);
virtual MCCOMErr MCCOMAPI Prepare ();
virtual boolean MCCOMAPI CanUndo ();
virtual boolean MCCOMAPI Do ();
virtual boolean MCCOMAPI Undo ();
virtual boolean MCCOMAPI Redo ();
private :
SetCellLightingModelPublicData fData;
TMCCountedPtr fSceneDocument;
TMCCountedPtr fTree;
TMCCountedPtr fScene;
TMCCountedPtr fSelection;
TMCCountedPtr fCloneSelection;
TMCCountedPtr fSelectionChannel;
TMCCountedPtr fTreePropertyChannel;
void ChangeLightingModel(I3DShMasterShader* masterShader);
};
#include "SetCellLightingModel.h"
#include "math.h"
#if VERSIONNUMBER >= 0x050000
#include "COMSafeUtilities.h"
#endif
#include "I3DShGroup.h"
#include "I3DShModifier.h"
#include "COMUtilities.h"
#include "IShUtilities.h"
#include "I3DShUtilities.h"
#include "com3dutilities.h"
#include "IComponentAnim.h"
#include "I3DShComponentOwner.h"
#include "IShComponent.h"
#include "I3DShShader.h"
#include "I3DShObject.h"
#include "I3DShInstance.h"
#include "MFPartMessages.h"
#include "IMFPart.h"
#if (VERSIONNUMBER >= 0x030000)
#include "I3DShMasterGroup.h"
#endif
#if VERSIONNUMBER >= 0x050000
const MCGUID CLSID_SetCellLightingModel(R_CLSID_SetCellLightingModel);
#else
const MCGUID CLSID_SetCellLightingModel={R_CLSID_SetCellLightingModel};
#endif
extern IChangeManager* gChangeManager;
SetCellLightingModelPublicData LastChanges;
boolean bLastChangesInitialized = false;
void SetDefaults(SetCellLightingModelPublicData &fData;)
{
fData.vec2ShadowHighlight.x = .50f;
fData.vec2ShadowHighlight.y = .75f;
fData.fShadowBrightness = .30f;
fData.lLevels = 1;
}
SetCellLightingModel::SetCellLightingModel()
{
// Data initialisation
SetDefaults(fData);
// fData variables
if (!bLastChangesInitialized) {
LastChanges = fData;
bLastChangesInitialized = true;
}
// Protected variables
fSceneDocument = NULL;
fTree = NULL;
fScene = NULL;
fSelection = NULL;
}
#if (VERSIONNUMBER >= 0x030000)
void SetCellLightingModel::GetMenuCallBack(ISelfPrepareMenuCallBack** callBack)
{
TMCCountedCreateHelper result(callBack);
result = new SetCellLightingModelMenuPreparer;
}
#endif
// TBasicSceneCommand methods :
#if (VERSIONNUMBER >= 0x030000)
boolean SetCellLightingModelMenuPreparer::SelfPrepareMenu(ISceneDocument* sceneDocument)
#else
boolean SetCellLightingModel::SelfPrepareMenus(ISceneDocument* sceneDocument)
#endif
{
TMCCountedPtr selection;
//if (sceneDocument)
{
sceneDocument->GetSceneSelection(&selection;);
TTreeSelectionIterator iter(selection);
TMCCountedPtr instance;
TMCCountedPtr tree;
if (!iter.IsElemCountEqualTo(1)){
return false;
}
tree=iter.First();
if (tree->QueryInterface(IID_I3DShInstance, (void**)&instance;)== MC_S_OK)
{
if (instance->GetInstanceKind() == I3DShInstance::kPrimitiveInstance)
{
return true;
}
}
}
return false;
}
MCCOMErr SetCellLightingModel::Init(ISceneDocument* sceneDocument)
{
fSceneDocument = sceneDocument;
sceneDocument -> GetSceneSelection(&fSelection;);
fSelection -> Clone(&fCloneSelection;);
return MC_S_OK;
}
MCCOMErr SetCellLightingModel::Prepare()
{
if (fSceneDocument)
{
fSceneDocument->GetScene(&fScene;);
fScene->GetTreePropertyChangeChannel(&fTreePropertyChannel;);
ThrowIfNil(fTreePropertyChannel);
fSceneDocument->GetSceneSelectionChannel(&fSelectionChannel;);
ThrowIfNil(fSelectionChannel);
}
if (!fTree)
{
TMCCountedPtr group;
fScene->GetTreeRoot(&group;);
if (group->QueryInterface(IID_I3DShTreeElement, (void **) &fTree;) != MC_S_OK)
return MC_S_OK;
}
return MC_S_OK;
}
void SetCellLightingModel::ChangeLightingModel(I3DShMasterShader* masterShader)
{
TMCCountedPtr shader;
TMCCountedPtr cellShader;
TMCCountedPtr cell;
TMCCountedPtr component;
masterShader->GetShader(&shader;);
ThrowIfNil(shader);
shader->QueryInterface(IID_Cel, (void**) &cell;);
//if there is already a cell lighting model, just update it's values
if (cell)
{
((CelPublicData*)(cell.fObject->GetExtensionDataBuffer()))->fShadowBrightness = fData.fShadowBrightness;
((CelPublicData*)(cell.fObject->GetExtensionDataBuffer()))->lLevels = fData.lLevels;
((CelPublicData*)(cell.fObject->GetExtensionDataBuffer()))->vec2ShadowHighlight = fData.vec2ShadowHighlight;
}
//otherwise set a new cell as the top most shader
else
{
gComponentUtilities->CreateComponent(kRID_ShaderFamilyID, 'SPCE', &component;);
ThrowIfNil(component);
component->QueryInterface(IID_I3DShShader, (void**) &cellShader;);
ThrowIfNil(cellShader);
cellShader->QueryInterface(IID_Cel, (void**) &cell;);
ThrowIfNil(cell);
((CelPublicData*)(cell.fObject->GetExtensionDataBuffer()))->fShadowBrightness = fData.fShadowBrightness;
((CelPublicData*)(cell.fObject->GetExtensionDataBuffer()))->lLevels = fData.lLevels;
((CelPublicData*)(cell.fObject->GetExtensionDataBuffer()))->vec2ShadowHighlight = fData.vec2ShadowHighlight;
TMCCountedPtr parameterComponent;
shader->QueryInterface(IID_IShParameterComponent, (void**)¶meterComponent;);
((CelPublicData*)(cell.fObject->GetExtensionDataBuffer()))->param = parameterComponent;
cell.fObject->ExtensionDataChanged();
masterShader->SetShader(cellShader, kWithAnim);
}
}
boolean SetCellLightingModel::Do()
{
LastChanges = fData;
//do stuff
TMCCountedPtr tree;
TMCCountedPtr instance;
TMCCountedPtr masterShader;
TTreeSelectionIterator iter(fSelection);
tree = iter.First();
ThrowIfNil(tree);
tree->QueryInterface(IID_I3DShInstance, (void**)&instance;);
ThrowIfNil(instance);
instance->GetShader(&masterShader;);
ThrowIfNil(masterShader);
ChangeLightingModel(masterShader);
uint32 uvSpaceCount = instance->GetUVSpaceCount();
for (uint32 uvSpaceIndex = 0; uvSpaceIndex < uvSpaceCount; uvSpaceIndex++)
{
instance->GetUVSpaceShader(uvSpaceIndex, &masterShader;);
if (masterShader)
{
ChangeLightingModel(masterShader);
}
}
gChangeManager->PostChange(fSelectionChannel, 0, fSelection);
fSceneDocument -> GetSceneSelection(&fSelection;);
fSelection->ClearSelection();
gChangeManager->PostChange(fSelectionChannel, 0, fSelection);
return true;
}
boolean SetCellLightingModel::CanUndo()
{
return false;
}
boolean SetCellLightingModel::Undo()
{
return false;
}
boolean SetCellLightingModel::Redo()
{
return false;
}
int16 SetCellLightingModel::GetResID()
{
return 400;
}
void* SetCellLightingModel::GetExtensionDataBuffer()
{
return &fData;;
}
MCCOMErr SetCellLightingModel::HandleEvent(MessageID message, IMFResponder* source, void* data)
{
IDType sourceID;
TMCCountedPtr sourcePart;
source->QueryInterface(IID_IMFPart, (void**) &sourcePart;);
ThrowIfNil(sourcePart);
sourceID = sourcePart->GetIMFPartID();
if (sourceID == 'LRUN' && message == EMFPartMessage::kMsg_PartValueChanged)
{
fData = LastChanges;
}
else if(sourceID == 'DEFT' && message == EMFPartMessage::kMsg_PartValueChanged)
{
SetDefaults(fData);
}
return MC_S_OK;
}
Wow, thanks for the snippet, that is really going to help.
I am finding that there is a limited amount you can do from within a shader. You cannot navigate up from a sub-shader to its parent, and even though there is a GetMasterShader() method on I3DShShader it always returns NULL.
I already have one scene command so now I will add a few more...
Thanks again for your help.
Regarding menus...
It is possible to add your own 'main' menu to Carrara? I see all kinds of MNU resources but little info on how to add your own outside of a modeller (which I don't want to get into).
Yeah, there's not much navigation within a shader, but you can always take the incoming instance, and work your way back from there. There's a get master shader on the instance or maybe the tree element. Wiith a bit of work you could probably figure out each shader's pmap and navigate the tree to find yourself.
I don't recall running across any arbitrary menu adding functions.
Regards,
I have the scene command working, yay.
One complication I had to resolve was with Component Lists. If you want to change an element in a component list ('cmp#') then you need to post the list back to its owner, you cannot just change the list element. This is true all the way up the shader tree back to the master shader. Take a tree for example. The shader tree looks something like this:
Layer List
- Parametric Mapping
- Multi-Channel
- Parametric Mapping
- Multi-Channel
- Parametric Mapping
- Multi-Channel
- Parametric Mapping
- Multi-Channel
There are four mappings for a tree (leaf, twig, branch, and trunk). Also. the multi-channel shader for the Parametric Mapping is also held in a component list (containing one element).
Now if you want to replace the Multi-Channel shader then you need update the component list of the Parametric Mapping and the Layer List otherwise your change does not get applied.