v3.0
CustomProperty のバイナリ表現を設定したり、戻したりします。このデータはコンパクトな表現で、BSTR(Stringを参照)にエンコードされています。これはバイト列で、バイナリ情報を含みます。このバイト列には、可読文字は含まれません。C++ の開発者は、::SysFreeString を使用してこのバイト列を開放する必要があります。一方、スクリプトの開発者は、自動的にリソースが管理されるので、何もする必要はありません。プリセットとは異なり、実際の値のみがバイナリ表現で保存されるため、メモリ効率が向上します。アニメーションはバイナリ データには保持されません。バイナリ データには、Parameter.Value のスナップショットが保存されます。バイナリ表現は UserDataItem.Value を使用して UserDataItem 内に保存する場合に適しています。このため、コンポーネントごとのデータを UserDataMap 内に容易に格納できます。このデータを表示して編集するには、データに関連付けられているカスタムプロパティセットを使用します。
カスタムプロパティオブジェクトにはバイナリ表現を設定しないでください。カスタムプロパティとは異なり、カスタムプロパティオブジェクトからはバイナリ表現が取得されません。特に、パラメータの数、パラメータの順序、およびパラメータの型が一致する必要があります。カスタムプロパティが API コールと一致しない場合は、失敗するか、カスタムプロパティに無意味な値が設定されます。ただし、Custom Property のプロパティページに表示されるパラメータの順序を変更することはできます。順序の変更は、spdl ファイルの"Layout"セクションを編集することで行います。不正確な結果になることは避けられます。
アニメートするパラメータの場合は、バイナリ表現で記録された値が現在のフレームの値になります。同様に、プロキシパラメータの現在値もバイナリ表現で記録されます。
通常、SDK の開発者は、このバイナリデータ内の書式を考慮する必要はありません。ただし、データが Softimage の外部で生成または処理される場合は、次の情報が役立ちます。バイナリ表現は、エラー処理用の「マジックナンバー」として使用できる 2 バイトのヘッダで始まります。バッファの残りのスペースには編集する前のカスタムプロパティ設定値が並べられます。当初カスタムプロパティに追加されたとおりの順序で並べられ、リトルエンディアンバイト順に格納されます。たとえば、double は 8 バイト、float は 4 バイト、unsigned char(siUByte)は 1 バイトを占有します。文字列は、文字数を表わす 32 ビット数で始まり、その後に各文字の 16 ビット Unicode 表現が続き、文字列終端文字はありません。F カーブのような簡易型ではなくオブジェクト型のパラメータでは、32 ビットのバッファサイズに続きオブジェクトのバイナリ表現が格納されます。
// get accessor String rtn = CustomProperty.BinaryData; // set accessor CustomProperty.BinaryData = String; |
' This example demonstrates how the BinaryData property could be used to roll back ' changes that a user might have made on a custom property set. The property set is ' shown as a modal dialog, with OK/CANCEL buttons and if the user clicks Cancel we ' restore the property set values back to their original values. ' dim oRoot, oPropSet ' Set up the example set oRoot = Application.ActiveProject.ActiveScene.Root set oPropSet = oRoot.AddProperty("Custom_parameter_list", , "ExamplePSet") SIAddCustomParameter oPropSet, "TestVal", siDouble, 5, 0, 10 call ModalInspectCustomProperty(oPropSet) ' Convenience routine to do the work sub ModalInspectCustomProperty( in_oPropertySet ) dim strOriginalValues strOriginalValues = in_oPropertySet.BinaryData ' If cancel is pressed then Inspect object will fail. This "on error" ' statement says that we want to handle the error rather than have the ' whole script fail. on error resume next InspectObj in_oPropertySet, , , siModal if ( err <> 0 ) then ' User pressed cancel - roll back any changes that they made in_oPropertySet.BinaryData = strOriginalValues end if end sub |
// // This example shows how FCurves can be stored inside a UserDataMap, // and edited via a Custom Property. It demonstrates: (a) how to create // a single script plug-in; (b) how to use a templated UserDataMap object // to store data on points; (c) how to use CustomProperty.BinaryData to // store and retrieve FCurves represented as strings; and (d) how to use // the PPGLayout to build dynamic user interfaces (with no SPDL). // BuildSampleScene() ; function BuildSampleScene() { NewScene(null, false); var oGrid = ActiveSceneRoot.AddGeometry("Grid", "MeshSurface", "GridWithUserData"); oGrid.subdivu = 1; oGrid.subdivv = 1; var oCluster = oGrid.ActivePrimitive.Geometry.AddCluster("pnt"); var oUserDataMap = oCluster.AddProperty("UserDataMap", false, "FCurveData"); var oCustomProperty = CreateUserDataTemplate(oCluster); oUserDataMap.Template = oCustomProperty; InspectObj(oCustomProperty); SelectObj(oGrid, null, true); SetSelFilter("Point"); } function CreateUserDataTemplate( in_oCluster ) { var oCustomProperty = in_oCluster.AddProperty("CustomProperty", false, "DataTemplate"); oCustomProperty.AddFCurveParameter("CurveData"); var oLayout = oCustomProperty.PPGLayout; var oLayoutItem = oLayout.AddFCurve("CurveData", 150); oLayout.AddRow(); oLayout.AddButton("ReadFromSelection", "Read from selected Point"); oLayout.AddButton("SaveFromSelection", "Save on selected Point(s)"); oLayout.AddButton("Instructions"); oLayout.EndRow() ; oLayout.Language = "JScript"; oLayout.Logic = ReadFromSelection_OnClicked.toString() + SaveFromSelection_OnClicked.toString() + Instructions_OnClicked.toString() + GetSelectedPoints.toString() + GetUserDataMap.toString(); return oCustomProperty; } // // The following code is Logic code for the Property Page // function GetSelectedPoints() { // Find out what points are selected. They are returned as an array of // Cluster Indices (-1 is returned in the array if the selected point // is not part of the cluster) var oSubComp = null; for ( var i=0; i<Selection.Count; i++) { var oObj = Selection(i); if (oObj.Type == "pntSubComponent") { oSubComp = oObj.SubComponent; break; } } if (oSubComp == null) { Application.LogMessage("Please select a point"); return null; } // (We could also double check that the points are selected on the // right object using the parent property!) var aVBSelectedElements = new VBArray(oSubComp.ElementArray); var aGeometryIndices = aVBSelectedElements.toArray(); // Index on a cluster is not necessarily identical to a // geometry index. Use the Cluster object to do a lookup var oThisCustomProperty = PSet.Inspected.Item(0); var oCluster = oThisCustomProperty.Parent; for ( var j=0; j<aGeometryIndices.length; j++) { aGeometryIndices[j] = oCluster.FindIndex(aGeometryIndices[j]); } return aGeometryIndices; } function GetUserDataMap() { // Find the UserDataMap associated with this Custom Property // We know they are both nested under the same Cluster // and what the name of the User Data Map is var oThisCustomProperty = PSet.Inspected.Item(0); var oCluster = oThisCustomProperty.Parent; return oCluster.Properties.Item("FCurveData"); } function ReadFromSelection_OnClicked() { var aSelectedElements = GetSelectedPoints(); if (aSelectedElements == null || aSelectedElements.length == 0) { return; } // If more than one item is selected we only look at the first one var clusterIndex = aSelectedElements[0]; if (clusterIndex == -1) { Application.LogMessage("The selected index is not part of the cluster"); return; } var oUserDataMap = GetUserDataMap(); if ( oUserDataMap.IsEmpty(clusterIndex) ) { Application.LogMessage("There is no user data stored on this item yet"); return; } // Transfer the data from the UserDataMap // to the ClusterProperty var oThisCustomProperty = PSet.Inspected.Item(0); oThisCustomProperty.BinaryData = oUserDataMap.ItemValue(clusterIndex); } function SaveFromSelection_OnClicked() { var aSelectedElements = GetSelectedPoints(); if (!aSelectedElements || !aSelectedElements.length) { Application.LogMessage("You must select at least one point in order to save this fcurve"); return; } var oThisCustomProperty = PSet.Inspected.Item(0); binarySnapShotOfCustomProperty = oThisCustomProperty.BinaryData; var oUserDataMap = GetUserDataMap() ; for ( var k=0; k<aSelectedElements.length; k++) { if (aSelectedElements[k] != -1) { oUserDataMap.ItemValue(aSelectedElements[k]) = binarySnapShotOfCustomProperty; } } } function Instructions_OnClicked() { // There are lots of different ways to show information to a user, for example popping // up netview, showing a message box, or putting static text right on the dialog. // // For demonstration purposes we create a temporary custom pset with a multiline edit // box, which we fill with text. This doesn't have ideal usability but shows some of // the potential for doing totally dynamic UI var oInfoPSet = ActiveSceneRoot.AddProperty("CustomProperty", false, "Info"); var oParameter = oInfoPSet.AddParameter3("Info", siString); oParameter.ReadOnly = true; oParameter.Value = "Plug-in Instructions\r\n\r\n" + "This example demonstrates how to store FCurves as User Data.\r\n" + "Each of the 4 vertices of the Grid can store a different Curve\r\n\r\n" + "To view the FCurve on a vertex, select it and press the \r\n" + "'Read from Selected Point' button.\r\n\r\n" + "To set the fcurve, change the fcurve in the view, " + "select one or more points\r\n" + "and then press the 'Save on selected Point(s)' button" var oLayoutItem = oInfoPSet.PPGLayout.AddString("Info", "", true, 300); oLayoutItem.SetAttribute("ValueOnly", true); InspectObj(oInfoPSet, null, null, siModal, false); DeleteObj(oInfoPSet); } |