diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0d20b648 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/InitGui.py b/InitGui.py new file mode 100644 index 00000000..c50b2e73 --- /dev/null +++ b/InitGui.py @@ -0,0 +1,106 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2017 Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + + + +class RenderWorkbench (Workbench): + + + "the Render Workbench" + + def __init__(self): + self.__class__.MenuText = "Render" + self.__class__.ToolTip = "The Render module is a modern replacement for the Raytracing module" + self.__class__.Icon=''' +/* XPM */ +static char * Render_xpm[] = { +"16 16 33 1", +" c None", +". c #103F06", +"+ c #224702", +"@ c #1A580E", +"# c #2F5600", +"$ c #386402", +"% c #297014", +"& c #416B00", +"* c #537200", +"= c #3C7B0F", +"- c #4B880D", +"; c #579310", +"> c #609702", +", c #858B13", +"' c #6AA20A", +") c #92A709", +"! c #9EA610", +"~ c #ACA137", +"{ c #86B208", +"] c #B6A94A", +"^ c #9DC400", +"/ c #ADC50F", +"( c #B4C31F", +"_ c #C8BD71", +": c #C2C731", +"< c #CAD51A", +"[ c #D7D736", +"} c #E9CD77", +"| c #DCDA50", +"1 c #E5DF6D", +"2 c #EBE581", +"3 c #F4EFA8", +"4 c #F8F4C7", +" ", +" ", +" #*)!~] ", +" &^^<[|2_} ", +" ${{/(|243_ ", +" +;'{{(|2431~ ", +" #->'{(:|11[, ", +" @--;')(::[<, ", +" @==-;'{)(//* ", +" .%%=-;>'{^^# ", +" .@%%=-;>'{; ", +" .@%%=-;>>+ ", +" .@%%=-&+ ", +" .... ", +" ", +" "}; +''' + + + def Initialize(self): + + def QT_TRANSLATE_NOOP(scope, text): + return text + + import Render + commands = Render.RenderCommands + self.appendToolbar(QT_TRANSLATE_NOOP("Workbench","Render"),commands) + self.appendMenu(QT_TRANSLATE_NOOP("Workbench","&Render"),commands) + FreeCADGui.addPreferencePage(Render.prefpage,"Render") + Log ('Loading Render module...done\n') + + def GetClassName(self): + return "Gui::PythonWorkbench" + +FreeCADGui.addWorkbench(RenderWorkbench) + + diff --git a/Render.py b/Render.py new file mode 100644 index 00000000..b0d4478e --- /dev/null +++ b/Render.py @@ -0,0 +1,413 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2017 Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# This module handles all the external renderers implemented as Python modules. +# It will add all renderer modules specified below at FreeCAD launch, and +# create the necessary UI controls. + +import os,re,tempfile,FreeCAD,importlib + +if FreeCAD.GuiUp: + from PySide import QtCore, QtGui + def translate(context, text): + if sys.version_info.major >= 3: + return QtGui.QApplication.translate(context, text, None, QtGui.QApplication.UnicodeUTF8) + else: + return QtGui.QApplication.translate(context, text, None, QtGui.QApplication.UnicodeUTF8).encode("utf8") +else: + def translate(context,txt): + return txt +def QT_TRANSLATE_NOOP(scope, text): + return text + + +class RenderProjectCommand: + + + "Creates a rendering project. The renderer parameter must be a valid rendering module" + + def __init__(self,renderer): + self.renderer = renderer + + def GetResources(self): + return {'Pixmap' : os.path.join(os.path.dirname(__file__),"icons",self.renderer+".svg"), + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Render", "%s Project") % self.renderer, + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Render", "Creates a %s project") % self.renderer} + + def Activated(self): + if self.renderer: + project = FreeCAD.ActiveDocument.addObject("App::FeaturePython",self.renderer+"Project") + Project(project) + project.Label = self.renderer + " Project" + project.Renderer = self.renderer + ViewProviderProject(project.ViewObject) + filename = QtGui.QFileDialog.getOpenFileName(QtGui.qApp.activeWindow(),'Select template','*.*') + if filename: + project.Template = filename[0] + project.ViewObject.Proxy.setCamera() + FreeCAD.ActiveDocument.recompute() + + + +class RenderViewCommand: + + + "Creates a Raytracing view of the selected object(s) in the selected project or the default project" + + def GetResources(self): + return {'Pixmap' : os.path.join(os.path.dirname(__file__),"icons","RenderView.svg"), + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Render", "Create View"), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Render", "Creates a Render view of the selected object(s) in the selected project or the default project")} + + def Activated(self): + import FreeCADGui + project = None + objs = [] + sel = FreeCADGui.Selection.getSelection() + for o in sel: + if "Renderer" in o.PropertiesList: + project = o + else: + if o.isDerivedFrom("Part::Feature") or o.isDerivedFrom("Mesh::Feature"): + objs.append(o) + if not project: + for o in FreeCAD.ActiveDocument.Objects: + if "Renderer" in o.PropertiesList: + project = o + break + if not project: + FreeCAD.Console.PrintError(translate("Render","Unable to find a valid project in selection or document")) + return + for obj in objs: + view = FreeCAD.ActiveDocument.addObject("App::FeaturePython",obj.Name+"View") + view.Label = "View of "+ obj.Name + View(view) + view.Source = obj + project.addObject(view) + ViewProviderView(view.ViewObject) + FreeCAD.ActiveDocument.recompute() + + + +class RenderCommand: + + + "Renders a selected Render project" + + + def GetResources(self): + return {'Pixmap' : os.path.join(os.path.dirname(__file__),"icons","Render.svg"), + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Render", "Render"), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Render", "Performs the render of a selected project or the default project")} + + def Activated(self): + import FreeCADGui + project = None + sel = FreeCADGui.Selection.getSelection() + for o in sel: + if "Renderer" in o.PropertiesList: + project = o + break + if not project: + for o in FreeCAD.ActiveDocument.Objects: + if "Renderer" in o.PropertiesList: + project = o + break + if not project: + FreeCAD.Console.PrintError(translate("Render","Unable to find a valid project in selection or document")) + return + project.Proxy.render(project) + + +class RenderExternalCommand: + + + "Sends a selected Render project" + + + def GetResources(self): + return {'Pixmap' : os.path.join(os.path.dirname(__file__),"icons","Render.svg"), + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Render", "Render"), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Render", "Performs the render of a selected project or the default project")} + + def Activated(self): + import FreeCADGui + project = None + sel = FreeCADGui.Selection.getSelection() + for o in sel: + if "Renderer" in o.PropertiesList: + project = o + break + if not project: + for o in FreeCAD.ActiveDocument.Objects: + if "Renderer" in o.PropertiesList: + project = o + break + if not project: + FreeCAD.Console.PrintError(translate("Render","Unable to find a valid project in selection or document")) + return + project.Proxy.render(project) + + +class Project: + + + "A rendering project" + + def __init__(self,obj): + + obj.addProperty("App::PropertyString", "Renderer", "Render", QT_TRANSLATE_NOOP("App::Property","The name of the raytracing engine to use")) + obj.addProperty("App::PropertyBool", "DelayedBuild", "Render", QT_TRANSLATE_NOOP("App::Property","If true, the views will be updated on render only")) + obj.addProperty("App::PropertyFile", "Template", "Render", QT_TRANSLATE_NOOP("App::Property","The template to be use by this rendering")) + obj.addProperty("App::PropertyString", "Camera", "Render", QT_TRANSLATE_NOOP("App::Property","The camera data to be used")) + obj.addProperty("App::PropertyFileIncluded", "PageResult", "Render", QT_TRANSLATE_NOOP("App::Property","The result file to be sent to the renderer")) + obj.addExtension("App::GroupExtensionPython", self) + obj.DelayedBuild = True + obj.Proxy = self + + def execute(self,obj): + + return True + + def onChanged(self,obj,prop): + + if prop == "DelayedBuild": + if not obj.DelayedBuild: + for view in obj.Group: + view.touch() + + def setCamera(self,obj): + + if FreeCAD.GuiUp: + import FreeCADGui + obj.Camera = FreeCADGui.ActiveDocument.ActiveView.getCamera() + + def writeCamera(self,obj): + + if not obj.Camera: + self.setCamera(obj) + if not obj.Camera: + FreeCAD.Console.PrintError(translate("Render","Unable to set the camera")) + return "" + if obj.Renderer: + try: + renderer = importlib.import_module("renderers."+obj.Renderer) + except ImportError: + FreeCAD.Console.PrintError(translate("Render","Error importing renderer")+" "+str(obj.Renderer)) + return "" + else: + return renderer.writeCamera(obj.Camera) + + def writeObject(self,obj,view): + + if not view.Source: + return "" + if obj.Renderer: + try: + renderer = importlib.import_module("renderers."+obj.Renderer) + except ImportError: + FreeCAD.Console.PrintError(translate("Render","Error importing renderer")+" "+str(obj.Renderer)) + return "" + else: + return renderer.writeObject(view) + + def render(self,obj,external=True): + + if obj.Renderer: + + # open template + template = None + if obj.Template: + if os.path.exists(obj.Template): + f = open(obj.Template,"rb") + template = f.read() + f.close() + if not template: + return + + # write camera + cam = self.writeCamera(obj) + template = re.sub("(.*RaytracingCamera.*)",cam,template) + + # write objects + renderobjs = "" + for view in obj.Group: + if obj.DelayedBuild: + renderobjs += self.writeObject(obj,view) + else: + renderobjs += view.ViewResult + template = re.sub("(.*RaytracingContent.*)",renderobjs,template) + + # save page result + fp = tempfile.mkstemp(prefix=obj.Name,suffix=os.path.splitext(obj.Template)[-1])[1] + f = open(fp,"wb") + f.write(template) + f.close() + obj.PageResult = fp + os.remove(fp) + + FreeCAD.ActiveDocument.recompute() + + try: + renderer = importlib.import_module("renderers."+obj.Renderer) + except ImportError: + FreeCAD.Console.PrintError(translate("Render","Error importing renderer")+" "+str(obj.Renderer)) + return "" + else: + try: + return renderer.render(obj,external) + except: + FreeCAD.Console.PrintError(translate("Render","Error while executing renderer")+" "+str(obj.Renderer)) + + +class ViewProviderProject: + + + def __init__(self,vobj): + vobj.Proxy = self + + def attach(self,vobj): + self.Object = vobj.Object + return True + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + def getDisplayModes(self,vobj): + return ["Default"] + + def getDefaultDisplayMode(self): + return "Default" + + def setDisplayMode(self,mode): + return mode + + def isShow(self): + return True + + def getIcon(self): + return os.path.join(os.path.dirname(__file__),"icons","RenderProject.svg") + + def setupContextMenu(self,vobj,menu): + from PySide import QtCore,QtGui + import FreeCADGui + action1 = QtGui.QAction(QtGui.QIcon(":/icons/camera-photo.svg"),"Save camera position",menu) + QtCore.QObject.connect(action1,QtCore.SIGNAL("triggered()"),self.setCamera) + menu.addAction(action1) + action2 = QtGui.QAction(QtGui.QIcon(os.path.join(os.path.dirname(__file__),"icons","Render.svg")),"Render",menu) + QtCore.QObject.connect(action2,QtCore.SIGNAL("triggered()"),self.render) + menu.addAction(action2) + + def setCamera(self): + if hasattr(self,"Object"): + self.Object.Proxy.setCamera(self.Object) + + def render(self): + if hasattr(self,"Object"): + self.Object.Proxy.render(self.Object) + + def claimChildren(self): + if hasattr(self,"Object"): + return self.Object.Group + + +class View: + + + "A rendering view" + + def __init__(self,obj): + + obj.addProperty("App::PropertyLink", "Source", "Render", QT_TRANSLATE_NOOP("App::Property","The name of the raytracing engine to use")) + obj.addProperty("App::PropertyLink", "Material", "Render", QT_TRANSLATE_NOOP("App::Property","The template to be use by this rendering")) + obj.addProperty("App::PropertyString", "ViewResult", "Render", QT_TRANSLATE_NOOP("App::Property","The rendering output of this view")) + obj.Proxy = self + + def execute(self,obj): + + for proj in obj.InList: + if hasattr(proj,"Group"): + for c in proj.Group: + if c == obj: + if not proj.DelayedBuild: + obj.ViewResult = proj.Proxy.writeObject(proj,obj) + break + + +class ViewProviderView: + + + def __init__(self,vobj): + vobj.Proxy = self + + def attach(self,vobj): + self.Object = vobj.Object + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + def getDisplayModes(self,vobj): + return ["Default"] + + def getDefaultDisplayMode(self): + return "Default" + + def setDisplayMode(self,mode): + return mode + + def isShow(self): + return True + + def getIcon(self): + return os.path.join(os.path.dirname(__file__),"icons","RenderViewTree.svg") + + + +# Load available renderers and create the FreeCAD commands + + + +if FreeCAD.GuiUp: + + import FreeCADGui + + RenderCommands = [] + Renderers = os.listdir(os.path.dirname(__file__)+os.sep+"renderers") + Renderers = [r for r in Renderers if not ".pyc" in r] + Renderers = [r for r in Renderers if not "__init__" in r] + Renderers = [os.path.splitext(r)[0] for r in Renderers] + for renderer in Renderers: + FreeCADGui.addCommand('Render_'+renderer, RenderProjectCommand(renderer)) + RenderCommands.append('Render_'+renderer) + FreeCADGui.addCommand('Render_View', RenderViewCommand()) + RenderCommands.append('Render_View') + FreeCADGui.addCommand('Render_Render', RenderCommand()) + RenderCommands.append('Render_Render') + + # This is for InitGui.py because it cannot import os + prefpage = os.path.join(os.path.dirname(__file__),"ui","RenderSettings.ui") diff --git a/icons/Appleseed.svg b/icons/Appleseed.svg new file mode 100644 index 00000000..71fbae68 --- /dev/null +++ b/icons/Appleseed.svg @@ -0,0 +1,109 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/Luxrender.svg b/icons/Luxrender.svg new file mode 100644 index 00000000..4f098d7e --- /dev/null +++ b/icons/Luxrender.svg @@ -0,0 +1,264 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/Povray.svg b/icons/Povray.svg new file mode 100644 index 00000000..28fb2af8 --- /dev/null +++ b/icons/Povray.svg @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/icons/Render.svg b/icons/Render.svg new file mode 100644 index 00000000..8ce9fbfd --- /dev/null +++ b/icons/Render.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/icons/RenderProject.svg b/icons/RenderProject.svg new file mode 100644 index 00000000..453cec6b --- /dev/null +++ b/icons/RenderProject.svg @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/icons/RenderView.svg b/icons/RenderView.svg new file mode 100644 index 00000000..745e74a2 --- /dev/null +++ b/icons/RenderView.svg @@ -0,0 +1,446 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + [agryson] Alexander Gryson + + + http://agryson.net + + 2011-10-10 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/Drawing/Gui/Resources/icons/actions/drawing-view.svg + + + FreeCAD LGPL2+ + + + + + [agryson] Alexander Gryson + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/RenderViewTree.svg b/icons/RenderViewTree.svg new file mode 100644 index 00000000..2e644280 --- /dev/null +++ b/icons/RenderViewTree.svg @@ -0,0 +1,419 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + [agryson] Alexander Gryson + + + http://agryson.net + + 2011-10-10 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/Drawing/Gui/Resources/icons/actions/drawing-view.svg + + + FreeCAD LGPL2+ + + + + + [agryson] Alexander Gryson + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/renderers/Appleseed.py b/renderers/Appleseed.py new file mode 100644 index 00000000..6f22d826 --- /dev/null +++ b/renderers/Appleseed.py @@ -0,0 +1,249 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2017 Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# Appleseed renderer for FreeCAD + +# This file can also be used as a template to add more rendering engines. +# You will need to make sure your file is named with a same name (case sensitive) +# That you will use everywhere to describe your renderer, ex: Appleseed or Povray + + +# A render engine module must contain the following functions: +# +# writeCamera(camdata): returns a string containing an openInventor camera string in renderer format +# writeObject(viewobj): returns a string containing a RaytracingView object in renderer format +# render(project,external=True): renders the given project, external means if the user wishes to open +# the render file in an external application/editor or not. If this +# is not supported by your renderer, you can simply ignore it +# +# Additionally, you might need/want to add: +# +# Preference page items, that can be used in your functions below +# An icon under the name Renderer.svg (where Renderer is the name of your Renderer + + +import tempfile,FreeCAD,os,math + + +def writeCamera(camdata): + + + # this is where you create a piece of text in the format of + # your renderer, that represents the camera. You can use the contents + # of obj.Camera, which contain a string in OpenInventor format + # ex: + # #Inventor V2.1 ascii + # + # + # PerspectiveCamera { + # viewportMapping ADJUST_CAMERA + # position 0 -1.3207401 0.82241058 + # orientation 0.99999666 0 0 0.26732138 + # nearDistance 1.6108983 + # farDistance 6611.4492 + # aspectRatio 1 + # focalDistance 5 + # heightAngle 0.78539819 + # + # } + # + # or (ortho camera): + # + # #Inventor V2.1 ascii + # + # + # OrthographicCamera { + # viewportMapping ADJUST_CAMERA + # position 0 0 1 + # orientation 0 0 1 0 + # nearDistance 0.99900001 + # farDistance 1.001 + # aspectRatio 1 + # focalDistance 5 + # height 4.1421356 + # + # } + + if not camdata: + return "" + camdata = camdata.split("\n") + pos = [float(p) for p in camdata[5].split()[-3:]] + pos = FreeCAD.Vector(pos) + rot = [float(p) for p in camdata[6].split()[-4:]] + rot = FreeCAD.Rotation(FreeCAD.Vector(rot[0],rot[1],rot[2]),math.degrees(rot[3])) + tpos = pos.add(rot.multVec(FreeCAD.Vector(0,0,-1))) + tpos = str(tpos.x)+" "+str(tpos.y)+" "+str(tpos.z) + up = rot.multVec(FreeCAD.Vector(0,1,0)) + up = str(up.x)+" "+str(up.y)+" "+str(up.z) + pos = str(pos.x)+" "+str(pos.y)+" "+str(pos.z) + print "cam:",pos," : ",tpos," : ",up + cam = """ + + + + + + + + + + """ % (pos, tpos, up) + + return cam + + +def writeObject(viewobj): + + + # This is where you write your object/view in the format of your + # renderer. "obj" is the real 3D object handled by this project, not + # the project itself. This is your only opportunity + # to write all the data needed by your object (geometry, materials, etc) + # so make sure you include everything that is needed + + if not viewobj.Source: + return "" + obj = viewobj.Source + objname = viewobj.Name + colorname = objname + "_color" + color = None + alpha = None + mat = None + if viewobj.Material: + mat = viewobj.Material + else: + if "Material" in obj.PropertiesList: + if obj.Material: + mat = obj.Material + if mat: + if "Material" in mat.PropertiesList: + if "DiffuseColor" in mat.Material: + color = mat.Material["DiffuseColor"].strip("(").strip(")").split(",") + color = str(color[0])+" "+str(color[1])+" "+str(color[2]) + if "Transparency" in mat.Material: + if float(mat.Material["Transparency"]) > 0: + alpha = str(1.0/float(mat.Material["Transparency"])) + else: + alpha = "1.0" + if obj.ViewObject: + if not color: + if hasattr(obj.ViewObject,"ShapeColor"): + color = obj.ViewObject.ShapeColor[:3] + color = str(color[0])+" "+str(color[1])+" "+str(color[2]) + if not alpha: + if hasattr(obj.ViewObject,"Transparency"): + if obj.ViewObject.Transparency > 0: + alpha = str(1.0/(float(obj.ViewObject.Transparency)/100.0)) + if not color: + color = "1.0 1.0 1.0" + if not alpha: + alpha = "1.0" + bsdfname = objname + "_bsdf" + matname = objname + "_mat" + meshfile = tempfile.mkstemp(suffix=".obj")[1] + objfile = os.path.splitext(os.path.basename(meshfile))[0] + m = None + if obj.isDerivedFrom("Part::Feature"): + import MeshPart + m = MeshPart.meshFromShape(Shape=obj.Shape, + LinearDeflection=0.1, + AngularDeflection=0.523599, + Relative=False) + elif obj.isDerivedFrom("Mesh::Feature"): + m = obj.Mesh + if not m: + return "" + m.write(meshfile) + # fix for missing object name (mandatory in Appleseed) + f = open(meshfile, "rb") + contents = f.readlines() + f.close() + n = [] + found = False + for l in contents: + if (not found) and l.startswith("f "): + found = True + n.append("o "+objname+"\n") + n.append(l) + f = open(meshfile, "wb") + contents = "".join(n) + f.write(contents) + f.close() + objdef = """ + + + + + + %s + + + %s + + + + + + + + + + + + + + + + + + + + """ % (colorname, color, alpha, + bsdfname, colorname, + matname, bsdfname, + objfile, meshfile, + objfile+"."+objname, objfile+"."+objname, matname, matname) + + return objdef + + +def render(project,external=False): + + + if not project.PageResult: + return + p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Render") + if external: + rpath = p.GetString("AppleseedStudioPath","") + args = "" + else: + rpath = p.GetString("AppleseedCliPath","") + args = p.GetString("AppleseedParameters","") + if not rpath: + raise + if args: + args += " " + import os + os.system(rpath+" "+args+project.PageResult) + return + + diff --git a/renderers/Luxrender.py b/renderers/Luxrender.py new file mode 100644 index 00000000..035ab3a5 --- /dev/null +++ b/renderers/Luxrender.py @@ -0,0 +1,210 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2017 Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# Luxrender renderer for FreeCAD + +# This file can also be used as a template to add more rendering engines. +# You will need to make sure your file is named with a same name (case sensitive) +# That you will use everywhere to describe your renderer, ex: Appleseed or Povray + + +# A render engine module must contain the following functions: +# +# writeCamera(camdata): returns a string containing an openInventor camera string in renderer format +# writeObject(view): returns a string containing a RaytracingView object in renderer format +# render(project,external=True): renders the given project, external means if the user wishes to open +# the render file in an external application/editor or not. If this +# is not supported by your renderer, you can simply ignore it +# +# Additionally, you might need/want to add: +# +# Preference page items, that can be used in your functions below +# An icon under the name Renderer.svg (where Renderer is the name of your Renderer + + +import FreeCAD,math + + +def writeCamera(camdata): + + + # this is where you create a piece of text in the format of + # your renderer, that represents the camera. You can use the contents + # of obj.Camera, which contain a string in OpenInventor format + # ex: + # #Inventor V2.1 ascii + # + # + # PerspectiveCamera { + # viewportMapping ADJUST_CAMERA + # position 0 -1.3207401 0.82241058 + # orientation 0.99999666 0 0 0.26732138 + # nearDistance 1.6108983 + # farDistance 6611.4492 + # aspectRatio 1 + # focalDistance 5 + # heightAngle 0.78539819 + # + # } + # + # or (ortho camera): + # + # #Inventor V2.1 ascii + # + # + # OrthographicCamera { + # viewportMapping ADJUST_CAMERA + # position 0 0 1 + # orientation 0 0 1 0 + # nearDistance 0.99900001 + # farDistance 1.001 + # aspectRatio 1 + # focalDistance 5 + # height 4.1421356 + # + # } + + if not camdata: + return "" + camdata = camdata.split("\n") + cam = "" + pos = [float(p) for p in camdata[5].split()[-3:]] + pos = FreeCAD.Vector(pos) + rot = [float(p) for p in camdata[6].split()[-4:]] + rot = FreeCAD.Rotation(FreeCAD.Vector(rot[0],rot[1],rot[2]),math.degrees(rot[3])) + tpos = pos.add(rot.multVec(FreeCAD.Vector(0,0,-1))) + up = rot.multVec(FreeCAD.Vector(0,1,0)) + cam += "# declares position and view direction\n" + cam += "# Generated by FreeCAD (http://www.freecadweb.org/)\n" + cam += "LookAt " + str(pos.x) + " " + str(pos.y) + " " + str(pos.z) + " " + cam += str(tpos.x) + " " + str(tpos.y) + " " + str(tpos.z) + " " + cam += str(up.x) + " " + str(up.y) + " " + str(up.z) + "\n" + return cam + + +def writeObject(viewobj): + + + # This is where you write your object/view in the format of your + # renderer. "obj" is the real 3D object handled by this project, not + # the project itself. This is your only opportunity + # to write all the data needed by your object (geometry, materials, etc) + # so make sure you include everything that is needed + + if not viewobj.Source: + return "" + objdef = "" + obj = viewobj.Source + objname = viewobj.Name + color = None + alpha = None + mat = None + if viewobj.Material: + mat = viewobj.Material + else: + if "Material" in obj.PropertiesList: + if obj.Material: + mat = obj.Material + if mat: + if "Material" in mat.PropertiesList: + if "DiffuseColor" in mat.Material: + color = mat.Material["DiffuseColor"].strip("(").strip(")").split(",") + color = str(color[0])+" "+str(color[1])+" "+str(color[2]) + if "Transparency" in mat.Material: + if float(mat.Material["Transparency"]) > 0: + alpha = str(1.0/float(mat.Material["Transparency"])) + else: + alpha = "1.0" + if obj.ViewObject: + if not color: + if hasattr(obj.ViewObject,"ShapeColor"): + color = obj.ViewObject.ShapeColor[:3] + color = str(color[0])+" "+str(color[1])+" "+str(color[2]) + if not alpha: + if hasattr(obj.ViewObject,"Transparency"): + if obj.ViewObject.Transparency > 0: + alpha = str(1.0/(float(obj.ViewObject.Transparency)/100.0)) + if not color: + color = "1.0 1.0 1.0" + if not alpha: + alpha = "1.0" + m = None + if obj.isDerivedFrom("Part::Feature"): + import MeshPart + m = MeshPart.meshFromShape(Shape=obj.Shape, + LinearDeflection=0.1, + AngularDeflection=0.523599, + Relative=False) + elif obj.isDerivedFrom("Mesh::Feature"): + m = obj.Mesh + if not m: + return "" + P = "" + N = "" + tris = "" + for v in m.Topology[0]: + P += str(v.x) + " " + str(v.y) + " " + str(v.z) + " " + for n in m.getPointNormals(): + N += str(n.x) + " " + str(n.y) + " " + str(n.z) + " " + for t in m.Topology[1]: + tris += str(t[0]) + " " + str(t[1]) + " " + str(t[2]) + " " + + objdef += "MakeNamedMaterial \"" + objname + "_mat\"\n" + objdef += " \"color Kd\" [" + color + "]\n" + objdef += " \"float sigma\" [0.2]\n" + objdef += " \"string type\" [\"matte\"]\n" + objdef += "\n" + objdef += "AttributeBegin # \"" + objname + "\"\n" + objdef += "Transform [1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1]\n" + objdef += "NamedMaterial \"" + objname + "_mat\"\n" + objdef += "Shape \"mesh\"\n" + objdef += " \"integer triindices\" [" + tris + "]\n" + objdef += " \"point P\" [" + P + "]\n" + objdef += " \"normal N\" [" + N + "]\n" + objdef += " \"bool generatetangents\" [\"false\"]\n" + objdef += " \"string name\" [\"" + objname + "\"]\n" + objdef += "AttributeEnd # \"\"\n" + + return objdef + + +def render(project,external=True): + + + if not project.PageResult: + return + p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Render") + if external: + rpath = p.GetString("LuxRenderPath","") + args = "" + else: + rpath = p.GetString("LuxConsolePath","") + args = p.GetString("LuxParameters","") + if not rpath: + raise + if args: + args += " " + import os + os.system(rpath+" "+args+project.PageResult) + return + + diff --git a/renderers/Povray.py b/renderers/Povray.py new file mode 100644 index 00000000..a5f92afd --- /dev/null +++ b/renderers/Povray.py @@ -0,0 +1,224 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2017 Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# Povray renderer for FreeCAD + +# This file can also be used as a template to add more rendering engines. +# You will need to make sure your file is named with a same name (case sensitive) +# That you will use everywhere to describe your renderer, ex: Appleseed or Povray + + +# A render engine module must contain the following functions: +# +# writeCamera(camdata): returns a string containing an openInventor camera string in renderer format +# writeObject(view): returns a string containing a RaytracingView object in renderer format +# render(project,external=True): renders the given project, external means if the user wishes to open +# the render file in an external application/editor or not. If this +# is not supported by your renderer, you can simply ignore it +# +# Additionally, you might need/want to add: +# +# Preference page items, that can be used in your functions below +# An icon under the name Renderer.svg (where Renderer is the name of your Renderer + + +import FreeCAD,math + + +def writeCamera(camdata): + + + # this is where you create a piece of text in the format of + # your renderer, that represents the camera. You can use the contents + # of obj.Camera, which contain a string in OpenInventor format + # ex: + # #Inventor V2.1 ascii + # + # + # PerspectiveCamera { + # viewportMapping ADJUST_CAMERA + # position 0 -1.3207401 0.82241058 + # orientation 0.99999666 0 0 0.26732138 + # nearDistance 1.6108983 + # farDistance 6611.4492 + # aspectRatio 1 + # focalDistance 5 + # heightAngle 0.78539819 + # + # } + # + # or (ortho camera): + # + # #Inventor V2.1 ascii + # + # + # OrthographicCamera { + # viewportMapping ADJUST_CAMERA + # position 0 0 1 + # orientation 0 0 1 0 + # nearDistance 0.99900001 + # farDistance 1.001 + # aspectRatio 1 + # focalDistance 5 + # height 4.1421356 + # + # } + + if not camdata: + return "" + camdata = camdata.split("\n") + cam = "" + pos = [float(p) for p in camdata[5].split()[-3:]] + pos = FreeCAD.Vector(pos) + rot = [float(p) for p in camdata[6].split()[-4:]] + rot = FreeCAD.Rotation(FreeCAD.Vector(rot[0],rot[1],rot[2]),math.degrees(rot[3])) + tpos = rot.multVec(FreeCAD.Vector(0,0,-1)) + tpos.multiply(float(camdata[10].split()[-1])) + tpos = pos.add(tpos) + up = rot.multVec(FreeCAD.Vector(0,1,0)) + cam += "// declares position and view direction\n" + cam += "// Generated by FreeCAD (http://www.freecadweb.org/)\n" + cam += "#declare cam_location = <" + str(pos.x) + "," + str(pos.z) + "," + str(pos.y) + ">;\n" + cam += "#declare cam_look_at = <" + str(tpos.x) + "," + str(tpos.z) +"," + str(tpos.y) + ">;\n" + cam += "#declare cam_sky = <" + str(up.x) + "," + str(up.z) + "," + str(up.y) + ">;\n" + cam += "#declare cam_angle = 45;\n" + cam += "camera {\n" + cam += " location cam_location\n" + cam += " look_at cam_look_at\n" + cam += " sky cam_sky\n" + cam += " angle cam_angle\n" + cam += " right x*800/600\n" + cam += "}\n" + return cam + + +def writeObject(viewobj): + + + # This is where you write your object/view in the format of your + # renderer. "obj" is the real 3D object handled by this project, not + # the project itself. This is your only opportunity + # to write all the data needed by your object (geometry, materials, etc) + # so make sure you include everything that is needed + + if not viewobj.Source: + return "" + objdef = "" + obj = viewobj.Source + objname = viewobj.Name + color = None + alpha = None + mat = None + if viewobj.Material: + mat = viewobj.Material + else: + if "Material" in obj.PropertiesList: + if obj.Material: + mat = obj.Material + if mat: + if "Material" in mat.PropertiesList: + if "DiffuseColor" in mat.Material: + color = mat.Material["DiffuseColor"].strip("(").strip(")").split(",") + color = str(color[0])+","+str(color[1])+","+str(color[2]) + if "Transparency" in mat.Material: + if float(mat.Material["Transparency"]) > 0: + alpha = str(1.0/float(mat.Material["Transparency"])) + else: + alpha = "1.0" + if obj.ViewObject: + if not color: + if hasattr(obj.ViewObject,"ShapeColor"): + color = obj.ViewObject.ShapeColor[:3] + color = str(color[0])+","+str(color[1])+","+str(color[2]) + if not alpha: + if hasattr(obj.ViewObject,"Transparency"): + if obj.ViewObject.Transparency > 0: + alpha = str(1.0/(float(obj.ViewObject.Transparency)/100.0)) + if not color: + color = "1.0,1.0,1.0" + if not alpha: + alpha = "1.0" + m = None + if obj.isDerivedFrom("Part::Feature"): + import MeshPart + m = MeshPart.meshFromShape(Shape=obj.Shape, + LinearDeflection=0.1, + AngularDeflection=0.523599, + Relative=False) + elif obj.isDerivedFrom("Mesh::Feature"): + m = obj.Mesh + if not m: + return "" + objdef += "#declare " + objname + " = mesh2{\n" + objdef += " vertex_vectors {\n" + objdef += " " + str(len(m.Topology[0])) + ",\n" + for p in m.Topology[0]: + objdef += " <" + str(p.x) + "," + str(p.z) + "," + str(p.y) + ">,\n" + objdef += " }\n" + objdef += " normal_vectors {\n" + objdef += " " + str(len(m.Topology[0])) + ",\n" + for p in m.getPointNormals(): + objdef += " <" + str(p.x) + "," + str(p.z) + "," + str(p.y) + ">,\n" + objdef += " }\n" + objdef += " face_indices {\n" + objdef += " " + str(len(m.Topology[1])) + ",\n" + for t in m.Topology[1]: + objdef += " <" + str(t[0]) + "," + str(t[1]) + "," + str(t[2]) + ">,\n" + objdef += " }\n" + objdef += "}\n" + + objdef += "// instance to render\n" + objdef += "object {" + objname + "\n" + objdef += " texture {\n" + objdef += " pigment {\n" + objdef += " color rgb <" + color + ">\n" + objdef += " }\n" + objdef += " finish {StdFinish }\n" + objdef += " }\n" + objdef += "}\n" + + return objdef + + +def render(project,external=True): + + + if not project.PageResult: + return + p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Render") + rpath = p.GetString("PovRayPath","") + args = p.GetString("PovRayParameters","") + if not rpath: + raise + if args: + args += " " + import os + exe = rpath+" "+args+project.PageResult + print("Executing "+exe) + os.system(exe) + import ImageGui + print("Saving image as "+imgname) + imgname = os.path.splitext(project.PageResult)[0]+".png" + ImageGui.open(imgname) + return + + diff --git a/renderers/__init__.py b/renderers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/templates/RadiosityOutdoorHQ.pov b/templates/RadiosityOutdoorHQ.pov new file mode 100644 index 00000000..ef46ca1c --- /dev/null +++ b/templates/RadiosityOutdoorHQ.pov @@ -0,0 +1,42 @@ +// Persistence of Vision Ray Tracer Scene Description File +// for FreeCAD (http://www.freecadweb.org) + +#version 3.6; + +#include "colors.inc" +#include "metals.inc" +#include "rad_def.inc" + +global_settings { + radiosity { + Rad_Settings(Radiosity_OutdoorHQ,off,off) + } +} + +#default {finish{ambient 0}} + +sky_sphere { + pigment { + gradient y + color_map { + [0.0 color LightGray] + [0.3 color White] + [0.7 color LightGray] + } + } +} + +// Standard finish +#declare StdFinish = finish {}; + +//RaytracingCamera +//RaytracingContent + +//default light +light_source { + cam_location + color White + area_light <100, 0, 0>, <0, 0, 100>, 10, 10 + adaptive 1 + jitter +} diff --git a/templates/empty.appleseed b/templates/empty.appleseed new file mode 100644 index 00000000..dda7803a --- /dev/null +++ b/templates/empty.appleseed @@ -0,0 +1,108 @@ + + + + + + + + 0.1 0.1 0.1 + + + + + 0.3 0.3 0.4 + + + + + + + + + + + + + + + + + + + 1.000000000000000 0.000000000000000 0.000000000000000 0.000000000000000 + 0.000000000000000 1.000000000000000 0.000000000000000 0.000000000000000 + 0.000000000000000 0.000000000000000 1.000000000000000 0.000000000000000 + 0.000000000000000 0.000000000000000 0.000000000000000 1.000000000000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/RenderSettings.ui b/ui/RenderSettings.ui new file mode 100644 index 00000000..4409cfe5 --- /dev/null +++ b/ui/RenderSettings.ui @@ -0,0 +1,231 @@ + + + RaytracingGui::DlgSettingsRay + + + + 0 + 0 + 487 + 492 + + + + Render preferences + + + + 9 + + + 6 + + + + + Appleseed + + + + + + Appleseed command (cli) path + + + + + + + Appleseed studio path + + + + + + + The path to the Appleseed cli executable + + + AppleseedCliPath + + + Mod/Render + + + + + + + The path to the Appleseed studio executable (optional) + + + AppleseedStudioPath + + + Mod/Render + + + + + + + Render parameters + + + + + + + Optional rendering parameters to be passed to the Appleseed executable + + + + + + AppleseedParameters + + + Mod/Render + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Luxrender + + + + + + Luxrender UI path + + + + + + + Luxrender command (cli) path + + + + + + + The path to the Luxrender console (luxconsole) executable + + + LuxConsolePath + + + Mod/Render + + + + + + + The path to the luxrender UI executable + + + LuxRenderPath + + + Mod/Render + + + + + + + + + + PovRay + + + + + + PovRay executable path + + + + + + + The path to the Pov-Ray executable + + + PovRayPath + + + Mod/Render + + + + + + + Render parameters + + + + + + + Optional parameters to be passed to Pov-Ray when rendering + + + +P +A +W800 +H600 + + + PovRayParameters + + + Mod/Render + + + + + + + + + + + + Gui::FileChooser + QWidget +
Gui/FileDialog.h
+
+ + Gui::PrefFileChooser + Gui::FileChooser +
Gui/PrefWidgets.h
+
+ + Gui::PrefLineEdit + QLineEdit +
Gui/PrefWidgets.h
+
+
+ + +