Blender script for import minetest schematics

minetesprogramercor
New member
 
Posts: 2
Joined: Tue Oct 08, 2019 18:49
In-game: None

Blender script for import minetest schematics

by minetesprogramercor » Mon Nov 04, 2019 21:06

First i don't speak English(i speak Spanish)
Hello i am “minetesprogramer” and this time i create this script for blender who is capable of import the default minetest schematics from a .mts file in blender,is still a very early version but work correctly in my opinion.
Note:This work is possible thanks to the code created by Gaël de Sailly.

Requirements:
1-You need to have installed “Blender 2.79”(I dont know if functions in “2.80”)
2-Your operative system must be Linux(only probe in Linux)
Instructions
1-Open Blender.
2-Change the render mode to “Cycles Render”(“Blender Render” is the default).
3-Copy the script in the Blender text editor.
4-Run the script(mouse over the blender text editor and (Alt+P) or in the Blender text editor:text>Run Script )
5-Open the new Minetest Panel.
6-Press the Import button.
7-Select the file from you want to import the schematics(and activate this)
Code: Select all
The Script(V0.2):
###Nota:El codigo que crea la clase Schem,fue creado por Gaël de Sailly.
####Note:The code for the Schem class was created by Gaël de Sailly.

# Python library to handle Minetest schematics, by Gaël de Sailly, Nov 10 2018.
# Inspired by python-minetest library by LeMagnesium: https://github.com/LeMagnesium/python-minetest/blob/3d81909/libminetest/schematics.py
# Using NumPy for speed and memory efficiency (more adapted to huge schematics)

import numpy as np
import zlib
from io import BytesIO

bulk_dtype = np.dtype([("node", ">u2"), ("prob", "u1"), ("force", "?"), ("param2", "u1")])

class Schem:
   def __init__(self, *args, **kwargs):
      if len(args) >= 1:
         if isinstance(args[0], str):
            self.load(args[0])
         else:
            if isinstance(args[0], tuple):
               shape = args
            else:
               shape = tuple(args[:3])
            self.version = 4
            self.yprobs = np.zeros(shape[1], dtype="u1")
            self.nodes = ["air"]
            self.data = np.zeros(shape, dtype=bulk_dtype)

      if 'version' in kwargs:
         self.version = kwargs['version']
      if 'yprobs' in kwargs:
         self.yprobs = kwargs['yprobs']
      if 'nodes' in kwargs:
         self.nodes = kwargs['nodes']
      if 'data' in kwargs:
         self.data = kwargs['data']

   def load(self, filename):
      f = open(filename, "rb")

      if f.read(4) != b"MTSM":
         print("WARNING: Signature 'MTSM' not recognized!")

      self.version = np.fromstring(f.read(2), dtype=">u2")[0]
      size = tuple(np.fromstring(f.read(6), dtype=">u2"))
      volume = int(size[0])*int(size[1])*int(size[2])
      print(size)
      rev_size = tuple(reversed(size)) # Reversed shape tuple, to handle the schematic file in which data are stored as Z[Y[X]]
      self.yprobs = np.fromstring(f.read(size[1]), dtype="u1")

      nodecount = np.fromstring(f.read(2), dtype=">u2")[0]
      self.nodes = []
      for node in range(nodecount):
         namelength = np.fromstring(f.read(2), dtype=">u2")[0]
         self.nodes.append(f.read(namelength).decode("utf-8"))
      bulk = BytesIO(zlib.decompress(f.read()))
      f.close() # We have read all

      data = np.zeros(rev_size, dtype=bulk_dtype)

      data["node"] = np.fromstring(bulk.read(volume*2), dtype=">u2").reshape(rev_size)
      #data["force"], data["prob"] = np.divmod(np.fromstring(bulk.read(volume), dtype="u1").reshape(rev_size), 128)
      data["param2"] = np.fromstring(bulk.read(volume), dtype="u1").reshape(rev_size)

      self.data = data.swapaxes(0, 2) # data axis order is Z[Y[X]], we want X[Y[Z]]

   def save(self, filename, compression=9):
      self.cleanup_nodelist()

      f = open(filename, "wb")

      f.write(b"MTSM")

      f.write(np.uint16(self.version).newbyteorder(">").tobytes())
      size = self.data.shape
      f.write(np.array(size, dtype=">u2").tobytes())

      f.write(np.resize(self.yprobs, size[1]).tobytes()) # the yprobs list's size must be equal to the vertical size of the schematic ; if not, resize it.

      nodecount = len(self.nodes)
      f.write(np.uint16(nodecount).newbyteorder(">").tobytes())
      for node in self.nodes:
         namelength = len(node)
         f.write(np.uint16(namelength).newbyteorder(">").tobytes())
         f.write(node.encode("utf-8"))

      data = self.data.swapaxes(0, 2) # get MTS's axes order again

      bulk = BytesIO()

      bulk.write(data["node"].tobytes())
      bulk.write((data["force"] * 128 + data["prob"]).astype("u1").tobytes())
      bulk.write(data["param2"].tobytes())

      f.write(zlib.compress(bulk.getbuffer(), compression))
      f.close()

   def cleanup_nodelist(self):
      existing_nodes = np.unique(self.data["node"])
      existing_nodes.sort()

      new_nodelist = [self.nodes[i] for i in existing_nodes]
      
      transform_list = np.zeros(len(self.nodes), dtype=">u2")
      duplicates = 0
      update_array = False
      for new_i, old_i in enumerate(existing_nodes):
         i = new_i - duplicates
         if new_nodelist[i] in new_nodelist[:i]: # If this node is a duplicate
            transform_list[old_i] = new_nodelist.index(new_nodelist[i])
            new_nodelist.pop(i)
            duplicates += 1 # Keep count of removed duplicates to offset
            continue
         transform_list[old_i] = i
         if old_i != i:
            update_array = True

      self.nodes = new_nodelist
      if update_array:
         self.data["node"] = transform_list[self.data["node"]]

   def __getitem__(self, slices):
      data = self.data[slices].copy()
      nodes = self.nodes[:]
      if isinstance(slices, tuple) and len(slices) >= 2:
         yprobs = self.yprobs[slices[1]].copy()
      else:
         yprobs = self.yprobs.copy()

      return Schem(data=data, nodes=nodes, yprobs=yprobs, version=self.version)


