EDIT: Cleaned up the code quite a bit, changed triangle creation to avoid wrapping around the edge of the shape map
Thanks Azraiyl, your primitive idea got me started doing this and your code was a huge help.
This class is builds a geom object from a three color image. Similar to a height map for terrain it uses the RGB of the image to get the XYZ of each of the models vertices.
SculptMaker.py
import math
import Image
from pandac.PandaModules import Geom
from pandac.PandaModules import GeomNode
from pandac.PandaModules import GeomPoints
from pandac.PandaModules import GeomTriangles
from pandac.PandaModules import GeomVertexData
from pandac.PandaModules import GeomVertexFormat
from pandac.PandaModules import GeomVertexWriter
from pandac.PandaModules import NodePath
class Sculpt:
def __init__(self, name, smap, tint):
self.name = name
self.smap = Image.open(smap)
self.dimx = self.smap.size[0]
self.dimy = self.smap.size[1]
self.tint = tint
def generate(self):
format = GeomVertexFormat.getV3n3c4t2()
data = GeomVertexData("Data", format, Geom.UHDynamic)
vertices = GeomVertexWriter(data, "vertex")
normal = GeomVertexWriter(data, 'normal')
colors = GeomVertexWriter(data, "color")
texcoord = GeomVertexWriter(data, 'texcoord')
triangles = GeomTriangles(Geom.UHDynamic)
self.vcount = 0
uv_unum = 1.0 / (self.dimx - 1)
uv_vnum = 1.0 / (self.dimy - 1)
u = 0.0
self.uvu = []
for i in range(self.dimx):
self.uvu.append(u)
u = u + uv_unum
v = 0.0
self.uvv = []
for i in range(self.dimy):
self.uvv.append(v)
v = v + uv_vnum
x = 0
y = 0
while y < self.dimy:
while x < self.dimx:
red, green, blue = self.smap.getpixel((x, y))
r = float(red) / 256.0 - 0.5
g = float(green) / 256.0 - 0.5
b = float(blue) / 256.0 - 0.5
vertices.addData3f(r, g, b)
normal.addData3f(r, g, b)
colors.addData4f(self.tint)
texcoord.addData2f(self.uvu[x], self.uvv[y])
x = x + 1
self.vcount = self.vcount + 1
x = 0
y = y + 1
self.vcount = self.vcount - 1
def makeFace(vert1, vert2, vert3, vert4):
triangles.addVertices(vert1, vert2, vert3)
triangles.addVertices(vert2, vert4, vert3)
triangles.closePrimitive()
q = 0
a = self.dimx
qcount = self.dimx
while qcount<self.vcount:
w = q + 1
s = a + 1
if ((w)%self.dimy == 0):
q = q + 1
a = a + 1
else:
makeFace(q,a,w,s)
q = q + 1
a = a + 1
qcount = qcount + 1
makeFace(q,a,w,s)
geom = Geom(data)
geom.addPrimitive(triangles)
node = GeomNode(self.name)
node.addGeom(geom)
return NodePath(node)
To use it just import it into your script like usual:
from SculptMaker import *
The Sculpt function takes 3 variables, name (a string), image map (the image for the model shape) and tint (four floats for the color of the mesh).
example:
import sys
import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject
from pandac.PandaModules import *
from SculptMaker import *
base.setBackgroundColor(0.0, 0.0, 0.0)
class Master(DirectObject):
def __init__(self):
self.accept('f1', self.wire)
self.accept('f2', self.tex)
self.accept("escape", sys.exit)
def tex(self):
base.toggleTexture()
def wire(self):
base.toggleWireframe()
cube = Sculpt("Test", "scu-easle.png", (1,1,1,1)).generate()
ts = TextureStage('ts')
uvmap = loader.loadTexture('wood-edge.png')
uvmap.setWrapU(Texture.WMRepeat)
uvmap.setWrapV(Texture.WMRepeat)
cube.setTexture(ts, uvmap)
cube.setTexOffset(ts, 0.0, 0.52);
cube.setTexRotate(ts, 90);
cube.setTexScale(ts, 4, 78)
cube.reparentTo(render)
cube.setScale(.75,.75,2)
# Create Ambient Light
ambientLight = AmbientLight('ambientLight')
ambientLight.setColor(Vec4(0.189, 0.189, 0.189, 0))
ambientLightNP = render.attachNewNode(ambientLight.upcastToPandaNode())
render.setLight(ambientLightNP)
# Directional light 01
directionalLight = DirectionalLight('directionalLight')
directionalLight.setColor(Vec4(0.889, 0.912, 0.778, 1))
directionalLightNP = render.attachNewNode(directionalLight)
directionalLightNP.setHpr(160, -20, 0)
render.setLight(directionalLightNP)
m = Master()
run()
Here’s a screenshot of what this example creates:
And these are the source images for the map and texture:
map:
texture:
Hope some folks find it useful, needs work of course but I think it’s off to a good start.
Enjoy!