diff --git a/src/mcedit2/rendering/chunkmeshes/entity/__init__.py b/src/mcedit2/rendering/chunkmeshes/entity/__init__.py new file mode 100644 index 0000000..cc5e699 --- /dev/null +++ b/src/mcedit2/rendering/chunkmeshes/entity/__init__.py @@ -0,0 +1,7 @@ +""" + __init__.py +""" +from __future__ import absolute_import, division, print_function, unicode_literals +import logging + +log = logging.getLogger(__name__) \ No newline at end of file diff --git a/src/mcedit2/rendering/chunkmeshes/entity/biped.py b/src/mcedit2/rendering/chunkmeshes/entity/biped.py new file mode 100644 index 0000000..e253818 --- /dev/null +++ b/src/mcedit2/rendering/chunkmeshes/entity/biped.py @@ -0,0 +1,80 @@ +""" + biped +""" +from __future__ import absolute_import, division, print_function, unicode_literals +import logging +from mcedit2.rendering.chunkmeshes.entity.modelrenderer import ModelRenderer + +log = logging.getLogger(__name__) + +class ModelBiped(object): + def __init__(self, expandOffset=0.0, headOffset=0.0): + self.bipedHead = ModelRenderer(self, 0, 0) + self.bipedHead.addBox(-4.0, -8.0, -4.0, 8, 8, 8, expandOffset) + self.bipedHead.setCenterPoint(0.0, 0.0 + headOffset, 0.0) + self.bipedHeadwear = ModelRenderer(self, 32, 0) + self.bipedHeadwear.addBox(-4.0, -8.0, -4.0, 8, 8, 8, expandOffset + 0.5) + self.bipedHeadwear.setCenterPoint(0.0, 0.0 + headOffset, 0.0) + self.bipedBody = ModelRenderer(self, 16, 16) + self.bipedBody.addBox(-4.0, 0.0, -2.0, 8, 12, 4, expandOffset) + self.bipedBody.setCenterPoint(0.0, 0.0 + headOffset, 0.0) + self.bipedRightArm = ModelRenderer(self, 40, 16) + self.bipedRightArm.addBox(-3.0, -2.0, -2.0, 4, 12, 4, expandOffset) + self.bipedRightArm.setCenterPoint(-5.0, 2.0 + headOffset, 0.0) + self.bipedLeftArm = ModelRenderer(self, 40, 16) + self.bipedLeftArm.mirror = True + self.bipedLeftArm.addBox(-1.0, -2.0, -2.0, 4, 12, 4, expandOffset) + self.bipedLeftArm.setCenterPoint(5.0, 2.0 + headOffset, 0.0) + self.bipedRightLeg = ModelRenderer(self, 0, 16) + self.bipedRightLeg.addBox(-2.0, 0.0, -2.0, 4, 12, 4, expandOffset) + self.bipedRightLeg.setCenterPoint(-1.9, 12.0 + headOffset, 0.0) + self.bipedLeftLeg = ModelRenderer(self, 0, 16) + self.bipedLeftLeg.mirror = True + self.bipedLeftLeg.addBox(-2.0, 0.0, -2.0, 4, 12, 4, expandOffset) + self.bipedLeftLeg.setCenterPoint(1.9, 12.0 + headOffset, 0.0) + + + @property + def parts(self): + return [ + self.bipedHead, + self.bipedHeadwear, + self.bipedBody, + self.bipedRightArm, + self.bipedLeftArm, + self.bipedRightLeg, + self.bipedLeftLeg + ] + + +class ModelZombie(ModelBiped): + modelTexture = "assets/minecraft/textures/entity/zombie/zombie.png" + id = "Zombie" + + +class ModelPigZombie(ModelBiped): + modelTexture = "assets/minecraft/textures/entity/zombie_pigman.png" + id = "PigZombie" + + +class ModelSkeleton(ModelBiped): + modelTexture = "assets/minecraft/textures/entity/skeleton/skeleton.png" + id = "Skeleton" + + def __init__(self, expandOffset=1.0, headOffset=0.0): + super(ModelSkeleton, self).__init__(expandOffset, headOffset) + + self.bipedRightArm = ModelRenderer(self, 40, 16) + self.bipedRightArm.addBox(-1.0, -2.0, -1.0, 2, 12, 2, expandOffset) + self.bipedRightArm.setCenterPoint(-5.0, 2.0, 0.0) + self.bipedLeftArm = ModelRenderer(self, 40, 16) + self.bipedLeftArm.mirror = True + self.bipedLeftArm.addBox(-1.0, -2.0, -1.0, 2, 12, 2, expandOffset) + self.bipedLeftArm.setCenterPoint(5.0, 2.0, 0.0) + self.bipedRightLeg = ModelRenderer(self, 0, 16) + self.bipedRightLeg.addBox(-1.0, 0.0, -1.0, 2, 12, 2, expandOffset) + self.bipedRightLeg.setCenterPoint(-2.0, 12.0, 0.0) + self.bipedLeftLeg = ModelRenderer(self, 0, 16) + self.bipedLeftLeg.mirror = True + self.bipedLeftLeg.addBox(-1.0, 0.0, -1.0, 2, 12, 2, expandOffset) + self.bipedLeftLeg.setCenterPoint(2.0, 12.0, 0.0) \ No newline at end of file diff --git a/src/mcedit2/rendering/chunkmeshes/entity/creeper.py b/src/mcedit2/rendering/chunkmeshes/entity/creeper.py new file mode 100644 index 0000000..1d01fd3 --- /dev/null +++ b/src/mcedit2/rendering/chunkmeshes/entity/creeper.py @@ -0,0 +1,41 @@ +""" + creeper +""" +from __future__ import absolute_import, division, print_function, unicode_literals +import logging +from mcedit2.rendering.chunkmeshes.entity.modelrenderer import ModelRenderer + +log = logging.getLogger(__name__) + + +class ModelCreeper(object): + modelTexture = "assets/minecraft/textures/entity/creeper/creeper.png" + id = "Creeper" + + def __init__(self): + headCenterHeight = 6 + head = ModelRenderer(self, 0, 0) + head.addBox(-4.0, -8.0, -4.0, 8, 8, 8) + head.setCenterPoint(0.0, headCenterHeight, 0.0) + # creeperArmor = ModelRenderer(self, 32, 0) + # creeperArmor.addBox(-4.0, -8.0, -4.0, 8, 8, 8) + # creeperArmor.setCenterPoint(0.0, headCenterHeight, 0.0) + body = ModelRenderer(self, 16, 16) + body.addBox(-4.0, 0.0, -2.0, 8, 12, 4) + body.setCenterPoint(0.0, headCenterHeight, 0.0) + leg1 = ModelRenderer(self, 0, 16) + leg1.addBox(-2.0, 0.0, -2.0, 4, 6, 4) + leg1.setCenterPoint(-2.0, (12 + headCenterHeight), 4.0) + leg2 = ModelRenderer(self, 0, 16) + leg2.addBox(-2.0, 0.0, -2.0, 4, 6, 4) + leg2.setCenterPoint(2.0, (12 + headCenterHeight), 4.0) + leg3 = ModelRenderer(self, 0, 16) + leg3.addBox(-2.0, 0.0, -2.0, 4, 6, 4) + leg3.setCenterPoint(-2.0, (12 + headCenterHeight), -4.0) + leg4 = ModelRenderer(self, 0, 16) + leg4.addBox(-2.0, 0.0, -2.0, 4, 6, 4) + leg4.setCenterPoint(2.0, (12 + headCenterHeight), -4.0) + + self.parts = [ + head, body, leg1, leg2, leg3, leg4 + ] diff --git a/src/mcedit2/rendering/chunkmeshes/entity/modelrenderer.py b/src/mcedit2/rendering/chunkmeshes/entity/modelrenderer.py new file mode 100644 index 0000000..abaaf9d --- /dev/null +++ b/src/mcedit2/rendering/chunkmeshes/entity/modelrenderer.py @@ -0,0 +1,60 @@ +""" + modelrenderer +""" +from __future__ import absolute_import, division, print_function, unicode_literals +import collections +import logging + +log = logging.getLogger(__name__) + + +class ModelBox(collections.namedtuple( + 'ModelBox', + 'x y z w h l expandOffset mirror', +)): + + @property + def x2(self): + return self.x + self.w + + @property + def y2(self): + return self.y + self.h + + @property + def z2(self): + return self.z + self.l + + @property + def dx(self): + return self.w + + @property + def dy(self): + return self.h + + @property + def dz(self): + return self.l + + +class ModelRenderer(object): + def __init__(self, parent, u, v): + self.parent = parent + self.boxes = [] + self.u = u + self.v = v + self.mirror = False + self.centerPoint = 0, 0, 0 + self.rotation = 0, 0, 0 + + def addBox(self, x, y, z, w, h, l, expandOffset=0.0, mirror=None): + if mirror is None: + mirror = self.mirror + self.boxes.append(ModelBox(x, y, z, w, h, l, expandOffset, mirror)) + + def setCenterPoint(self, x, y, z): + self.centerPoint = x, y, z + + def setRotation(self, rx, ry, rz): + self.rotation = rx, ry, rz diff --git a/src/mcedit2/rendering/chunkmeshes/entity/models.py b/src/mcedit2/rendering/chunkmeshes/entity/models.py new file mode 100644 index 0000000..0710564 --- /dev/null +++ b/src/mcedit2/rendering/chunkmeshes/entity/models.py @@ -0,0 +1,139 @@ +""" + models +""" +from __future__ import absolute_import, division, print_function, unicode_literals +import logging +import math +import numpy +from mcedit2.rendering.chunkmeshes.entity.biped import ModelZombie, ModelSkeleton, \ + ModelPigZombie +from mcedit2.rendering.chunkmeshes.entity.creeper import ModelCreeper +from mcedit2.rendering.chunkmeshes.entity.spider import ModelSpider + +log = logging.getLogger(__name__) + + +def makeQuad(c1, c2, c3, c4, u1, v1, u2, v2): + return [ + c1 + (u2, v1), + c2 + (u1, v1), + c3 + (u1, v2), + c4 + (u2, v2), + ] + + +def makeBoxQuads(u, v, box): + x, y, z = box.x, box.y, box.z + x2, y2, z2 = box.x2, box.y2, box.z2 + expandOffset = box.expandOffset + x -= expandOffset + y -= expandOffset + z -= expandOffset + x2 += expandOffset + y2 += expandOffset + z2 += expandOffset + + if box.mirror: + x, x2 = x2, x + + + xyz = x, y, z + x2yz = x2, y, z + xy2z = x, y2, z + xyz2 = x, y, z2 + x2y2z = x2, y2, z + x2yz2 = x2, y, z2 + xy2z2 = x, y2, z2 + x2y2z2 = x2, y2, z2 + dx, dy, dz = box.dx, box.dy, box.dz + + quadArgs = [ + (x2yz2, x2yz, x2y2z, x2y2z2, u + dz + dx, v + dz, u + dz + dx + dz, v + dz + dy), + (xyz, xyz2, xy2z2, xy2z, u, v + dz, u + dz, v + dz + dy), + (x2yz2, xyz2, xyz, x2yz, u + dz, v, u + dz + dx, v + dz ), + (x2y2z, xy2z, xy2z2, x2y2z2, u + dz + dx, v + dz, u + dz + dx + dx, v ), + (x2yz, xyz, xy2z, x2y2z, u + dz, v + dz, u + dz + dx, v + dz + dy), + (xyz2, x2yz2, x2y2z2, xy2z2, u + dz + dx + dz, v + dz, u + dz + dx + dz + dx, v + dz + dy), + ] + quads = [] + for a in quadArgs: + quad = makeQuad(*a) + if box.mirror: + quad = reversed(quad) + quads.extend(quad) + + return quads + + +# xxx dup from blockmodels.pyx +def npRotate(axis, angle, rescale=False): + # ( xx(1-c)+c xy(1-c)-zs xz(1-c)+ys 0 ) + # | yx(1-c)+zs yy(1-c)+c yz(1-c)-xs 0 | + # | xz(1-c)-ys yz(1-c)+xs zz(1-c)+c 0 | + # ( 0 0 0 1 ) + # axis: + # "x": (1, 0, 0) + # "y": (0, 1, 0) + # "z": (0, 0, 1) + x = y = z = 0 + if axis == "x": + x = 1 + elif axis == "y": + y = 1 + elif axis == "z": + z = 1 + else: + raise ValueError("Unknown axis: %r" % axis) + + s = math.sin(angle) + c = math.cos(angle) + rotate = numpy.matrix([[x*x*(1-c)+c, x*y*(1-c)-z*s, x*z*(1-c)+y*s, 0], + [y*x*(1-c)+z*s, y*y*(1-c)+c, y*z*(1-c)-x*s, 0], + [x*z*(1-c)-y*s, y*z*(1-c)+x*s, z*z*(1-c)+c, 0], + [0, 0, 0, 1]]) + # xxx rescale + return rotate + +def cookEntityModel(model): + allVerts = [] + for part in model.parts: + partVerts = [] + u, v = part.u, part.v + for box in part.boxes: + partVerts.extend(makeBoxQuads(u, v, box)) + + cx, cy, cz = part.centerPoint + rx, ry, rz = part.rotation + partMatrix = numpy.identity(4) + if ry: + partMatrix = npRotate("y", ry) * partMatrix + if rx: + partMatrix = npRotate("x", rx) * partMatrix + if rz: + partMatrix = npRotate("z", rz) * partMatrix + + for x, y, z, u, v in partVerts: + coord = numpy.matrix([x, y, z, 0]).T + x, y, z = numpy.array(partMatrix * coord)[:3, 0] + + allVerts.append((x+cx, y+cy, z+cz, u, v)) + + return allVerts + +cookedModels = {} +textures = {} + + +def addModel(model): + cookedModels[model.id] = cookEntityModel(model) + textures[model.id] = model.modelTexture + +addModel(ModelCreeper()) +addModel(ModelZombie()) +addModel(ModelSkeleton()) +addModel(ModelPigZombie()) +addModel(ModelSpider()) + +if __name__ == '__main__': + from pprint import pprint + pprint(cookedModels["Spider"]) \ No newline at end of file diff --git a/src/mcedit2/rendering/chunkmeshes/entity/spider.py b/src/mcedit2/rendering/chunkmeshes/entity/spider.py new file mode 100644 index 0000000..2e6ac92 --- /dev/null +++ b/src/mcedit2/rendering/chunkmeshes/entity/spider.py @@ -0,0 +1,76 @@ +""" + spider +""" +from __future__ import absolute_import, division, print_function, unicode_literals +import logging +import math +from mcedit2.rendering.chunkmeshes.entity.modelrenderer import ModelRenderer + +log = logging.getLogger(__name__) + +class ModelSpider(object): + id = "Spider" + modelTexture = "assets/minecraft/textures/entity/spider/spider.png" + + def __init__(self): + var1 = 0.0 + var2 = 15 + self.spiderHead = ModelRenderer(self, 32, 4) + self.spiderHead.addBox(-4.0, -4.0, -8.0, 8, 8, 8, var1) + self.spiderHead.setCenterPoint(0.0, var2, -3.0) + self.spiderNeck = ModelRenderer(self, 0, 0) + self.spiderNeck.addBox(-3.0, -3.0, -3.0, 6, 6, 6, var1) + self.spiderNeck.setCenterPoint(0.0, var2, 0.0) + self.spiderBody = ModelRenderer(self, 0, 12) + self.spiderBody.addBox(-5.0, -4.0, -6.0, 10, 8, 12, var1) + self.spiderBody.setCenterPoint(0.0, var2, 9.0) + self.spiderLeg1 = ModelRenderer(self, 18, 0) + self.spiderLeg1.addBox(-15.0, -1.0, -1.0, 16, 2, 2, var1) + self.spiderLeg1.setCenterPoint(-4.0, var2, 2.0) + self.spiderLeg2 = ModelRenderer(self, 18, 0) + self.spiderLeg2.addBox(-1.0, -1.0, -1.0, 16, 2, 2, var1) + self.spiderLeg2.setCenterPoint(4.0, var2, 2.0) + self.spiderLeg3 = ModelRenderer(self, 18, 0) + self.spiderLeg3.addBox(-15.0, -1.0, -1.0, 16, 2, 2, var1) + self.spiderLeg3.setCenterPoint(-4.0, var2, 1.0) + self.spiderLeg4 = ModelRenderer(self, 18, 0) + self.spiderLeg4.addBox(-1.0, -1.0, -1.0, 16, 2, 2, var1) + self.spiderLeg4.setCenterPoint(4.0, var2, 1.0) + self.spiderLeg5 = ModelRenderer(self, 18, 0) + self.spiderLeg5.addBox(-15.0, -1.0, -1.0, 16, 2, 2, var1) + self.spiderLeg5.setCenterPoint(-4.0, var2, 0.0) + self.spiderLeg6 = ModelRenderer(self, 18, 0) + self.spiderLeg6.addBox(-1.0, -1.0, -1.0, 16, 2, 2, var1) + self.spiderLeg6.setCenterPoint(4.0, var2, 0.0) + self.spiderLeg7 = ModelRenderer(self, 18, 0) + self.spiderLeg7.addBox(-15.0, -1.0, -1.0, 16, 2, 2, var1) + self.spiderLeg7.setCenterPoint(-4.0, var2, -1.0) + self.spiderLeg8 = ModelRenderer(self, 18, 0) + self.spiderLeg8.addBox(-1.0, -1.0, -1.0, 16, 2, 2, var1) + self.spiderLeg8.setCenterPoint(4.0, var2, -1.0) + + legYaw = 0.3926991 + legPitch = math.pi / 4 + self.spiderLeg1.setRotation(0, 2*legYaw, -legPitch) + self.spiderLeg2.setRotation(0, -2*legYaw, legPitch) + self.spiderLeg3.setRotation(0, legYaw, -legPitch*3/4.) + self.spiderLeg4.setRotation(0, -legYaw, legPitch*3/4.) + self.spiderLeg5.setRotation(0, -legYaw, -legPitch*3/4.) + self.spiderLeg6.setRotation(0, legYaw, legPitch*3/4.) + self.spiderLeg7.setRotation(0, -2*legYaw, -legPitch) + self.spiderLeg8.setRotation(0, 2*legYaw, legPitch) + + self.parts = [ + self.spiderHead, + self.spiderNeck, + self.spiderBody, + self.spiderLeg1, + self.spiderLeg2, + self.spiderLeg3, + self.spiderLeg4, + self.spiderLeg5, + self.spiderLeg6, + self.spiderLeg7, + self.spiderLeg8, + + ] \ No newline at end of file diff --git a/src/mcedit2/rendering/chunkmeshes/entitymesh.py b/src/mcedit2/rendering/chunkmeshes/entitymesh.py index e5b1630..a9fabe6 100644 --- a/src/mcedit2/rendering/chunkmeshes/entitymesh.py +++ b/src/mcedit2/rendering/chunkmeshes/entitymesh.py @@ -8,6 +8,7 @@ import numpy from mcedit2.rendering import renderstates, scenegraph from mcedit2.rendering.blockmeshes import standardCubeTemplates from mcedit2.rendering.blockmeshes import ChunkMeshBase +from mcedit2.rendering.chunkmeshes.entity import models from mcedit2.rendering.layers import Layer from mcedit2.rendering.scenegraph import PolygonModeNode, DepthFuncNode from mcedit2.rendering.slices import _XYZ @@ -126,6 +127,46 @@ class ItemFrameMesh(EntityMeshBase): PCPaintingEntityRefBase.EastFacing: ((0.01, 0, 0), (0.01, 1, 0), (0.01, 1, 1), (0.01, 0, 1)), } + +class MonsterModelRenderer(ChunkMeshBase): + def makeChunkVertices(self, chunk, limitBox): + sceneNode = scenegraph.Node() + for i, ref in enumerate(chunk.Entities): + ID = ref.id + if ID not in models.cookedModels: + assert ID != "Zombie" + ID = "Creeper" + + model = models.cookedModels[ID] + + modelVerts = numpy.array(model) + modelVerts.shape = modelVerts.shape[0]/4, 4, modelVerts.shape[1] + # scale down + modelVerts[..., :3] *= 1/16. + modelVerts[..., 1] = -modelVerts[..., 1] + 1.5 + 1/64. + modelVerts[..., 0] = -modelVerts[..., 0] + + vertexBuffer = QuadVertexArrayBuffer(len(modelVerts), lights=False, textures=True) + vertexBuffer.vertex[:] = modelVerts[..., :3] + vertexBuffer.texcoord[:] = modelVerts[..., 3:5] + + node = scenegraph.VertexNode(vertexBuffer) + rotateNode = scenegraph.RotateNode(ref.Rotation[0], (0., 1., 0.)) + rotateNode.addChild(node) + translateNode = scenegraph.TranslateNode((ref.Position - (chunk.cx << 4, 0, chunk.cz << 4))) + translateNode.addChild(rotateNode) + + modelTex = self.chunkUpdate.updateTask.getModelTexture(models.textures[ID]) + + textureNode = scenegraph.BindTextureNode(modelTex, (1./modelTex.w, 1./modelTex.h, 1)) + textureNode.addChild(translateNode) + sceneNode.addChild(textureNode) + + yield + + self.sceneNode = sceneNode + + class MonsterRenderer(EntityMeshBase): layer = Layer.Entities # xxx Monsters notMonsters = {"Item", "XPOrb", "Painting", "ItemFrame"} diff --git a/src/mcedit2/rendering/chunkupdate.py b/src/mcedit2/rendering/chunkupdate.py index da10b21..fffddad 100644 --- a/src/mcedit2/rendering/chunkupdate.py +++ b/src/mcedit2/rendering/chunkupdate.py @@ -10,7 +10,7 @@ from mcedit2.rendering.modelmesh import BlockModelMesh from mcedit2.rendering import layers from mcedit2.rendering.chunkmeshes.chunksections import ChunkSectionsRenderer from mcedit2.rendering.chunkmeshes.entitymesh import TileEntityMesh, MonsterRenderer, ItemRenderer, \ - ItemFrameMesh + ItemFrameMesh, MonsterModelRenderer from mcedit2.rendering.chunkmeshes.lowdetail import LowDetailBlockMesh, OverheadBlockMesh from mcedit2.rendering.chunkmeshes.terrainpop import TerrainPopulatedRenderer from mcedit2.rendering.chunkmeshes.tileticks import TileTicksRenderer @@ -125,6 +125,7 @@ class ChunkUpdate(object): TileEntityMesh, ItemFrameMesh, MonsterRenderer, + MonsterModelRenderer, ItemRenderer, TileTicksRenderer, TerrainPopulatedRenderer,