| 1 | bl_info = { |
|---|
| 2 | "name": "Framsticks POV import & camera manipulator", |
|---|
| 3 | "author": "Szymon Ulatowski, jm soler", |
|---|
| 4 | "blender": (2, 70, 0), |
|---|
| 5 | "location": "File > Import-Export", |
|---|
| 6 | "category": "Import-Export", |
|---|
| 7 | "description": "Imports POV-Ray files generated by Framsticks and exports Blender camera to POV-Ray files", |
|---|
| 8 | "wiki_url": "http://www.framsticks.com/3d-animations-in-blender" |
|---|
| 9 | } |
|---|
| 10 | |
|---|
| 11 | import os, math, bpy, re, copy, functools |
|---|
| 12 | from bpy_extras.io_utils import ImportHelper |
|---|
| 13 | from bpy.props import (CollectionProperty, StringProperty, BoolProperty, EnumProperty, FloatProperty) |
|---|
| 14 | |
|---|
| 15 | re_field=re.compile('^#declare field_([^_]+)_([^=]+)=(.*)$') |
|---|
| 16 | re_object=re.compile('^BeginObject\(\)$') |
|---|
| 17 | re_part=re.compile('^BeginObject\(\)$') |
|---|
| 18 | re_partgeo=re.compile('^PartGeometry\(<([^>]+)>,<([^>]+)>\)$') |
|---|
| 19 | re_joint=re.compile('^BeginJoint\(([0-9]+),([0-9]+)\)$') |
|---|
| 20 | re_jointgeo=re.compile('^JointGeometry\(<([^>]+)>,<([^>]+)>,<([^>]+)>\)$') |
|---|
| 21 | re_neuro=re.compile('^BeginNeuro\(([^)])\)$') |
|---|
| 22 | |
|---|
| 23 | Folder = "" |
|---|
| 24 | FirstPOV=-1 |
|---|
| 25 | LastPOV=-1 |
|---|
| 26 | CREATURES={} |
|---|
| 27 | |
|---|
| 28 | # assumes the filename is "*_number.pov" |
|---|
| 29 | def numFromFilename(f): |
|---|
| 30 | try: |
|---|
| 31 | return int(f[f.find('_')+1:f.find('.')]) |
|---|
| 32 | except: |
|---|
| 33 | return -1 |
|---|
| 34 | |
|---|
| 35 | # creates a global dictionary of filenames ("*_number.pov"), where key=number, updates FirstPOV/LastPOV |
|---|
| 36 | def scanDir(startfile): |
|---|
| 37 | global Files, Folder, FileName |
|---|
| 38 | global FirstPOV, LastPOV |
|---|
| 39 | |
|---|
| 40 | Files={} |
|---|
| 41 | Folder, FileName=os.path.split(startfile) |
|---|
| 42 | |
|---|
| 43 | #print("startfile=",startfile,"Folder=",Folder,"FileName=",FileName) |
|---|
| 44 | |
|---|
| 45 | underscore=FileName.find("_") |
|---|
| 46 | if underscore==-1: |
|---|
| 47 | return |
|---|
| 48 | if FileName.find(".pov")==-1: |
|---|
| 49 | return |
|---|
| 50 | |
|---|
| 51 | FirstPOV=numFromFilename(FileName) |
|---|
| 52 | LastPOV=FirstPOV |
|---|
| 53 | |
|---|
| 54 | for f in os.listdir(Folder): |
|---|
| 55 | if not f.endswith('.pov'): |
|---|
| 56 | continue |
|---|
| 57 | if not f.startswith(FileName[:underscore+1]): |
|---|
| 58 | continue |
|---|
| 59 | num=numFromFilename(f) |
|---|
| 60 | if num<0: |
|---|
| 61 | continue |
|---|
| 62 | Files[num]=f |
|---|
| 63 | if num>LastPOV: |
|---|
| 64 | LastPOV=num |
|---|
| 65 | #print("N=",len(Files)) |
|---|
| 66 | |
|---|
| 67 | def extractValue(val): |
|---|
| 68 | if val[0]=='"': |
|---|
| 69 | # "string" |
|---|
| 70 | return val[1:-1] |
|---|
| 71 | if val.find('.')!=-1 or val.find('e')!=-1: |
|---|
| 72 | # floating point |
|---|
| 73 | return float(val) |
|---|
| 74 | else: |
|---|
| 75 | # integer |
|---|
| 76 | return int(val) |
|---|
| 77 | |
|---|
| 78 | def floatList(str): |
|---|
| 79 | return [float(x) for x in str.split(',')] |
|---|
| 80 | |
|---|
| 81 | def analysePOV(fname): |
|---|
| 82 | global re_field,re_object,re_part,re_partgeo,re_joint,re_jointgeo,re_neuro |
|---|
| 83 | f=open(fname,'r',encoding='latin-1') |
|---|
| 84 | POVlines=f.readlines() |
|---|
| 85 | f.close() |
|---|
| 86 | tmpfields={} |
|---|
| 87 | objects={} |
|---|
| 88 | for line in POVlines: |
|---|
| 89 | m=re_field.match(line) |
|---|
| 90 | if m: |
|---|
| 91 | value=m.group(3) |
|---|
| 92 | if value.endswith(';'): |
|---|
| 93 | value=value[:-1] |
|---|
| 94 | value=extractValue(value) |
|---|
| 95 | objname=m.group(1) # creature,m,p,j,n |
|---|
| 96 | fieldname=m.group(2) |
|---|
| 97 | if not objname in tmpfields: |
|---|
| 98 | tmpfields[objname]={} |
|---|
| 99 | tmpfields[objname][fieldname]=value |
|---|
| 100 | #print("obj=",m.group(1)," name=",m.group(2)," value=",value) |
|---|
| 101 | m=re_object.match(line) |
|---|
| 102 | if m: |
|---|
| 103 | objkey=tmpfields['Creature']['name']+'_'+tmpfields['Creature']['uid'] |
|---|
| 104 | objkey=objkey.replace(' ','_') |
|---|
| 105 | recentobj={'fields':copy.deepcopy(tmpfields['Creature']),'parts':[],'joints':[],'neurons':[]} |
|---|
| 106 | objects[objkey]=recentobj |
|---|
| 107 | m=re_jointgeo.match(line) |
|---|
| 108 | if m: |
|---|
| 109 | joint={'StickLoc1':floatList(m.group(1)), |
|---|
| 110 | 'StickLoc2':floatList(m.group(2)), |
|---|
| 111 | 'StickRot':floatList(m.group(3)), |
|---|
| 112 | 'fields':copy.deepcopy(tmpfields['j'])} |
|---|
| 113 | #print(joint) |
|---|
| 114 | recentobj['joints'].append(joint) |
|---|
| 115 | m=re_partgeo.match(line) |
|---|
| 116 | if m: |
|---|
| 117 | part={'Loc':floatList(m.group(1)), |
|---|
| 118 | 'Rot':floatList(m.group(2)), |
|---|
| 119 | 'fields':copy.deepcopy(tmpfields['p'])} |
|---|
| 120 | #print(joint) |
|---|
| 121 | recentobj['parts'].append(part) |
|---|
| 122 | #print(tmpfields) |
|---|
| 123 | #print(objects) |
|---|
| 124 | #print(json.dumps(objects,indent=4)) |
|---|
| 125 | return objects |
|---|
| 126 | |
|---|
| 127 | # vector length |
|---|
| 128 | def vecLength(p1,p2): |
|---|
| 129 | p2[0]=p2[0]-p1[0] |
|---|
| 130 | p2[1]=p2[1]-p1[1] |
|---|
| 131 | p2[2]=p2[2]-p1[2] |
|---|
| 132 | return (p2[0]**2+p2[1]**2+p2[2]**2)**0.5 |
|---|
| 133 | |
|---|
| 134 | def vecSub(p1,p2): |
|---|
| 135 | return [p1[0]-p2[0], p1[1]-p2[1], p1[2]-p2[2]] |
|---|
| 136 | |
|---|
| 137 | def vecMul(p1,m): |
|---|
| 138 | return [p1[0]*m, p1[1]*m, p1[2]*m] |
|---|
| 139 | |
|---|
| 140 | # create an object containing reference to blender cylinder object |
|---|
| 141 | class cylindre: |
|---|
| 142 | def __init__(self,nom='cylinderModel',type='cyl',r1=0.1, r2=0.1,h=1.0, n=8,smooth=1): |
|---|
| 143 | me=bpy.data.meshes.new(name=nom) |
|---|
| 144 | r=[r1,r2] |
|---|
| 145 | verts=[] |
|---|
| 146 | for i in range(0,2): |
|---|
| 147 | for j in range(0,n): |
|---|
| 148 | z=math.sin(j*math.pi*2/(n))*r[i] |
|---|
| 149 | y=math.cos(j*math.pi*2/(n))*r[i] |
|---|
| 150 | x=float(i)*h |
|---|
| 151 | verts.append((x,y,z)) |
|---|
| 152 | |
|---|
| 153 | vlist=[v for v in range(n)] |
|---|
| 154 | vlist.append(0) |
|---|
| 155 | faces=[] |
|---|
| 156 | for i in range(n): |
|---|
| 157 | faces.append((vlist[i],vlist[i+1],vlist[i+1]+n,vlist[i]+n)) |
|---|
| 158 | |
|---|
| 159 | if type=='cyl': |
|---|
| 160 | pos=[[0.0,0.0,0.0],[0.0,0.0,h]] |
|---|
| 161 | verts.append((pos[0][0],pos[0][1],pos[0][2])) |
|---|
| 162 | verts.append((pos[1][0],pos[1][1],pos[1][2])) |
|---|
| 163 | |
|---|
| 164 | for i in range(n): |
|---|
| 165 | faces.append((vlist[i],vlist[i+1],len(vlist)-2)) |
|---|
| 166 | faces.append((vlist[i],vlist[i+1],len(vlist)-1)) |
|---|
| 167 | |
|---|
| 168 | me.from_pydata(verts,[],faces) |
|---|
| 169 | me.update() |
|---|
| 170 | self.objet=bpy.data.objects.new(nom,me) |
|---|
| 171 | bpy.context.scene.objects.link(self.objet) |
|---|
| 172 | |
|---|
| 173 | # build or update blender objects from a POV file |
|---|
| 174 | def updateBlender(SceneParent,num): |
|---|
| 175 | global Folder, Files, Current, RecentlyCreated, RecentlyDisappeared |
|---|
| 176 | Incoming=analysePOV(os.path.join(Folder,Files[num])) |
|---|
| 177 | RecentlyCreated=[] |
|---|
| 178 | RecentlyDisappeared=[] |
|---|
| 179 | for oname,obj in Incoming.items(): |
|---|
| 180 | if not oname in Current: |
|---|
| 181 | # add object properties |
|---|
| 182 | print('Creature added:',oname) |
|---|
| 183 | RecentlyCreated.append(oname) |
|---|
| 184 | newobj=[] # will contain: [ parent, joint0, joint1, ... ] |
|---|
| 185 | Current[oname]=newobj |
|---|
| 186 | # create new blender objects |
|---|
| 187 | bcrea=bpy.data.objects.new(oname,None) |
|---|
| 188 | bpy.context.scene.objects.link(bcrea) |
|---|
| 189 | bcrea.parent=SceneParent |
|---|
| 190 | newobj.append(bcrea) |
|---|
| 191 | for j in obj['joints']: |
|---|
| 192 | cyl=cylindre(oname+'_j','tube',0.1,0.1,1.0,6) |
|---|
| 193 | cyl.objet.parent=bcrea |
|---|
| 194 | newobj.append(cyl.objet) |
|---|
| 195 | |
|---|
| 196 | # update blender loc/rot/scale |
|---|
| 197 | existing_b=Current[oname] |
|---|
| 198 | if len(obj['joints']): |
|---|
| 199 | avg_loc=vecMul(functools.reduce(lambda a,b: [a[0]+b[0],a[1]+b[1],a[2]+b[2]], [j['StickLoc1'] for j in obj['joints']]),1/len(obj['joints'])) |
|---|
| 200 | elif len(obj['parts']): |
|---|
| 201 | avg_loc=vecMul(functools.reduce(lambda a,b: [a[0]+b[0],a[1]+b[1],a[2]+b[2]], [p['Loc'] for p in obj['parts']]),1/len(obj['parts'])) |
|---|
| 202 | else: |
|---|
| 203 | avg_loc=[0,0,0] |
|---|
| 204 | if len(existing_b)>0: |
|---|
| 205 | existing_b[0].location=avg_loc |
|---|
| 206 | existing_b[0].keyframe_insert(data_path='location',frame=bpy.context.scene.frame_current) |
|---|
| 207 | for i in range(len(obj['joints'])): |
|---|
| 208 | if i>=(len(existing_b)-1): |
|---|
| 209 | continue # number of joints has changed -> ignore |
|---|
| 210 | incoming_geo=obj['joints'][i] |
|---|
| 211 | #print('incoming:',incoming_geo) |
|---|
| 212 | bo=existing_b[i+1] # blender object |
|---|
| 213 | scale=[vecLength(incoming_geo['StickLoc1'],incoming_geo['StickLoc2']), 1.0, 1.0] |
|---|
| 214 | for xyz in [0,1,2]: |
|---|
| 215 | getattr(bo,'location')[xyz]=vecSub(incoming_geo['StickLoc1'],avg_loc)[xyz] |
|---|
| 216 | #getattr(bo,'location')[xyz]=incoming_geo['StickLoc1'][xyz] |
|---|
| 217 | getattr(bo,'rotation_euler')[xyz]=incoming_geo['StickRot'][xyz] |
|---|
| 218 | getattr(bo,'scale')[xyz]=scale[xyz] |
|---|
| 219 | for field in ['location','rotation_euler','scale']: |
|---|
| 220 | bo.keyframe_insert(data_path=field,frame=bpy.context.scene.frame_current) |
|---|
| 221 | for oname,obj in Current.items(): |
|---|
| 222 | if not oname in Incoming: |
|---|
| 223 | RecentlyDisappeared.append(oname) |
|---|
| 224 | print('Creature disappeared:',oname) |
|---|
| 225 | |
|---|
| 226 | # import a sequence of POV files, create object hiererchy, animate |
|---|
| 227 | def framsImport(startfile): |
|---|
| 228 | global FirstPOV, LastPOV, Files |
|---|
| 229 | global Folder, FileName |
|---|
| 230 | global Current, FirstFrame, FrameCount |
|---|
| 231 | global RecentlyCreated, RecentlyDisappeared |
|---|
| 232 | global SceneParent |
|---|
| 233 | global SkipFrames |
|---|
| 234 | |
|---|
| 235 | scanDir(startfile) |
|---|
| 236 | |
|---|
| 237 | if len(Files)<1: |
|---|
| 238 | print("No files found") |
|---|
| 239 | return |
|---|
| 240 | |
|---|
| 241 | bpy.context.scene.frame_end=max(bpy.context.scene.frame_end,FirstFrame+FrameCount-1) |
|---|
| 242 | |
|---|
| 243 | SceneParent=bpy.data.objects.new("Framsticks_"+str(FirstFrame),None) |
|---|
| 244 | bpy.context.scene.objects.link(SceneParent) |
|---|
| 245 | SceneParent.framspov_file=startfile |
|---|
| 246 | SceneParent.framspov_frame=FirstFrame |
|---|
| 247 | SceneParent.framspov_count=FrameCount |
|---|
| 248 | |
|---|
| 249 | Current={} |
|---|
| 250 | NextSkip=0 |
|---|
| 251 | for k in sorted(Files.keys()): |
|---|
| 252 | if k<NextSkip: |
|---|
| 253 | continue |
|---|
| 254 | NextSkip=k+SkipFrames |
|---|
| 255 | bpy.context.scene.frame_set(FirstFrame-FirstPOV+k) |
|---|
| 256 | if bpy.context.scene.frame_current >= FirstFrame+FrameCount: |
|---|
| 257 | break |
|---|
| 258 | print("Frame %d - loading POV %s" % (bpy.context.scene.frame_current,Files[k])) |
|---|
| 259 | updateBlender(SceneParent,k) |
|---|
| 260 | if len(RecentlyDisappeared)>0 or len(RecentlyCreated)>0: |
|---|
| 261 | bpy.context.scene.frame_set(FirstFrame-FirstPOV+k-1) |
|---|
| 262 | for oname in RecentlyCreated: |
|---|
| 263 | obj=Current[oname] |
|---|
| 264 | for bo in obj: |
|---|
| 265 | bo.hide=True |
|---|
| 266 | bo.keyframe_insert(data_path="hide",frame=bpy.context.scene.frame_current) |
|---|
| 267 | for oname in RecentlyDisappeared: |
|---|
| 268 | obj=Current[oname] |
|---|
| 269 | for bo in obj: |
|---|
| 270 | bo.hide=False |
|---|
| 271 | bo.keyframe_insert(data_path="hide",frame=bpy.context.scene.frame_current) |
|---|
| 272 | bpy.context.scene.frame_set(FirstFrame-FirstPOV+k) |
|---|
| 273 | for oname in RecentlyCreated: |
|---|
| 274 | obj=Current[oname] |
|---|
| 275 | for bo in obj: |
|---|
| 276 | bo.hide=False |
|---|
| 277 | bo.keyframe_insert(data_path="hide",frame=bpy.context.scene.frame_current) |
|---|
| 278 | for oname in RecentlyDisappeared: |
|---|
| 279 | obj=Current[oname] |
|---|
| 280 | for bo in obj: |
|---|
| 281 | bo.hide=True |
|---|
| 282 | bo.keyframe_insert(data_path="hide",frame=bpy.context.scene.frame_current) |
|---|
| 283 | Current.pop(oname) |
|---|
| 284 | |
|---|
| 285 | |
|---|
| 286 | ############################### |
|---|
| 287 | |
|---|
| 288 | def povUpdateFile(filename,cam): |
|---|
| 289 | f=open(filename,'r',encoding='latin-1') |
|---|
| 290 | lines=f.readlines() |
|---|
| 291 | f.close() |
|---|
| 292 | for i in range(len(lines)): |
|---|
| 293 | line=lines[i] |
|---|
| 294 | if line.startswith('Camera('): |
|---|
| 295 | line='Camera(<%g,%g,%g>,<%g,%g,%g>)\n' % tuple(cam); |
|---|
| 296 | lines[i]=line |
|---|
| 297 | f=open(filename,'w',encoding='latin-1') |
|---|
| 298 | f.writelines(lines) |
|---|
| 299 | f.close() |
|---|
| 300 | |
|---|
| 301 | def framsCameraFromObj(obj): |
|---|
| 302 | #print(obj.location.x) |
|---|
| 303 | m=obj.matrix_local |
|---|
| 304 | return [obj.location.x, obj.location.y, obj.location.z, obj.location.x-m[0][2], obj.location.y-m[1][2], obj.location.z-m[2][2]] |
|---|
| 305 | |
|---|
| 306 | def povUpdateScene(obj,writepath): |
|---|
| 307 | global Folder,FirstFrame,FrameCount,Files,FirstPOV |
|---|
| 308 | print("Updating scene %s" % obj.name) |
|---|
| 309 | scanDir(obj.framspov_file) |
|---|
| 310 | if len(Files)<1: |
|---|
| 311 | print("No files found for "+obj.name) |
|---|
| 312 | return |
|---|
| 313 | |
|---|
| 314 | if writepath: |
|---|
| 315 | f=open(os.path.join(Folder,'camerapath.inc'),'w',encoding='latin-1') |
|---|
| 316 | f.write("#local CameraPathFirst=1;\n") |
|---|
| 317 | f.write("#local CameraPath=array["+str(len(Files))+"*2]\n") |
|---|
| 318 | f.write("{\n") |
|---|
| 319 | |
|---|
| 320 | FirstFrame=obj.framspov_frame |
|---|
| 321 | FrameCount=obj.framspov_count |
|---|
| 322 | for k in sorted(Files.keys()): |
|---|
| 323 | #bpy.context.scene.frame_current=FirstFrame-FirstPOV+k |
|---|
| 324 | bpy.context.scene.frame_set(FirstFrame-FirstPOV+k) |
|---|
| 325 | if bpy.context.scene.frame_current >= FirstFrame+FrameCount: |
|---|
| 326 | break |
|---|
| 327 | print("Frame %d - updating camera in %s" % (bpy.context.scene.frame_current,Files[k])) |
|---|
| 328 | cam=framsCameraFromObj(bpy.context.scene.camera) |
|---|
| 329 | cam[0]-=obj.location.x |
|---|
| 330 | cam[1]-=obj.location.y |
|---|
| 331 | cam[2]-=obj.location.z |
|---|
| 332 | cam[3]-=obj.location.x |
|---|
| 333 | cam[4]-=obj.location.y |
|---|
| 334 | cam[5]-=obj.location.z |
|---|
| 335 | if writepath: |
|---|
| 336 | f.write((" <%g,%g,%g>,<%g,%g,%g>," % tuple(cam))+" //"+Files[k]+"\n") |
|---|
| 337 | else: |
|---|
| 338 | povUpdateFile(os.path.join(Folder,Files[k]),cam) |
|---|
| 339 | |
|---|
| 340 | if writepath: |
|---|
| 341 | f.write(''' |
|---|
| 342 | } |
|---|
| 343 | #if ((AnimFrame>=CameraPathFirst) & ((AnimFrame-CameraPathFirst)<(dimension_size(CameraPath,1)/2))) |
|---|
| 344 | #local i=2*(AnimFrame-CameraPathFirst); |
|---|
| 345 | Camera(CameraPath[i],CameraPath[i+1]) |
|---|
| 346 | #end |
|---|
| 347 | ''') |
|---|
| 348 | f.close() |
|---|
| 349 | |
|---|
| 350 | #################################### |
|---|
| 351 | |
|---|
| 352 | class FramsticksPOVImporter(bpy.types.Operator, ImportHelper): |
|---|
| 353 | """Load a collection of Framsticks POV files""" |
|---|
| 354 | bl_idname = "framspov.import" |
|---|
| 355 | bl_label = "Import Framsticks POV" |
|---|
| 356 | bl_options = {'UNDO'} |
|---|
| 357 | |
|---|
| 358 | files = CollectionProperty(name="File Path", |
|---|
| 359 | description="File path used for importing", |
|---|
| 360 | type=bpy.types.OperatorFileListElement) |
|---|
| 361 | directory = StringProperty() |
|---|
| 362 | |
|---|
| 363 | framspov_skip = bpy.props.IntProperty(name="Frame step",min=1,max=100) |
|---|
| 364 | |
|---|
| 365 | filename_ext = ".pov" |
|---|
| 366 | filter_glob = StringProperty(default="*.pov", options={'HIDDEN'}) |
|---|
| 367 | |
|---|
| 368 | def execute(self, context): |
|---|
| 369 | global FirstFrame, FrameCount,FileName,SkipFrames |
|---|
| 370 | FirstFrame = bpy.context.scene.frame_current |
|---|
| 371 | FrameCount = 9999 |
|---|
| 372 | SkipFrames = self.framspov_skip |
|---|
| 373 | framsImport(os.path.join(self.directory, self.files[0].name)) |
|---|
| 374 | return {'FINISHED'} |
|---|
| 375 | |
|---|
| 376 | |
|---|
| 377 | def menu_func_import(self, context): |
|---|
| 378 | self.layout.operator(FramsticksPOVImporter.bl_idname, text="Framsticks POV (.pov)") |
|---|
| 379 | |
|---|
| 380 | class OBJECT_PT_framspov(bpy.types.Panel): |
|---|
| 381 | bl_label = "Framsticks POV" |
|---|
| 382 | bl_space_type = "PROPERTIES" |
|---|
| 383 | bl_region_type = "WINDOW" |
|---|
| 384 | bl_context = "object" |
|---|
| 385 | |
|---|
| 386 | @classmethod |
|---|
| 387 | def poll(cls, context): |
|---|
| 388 | obj=context.object |
|---|
| 389 | return obj.framspov_file!='' |
|---|
| 390 | |
|---|
| 391 | def draw(self, context): |
|---|
| 392 | layout = self.layout |
|---|
| 393 | row = layout.row() |
|---|
| 394 | row.operator("framspov.updatecam",icon='SCRIPT') |
|---|
| 395 | row = layout.row() |
|---|
| 396 | row.operator("framspov.writecamerapath",icon='SCRIPT') |
|---|
| 397 | |
|---|
| 398 | |
|---|
| 399 | class VIEW3D_OT_UpdatePOVCamera(bpy.types.Operator): |
|---|
| 400 | bl_idname = "framspov.updatecam" |
|---|
| 401 | bl_label = "Update POV camera" |
|---|
| 402 | |
|---|
| 403 | def execute(self, context): |
|---|
| 404 | povUpdateScene(context.object,False) |
|---|
| 405 | return{'FINISHED'} |
|---|
| 406 | |
|---|
| 407 | class VIEW3D_OT_WritePOVCameraPath(bpy.types.Operator): |
|---|
| 408 | bl_idname = "framspov.writecamerapath" |
|---|
| 409 | bl_label = "Write camerapath.inc" |
|---|
| 410 | |
|---|
| 411 | def execute(self, context): |
|---|
| 412 | povUpdateScene(context.object,True) |
|---|
| 413 | return{'FINISHED'} |
|---|
| 414 | |
|---|
| 415 | def register(): |
|---|
| 416 | bpy.types.Object.framspov_file=bpy.props.StringProperty(name="Name of the first POV file") |
|---|
| 417 | bpy.types.Object.framspov_frame=bpy.props.IntProperty(name="First frame",min=0,max=999999) |
|---|
| 418 | bpy.types.Object.framspov_count=bpy.props.IntProperty(name="Number of frames",min=0,max=999999) |
|---|
| 419 | bpy.utils.register_class(FramsticksPOVImporter) |
|---|
| 420 | bpy.utils.register_class(VIEW3D_OT_UpdatePOVCamera) |
|---|
| 421 | bpy.utils.register_class(VIEW3D_OT_WritePOVCameraPath) |
|---|
| 422 | bpy.utils.register_class(OBJECT_PT_framspov) |
|---|
| 423 | bpy.types.INFO_MT_file_import.append(menu_func_import) |
|---|
| 424 | |
|---|
| 425 | def unregister(): |
|---|
| 426 | bpy.utils.unregister_class(FramsticksPOVImporter) |
|---|
| 427 | bpy.utils.unregister_class(VIEW3D_OT_UpdatePOVCamera) |
|---|
| 428 | bpy.utils.unregister_class(VIEW3D_OT_WritePOVCameraPath) |
|---|
| 429 | bpy.utils.unregister_class(OBJECT_PT_framspov) |
|---|
| 430 | bpy.types.INFO_MT_file_import.remove(menu_func_import) |
|---|
| 431 | |
|---|