1-- NW4C Menu Utilities(2010/12/07)
2-- Version 0.5.0d
3-- (c)2010 Nintendo
4
5struct nw4c_userData
6(
7	dataname = "",
8	type = String,
9	val = #(),
10	fn copyTo dst =
11	(
12		if (classof dst) != nw4c_userData do return false
13		dst.dataname = dataname
14		dst.type = type
15		dst.val = deepCopy val
16		return true
17	),
18	fn valueToString oneline:false =
19	(
20		local str = ""
21		for j = 1 to val.count do
22		(
23			if j != 1 do
24			(
25				str += if (not oneline) and (type == String) then "\n" else " "
26			)
27			str += (val[j] as string)
28		)
29		return str
30	),
31	fn valueCount =
32	(
33		local ret = 0
34		if type == String then
35		(
36			for v in val do
37			(
38				ret += v.count
39			)
40		)
41		else
42		(
43			ret = val.count
44		)
45		return ret
46	),
47	fn toString =
48	(
49		local str = dataname
50		str += "\""
51		str += case type of
52		(
53			Integer: "i"
54			Float: "f"
55			default: "s" -- string
56		)
57		str += " "
58		str += valueToString()
59		str += "\""
60		return str
61	),
62	fn fromString s =
63	(
64		local tokens = filterString s "\""
65		if tokens.count != 2 do return false
66		dataname = tokens[1]
67
68		local valStr = tokens[2]
69		case valStr[1] of
70		(
71			"i": type = Integer
72			"f": type = Float
73			default: type = String
74		)
75		val = #( )
76
77		local valTokens = #()
78
79		if type == String then
80		(
81			valTokens = filterString (substring valStr 3 -1) "\n"
82		)
83		else
84		(
85			valTokens = filterString (substring valStr 3 -1) " "
86		)
87
88		for i = 1 to valTokens.count do
89		(
90			local v = case type of
91			(
92				Integer: (valTokens[i] as Integer)
93				Float: (valTokens[i] as Float)
94				default: (valTokens[i])
95			)
96			if v != undefined do
97			(
98				append val v
99			)
100		)
101		return true
102	)
103)
104
105struct nw4c_userDataSet
106(
107	list = #(),
108	fn appendData data =
109	(
110		if (classof data) != nw4c_userData do return false
111		append list data
112	),
113	fn toString =
114	(
115		local ret = ""
116		if (classof list) != Array do return ""
117		for i = 1 to list.count do
118		(
119			local data = list[i]
120			if (classof data) != nw4c_userData do continue
121			if i != 1 do ret += " "
122			ret += data.toString()
123		)
124		return ret
125	),
126	fn fromString s =
127	(
128		if (classof s) != String do return false
129		list = #()
130		local p = 1
131		while p <= s.count do
132		(
133			while s[p] == " " and p < s.count do p += 1 -- �X�y�[�X���X�L�b�v
134			local ep = p
135			while s[ep] != "\"" and ep < s.count do ep += 1 -- �ŏ���"��T��
136			ep += 1
137			while s[ep] != "\"" and ep <= s.count do ep += 1 -- ���"��T��
138			if ep <= s.count do
139			(
140				local len = ep - p + 1
141				local d = nw4c_userData()
142				if (d.fromString (substring s p len) ) do
143				(
144					append list d
145				)
146			)
147			p = ep + 1
148		)
149		return true
150	),
151	fn getFromAttribute attr =
152	(
153		list = #()
154	),
155	fn setToAttribute attr =
156	(
157	)
158)
159
160struct nw4c_utils
161(
162	enableSaveToScene = true,
163
164	fn outComment fs str =
165	(
166		format "# %\n" str to:fs
167	),
168
169	fn outReturn fs=
170	(
171		format "\n" to:fs
172	),
173
174	fn outValue fs key v =
175	(
176		format "%=\"%\"\n" key (v as string) to:fs
177	),
178
179	fn outVersion fs key v =
180	(
181		local str = ""
182		for i = 1 to 3 do
183		(
184			local d = (mod v 10) as integer
185			v = (v - d) / 10
186			str = (d as string) + str
187			if i != 3 do str = "." + str
188		)
189		outValue fs key str
190	),
191
192	fn outQuantValue fs key v =
193	(
194		local str = case v of
195		(
196			3: "Byte"
197			2: "Short"
198			default: "Float"
199		)
200
201		outValue fs key str
202	),
203
204	fn QuantStrToInt str =
205	(
206		local val = toLower str
207		case val of
208		(
209			"byte": 3
210			"short": 2
211			default: 1
212		)
213	),
214
215	fn loadSettingFromFile filename isDefault:false =
216	(
217		local nw4c = nw4cmax
218		if nw4c == undefined do return false
219		if filename == undefined do return false
220		local fs = openFile filename mode:"rt"
221		if fs == undefined do
222		(
223			format "File Open Error(%)\n" filename
224			return false
225		)
226		-- read header
227		if (findString (readLine fs) "NW4C_Export settings") == undefined do return false
228		local str, key, val, ver = 0
229		local isEOF = false
230		while isEOF == false do
231		(
232			str = readLine fs
233			-- skip comment and null line
234			if (str.count < 1) or (str[1] == "#") do continue
235			local elem = filterString str "="
236			if (classof elem != Array) or elem.count != 2 do continue
237			key = elem[1]
238			val = substituteString elem[2] "\"" ""
239			--format "\"%\"=\"%\"\n" key val
240			case key of
241			(
242				"ExportTarget": (
243					nw4c.doesExportSelected = ((stricmp val "Selection") == 0)
244				)
245				"OutputFileName": nw4c.filename = val
246				"OutputFolder": nw4c.outFolder = val
247				"OutputMode": nw4c.UseCreativeStudio = ((stricmp val "CreativeStudio") == 0)
248				"MergeCmdl": nw4c.UseMerge = val as booleanclass
249				"MergeCmdlPath": nw4c.MergeFilename = val
250				"Magnify": nw4c.Magnify = val as float
251				"FrameRange": nw4c.doesExportAllFrames =  ((stricmp val "Range") != 0)
252				"StartFrame": nw4c.StartFrame = val as integer
253				"EndFrame": nw4c.EndFrame = val as integer
254				"UseFigureMode": nw4c.useFiguremode = val as booleanclass
255				"OutputCmdl": nw4c.doesExportModel = val as booleanclass
256				"OutputCtex": nw4c.doesExportTexture = val as booleanclass
257				"OutputCskla": nw4c.doesExportAnimation = val as booleanclass
258				"OutputCmata": nw4c.doesExportMtlAnim = val as booleanclass
259				"OutputCcam": nw4c.doesExportCamera = val as booleanclass
260				"OutputClgt": nw4c.doesExportLight = val as booleanclass
261				"OutputCenv": nw4c.doesExportEnv = val as booleanclass
262				"OutputCmdla": nw4c.doesExportModelAnim = val as booleanclass
263				"CompressNode": (
264					nw4c.CompressNode = case val of
265					(
266						"Cull": 2
267						"CullUninfluential": 3
268						"UniteCompressible": 4
269						"UniteAll": 5
270						default: 1
271					)
272				)
273				"CompressMaterial": ()--nw4c.
274				"OptimizePrimitive": nw4c.OptimizePrimitive = val as booleanclass
275				"ConvertToModel": ( -- 0.5.0���狓�����ς�����̂ŁA����ȑO�̃t���O�͖���
276					format "version %\n" ver
277					if ver < 130 then
278					(
279						--print "old file is load.\n convert to model flag is ignored."
280						nw4c.DisableModelSimplification = true
281					)
282					else
283					(
284						nw4c.DisableModelSimplification = not (val as booleanclass)
285					)
286				)
287				"QuantizePos": nw4c.QuantPos = QuantStrToInt val
288				"QuantizeNrm": nw4c.QuantNormal = QuantStrToInt val
289				"QuantizeTex": nw4c.QuantTex = QuantStrToInt val
290				"NonUniformScale": nw4c.NonUniformScale = val as booleanclass
291				"MaxReservedUniformRegisters": nw4c.ReservedUniformRegister = val as integer
292				"MeshVisibilityMode":  nw4c.VisibilityBindByName = ((stricmp val "BindByName") == 0)
293				"AdjustSkinning": nw4c.SkinningMode = if (val as booleanclass) then 2 else 1
294
295				"BakeAllAnim": ()
296				"FramePrecision": (
297					local fval = val as float
298					if fval > 0.0 do
299					(
300						local ival = (1.0 / fval) as integer
301						nw4c.AnimPrecision = case of
302						(
303							(ival <= 1): 1
304							(ival <= 2): 2
305							(ival <= 5): 5
306							default: 10
307						)
308					)
309				 )
310				"LoopAnim": nw4c.IsLoop = val as booleanclass
311				"FrameFormat": nw4c.IsFrameFormat = val as booleanclass
312				"BinPrecisionScale": ()--nw4c.PrecisionScale = val as float
313				"BinPrecisionRotate": ()--nw4c.PrecisionRotate = val as float
314				"BinPrecisionTranslate": ()--nw4c.PrecisionTrans = val as float
315				"ScaleQuantizeQuality": nw4c.ScaleQuality = val as integer
316				"RotateQuantizeQuality": nw4c.RotateQuality = val as integer
317				"TranslateQuantizeQuality": nw4c.TransQuality = val as integer
318
319				"ToleranceScale": nw4c.ToleranceScale = val as float
320				"ToleranceRotate": nw4c.ToleranceRotate = val as float
321				"ToleranceTranslate": nw4c.ToleranceTrans = val as float
322				"ToleranceUVScale": nw4c.ToleranceUVScale = val as float
323				"ToleranceUVRotate": nw4c.ToleranceUVRotate = val as float
324				"ToleranceUVTranslate": nw4c.ToleranceUVTrans = val as float
325				"ToleranceColor": nw4c.ToleranceColor= val as float
326
327				"3dsmax_SaveToScene": (
328					 --if isDefault do enableSaveToScene = val as booleanclass
329				 )
330				 "SettingsVersion": (
331					ver = (substituteString val "." "") as integer
332				 )
333				 "GeneratorName": ()
334				 "GeneratorVersion": ()
335				 "Date": ()
336				 default: format "WARNING: unknown key found in c3es(%=%)\n" key val
337			)
338			--detect eof
339			if eof fs do
340			(
341				isEOF = true
342			)
343		)
344
345		return true
346	),
347
348	fn saveSettingToFile filename isDefault:false =
349	(
350		local nw4c = nw4cmax
351		if nw4c == undefined do return false
352		if filename == undefined do return false
353		local fs = openFile filename mode:"wt"
354		if fs == undefined do
355		(
356			format "File Open Error(%)\n" filename
357			return false
358		)
359		outComment fs "NW4C_Export settings"
360		local ver = nw4c.GetVersion()
361		outVersion fs "SettingsVersion" 130
362		local str = "3ds Max "
363		append str (((maxVersion())[1] / 1000 + 1998) as string)
364		append str " NW4C_Export"
365		outValue fs "GeneratorName" str
366		outVersion fs "GeneratorVersion" ver
367		local dt = getLocalTime()
368		local ss = stringStream ""
369		format "%-%-%T%:%:%" dt[1] dt[2] dt[4] dt[5] dt[6] dt[7]  to:ss
370		outValue fs "Date" (ss as string)
371		if isDefault do
372		(
373			--outValue fs "3dsmax_SaveToScene" enableSaveToScene
374		)
375		outReturn fs
376		--
377		outComment fs "Output Options"
378		outValue fs "ExportTarget" (if nw4c.doesExportSelected then "Selection" else "All")
379		outValue fs "OutputFileName" nw4c.filename
380		outValue fs "OutputMode" (if nw4c.UseCreativeStudio then "CreativeStudio" else "File")
381		outValue fs "OutputFolder" nw4c.outFolder
382		outValue fs "MergeCmdl" nw4c.UseMerge
383		outValue fs "MergeCmdlPath" nw4c.MergeFilename
384		outReturn fs
385		--
386		outComment fs "General Options"
387		outValue fs "Magnify" nw4c.Magnify
388		outValue fs "FrameRange" (if nw4c.doesExportAllFrames then "All" else "Range")
389		outValue fs "StartFrame" nw4c.StartFrame
390		outValue fs "EndFrame" nw4c.EndFrame
391		outValue fs "UseFigureMode" nw4c.useFiguremode
392		outReturn fs
393		--
394		outComment fs "Output File Selection"
395		outValue fs "OutputCmdl" nw4c.doesExportModel
396		outValue fs "OutputCtex" nw4c.doesExportTexture
397		outValue fs "OutputCskla" nw4c.doesExportAnimation
398		outValue fs "OutputCmata" nw4c.doesExportMtlAnim
399		outValue fs "OutputCcam" nw4c.doesExportCamera
400		outValue fs "OutputClgt" nw4c.doesExportLight
401		outValue fs "OutputCenv" nw4c.doesExportEnv
402		outValue fs "OutputCmdla" nw4c.doesExportModelAnim
403		outReturn fs
404		--
405		outComment fs "Optimization Options"
406		outValue fs "CompressNode" ( case nw4c.CompressNode of
407			(
408				2: "Cull"
409				3: "CullUninfluential"
410				4: "UniteCompressible"
411				5: "UniteAll"
412				default: "None"
413			)
414		)
415		outValue fs "CompressMaterial" nw4c.OptimizeMaterial
416		outValue fs "OptimizePrimitive" nw4c.OptimizePrimitive
417		outValue fs "ConvertToModel" (not nw4c.DisableModelSimplification)
418		outReturn fs
419		--
420		outComment fs "Quantization Options"
421		outQuantValue fs "QuantizePos" nw4c.QuantPos
422		outQuantValue fs "QuantizeNrm" nw4c.QuantNormal
423		outQuantValue fs "QuantizeTex" nw4c.QuantTex
424		outReturn fs
425		--
426		outComment fs "Model Options"
427		outValue fs "NonUniformScale" nw4c.NonUniformScale
428		outValue fs "MaxReservedUniformRegisters" nw4c.ReservedUniformRegister
429		outValue fs "MeshVisibilityMode" ( if nw4c.VisibilityBindByName then "BindByName" else "BindByIndex")
430
431
432		outValue fs "AdjustSkinning" (nw4c.SkinningMode == 2)
433
434		outReturn fs
435		--
436		outComment fs "Animation Options"
437		outValue fs "BakeAllAnim" true
438		outValue fs "FramePrecision" (1.0 / nw4c.AnimPrecision)
439		outValue fs "LoopAnim" nw4c.IsLoop
440		outValue fs "FrameFormat" nw4c.IsFrameFormat
441		--outValue fs "BinPrecisionScale" nw4c.PrecisionScale
442		--outValue fs "BinPrecisionRotate" nw4c.PrecisionRotate
443		--outValue fs "BinPrecisionTranslate" nw4c.PrecisionTrans
444		outValue fs	"ScaleQuantizeQuality"  nw4c.ScaleQuality
445		outValue fs	"RotateQuantizeQuality"  nw4c.RotateQuality
446		outValue fs	"TranslateQuantizeQuality"  nw4c.TransQuality
447		outReturn fs
448		--
449		outComment fs "Tolerance Options"
450		outValue fs "ToleranceScale" nw4c.ToleranceScale
451		outValue fs "ToleranceRotate" nw4c.ToleranceRotate
452		outValue fs "ToleranceTranslate" nw4c.ToleranceTrans
453		outValue fs "ToleranceUVScale" nw4c.ToleranceUVScale
454		outValue fs "ToleranceUVRotate" nw4c.ToleranceUVRotate
455		outValue fs "ToleranceUVTranslate" nw4c.ToleranceUVTrans
456		outValue fs "ToleranceColor" nw4c.ToleranceColor
457
458		close fs
459		return true
460	),
461
462	fn loadSettingConfig =
463	(
464		local fname = GetDir #plugcfg
465		append fname "\\nw4cmax.ini"
466		local val = getINISetting fname "nw4c_exporter" "enableSaveToScene"
467		enableSaveToScene = if val == "false" then false else true
468	),
469
470	fn saveSettingConfig =
471	(
472		local fname = GetDir #plugcfg
473		append fname "\\nw4cmax.ini"
474		setINISetting fname "nw4c_exporter" "enableSaveToScene" (enableSaveToScene as string)
475	),
476
477	fn loadSettingDefault =
478	(
479		local fname = GetDir #plugcfg
480		append fname "\\nw4cmax.c3es"
481		if doesFileExist fname then
482		(
483			loadSettingFromFile fname isDefault:true
484			loadSettingConfig()
485		)
486		else
487		(
488			false
489		)
490	),
491
492	fn saveSettingDefault =
493	(
494		local fname = GetDir #plugcfg
495		append fname "\\nw4cmax.c3es"
496		saveSettingToFile fname isDefault:true
497		saveSettingConfig()
498	)
499)
500
501-- common user data setting
502nw4cEditUserDataRollout_Value = undefined
503nw4cEditUserDataRollout_ValueSet = undefined
504
505rollout nw4cEditUserDataRollout "NW4C Edit User Data" width:320 height:320
506(
507	editText editName "Name" align:#left
508	radioButtons rdoType "Type"  labels:#("String", "Integer", "Float") columns:3 align:#left
509	--label lblString "String" align:#left enabled:false
510	--editText editString "" align:#left
511	label lblNumber "String or Number : (One value per line)" align:#left
512	editText editNumber "" height: 200 align:#left
513	label lbl3 "" across:3
514	button btnOK "Ok" width:80
515	button btnCancel "Cancel" width:80
516
517	fn isValidName n =
518	(
519		if (classof n) != String do return false
520		local c, f = true
521		for i = 1 to n.count while f do
522		(
523			c = n[i]
524			f = false
525			f = f or ("A" <= c and c <= "Z") or ("a" <= c and c <= "z")
526			f = f or ("0" <= c and c <= "9")
527			f = f or (c == "-") or (c == "_") or (c == ".")
528		)
529		if (not f) do
530		(
531			messagebox ("invalid character used in name(" + c + ")")
532			return false
533		)
534		-- find same name
535		local list = nw4cEditUserDataRollout_ValueSet.list
536		for d in list do
537		(
538			if d != nw4cEditUserDataRollout_Value and d.dataname == n do -- �����f�[�^�ւ̎Q�Ƃ͏���
539			(
540				messagebox ("Can't use same name")
541				return false
542			)
543		)
544		return true
545	)
546
547	fn isValidString s =
548	(
549		if (classof s) != String do return false
550		local c, f = true
551		for i = 1 to s.count while f do
552		(
553			c = s[i]
554			f = (c == "<") or (c == ">") or (c == "&") or (c == "'") or (c == "\"")
555			if f do
556			(
557				messagebox ("invalid character used in String(" + c + ")")
558				return false
559			)
560		)
561		return true
562	)
563
564	fn updateView =
565	(
566		if 	(classof nw4cEditUserDataRollout_Value) == nw4c_userData and \
567			(classof nw4cEditUserDataRollout_ValueSet) == nw4c_userDataSet do
568		(
569			local data = nw4cEditUserDataRollout_Value
570			editName.text = data.dataname
571			rdoType.state = case data.type of
572			(
573				Integer: 2
574				Float: 3
575				default: 1 -- string
576			)
577/*			if data.type == String then
578			(
579				editString.text  = if (classof data.val[1]) == String then data.val[1] else ""
580			)
581			else
582*/
583			(
584				local str = ""
585				for i = 1 to data.val.count do
586				(
587					str += (data.val[i] as String) + "\n"
588				)
589				editNumber.text = str
590			)
591		)
592	)
593
594	on nw4cEditUserDataRollout open do
595	(
596		if 	(classof nw4cEditUserDataRollout_Value) == nw4c_userData and \
597			(classof nw4cEditUserDataRollout_ValueSet) == nw4c_userDataSet then
598		(
599			updateView()
600		)
601		else
602		(
603			messagebox "value error"
604			DestroyDialog nw4cEditUserDataRollout
605		)
606	)
607
608	on btnOK pressed do
609	(
610		if (classof nw4cEditUserDataRollout_Value) == nw4c_userData do
611		(
612			local data = nw4c_userData()
613			data.type = case rdoType.state of
614			(
615				2: Integer
616				3: Float
617				default: String
618			)
619			if editName.text.count == 0 do
620			(
621				messagebox "Name is Empty"
622				return()
623			)
624			if (not (isValidName editName.text)) do
625			(
626				return()
627			)
628			data.dataname = editName.text
629
630			local vs = filterString  editNumber.text "\n"
631			if vs.count == 0 do
632			(
633				messagebox "Number is Empty"
634				return()
635			)
636
637			for i = 1 to vs.count do
638			(
639				local v = case data.type of
640				(
641					Integer: (vs[i] as Integer)
642					Float: (vs[i] as Float)
643					default: (vs[i] as String)
644				)
645				if v == undefined do
646				(
647					local mes = "Value is not valid(" + vs[i] + ")"
648					messagebox mes
649					return()
650				)
651				append data.val v
652			)
653			if data.val.count == 0 do
654			(
655				messagebox "Number is Empty"
656				return()
657			)
658			data.copyTo nw4cEditUserDataRollout_Value
659		)
660		DestroyDialog nw4cEditUserDataRollout
661	)
662
663	on btnCancel pressed do
664	(
665		nw4cEditUserDataRollout_Value = undefined
666		DestroyDialog nw4cEditUserDataRollout
667	)
668	on rdoType changed val do
669	(
670/*
671		editString.enabled = (val == 1)
672		lblString.enabled = (val == 1)
673		editNumber.enabled = (val != 1)
674		lblNumber.enabled = (val != 1)
675*/
676		)
677)
678