On 04/02/2016 at 04:48, xxxxxxxx wrote:
"""
Diffence Render
Written for Cinema 4D R15 and up
"""
import c4d
from c4d import gui, plugins, utils, bitmaps, storage
from c4d.threading import C4DThread
from datetime import datetime
import time
import os
v_start_frame=2000
v_end_frame=2001
b_close=2010
b_calculate=2011
b_copy_rs=2012
quality=2020
the_filename=2030
b_filename=2040
b_abort=2050
t_info=2060
t_info2=2070
#be sure to use a unique ID obtained from www.plugincafe.com
PLUGIN_ID = 1036575
rImage1 = bitmaps.BaseBitmap()
rImage2 = bitmaps.BaseBitmap()
mImage = bitmaps.BaseBitmap()
start_frame=0
end_frame=99
current_frame=99
abort = False
calculating = False
document_time = c4d.BaseTime()
start_time = time.time()
last_estimate = 0
last_estimate_string = ""
frames_dir = ""
masks_dir = ""
main_dir = ""
average_change = 0
# *******************************************************************************
class RenderDisplay(gui.GeUserArea) :
highlight_line = c4d.Vector(0, 0.6, 0)
black = c4d.Vector(0)
white = c4d.Vector(1)
shadow_line = c4d.Vector(0.15)
success = False
def __init__(self,bmp) :
super(RenderDisplay, self).__init__()
self.bmp = rImage1
def DrawMsg(self, x1, y1, x2, y2, msg_ref) :
global start_frame
global end_frame
global current_frame
global abort
global calculating
#init draw region
self.OffScreenOn()
self.SetClippingRegion(x1, y1, x2, y2)
the_color=self.GetColorRGB(c4d.COLOR_BG)
back_color=c4d.Vector(the_color['r']/255.0,the_color['g']/255.0,the_color['b']/255.0)
dimmed=c4d.Vector(c4d.utils.Clamp(0.0,1.0,back_color.x*1.1),c4d.utils.Clamp(0.0,1.0,back_color.y*1.1),c4d.utils.Clamp(0.0,1.0,back_color.z*1.1))
self.DrawSetPen(back_color)
self.DrawRectangle(x1, y1, x2, y2)
self.DrawBorder(c4d.BORDER_THIN_IN,0,0,256,256)
self.DrawBorder(c4d.BORDER_THIN_IN,272,0,528,256)
if self.bmp:
ww=self.bmp.GetBw()
hh=self.bmp.GetBh()
if ww>0 and hh>0:
sc=254.0/float(ww)
if hh*sc>254: sc=254.0/float(hh)
nw=int(ww*sc)
nh=int(hh*sc)
nx=128-int(nw/2)
ny=128-int(nh/2)
self.DrawBitmap(self.bmp,nx,ny,nw,nh,0,0,ww,hh,c4d.BMP_NORMAL)
if mImage:
self.DrawBitmap(mImage,270+nx,ny,nw,nh,0,0,ww,hh,c4d.BMP_NORMAL)
# progress bar
self.DrawBorder(c4d.BORDER_THIN_IN,100,268,528,278)
self.DrawSetPen(self.white if calculating else dimmed)
percent=0.0
progress="Frame --/--"
if float(end_frame-start_frame)>0.0: percent=float(current_frame-start_frame+1)/float(end_frame-start_frame+1)
self.DrawRectangle(102, 270, int(102+424*percent), 276)
#self.DrawSetTextCol(self.white, back_color)
self.DrawSetTextCol(self.white if calculating else dimmed, back_color)
progress="Frame "+str(int(current_frame-start_frame+1))+"/"+str(int(end_frame-start_frame)+1)
self.DrawText(progress if calculating else "Frame --/--", 2, 266)
# *******************************************************************************
class MyThread(c4d.threading.C4DThread) :
thumbnail = RenderDisplay(None)
v_quality=0
def Main(self) :
global start_frame
global end_frame
global current_frame
global abort
global calculating
global frames_dir
global masks_dir
global start_time
global main_dir
global average_change
# not calculating at the moment...
if calculating==False: return
# get the render data
doc=c4d.documents.GetActiveDocument()
rd=doc.GetActiveRenderData()
# bitmap size
o_ww=rd[c4d.RDATA_XRES]
o_hh=rd[c4d.RDATA_YRES]
ww=int(o_ww*self.v_quality)
hh=int(o_hh*self.v_quality)
# get the content of the render data container
new_rd=doc.GetActiveRenderData().GetData()
new_rd[c4d.RDATA_MULTIPASS_ENABLE] = False
new_rd[c4d.RDATA_PROJECTFILE] = False
new_rd[c4d.RDATA_FRAMESEQUENCE] = 1
new_rd[c4d.RDATA_SAVEIMAGE] = False
new_rd[c4d.RDATA_MULTIPASS_SAVEIMAGE] = False
new_rd[c4d.RDATA_XRES]=ww
new_rd[c4d.RDATA_YRES]=hh
new_rd[c4d.RDATA_ANTIALIASING] = 0 # no antialiasing
rImage1.Init(ww,hh,24)
rImage2.Init(ww,hh,24)
mImage.Init(ww,hh,24)
# perform the renders while not aborting and before the end of all frames
while current_frame<=end_frame and abort==False and calculating==True:
new_rd[c4d.RDATA_FRAMEFROM]=c4d.BaseTime(current_frame,doc.GetFps())
new_rd[c4d.RDATA_FRAMETO]=c4d.BaseTime(current_frame,doc.GetFps())
doc.SetTime(c4d.BaseTime(current_frame,doc.GetFps()))
res2=c4d.documents.RenderDocument(doc,new_rd,rImage1,c4d.RENDERFLAGS_EXTERNAL|c4d.RENDERFLAGS_NODOCUMENTCLONE)
# prepare the mask
if current_frame==start_frame:
# first frame
for yy in xrange(hh) :
for xx in xrange(ww) :
mImage.SetPixel(xx,yy,255,255,255)
else:
for yy in xrange(hh) :
for xx in xrange(ww) :
diff= 0 if rImage1.GetPixel(xx,yy)==rImage2.GetPixel(xx,yy) else 255
mImage.SetPixel(xx,yy,diff,diff,diff)
# save the frame and the mask
frame_name=os.path.join(frames_dir,"frame_%05d.png" % (current_frame,))
result=rImage1.Save(frame_name,c4d.FILTER_PNG)
masks_name=os.path.join(masks_dir,"mask_%05d.png" % (current_frame,))
result=mImage.Save(masks_name,c4d.FILTER_PNG)
rImage1.CopyTo(rImage2)
current_frame=current_frame+1
# RENDER FINISHED!!!
# create a report file
if os.path.exists(main_dir) :
report_path=os.path.join(main_dir,"Report.txt")
hf=open(report_path,'w')
if hf:
hf.write("Report for file < "+doc.GetDocumentName()+" >\n\n")
hf.write("Date: "+time.strftime("%d/%m/%Y")+"\n\n")
hf.write("Original frame size: %d x %d\n" % (o_ww,o_hh))
hf.write("Final masks frame size: %d x %d\n\n" % (ww,hh))
hf.write("Frame Range: frame %d to frame %d (%d in total)\n\n" % (start_frame,end_frame,end_frame-start_frame+1))
if abort:
hf.write("ABORTED BEFORE COMPLETION AT FRAME "+str(current_frame)+"!\n\n")
spent=time.time()-start_time
d=divmod(spent,86400)
h=divmod(d[1],3600)
m=divmod(h[1],60)
s=m[1]
hf.write("Total render calculation time: %02d:%02d:%02d\n\n" % (h[0],m[0],s))
total_pixels=(end_frame-start_frame+1)*(ww*hh)
aver=float(total_pixels-average_change)/float(total_pixels)
hf.write("Average change: %d%% (less change is better)" % (100*(1.0-aver),))
hf.close()
# clean the house
rImage1.FlushAll()
rImage2.FlushAll()
mImage.FlushAll()
abort=True
calculating=False
# *******************************************************************************
class DiffRender(gui.GeDialog) :
thumbnail = RenderDisplay(None)
thread = MyThread()
counter=0
def CreateLayout(self) :
self.SetTitle("Render Differences")
self.GroupBegin(id=0, flags=c4d.BFH_SCALEFIT, title="", rows=1, cols=1, groupflags=c4d.BORDER_GROUP_IN)
self.GroupBorderSpace(5, 5, 5, 5)
#give really unique ID to userarea, otherwise the update process will fail!
area = self.AddUserArea(id=1001, flags=c4d.BFH_SCALEFIT|c4d.BFV_TOP,initw=528, inith=204)
self.AttachUserArea(self.thumbnail, area)
self.AddStaticText(id=t_info2,flags=c4d.BFH_SCALEFIT|c4d.BFV_TOP,inith=0, name="Info: ---", borderstyle=c4d.BORDER_THIN_IN)
self.AddButton(id=b_abort, flags=c4d.BFH_CENTER,initw=64, inith=16, name="Abort")
self.AddSeparatorH(inith=0, flags=c4d.BFH_FIT)
# Filename
self.GroupBegin(id=0, flags=c4d.BFH_SCALEFIT|c4d.BFV_TOP, title="", rows=1, cols=3,)
self.AddStaticText(0,flags=c4d.BFH_LEFT|c4d.BFV_TOP,initw=90, inith=12, name="Masks Path")
self.AddEditText(id=the_filename,flags=c4d.BFH_SCALEFIT|c4d.BFV_TOP,initw=0, inith=12)
self.AddButton(id=b_filename, flags=c4d.BFH_FIT|c4d.BFV_TOP,initw=50, inith=12, name="...")
self.GroupEnd()
# quality
self.GroupBegin(id=0, flags=c4d.BFH_SCALEFIT|c4d.BFV_TOP, title="", rows=1, cols=3,)
self.AddStaticText(0,flags=c4d.BFH_FIT|c4d.BFV_TOP,initw=90, inith=12,name="Quality")
self.AddEditSlider(id=quality, flags=c4d.BFH_SCALEFIT|c4d.BFV_TOP,initw=50, inith=12)
self.AddStaticText(id=t_info,flags=c4d.BFH_FIT|c4d.BFV_TOP,initw=120, inith=12, name="--- x ---")
self.GroupEnd()
self.AddSeparatorH(inith=0, flags=c4d.BFH_FIT)
# frames
self.GroupBegin(id=0, flags=c4d.BFH_SCALEFIT|c4d.BFV_TOP, title="", rows=1, cols=6,)
self.AddStaticText(0,flags=c4d.BFH_FIT|c4d.BFV_TOP,initw=90, inith=12,name="From frame")
self.AddEditNumberArrows(id=v_start_frame, flags=c4d.BFH_FIT|c4d.BFV_TOP,initw=80, inith=12)
self.AddStaticText(0,flags=c4d.BFH_FIT|c4d.BFV_TOP,initw=74, inith=12,name="To frame")
self.AddEditNumberArrows(id=v_end_frame, flags=c4d.BFH_FIT|c4d.BFV_TOP,initw=80, inith=12)
self.AddButton(id=b_copy_rs, flags=c4d.BFH_SCALEFIT,initw=0, inith=12, name="Copy from Render Settings")
self.GroupEnd()
self.AddSeparatorH(inith=0, flags=c4d.BFH_FIT)
self.GroupBegin(id=0, flags=c4d.BFH_RIGHT, title="", rows=1, cols=2,)
self.AddButton(id=b_close, flags=c4d.BFH_RIGHT,initw=60, inith=16, name="Close")
self.AddButton(id=b_calculate, flags=c4d.BFH_RIGHT,initw=80, inith=16, name="Calculate")
self.GroupEnd()
self.GroupEnd()
return True
# *******************************************************************************
def InitValues(self) :
self.SetInt32(id=quality,value=75,min=10,max=100)
self.SetInt32(id=v_start_frame,value=0)
self.SetInt32(id=v_end_frame,value=99)
doc=c4d.documents.GetActiveDocument()
rd=doc.GetActiveRenderData()
ww=rd[c4d.RDATA_XRES_VIRTUAL]
hh=rd[c4d.RDATA_YRES_VIRTUAL]
qual=float(self.GetInt32(quality))/100.0
self.SetString(id=t_info,value="( "+str(int(ww*qual))+" x "+str(int(hh*qual))+" )")
calculating=False
success=False
self.Enable(b_abort,False)
self.SetTimer(700)
return True
# *******************************************************************************
def Command(self,id,msg) :
global start_frame
global end_frame
global current_frame
global abort
global calculating
global document_time
global start_time
global last_estimate
global last_estimate_string
global frames_dir
global masks_dir
global main_dir
global average_change
doc=c4d.documents.GetActiveDocument()
if id==quality:
rd=doc.GetActiveRenderData()
ww=rd[c4d.RDATA_XRES_VIRTUAL]
hh=rd[c4d.RDATA_YRES_VIRTUAL]
qual=float(self.GetInt32(quality))/100.0
self.SetString(id=t_info,value="( "+str(int(ww*qual))+" x "+str(int(hh*qual))+" )")
if id==b_close:
self.Close()
if id==b_copy_rs:
doc=c4d.documents.GetActiveDocument()
rd=doc.GetActiveRenderData()
start=rd[c4d.RDATA_FRAMEFROM].GetFrame(doc.GetFps())
end=rd[c4d.RDATA_FRAMETO].GetFrame(doc.GetFps())
self.SetInt32(id=v_start_frame,value=start)
self.SetInt32(id=v_end_frame,value=end)
if id==b_filename:
new_filename=c4d.storage.LoadDialog(title="Select the destination folder",flags=c4d.FILESELECT_DIRECTORY)
if new_filename!=None:
self.SetFilename(id=the_filename,fn=new_filename)
if id==b_calculate:
# check the path
new_filename=self.GetFilename(id=the_filename)
if new_filename=="":
c4d.gui.MessageDialog("A valid path needs to be set, in order to save the difference masks.")
return True
if not os.path.exists(new_filename) :
c4d.gui.MessageDialog("A valid path needs to be set, in order to save the difference masks.")
return True
main_dir=new_filename
# check start and end frames
start_frame=self.GetInt32(id=v_start_frame)
end_frame=self.GetInt32(id=v_end_frame)
if start_frame>=end_frame:
c4d.gui.MessageDialog("There is a problem with the start and/or end frames.")
return True
# check if the required folders exist. If not, create them
frames_dir=os.path.join(new_filename,"frames")
if not os.path.exists(frames_dir) :
os.mkdir(frames_dir)
masks_dir=os.path.join(new_filename,"masks")
if not os.path.exists(masks_dir) :
os.mkdir(masks_dir)
# clip the end_frame, if necessary
max_frames=doc.GetMaxTime().GetFrame(doc.GetFps())
if end_frame>max_frames:
end_frame=max_frames
# disable/enable GUI for calculations
self.Enable(b_abort,True)
self.Enable(b_calculate,False)
self.Enable(b_close,False)
self.Enable(b_filename,False)
self.Enable(the_filename,False)
self.Enable(quality,False)
self.Enable(t_info,False)
self.Enable(v_start_frame,False)
self.Enable(v_end_frame,False)
self.Enable(b_copy_rs,False)
# adjust all the required variables
abort=False
calculating=True
current_frame=start_frame
start_time=time.time()
last_estimate=time.strptime("23:59:59","%H:%M:%S")
last_estimate_string=""
average_change=0
self.counter=1
document_time=doc.GetTime()
self.thread.v_quality=float(self.GetInt32(quality))/100.0
# start the calculation
self.thread.Start()
if id==b_abort:
abort=True
self.SetString(id=t_info2,value="Aborting...")
self.Enable(b_abort,False)
self.Enable(b_calculate,True)
self.Enable(b_close,True)
self.Enable(b_filename,True)
self.Enable(the_filename,True)
self.Enable(quality,True)
self.Enable(t_info,True)
self.Enable(v_start_frame,True)
self.Enable(v_end_frame,True)
self.Enable(b_copy_rs,True)
doc.SetTime(document_time)
#c4d.EventAdd()
return True
# *******************************************************************************
def Timer(self, msg) :
global start_frame
global end_frame
global current_frame
global abort
global calculating
global document_time
global start_time
global last_estimate
global last_estimate_string
if self.counter==0:
if calculating and not abort:
spent=time.time()-start_time
d=divmod(spent,86400)
h=divmod(d[1],3600)
m=divmod(h[1],60)
s=m[1]
time_string="Elapsed: %02d:%02d:%02d" % (h[0],m[0],s)
if current_frame>start_frame:
each_frame=spent/float(current_frame-start_frame-.1)
estimated=(end_frame-current_frame)*each_frame
d=divmod(estimated,86400)
h=divmod(d[1],3600)
m=divmod(h[1],60)
s=m[1]
last_estimate_string=" ( Estimated time to finish: %02d:%02d:%02d )" % (h[0],m[0],s)
last_estimate=estimated
time_string=time_string+last_estimate_string
self.SetString(id=t_info2,value=time_string)
self.counter=(self.counter+1) % 3
if abort==True:
self.thread.End(True)
calculating=False
self.Enable(b_abort,False)
self.Enable(b_calculate,True)
self.Enable(b_close,True)
self.Enable(b_filename,True)
self.Enable(the_filename,True)
self.Enable(quality,True)
self.Enable(t_info,True)
self.Enable(v_start_frame,True)
self.Enable(v_end_frame,True)
self.Enable(b_copy_rs,True)
doc=c4d.documents.GetActiveDocument()
doc.SetTime(document_time)
c4d.StopAllThreads()
#Since we can't update the USER_AREA manually while rendering
#We must ReDraw it constantly before we start rendering
#Only do this ReDrawing when we are actually rendering!!!
if self.thread.IsRunning() :
self.thumbnail.Redraw()
# *******************************************************************************
class BitmapManagerCommandData(c4d.plugins.CommandData) :
dialog = None
def Execute(self, doc) :
"""Just create the dialog when the user clicked on the entry
in the plugins menu to open it."""
if self.dialog is None:
self.dialog = DiffRender()
return self.dialog.Open(dlgtype=c4d.DLG_TYPE_MODAL, pluginid=PLUGIN_ID, defaulth=300, defaultw=550)
def RestoreLayout(self, sec_ref) :
"""Same for this method. Just allocate it when the dialog
is needed"""
if self.dialog is None:
self.dialog = DiffRender()
return self.dialog.Restore(pluginid=PLUGIN_ID, secret=sec_ref)
# *******************************************************************************
if __name__ == "__main__":
bmp = bitmaps.BaseBitmap()
dir, f = os.path.split(__file__)
fn = os.path.join(dir, "res", "icon.tif")
bmp.InitWith(fn)
c4d.plugins.RegisterCommandPlugin(id=PLUGIN_ID, str="Differences Render",
help="Renders set of bitmaps with differences between animation frames.",info=0,
dat=BitmapManagerCommandData(), icon=bmp)