## Geometry.SaveShapeKey

### Description

Saves a shape key for this Geometry and creates a new ShapeClip object in the mixer to represent this shape. Shape keys have two parts: sources (a shape that you have stored) and clips (an instance of a shape key (source) at a particular position along a track in the animation mixer).
There are two possible workflows for using this method: the first is to change the actual shape of the primitive and then call this method (which is similar to the SaveShapeKey command); the second is to specify the shape using the PositionArray argument. The example below illustrates these two approaches.
The actual data for the shape is stored as a ClusterProperty. It is possible to store a shape key that defines positions for only a subset of the points, by using the IndexArray argument. This can save memory on a large object. Each subset of points with a shape key is represented by a separate Cluster.

### C# Syntax

    ShapeClip Geometry.SaveShapeKey( Double in_time, Double in_clipDuration, siShapeReferenceMode in_refMode, siShapeInstanceMode in_instMode, String in_name, Object in_indexArray, Object in_positionArray, Object in_normalArray );

### Scripting Syntax

    oReturn = Geometry.SaveShapeKey( Time, [ClipDuration], [RefMode], [InstMode], [Name], [IndexArray], [PositionArray], [NormalArray] );

### Return Value

ShapeClip

### Parameters

Parameter | Type | Description
--- | --- | ---
Time | Double | Time in seconds. The shape of the object is evaluated at this time, unless the shape is explicitly defined by the PositionArray argument. The time argument is also used when adding the ShapeClip to the Mixer. For example, if siShapeMixedWeightMode is specified as the InstMode argument, then the new shape will account for 100% of the shape at the specified time. Only a single shape key can be specified at any particular time.
ClipDuration | Double | Clip duration in seconds. Specify -1.0 to have the default behavior, which depends on the InstMode argument. For example if siShapeMixedWeightMode is specified then the ClipDuration is the entire scene duration, but if siShapeTransitionMode is specified then clip will last 1 second. Default Value: -1.0
RefMode | siShapeReferenceMode | Shape reference mode. This specifies how the shape is stored and how the shape will change if the underlying reference shape changes. The reference mode is ignored when more than one shape is saved on the same cluster, the reference mode of the first shape key is used instead. Default Value: siShapeLocalReferenceMode
InstMode | siShapeInstanceMode | Shape instantiation mode. This controls the way the new ShapeClip is added to the Mixer. With siShapeMixedWeightMode a new track is created, the new clip added to this track, and the fcurves controlling the weighting are adjusted. With siShapeInstanceOnlyMode the clips are added to separate tracks, but no fcurves are used to weight between the shapes. With siShapeTransitionMode all clips are added to the same track, with transition effects applied between them. Default Value: siShapeMixedWeightMode
Name | String | Name to use for the new ShapeClip. The ClusterProperty associated with this shape is not affected by this argument. Default Value: "Point_AUTO_ShapeAction_Clip"
IndexArray | Array | A 1-dimensional array of geometry point indices. Only these points will be included in the shape definition. If no other shapes use this subset of points then a new Cluster will be created. Default Value: If not specified the shape key is saved for every point on the geometry
PositionArray | Array | A 1- or 2- dimensional array of key position points used for computing the offsets of the geometry points referred by the array of indices. The points must be specified in absolute coordinates. A 1-dimensional array must be formatted as a sequence of x,y,z values: {Xo,Yo,Zo,...X(n-1),Y(n-1),Z(n-1)}. A 2 dimensional array must be formatted as a Nx3 array of x,y,z values. This argument is only considered if the IndexArray argument is specified, and must specify the same number of points as the IndexArray. Default Value: Shape key is based on the current positions of the geometry points
NormalArray | Array | A 1 or 2 dimensional array of normal points. Normals are not yet supported.

### Examples

