The following code might not do exactly what you want (it doesn’t project shapes), but it might help with the interactive drawing (free-hand style, if I understood correctly):
from direct.showbase.ShowBase import ShowBase
from panda3d.core import *
import random
import array
base = ShowBase()
def create_terrain(x_size, y_size, max_height, resolution):
v_format = GeomVertexFormat.get_v3n3t2()
v_data = GeomVertexData("vertex_data", v_format, Geom.UH_static)
prim = GeomTriangles(Geom.UH_static)
mesh_data = array.array("f", [])
idx_data = array.array("H", [])
x_spacing = x_size / resolution
y_spacing = y_size / resolution
for i in range(resolution + 1):
x = x_spacing * i
u = i / resolution
for j in range(resolution + 1):
y = y_spacing * j
z = random.random() * max_height
v = j / resolution
mesh_data.extend([x, y, z, 0., 0., 1., u, v])
for i in range(resolution):
for j in range(resolution):
i1 = i * (resolution + 1) + j
i2 = i1 + resolution + 1
i3 = i2 + 1
i4 = i1 + 1
idx_data.extend([i1, i2, i3, i1, i3, i4])
vertex_count = (resolution + 1) * (resolution + 1)
v_data.unclean_set_num_rows(vertex_count)
view = memoryview(v_data.modify_array(0)).cast("B").cast("f")
view[:] = mesh_data
idx_array = prim.modify_vertices()
idx_array.unclean_set_num_rows(len(idx_data))
view = memoryview(idx_array).cast("B").cast("H")
view[:] = idx_data
geom = Geom(v_data)
geom.add_primitive(prim)
node = GeomNode("terrain_node")
node.add_geom(geom)
terrain_mesh = base.render.attach_new_node(node)
terrain_mesh.set_render_mode_filled_wireframe((1., 1., 1., 1.))
img = PNMImage(1, 1)
img.fill(0., 1., 0.)
tex = Texture()
tex.load(img)
terrain_mesh.set_texture(TextureStage.default, tex)
return terrain_mesh
# Setup camera
base.disable_mouse()
base.camera.set_hpr(10., -35, 0.)
base.camera.set_pos(10., -25., 25.)
# Setup a light source
p_light = PointLight("point_light")
base.light = base.camera.attach_new_node(p_light)
base.light.set_pos(5., -100., 7.)
base.render.set_light(base.light)
# Setup the terrain
terrain_x_size = 10.
terrain_y_size = 10.
terrain_mesh = create_terrain(terrain_x_size, terrain_y_size, 2., 6)
tex_size = 128
img = PNMImage(tex_size, tex_size, 4) # has alpha channel for decal mode
tex = Texture()
tex.load(img)
ts = TextureStage("paint")
ts.sort = 1
ts.mode = TextureStage.M_decal
terrain_mesh.set_texture(ts, tex)
# Setup texture painting
brush_color = (0., 0., 1., 1.)
brush_size = 5
brush = PNMBrush.make_spot(brush_color, brush_size, True)
painter = PNMPainter(img)
painter.set_pen(brush)
# Create canvas plane to paint on
cm = CardMaker("canvas")
cm.set_frame(0., 1., 0., 1.)
canvas = base.render.attach_new_node(cm.generate())
canvas.set_p(-90.)
canvas.set_scale(terrain_x_size, 1., terrain_y_size)
canvas.set_pos(0., 0., 10.)
canvas.set_texture(ts, tex)
plane = Plane(Vec3.up(), Point3(0., 0., 10.))
def paint(task):
if not base.mouseWatcherNode.is_button_down("mouse1"):
return task.cont
if not base.mouseWatcherNode.has_mouse():
return task.cont
mouse_pos = base.mouseWatcherNode.get_mouse()
world_pos = Point3()
near_point = Point3()
far_point = Point3()
base.camLens.extrude(mouse_pos, near_point, far_point)
near_point = base.render.get_relative_point(base.camera, near_point)
far_point = base.render.get_relative_point(base.camera, far_point)
if plane.intersects_line(world_pos, near_point, far_point):
canvas_pos = canvas.get_relative_point(base.render, world_pos)
x = canvas_pos.x * tex_size
y = (1. - canvas_pos.z) * tex_size
painter.draw_point(x, y)
tex.load(img)
return task.cont
base.task_mgr.add(paint, "paint")
base.run()
When you run the code sample, you can press and hold the left mouse button to “paint” onto the flat plane that hovers above the (procedurally-generated) terrain mesh (you should of course load your own terrain model).
The same texture is applied to both the plane and the terrain to make it easier to see how the painting follows the mouse cursor. It is applied in “decal” mode to preserve the color of the “brush” (in “modulate” mode it would get multiplied with the underlying color).
If there’s anything that requires clarification, don’t hesitate to ask!
At any rate, I hope it will prove helpful to some extent, at least.