Hi everyone!
After a lot of head bashing, I have managed to make something which is plausibly functional, a GoL board. However, I can’t seem to figure out why the empty cells all turn out white, when I set their vertex colors to what should be grey values.
Since it looks okay without the shader, I’m assuming the problem lies there.
Another funny thing I discovered is that the shader crashes when I try to send any more than 1024 uniforms. It doesn’t seem to matter whether they are floats or float4s. Is there any way around it?
I’m hoping this prototype will provide a basis for a game with a lots of tiles that need to switch textures rapidly.
__author__="Jon"
__date__ ="$Apr 6, 2011 8:42:59 AM$"
from direct.showbase.ShowBase import ShowBase
from direct.task.Task import Task
from direct.gui.OnscreenText import OnscreenText
from panda3d.core import GeomVertexFormat, GeomVertexData, Geom
from panda3d.core import GeomVertexWriter, GeomTriangles, GeomNode
from panda3d.core import Vec4, PTAFloat, Point3, Plane, Vec3, Mat4
from panda3d.core import PStatClient, TextNode
def makePlane(name='plane', width=1, height=1):
'''Make a flat plane Geomnode'''
width = 0.5 * width
height = 0.5 * height
format = GeomVertexFormat.getV3n3c4t2()
vdata = GeomVertexData(name, format, Geom.UHStatic)
vertex = GeomVertexWriter(vdata, 'vertex')
normal = GeomVertexWriter(vdata, 'normal')
color = GeomVertexWriter(vdata, 'color')
texcoord = GeomVertexWriter(vdata, 'texcoord')
vertex.addData3f(-width, -height, 0)
vertex.addData3f(width, -height, 0)
vertex.addData3f(-width, height, 0)
vertex.addData3f(width, height, 0)
normal.addData3f(0,0,1)
normal.addData3f(0,0,1)
normal.addData3f(0,0,1)
normal.addData3f(0,0,1)
color.addData4f(.5,.5,.5,1)
color.addData4f(.5,.5,.5,1)
color.addData4f(.5,.5,.5,1)
color.addData4f(.5,.5,.5,1)
texcoord.addData2f(0,0)
texcoord.addData2f(1,0)
texcoord.addData2f(0,1)
texcoord.addData2f(1,1)
#Define triangles
prim = GeomTriangles(Geom.UHStatic)
prim.addConsecutiveVertices(0,3)
prim.closePrimitive()
prim.addVertex(3)
prim.addVertex(2)
prim.addVertex(1)
prim.closePrimitive()
geom = Geom(vdata)
geom.addPrimitive(prim)
geomnode = GeomNode(name)
geomnode.addGeom(geom)
return geomnode
class World(ShowBase):
def __init__(self):
ShowBase.__init__(self)
#Create text
self.title = OnscreenText(text="Hardware-instanced Tiles - Game of Life",
fg=(1,1,1,1), pos=(-1.3, 0.95), scale=.07,
align=TextNode.ALeft)
self.atext = OnscreenText(text="A to draw cells",
fg=(1,1,1,1), pos=(-1.3, 0.88), scale=.05,
align=TextNode.ALeft)
self.stext = OnscreenText(text="S to Erase cells",
fg=(1,1,1,1), pos=(-1.3, 0.83), scale=.05,
align=TextNode.ALeft)
self.spacetext = OnscreenText(text="SPACE to toggle sim",
fg=(1,1,1,1), pos=(-1.3, 0.78), scale=.05,
align=TextNode.ALeft)
self.mousetext = OnscreenText(text="MOUSE to move camera",
fg=(1,1,1,1), pos=(-1.3, 0.73), scale=.05,
align=TextNode.ALeft)
#Set Board Parameters
self.xtiles = 50
self.ytiles = 50
self.xwidth = 2.0 / self.xtiles
self.yheight = 2.0 / self.ytiles
self.root = render.attachNewNode('Root')
self.tile = self.root.attachNewNode(makePlane(width=self.xwidth,
height=self.yheight))
self.root.setHpr(0,90,0)
self.root.setPos(0,4,0)
#Create plane object for intersection calculation
self.plane = Plane(Vec3(0,1,0), self.root.getPos())
self.instances = self.xtiles * self.ytiles
self.gridprops = Vec4(self.xtiles, self.ytiles, self.xwidth, self.yheight)
#The array to be sent to the shader
self.colors = PTAFloat.emptyArray(self.instances)
shader = loader.loadShader("shader2.sha")
self.root.setInstanceCount(self.instances)
self.root.setShaderInput("props", self.gridprops)
self.root.setShaderInput("cinfo", self.colors)
self.root.setShader(shader)
self.nextgen = [0]*self.instances
self.activated = False
taskMgr.add(self.iterateBoard, 'iterateBoard')
self.accept("space", self.toggleBoard)
self.accept("a", self.keyDown, [True])
self.accept("a-up", self.keyUp)
self.accept("s", self.keyDown, [False])
self.accept("s-up", self.keyUp)
#PStatClient.connect()
#END INIT
def keyDown(self, write):
taskMgr.add(self.drawTask, 'drawTask', extraArgs = [write], appendTask = True)
def keyUp(self):
taskMgr.remove('drawTask')
def toggleBoard(self):
if self.activated:
self.activated = False
else:
self.activated = True
def drawTask(self, write, task):
if self.mouseWatcherNode.hasMouse():
m2dpos = self.mouseWatcherNode.getMouse()
#Find the intersection between the mouse ray and the plane
pos3d = Point3()
nearPoint = Point3()
farPoint = Point3()
self.camLens.extrude(m2dpos, nearPoint, farPoint)
if self.plane.intersectsLine(pos3d,
render.getRelativePoint(camera, nearPoint),
render.getRelativePoint(camera, farPoint)):
plane_mat = Mat4()
plane_mat.invertFrom(self.root.getMat())
#Get the relative position after transforming by the inverse rotation matrix
mpos = plane_mat.xformVec(pos3d - self.root.getPos())
column = int((mpos.x + 0.5*self.xtiles*self.xwidth)/self.xwidth)
row = int((mpos.y + 0.5*self.ytiles*self.yheight)/self.yheight)
if (row >= 0 and column >=0 and
row < self.xtiles and column < self.ytiles):
#convert row/column to the array index
index = row * self.xtiles + column
if write:
self.colors[index] = 1
else:
self.colors[index] = 0
self.root.setShaderInput("cinfo", self.colors)
return Task.cont
def iterateBoard(self, task):
if self.activated:
#loop through the board
for y in xrange(self.ytiles):
for x in xrange(self.xtiles):
neighbours = 0
for ny in xrange(y-1, y+2):
for nx in xrange(x-1, x+2):
if ny == y and nx == x:
state = self.colors[ny * self.xtiles + nx]
elif ((0 <= ny < self.ytiles ) and (0 <= nx < self.xtiles)
and self.colors[ny * self.xtiles + nx] == 1):
neighbours += 1
if neighbours == 3:
self.nextgen[y * self.xtiles + x] = 1
elif neighbours == 2 and state == 1:
self.nextgen[y * self.xtiles + x] = 1
else:
self.nextgen[y * self.xtiles + x] = 0
#end loop
self.colors = PTAFloat(self.nextgen)
self.root.setShaderInput("cinfo", self.colors)
return Task.cont
app = World()
app.run()
//Cg
//Cg profile gp4vp gp4fp
struct VertexDataIN{
float4 vtx_position : POSITION;
float4 vtx_color : COLOR;
float2 vtx_texcoord0 : TEXCOORD0;
int l_id : INSTANCEID;
};
struct VertexDataOUT{
float4 o_position : POSITION;
float4 o_color : COLOR;
float2 o_texcoord0 : TEXCOORD0;
};
//vertex shader
void vshader(VertexDataIN IN,
out VertexDataOUT OUT,
uniform float4x4 mat_modelproj,
uniform float4 props,
uniform float4 cinfo[625])
{
//calculates vertex position based on grid properties and index number
float row;
float remainder = modf(IN.l_id/props.x, row);
float xpos = ((remainder - 0.5) * props.x + 0.5) * props.z;
float ypos = (((row-0.5 * props.y) + 0.5) * props.w);
float4 vpos = IN.vtx_position + float4(xpos, ypos,0,0);
OUT.o_position = mul(mat_modelproj, vpos);
//Get the color info from the array
float col = cinfo[IN.l_id/4][IN.l_id%4];
if (col == 0)
{
OUT.o_color = IN.vtx_color;
} else {
OUT.o_color = float4 (0,0,1,1);
}
OUT.o_texcoord0 = IN.vtx_texcoord0;
}
//fragment shader
void fshader(VertexDataOUT vIN,
uniform sampler2D tex0,
out float4 o_color : COLOR)
{
o_color = vIN.o_color;
}