270 lines
7.7 KiB
Python
270 lines
7.7 KiB
Python
#!BPY
|
|
"""
|
|
Name: 'Billboard Render on Active'
|
|
Blender: 242
|
|
Group: 'Image'
|
|
Tooltip: 'Selected objects and lamps to rendered faces on the act mesh'
|
|
"""
|
|
__author__= "Campbell Barton"
|
|
__url__= ["blender", "blenderartist"]
|
|
__version__= "1.0"
|
|
|
|
__bpydoc__= """\
|
|
Render Billboard Script
|
|
This can texture a simple billboard mesh from any number of selected objects.
|
|
|
|
Renders objects in the selection to quad faces on the active mesh.
|
|
|
|
Usage
|
|
* Light your model or enable the shadless flag so it is visible
|
|
* Make a low poly mesh out of quads with 90d corners. (this will be you billboard mesh)
|
|
* Select the model and any lamps that light it
|
|
* Select the billboard mesh so that it is active
|
|
* Run this script, Adjust settings such as image size or oversampling.
|
|
* Select a place to save the PNG image.
|
|
* Once the script has finished running return to the 3d view by pressing Shift+F5
|
|
* To see the newly applied textures change the drawtype to 'Textured Solid'
|
|
"""
|
|
# ***** BEGIN GPL LICENSE BLOCK *****
|
|
#
|
|
# Script copyright (C) Campbell J Barton 2006
|
|
#
|
|
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
#
|
|
# ***** END GPL LICENCE BLOCK *****
|
|
# --------------------------------------------------------------------------
|
|
|
|
import Blender
|
|
from Blender import Mesh, Material, Draw
|
|
import BPyMathutils
|
|
import bpy
|
|
import BPyRender
|
|
from Blender.Scene import Render
|
|
|
|
# reload(BPyRender)
|
|
# reload(BPyMathutils)
|
|
|
|
import os
|
|
Vector= Blender.Mathutils.Vector
|
|
|
|
def alpha_mat(image):
|
|
# returns a material useable for
|
|
mtl= bpy.data.materials.new()
|
|
mtl.mode |= (Material.Modes.SHADELESS | Material.Modes.ZTRANSP | Material.Modes.FULLOSA | Material.Modes.TEXFACE | Material.Modes.TEXFACE_ALPHA )
|
|
return mtl
|
|
|
|
# PupBlock Settings
|
|
GLOBALS= {}
|
|
PREF_RES= Draw.Create(512)
|
|
PREF_TILE_RES= Draw.Create(256)
|
|
PREF_AA = Draw.Create(1)
|
|
PREF_ALPHA= Draw.Create(1)
|
|
PREF_Z_OFFSET = Draw.Create(10.0)
|
|
PREF_IMG_PACK= Draw.Create(1)
|
|
|
|
|
|
def save_billboard(PREF_IMAGE_PATH):
|
|
Blender.Window.WaitCursor(1)
|
|
# remove png, add it later
|
|
PREF_IMAGE_PATH= PREF_IMAGE_PATH.replace('.png', '')
|
|
|
|
ob_sel= GLOBALS['ob_sel']
|
|
me_ob = GLOBALS['me_ob']
|
|
me_data = GLOBALS['me_data']
|
|
|
|
time= Blender.sys.time()
|
|
|
|
me_mat= me_ob.matrixWorld
|
|
|
|
# Render images for all faces
|
|
face_data= [] # Store faces, images etc
|
|
boxes2Pack= []
|
|
me_data.faceUV= True
|
|
|
|
for i, f in enumerate(me_data.faces):
|
|
no= f.no
|
|
|
|
# Offset the plane by the zoffset on the faces normal
|
|
plane= [v.co * me_mat for v in f]
|
|
|
|
# Horizontal stacking, make sure 0,1 and 2,3 are the longest
|
|
if\
|
|
(plane[0]-plane[1]).length + (plane[2]-plane[3]).length < \
|
|
(plane[1]-plane[2]).length + (plane[3]-plane[0]).length:
|
|
plane.append(plane.pop(0))
|
|
rot90= True
|
|
else:
|
|
rot90= False
|
|
|
|
no= Blender.Mathutils.QuadNormal(*plane)
|
|
plane= [v + no*PREF_Z_OFFSET.val for v in plane]
|
|
|
|
cent= (plane[0]+plane[1]+plane[2]+plane[3] ) /4.0
|
|
camera_matrix= BPyMathutils.plane2mat(plane)
|
|
tmp_path= '%s_%d' % (PREF_IMAGE_PATH, i)
|
|
img= BPyRender.imageFromObjectsOrtho(ob_sel, tmp_path, PREF_TILE_RES.val, PREF_TILE_RES.val, PREF_AA.val, PREF_ALPHA.val, camera_matrix)
|
|
img.reload()
|
|
#img.pack() # se we can keep overwriting the path
|
|
#img.filename= ""
|
|
|
|
if rot90:
|
|
f.uv=Vector(1,1), Vector(0,1), Vector(0,0), Vector(1,0)
|
|
else:
|
|
f.uv= Vector(0,1), Vector(0,0), Vector(1,0), Vector(1,1)
|
|
|
|
if not PREF_IMG_PACK.val:
|
|
f.mode |= Mesh.FaceModes.TEX
|
|
f.image = img
|
|
|
|
if PREF_ALPHA.val:
|
|
f.transp |= Mesh.FaceTranspModes.ALPHA
|
|
else:
|
|
w= ((plane[0]-plane[1]).length + (plane[2]-plane[3]).length)/2
|
|
h= ((plane[1]-plane[2]).length + (plane[3]-plane[0]).length)/2
|
|
|
|
face_data.append( (f, img) )
|
|
boxes2Pack.append( [0.0,0.0,h, w, i] )
|
|
|
|
if PREF_IMG_PACK.val:
|
|
# pack the quads into a square
|
|
packWidth, packHeight = Blender.Geometry.BoxPack2D(boxes2Pack)
|
|
|
|
render_obs= []
|
|
|
|
render_mat= alpha_mat(img)
|
|
|
|
# Add geometry to the mesh
|
|
for box in boxes2Pack:
|
|
i= box[4]
|
|
|
|
orig_f, img= face_data[i]
|
|
|
|
# New Mesh and Object
|
|
|
|
render_me= bpy.data.meshes.new()
|
|
|
|
render_ob= Blender.Object.New('Mesh')
|
|
render_me.materials= [render_mat]
|
|
render_ob.link(render_me)
|
|
|
|
render_obs.append(render_ob)
|
|
|
|
# Add verts clockwise from the bottom left.
|
|
_x= box[0] / packWidth
|
|
_y= box[1] / packHeight
|
|
_w= box[2] / packWidth
|
|
_h= box[3] / packHeight
|
|
|
|
|
|
render_me.verts.extend([\
|
|
Vector(_x, _y, 0),\
|
|
Vector(_x, _y +_h, 0),\
|
|
Vector(_x + _w, _y +_h, 0),\
|
|
Vector(_x + _w, _y, 0),\
|
|
])
|
|
|
|
render_me.faces.extend(list(render_me.verts))
|
|
render_me.faceUV= True
|
|
|
|
render_me.faces[0].uv = [Vector(0,0), Vector(0,1), Vector(1,1), Vector(1,0)]
|
|
render_me.faces[0].image = img
|
|
|
|
# Set the UV's, we need to flip them HOZ?
|
|
for uv in orig_f.uv:
|
|
uv.x = _x + (uv.x * _w)
|
|
uv.y = _y + (uv.y * _h)
|
|
|
|
target_image= BPyRender.imageFromObjectsOrtho(render_obs, PREF_IMAGE_PATH, PREF_RES.val, PREF_RES.val, PREF_AA.val, PREF_ALPHA.val, None)
|
|
target_image.reload() # incase your overwriting an existing image.
|
|
|
|
# Set to the 1 image.
|
|
for f in me_data.faces:
|
|
f.image= target_image
|
|
if PREF_ALPHA.val:
|
|
f.transp |= Mesh.FaceTranspModes.ALPHA
|
|
|
|
# Free the images data and remove
|
|
for data in face_data:
|
|
img= data[1]
|
|
os.remove(img.filename)
|
|
img.reload()
|
|
|
|
# Finish pack
|
|
|
|
me_data.update()
|
|
me_ob.makeDisplayList()
|
|
Blender.Window.WaitCursor(0)
|
|
print '%.2f secs taken' % (Blender.sys.time()-time)
|
|
|
|
|
|
def main():
|
|
scn= bpy.data.scenes.active
|
|
ob_sel= list(scn.objects.context)
|
|
|
|
PREF_KEEP_ASPECT= False
|
|
|
|
# Error Checking
|
|
if len(ob_sel) < 2:
|
|
Draw.PupMenu("Error%t|Select 2 mesh objects")
|
|
return
|
|
|
|
me_ob= scn.objects.active
|
|
|
|
if not me_ob:
|
|
Draw.PupMenu("Error%t|No active mesh selected.")
|
|
|
|
try:
|
|
ob_sel.remove(me_ob)
|
|
except:
|
|
pass
|
|
|
|
if me_ob.type != 'Mesh':
|
|
Draw.PupMenu("Error%t|Active Object must be a mesh to write billboard images too")
|
|
return
|
|
|
|
me_data= me_ob.getData(mesh=1)
|
|
|
|
for f in me_data.faces:
|
|
if len(f) != 4:
|
|
Draw.PupMenu("Error%t|Active mesh must have only quads")
|
|
return
|
|
|
|
|
|
# Get user input
|
|
block = [\
|
|
'Image Pixel Size',\
|
|
("Packed Size: ", PREF_RES, 128, 2048, "Pixel width and height to render the billboard to"),\
|
|
("Tile Size: ", PREF_TILE_RES, 64, 1024, "Pixel width and height for each tile to render to"),\
|
|
'Render Settings',\
|
|
("Pack Final", PREF_IMG_PACK , "Pack the image for each face into images into a single image"),\
|
|
("Oversampling", PREF_AA , "Higher quality woth extra sampling"),\
|
|
("Alpha Clipping", PREF_ALPHA , "Render empty areas as transparent"),\
|
|
("Cam ZOffset: ", PREF_Z_OFFSET, 0.1, 100, "Distance to place the camera away from the quad when rendering")\
|
|
]
|
|
|
|
if not Draw.PupBlock("Billboard Render", block):
|
|
return
|
|
|
|
# Set globals
|
|
GLOBALS['ob_sel'] = ob_sel
|
|
GLOBALS['me_ob'] = me_ob
|
|
GLOBALS['me_data'] = me_data
|
|
|
|
Blender.Window.FileSelector(save_billboard, 'SAVE BILLBOARD', Blender.sys.makename(ext='.png'))
|
|
# save_billboard('/tmp/test.png')
|
|
|
|
if __name__=='__main__':
|
|
main()
|