#### 1. VBScript Example

    '

    ' This example shows how it is possible to create shape animations from the object model.

    ' Two techniques are used to create the same animation on two polygons

    '

    dim oGrid1, oGrid2

    ' Technique one is similar to the process used when interactively

    ' saving shape keys - we change the shape of the object and then save a

    ' shape key

    ' Create a polygon

    set oGrid1 = CreatePrim( "Grid", "MeshSurface" )

    SetValue oGrid1 & ".polymsh.geom.subdivu", 1

    SetValue oGrid1 & ".polymsh.geom.subdivv", 1

    Scale oGrid1, 0.25, 0.25, 0.25, siAbsolute, siParent, siObj, siXYZ

    Translate  oGrid1, -2, 0, 0, siRelative, siParent, siObj, siXYZ

    ' Our animation will start with the initial shape at frame 1 and

    ' morph to the second shape until frame 30

    dim frameRate, frame1Seconds, frame30Seconds

    frameRate = GetValue( "PlayControl.Rate" )

    frame1Seconds = 1 / frameRate

    frame30Seconds = 30 / frameRate

    ' Save the current shape as the reference

    ' This remembers the locations of each vertex.  This information will

    ' be all zeros because we haven't moved any vertices and use siShapeLocalReferenceMode

    call oGrid1.ActivePrimitive.Geometry.SaveShapeKey( frame1Seconds, -1.0, _

    	siShapeLocalReferenceMode, siShapeMixedWeightMode )

    ' Move some of the points

    Translate oGrid1 & ".pnt[0]" , 0, 0, -1, siRelative, siGlobal, siObj, siXYZ

    Translate oGrid1 & ".pnt[2]" , 0, 0, 1, siRelative, siGlobal, siObj, siXYZ

    Translate oGrid1 & ".pnt[3]" , 0, 0, -1, siRelative, siGlobal, siObj, siXYZ

    ' Take a second snap shot of the object

    call oGrid1.ActivePrimitive.Geometry.SaveShapeKey( frame30Seconds, -1.0, _

    	siShapeLocalReferenceMode, siShapeMixedWeightMode )

    ' Technique two is direct.  We describe the actual positions

    ' of the vertices for each shape.

    set oGrid2 = CreatePrim( "Grid", "MeshSurface" )

    SetValue oGrid2 & ".polymsh.geom.subdivu", 1

    SetValue oGrid2 & ".polymsh.geom.subdivv", 1

    Scale oGrid2, 0.25, 0.25, 0.25, siAbsolute, siParent, siObj, siXYZ

    Translate oGrid2, +2, 0, 0, siRelative, siParent, siObj, siXYZ

    ' We only specify the vertices that actually move.  In the case of

    ' a large object this could save a lot of memory

    call oGrid2.ActivePrimitive.Geometry.SaveShapeKey( frame1Seconds, -1.0, _

    	siShapeLocalReferenceMode, siShapeMixedWeightMode, ,_

    	Array( 0,2,3 ), Array( -4,0,-4,  4,0,-4,  4,0,4) )

    call oGrid2.ActivePrimitive.Geometry.SaveShapeKey( frame30Seconds, -1.0, _

    	siShapeLocalReferenceMode, siShapeMixedWeightMode, ,_

    	Array( 0,2,3 ), Array( -4,0,-8,  4,0,0,  4,0,0) )

#### 2. VBScript Example

    '

    ' Three cones are created with identical animation

    ' although they use different Reference Modes to record the shape

    '

    set root = Application.ActiveProject.ActiveScene.Root

    set obj = root.AddGeometry( "Cone", "MeshSurface" )

    ApplyOp "bend", obj

    obj.posz.Value = -3

    set geometry = obj.ActivePrimitive.Geometry

    ' Move the point to 1,1,0 in local mode

    set clip = geometry.SaveShapeKey( 1, 1, siShapeLocalReferenceMode, _

    	siShapeInstanceOnlyMode, "MyShapeKey", Array(1), Array(1.,1.,0) )

    set obj = root.AddGeometry( "Cone", "MeshSurface" )

    ApplyOp "bend", obj

    set geometry = obj.ActivePrimitive.Geometry

    ' Move the point to 1,1,0 in absolute mode

    set clip = geometry.SaveShapeKey( 1, 1, siShapeAbsoluteReferenceMode, _

    	siShapeInstanceOnlyMode, "MyShapeKey", Array(1), Array(1.,1.,0) )

    set obj = root.AddGeometry( "Cone", "MeshSurface" )

    ApplyOp "bend", obj

    obj.posz.value = 3

    set geometry = obj.ActivePrimitive.Geometry

    ' Move the point to 1,1,0 in object mode

    set clip = geometry.SaveShapeKey( 1, 1, siShapeObjectReferenceMode, _

    	siShapeInstanceOnlyMode, "MyShapeKey", Array(1), Array(1.,1.,0) )

