Difference between revisions of "Talk:HowTo:Radiosity baking in Blender"

From VsWiki
Jump to: navigation, search
(Geometry changes)
m (Development Script: fixed icosphere size)
 
(34 intermediate revisions by 2 users not shown)
Line 1: Line 1:
== Image formatting ==
+
== Script talk ==
I'm putting out a request for help from the more experienced wiki users on a few matters:
+
* I moved the (GUI) script to an extra page so it is possible to download it here AND edit it with the wiki interface. See [[HowTo:Radiosity baking in Blender#Script]] and [[HowTo:Radiosity baking in Blender/vccopy.py]]--[[User:pontiac|Pontiac]] 16:47, 23 Sep 2005 (PDT)
* Image formatting: Is there a way of inserting some (vertical) space between the images and the text? Image captions? --[[User:tiny paintings|tiny paintings]] 12:22, 22 Sep 2005 (PDT)
 
** Space is possible via several ways (CSS padding/spacing, tables, etc...) where do you want to add spaces? --[[User:pontiac|Pontiac]] 13:10, 22 Sep 2005 (PDT)
 
*** Here: [[HowTo:Radiosity_baking_in_Blender#A_step_by_step_guide]]. --[[User:tiny paintings|tiny paintings]] 13:19, 22 Sep 2005 (PDT)
 
**** Ok, done (by using <nowiki><BR/></nowiki>s). Now the auto-numbering is correct again as well (no newlines). --[[User:pontiac|Pontiac]] 09:02, 23 Sep 2005 (PDT)
 
** images captions, thumbnails etc... are only (possible (at least easy) with the wiki-uploaded pictures. Depends on what you want to do. --[[User:pontiac|Pontiac]] 13:10, 22 Sep 2005 (PDT)
 
  
== Geometry changes ==
+
=== Vertex colors messed up again ===
I've followed the tutorial exactly and it does what i expected (not to the end though). But there are a few points that do not work as expected:
+
Thsi topic again :( Ok, i've tried to reproduce my perfect copied vertex colors with the original blender-file and it just doesn't work (in all cases). When i use the obj file (by importing it again... as i did at work) i get a fairly good result. But when using the blender-file (basically the source file to the obj) i get the messed up VCols again. :-/ It's a bit frustrating when i have to do the rad-process over and over again, just to find out that it didn't do it right. Did you get the .blend file i sent you via mail mail? Just purge the vertex colors of the mesh on the first layer and try doing the rad-process with that mesh. I dunno what i can do to improve this behaviour, but i'll try to help where i can. --[[User:pontiac|Pontiac]] 10:40, 26 Sep 2005 (PDT)
* The Mesh created by the radiosity progress has a different vertex-count than the source mesh (without the sphere and with <code>MaxEl</code> set to 1)--[[User:pontiac|Pontiac]] 12:29, 23 Sep 2005 (PDT)
+
----
** If i "Remove Doubles" (in the vertex-edit menu that is. The button in the radiosity menu doesn't really work) on the vertices then the vertex count is right again, but the order seems to be messed up. --[[User:pontiac|Pontiac]] 13:03, 23 Sep 2005 (PDT)
+
What the... please say you forgot to use the new script?!
*** I suspected that would happend (different vertex counts)... I'll rewrite that part to reflect how I actually did it. --[[User:tiny paintings|tiny paintings]] 15:55, 23 Sep 2005 (PDT)
+
Do you happend to have ICQ? If you do, my number is 1949713.
*** With regards to "messed up order": Can you be more specific? I designed the script explictily so that vertex order DOES NOT matter (well, you probably shouldn't move the meshes, though...). --[[User:tiny paintings|tiny paintings]] 16:03, 23 Sep 2005 (PDT)
 
**** What i meant has to do with the vertex-creation of the radiosity process. AFAIK the mess is created when vertices are duplicated and then merged again (via "Remove Doubles" in this case). It depends by what criteria you sort the vertices (x,y,z coords or other?) In theorie you wouldnt need a sorting if you compare the x,y,z coords and only copy the data when they are the same (i know that's not realistical) --[[User:pontiac|Pontiac]] 16:21, 23 Sep 2005 (PDT)
 
***** Does or does the script not work for you? I'm confused. --[[User:tiny paintings|tiny paintings]] 16:54, 23 Sep 2005 (PDT)
 
***** The order-mess is always created somehow afaik, or that's what my tests showed. Even if your original mesh has duplicate vertices the order gets screwed up after you bake the radiosity. Yes they are sorted on coordinate. True, sorting isn't needed but I figured python sort function should be blazing fast and complexity of my script isn't bad - in theory it could be O(|V|log|V|), where as a naive implementation of "compare and copy on match" is O(|V|^2). Anyhow, I don't think running times are a problem. --[[User:tiny paintings|tiny paintings]] 16:54, 23 Sep 2005 (PDT)
 
****** Ok, here is what i currently do: '''1.''' I follow your script until the "seperate the mesh from the sphere". This is not possible, because the mesh is somehow splitted into smaller sections (and it would give me dozens of objects when splitting) and thus have more vertices. '''2.''' to get the same amount of vertices i remove the sphere (duh) and then say "Edit"->"Vertices"->"Remove Doubles". But then i get some really messed up results when baking the texture on the original mesh. it 'looks' like the vertices are mixed up somehow. I'll put my test file up somewhere asap. i'm too tired right now :-/ --[[User:pontiac|Pontiac]] 17:12, 23 Sep 2005 (PDT)
 
****** Ok, here's the test file i mentioned above: [http://vegastrike.sourceforge.net/users/tiny_paintings/radiosity_baking_04_rad.blend radiosity_baking_04_rad.blend]<BR/>On the 1. layer is the original mesh (OB:"Cube") with the vertex colors already applied (and messed up). On the 3. layer ist the geometry that  was created by the rad-process (OB:"Mesh.002"). (you can ignor the 2. layer since it's just a copy of the 1. with the same material) --[[User:pontiac|Pontiac]] 01:30, 25 Sep 2005 (PDT)
 
******* Can't open it. Send it to seaghost at hellokitty.com, and include an .obj of the original mesh. --[[User:tiny paintings|tiny paintings]] 05:12, 25 Sep 2005 (PDT)
 
******** Uploaded it (+OBJ files) again:<BR/>[http://vegastrike.sourceforge.net/users/tiny_paintings/radiosity_baking_04_rad.blend radiosity_baking_04_rad.blend]<BR/>[http://vegastrike.sourceforge.net/users/tiny_paintings/radiosity_baking_04.obj radiosity_baking_04.obj]<BR/>[http://vegastrike.sourceforge.net/users/tiny_paintings/radiosity_baking_04.mtl radiosity_baking_04.mtl]<BR/>--09:15, 25 Sep 2005 (PDT)
 
******** I still can't get the blend file to work, or rather it seems I can't download it. It downloads at 0.3 kb/s and seems to cancel after a while, says it's done though. Weird, very weird. There other files download fast and are okay. I tried, however, the obj file. With success. Radiosity baked okay and the texture baked dandily too. Are you using the latest version of blender? --[[User:tiny paintings|tiny paintings]] 12:52, 25 Sep 2005 (PDT)
 
********* I'm using the Blender Bersion 2.37a here (most recent Debian package). Just sent you a mail with the file attached. Dunno why downlaod doesn't work for you. It certainly does for me :-/ --[[User:pontiac|Pontiac]] 13:08, 25 Sep 2005 (PDT)
 
********* Okay, I'm able to reproduce your problems now. After a little stdout-debugging I (think I) have found the problem. The object center changes after the radio baking => sorting is messed up because it's done on object space coords. As I see it there are two possible solutions: 1) Transform object space coords -> world space and sort on that (how??) 2) change the object centers (again, how??). --[[User:tiny paintings|tiny paintings]] 05:45, 26 Sep 2005 (PDT)
 
********** I have a working remedy. I did object -> world transformations. Might not be pretty but it works. I'll post it in a sec... --[[User:tiny paintings|tiny paintings]] 06:04, 26 Sep 2005 (PDT)
 
********** You might need the absolute coordiantes of the object and add them to the relatives (if i understand the documentation at http://www.blender3d.org/documentation/237PythonDoc/Object.Object-class.html#loc right that is)
 
<pre>object_coords = Blender.Object.Get(ObjectName).loc()</pre> I don't know if this is already with or without the objects transformation data. But that shouldt be a problem for this simple script. --[[User:pontiac|Pontiac]] 06:09, 26 Sep 2005 (PDT)
 
********** Ah, i've seen you answer too late ;) --[[User:pontiac|Pontiac]] 06:09, 26 Sep 2005 (PDT)
 
  
== Script Talk ==
+
I got the .blend file - it's the file I used to reproduce the bug I fixed. When I used the obj file I had no problems, even with the old script.
* There is a third point, but it was only a script error, which i fixed already. (a missing ":")
 
** I'm making some changes to the tooltip description of the new script, since it's a bit off, really. --[[User:tiny paintings|tiny paintings]] 15:55, 23 Sep 2005 (PDT)
 
*** I confess that i didn't check the tooltips before posting it here ;) Thanks --[[User:pontiac|Pontiac]] 16:21, 23 Sep 2005 (PDT)
 
*** Ah, now i know what you mean, the whole "radiosity data" things i wrote. I inserted that as a dummy (data generated by the radiosity process) ;) --[[User:pontiac|Pontiac]] 16:47, 23 Sep 2005 (PDT)
 
** Also, the tutorial is becoming rather cluttered with two scripts in it. Maybe you can put my original script in a file, say vccopy.py, and link to it? The GUI-capable script should be downloadable too to avoid the unnecessary copy-open_emacs-paste-save procedure. --[[User:tiny paintings|tiny paintings]] 15:55, 23 Sep 2005 (PDT)
 
*** If you have no problem with it i would vote to keep only the new (GUI) script (your script is 'nearly 1:1 in there), but keep the original script on the talk page for reference. (less clutter on the main page) ... concentrating on one single script is the way to go IMHO --[[User:pontiac|Pontiac]] 16:21, 23 Sep 2005 (PDT)
 
*** I moved the (GUI) script to an extra page so it is possible to download it here AND edit it with the wiki interface. See [[HowTo:Radiosity baking in Blender#Script]] and [[HowTo:Radiosity baking in Blender/vccopy.py]]--[[User:pontiac|Pontiac]] 16:47, 23 Sep 2005 (PDT)
 
  
== old Script==
+
Why do you have to relcalculate the radiosity? Isn't it only transfering the vertex colors to the original mesh that's causing trouble?
''This is the original Script by [[User:tiny paintings|tiny paintings]]. See the main page for the most recent version''
+
I put together a little debugging version of the script, [http://www.nada.kth.se/~gimaker/vs/vccopy_dbg.py here]. It dumps some debugging info to stdout. Use it to reproduce your errornous results and send the stdout to me (at seaghost at hellokitty.com, or whatever you find suiting). Also post screendumps if you think they'd be useful in finding the problem. --[[User:tiny paintings|tiny paintings]] 12:30, 26 Sep 2005 (PDT)
 +
 
 +
Maybe 'recalculate' is the wrong term... yes, transfering the vertex colors is the thing that doesn't work (or bette: it works, but there are vertices with the wrong 'shade')  with the blend file, but only with the obj. I'll do some debugging with your script tomorrow. I'm pretty sure that it's just a minor problem, that i (we) just didn't see. (PS: At the time i tested the new script with the matrix transform i only had the obj available (which worked), so testing the blend file was no option at that time :-/ )  --[[User:pontiac|Pontiac]] 15:16, 26 Sep 2005 (PDT)
 +
----
 +
I've been able to reproduce the symptoms you described, I believe. It's an accuracy issue... I'll look into it. --[[User:tiny paintings|tiny paintings]] 15:40, 26 Sep 2005 (PDT)
 +
 
 +
Here's the hack (you have to mess with acc till it gets right):
 +
<pre>
 +
...
 +
 
 +
acc = 0.001
 +
accmul = 1/acc
 +
 +
def accmod(x,y,z,i,j):
 +
return (round(x*accmul)*acc, round(y*accmul)*acc, round(z*accmul)*acc, i, j)
 +
 +
for i in range(len(me_to.faces)):
 +
for j in range(len(me_to.faces[i].v)):
 +
vert = me_to.faces[i].v[j].co
 +
l_to.append(accmod(vert[0], vert[1], vert[2], i, j))
 +
 +
for j in range(len(me_from.faces[i].v)):
 +
vert = me_from.faces[i].v[j].co
 +
l_from.append(accmod(vert[0], vert[1], vert[2], i, j))
 +
 
 +
...
 +
</pre>
 +
 
 +
The plan is rewriting it to be less of a hack (at the expense of quadratic running time)... But that should work, for now.
 +
----
 +
[http://www.nada.kth.se/~gimaker/vs/vccopy_n2.py Here's] a script that shouldn't be plauged by accuracy problems (well, not quite true as accuracy will always be a problem if have vertices close enough...). The downside is that it runs a lot slower. I'm working on speeding it up... (I need ''arrays'', hints anyone?) But first I need to sleeeep! --[[User:tiny paintings|tiny paintings]] 17:16, 26 Sep 2005 (PDT)
 +
----
 +
It seems to work with my .blend-testcase now. I'll test it some more today. And speed isn't really a problem since you (normally) do this once per mesh and hopefully never again after that. Or are there other scenarios? --[[User:pontiac|Pontiac]] 00:03, 27 Sep 2005 (PDT)
 +
* But it is almost painfully slow, even with a small test mesh. It requires <math>\Theta(|V|^2)</math> iterations so it's going to take a looong time for a large mesh. If I can't come up with anything else I, myself, would rather use the variant with truncating accuracy in the vertex coordinates.
 +
----
 +
Regarding arrays in python ... you might want to check out [http://www.penzilla.net/tutorial/python/numeric/] and [http://www-teaching.physics.ox.ac.uk/computing/ProgrammingResources/Oxford/handbook_Python_html/node35.html] Is that what you meant? --[[User:pontiac|Pontiac]] 00:23, 27 Sep 2005 (PDT)
 +
* Nah. array arrays only hold primitive types, it seems (I needed to store (nested) tuples). Random access in lists seems to be pretty fast in python, anyhow. --[[User:tiny paintings|tiny paintings]] 09:08, 27 Sep 2005 (PDT)
 +
 
 +
=== Possible improvments ===
 +
* Somehow change the script so that it sets the object centers correctly. Then we can remove the object->world transformation, sort on object space coords and and the vertex color copy would work even if one or both of the meshes are moved around. Not very urgent, just don't move the meshes around!
 +
 
 +
== Original script==
 +
''This is the original Script by [[User:tiny paintings|tiny paintings]]. See the main page for the most recent version.''
 +
 
 +
:'''Warning: This script contains a serious bug and will only work if your objects origin (the little purple dot) is in the origin.'''
  
 
You have to pardon my python n00bness, this is all I could come up with. Feel free to improve it!
 
You have to pardon my python n00bness, this is all I could come up with. Feel free to improve it!
Line 97: Line 114:
 
me_to.update()
 
me_to.update()
 
print "Copied vertex color information from object "+fromObj+" to object "+toObj+"."
 
print "Copied vertex color information from object "+fromObj+" to object "+toObj+"."
 +
</pre>
 +
 +
==Development Script==
 +
 +
Regarding the development script:
 +
 +
You're overdoing it. KISS. The purpose of the howto is to show the general technique for baking radiosity - ambient baking is a good application and serves as the example. And the script itself made ''only for transfering vertex color information''. Baking glowmaps is an application just as good as ambient baking, and even if you're baking ambient light you don't always want to utilize a sphere as the lightsource. And if you do want a sphere adding one is simple, and usually done once per project, so the manual labour time is minimal. --[[User:tiny paintings|tiny paintings]] 09:38, 27 Sep 2005 (PDT)
 +
 +
Oh, i'm just learning myself python by doing this little modification. i'm giving myself a few aims (inverting faces, applying materials, automatically positioning and scaling) and try to realize them. ''If'' it becomes useable without having any disadvantages i'll incorporate it into the main script, but we'll see.... the main purpose is to get a hold of the python language (and the Blender API). --[[User:pontiac|Pontiac]] 09:49, 27 Sep 2005 (PDT)
 +
 +
Ok, so i'm putting the API link here (as a reminder for myself, the blender3d.org search engine didn't even find it) ;)
 +
* [http://www.blender.org/modules/documentation/237PythonDoc/API_intro-module.html The Blender Python API Reference (Blender 2.37)]
 +
 +
<pre>
 +
#!BPY
 +
 +
""" Registration info for Blender menus: <- these words are ignored
 +
Name: 'Vertex Color Copy'
 +
Blender: 232
 +
Group: 'Object'
 +
Tip: 'Copies the vertex color information from one object to another.'
 +
"""
 +
 +
__author__ = ""
 +
__url__ = ("blender",
 +
"Script's homepage, http://vegastrike.sourceforge.net/wiki/HowTo:Radiosity_baking_in_Blender")
 +
__version__ = "233"
 +
 +
__bpydoc__ = """\
 +
"""
 +
 +
# WHAT DOES IT DO?
 +
# This script copies the vertex color information from one mesh
 +
# to another, and does so correctly assuming the meshes have the
 +
# same geometry, vertex-wise.
 +
 +
import math
 +
import Blender
 +
from Blender import *
 +
from Blender.Draw import *
 +
from Blender.BGL import *
 +
from math import pi,cos,sin
 +
 +
 +
fromObj = "Mesh.rad" # Set this to the resulting mesh from radio calcuation
 +
toObj = "Mesh.orig" # Set this to the name of your original, UV-mapped mesh.
 +
sphere_faces = 50
 +
ico_subd = 4
 +
fromObjx=Create(fromObj)
 +
toObjx=Create(toObj)
 +
sphere_facesx=Create(sphere_faces)
 +
ico_subdx=Create(ico_subd)
 +
 +
def obj_exists(name):
 +
objects=Blender.Object.Get()
 +
for object in objects:
 +
if object.name == name:
 +
return 1
 +
return 0
 +
 +
def mesh_exists(name):
 +
objects=Blender.Object.Get()
 +
for object in objects:
 +
if object.getType() == 'Mesh':
 +
mesh=object.getData()
 +
if mesh.name == name:
 +
return 1
 +
return 0
 +
 +
def icosphere(name='IcoSphere', n=0, normals_out=1): # n= number subds, normals_out=the normals of the faces points outwards
 +
#smooth=0
 +
radius=0.5
 +
me=NMesh.GetRaw()
 +
icovert = [
 +
[0.0,0.0,-2.0], #0
 +
[1.4472, -1.05144,-0.89443], #1
 +
[-0.55277, -1.70128,-0.89443], #2
 +
[-1.78885,0.0,-0.89443], #3
 +
[-0.55277,1.70128,-0.89443], #4
 +
[1.4472,1.05144,-0.89443], #5
 +
[0.55277,-1.70128,0.89443], #6
 +
[-1.4472,-1.05144,0.89443], #7
 +
[-1.4472,1.05144,0.89443], #8
 +
[0.55277,1.70128,0.89443], #9
 +
[1.78885,0.0,0.89443], #10
 +
[0.0,0.0,2.0]] #11
 +
 +
icoface = [
 +
[2,0,1],
 +
[1,0,5],
 +
[3,0,2],
 +
[4,0,3],
 +
[5,0,4],
 +
[1,5,10],
 +
[2,1,6],
 +
[3,2,7],
 +
[4,3,8],
 +
[5,4,9],
 +
[6,1,10],
 +
[7,2,6],
 +
[8,3,7],
 +
[9,4,8],
 +
[10,5,9],
 +
[6,10,11],
 +
[7,6,11],
 +
[8,7,11],
 +
[9,8,11],
 +
[10,9,11]]
 +
 +
for v in icovert: # v=vertex
 +
vn=NMesh.Vert(v[0]*radius*1.3333333/2, v[1]*radius*1.3333333/2, v[2]*radius*1.3333333/2)
 +
me.verts.append(vn)
 +
 +
for f in icoface: # f=face
 +
f_=NMesh.Face()
 +
 +
if not normals_out:
 +
f.reverse()
 +
 +
for v in f:
 +
f_.append(me.verts[v])
 +
#f_.smooth=smooth
 +
me.faces.append(f_)
 +
 +
if n>0:
 +
me.setSubDivLevels([n, n]) #Set Subdivisions. (Just the display+render mode, no real geometry created)
 +
me.setMode('SubSurf')
 +
mesh2=NMesh.PutRaw(me)
 +
mesh = Blender.NMesh.PutRaw(Blender.NMesh.GetRawFromObject(mesh2.name), 'light_sphere')
 +
mesh.setName(name)
 +
scene = Blender.Scene.getCurrent()
 +
scene.unlink(mesh2) # TODO: better solution?
 +
else:
 +
mesh=NMesh.PutRaw(me, name, 0)
 +
 +
return mesh
 +
 +
def sphere(name='SphereMesh', n=20, normals_out=1): # n= number of faces,normals_out= the normals of the faces points outwards
 +
#smooth=0
 +
radius=0.5
 +
me=NMesh.GetRaw()
 +
for i in range(0, n):
 +
for j in range(0, n):
 +
x=sin(j*pi*2.0/(n-1))*cos(-pi/2.0+i*pi/(n-1))*radius
 +
y=cos(j*pi*2.0/(n-1))*(cos(-pi/2.0+i*pi/(n-1)))*radius
 +
z=sin(-pi/2.0+i*pi/(n-1))*radius
 +
v=NMesh.Vert(x,y,z)
 +
me.verts.append(v)
 +
n0=len(range(0, n))
 +
for i in range(0, n-1):
 +
for j in range(0, n-1):
 +
f=NMesh.Face()
 +
if normals_out == 0:
 +
f.v.append(me.verts[i*n0+j])
 +
f.v.append(me.verts[i*n0+j+1])
 +
f.v.append(me.verts[(i+1)*n0+j+1])
 +
f.v.append(me.verts[(i+1)*n0+j])
 +
else:
 +
f.v.append(me.verts[(i+1)*n0+j])
 +
f.v.append(me.verts[(i+1)*n0+j+1])
 +
f.v.append(me.verts[i*n0+j+1])
 +
f.v.append(me.verts[i*n0+j])
 +
me.faces.append(f)
 +
me.faces.append(f)
 +
#f.smooth=smooth
 +
mesh=NMesh.PutRaw(me, name, 0)
 +
return mesh
 +
 +
def add_sphere(type=1):
 +
size_mult=2.0
 +
selected_objects = Blender.Object.GetSelected() # gets the selected objects
 +
midpoint=[0, 0, 0]
 +
max=[0, 0, 0]
 +
min=[0, 0, 0]
 +
if len(selected_objects) > 0:
 +
max=selected_objects[0].getBoundBox()[0]
 +
min=selected_objects[0].getBoundBox()[7]
 +
size=[1,1,1]
 +
object_counter=0
 +
for object in selected_objects:
 +
object_counter=object_counter+1
 +
BB=object.getBoundBox()
 +
bb_point= [0, 0, 0]
 +
for point in BB:
 +
if point[0]>max[0]: max[0]=point[0]
 +
if point[1]>max[1]: max[1]=point[1]
 +
if point[2]>max[2]: max[2]=point[2]
 +
if point[0]<min[0]: min[0]=point[0]
 +
if point[1]<min[1]: min[1]=point[1]
 +
if point[2]<min[2]: min[2]=point[2]
 +
bb_point=[
 +
bb_point[0]+point[0],
 +
bb_point[1]+point[1],
 +
bb_point[2]+point[2] ]
 +
midpoint=[
 +
midpoint[0]+bb_point[0]/8,
 +
midpoint[1]+bb_point[1]/8,
 +
midpoint[2]+bb_point[2]/8 ]
 +
 +
if object_counter > 0:
 +
midpoint = [
 +
midpoint[0]/object_counter,
 +
midpoint[1]/object_counter,
 +
midpoint[2]/object_counter]
 +
size = [
 +
max[0]-min[0],
 +
max[1]-min[1],
 +
max[2]-min[2] ]
 +
 +
if not mesh_exists('light_sphere') :
 +
 +
if type==1:
 +
sphere_object = icosphere('light_sphere', ico_subd, 0)
 +
elif type == 2:
 +
sphere_object = sphere('light_sphere', sphere_faces, 0)
 +
sphere_object.setName('light_sphere')
 +
 +
print "Object",sphere_object.name,"added."
 +
sphere_object.setDrawType(2) # set drawtype 2=wire
 +
sphere_object.setLocation(midpoint)
 +
sphere_object.setSize(size[0]*size_mult, size[1]*size_mult, size[2]*size_mult)
 +
sphere_mat = Material.New('light_sphere')
 +
print "Material",sphere_mat.name,"added."
 +
sphere_mat.rgbCol = [1.0, 1.0, 1.0]
 +
sphere_mat.emit = 0.02 # equivalent to mat.setEmit(xxx)
 +
me=sphere_object.getData()
 +
me.addMaterial(sphere_mat)
 +
me.update()
 +
print sphere_object.getData().materials
 +
Blender.Redraw()
 +
else:
 +
print "ERROR: Sphere already exists."
 +
Blender.Draw.PupMenu("ERROR%t|Sphere already exists.")
 +
 +
def copy_data():
 +
#global fromObj, toObj
 +
me_from = Blender.Object.Get(fromObj).getData()
 +
me_to = Blender.Object.Get(toObj).getData()
 +
 +
if not me_to and not me_from:
 +
print "ERROR: Source/destination object does not exist"
 +
Blender.Draw.PupMenu("ERROR%t|Source/destination object does not exist")
 +
 +
elif len(me_to.verts) != len(me_from.verts):
 +
print "ERROR: Source and destination objects must have the same number of vertices"
 +
Blender.Draw.PupMenu("ERROR%t|Source and destination objects must have the same number of vertices")
 +
 +
else:
 +
 +
# Transform objects to world space coordinates:
 +
me_to.transform(Blender.Object.Get(toObj).getMatrix())
 +
me_from.transform(Blender.Object.Get(fromObj).getMatrix())
 +
 +
 +
l_from = []
 +
 +
for i in range(len(me_from.faces)):
 +
for j in range(len(me_from.faces[i].v)):
 +
vert = me_from.faces[i].v[j].co
 +
l_from.append((vert, i, j))
 +
 +
 +
#me_to.faces[to_f].col[to_v] = me_from.faces[from_f].col[from_v]
 +
 +
def sqdist((x2,y2,z2), (x1,y1,z1)):
 +
return (x2-x1)**2 + (y2-y1)**2 + (z2-z1)**2
 +
 +
 +
for i in range(len(me_to.faces)):
 +
for j in range(len(me_to.faces[i].v)):
 +
# find nearest vertex in me_from:
 +
 +
list_index = -1
 +
least_sqdist = 0
 +
 +
for k in range(len(l_from)):
 +
d = sqdist(l_from[k][0], me_to.faces[i].v[j])
 +
 +
if list_index < 0 or d < least_sqdist:
 +
least_sqdist = d
 +
list_index = k
 +
 +
assert(list_index != -1)
 +
 +
face_index = l_from[list_index][1]
 +
vert_index = l_from[list_index][2]
 +
 +
me_to.faces[i].col[j] = me_from.faces[face_index].col[vert_index]
 +
 +
del l_from[list_index]
 +
 +
# Transform objects back to object space coordinates:
 +
me_to.transform(Blender.Object.Get(toObj).getInverseMatrix())
 +
me_from.transform(Blender.Object.Get(fromObj).getInverseMatrix())
 +
 +
me_to.update()
 +
print "Copied vertex color information from object "+fromObj+" to object "+toObj+"."
 +
Blender.Draw.PupMenu("SUCESS%t|Copied vertex color information from object "+fromObj+" to object "+toObj+".")
 +
 +
def EVENT(evt,val):
 +
  pass
 +
 +
def BUTTON(evt):
 +
if (evt==1):
 +
Exit()
 +
elif (evt==2):
 +
copy_data()
 +
elif (evt==3):
 +
add_sphere(1)
 +
elif (evt==4):
 +
add_sphere(2)
 +
Blender.Redraw()
 +
 +
def DRAW():
 +
global fromObj, toObj, fromObjx, toObjx, ico_subd, ico_subdx, sphere_faces, sphere_facesx
 +
 +
glClear(GL_COLOR_BUFFER_BIT)
 +
glColor3f(0.1, 0.1, 0.15)   
 +
 +
ligne=20
 +
#PushButton(name, event, x, y, width, height, tooltip=None)
 +
PushButton ("Exit",1,20,1,80,ligne)
 +
PushButton ("Copy Data",2,102,1,80,ligne)
 +
 +
glRasterPos2f(20, ligne*2-10)
 +
Text("Vertex Color Copy")
 +
glRasterPos2f(20, ligne*3-5)
 +
Text("To")
 +
# String(name, event, x, y, width, height, initial, length, tooltip=None)
 +
toObj = String('OB:', 999, 50, ligne*3-10, 200, 18, toObjx.val, 120, "The object to which the vertex colors should be copied." )
 +
 +
glRasterPos2f(20, ligne*4-5)
 +
Text("From")
 +
fromObj = String('OB:', 999, 50, ligne*4-10, 200, 18, fromObjx.val, 120, "The object from which the vertex colors should be copied." )
 +
 +
 +
PushButton ("Add IcoSphere", 3, 50, ligne*5-10, 90, ligne)
 +
#Slider(name, event, x, y, width, height, initial, min, max, realtime=1, tooltip=None)
 +
ico_subd = Slider('Subd:', 999, 50, ligne*6-10, 200, 18, ico_subdx.val, 0, 6, 1, "The subdivisions of the IcoSphere.")
 +
 +
PushButton ("Add Sphere", 4, 50, ligne*7-10, 90, ligne)
 +
sphere_faces = Slider('Sphere faces:', 999, 50, ligne*8-10, 200, 18, sphere_facesx.val, 20, 500, 1, "The faces of the sphere.")
 +
 +
 +
toObjx=toObj
 +
sphere_facesx = sphere_faces
 +
ico_subdx = ico_subd
 +
fromObjx=fromObj
 +
toObj=toObjx.val
 +
fromObj=fromObjx.val
 +
sphere_faces = sphere_facesx.val
 +
ico_subd = ico_subdx.val
 +
 +
 +
Register(DRAW,EVENT,BUTTON)
 
</pre>
 
</pre>

Latest revision as of 15:39, 28 September 2005

Script talk

Vertex colors messed up again

Thsi topic again :( Ok, i've tried to reproduce my perfect copied vertex colors with the original blender-file and it just doesn't work (in all cases). When i use the obj file (by importing it again... as i did at work) i get a fairly good result. But when using the blender-file (basically the source file to the obj) i get the messed up VCols again. :-/ It's a bit frustrating when i have to do the rad-process over and over again, just to find out that it didn't do it right. Did you get the .blend file i sent you via mail mail? Just purge the vertex colors of the mesh on the first layer and try doing the rad-process with that mesh. I dunno what i can do to improve this behaviour, but i'll try to help where i can. --Pontiac 10:40, 26 Sep 2005 (PDT)


What the... please say you forgot to use the new script?! Do you happend to have ICQ? If you do, my number is 1949713.

I got the .blend file - it's the file I used to reproduce the bug I fixed. When I used the obj file I had no problems, even with the old script.

Why do you have to relcalculate the radiosity? Isn't it only transfering the vertex colors to the original mesh that's causing trouble? I put together a little debugging version of the script, here. It dumps some debugging info to stdout. Use it to reproduce your errornous results and send the stdout to me (at seaghost at hellokitty.com, or whatever you find suiting). Also post screendumps if you think they'd be useful in finding the problem. --tiny paintings 12:30, 26 Sep 2005 (PDT)

Maybe 'recalculate' is the wrong term... yes, transfering the vertex colors is the thing that doesn't work (or bette: it works, but there are vertices with the wrong 'shade') with the blend file, but only with the obj. I'll do some debugging with your script tomorrow. I'm pretty sure that it's just a minor problem, that i (we) just didn't see. (PS: At the time i tested the new script with the matrix transform i only had the obj available (which worked), so testing the blend file was no option at that time :-/ ) --Pontiac 15:16, 26 Sep 2005 (PDT)


I've been able to reproduce the symptoms you described, I believe. It's an accuracy issue... I'll look into it. --tiny paintings 15:40, 26 Sep 2005 (PDT)

Here's the hack (you have to mess with acc till it gets right):

...

acc = 0.001
accmul = 1/acc
		
def accmod(x,y,z,i,j):
	return (round(x*accmul)*acc, round(y*accmul)*acc, round(z*accmul)*acc, i, j)		
	
for i in range(len(me_to.faces)):
	for j in range(len(me_to.faces[i].v)):
		vert = me_to.faces[i].v[j].co
		l_to.append(accmod(vert[0], vert[1], vert[2], i, j))
			
for j in range(len(me_from.faces[i].v)):
	vert = me_from.faces[i].v[j].co
		l_from.append(accmod(vert[0], vert[1], vert[2], i, j))

...

The plan is rewriting it to be less of a hack (at the expense of quadratic running time)... But that should work, for now.


Here's a script that shouldn't be plauged by accuracy problems (well, not quite true as accuracy will always be a problem if have vertices close enough...). The downside is that it runs a lot slower. I'm working on speeding it up... (I need arrays, hints anyone?) But first I need to sleeeep! --tiny paintings 17:16, 26 Sep 2005 (PDT)


It seems to work with my .blend-testcase now. I'll test it some more today. And speed isn't really a problem since you (normally) do this once per mesh and hopefully never again after that. Or are there other scenarios? --Pontiac 00:03, 27 Sep 2005 (PDT)

  • But it is almost painfully slow, even with a small test mesh. It requires <math>\Theta(|V|^2)</math> iterations so it's going to take a looong time for a large mesh. If I can't come up with anything else I, myself, would rather use the variant with truncating accuracy in the vertex coordinates.

Regarding arrays in python ... you might want to check out [1] and [2] Is that what you meant? --Pontiac 00:23, 27 Sep 2005 (PDT)

  • Nah. array arrays only hold primitive types, it seems (I needed to store (nested) tuples). Random access in lists seems to be pretty fast in python, anyhow. --tiny paintings 09:08, 27 Sep 2005 (PDT)

Possible improvments

  • Somehow change the script so that it sets the object centers correctly. Then we can remove the object->world transformation, sort on object space coords and and the vertex color copy would work even if one or both of the meshes are moved around. Not very urgent, just don't move the meshes around!

Original script

This is the original Script by tiny paintings. See the main page for the most recent version.

Warning: This script contains a serious bug and will only work if your objects origin (the little purple dot) is in the origin.

You have to pardon my python n00bness, this is all I could come up with. Feel free to improve it!

import Blender

# WHAT DOES IT DO?
# This script copies the vertex color information from one mesh
# to another, and does so correctly assuming the meshes have the
# same geometry, vertex-wise.

# IMPORTANT: Assumes meshes with exactly equal geometry, vertex-wise.

fromObj = "Mesh"  # Set this to the resulting mesh from radio calcuation
toObj = "Fuselage_default" # Set this to the name of your original, UV-mapped mesh.

me_from = Blender.Object.Get(fromObj).getData()
me_to = Blender.Object.Get(toObj).getData()

if not me_to and not me_from:
	print "ERROR: Source/destination object does not exist"

elif len(me_to.verts) != len(me_from.verts):
	print "ERROR: Source and destination objects must have the same number of vertices"
	
else:
	# make two lists sorted on coordinates,
	# containing a face and vertex index i and j respectively
	
	l_to = []
	l_from = []
	
	# format: [ (x,y,z, face index, vertex index), ... ]
	
	for i in range(len(me_to.faces)):
		for j in range(len(me_to.faces[i].v)):
			vert = me_to.faces[i].v[j].co
			l_to.append((vert[0], vert[1], vert[2], i, j))
			
		for j in range(len(me_from.faces[i].v)):
			vert = me_from.faces[i].v[j].co
			l_from.append((vert[0], vert[1], vert[2], i, j))
		
	# Sort the lists after vertex coordinates
	l_to.sort()	
	l_from.sort()
	
	for i in range(len(l_to)):
		to_f = l_to[i][3]
		from_f = l_from[i][3]
		to_v = l_to[i][4]
		from_v = l_from[i][4]
		
		me_to.faces[to_f].col[to_v] = me_from.faces[from_f].col[from_v]
			

	me_to.update()
	print "Copied vertex color information from object "+fromObj+" to object "+toObj+"."

Development Script

Regarding the development script:

You're overdoing it. KISS. The purpose of the howto is to show the general technique for baking radiosity - ambient baking is a good application and serves as the example. And the script itself made only for transfering vertex color information. Baking glowmaps is an application just as good as ambient baking, and even if you're baking ambient light you don't always want to utilize a sphere as the lightsource. And if you do want a sphere adding one is simple, and usually done once per project, so the manual labour time is minimal. --tiny paintings 09:38, 27 Sep 2005 (PDT)

Oh, i'm just learning myself python by doing this little modification. i'm giving myself a few aims (inverting faces, applying materials, automatically positioning and scaling) and try to realize them. If it becomes useable without having any disadvantages i'll incorporate it into the main script, but we'll see.... the main purpose is to get a hold of the python language (and the Blender API). --Pontiac 09:49, 27 Sep 2005 (PDT)

Ok, so i'm putting the API link here (as a reminder for myself, the blender3d.org search engine didn't even find it) ;)

#!BPY

""" Registration info for Blender menus: <- these words are ignored
Name: 'Vertex Color Copy'
Blender: 232
Group: 'Object'
Tip: 'Copies the vertex color information from one object to another.'
"""

__author__ = ""
__url__ = ("blender",
"Script's homepage, http://vegastrike.sourceforge.net/wiki/HowTo:Radiosity_baking_in_Blender")
__version__ = "233"

__bpydoc__ = """\
"""

# WHAT DOES IT DO?
# This script copies the vertex color information from one mesh
# to another, and does so correctly assuming the meshes have the
# same geometry, vertex-wise.

import math
import Blender
from Blender import *
from Blender.Draw import *
from Blender.BGL import *
from math import pi,cos,sin


fromObj = "Mesh.rad"		# Set this to the resulting mesh from radio calcuation
toObj = "Mesh.orig"		# Set this to the name of your original, UV-mapped mesh.
sphere_faces = 50
ico_subd = 4
fromObjx=Create(fromObj)
toObjx=Create(toObj)
sphere_facesx=Create(sphere_faces)
ico_subdx=Create(ico_subd)

def obj_exists(name):
	objects=Blender.Object.Get()
	for object in objects:
		if object.name == name:
			return 1
	return 0
	
def mesh_exists(name):
	objects=Blender.Object.Get()
	for object in objects:
		if object.getType() == 'Mesh':
			mesh=object.getData()
			if mesh.name == name:
				return 1
	return 0

def icosphere(name='IcoSphere', n=0, normals_out=1): # n= number subds, normals_out=the normals of the faces points outwards
	#smooth=0
	radius=0.5
	me=NMesh.GetRaw()
	icovert = [
		[0.0,0.0,-2.0],	#0
		[1.4472, -1.05144,-0.89443],	#1
		[-0.55277, -1.70128,-0.89443],	#2
		[-1.78885,0.0,-0.89443],	#3
		[-0.55277,1.70128,-0.89443],	#4
		[1.4472,1.05144,-0.89443],	#5
		[0.55277,-1.70128,0.89443],	#6
		[-1.4472,-1.05144,0.89443],	#7
		[-1.4472,1.05144,0.89443],	#8
		[0.55277,1.70128,0.89443],	#9
		[1.78885,0.0,0.89443],	#10
		[0.0,0.0,2.0]]	#11

	icoface = [
		[2,0,1],
		[1,0,5],
		[3,0,2],
		[4,0,3],	
		[5,0,4],
		[1,5,10],
		[2,1,6],
		[3,2,7],
		[4,3,8],
		[5,4,9],
		[6,1,10],
		[7,2,6],
		[8,3,7],
		[9,4,8],
		[10,5,9],
		[6,10,11],
		[7,6,11],
		[8,7,11],
		[9,8,11],
		[10,9,11]] 

	for v in icovert: # v=vertex
		vn=NMesh.Vert(v[0]*radius*1.3333333/2, v[1]*radius*1.3333333/2, v[2]*radius*1.3333333/2)
		me.verts.append(vn)

	for f in icoface: # f=face
		f_=NMesh.Face()
		
		if not normals_out:
			f.reverse()

		for v in f: 
			f_.append(me.verts[v])
		#f_.smooth=smooth
		me.faces.append(f_)
	
	if n>0:
		me.setSubDivLevels([n, n]) #Set Subdivisions. (Just the display+render mode, no real geometry created)
		me.setMode('SubSurf')	
		mesh2=NMesh.PutRaw(me)
		mesh = Blender.NMesh.PutRaw(Blender.NMesh.GetRawFromObject(mesh2.name), 'light_sphere')
		mesh.setName(name)
		scene = Blender.Scene.getCurrent()
		scene.unlink(mesh2)	# TODO: better solution?
	else:
		mesh=NMesh.PutRaw(me, name, 0)

	return mesh
	
def sphere(name='SphereMesh', n=20, normals_out=1):	# n= number of faces,normals_out= the normals of the faces points outwards
	#smooth=0
	radius=0.5
	me=NMesh.GetRaw() 
	for i in range(0, n): 
		for j in range(0, n):
			x=sin(j*pi*2.0/(n-1))*cos(-pi/2.0+i*pi/(n-1))*radius
			y=cos(j*pi*2.0/(n-1))*(cos(-pi/2.0+i*pi/(n-1)))*radius
			z=sin(-pi/2.0+i*pi/(n-1))*radius
			v=NMesh.Vert(x,y,z)
			me.verts.append(v)
	n0=len(range(0, n))
	for i in range(0, n-1): 
		for j in range(0, n-1): 
			f=NMesh.Face() 
			if normals_out == 0:
				f.v.append(me.verts[i*n0+j])
				f.v.append(me.verts[i*n0+j+1])
				f.v.append(me.verts[(i+1)*n0+j+1])
				f.v.append(me.verts[(i+1)*n0+j])
			else:
				f.v.append(me.verts[(i+1)*n0+j])
				f.v.append(me.verts[(i+1)*n0+j+1])
				f.v.append(me.verts[i*n0+j+1])
				f.v.append(me.verts[i*n0+j])
			me.faces.append(f) 
		me.faces.append(f)
		#f.smooth=smooth
	mesh=NMesh.PutRaw(me, name, 0)
	return mesh

def add_sphere(type=1):
	size_mult=2.0
	selected_objects = Blender.Object.GetSelected() # gets the selected objects
	midpoint=[0, 0, 0]
	max=[0, 0, 0]
	min=[0, 0, 0]
	if len(selected_objects) > 0:
		max=selected_objects[0].getBoundBox()[0]
		min=selected_objects[0].getBoundBox()[7]
	size=[1,1,1]
	object_counter=0
	for object in selected_objects:
		object_counter=object_counter+1
		BB=object.getBoundBox()
		bb_point= [0, 0, 0]
		for point in BB:
			if point[0]>max[0]:	max[0]=point[0]
			if point[1]>max[1]:	max[1]=point[1]
			if point[2]>max[2]:	max[2]=point[2]
			if point[0]<min[0]:	min[0]=point[0]
			if point[1]<min[1]:	min[1]=point[1]
			if point[2]<min[2]:	min[2]=point[2]
			bb_point=[
				bb_point[0]+point[0],
				bb_point[1]+point[1],
				bb_point[2]+point[2] ]
		midpoint=[
			midpoint[0]+bb_point[0]/8,
			midpoint[1]+bb_point[1]/8,
			midpoint[2]+bb_point[2]/8 ]

	if object_counter > 0:
		midpoint = [
			midpoint[0]/object_counter,
			midpoint[1]/object_counter,
			midpoint[2]/object_counter]
		size = [
			max[0]-min[0],
			max[1]-min[1],
			max[2]-min[2] ]

	if not mesh_exists('light_sphere') :
		
		if type==1:
			sphere_object = icosphere('light_sphere', ico_subd, 	0)
		elif type == 2:
			sphere_object = sphere('light_sphere', sphere_faces, 0)
			sphere_object.setName('light_sphere')
		
		print "Object",sphere_object.name,"added."
		sphere_object.setDrawType(2) # set drawtype 2=wire
		sphere_object.setLocation(midpoint)
		sphere_object.setSize(size[0]*size_mult, size[1]*size_mult, size[2]*size_mult)
		sphere_mat = Material.New('light_sphere')
		print "Material",sphere_mat.name,"added."
		sphere_mat.rgbCol = [1.0, 1.0, 1.0]
		sphere_mat.emit = 0.02				# equivalent to mat.setEmit(xxx)
		me=sphere_object.getData()
		me.addMaterial(sphere_mat)
		me.update()
		print sphere_object.getData().materials
		Blender.Redraw()
	else:
		print "ERROR: Sphere already exists."
		Blender.Draw.PupMenu("ERROR%t|Sphere already exists.")

def copy_data():
	#global fromObj, toObj
	me_from = Blender.Object.Get(fromObj).getData()
	me_to = Blender.Object.Get(toObj).getData()

	if not me_to and not me_from:
		print "ERROR: Source/destination object does not exist"
		Blender.Draw.PupMenu("ERROR%t|Source/destination object does not exist")

	elif len(me_to.verts) != len(me_from.verts):
		print "ERROR: Source and destination objects must have the same number of vertices"
		Blender.Draw.PupMenu("ERROR%t|Source and destination objects must have the same number of vertices")
	
	else:
		
		# Transform objects to world space coordinates:
		me_to.transform(Blender.Object.Get(toObj).getMatrix())
		me_from.transform(Blender.Object.Get(fromObj).getMatrix())
	

		l_from = []
			
		for i in range(len(me_from.faces)):
			for j in range(len(me_from.faces[i].v)):
				vert = me_from.faces[i].v[j].co
				l_from.append((vert, i, j))

		
		#me_to.faces[to_f].col[to_v] = me_from.faces[from_f].col[from_v]
		
		def sqdist((x2,y2,z2), (x1,y1,z1)):
			return (x2-x1)**2 + (y2-y1)**2 + (z2-z1)**2
		
		
		for i in range(len(me_to.faces)):
			for j in range(len(me_to.faces[i].v)):
				# find nearest vertex in me_from:
			
				list_index = -1
				least_sqdist = 0
			
				for k in range(len(l_from)):
					d = sqdist(l_from[k][0], me_to.faces[i].v[j])
					
					if list_index < 0 or d < least_sqdist:
						least_sqdist = d
						list_index = k
					
				assert(list_index != -1)
				
				face_index = l_from[list_index][1]
				vert_index = l_from[list_index][2]
				
				me_to.faces[i].col[j] = me_from.faces[face_index].col[vert_index]
				
				del l_from[list_index]

		# Transform objects back to object space coordinates:
		me_to.transform(Blender.Object.Get(toObj).getInverseMatrix())
		me_from.transform(Blender.Object.Get(fromObj).getInverseMatrix())

		me_to.update()
		print "Copied vertex color information from object "+fromObj+" to object "+toObj+"."
		Blender.Draw.PupMenu("SUCESS%t|Copied vertex color information from object "+fromObj+" to object "+toObj+".")

def EVENT(evt,val):
   pass

def BUTTON(evt):
	if (evt==1):
		Exit()
	elif (evt==2):
		copy_data()
	elif (evt==3):
		add_sphere(1)
	elif (evt==4):
		add_sphere(2)
	Blender.Redraw()

def DRAW():
	global fromObj, toObj, fromObjx, toObjx, ico_subd, ico_subdx, sphere_faces, sphere_facesx

	glClear(GL_COLOR_BUFFER_BIT)
	glColor3f(0.1, 0.1, 0.15)    

	ligne=20
	#PushButton(name, event, x, y, width, height, tooltip=None)
	PushButton ("Exit",1,20,1,80,ligne)
	PushButton ("Copy Data",2,102,1,80,ligne)

	glRasterPos2f(20, ligne*2-10)
	Text("Vertex Color Copy")
	glRasterPos2f(20, ligne*3-5)
	Text("To")
	# String(name, event, x, y, width, height, initial, length, tooltip=None)
	toObj =		String('OB:', 999, 50, ligne*3-10, 200, 18, toObjx.val, 120, "The object to which the vertex colors should be copied." )
	
	glRasterPos2f(20, ligne*4-5)
	Text("From")
	fromObj =	String('OB:', 999, 50, ligne*4-10, 200, 18, fromObjx.val, 120, "The object from which the vertex colors should be copied." )
	
	
	PushButton ("Add IcoSphere", 3, 50, ligne*5-10, 90, ligne)
	#Slider(name, event, x, y, width, height, initial, min, max, realtime=1, tooltip=None)
	ico_subd	=	Slider('Subd:', 999, 50, ligne*6-10, 200, 18, ico_subdx.val, 0, 6, 1, "The subdivisions of the IcoSphere.")
	
	PushButton ("Add Sphere", 4, 50, ligne*7-10, 90, ligne)
	sphere_faces	=	Slider('Sphere faces:', 999, 50, ligne*8-10, 200, 18, sphere_facesx.val, 20, 500, 1, "The faces of the sphere.")
	
	
	toObjx=toObj
	sphere_facesx = sphere_faces
	ico_subdx = ico_subd
	fromObjx=fromObj
	toObj=toObjx.val
	fromObj=fromObjx.val
	sphere_faces = sphere_facesx.val
	ico_subd = ico_subdx.val
	

Register(DRAW,EVENT,BUTTON)