Is there any way to get the class data from the animation blueprint asset obtained from the Content Manager? - c++

I have a custom C++ class named UHandsAnimInstance :
UCLASS(transient, Blueprintable, hideCategories = AnimInstance, BlueprintType)
class SENSORIALSDK_API UHandsAnimInstance : public UAnimInstance
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Fingers)
bool isRight = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Fingers)
FRotator wrist;
}
It has some custom properties inside and it is the parent class of an animation blueprint asset as you can see in this picture:
Blueprint Animation Editor
And it is located in this location: "/Game/SensorialXR/Blueprints/Gestures/RightHand"
Asset Location in Content Manager
My main problem is that I want to select that object from Content Manager so first I try to put that as a property in another class (AActor class):
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<UHandsAnimInstance*> gestureData;
But I cannot select any object in the editor selector:
Editor Selector Example
So I tried to load all assets from a FString parameter that is the path of all UHandsAnimInstance at runtime using this code in the BeginPlay function inside the custom AActor class:
FString gesturesPath = "/Game/SensorialXR/Blueprints/Gestures/RightHand";
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
TArray<FAssetData> AssetData;
AssetRegistryModule.Get().GetAssetsByPath(FName(*gesturesPath), AssetData);
for (int i = 0; i < AssetData.Num(); i++) {
UAnimBlueprint* animx = Cast<UAnimBlueprint>(AssetData[i].GetAsset());
if (animx) {
//Do Something (like add to the gestureData list)
}
}
The problem is that the class of the obtained asset is UAnimBlueprint (that is why I cast that asset to that type):
Main class of the obtained asset
But I cannot do anything neither get the UHandsAnimInstance object from that asset:
Data from the UAnimBlueprint obtained variable
So, what can I do to get the data I want of my custom C++ class from a blueprint object in the content manager?

The Assets within the Content Browser are not real "instances" of those Objects.
If you want to be able to select the Asset Type from the Content Browser and input it into a Property, you will be required to use a TSubclassOf<UHandsAnimInstance> type.
This will give you a UClass* to the Asset Type. Keep in mind this is not an instance. If you want to access default property data on that Type you can use its CDO (Class Default Object). The CDO can be accessed directly from the UClass*.
UHandsAnimInstance* MyHandsAnimCDO = Cast<UHandsAnimInstance>(MyClass->GetDefaultObject());
The above CDO will require casting to your particular type. Then you can access any Property Default values from there. Do not modify or call non-const functions on the CDO, it is not design to be mutable in the general sense as it is only the template Object from which real instances are generated with at runtime.
If you are looking for a specific instance of your UHandsAnimInstance you will need to find the associated Skeletal Mesh that it has been instantiated with.

Related

How to get an Ecore feature disply name without an object instance?

I'd like to create a GUI table to display a given list of features of an EObject sub-class. To do this I have to get the display names of the features for the column header.
How do I get the feature display names in the best way?
One solution that seems a bit like a hack:
If I have an instance of the class then I can use the adaptor factory to get a IItemPropertySource that can do this:
SomeEntity e = ...
String displayName = adaptorFactory.adapt(e, IItemPropertySource.class)
.getPropertyDescriptor(null, feature).getDisplayName(null));
But when the table is empty there is no SomeEntity object handy to use to get the IItemPropertySource.
I can create a dummy object using the EFactory in this way:
EClass containingClass = feature.getEContainingClass();
SomeEntity dummy = containingClass.getEPackage().getEFactoryInstance()
.create(containingClass));
... and then use that object the get the IItemPropertySource. But this seem a bit like a hack. Is there no better solution?
If you know the class at compile time, you can create the ItemProviderAdapter yourself:
MyClassItemProvider provider = new MyClassItemProvider(adaptorFactory);
String name = provider.getPropertyDescriptor(null, property).getDisplayName(null);
If you do not know the class at compile time, but only have an EClass instance at runtime, things are more complicated, because the necessary methods are protected. You have to "make" them public first.
I would add respective methods to the generated MyPackageSwitch and MyPackageAdapterFactory classes (in myPackage.util).
In MyPackageAdapterFactory:
/**
* #generated NOT
*/
public MyPackageSwitch<Adapter> getModelSwitch() {
return modelSwitch;
}
In MyPackageSwitch:
/**
* generated NOT
*/
public T doPublicSwitch(EClass theEClass, EObject theEObject) {
return doSwitch(theEClass, theEObject);
}
Now you can create an ItemProviderAdapter for an EClass theEClass like this:
provider = (ItemProviderAdapter) adapterFactory.getModelSwitch()
.doPublicSwitch(theEClass, null);
EMF was obviously not made for this. Keep in mind that this all is only working if you do not have any custom provider implementations that uses the EObject values.

