Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
459 lines (379 sloc)
15 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
"""Land classes - chunks, LandPlane, trees | |
""" | |
import random, time | |
from panda3d.core import Geom, GeomNode | |
from panda3d.core import GeomVertexData | |
from panda3d.core import GeomTriangles, GeomVertexWriter | |
from panda3d.core import GeomVertexFormat | |
from panda3d.core import Vec3, Vec2 | |
from panda3d.core import VBase3, VBase2, BitMask32 | |
from pandac.PandaModules import CardMaker | |
from pandac.PandaModules import NodePath | |
from pandac.PandaModules import TextureStage | |
from pandac.PandaModules import TransparencyAttrib | |
from voxplanet.support import make_square4v | |
from voxplanet.treegen import make_fractal_tree, treeform | |
from pandac.PandaModules import UnalignedLVecBase4f as UVec4 | |
from pandac.PandaModules import PTA_LVecBase4f as PTAVecBase4 | |
from pandac.PandaModules import Shader | |
from panda3d.core import RigidBodyCombiner as RBC | |
from voxplanet.shapeGenerator import Cube as CubeModel | |
from voxplanet.support import profile_decorator | |
from pandac.PandaModules import CollisionNode, CollisionPolygon | |
class LandNode(NodePath): | |
"""Water / Land | |
just square | |
""" | |
def __init__(self, sx, sy, z, length): | |
maker = CardMaker( 'land' ) | |
NodePath.__init__(self, maker.generate()) | |
#self.landNP = render.attachNewNode(maker.generate()) | |
self.setHpr(0,-90,0) | |
self.setPos(sx, sy, z) | |
self.setScale(length, 0, length) | |
class WaterNode(LandNode): | |
"""Water plane for nya | |
config - voxplanet.config | |
water_z - Z coord for water | |
""" | |
def __init__(self, sx, sy, length, tex): | |
LandNode.__init__(self, sx, sy, 0.5, length) | |
ts = TextureStage('ts') | |
self.setTransparency(TransparencyAttrib.MAlpha) | |
self.setTexture(ts, tex) | |
self.setTexScale(ts, 10000, 10000) | |
class LowTreeModel(NodePath): | |
"""Cube-like tree | |
""" | |
def __init__(self, name, size, body_tex = None, leaf_tex = None): | |
NodePath.__init__(self, name) | |
self.name = name | |
self.size = size | |
self.body_tex = body_tex | |
self.leaf_tex = leaf_tex | |
self.make() | |
self.flattenStrong() | |
def make(self): | |
self.body = CubeModel(*self.size) | |
self.body.geom_node.setIntoCollideMask(BitMask32.bit(2)) | |
self.body.reparentTo(self) | |
self.body.setTag('Tree', '{0}'.format(self.name)) | |
self.set_body_tex(self.body_tex) | |
start_leaf = random.randint(2, 4) | |
if start_leaf > self.size[2]-1: | |
start_leaf = self.size[2]-1 | |
self.leafs = [] | |
i = 0 | |
for z in xrange(start_leaf, self.size[2]-1): | |
for ix in xrange(-1, 1): | |
for iy in xrange(-1, 1): | |
size_x = random.randint(int((self.size[2] - z + 4)*0.2),int((self.size[2] - z + 4)*0.4)) | |
size_y = random.randint(int((self.size[2] - z + 4)*0.2),int((self.size[2] - z + 4)*0.4)) | |
if size_x == 0 or size_y == 0: | |
continue | |
self.leafs.append(CubeModel(size_x, size_y, 1)) | |
x = size_x * ix | |
y = size_y * iy | |
self.leafs[i].setPos(x, y, z) | |
self.leafs[i].reparentTo(self) | |
i += 1 | |
self.set_leaf_tex(self.leaf_tex) | |
def set_body_tex(self, body_tex): | |
if body_tex != None: | |
self.body.setTexture(body_tex) | |
def set_leaf_tex(self, leaf_tex): | |
if leaf_tex != None: | |
ts = TextureStage('ts') | |
for leaf in self.leafs: | |
leaf.setTexture(ts, leaf_tex) | |
leaf.setTexScale(ts, 10, 10) | |
leaf.setTransparency(TransparencyAttrib.MAlpha) | |
class TreeModel(NodePath): | |
def __init__(self, world, name, length, tex, leafModel, leafTex, | |
pos=Vec3(0, 0, 0), numIterations = 11, | |
numCopies = 6, vecList=[Vec3(0,0,1), | |
Vec3(1,0,0), Vec3(0,-1,0)]): | |
NodePath.__init__(self, name) | |
self.world = world | |
self.gui = self.world.gui | |
self.bodydata=GeomVertexData("body vertices", treeform, Geom.UHStatic) | |
self.length = length | |
self.pos = pos | |
self.numIterations = numIterations | |
self.numCopies = numCopies | |
self.vecList = vecList | |
self.tex = tex | |
self.leafModel = leafModel | |
self.leafTex = leafTex | |
make_fractal_tree(self.bodydata, self, self.length) | |
self.setTexture(self.tex, 1) | |
self.flattenStrong() | |
#self.buff = self.gui.win.makeCubeMap(name, 64, self) | |
class ForestNode(NodePath): | |
def __init__(self, config, world): | |
NodePath.__init__(self, 'ForestNode') | |
self.config = config | |
self.world = world | |
self.added = [] | |
self.trees = {} | |
self.trees_in_node = 10 | |
self.mutex = self.world.mutex_repaint | |
self.flatten_node = NodePath('flatten_nodes') | |
def add_trees(self, chunk): | |
# level | |
if chunk not in self.added: | |
self.added.append(chunk) | |
else: | |
return | |
size2 = chunk[1] / 2 | |
x = chunk[0][0] | |
y = chunk[0][1] | |
sx = x - size2 | |
sy = y - size2 | |
ex = x + size2 | |
ey = y + size2 | |
t = time.time() | |
trees = self.world.treeland[sx, sy, ex, ey] | |
for tree in trees: | |
if not self.trees.has_key(tree): | |
self.trees[tree] = [] | |
self.trees[tree] = self.trees[tree] + trees[tree] | |
def clear(self): | |
self.flatten_node.detachNode() | |
self.flatten_node.removeNode() | |
self.flatten_node = NodePath('flatten_nodes') | |
def show_forest(self, DX, DY, char_far, far, charpos, Force = False): | |
"""Set to new X | |
center X - self.size/2 - DX | |
""" | |
tmp_node = NodePath('tmp') | |
self.flatten_node.copyTo(tmp_node) | |
self.mutex.acquire() | |
tmp_node.reparentTo(self) | |
self.flatten_node.removeNode() | |
self.mutex.release() | |
self.tree_nodes = [] | |
self.flatten_node = NodePath('flatten_nodes') | |
t = time.time() | |
count_trees = 0 | |
for tree_n in self.trees: | |
# create or attach | |
for coord in self.trees[tree_n]: | |
#length_cam_2d = VBase2.length(Vec2(coord[0], coord[1]) - Vec2(charpos[0], charpos[1])) | |
length_cam_3d = VBase3.length(Vec3(coord) - Vec3(charpos)) | |
if char_far >= length_cam_3d <= far: | |
tree = self.world.trees[tree_n] | |
self.tree_nodes.append(NodePath('TreeNode')) | |
tree.copyTo(self.tree_nodes[count_trees]) | |
x, y, z = coord | |
self.tree_nodes[count_trees].setPos(x - DX, y - DY, z-1) | |
count_trees += 1 | |
print 'Attach detach loop: ', time.time() - t | |
if count_trees == 0: | |
self.mutex.acquire() | |
tmp_node.removeNode() | |
self.mutex.release() | |
return | |
t = time.time() | |
self.count_f_nodes = (count_trees / self.trees_in_node)+1 | |
self.flatten_nodes = [NodePath('flatten_node') for i in xrange(self.count_f_nodes)] | |
s = 0 | |
e = self.trees_in_node | |
added = 0 | |
for node in self.flatten_nodes: | |
t_nodes = self.tree_nodes[s : e] | |
s += self.trees_in_node | |
e += self.trees_in_node | |
for t_node in t_nodes: | |
t_node.reparentTo(node) | |
added += 1 | |
node.flattenStrong() | |
if not Force: | |
time.sleep(self.config.tree_sleep) | |
node.reparentTo(self.flatten_node) | |
if added == count_trees: | |
break | |
print 'Added: ', added, time.time() - t | |
t = time.time() | |
self.flatten_node.flattenStrong() | |
self.mutex.acquire() | |
self.flatten_node.reparentTo(self) | |
tmp_node.removeNode() | |
self.mutex.release() | |
print 'flatten all trees: ', time.time() - t | |
class Voxel(): | |
def __init__(self, empty, block): | |
self.empty = empty | |
self.block = block | |
self.get_top_uv = self.block.get_top_uv | |
self.get_uv = self.block.get_uv | |
def __call__(self): | |
return self.empty | |
class Voxels(dict): | |
def __init__(self, heights, get_coord_block): | |
self.heights = heights | |
self.get_coord_block = get_coord_block | |
self.repaint = [] | |
def __setitem__(self, key, voxel): | |
if self.has_key(key): | |
if item not in self.repaint: | |
self.repaint.append(key) | |
dict.__setitem__(self, key, voxel) | |
def check_repaint(self, chunk): | |
center, size, level = chunk | |
# TODO: check chunk for repaint | |
return False | |
def __getitem__(self, item): | |
if self.has_key(item): | |
return dict.__getitem__(self, item) | |
else: | |
x, y, z = item | |
if self.heights[x, y] > z: | |
empty = True | |
else: | |
empty = self.heights.check_empty(item) | |
block = self.get_coord_block(item) | |
vox = Voxel(empty, block) | |
self[item] = vox | |
return vox | |
class ChunkModel(NodePath): | |
"""Chunk for quick render and create voxel-objects | |
config - config of voxplanet | |
heights - {(centerX, centerY): height, (X2, Y2: height, ... (XN, YN): height} | |
centerX, centerY - center coordinates | |
size - size of chunk (in scene coords) | |
chunk_len - count of voxels | |
tex_uv_height - function return of uv coordinates for height voxel | |
tex - texture map | |
""" | |
dirty = False | |
def __init__(self, config, heights, voxels, X, Y, size, chunk_len, tex, water_tex): | |
NodePath.__init__(self, 'ChunkModel') | |
self.centerX = X | |
self.centerY = Y | |
self.config = config | |
self.heights = heights | |
self.tex = tex | |
self.water_tex = water_tex | |
self.size = size | |
self.chunk_len = chunk_len | |
self.size_voxel = self.size / self.chunk_len | |
if self.size_voxel <1: | |
self.size_voxel = 1 | |
self.size2 = self.size / 2 | |
self.start_x = self.centerX - self.size2 | |
self.start_y = self.centerY - self.size2 | |
self.voxels = voxels | |
self.v_format = GeomVertexFormat.getV3n3t2() | |
self.v_data = GeomVertexData('chunk', self.v_format, Geom.UHStatic) | |
self.create() | |
def create(self): | |
self.chunk_geom = GeomNode('self.chunk_geom') | |
vertex = GeomVertexWriter(self.v_data, 'vertex') | |
normal = GeomVertexWriter(self.v_data, 'normal') | |
texcoord = GeomVertexWriter(self.v_data, 'texcoord') | |
# make buffer of vertices | |
vertices = [] | |
n_vert = 0 | |
t = time.time() | |
tri=GeomTriangles(Geom.UHStatic) | |
def add_data(n_vert, v0, v1, v2, v3, voxel): | |
vertex.addData3f(v0) | |
vertex.addData3f(v1) | |
vertex.addData3f(v2) | |
vertex.addData3f(v3) | |
side1 = v0 - v1 | |
side2 = v0 - v3 | |
norm1 = side1.cross(side2) | |
side1 = v1 - v2 | |
side2 = v1 - v3 | |
norm2 = side1.cross(side2) | |
normal.addData3f(norm1) | |
normal.addData3f(norm1) | |
normal.addData3f(norm1) | |
normal.addData3f(norm2) | |
u1, v1, u2, v2 = voxel.get_uv() | |
texcoord.addData2f(v1, u1) | |
texcoord.addData2f(v2, u1) | |
texcoord.addData2f(v2, u2) | |
texcoord.addData2f(v1, u2) | |
tri.addConsecutiveVertices(0 + n_vert, 3) | |
tri.addVertex(2 + n_vert) | |
tri.addVertex(3 + n_vert) | |
tri.addVertex(0 + n_vert) | |
end = self.chunk_len - 1 | |
for x in xrange(0, self.chunk_len): | |
for y in xrange(0, self.chunk_len): | |
X = self.centerX - self.size2 + (x * self.size_voxel) | |
Y = self.centerY - self.size2 + (y * self.size_voxel) | |
dX = X + self.size_voxel | |
dY = Y + self.size_voxel | |
dx = x + 1 | |
dy = y + 1 | |
v0 = Vec3(x, dy, self.heights[X, dY]) | |
v1 = Vec3(x, y, self.heights[X, Y]) | |
v2 = Vec3(dx, y, self.heights[dX, Y]) | |
v3 = Vec3(dx, dy, self.heights[dX, dY]) | |
add_data(n_vert, v0, v1, v2, v3, self.voxels[X, Y, self.heights[X, Y]]) | |
n_vert += 4 | |
if x == 0: | |
v0 = Vec3(x, y, self.heights[X, Y] - self.size_voxel) | |
v1 = Vec3(x, y, self.heights[X, Y]) | |
v2 = Vec3(x, dy, self.heights[X, dY]) | |
v3 = Vec3(x, dy, self.heights[X, dY] - self.size_voxel) | |
add_data(n_vert, v0, v1, v2, v3, self.voxels[X, Y, self.heights[X, Y]]) | |
n_vert += 4 | |
if y == 0: | |
v0 = Vec3(dx, y, self.heights[dX, Y] - self.size_voxel) | |
v1 = Vec3(dx, y, self.heights[dX, Y]) | |
v2 = Vec3(x, y, self.heights[X, Y]) | |
v3 = Vec3(x, y, self.heights[X, Y] - self.size_voxel) | |
add_data(n_vert, v0, v1, v2, v3, self.voxels[X, Y, self.heights[X, Y]]) | |
n_vert += 4 | |
if x == end: | |
v0 = Vec3(dx, dy, self.heights[dX, dY] - self.size_voxel) | |
v1 = Vec3(dx, dy, self.heights[dX, dY]) | |
v2 = Vec3(dx, y, self.heights[dX, Y]) | |
v3 = Vec3(dx, y, self.heights[dX, Y] - self.size_voxel) | |
add_data(n_vert, v0, v1, v2, v3, self.voxels[X, Y, self.heights[X, Y]]) | |
n_vert += 4 | |
if y == end: | |
v0 = Vec3(x, dy, self.heights[X, dY] - self.size_voxel) | |
v1 = Vec3(x, dy, self.heights[X, dY]) | |
v2 = Vec3(dx, dy, self.heights[dX, dY]) | |
v3 = Vec3(dx, dy, self.heights[dX, dY] - self.size_voxel) | |
add_data(n_vert, v0, v1, v2, v3, self.voxels[X, Y, self.heights[X, Y]]) | |
n_vert += 4 | |
self.geom = Geom(self.v_data) | |
self.geom.addPrimitive(tri) | |
self.chunk_geom.addGeom( self.geom ) | |
self.chunk_np = self.attachNewNode('chunk_np') | |
self.chunk_np.attachNewNode(self.chunk_geom) | |
self.chunk_geom.setIntoCollideMask(BitMask32.bit(1)) | |
self.chunk_np.setTag('Chunk', 'Chunk: {0} {1} {2}'.format(self.centerX, self.centerY, self.size)) | |
#self.setTwoSided(True) | |
self.water = WaterNode(0, 0, self.size, self.water_tex) | |
self.water.setTwoSided(True) | |
self.water.reparentTo(self) | |
ts = TextureStage('ts') | |
self.chunk_np.setTexture(ts, self.tex) | |
self.chunk_np.setScale(self.size_voxel, self.size_voxel, 1) | |
#t = time.time() | |
# i love this function ^--^ | |
self.flattenStrong() | |
#print 'flatten chunk: ', time.time() - t | |
def setX(self, DX): | |
"""Set to new X | |
center X - self.size/2 - DX | |
""" | |
x = self.start_x - DX | |
NodePath.setX(self, x) | |
def setY(self, DY): | |
"""Set to new Y | |
center Y - self.size/2 - DY | |
""" | |
y = self.start_y - DY | |
NodePath.setY(self, y) | |
# vi: ft=python:tw=0:ts=4 |