#!BPY
"""
Name: 'VRM'
Blender: 241
Group: 'Export'
Tooltip: 'Vector Rendering Method Export Script 0.3'
"""
# ---------------------------------------------------------------------
# Copyright (c) 2006 Antonio Ospite
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ---------------------------------------------------------------------
#
# NOTE: I do not know who is the original author of 'vrm'.
# The present code is almost entirely rewritten from scratch,
# but if I have to give credits to anyone, please let me know,
# so I can update the copyright.
#
# ---------------------------------------------------------------------
#
# Additional credits:
# Thanks to Emilio Aguirre for S2flender from which I took inspirations :)
# Thanks to Anthony C. D'Agostino for the backface.py script
#
# ---------------------------------------------------------------------
import Blender
from Blender import Scene, Object, NMesh, Lamp, Camera
from Blender.Mathutils import *
from math import *
# ---------------------------------------------------------------------
#
## Projections classes
#
# ---------------------------------------------------------------------
class Projection:
def __init__(self):
print "New projection"
class PerspectiveProjection(Projection):
def __init___(self):
Projection.__init__(self)
print "Perspective"
def doProjection():
print "do a perspective projection!!"
def Perspective(fovy, aspect, near, far):
top = near * tan(fovy * pi / 360.0)
bottom = -top
left = bottom*aspect
right= top*aspect
x = (2.0 * near) / (right-left)
y = (2.0 * near) / (top-bottom)
a = (right+left) / (right-left)
b = (top+bottom) / (top - bottom)
c = - ((far+near) / (far-near))
d = - ((2*far*near)/(far-near))
return Matrix([x,0.0,a,0.0],[0.0,y,b,0.0],[0.0,0.0,c,d],[0.0,0.0,-1.0,0.0])
def flatten_new(v, cameraObj, canvasSize, obMesh):
cam = cameraObj.getInverseMatrix()
cam.transpose()
# Changing the view mode
cmra = cameraObj.getData()
#if cmra.type:
# print "Ortho"
#m2 = Ortho(fovy,float(w*ax)/float(h*ay),cmra.clipStart, cmra.clipEnd,17) #cmra.scale)
#else:
# print "Perspective"
#Create Frustum
#frustum = _Frustum(cam,m2)
m1 = Matrix()
mP = Matrix()
fovy = atan(0.5/(float(canvasSize[0])/float(canvasSize[1]))/(cmra.lens/32))
fovy = fovy * 360/pi
m2 = Perspective(fovy,float(canvasSize[0])/float(canvasSize[1]),cmra.clipStart, cmra.clipEnd)
m1 = obMesh.matrixWorld #mat
m1.transpose()
mP = cam * m1
mP = m2 * mP
#Transform the vertices to global coordinates
p = mP*Vector([v.co[0],v.co[1],v.co[2],1.0])
#tf.append(p)
#p = m1*Vector([v.co[0],v.co[1],v.co[2],1.0])
#t2.append([p[0],p[1],p[2]])
mW = canvasSize[0]/2
mH = canvasSize[1]/2
if p[3]<=0:
p[0] = int(p[0]*mW)+mW
p[1] = int(p[1]*mH)+mH
else:
p[0] = int((p[0]/p[3])*mW)+mW
p[1] = int((p[1]/p[3])*mH)+mH
# Mirror and translate along y
p[1] *= -1
p[1] += canvasSize[1]
return p
# distance from camera Z'
def Distance(PX,PY,PZ):
dist = sqrt(PX*PX+PY*PY+PZ*PZ)
return dist
def RotatePoint(PX,PY,PZ,AngleX,AngleY,AngleZ):
NewPoint = []
# Rotate X
NewY = (PY * cos(AngleX))-(PZ * sin(AngleX))
NewZ = (PZ * cos(AngleX))+(PY * sin(AngleX))
# Rotate Y
PZ = NewZ
PY = NewY
NewZ = (PZ * cos(AngleY))-(PX * sin(AngleY))
NewX = (PX * cos(AngleY))+(PZ * sin(AngleY))
PX = NewX
PZ = NewZ
# Rotate Z
NewX = (PX * cos(AngleZ))-(PY * sin(AngleZ))
NewY = (PY * cos(AngleZ))+(PX * sin(AngleZ))
NewPoint.append(NewX)
NewPoint.append(NewY)
NewPoint.append(NewZ)
return NewPoint
def flatten(vertx, verty, vertz, cameraObj, canvasSize):
camera = cameraObj.getData()
Lens = camera.getLens() # The Camera lens
xres = canvasSize[0] # X res for output
yres = canvasSize[1] # Y res for output
ratio = xres/yres
fov = atan(ratio * 16.0 / Lens) # Get fov stuff
dist = xres/2*tan(fov) # Calculate dist from pinhole camera to image plane
screenxy=[0,0,vertz]
x=-vertx
y=verty
z=vertz
#----------------------------
# calculate x'=dist*x/z & y'=dist*x/z
#----------------------------
screenxy[0]=int(xres/2.0+4*x*dist/z)
screenxy[1]=int(yres/2.0+4*y*dist/z)
return screenxy
## Backface culling routine
#
def isFaceVisible(face, obj, cameraObj):
"""
Determine if the face is visible from the current camera.
"""
numvert = len(face)
# backface culling
a = []
a.append(face[0][0])
a.append(face[0][1])
a.append(face[0][2])
a = RotatePoint(a[0], a[1], a[2], obj.RotX, obj.RotY, obj.RotZ)
a[0] += obj.LocX - cameraObj.LocX
a[1] += obj.LocY - cameraObj.LocY
a[2] += obj.LocZ - cameraObj.LocZ
b = []
b.append(face[1][0])
b.append(face[1][1])
b.append(face[1][2])
b = RotatePoint(b[0], b[1], b[2], obj.RotX, obj.RotY, obj.RotZ)
b[0] += obj.LocX - cameraObj.LocX
b[1] += obj.LocY - cameraObj.LocY
b[2] += obj.LocZ - cameraObj.LocZ
c = []
c.append(face[numvert-1][0])
c.append(face[numvert-1][1])
c.append(face[numvert-1][2])
c = RotatePoint(c[0], c[1], c[2], obj.RotX, obj.RotY, obj.RotZ)
c[0] += obj.LocX - cameraObj.LocX
c[1] += obj.LocY - cameraObj.LocY
c[2] += obj.LocZ - cameraObj.LocZ
norm = [0,0,0]
norm[0] = (b[1] - a[1])*(c[2] - a[2]) - (c[1] - a[1])*(b[2] - a[2])
norm[1] = -((b[0] - a[0])*(c[2] - a[2]) - (c[0] - a[0])*(b[2] - a[2]))
norm[2] = (b[0] - a[0])*(c[1] - a[1]) - (c[0] - a[0])*(b[1] - a[1])
d = norm[0]*a[0] + norm[1]*a[1] + norm[2]*a[2]
return (d<0)
# ---------------------------------------------------------------------
#
## Mesh representation class
#
# ---------------------------------------------------------------------
# TODO: a class to represent the needed properties of a 2D vector image
# ---------------------------------------------------------------------
#
## Vector Drawing Classes
#
# ---------------------------------------------------------------------
## A generic Writer
class VectorWriter:
"""
A class for printing output in a vectorial format.
Given a 2D representation of the 3D scene the class is responsible to
write it is a vector format.
Every subclasses of VectorWriter must have at last the following public
methods:
- printCanvas(mesh) --- where mesh is as specified before.
"""
def __init__(self, fileName, canvasSize):
"""Open the file named #fileName# and set the canvas size."""
self.file = open(fileName, "w")
print "Outputting to: ", fileName
self.canvasSize = canvasSize
# Public Methods
#
def printCanvas(mesh):
return
# Private Methods
#
def _printHeader():
return
def _printFooter():
return
## SVG Writer
class SVGVectorWriter(VectorWriter):
"""A concrete class for writing SVG output.
The class does not support animations, yet.
Sorry.
"""
def __init__(self, file, canvasSize):
"""Simply call the parent Contructor."""
VectorWriter.__init__(self, file, canvasSize)
# Public Methods
#
def printCanvas(self, mesh):
"""Convert the mesh representation to SVG."""
self._printHeader()
for obj in mesh:
for face in obj:
self._printPolygon(face)
self._printFooter()
# Private Methods
#
def _printHeader(self):
"""Print SVG header."""
self.file.write("\n")
self.file.write("\n")
self.file.close()
def _printPolygon(self, face):
"""Print our primitive, finally.
There is no color Handling for now, *FIX!*
"""
intensity = 128
stroke_width=1
self.file.write("\n")
# ---------------------------------------------------------------------
#
## Rendering Classes
#
# ---------------------------------------------------------------------
class Renderer:
"""Render a scene viewed from a given camera.
This class is responsible of the rendering process, hence transormation
and projection of the ojects in the scene are invoked by the renderer.
The user can optionally provide a specific camera for the rendering, see
the #doRendering# method for more informations.
"""
def __init__(self):
"""Set the canvas size to a defaulr value.
The only instance attribute here is the canvas size, which can be
queryed to the renderer by other entities.
"""
self.canvasSize = (0.0, 0.0)
# Public Methods
#
def getCanvasSize(self):
"""Return the current canvas size read from Blender rendering context"""
return self.canvasSize
def doRendering(self, scene, cameraObj=None):
"""Control the rendering process.
Here we control the entire rendering process invoking the operation
needed to transforma project the 3D scene in two dimensions.
Parameters:
scene --- the Blender Scene to render
cameraObj --- the camera object to use for the viewing processing
"""
if cameraObj == None:
cameraObj = scene.getCurrentCamera()
# TODO: given the camera get the Wold-to-camera transform and the
# projection matrix
context = scene.getRenderingContext()
self.canvasSize = (context.imageSizeX(), context.imageSizeY())
Objects = scene.getChildren()
# A mesh to store the transformed geometrical structure
mesh = []
for obj in Objects:
if (obj.getType() != "Mesh"):
print "Type:", obj.getType(), "\tSorry, only mesh Object supported!"
continue
OBJmesh = obj.getData() # Get the mesh data for the object
meshfaces = OBJmesh.faces # The number of faces in the object
transformed_object = []
for face in meshfaces:
# TODO: per face color calculation
# TODO: add/sorting in Z' direction (per face??)
# if the face is visible flatten it on the "picture plane"
if isFaceVisible(face, obj, cameraObj):
# Store transformed face
transformed_face = []
for vert in face:
vertxyz = list(vert)
p1 = flatten_new(vert, cameraObj, self.canvasSize,
obj)
transformed_face.append(p1)
continue
# rotate camera
vertxyz = RotatePoint(vertxyz[0], vertxyz[1], vertxyz[2],
cameraObj.RotX, cameraObj.RotY, cameraObj.RotZ)
#-cameraObj.RotX, -cameraObj.RotY, -cameraObj.RotZ)
# original setting for translate
vertxyz[0] -= (obj.LocX - cameraObj.LocX)
vertxyz[1] -= (obj.LocY - cameraObj.LocY)
vertxyz[2] -= (obj.LocZ - cameraObj.LocZ)
# rotate object
vertxyz = RotatePoint(vertxyz[0], vertxyz[1], vertxyz[2], obj.RotX, obj.RotY, obj.RotZ)
p1 = flatten(vertxyz[0], vertxyz[1], vertxyz[2],
cameraObj, self.canvasSize)
transformed_face.append(p1)
# just some fake lighting...
transformed_object.append(transformed_face)
# at the end of the loop on obj
mesh.append(transformed_object)
return mesh
# Private Methods
#
def _removehiddenFaces(obj):
return
def _testClipping(face):
return
# ---------------------------------------------------------------------
#
## Main Program
#
# ---------------------------------------------------------------------
scene = Scene.GetCurrent()
renderer = Renderer()
projectedMesh = renderer.doRendering(scene)
canvasSize = renderer.getCanvasSize()
# hackish sorting of faces according to the max z value of a vertex
for o in projectedMesh:
o.sort(lambda f1, f2:
cmp(sum([v[2] for v in f1])/len(f1), sum([v[2] for v in f2])/len(f2)))
o.reverse()
writer = SVGVectorWriter("proba.svg", canvasSize)
writer.printCanvas(projectedMesh)