#### 3. VBScript Example

    '

    ' This example demonstrates how you access the shape information for a particular shape key by looking

    ' at the cluster properties which store this data

    '

    option explicit

    ' Set up a little scene

    dim oCircle

    set oCircle = CreatePrim( "Sphere", "MeshSurface" )

    ' Move the top and bottom point of the sphere outwards

    oCircle.ActivePrimitive.Geometry.SaveShapeKey 0.5, 3.0, _

    	siShapeAbsoluteReferenceMode, siShapeInstanceOnlyMode, ,_

    	Array( 1, 0 ), Array( 0,6,0,  0,-6,0 )

    ' Move two different points - this creates a new, independent cluster

    ' We can use a different reference mode for this, but we still specify the

    ' point positions in Absolute terms.

    oCircle.ActivePrimitive.Geometry.SaveShapeKey 0.8, 1.0, _

    	siShapeLocalReferenceMode, siShapeInstanceOnlyMode,, _

    	Array( 5, 33 ), Array( -2,0,0, 2,0,0 )

    ShowShapeInformation( oCircle )

    sub ShowShapeInformation( in_obj )

    	dim oClusters, oCluster, oProperties, oProperty, cntFoundShapes

    	on error resume next

    	set oClusters = in_obj.ShapeAnimatedClusters

    	if ( err <> 0 ) then

    		Application.LogMessage "Please select a 3D Object"

    		exit sub

    	end if

    	on error goto 0

    	Application.LogMessage "Dump of shape key data on object " & in_obj.Fullname

    	cntFoundShapes = 0

    	' Go through the various clusters on the object

    	' (In practice only the Point clusters will have shape keys)

    	for each oCluster in oClusters

    		' Cluster indices are not the same as the indices on the

    		' geometry, but we can easily determine the relationship with

    		' this array:

    		dim oClusterElementsCollection, aElements

    		set oClusterElementsCollection = oCluster.Elements

    		aElements = oClusterElementsCollection.Array

    		' Only search for the shape keys, which have type "clskey"

    		set oProperties = oCluster.LocalProperties.filter( "clskey" )

    		for each oProperty in oProperties

    			' Found a shape key

    			dim i, XYZArray

    			cntFoundShapes = cntFoundShapes + 1

    			if InstrRev( oProperty, ".ResultClusterKey" ) <> 0 then

    				' There may also be an internal cluster property

    				' called "ResultClusterKey" which stores the

    				' result of blending the various shapes at the

    				' current time

    				Application.LogMessage "Blended shape at this time: " & oProperty

    			else

    				Application.LogMessage "Shape key: " & oProperty

    			end if

    			' The reference mode is available from the KeyType parameter

    			dim ReferenceType

    			ReferenceType =	oProperty.Parameters("KeyType").Value

    			if ( ReferenceType = 0 ) then

    				Logmessage "        Uses: Absolute Reference Mode"

    			elseif ( ReferenceType = 1 ) then

    				Logmessage "        Uses: Local Reference Mode"

    			elseif ( ReferenceType = 2 ) then

    				Logmessage "        Uses: Object Reference Mode"

    			end if

    			' The contents of the cluster can be found in this safearray

    			XYZArray = oProperty.Elements.Array

    			For i=0 to UBound(oProperty.Elements.Array,2)

    				' Print out the x,y,z values

    				LogMessage "        pnt["& aElements( i ) & "] has position (" _

    					& Round(XYZArray(0,i),3) & "," & Round(XYZArray(1,i),3) & ","_

    					& Round(XYZArray(2,i),3) & ")"

    			Next

    		next

    	next

    	if ( cntFoundShapes = 0 ) then

    		LogMessage "There are no shapes on this object"

    	end if

    end sub

### See Also

SaveShapeKey Cluster ClusterProperty ShapeClip