# HG changeset patch # User Emmanuel Gil Peyrot # Date 1362510610 -3600 # Node ID 8265ef6db380389a1cad4c06d34ae0cd6c3ced2b Hello Gensokyo _o/ diff --git a/io_scene_touhou/__init__.py b/io_scene_touhou/__init__.py new file mode 100644 --- /dev/null +++ b/io_scene_touhou/__init__.py @@ -0,0 +1,82 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + + +bl_info = { + "name": "Touhou stage format (.std)", + "author": "Emmanuel Gil Peyrot", + "version": (0, 0), + "blender": (2, 66, 0), + "location": "File > Import-Export > Touhou Stage (.std) ", + "description": "Import-Export Touhou Stage", + "warning": "", + "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/" + "Scripts/Import-Export/Raw_Mesh_IO", + "tracker_url": "https://projects.blender.org/tracker/index.php?" + "func=detail&aid=25692", + "category": "Import-Export"} + +if "bpy" in locals(): + import imp + if "import_std" in locals(): + imp.reload(import_std) +else: + import bpy + +from bpy.props import StringProperty + + +class StageImporter(bpy.types.Operator): + """Load Touhou triangle mesh data""" + bl_idname = "import_mesh.std" + bl_label = "Import Touhou" + bl_options = {'UNDO'} + + filepath = StringProperty( + subtype='FILE_PATH', + ) + filter_glob = StringProperty(default="stage*.std", options={'HIDDEN'}) + + def execute(self, context): + from . import import_std + import_std.read(self.filepath) + return {'FINISHED'} + + def invoke(self, context, event): + wm = context.window_manager + wm.fileselect_add(self) + return {'RUNNING_MODAL'} + + +def menu_import(self, context): + self.layout.operator(StageImporter.bl_idname, text="Touhou Stage (.std)") + + +def register(): + bpy.utils.register_module(__name__) + bpy.types.INFO_MT_file_import.append(menu_import) + + +def unregister(): + bpy.utils.unregister_module(__name__) + bpy.types.INFO_MT_file_import.remove(menu_import) + +if __name__ == "__main__": + register() diff --git a/io_scene_touhou/import_std.py b/io_scene_touhou/import_std.py new file mode 100644 --- /dev/null +++ b/io_scene_touhou/import_std.py @@ -0,0 +1,150 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + +""" +This script imports Touhou EoSD stages files to Blender. + +Usage: +Execute this script from the "File->Import" menu and choose a Touhou +stage file to open. +""" + + +from pytouhou.formats.std import Stage +from pytouhou.formats.anm0 import ANM0 +from pytouhou.resource.anmwrapper import AnmWrapper +from pytouhou.vm.anmrunner import ANMRunner +from pytouhou.game.sprite import Sprite +from pytouhou.ui.sprite import get_sprite_rendering_data +import bpy +import os.path + + +def build_models(stage, anm_wrapper): + """Taken from pytouhou.game.background.""" + models = [] + for obj in stage.models: + quads = [] + for script_index, ox, oy, oz, width_override, height_override in obj.quads: + sprite = Sprite(width_override, height_override) + anm_runner = ANMRunner(anm_wrapper, script_index, sprite) + anm_runner.run_frame() + quads.append((ox, oy, oz, sprite)) + models.append(quads) + return models + + +def read_mesh(obj_name, stage, model, image): + verts = [] + texcoords = [] + cols = [] + nb_vertices = 0 + faces_indices = [] + + for ox, oy, oz, sprite in model: + key, (vertices, uvs, colors) = get_sprite_rendering_data(sprite) + (x1, y1, z1), (x2, y2, z2), (x3, y3, z3), (x4, y4, z4) = vertices + left, right, bottom, top = uvs + r, g, b, a = colors + + #verts.extend(((x1 + ox, y1 + oy, z1 + oz), + # (x2 + ox, y2 + oy, z2 + oz), + # (x3 + ox, y3 + oy, z3 + oz), + # (x4 + ox, y4 + oy, z4 + oz))) + + # Blender coordinates are z-inverted. + verts.extend(((x1 + ox, y1 + oy, -(z1 + oz)), + (x2 + ox, y2 + oy, -(z2 + oz)), + (x3 + ox, y3 + oy, -(z3 + oz)), + (x4 + ox, y4 + oy, -(z4 + oz)))) + + texcoords.extend(((left, bottom), + (right, bottom), + (right, top), + (left, top))) + + #TODO: use them. + cols.append((r, g, b, a)) + + faces_indices.append((nb_vertices, nb_vertices + 1, nb_vertices + 2, nb_vertices + 3)) + + nb_vertices += 4 + + mesh = bpy.data.meshes.new(obj_name) + mesh.from_pydata(verts, [], faces_indices) + + texture = mesh.uv_textures.new('Texture') + for tex in texture.data: + tex.image = image + + uvs = mesh.uv_layers[0] + for i, uv_loop in enumerate(uvs.data): + uv_loop.uv = texcoords[i] + + return mesh + + +def add_object(name, mesh, position): + scene = bpy.context.scene + + for obj in scene.objects: + obj.select = False + + mesh.update() + mesh.validate() + + obj = bpy.data.objects.new(name, mesh) + obj.location = position + scene.objects.link(obj) + obj.select = True + + if scene.objects.active is None or scene.objects.active.mode == 'OBJECT': + scene.objects.active = obj + + +def read(stage_path): + dirname = os.path.dirname(stage_path) + + obj_name = bpy.path.display_name_from_filepath(stage_path) + assert obj_name[:5] == 'stage' + stage_number = int(obj_name[5]) + + with open(stage_path, "rb") as filehandle: + stage = Stage.read(filehandle) + + anm_path = os.path.join(dirname, 'stg{}bg.anm'.format(stage_number)) + with open(anm_path, "rb") as filehandle: + anm = ANM0.read(filehandle) + anm_wrapper = AnmWrapper((anm,), (0,)) + + models = build_models(stage, anm_wrapper) + + texture_path = os.path.join(dirname, 'stg{}bg.png'.format(stage_number)) + image = bpy.data.images.load(texture_path) + + meshes = [] + for i, model in enumerate(models): + name = '{}-mesh{}'.format(obj_name, i) + mesh = read_mesh(name, stage, model, image) + meshes.append(mesh) + + for i, (model_id, ox, oy, oz) in enumerate(stage.object_instances): + name = '{}-object{}'.format(obj_name, i) + add_object(name, meshes[model_id], (ox, oy, -oz))