import bpy

bpy.context.scene.render.engine = "CYCLES"

def importschem(context, filepath):
    s = Schem(filepath)

    #Esta funcion crea una lista con todos los nodos.
    #This function create a list whit all nodes from the schem.
    def lnode_pos(schem = s):
        len = schem.data.__len__()
        i1,i2,i3 = 0,0,0
        lista = []
        while i1 < len:
            l2 = schem.data[i1]
            ll2 = l2.__len__()
            print("ll2",ll2)
            while i2 <ll2:
                l3 = schem.data[i1][i2]
                ll3 = l3.__len__()
                print("ll3",ll3)
                while i3 <ll3:
                    element = schem.data[i1,i2,i3]
                    node = schem.nodes[element["node"]]
                    tnode = str(node)+"/"+str(i1)+"/"+str(i2)+"/"+str(i3)
                    lista.append(tnode)
                    i3 = i3 + 1
                i2 = i2 +1
                i3 = 0
            i1 = i1 + 1
            i2 = 0
        return lista

    #Crea la schematica.
    #Create the schem.
    def imp():
        for n in lnode_pos():
            d = n.split("/")
            obj = d[0]
            if obj != "air":
                #Si existe el nodo(grupo) en la escena,lo utiliza para crear la "schem".
                #If the node exist(group) in the scene,is used for create the schem.
                try:
                    bpy.ops.object.group_instance_add(group=obj, view_align=False, location=(0, 0, 0), layers=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))
                #De no existir,crea un cubo.
                #If not exist,create a cube.
                except:
                    bpy.ops.mesh.primitive_cube_add(radius=0.5)
                bpy.context.object.location[0] = int(d[1])
                bpy.context.object.location[1] = int(d[3])
                bpy.context.object.location[2] = int(d[2])
               
    imp()
    return {'FINISHED'}

bl_info = {#Metadato que contiene informacion sobre el script o add-on.
    "name": "Import Schemmatics",
    "author": "Lazaro",
    "version": (0, 2),
    "location": "View3D > Properties > Minetest",
    "description": "A buton to 'Minetest Panel' who inmports Schemmatics of Minetest from a .mts file ",
    "category": "Minetest"}


from bpy_extras.io_utils import ImportHelper
class ImportMinetestSchemsfromfile(bpy.types.Operator,ImportHelper):
    """My Object Moving Script"""     
    bl_idname = "minetest.importschems"       
    bl_label = "Minetest:Import Nodes"         
    bl_options = {'REGISTER', 'UNDO'}
       
    from bpy.props import StringProperty, BoolProperty, EnumProperty
   
    filename_ext = ".mts"
   
    filter_glob = StringProperty(
            default="*.mts",
            options={'HIDDEN'},
            maxlen=255,  # Max internal buffer length, longer would be clamped.
        )
   
   

    def execute(self, context):        # execute() is called when running the operator.
        importschem(context,self.filepath)
        return {'FINISHED'}


   
class MinetestImportShemsPanel(bpy.types.Panel):
    bl_space_type = 'VIEW_3D'
    bl_context = "objectmode"
    bl_label = "Import Shems"
    bl_region_type = "TOOLS"
    bl_category = "Minetest"   
   
    def draw(self,context):
        layout = self.layout
       
        row = layout.row()
        row.scale_y = 1.0
        row.operator("minetest.importschems",text = "Import")
   


# Only needed if you want to add into a dynamic menu
def menu_func_import(self, context):
    self.layout.operator(ImportMinetestSchemsfromfile.bl_idname, text="Text Import Operator")

def register():
    print("The Minetest Panel was created")
    bpy.utils.register_class(MinetestImportShemsPanel)
    bpy.utils.register_class(ImportMinetestSchemsfromfile)
    bpy.types.INFO_MT_file_import.append(menu_func_import)
def unregister():
    print("Closing the Minetest Panel")
    bpy.utils.register_class(MinetestImportShemsPanel)
    bpy.utils.unregister_class(ImportMinetestSchemsfromfile)
    bpy.types.INFO_MT_file_import.remove(menu_func_import)
   
if __name__ == "__main__":#Esto permite correr el script desde el editor de texto de Blender.
    register()
 

User avatar
runs
Member
 
Posts: 897
Joined: Sat Oct 27, 2018 08:32
GitHub: runsy
 


Return to Modding Discussion



Who is online

Users browsing this forum: No registered users and 2 guests