1 /*
2    Copyright (c) 2008-2009 NetAllied Systems GmbH
3 
4 	This file is part of COLLADAMax.
5 
6    Portions of the code are:
7    Copyright (c) 2005-2007 Feeling Software Inc.
8    Copyright (c) 2005-2007 Sony Computer Entertainment America
9 
10    Based on the 3dsMax COLLADASW Tools:
11    Copyright (c) 2005-2006 Autodesk Media Entertainment
12 
13    Licensed under the MIT Open Source License,
14    for details please see LICENSE file or the website
15    http://www.opensource.org/licenses/mit-license.php
16 */
17 
18 
19 #include "COLLADAMaxStableHeaders.h"
20 
21 #include "COLLADAMaxOptions.h"
22 
23 #include <max.h>
24 #include <maxapi.h>
25 #include <custcont.h>
26 
27 #include "resource.h"
28 
29 namespace COLLADAMax
30 {
31 
32 	const String Options::OPTION_NORMALS_NAME = "normals";
33 	const String Options::OPTION_TRIANGULAT_NAME = "triangulate";
34 	const String Options::OPTION_XREFS_NAME = "xrefs";
35 	const String Options::OPTION_TANGENTS_NAME =  "tangents";
36 	const String Options::OPTION_ANIMATIONS_NAME =  "animations";
37 	const String Options::OPTION_SAMPLEANIMATIONS_NAME =  "sampleAnim";
38 	const String Options::OPTION_CREATECLIP_NAME =  "createClip";
39 	const String Options::OPTION_BAKEMATRICES_NAME =  "bakeMatrices";
40 	const String Options::OPTION_RELATIVEPATHS_NAME =  "relativePaths";
41 	const String Options::OPTION_CHECKIFANIMATIONISANIMATED_NAME =  "checkIfAnimationIsAnimated";
42 	const String Options::OPTION_ANIMATIONSTART_NAME =  "animStart";
43 	const String Options::OPTION_ANIMATIONEND_NAME =  "animEnd";
44 	const String Options::OPTION_COPY_IMAGES_NAME =  "copyImages";
45 	const String Options::OPTION_EXPORT_USERDEFINED_PROPERTIES_NAME =  "exportUserdefinedProperties";
46 
47 
48 
EnableDlgControl(HWND window,int controlId,BOOL enabled)49 	static void EnableDlgControl(HWND window, int controlId, BOOL enabled)
50 	{
51 		HWND control = GetDlgItem(window, controlId);
52 		if (control)
53 			EnableWindow(control, enabled);
54 	}
55 
56 	template<class OptionType>
writeOption(FILE * file,const String & optionName,OptionType optionValue)57 	void writeOption(FILE* file, const String & optionName, OptionType optionValue)
58 	{
59 		String optionString = optionName + "=%d\r\n";
60 		fprintf_s(file, optionString.c_str(), optionValue);
61 	}
62 
63 	template<class OptionType>
readOption(char * token,const String & optionName,char * value,OptionType & optionValue)64 	bool readOption(char* token, const String & optionName, char* value, OptionType &optionValue)
65 	{
66 		bool match = strcmp(token, optionName.c_str()) == 0;
67 		if ( match )
68 			optionValue = (OptionType) atoi(value);
69 		return match;
70 	}
71 
72 	template<>
readOption(char * token,const String & optionName,char * value,bool & optionValue)73 	bool readOption<bool>(char* token, const String & optionName, char* value, bool &optionValue)
74 	{
75 		bool match = strcmp(token, optionName.c_str()) == 0;
76 		if ( match )
77 			optionValue = atoi(value) != false;
78 		return match;
79 	}
80 
81 
82 	const String Options::CONFIGURATION_FILE_NAME = "OpenCOLLADA.ini";
83 	const String Options::CONFIGURATION_HEADER_NAME = "OpenCOLLADA";
84 
Options(Interface * maxInterface)85 	Options::Options(Interface* maxInterface)
86 		:
87 		mMaxInterface(maxInterface),
88 		// Set the option values to their default
89 		mNormals(true),
90 		mTriangulate(true),
91 		mIncludeXrefs(false),
92 		mTangents(false),
93 		mAnimations(true),
94 		mSampleAnimation(false),
95 		mCreateClip(false),
96 		mLayersAsClips(false),
97 		mBakeMatrices(false),
98 		mRelativePaths(false),
99 		mCopyImages(true),
100 		mImageDirectory("images"),
101 		mCheckIfAnimationIsAnimated(false),
102 		importUnits(true),
103 		importUpAxis(true),
104 		mSelectionOnly(false),
105 
106 		mAnimationStart(TIME_INITIAL_POSE),
107 		mAnimationEnd(TIME_PosInfinity),
108 		mXRefOutputDir("C:\\Temp\\xref\\"),
109 		mExportUserDefinedProperties(false)
110 	{
111 
112 		// Load the export options from the configuration file
113 	//	LoadOptions();
114 	}
115 
116 	// Displays the exporter options dialog to allow the user to change the options.
ShowDialog()117 	bool Options::ShowDialog()
118 	{
119 
120 		LoadOptions();
121 		// Prompt the user with our dialogbox, and get all the options.
122 		bool doExport = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_EXPORT_OPTIONS), mMaxInterface->GetMAXHWnd(), ExportOptionsDlgProcS, (LPARAM)this) != FALSE;
123 
124 		if (!doExport)
125 			return false;
126 
127 		// Save the export options to the configuration file
128 		SaveOptions();
129 
130 		return true;
131 	}
132 
133 
134 	// options dialog message handler
ExportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM)135 	INT_PTR Options::ExportOptionsDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM)
136 	{
137 		ISpinnerControl* spin;
138 		//	int ticksPerFrame = GetTicksPerFrame();
139 		Interval animRange = mMaxInterface->GetAnimRange();
140 		int sceneStart = animRange.Start();
141 		int sceneEnd = animRange.End();
142 
143 		switch (message)
144 		{
145 		case WM_INITDIALOG: {
146 			CenterWindow(hWnd, GetParent(hWnd));
147 
148 			// grab scene timing details & clip anim start & end if needed
149 			if (mAnimationStart < sceneStart)
150 				mAnimationStart = sceneStart;
151 
152 			if (mAnimationStart > sceneEnd)
153 				mAnimationStart = sceneEnd;
154 
155 			if (mAnimationEnd < sceneStart)
156 				mAnimationEnd = sceneStart;
157 
158 			if (mAnimationEnd > sceneEnd)
159 				mAnimationEnd = sceneEnd;
160 
161 			// setup checkboxes
162 			CheckDlgButton(hWnd, IDC_GEOM_NORMALS, mNormals);
163 			CheckDlgButton(hWnd, IDC_GEOM_TRIANGLES, mTriangulate);
164 			CheckDlgButton(hWnd, IDC_GEOM_XREFS, mIncludeXrefs);
165 			CheckDlgButton(hWnd, IDC_GEOM_TANGENTS, mTangents);
166 			CheckDlgButton(hWnd, IDC_ANIM_ENABLE, mAnimations);
167 			CheckDlgButton(hWnd, IDC_ANIM_SAMPLE, mSampleAnimation);
168 			CheckDlgButton(hWnd, IDC_ANIM_CLIP, mCreateClip);
169 			CheckDlgButton(hWnd, IDC_LAYERS_TO_CLIPS, mLayersAsClips);
170 			CheckDlgButton(hWnd, IDC_BAKE_MATRICES, mBakeMatrices);
171 			CheckDlgButton(hWnd, IDC_RELATIVE_PATHS, mRelativePaths);
172 			CheckDlgButton(hWnd, IDC_COPY_IMAGES, mCopyImages);
173 			CheckDlgButton(hWnd, IDC_EXPORT_USER_PROPERTIES, mExportUserDefinedProperties);
174 
175 			// Animation checkboxes depend on the enable button.
176 			EnableDlgControl(hWnd, IDC_ANIM_SAMPLE, mAnimations);
177 			EnableDlgControl(hWnd, IDC_ANIM_CLIP, mAnimations);
178 
179 #if defined(MAX_RELEASE_R17) && (MAX_RELEASE >= MAX_RELEASE_R17)
180 			EnableDlgControl(hWnd, IDC_LAYERS_TO_CLIPS, mAnimations);
181 #else
182 			EnableDlgControl(hWnd, IDC_LAYERS_TO_CLIPS, false);
183 #endif
184 
185 			// setup spinners
186 			spin = GetISpinner(GetDlgItem(hWnd, IDC_ANIM_START_SPIN));
187 			spin->LinkToEdit(GetDlgItem(hWnd,IDC_ANIM_START), EDITTYPE_TIME);
188 			spin->SetLimits(sceneStart, sceneEnd, true);
189 			spin->SetScale(1.0f);
190 			spin->SetValue(mAnimationStart, true);
191 			spin->Enable(mSampleAnimation && mAnimations);
192 			EnableWindow(GetDlgItem(hWnd, IDC_START_LABEL), mSampleAnimation && mAnimations);
193 			ReleaseISpinner(spin);
194 
195 			spin = GetISpinner(GetDlgItem(hWnd, IDC_ANIM_END_SPIN));
196 			spin->LinkToEdit(GetDlgItem(hWnd,IDC_ANIM_END), EDITTYPE_TIME);
197 			spin->SetLimits(sceneStart, sceneEnd, true);
198 			spin->SetScale(1.0f);
199 			spin->SetValue(mAnimationEnd, false);
200 			spin->Enable(mSampleAnimation && mAnimations);
201 			EnableWindow(GetDlgItem(hWnd, IDC_END_LABEL), mSampleAnimation && mAnimations);
202 			ReleaseISpinner(spin);
203 
204 			return true;
205 							}
206 
207 		case WM_COMMAND:
208 			switch (LOWORD(wParam)) {
209 		case IDC_ANIM_ENABLE:
210 			mAnimations = IsDlgButtonChecked(hWnd, IDC_ANIM_ENABLE) == BST_CHECKED;
211 			EnableDlgControl(hWnd, IDC_ANIM_SAMPLE, mAnimations);
212 			EnableDlgControl(hWnd, IDC_ANIM_CLIP, mAnimations);
213 #if defined(MAX_RELEASE_R17) && (MAX_RELEASE >= MAX_RELEASE_R17)
214 			EnableDlgControl(hWnd, IDC_LAYERS_TO_CLIPS, mAnimations);
215 #else
216 			EnableDlgControl(hWnd, IDC_LAYERS_TO_CLIPS, false);
217 #endif
218 
219 			/*spin = GetISpinner(GetDlgItem(hWnd, IDC_ANIM_START_SPIN));
220 			//spin->LinkToEdit(GetDlgItem(hWnd,IDC_ANIM_START), EDITTYPE_INT);
221 			spin->Enable(sampleAnim && animations);
222 			EnableWindow(GetDlgItem(hWnd, IDC_START_LABEL), sampleAnim && animations);
223 			ReleaseISpinner(spin);
224 
225 			spin = GetISpinner(GetDlgItem(hWnd, IDC_ANIM_END_SPIN));
226 			//spin->LinkToEdit(GetDlgItem(hWnd,IDC_ANIM_END), EDITTYPE_INT);
227 			spin->Enable(sampleAnim && animations);
228 			EnableWindow(GetDlgItem(hWnd, IDC_END_LABEL), sampleAnim && animations);
229 			ReleaseISpinner(spin);
230 			break; */
231 
232 		case IDC_ANIM_SAMPLE:
233 			mSampleAnimation = IsDlgButtonChecked(hWnd, IDC_ANIM_SAMPLE) == BST_CHECKED;
234 
235 			spin = GetISpinner(GetDlgItem(hWnd, IDC_ANIM_START_SPIN));
236 			spin->Enable(mSampleAnimation && mAnimations);
237 			EnableWindow(GetDlgItem(hWnd, IDC_START_LABEL), mSampleAnimation && mAnimations);
238 			ReleaseISpinner(spin);
239 
240 			spin = GetISpinner(GetDlgItem(hWnd, IDC_ANIM_END_SPIN));
241 			spin->Enable(mSampleAnimation && mAnimations);
242 			EnableWindow(GetDlgItem(hWnd, IDC_END_LABEL), mSampleAnimation && mAnimations);
243 			ReleaseISpinner(spin);
244 			break;
245 
246 		case IDOK:
247 			// hit OK, pick up control values & end dialog
248 			mBakeMatrices = IsDlgButtonChecked(hWnd, IDC_BAKE_MATRICES) == BST_CHECKED;
249 			mRelativePaths = IsDlgButtonChecked(hWnd, IDC_RELATIVE_PATHS) == BST_CHECKED;
250 			mAnimations = IsDlgButtonChecked(hWnd, IDC_ANIM_ENABLE) == BST_CHECKED;
251 			mSampleAnimation = IsDlgButtonChecked(hWnd, IDC_ANIM_SAMPLE) == BST_CHECKED;
252 			mCreateClip = IsDlgButtonChecked(hWnd, IDC_ANIM_CLIP) == BST_CHECKED;
253 			mLayersAsClips = IsDlgButtonChecked(hWnd, IDC_LAYERS_TO_CLIPS) == BST_CHECKED;
254 			mNormals = IsDlgButtonChecked(hWnd, IDC_GEOM_NORMALS) == BST_CHECKED;
255 			mTriangulate = IsDlgButtonChecked(hWnd, IDC_GEOM_TRIANGLES) == BST_CHECKED;
256 			mIncludeXrefs = IsDlgButtonChecked(hWnd, IDC_GEOM_XREFS) == BST_CHECKED;
257 			mTangents = IsDlgButtonChecked(hWnd, IDC_GEOM_TANGENTS) == BST_CHECKED;
258 			mCopyImages = IsDlgButtonChecked(hWnd, IDC_COPY_IMAGES) == BST_CHECKED;
259 			mExportUserDefinedProperties = IsDlgButtonChecked(hWnd, IDC_EXPORT_USER_PROPERTIES) == BST_CHECKED;
260 
261 			spin = GetISpinner(GetDlgItem(hWnd, IDC_ANIM_START_SPIN));
262 			mAnimationStart = mSampleAnimation ? spin->GetIVal() : sceneStart;
263 			ReleaseISpinner(spin);
264 			spin = GetISpinner(GetDlgItem(hWnd, IDC_ANIM_END_SPIN));
265 			mAnimationEnd = mSampleAnimation ? spin->GetIVal() : sceneEnd;
266 			ReleaseISpinner(spin);
267 
268 			EndDialog(hWnd, 1);
269 			break;
270 
271 		case IDCANCEL:
272 			EndDialog(hWnd, 0);
273 			break;
274 			}
275 		default:
276 			return false;
277 		}
278 		return true;
279 	}
280 
281 
282 
283 	// options dialog proc
ExportOptionsDlgProcS(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)284 	INT_PTR CALLBACK Options::ExportOptionsDlgProcS(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
285 	{
286 		Options* exp;
287 		if (message == WM_INITDIALOG)
288 		{
289 			// record exp instance pointer for subsequent callbacks
290 			exp = (Options*) lParam;
291 			SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)lParam);
292 		}
293 		else
294 		{
295 			exp = (Options*)(size_t) GetWindowLongPtr(hWnd, GWLP_USERDATA);
296 		}
297 		// hand off to message-handler method
298 		return exp ? exp->ExportOptionsDlgProc(hWnd, message, wParam, lParam) : false;
299 	}
300 
301 
302 
SaveOptions()303 	void Options::SaveOptions()
304 	{
305 		NativeString configurationPath(NativeString(mMaxInterface->GetDir(APP_PLUGCFG_DIR)));
306 		URI configurationPathUri(URI::nativePathToUri(configurationPath.toUtf8String()));
307 		URI optionsFileUri(configurationPathUri, CONFIGURATION_FILE_NAME);
308 	//	NativeString filename = configurationPath + "\\" + CONFIGURATION_FILE_NAME;
309 		FILE* file;
310 		errno_t error = fopen_s(&file, optionsFileUri.toNativePath().c_str(), "wb");
311 		if ( error )
312 			return;
313 
314 
315 
316 		// Write down a standard INI header to allow MaxScript edition.
317 		String headerString("[" + CONFIGURATION_HEADER_NAME + "]\r\n");
318 		fwrite(headerString.data(), headerString.length(), 1, file);
319 
320 		// Write down the options, one by one, in the form: 'option=X\n'
321 		writeOption(file, OPTION_NORMALS_NAME, mNormals );
322 
323 		writeOption(file, OPTION_TRIANGULAT_NAME, mTriangulate );
324 		writeOption(file, OPTION_XREFS_NAME, mIncludeXrefs );
325 		writeOption(file, OPTION_TANGENTS_NAME, mTangents );
326 		writeOption(file, OPTION_ANIMATIONS_NAME, mAnimations );
327 		writeOption(file, OPTION_SAMPLEANIMATIONS_NAME, mSampleAnimation );
328 		writeOption(file, OPTION_CREATECLIP_NAME, mCreateClip );
329 		writeOption(file, OPTION_BAKEMATRICES_NAME, mBakeMatrices );
330 		writeOption(file, OPTION_RELATIVEPATHS_NAME, mRelativePaths );
331 		writeOption(file, OPTION_CHECKIFANIMATIONISANIMATED_NAME, mCheckIfAnimationIsAnimated );
332 		writeOption(file, OPTION_ANIMATIONSTART_NAME, mAnimationStart );
333 		writeOption(file, OPTION_ANIMATIONEND_NAME, mAnimationEnd );
334 		writeOption(file, OPTION_COPY_IMAGES_NAME, mCopyImages );
335 		writeOption(file, OPTION_EXPORT_USERDEFINED_PROPERTIES_NAME, mExportUserDefinedProperties );
336 
337 		fclose(file);
338 	}
339 
340 
341 
LoadOptions()342 	void Options::LoadOptions()
343 	{
344 		NativeString configurationPath(NativeString(mMaxInterface->GetDir(APP_PLUGCFG_DIR)));
345 		URI configurationPathUri(URI::nativePathToUri(configurationPath.toUtf8String()));
346 		URI optionsFileUri(configurationPathUri, CONFIGURATION_FILE_NAME);
347 
348 		FILE* file;
349 		errno_t error = fopen_s(&file, optionsFileUri.toNativePath().c_str(), "rb");
350 		if ( error )
351 			return;
352 
353 		// Read the whole configuration file
354 		fseek(file, 0, SEEK_END);
355 		int fileLength = ftell(file);
356 		fseek(file, 0, SEEK_SET);
357 		char* wholeFile = new char[fileLength + 1];
358 		fread(wholeFile, 1, fileLength, file);
359 		fclose(file);
360 		wholeFile[fileLength] = 0;
361 
362 		char *context;
363 		// Read in the options, one by one, in the form: 'option=X\n'
364 		char* token = strtok_s(wholeFile, "\n", &context);
365 		while (token )
366 		{
367 			// Skip whitespaces
368 			while (*token != 0 && (*token == ' ' || *token == '\r' || *token == '\t')) ++token;
369 			if (*token != 0)
370 			{
371 				if (*token == '[') {} // Standard INI header. Just skip the line for now.
372 
373 				// Read in the Property=Value lines.
374 				char* value = strchr(token, '=');
375 				if (value )
376 				{
377 					*(value++) = 0;
378 					while (*value != 0 && (*value == ' ' || *value == '\r' || *value == '\t')) ++value;
379 					if (*value != 0) // skip empty lines.
380 					{
381 						// Look for/read in the COLLADAMax options.
382 						readOption<bool>(token, OPTION_NORMALS_NAME, value, mNormals) ||
383 						readOption<bool>(token, OPTION_TRIANGULAT_NAME, value, mTriangulate) ||
384 						readOption<bool>(token, OPTION_XREFS_NAME, value, mIncludeXrefs) ||
385 						readOption<bool>(token, OPTION_TANGENTS_NAME, value, mTangents) ||
386 						readOption<bool>(token, OPTION_SAMPLEANIMATIONS_NAME, value, mSampleAnimation) ||
387 						readOption<bool>(token, OPTION_ANIMATIONS_NAME, value, mAnimations) ||
388 						readOption<bool>(token, OPTION_CREATECLIP_NAME, value, mCreateClip) ||
389 						readOption<bool>(token, OPTION_BAKEMATRICES_NAME, value, mBakeMatrices) ||
390 						readOption<bool>(token, OPTION_RELATIVEPATHS_NAME, value, mRelativePaths) ||
391 						readOption<bool>(token, OPTION_CHECKIFANIMATIONISANIMATED_NAME, value, mCheckIfAnimationIsAnimated) ||
392 						readOption<int>(token, OPTION_ANIMATIONSTART_NAME, value, mAnimationStart) ||
393 						readOption<int>(token, OPTION_ANIMATIONEND_NAME, value, mAnimationEnd)||
394 						readOption<bool>(token, OPTION_COPY_IMAGES_NAME, value, mCopyImages)||
395 						readOption<bool>(token, OPTION_EXPORT_USERDEFINED_PROPERTIES_NAME, value, mExportUserDefinedProperties);
396 
397 						{
398 							// Handle unknown option here.
399 						}
400 					}
401 				}
402 			}
403 
404 			token = strtok_s(0, "\n", &context);
405 		}
406 		delete[] wholeFile;
407 	}
408 
409 
410 
411 
412 }
413