├── LICENSE
├── advancedfx_export_bvh.py
├── readme.txt
├── advancedfx_import_bvh.py
└── advancedfx_import_gameRecord.py
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 advancedfx.org
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/advancedfx_export_bvh.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) advancedfx.org
2 | #
3 | # Last changes:
4 | # 2016-06-28 by dominik.matrixstorm.com
5 | #
6 | # First changes:
7 | # 2009-09-03 by dominik.matrixstorm.com
8 |
9 |
10 | # 57.29577951308232087679815481410...
11 | RAD2DEG = 57.2957795130823208767981548141
12 |
13 |
14 |
15 | import sfm;
16 | import sfmUtils;
17 | import sfmApp;
18 | from PySide import QtGui
19 |
20 |
21 | def SetError(error):
22 | print 'ERROR:', error
23 | QtGui.QMessageBox.warning( None, "ERROR:", error )
24 |
25 |
26 | # Formats a float value to be suitable for bvh output
27 | def FloatToBvhString(value):
28 | return "{0:f}".format(value)
29 |
30 |
31 | def WriteHeader(file, frames, frameTime):
32 | file.write("HIERARCHY\n")
33 | file.write("ROOT MdtCam\n")
34 | file.write("{\n")
35 | file.write("\tOFFSET 0.00 0.00 0.00\n")
36 | file.write("\tCHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation\n")
37 | file.write("\tEnd Site\n")
38 | file.write("\t{\n")
39 | file.write("\t\tOFFSET 0.00 0.00 -1.00\n")
40 | file.write("\t}\n")
41 | file.write("}\n")
42 | file.write("MOTION\n")
43 | file.write("Frames: "+str(frames)+"\n")
44 | file.write("Frame Time: "+FloatToBvhString(frameTime)+"\n")
45 |
46 | def LimDeg(val):
47 | return val
48 |
49 | def WriteFile(fileName, scale):
50 | shot = sfm.GetCurrentShot()
51 | animSet = sfm.GetCurrentAnimationSet()
52 |
53 | dag = sfm.FindDag("transform")
54 |
55 | if dag == None:
56 | SetError("Selected animation set does not have transform DAG node.")
57 | return False
58 |
59 | curFrame = 0
60 | fps = sfmApp.GetFramesPerSecond()
61 | frameTime = fps
62 | if not 0 == frameTime:
63 | frameTime = 1.0/float(frameTime)
64 | frameCount = shot.GetDuration().CurrentFrame(vs.DmeFramerate_t(fps))
65 |
66 | file = open(fileName, 'wb')
67 | if not file:
68 | SetError('Could not open file '+fileName+' for writing')
69 | return False
70 |
71 | oldFrame = sfm.GetCurrentFrame()
72 | try:
73 | WriteHeader(file, frameCount, frameTime)
74 |
75 | while curFrame advancedfx_import_bvh
16 |
17 | It is possible to import into an camera with existing keyframes, keyframes
18 | in the imported timespan will be removed.
19 |
20 | The import will not set the correct FOV for the camera (the BVH file also does
21 | not contain FOV information), that is left up to the user.
22 | Also be aware that CS:GO actually uses an higher FOV than you set in-game:
23 | http://advancedfx.style-productions.net/forum/viewtopic.php?f=17&t=1811
24 |
25 |
26 |
27 |
28 | How to use advancedfx_export_bvh:
29 |
30 | 1. Select a clip
31 | 5. Right click the camera you want to export in Animation Set Editor and
32 | select Rig -> advancedfx_export_bvh
33 |
34 | The FPS exported is determined by your project output FPS.
35 |
36 |
37 |
38 |
39 | How to use advancedfx_import_gameRecord:
40 |
41 | Create an afxGameRecording in HLAE / AfxHookSource, preferably with
42 | low FPS / host_framerate (i.e. 30) and not too long,
43 | because otherwise you will run out of memory upon importing
44 | into SFM!
45 | You can do that using the "mirv_streams record agr" command in HLAE /
46 | AfxHookSource.
47 |
48 | To import the recording in SFM:
49 |
50 | 1. Create an dummy animation set (i.e. camera) on the clip where you want to
51 | import
52 | 2. Right click the created set and in Animation Set Editor and
53 | select Rig -> advancedfx_import_gameRecord
54 |
55 | Attention:
56 |
57 | Automatic importing of faulty / broken models (i.e. from the
58 | models\player\custom_player\legacy folder, which is used in older de_cache
59 | demos for example) will cause memory
60 | corruptions that usually lead to a crash of SFM.
61 | To avoid those crashes, don't make such models available to SFM!
62 |
63 | Notes:
64 |
65 | In the current version keyframes will only be created when the
66 | gameModel is visible, otherwise we save some memory.
67 |
68 | Currently this feature is mainly meant to import player models.
69 | It will import some viewmodels too, however those
70 | might be missing arms or have the Error model in SFM, due to being
71 | a custom (skin) model (modelName is '?' then).
72 |
73 |
74 |
75 | Changelog:
76 |
77 | 1.7.2 (2022-07-03T13:03Z):
78 | - advancedfx_import_gameRecord:
79 | - Updated to support AGR version 6.
80 | Please note: AGR version 6 can express more than SFM in terms of matrices for models and model bones,
81 | if the matrices can not be converted to vector position + quaternion rotation, then there will be
82 | problems. E.g. mirrored (view) models would be a problem.
83 |
84 | 1.7.1 (2020-12-14T18:18Z):
85 | - advancedfx_import_gameRecord:
86 | - Fixed error in script that could cause import to fail in some conditions.
87 |
88 | 1.7.0 (2020-08-11T19:14Z):
89 | - advancedfx_import_gameRecord:
90 | - Updated to AGR version 5
91 |
92 | 1.6.0 (2017-09-16T22:00Z):
93 | - advancedfx_import_gameRecord:
94 | - Updated to AGR version 4
95 |
96 | 1.5.0 (2017-09-13T17:26Z):
97 | - advancedfx_import_gameRecord:
98 | - Updated to AGR version 3
99 |
100 |
101 | 1.4.0 (2017-08-03T12:00Z):
102 | - advancedfx_import_gameRecord:
103 | - Updated to agr version 2 (also bug fixes)
104 |
105 | 1.3.0 (2017-06-26T05:07Z):
106 | - advancedfx_import_gameRecord:
107 | - Updated to agr version 1
108 |
109 | 1.2.1 (2016-07-16T10:43Z):
110 | - advancedfx_import_gameRecord:
111 | - Now sets gameModel.evaluateProceduralBones = False, to avoid player
112 | models wrapping and hopefully also avoid crashing SFM (this is
113 | relevant for demos using the new models in
114 |
115 | 1.2.0 (2016-07-15T13:37Z):
116 | - advancedfx_import_gameRecord:
117 | - Improved for AfxHookSource 1.6.0, now handles entity delete envents
118 | properly and model switching.
119 |
120 | 1.1.0 (2016-07-15T13:37Z):
121 | - advancedfx_import_gameRecord:
122 | - First version
123 | - advancedfx_import_bvh:
124 | - Does not create bookmarks for keys anymore, in order to save memory
125 | (you can add and edit bookmarks manually).
126 |
127 | 1.0.1 (2016-06-30T19:10Z):
128 | - advancedfx_import_bvh:
129 | - Now un-pushes the Snap / Snap Frame tool buttons in timeline if required.
130 |
131 | 1.0.0 (2016-06-28T21:16Z):
132 | - advancedfx_import_bvh:
133 | - Minor imporvements
134 | - advancedfx_export_bvh:
135 | - First version
136 |
137 | 0.1.0 (2016-06-28T06:07Z):
138 | - advancedfx_import_bvh:
139 | - Fixed awkward camera movment / rolling
140 | - Now allows to import into existing graph data (so you can append / insert
141 | into existing camera)
142 |
143 | 0.0.1 (2016-06-27T20:58Z):
144 | - advancedfx_import_bvh:
145 | - First version
146 |
--------------------------------------------------------------------------------
/advancedfx_import_bvh.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) advancedfx.org
2 | #
3 | # Last changes:
4 | # 2016-07-14 by dominik.matrixstorm.com
5 | #
6 | # First changes:
7 | # 2009-09-01 by dominik.matrixstorm.com
8 |
9 |
10 | # DEG2RAD = 0.0174532925199432957692369076849
11 | # PI = 3.14159265358979323846264338328
12 |
13 |
14 | import sfm;
15 | import sfmUtils;
16 | import sfmApp;
17 | from PySide import QtGui
18 |
19 |
20 | def SetError(error):
21 | print 'ERROR:', error
22 | QtGui.QMessageBox.warning( None, "ERROR:", error )
23 |
24 |
25 | # reads a line from file and separates it into words by splitting whitespace
26 | # file to read from
27 | # list of words
28 | def ReadLineWords(file):
29 | line = file.readline()
30 | words = [ll for ll in line.split() if ll]
31 | return words
32 |
33 |
34 | # searches a list of words for a word by lower case comparison
35 | # list to search
36 | # word to find
37 | # less than 0 if not found, otherwise the first list index
38 | def FindWordL(words, word):
39 | i = 0
40 | word = word.lower()
41 | while i < len(words):
42 | if words[i].lower() == word:
43 | return i
44 | i += 1
45 | return -1
46 |
47 |
48 | # Scans the file till a line containing a lower case match of filterWord is found
49 | # file to read from
50 | # word to find
51 | # False on fail, otherwise same as ReadLineWords for this line
52 | def ReadLineWordsFilterL(file, filterWord):
53 | while True:
54 | words = ReadLineWords(file)
55 | if 0 < len(words):
56 | if 0 <= FindWordL(words, filterWord):
57 | return words
58 | else:
59 | return False
60 |
61 |
62 | # Scans the file till the channels line and reads channel information
63 | # file to read from
64 | # False on fail, otherwise channel indexes as follows: [Xposition, Yposition, Zposition, Zrotation, Xrotation, Yrotation]
65 | def ReadChannels(file):
66 | words = ReadLineWordsFilterL(file, 'CHANNELS')
67 |
68 | if not words:
69 | return False
70 |
71 | channels = [\
72 | FindWordL(words, 'Xposition'),\
73 | FindWordL(words, 'Yposition'),\
74 | FindWordL(words, 'Zposition'),\
75 | FindWordL(words, 'Zrotation'),\
76 | FindWordL(words, 'Xrotation'),\
77 | FindWordL(words, 'Yrotation')\
78 | ]
79 |
80 | idx = 0
81 | while idx < 6:
82 | channels[idx] -= 2
83 | idx += 1
84 |
85 | for channel in channels:
86 | if not (0 <= channel and channel < 6):
87 | return False
88 |
89 | return channels
90 |
91 |
92 | def ReadRootName(file):
93 | words = ReadLineWordsFilterL(file, 'ROOT')
94 |
95 | if not words or len(words)<2:
96 | return False
97 |
98 | return words[1]
99 |
100 |
101 | def ReadFrames(file):
102 | words = ReadLineWordsFilterL(file, 'Frames:')
103 |
104 | if not words or len(words)<2:
105 | return -1
106 |
107 | return int(words[1])
108 |
109 | def ReadFrameTime(file):
110 | words = ReadLineWordsFilterL(file, 'Time:')
111 |
112 | if not words or len(words)<3:
113 | return -1
114 |
115 | return float(words[2])
116 |
117 |
118 | def ReadFrame(file, channels):
119 | line = ReadLineWords(file)
120 |
121 | if len(line) < 6:
122 | return False;
123 |
124 | Xpos = float(line[channels[0]])
125 | Ypos = float(line[channels[1]])
126 | Zpos = float(line[channels[2]])
127 | Zrot = float(line[channels[3]])
128 | Xrot = float(line[channels[4]])
129 | Yrot = float(line[channels[5]])
130 |
131 | return [Xpos, Ypos, Zpos, Zrot, Xrot, Yrot]
132 |
133 |
134 | def ClearBookmarks(log, component, start, end):
135 | idx = 0
136 | while idx < log.GetNumBookmarks(component):
137 | time = log.GetBookmarkTime(component, idx)
138 | if(start <= time and time <= end):
139 | log.RemoveBookmark(component, time)
140 | else:
141 | idx = idx + 1
142 |
143 |
144 | def ClearKeys(log, start, end):
145 | idx = 0
146 | while idx < log.GetKeyCount():
147 | time = log.GetKeyTime(idx)
148 | if(start <= time and time <= end):
149 | log.RemoveKey(idx)
150 | else:
151 | idx = idx + 1
152 |
153 |
154 | def ReadFile(fileName, scale, camFov):
155 | shot = sfm.GetCurrentShot()
156 | animSet = sfm.GetCurrentAnimationSet()
157 |
158 | channelsClip = sfmUtils.GetChannelsClipForAnimSet(animSet, shot)
159 |
160 | if channelsClip == None:
161 | SetError("Selected animation set does not have channels clip.")
162 | return False
163 |
164 | rootControlGroup = animSet.GetRootControlGroup()
165 |
166 | if None == rootControlGroup:
167 | SetError("Selected animation set does not have rootControlGroup.")
168 | return False
169 |
170 | transformCtrl = rootControlGroup.FindControlByName("transform", True)
171 |
172 | if None == transformCtrl:
173 | SetError("Selected animation set does not have transform control.")
174 | return False
175 |
176 | positionChan = transformCtrl.GetPositionChannel()
177 | orientationChan = transformCtrl.GetOrientationChannel()
178 |
179 | file = open(fileName, 'rU')
180 |
181 | rootName = ReadRootName(file)
182 | if not rootName:
183 | SetError('Failed parsing ROOT.')
184 | return False
185 |
186 | print 'ROOT:', rootName
187 |
188 | channels = ReadChannels(file)
189 | if not channels:
190 | SetError('Failed parsing CHANNELS.')
191 | return False
192 |
193 | frames = ReadFrames(file);
194 | if frames < 0:
195 | SetError('Failed parsing Frames.')
196 | return False
197 |
198 | if 0 == frames: frames = 1
199 |
200 | frames = float(frames)
201 |
202 | frameTime = ReadFrameTime(file)
203 | if not frameTime:
204 | SetError('Failed parsing Frame Time.')
205 | return False
206 |
207 | timeOffset = (shot.ToChildMediaTime(vs.DmeTime_t(sfmApp.GetHeadTimeInSeconds()),False) -channelsClip.timeFrame.start.GetValue())
208 |
209 | # Prepare curves:
210 | timeStart = timeOffset
211 | timeEnd = vs.DmeTime_t(float(frameTime) * float(frames)) +timeOffset
212 | ClearKeys(positionChan.log,timeStart,timeEnd)
213 | ClearBookmarks(positionChan.log,0,timeStart,timeEnd)
214 | ClearBookmarks(positionChan.log,1,timeStart,timeEnd)
215 | ClearBookmarks(positionChan.log,2,timeStart,timeEnd)
216 | ClearKeys(orientationChan.log,timeStart,timeEnd)
217 | ClearBookmarks(orientationChan.log,0,timeStart,timeEnd)
218 | ClearBookmarks(orientationChan.log,1,timeStart,timeEnd)
219 | ClearBookmarks(orientationChan.log,2,timeStart,timeEnd)
220 |
221 | frameCount = float(0)
222 |
223 | lastQuat = None
224 |
225 | while True:
226 | frame = ReadFrame(file, channels)
227 | if not frame:
228 | break
229 |
230 | frameCount += 1
231 |
232 | BTT = vs.DmeTime_t(float(frameTime) * float(frameCount-1)) +timeOffset
233 |
234 | BYP = -frame[0] *scale
235 | BZP = frame[1] *scale
236 | BXP = -frame[2] *scale
237 |
238 | BZR = -frame[3]
239 | BXR = -frame[4]
240 | BYR = frame[5]
241 |
242 | positionChan.log.SetKey(BTT, vs.Vector(BXP, BYP, BZP))
243 | # positionChan.log.AddBookmark(BTT, 0) # We cannot afford bookmarks (waste of memory)
244 | # positionChan.log.AddBookmark(BTT, 1) # We cannot afford bookmarks (waste of memory)
245 | # positionChan.log.AddBookmark(BTT, 2) # We cannot afford bookmarks (waste of memory)
246 |
247 | quat = vs.Quaternion()
248 | vs.AngleQuaternion(vs.QAngle(BXR,BYR,BZR), quat)
249 |
250 | # Make sure we travel the short way:
251 | if lastQuat:
252 | dp = vs.QuaternionDotProduct(lastQuat,quat)
253 | if dp < 0:
254 | quat = vs.Quaternion(-quat.x,-quat.y,-quat.z,-quat.w)
255 |
256 | lastQuat = quat
257 |
258 | orientationChan.log.SetKey(BTT, quat)
259 | # orientationChan.log.AddBookmark(BTT, 0) # We cannot afford bookmarks (waste of memory)
260 | # orientationChan.log.AddBookmark(BTT, 1) # We cannot afford bookmarks (waste of memory)
261 | # orientationChan.log.AddBookmark(BTT, 2) # We cannot afford bookmarks (waste of memory)
262 |
263 | if not frameCount == frames:
264 | SetError("Frames are missing in BVH file.")
265 | return False
266 |
267 | return True
268 |
269 | def GetSnapButtons():
270 | snap = None
271 | snapFrame = None
272 | for x in sfmApp.GetMainWindow().findChildren(QtGui.QToolButton):
273 | toolTip = x.toolTip()
274 | if "Snap" == toolTip:
275 | snap = x
276 | else:
277 | if "Snap Frame" == toolTip:
278 | snapFrame = x
279 |
280 | return snap, snapFrame
281 |
282 | def ImportCamera():
283 | fileName, _ = QtGui.QFileDialog.getOpenFileName(None, "Open HLAE BVH File", "", "HLAE BVH (*.bvh)")
284 | if not 0 < len(fileName):
285 | return
286 |
287 | oldTimelineMode = sfmApp.GetTimelineMode()
288 |
289 | snap, snapFrame = GetSnapButtons()
290 |
291 | snapChecked = snap.isChecked()
292 | snapFrameChecked = snapFrame.isChecked()
293 |
294 | try:
295 | sfmApp.SetTimelineMode(3) # Work around timeline bookmark update bug (can't be in Graph Editor or it won't update)
296 |
297 | # Work around bug/feature causing programatically inserted keyframes to be snapped:
298 | if(snapChecked):
299 | snap.click()
300 | if(snapFrameChecked):
301 | snapFrame.click()
302 |
303 | if ReadFile(fileName, 1.0, 90.0):
304 | print 'Done.'
305 | else:
306 | print 'FAILED'
307 |
308 | finally:
309 | checked = ""
310 | if(snapFrameChecked):
311 | #snapFrame.click() # if we unheck here it will still snap ...
312 | checked = "'Snap Frame'"
313 | if(snapChecked):
314 | #snap.click() # if we uncheck here it will still snap ...
315 | if 0 < len(checked):
316 | checked = " and " + checked
317 | checked = "'Snap'" + checked
318 |
319 | if 0 < len(checked):
320 | QtGui.QMessageBox.information( None, "Attention", "Had to un-push " + checked + " tool button in timeline! Push again (if wanted)." )
321 |
322 | sfmApp.SetTimelineMode(oldTimelineMode)
323 |
324 | ImportCamera()
325 |
--------------------------------------------------------------------------------
/advancedfx_import_gameRecord.py:
--------------------------------------------------------------------------------
1 |
2 | # DEG2RAD = 0.0174532925199432957692369076849
3 | # PI = 3.14159265358979323846264338328
4 |
5 | import sfm
6 | import sfmUtils
7 | import sfmApp
8 | from PySide import QtGui
9 | import gc
10 | import struct
11 | import math
12 |
13 | def SetError(error):
14 | print 'ERROR:', error
15 | QtGui.QMessageBox.warning( None, "ERROR:", error )
16 |
17 | # Slow as fuck!
18 | def FindChannel(channels,name):
19 | for i in channels:
20 | if name == i.GetName():
21 | return i
22 | return None
23 |
24 | def InitalizeAnimSet(animSet,makeVisibleChannel = True):
25 | shot = sfm.GetCurrentShot()
26 |
27 | channelsClip = sfmUtils.GetChannelsClipForAnimSet(animSet, shot)
28 |
29 | visibleChannel = None
30 |
31 | if makeVisibleChannel:
32 | # Ensure additional channels:
33 | visibleChannel = FindChannel(channelsClip.channels,'visible_channel')
34 | if visibleChannel is None:
35 | visibleChannel = sfmUtils.CreateControlAndChannel('visible', vs.AT_BOOL, False, animSet, shot).channel
36 | visibleChannel.mode = 3
37 | visibleChannel.fromElement = animSet.gameModel
38 | visibleChannel.fromAttribute = 'visible'
39 | visibleChannel.toElement = animSet.gameModel
40 | visibleChannel.toAttribute = 'visible'
41 |
42 | # clear channel logs:
43 | for chan in channelsClip.channels:
44 | chan.ClearLog()
45 |
46 | if visibleChannel:
47 | # Not visible initially:
48 | visibleChannel.log.SetKey(-channelsClip.timeFrame.start.GetValue(), False)
49 |
50 | class ChannelCache:
51 | dict = {}
52 |
53 | def GetChannel(self,animSet,channelName):
54 | key = animSet.GetName() + channelName
55 | value = self.dict.get(key, False)
56 | if False == value:
57 | channelsClip = sfmUtils.GetChannelsClipForAnimSet(animSet, sfm.GetCurrentShot())
58 | value = FindChannel(channelsClip.channels, channelName)
59 | self.dict[key] = value
60 |
61 | return value
62 |
63 | def MakeKeyFrameValue(channelCache,animSet,channelName,time,value):
64 | chan = channelCache.GetChannel(animSet, channelName)
65 | chan.log.SetKey(time, value)
66 |
67 | def MakeKeyFrameTransform(channelCache,animSet,channelName,time,vec,quat,shortestPath=False,posSuffix='_p',rotSuffix='_o'):
68 | positionChan = channelCache.GetChannel(animSet, channelName+posSuffix)
69 | orientationChan = channelCache.GetChannel(animSet, channelName+rotSuffix)
70 |
71 | positionChan.log.SetKey(time, vec)
72 | # positionChan.log.AddBookmark(time, 0) # We cannot afford bookmarks (waste of memory)
73 | # positionChan.log.AddBookmark(time, 1) # We cannot afford bookmarks (waste of memory)
74 | # positionChan.log.AddBookmark(time, 2) # We cannot afford bookmarks (waste of memory)
75 |
76 | if(shortestPath):
77 | # Make sure we travel the short way:
78 | lastQuatKeyIdx = orientationChan.log.FindKey(time)
79 | if(0 <= lastQuatKeyIdx and lastQuatKeyIdx < orientationChan.log.GetKeyCount()):
80 | lastQuat = orientationChan.log.GetValue(orientationChan.log.GetKeyTime(lastQuatKeyIdx))
81 | dp = vs.QuaternionDotProduct(lastQuat,quat)
82 | if dp < 0:
83 | quat = vs.Quaternion(-quat.x,-quat.y,-quat.z,-quat.w)
84 |
85 | orientationChan.log.SetKey(time, quat)
86 | # orientationChan.log.AddBookmark(time, 0) # We cannot afford bookmarks (waste of memory)
87 | # orientationChan.log.AddBookmark(time, 1) # We cannot afford bookmarks (waste of memory)
88 | # orientationChan.log.AddBookmark(time, 2) # We cannot afford bookmarks (waste of memory)
89 |
90 | def QuaternionFromQAngle(qangle):
91 | quat = vs.Quaternion()
92 | vs.AngleQuaternion(qangle, quat)
93 | return quat
94 |
95 | def Quaternion(x,y,z,w):
96 | quat = vs.Quaternion()
97 | quat.x = x;
98 | quat.y = y;
99 | quat.z = z;
100 | quat.w = w
101 | return quat
102 |
103 | def ReadString(file):
104 | buf = bytearray()
105 | while True:
106 | b = file.read(1)
107 | if len(b) < 1:
108 | return None
109 | elif b == '\0':
110 | return str(buf)
111 | else:
112 | buf.append(b[0])
113 |
114 | def ReadBool(file):
115 | buf = file.read(1)
116 | if(len(buf) < 1):
117 | return None
118 | return struct.unpack('', buf)[0]
119 |
120 | def ReadInt(file):
121 | buf = file.read(4)
122 | if(len(buf) < 4):
123 | return None
124 | return struct.unpack(' 60 else dagName
450 | dagName = "afx." +str(modelHandle.objNr)+ " " + dagName
451 |
452 | dagAnimSet = sfmUtils.CreateModelAnimationSet(dagName,modelName)
453 |
454 | if(hasattr(dagAnimSet,'gameModel')):
455 | dagAnimSet.gameModel.evaluateProceduralBones = False # This will look awkwardly and crash SFM otherwise
456 |
457 | print "Initalizing animSet " + dagName
458 | InitalizeAnimSet(dagAnimSet)
459 |
460 | modelHandle.modelData = dagAnimSet
461 |
462 | modelHandle.lastRenderOrigin = renderOrigin
463 |
464 | MakeKeyFrameValue(channelCache, dagAnimSet, 'visible_channel', timeConverter.GetTime(sfmUtils.GetChannelsClipForAnimSet(dagAnimSet, shot)), visible)
465 |
466 | MakeKeyFrameTransform(channelCache, dagAnimSet, "rootTransform", timeConverter.GetTime(sfmUtils.GetChannelsClipForAnimSet(dagAnimSet, shot)), renderOrigin, renderRotation, True)
467 |
468 | if dict.Peekaboo(file,'baseanimating'):
469 | #skin = ReadInt(file)
470 | #body = ReadInt(file)
471 | #sequence = ReadInt(file)
472 | hasBoneList = ReadBool(file)
473 | if hasBoneList:
474 | dagModel = None
475 | if dagAnimSet is not None and hasattr(dagAnimSet,'gameModel'):
476 | dagModel = dagAnimSet.gameModel
477 |
478 | numBones = ReadInt(file)
479 |
480 | for i in xrange(numBones):
481 | vec = vs.Vector(0,0,0)
482 | quat = vs.Quaternion(0,0,0,1)
483 | if version == 6:
484 | matrix3x4 = ReadMatrix3x4(file)
485 | vs.mathlib.MatrixPosition(matrix3x4,vec)
486 | vs.mathlib.MatrixQuaternion(matrix3x4,quat)
487 | else:
488 | vec = ReadVector(file)
489 | quat = ReadQuaternion(file)
490 |
491 | if dagModel is None:
492 | continue
493 |
494 | if(i < len(dagModel.bones)):
495 | name = dagModel.bones[i].GetName()
496 | #print name
497 |
498 | name = name[name.find('(')+1:]
499 | name = name[:name.find(')')]
500 | #print name
501 |
502 | MakeKeyFrameTransform(channelCache, dagAnimSet, name, timeConverter.GetTime(sfmUtils.GetChannelsClipForAnimSet(dagAnimSet, shot)), vec, quat)
503 |
504 | if dict.Peekaboo(file,'camera'):
505 | thidPerson = ReadBool(file)
506 | renderOrigin = ReadVector(file)
507 | renderAngles = ReadQAngle(file)
508 | fov = ReadFloat(file)
509 | fov = fov / 180.0
510 |
511 | modelCamera = modelHandle.camera
512 | if modelCamera is None:
513 | camName = "camera."+str(modelHandle.objNr)
514 | dmeAfxCam = vs.CreateElement( "DmeCamera", camName, shot.GetFileId())
515 | modelCamera = sfm.CreateAnimationSet( camName, target=dmeAfxCam)
516 | InitalizeAnimSet(modelCamera,makeVisibleChannel=False)
517 | channelsClip = sfmUtils.GetChannelsClipForAnimSet(modelCamera, sfm.GetCurrentShot())
518 | scaled_fieldOfView_channel = FindChannel(channelsClip.channels, "scaled_fieldOfView_channel")
519 | scaled_fieldOfView_channel.fromElement.lo = 0
520 | scaled_fieldOfView_channel.fromElement.hi = 180
521 | shot.scene.GetChild(shot.scene.FindChild("Cameras")).AddChild(dmeAfxCam)
522 | modelHandle.camera = modelCamera
523 |
524 | MakeKeyFrameValue(channelCache, modelCamera, 'fieldOfView', timeConverter.GetTime(sfmUtils.GetChannelsClipForAnimSet(modelCamera, shot)), fov)
525 | MakeKeyFrameTransform(channelCache, modelCamera, 'transform', timeConverter.GetTime(sfmUtils.GetChannelsClipForAnimSet(modelCamera, shot)), renderOrigin, QuaternionFromQAngle(renderAngles), True, '_pos', '_rot')
526 |
527 | dict.Peekaboo(file,'/')
528 |
529 | viewModel = ReadBool(file)
530 |
531 | elif 'afxCam' == node0:
532 |
533 | if afxCam is None:
534 | dmeAfxCam = vs.CreateElement( "DmeCamera", "afxCam", shot.GetFileId())
535 | afxCam = sfm.CreateAnimationSet( "afxCam", target=dmeAfxCam)
536 | InitalizeAnimSet(afxCam,makeVisibleChannel=False)
537 | channelsClip = sfmUtils.GetChannelsClipForAnimSet(afxCam, sfm.GetCurrentShot())
538 | scaled_fieldOfView_channel = FindChannel(channelsClip.channels, "scaled_fieldOfView_channel")
539 | scaled_fieldOfView_channel.fromElement.lo = 0
540 | scaled_fieldOfView_channel.fromElement.hi = 180
541 | shot.scene.GetChild(shot.scene.FindChild("Cameras")).AddChild(dmeAfxCam)
542 |
543 | renderOrigin = ReadVector(file)
544 | renderAngles = ReadQAngle(file)
545 | fov = ReadFloat(file)
546 | fov = fov / 180.0
547 |
548 | MakeKeyFrameValue(channelCache, afxCam, 'fieldOfView', timeConverter.GetTime(sfmUtils.GetChannelsClipForAnimSet(afxCam, shot)), fov)
549 | MakeKeyFrameTransform(channelCache, afxCam, 'transform', timeConverter.GetTime(sfmUtils.GetChannelsClipForAnimSet(afxCam, shot)), renderOrigin, QuaternionFromQAngle(renderAngles), True, '_pos', '_rot')
550 |
551 | else:
552 | SetError('Unknown packet: ')
553 | return False
554 | finally:
555 | if file is not None:
556 | file.close()
557 |
558 | return True
559 |
560 | def GetSnapButtons():
561 | snap = None
562 | snapFrame = None
563 | for x in sfmApp.GetMainWindow().findChildren(QtGui.QToolButton):
564 | toolTip = x.toolTip()
565 | if "Snap" == toolTip:
566 | snap = x
567 | else:
568 | if "Snap Frame" == toolTip:
569 | snapFrame = x
570 |
571 | return snap, snapFrame
572 |
573 | def ImportGameRecord():
574 | fileName, _ = QtGui.QFileDialog.getOpenFileName(None, "Open HLAE GameRecord File", "", "afxGameRecord (*.agr)")
575 | if not 0 < len(fileName):
576 | return
577 |
578 | oldTimelineMode = sfmApp.GetTimelineMode()
579 |
580 | snap, snapFrame = GetSnapButtons()
581 |
582 | snapChecked = snap.isChecked()
583 | snapFrameChecked = snapFrame.isChecked()
584 |
585 | try:
586 | gc.collect() # Free memory, since we need much of it
587 |
588 | #sfm.SetOperationMode("Record")
589 | sfmApp.SetTimelineMode(3) # Work around timeline bookmark update bug (can't be in Graph Editor or it won't update)
590 |
591 | # Work around bug/feature causing programatically inserted keyframes to be snapped:
592 | if(snapChecked):
593 | snap.click()
594 | if(snapFrameChecked):
595 | snapFrame.click()
596 |
597 | if ReadFile(fileName):
598 | print 'Done.'
599 | else:
600 | print 'FAILED'
601 |
602 | finally:
603 | gc.collect() # Free memory, since we needed much of it
604 |
605 | checked = ""
606 | if(snapFrameChecked):
607 | #snapFrame.click() # if we unheck here it will still snap ...
608 | checked = "'Snap Frame'"
609 | if(snapChecked):
610 | #snap.click() # if we uncheck here it will still snap ...
611 | if 0 < len(checked):
612 | checked = " and " + checked
613 | checked = "'Snap'" + checked
614 |
615 | if 0 < len(checked):
616 | QtGui.QMessageBox.information( None, "Attention", "Had to un-push " + checked + " tool button in timeline! Push again (if wanted)." )
617 |
618 | sfmApp.SetTimelineMode(oldTimelineMode)
619 | #sfm.SetOperationMode("Pass")
620 |
621 | ImportGameRecord()
622 |
--------------------------------------------------------------------------------