Accessing Sitecore template field values

This may be documented somewhere, but I cannot find it.
I am using the Sitecore helper and razor syntax to place values into my view:
#Html.Sitecore().Field("foo");
This works fine, but, I have Fields defined in Groups, and a few of them have the same name, like:
Group1: foo
Group2: foo
Question: Is there any way to access the field by group?
Something like this (what I have already tried):
#Html.Sitecore().Field("Group1.foo");
As long as I know it is not possible to use the sections name.
I would avoid to have the same field name even between differents sections.
Or a option is to pass the field ID and then create classes or constants to not hard code IDs
#Html.Sitecore().Field("{E77E229A-2A34-4F03-9A3E-A8636076CBDB}");
or
#Html.Sitecore().Field(MyTemplate.MyGroup.Foo); //where foo returns the id.
EDIT:
MyTemplate.MyGroup.Foo would be classes/structs you created your own just to make easier and more reliable the references all over you project. (you can use a tool to auto-generate it like a T4 template)
public static struct MyTemplate
{
public static struct MyGroup1
{
public static readonly ID Foo = new ID("{1111111-1111-1231-1231-12312323132}");
}
public static struct MyGroup2
{
public static readonly ID Foo = new ID("{9999999-9999-xxxx-1231-12312323132}");
}
}
Try accessing via the GUID of the field. This way, even if they have the same name, the API will be able to load the appropriate field.

Sitecore - Image field in parameter template

If I have an image field in a parameter template, what are the steps involved in getting the URL of the image in c#?
#mdresser makes a valid point about what should and should not be a rendering parameter. However, I don't think that Sitecore intentionally made it difficult to use image fields in parameter templates. They simply built the parameter template functionality over the existing key-value pair rendering parameter functionality.
If the name of your image field on the rendering parameters template was BackgroundImage, you could use the following code to get the URL of the selected image:
var imageId = XmlUtil.GetAttribute("mediaid", XmlUtil.LoadXml(this.Parameters["BackgroundImage"]));
MediaItem imageItem = Sitecore.Context.Database.GetItem(imageId);
backgroundImageUrl = MediaManager.GetMediaUrl(imageItem);
If you don't already have a base class for your sublayouts that provides the Parameters property, you will need to also add this:
private NameValueCollection parameters;
public virtual NameValueCollection Parameters
{
get
{
if (this.parameters == null)
{
var parameters = this.Attributes["sc_parameters"];
this.parameters = string.IsNullOrEmpty(parameters)
? new NameValueCollection()
: WebUtil.ParseUrlParameters(parameters);
}
return this.parameters;
}
}
To achieve this you would have to look at how the sitecore image field renders the raw text value into an img tag. However, there is a reason that this doesn't work out of the box with sitecore; parameters templates are designed to define info about how a rendering or sublayout should render. E.g. You could use it to tell a list control to show a certain number of items etc. I'd advise against using rendering parameters for content as this will make content editing very cumbersome. If your aim is to have the content of a particular sublayout defined somewhere other than the page itself, put it into a sub item instead.
You can have a 2 separate functions to retrieve the value of image field in the parameter template that you gave to the sub layout .
First Step : Get the value associated with the parameter of image . Please use below function to retrieve the value .
/// <summary>
/// Returns a specific parameter value
/// Use this for Single-line, multiline text fields, linkfield, Image field etc.
/// </summary>
/// <param name="parameterName"></param>
/// <returns></returns>
public MediaItem GetValueFromRenderingParameter(string parameterName)
{
var item = !string.IsNullOrEmpty(_params[parameterName]) ? _params[parameterName] : string.Empty;
if(item == null)
{
return null ;
}
return Sitecore.Context.Database.GetItem(item);
}
Second step : Create another function where you can use the above mentioned function to retrieve the value of image field and use it appropriately. Here is the code snippet of the same :
Public string RenderImage()
{
Sitecore.Data.Fields.ImageField imageField =GetValueFromRenderingParameter("Image parameter name");
return MediaManager.GetMediaUrl(imageField.MediaItem) ;
}
Hope This Helps .

How to extend Glass.Mapper.Sc.Fields.Image with additional properties?

