Object Hierarchy | Related C++ Class: UserDataMap
UserDataMap
v3.0
The UserDataMap is a Property that
can be attached to any cluster. It permits the storage of variable
length binary user data on the individual components of a geometry.
The data is stored as part of the scene file and can be accessed
via plug-ins using the Object Model.
Softimage plug-ins (both persistent and immediate effects) can use
this property to save and retrieve data that is associated with
individual vertices, polygons, edges, or other components of an
object. Example data possibilities include custom game structures,
raw simulation data, matrices, comments and images.
There are two ways that a UserDataMap can be used - for binary data
or for templated data. With a Binary UserDataMap, the structure of
the user data is left completely up to the plug-in. This is well
suited to C++ plug-ins. However, as the examples below demonstrate,
it is possible to access this data through scripting by using
strings. The second type of UserDataMap uses a CustomProperty to strictly define the
structure of the user data (see UserDataMap.Template). Templated
user data can be inspected in the UI (see InspectUserData) in addition
to scripting and C++ access.
UserDataMaps are created with CreateUserDataMap or
SceneItem.AddProperty.
UserDataMap differs from the CustomProperty and UserDataBlob objects in that it stores
separate data for each individual component of a cluster. For
example a cluster with ten polygons will have ten different
UserDataItem objects. It is
possible to enumerate the UserDataItem objects in a UserDataMap
using UserDataMap.Item, or the
VBScript "for each" loop construct, or the JScript Enumerator
object. It is also possible to access the contents of a UserDataMap
directly with UserDataMap.ItemValue.
The UserDataMap is similar to a ClusterProperty, which also stores
per-component data, but the UserDataMap allows variable length
data, whereas the ClusterProperty objects always store a fixed
number of Doubles at each component.
The User Data Map provides similar functionality to that available
from the SAAPHIRE SAA_subelement* UserData functions. For example,
the Name of the property corresponds to the Tag argument to
functions like SAA_subelementSetUserData.
It is possible to save a preset of a user data map and to reapply
the user data on a different object. However, for the best results,
the number of components in both clusters should be the same.
'example giving an overview of accessing per-component 'user data on a cluster dim oObj, oCluster, oUDProp, i, oUserDataAtIndex2 set oObj = CreatePrim( "Sphere", "MeshSurface" ) SetValue oObj & ".polymsh.geom.subdivu", 1 SetValue oObj & ".polymsh.geom.subdivv", 1 'Create a cluster covering all edges on the geometry set oCluster = oObj.ActivePrimitive.Geometry.AddCluster( siEdgeCluster,"UserDataCls" ) set oUDProp = oCluster.AddProperty( "UserDataMap",,"UserDataExample" ) 'UserDataMap logmessage oUDProp.Type 'Assign user data to a particular index oUDProp.ItemValue(2) = "Some String Data" 'Another way to do the same thing is to use a UserDataItem object oUDProp.Item(3).Value = "String data for index 3" 'Erase the data we just placed at 3 oUDProp.ClearItem(3) 'Now look at all the user data in the property looking for non-empty content for i = 0 to oUDProp.Count if ( not oUDProp.IsEmpty(i) ) then logmessage "Found data with size " & oUDProp.ItemSize(i) & " at cluster index " & i end if next 'Output of this script is: 'INFO : "UserDataMap" 'INFO : "Found data with size 32 at cluster index 2" |
//example giving an overview of accessing per-component //user data on a cluster var oObj = CreatePrim( "Sphere", "MeshSurface" ) ; SetValue( oObj + ".polymsh.geom.subdivu", 1 ) ; SetValue( oObj + ".polymsh.geom.subdivv", 1 ) ; //Create a cluster covering all edges on the geometry var oCluster = oObj.ActivePrimitive.Geometry.AddCluster( siEdgeCluster, "UserDataCls" ) ; var oUDProp = oCluster.AddProperty( "UserDataMap",false,"UserDataExample" ) ; //UserDataMap logmessage( oUDProp.Type ) ; //Assign user data to a particular index oUDProp.ItemValue(2) = "Some String Data" //Or we can use a UserDataItem object as another way to set data oUDProp.Item(3).Value = "String data for index 3" //Erase the data we just placed at 3 oUDProp.ClearItem(3) //Now look at all the user data in the property looking for non-empty content for( var i = 0 ; i < oUDProp.Count ; i++ ) { if ( !oUDProp.IsEmpty(i) ) { logmessage( "Found data with size " + oUDProp.ItemSize(i) + " at cluster index " + i ) ; } } //Output of this script is: //INFO : "UserDataMap" //INFO : "Found data with size 32 at cluster index 2" |
'example demonstrating the how to display the entire contents 'of a binary user data map in the command history window. '(For an example showing how to show the contents of a templated user data map 'see the Info OM Netview page that is part of XSI Local\Tools) dim oObj, oCluster, oUDProp, i 'First set up a little demo scenario set oObj = CreatePrim( "Arc", "NurbsCurve" ) set oCluster = oObj.ActivePrimitive.Geometry.AddCluster( siVertexCluster,"PntCluster",Array(1,4,7,10,13,16) ) set oUDProp = oCluster.AddProperty( "UserDataMap",,"UserDataExample" ) 'Fill in the user data with the string version of the index for i = 0 to oUDProp.Count - 1 oUDProp.Item( i ).Value = CStr( i ) next SelectObj oUDProp 'Then dump out all the content that was added TraceSelectedUserDataMap 'Output of this script will look approximately like this: 'INFO : "User Data Map: arc.crvlist.cls.PntCluster.UserDataExample" 'INFO : "Size of User Data Map 6- Size of Cluster 6" 'INFO : "Property was created on little-endian computer" 'INFO : "Contents:" 'INFO : "Item 0 pnt(1): 0 " 'INFO : "Item 1 pnt(4): 1 " 'INFO : "Item 2 pnt(7): 2 " 'INFO : "Item 3 pnt(10): 3 " 'INFO : "Item 4 pnt(13): 4 " 'INFO : "Item 5 pnt(16): 5 " 'The number of components with user data could get huge so this constant 'constrains the size of the output to reasonable proportions const g_MapItemsDisplay = 256 const g_ShowEmptyItems = false sub TraceSelectedUserDataMap 'This example could easily to enhanced to loop through multiple selections 'but for the sake of simplicity it only looks at the first item if ( selection.Count > 0 ) then if ( typename( selection( 0 ) ) = "UserDataMap" ) then TraceUserDataMap( selection( 0 ) ) else logmessage "Please select a user data map and try again" end if else logmessage "Please select a user data map and try again" end if end sub sub TraceUserDataMap( in_oUDM ) dim i, oItem, cntCluster, byteString, j, str, val cntCluster = in_oUDM.Parent.Elements.Count Logmessage "User Data Map: " & in_oUDM.FullName Logmessage "Size of User Data Map " & in_oUDM.Count & "- Size of Cluster " & cntCluster Logmessage "Contents:" dim oCluster, oClusterElementsCollection, aElements set oCluster = in_oUDM.Parent set oClusterElementsCollection = oCluster.Elements aElements = oClusterElementsCollection.Array dim iElementInCluster, strItemDesc dim cntItems cntItems = in_oUDM.Count dim cntDisplayedItems cntDisplayedItems = 0 for i = 0 to ( cntItems - 1) if ( i < cntCluster ) then iElementInCluster = aElements(i) strItemDesc = "Item " & i & " " & oCluster.type & "(" & iElementInCluster & "): " else strItemDesc = "Item " & i & " " & oCluster.type & "(INVALID INDEX): " end if set oItem = in_oUDM.Item( i ) if ( not oItem.IsEmpty ) then 'We convert the contents into a byte by byte description 'because we don't know if this is a safe to display unicode string strItemDesc = strItemDesc & GetUserDataContentsString( oItem ) logmessage strItemDesc cntDisplayedItems = cntDisplayedItems + 1 if ( cntDisplayedItems = g_MapItemsDisplay ) then exit for end if elseif ( g_ShowEmptyItems ) then logmessage strItemDesc & " is empty" end if next end sub 'Produces a string representation of the user data function GetUserDataContentsString( in_UserDataItem ) dim strValue strValue = in_UserDataItem.Value dim j 'Also attempt to produce a string representation dim strAsString for j = 1 to Len( strValue ) val = Asc( Mid( strValue, j, 1 ) ) 'We only accept the basic ascii values - other content is 'not necessary safe to print. For supporting non-English 'characters, line breaks and tabs this logic would need 'to be more sophisticated if (( val < 127 ) AND ( val > 31 )) then strAsString = strAsString & Chr( val ) else 'we assume that there is binary content strAsString = strAsString & "." end if next GetUserDataContentsString = strAsString end function |
'example of how to access a templated UserDataMap from from scripting. 'In this example we create user data for an imaginary game on the vertices of 'a sphere. ' 'The user data template is designed to match an associated structure in the game engine, 'and consists of 5 parameters ("ImagePath", "AttachmentPoint", "FixedPoint", "ZetaFactor", 'and "Friction") ' 'This example creates the object and sets some example values on a few points. (A user could also 'add and edit these values from the user interface using the InspectUserData command) ' 'When you run this example this is the output in the log window: ' 'INFO : "User data on sphere.polymsh.cls.AllVertices.GameData" 'INFO : "pnt[12]: ZetaFactor:0.2 Friction:64 AttachmentPoint Image: image12.tif" 'INFO : "pnt[31]: ZetaFactor:0.9 Friction:12 AttachmentPoint FixedPoint Image: unknown" 'INFO : "pnt[44]: ZetaFactor:0.5 Friction:28 FixedPoint Image: image1.jpg" option explicit const g_ClusterName = "AllVertices" const g_UserDataMapName = "GameData" const g_TemplateName = "VertexInfoTemplate" newscene ,false dim oSphere, oUserDataMap set oSphere = ActiveSceneRoot.AddGeometry( "Sphere", "MeshSurface" ) 'Create templated user data map - currently it is has no user data set oUserDataMap = SetupObject( oSphere ) 'Set these user data values on vertex 44 call AddUserData( oUserDataMap , 44, "image1.jpg", false, true, 0.5, 28 ) 'Set different values on points 12 call AddUserData( oUserDataMap , 12, "background14.tif", true, false, 0.1, 39 ) 'Change our minds about point 12 call AddUserData( oUserDataMap , 12, "image12.tif", true, false, 0.2, 64 ) call AddUserData( oUserDataMap , 31, "unknown", true, true, 0.9, 12 ) call LogUserData( oUserDataMap ) 'On the given object create a UserDataMap for the game data function SetupObject( in_oObj ) dim oCluster, oPSet, oUserDataMap 'Test if we have already setup this object on error resume next set oUserDataMap = in_oObj.ActivePrimitive.Geometry._ Clusters( g_ClusterName ).Properties( g_UserDataMapName ) on error goto 0 if typename( oUserDataMap ) = "UserDataMap" then 'Our user data map already exists set SetupObject = oUserDataMap exit function end if set oCluster = in_oObj.ActivePrimitive.Geometry.AddCluster( siVertexCluster, g_ClusterName ) set oPSet = CreateTemplate( oCluster ) set oUserDataMap = oCluster.AddProperty( "UserDataMap",,g_UserDataMapName ) 'Associate the template with our user data map set oUserDataMap.Template = oPSet 'Return the newly created User Data Map set SetupObject = oUserDataMap end function 'This function creates the template that defines the format of the data inside our UserDataMap 'The new template is returned. 'Note: this function does not associate the template with any user data map, or set any values on 'the user data map. function CreateTemplate( in_oParentObject ) dim oPSet set oPSet = in_oParentObject.AddProperty( "Custom_parameter_list",, g_TemplateName ) oPSet.AddParameter "ImagePath", siString oPSet.AddParameter "AttachmentPoint", siBool, , , ,, , false oPSet.AddParameter "FixedPoint", siBool, , , ,, , false oPSet.AddParameter "ZetaFactor", siDouble, , , , , , 0.0, 0.0, 1.0 oPSet.AddParameter "Friction", siUByte, , , , , , 64, 0, 128 set CreateTemplate = oPSet end function 'Given specific game parameter values, this routine will save those values 'on the specified vertex of the UserDataMap sub AddUserData( in_oUDM, in_CompIndex, in_ImagePath, in_AttachPoint, in_FixedPoint, in_ZetaFactor, in_Friction ) 'Get access to the Template for this user data map dim oTemplate set oTemplate = in_oUDM.Template 'Fill in the parameters on the template oTemplate.Parameters( "ImagePath" ).Value = in_ImagePath oTemplate.Parameters( "AttachmentPoint" ).Value = in_AttachPoint oTemplate.Parameters( "FixedPoint" ).Value = in_FixedPoint oTemplate.Parameters( "ZetaFactor" ).Value = in_ZetaFactor oTemplate.Parameters( "Friction" ).Value = in_Friction 'Now that the parameters are specified we need to associated 'these values with the specified vertex in_oUDM.ItemValue(in_CompIndex) = oTemplate.BinaryData end sub 'This routine prints out the contents of our user data map sub LogUserData( in_oUDM ) dim oTemplate, i, strAttachPnt, strFixedPnt Logmessage "User data on " & in_oUDM set oTemplate = in_oUDM.Template for i = 0 to in_oUDM.count 'We can only print out non-empty user data items if ( NOT in_oUDM.IsEmpty( i ) ) then 'Take the values from the user data and put them in the template 'so that we can read them oTemplate.BinaryData = in_oUDM.ItemValue( i ) 'Look at the boolean values to convert into readable strings if ( oTemplate.Parameters("AttachmentPoint").Value ) then strAttachPnt = "AttachmentPoint " else strAttachPnt = "" end if if ( oTemplate.Parameters("FixedPoint").Value ) then strFixedPnt = "FixedPoint " else strFixedPnt = "" end if 'Print a 1 line representation of all the values on this particular point logmessage "pnt[" & i & "]: " & _ " ZetaFactor:" & oTemplate.Parameters("ZetaFactor").Value & _ " Friction:" & oTemplate.Parameters("Friction").Value & _ " " & strAttachPnt & strFixedPnt & _ "Image: " & oTemplate.Parameters("ImagePath").Value end if next end sub |