1 /*
2 	Copyright (C) 2005-2007 Feeling Software Inc.
3 	Portions of the code are:
4 	Copyright (C) 2005-2007 Sony Computer Entertainment America
5 
6 	MIT License: http://www.opensource.org/licenses/mit-license.php
7 */
8 /*
9 	Based on the FS Import classes:
10 	Copyright (C) 2005-2006 Feeling Software Inc
11 	Copyright (C) 2005-2006 Autodesk Media Entertainment
12 	MIT License: http://www.opensource.org/licenses/mit-license.php
13 */
14 
15 #include "StdAfx.h"
16 #include "FCDocument/FCDocument.h"
17 #include "FCDocument/FCDController.h"
18 #include "FCDocument/FCDGeometry.h"
19 #include "FCDocument/FCDGeometryMesh.h"
20 #include "FCDocument/FCDGeometryPolygons.h"
21 #include "FCDocument/FCDGeometrySource.h"
22 #include "FCDocument/FCDGeometrySpline.h"
23 #include "FCDocument/FCDSkinController.h"
24 #include "FCDocument/FCDEntityReference.h"
25 
26 //
27 // FCDSkinController
28 //
29 
30 ImplementObjectType(FCDSkinController);
31 
FCDSkinController(FCDocument * document,FCDController * _parent)32 FCDSkinController::FCDSkinController(FCDocument* document, FCDController* _parent)
33 :	FCDObject(document)
34 ,	parent(_parent)
35 ,	InitializeParameter(bindShapeTransform, FMMatrix44::Identity)
36 {
37 	// Always create this.
38 	target = new FCDEntityReference(document, parent);
39 }
40 
~FCDSkinController()41 FCDSkinController::~FCDSkinController()
42 {
43 	SAFE_RELEASE(target);
44 }
45 
GetTargetUri() const46 FUUri FCDSkinController::GetTargetUri() const
47 {
48 	return target->GetUri();
49 }
50 
SetTargetUri(const FUUri & uri)51 void FCDSkinController::SetTargetUri(const FUUri& uri)
52 {
53 	target->SetUri(uri);
54 }
55 
GetTarget()56 FCDEntity* FCDSkinController::GetTarget()
57 {
58 	return target->GetEntity();
59 }
60 
GetTarget() const61 const FCDEntity* FCDSkinController::GetTarget() const
62 {
63 	return target->GetEntity();
64 }
65 
SetTarget(FCDEntity * _target)66 void FCDSkinController::SetTarget(FCDEntity* _target)
67 {
68 	target->SetEntity(NULL);
69 	SetNewChildFlag();
70 
71 	// Retrieve the actual base entity, as you can chain controllers.
72 	FCDEntity* baseEntity = _target;
73 	if (baseEntity != NULL && baseEntity->GetType() == FCDEntity::CONTROLLER)
74 	{
75 		baseEntity = ((FCDController*) baseEntity)->GetBaseGeometry();
76 	}
77 
78 	if (baseEntity == NULL || baseEntity->GetType() != FCDEntity::GEOMETRY)
79 	{
80 		// The new target is no good!
81 		return;
82 	}
83 
84 	target->SetEntity(_target);
85 	FCDGeometry* geometry = (FCDGeometry*) baseEntity;
86 
87 	// Retrieve the new vertex count
88 	size_t vertexCount = 0;
89 	if (geometry->IsMesh())
90 	{
91 		FCDGeometryMesh* mesh = geometry->GetMesh();
92 		FCDGeometrySource* positionSource = mesh->GetPositionSource();
93 		if (positionSource != NULL)
94 		{
95 			vertexCount = positionSource->GetValueCount();
96 		}
97 	}
98 	else if (geometry->IsSpline())
99 	{
100 		FCDGeometrySpline* spline = geometry->GetSpline();
101 		vertexCount = spline->GetTotalCVCount();
102 	}
103 
104 	// Modify the list of influences to match the new target's vertex count.
105 	if (influences.size() != 0)
106 	{
107 		// This is only really acceptable with equivalent meshes.  Ensure we
108 		// are still compatable.
109 		FUAssert(vertexCount == influences.size(), SetInfluenceCount(vertexCount));
110 	}
111 	else
112 	{
113 		SetInfluenceCount(vertexCount);
114 	}
115 }
116 
SetInfluenceCount(size_t count)117 void FCDSkinController::SetInfluenceCount(size_t count)
118 {
119 	// None of the list structures are allocated directly: resize() will work fine.
120 	influences.resize(count);
121 	SetDirtyFlag();
122 }
123 
SetJointCount(size_t count)124 void FCDSkinController::SetJointCount(size_t count)
125 {
126 	joints.resize(count);
127 	SetDirtyFlag();
128 }
129 
AddJoint(const fm::string jSubId,const FMMatrix44 & bindPose)130 FCDSkinControllerJoint* FCDSkinController::AddJoint(const fm::string jSubId, const FMMatrix44& bindPose)
131 {
132 	SetJointCount(GetJointCount() + 1);
133 	FCDSkinControllerJoint* joint = &joints.back();
134 	joint->SetId(jSubId);
135 	joint->SetBindPoseInverse(bindPose);
136 	SetDirtyFlag();
137 	return joint;
138 }
139 
140 
141 // Reduce the number of joints influencing each vertex to a maximum count
ReduceInfluences(uint32 maxInfluenceCount,float minimumWeight)142 void FCDSkinController::ReduceInfluences(uint32 maxInfluenceCount, float minimumWeight)
143 {
144 	// Pre-cache an empty weight list to the reduced count
145 	fm::vector<FCDJointWeightPair> reducedWeights;
146 	reducedWeights.reserve(maxInfluenceCount + 1);
147 
148 	for (FCDSkinControllerVertex* itM = influences.begin(); itM != influences.end(); ++itM)
149 	{
150 		FCDSkinControllerVertex& influence = (*itM);
151 		size_t oldInfluenceCount = influence.GetPairCount();
152 
153 		// Reduce the weights, keeping only the more important ones using a sorting algorithm.
154 		// Also, calculate the current total of the weights, to re-normalize the reduced weights
155 		float oldTotal = 0.0f;
156 		reducedWeights.clear();
157 		for (size_t i = 0; i < oldInfluenceCount; ++i)
158 		{
159 			FCDJointWeightPair* pair = influence.GetPair(i);
160 			if (pair->weight >= minimumWeight)
161 			{
162 				FCDJointWeightPair* itRW = reducedWeights.begin();
163 				for (; itRW != reducedWeights.end() && (*itRW).weight > pair->weight; ++itRW) {}
164 				if (itRW != reducedWeights.end() || reducedWeights.size() <= maxInfluenceCount)
165 				{
166 					reducedWeights.insert(itRW, *pair);
167 					if (reducedWeights.size() > maxInfluenceCount) reducedWeights.pop_back();
168 				}
169 			}
170 			oldTotal += pair->weight;
171 		}
172 
173 		size_t newInfluenceCount = reducedWeights.size();
174 		if (oldInfluenceCount > newInfluenceCount)
175 		{
176 			// Replace the old weights and re-normalize to their old total
177 			influence.SetPairCount(newInfluenceCount);
178 			for (size_t i = 0; i < newInfluenceCount; ++i) (*(influence.GetPair(i))) = reducedWeights[i];
179 
180 			float newTotal = 0.0f;
181 			for (size_t i = 0; i < newInfluenceCount; ++i) newTotal += influence.GetPair(i)->weight;
182 			float renormalizingFactor = oldTotal / newTotal;
183 			for (size_t i = 0; i < newInfluenceCount; ++i) influence.GetPair(i)->weight *= renormalizingFactor;
184 		}
185 	}
186 
187 	SetDirtyFlag();
188 }
189 
190 //
191 // FCDSkinControllerVertex
192 //
193 
194 
SetPairCount(size_t count)195 void FCDSkinControllerVertex::SetPairCount(size_t count)
196 {
197 	pairs.resize(count);
198 }
199 
AddPair(int32 jointIndex,float weight)200 void FCDSkinControllerVertex::AddPair(int32 jointIndex, float weight)
201 {
202 	pairs.push_back(FCDJointWeightPair(jointIndex, weight));
203 }
204 
205 //
206 // FCDSkinControllerJoint
207 //
208 
SetId(const fm::string & _id)209 void FCDSkinControllerJoint::SetId(const fm::string& _id)
210 {
211 	// Do not inline, since the line below does memory allocation.
212 	id = _id;
213 }
214