I am storing cropping information on my Sitecore Media Library images in a field that was added to the /sitecore/templates/System/Media/Unversioned/Image template.
I would like to access this field along with all of the other properties that exist in the Glass.Mapper.Sc.Fields.Image complex field type so that I can continue to use GlassHtml.RenderImage() in my views.
My initial attempts to inherit from the class were unsuccessful - it appears to break the mapping behavior - so I am wondering if there is another way to extend this class with additional properties?
Here's what I've tried:
[SitecoreType(AutoMap = true)]
public class MyImage : Glass.Mapper.Sc.Fields.Image
{
public virtual string CropInfo { get; set; }
}
You will need to implement a custom data handler to map the additional field.
I would create a data handler that inherits from the standard Image data handler:
https://github.com/mikeedwards83/Glass.Mapper/blob/master/Source/Glass.Mapper.Sc/DataMappers/SitecoreFieldImageMapper.cs
Then customise GetField and SetField.
Once you have created the custom data handler you need to register it with the Windsor container. See tutorial 19 for how to do this:
http://glass.lu/docs/tutorial/sitecore/tutorial19/tutorial19.html
The important part:
public static void CastleConfig(IWindsorContainer container){
var config = new Config();
container.Register(
Component.For < AbstractDataMapper>().ImplementedBy<TweetsDataHandler>().LifeStyle.Transient
);
container.Install(new SitecoreInstaller(config));
}

How to access variables in a sub-ccb file with a custom class as root node in cocos2d-x

Let's say I have two ccb files
FriendList.ccb
Friend.ccb
FriendList.ccb associate with a class named FriendList, it will read nodes from this ccb file with the code like below:
CCNodeLoaderLibrary * ccNodeLoaderLibrary = CCNodeLoaderLibrary::sharedCCNodeLoaderLibrary();
CCBReader reader = CCBReader(ccNodeLoaderLibrary);
CCLayer* layer = (CCLayer*)reader.readNodeGraphFromFile("FriendList.ccbi", this);
Friend.ccb associate with a custom class name Friend and a custom loader named FriendLoader. It can also contain its own variables, such as CCLabelTTF, CCSprit.
And in FriendList.ccb, it can contains many Friend.ccb as sub ccb file.
After these define, I now assume there are two Friend.ccb in FriendList.ccb, named m_friend1 and m_friend2, and in Friend.ccb there is a CCLabelTTF name m_friend_name.
I load the two instance of Friend with the following code in FriendList.cpp:
bool FriendList::onAssignCCBMemberVariable(CCObject *pTarget, CCString *pMemberVariableName, CCNode *pNode){
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "m_friend1", Friend*, m_friend1);
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "m_friend2", Friend*, m_friend2);
return false;
}
and load the instance of the CCLabelTTF with the following code in Friend.cpp:
bool Friend::onAssignCCBMemberVariable(CCObject *pTarget, CCString *pMemberVariableName, CCNode *pNode){
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "m_friend_name", CCLabelTTF*, m_friend_name);
return false;
}
With these work, I can access m_friend1 and m_friend2 successfully, but when accessing m_friend1->m_friend_name, I got EXC_BAD_ACCESS.
So how can I access the variables in sub ccb file?
When using a sub ccb file associate with a custom clase, we need add a custom loader first.
Let's name it FriendLoader, there is the code in FriendLoader.h:
#include "Friend.h"
/* Forward declaration. */
class CCBReader;
class FriendLoader : public cocos2d::extension::CCLayerLoader {
public:
CCB_STATIC_NEW_AUTORELEASE_OBJECT_METHOD(FriendLoader, loader);
protected:
CCB_VIRTUAL_NEW_AUTORELEASE_CREATECCNODE_METHOD(Friend);
};
And register it in AppDelegate::applicationDidFinishLaunching():
CCNodeLoaderLibrary * ccNodeLoaderLibrary = CCNodeLoaderLibrary::sharedCCNodeLoaderLibrary();
ccNodeLoaderLibrary->registerCCNodeLoader("Friend", FriendLoader::loader());
or some other place you like.
Then we can use the custom class(Friend) in ccb file. Open Friend.ccb file, select the root node, and fill in the 'Custom class' blank with Friend.
Then select the CCLabelTTF named m_friend_name, and select the 'Doc root var', this is very important ! ! !.
Here I want give a simple explain.
Since in FriendList class, it reads node from ccb file with reader.readNodeGraphFromFile("FriendList.ccbi", this);, and using itself as a 'owner', so these variables in it are 'owner var'. When reading nodes, the CCBReader will assign these 'owner var' to this 'owner' that is a FriendList instance directly.
While Friend is a custom class used in CocosBuilder, it is the root node in Friend.ccb, and the variable in it is 'Doc root var'. When reading nodes, the CCBReader will first read the instances of Friend, then assign these 'Doc root var' to these instances.
So what make me failed before is indeed these, I select 'owner var' of all of the variables in both FriendList.ccb and Friend.ccb.
Then CCBReader assign the variable m_friend_name in sub Friend.ccb to the 'owner'( an instance of Friend).
For more information, can see CocosBuilder: Connecting with cocos2d-x.