288 lines
13 KiB
Python
288 lines
13 KiB
Python
# io_import_minecraft
|
|
|
|
# ##### 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 #####
|
|
|
|
# <pep8 compliant>
|
|
|
|
# bl_info = {
|
|
# "name": "Import: Minecraft b1.7+",
|
|
# "description": "Importer for viewing Minecraft worlds",
|
|
# "author": "Adam Crossan (acro)",
|
|
# "version": (1,6,3),
|
|
# "blender": (2, 6, 0),
|
|
# "api": 41226,
|
|
# "location": "File > Import > Minecraft",
|
|
# "warning": '', # used for warning icon and text in addons panel
|
|
# "wiki_url": "http://randomsamples.info/project/mineblend",
|
|
# "category": "Import-Export"}
|
|
|
|
DEBUG_SCENE=False
|
|
|
|
# To support reload properly, try to access a package var, if it's there, reload everything
|
|
#if "bpy" in locals():
|
|
# import imp
|
|
# if "mineregion" in locals():
|
|
# imp.reload(mineregion)
|
|
|
|
#import bpy
|
|
#from bpy.props import StringProperty, FloatProperty, IntProperty, BoolProperty, EnumProperty
|
|
import imp
|
|
import mineregion
|
|
|
|
#def setSceneProps(scn):
|
|
# #Set up scene-level properties
|
|
# bpy.types.Scene.MCLoadNether = BoolProperty(
|
|
# name = "Load Nether",
|
|
# description = "Load Nether (if present) instead of Overworld.",
|
|
# default = False)
|
|
|
|
# scn['MCLoadNether'] = False
|
|
# return
|
|
#setSceneProps(bpy.context.scene)
|
|
|
|
# def createTestScene():
|
|
# bpy.ops.scene.new(type='NEW')
|
|
# bpy.context.scene.render.engine = 'CYCLES'
|
|
# # plane
|
|
# bpy.ops.mesh.primitive_plane_add(radius=1, view_align=True, enter_editmode=False, location=(0,0,0), rotation=(0,0,0), layers = (True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))
|
|
# bpy.ops.transform.resize(value=(10,10,10), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
|
|
# bpy.ops.material.new()
|
|
# # cube
|
|
# bpy.ops.mesh.primitive_cube_add(radius=1, view_align=True, enter_editmode=False, location=(0,0,0), rotation=(0,0,0), layers = (True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))
|
|
# # FIXME - error
|
|
# #bpy.context.space_data.context='MATERIAL'
|
|
# bpy.ops.transform.translate(value=(0.55,0.17,1.14), constraint_axis=(False,False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
|
|
# # set material to leaves?
|
|
# bpy.ops.object.editmode_toggle()
|
|
# bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001)
|
|
# # uv mapping - how do we tell blender?
|
|
# #bpy.ops.transform.resize(value=(0.0368432,0.0368432,0.0368432), constraint_axis=(False,False,False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
|
|
# #bpy.ops.transform.translate(value=(-0.202301, 0.07906, 0), constraint_axis=(False,False,False), constraint_orientation='GLOBAL', mirror=False, proportional_falloff='SMOOTH', proportional_size=1)
|
|
# bpy.ops.object.editmode_toggle()
|
|
# # lights...
|
|
# bpy.ops.object.lamp_add(type='SUN', view_align=True, location=(-8.12878,5.39259,9.70453), rotation=(-0.383973,0,0), layers=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))
|
|
# # camera...
|
|
# bpy.ops.object.camera_add(view_align=True, enter_editmode=False, location=(-8.12878,-9.13302,7.87796), rotation=(0,0,0), layers=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))
|
|
# #bpy.context.space_data.context='CONSTRAINT'
|
|
# bpy.ops.object.constraint_add(type='TRACK_TO')
|
|
# bpy.context.object.constraints["Track To"].target = bpy.data.objects["Cube.001"]
|
|
# bpy.context.object.constraints["Track To"].track_axis = 'TRACK_NEGATIVE_Z'
|
|
# bpy.context.object.constraints["Track To"].up_axis = 'UP_Y'
|
|
|
|
|
|
#Menu 'button' for the import menu (which calls the world selector)...
|
|
# class MinecraftWorldSelector(bpy.types.Operator):
|
|
#"""An operator defining a dialogue for choosing one on-disk Minecraft world to load.
|
|
#This supplants the need to call the file selector, since
|
|
"""Minecraft worlds require a preset specific folder structure of multiple files which cannot be selected singly."""
|
|
|
|
bl_idname = "mcraft.selectworld"
|
|
bl_label = "Select Minecraft World"
|
|
|
|
#bl_space_type = "PROPERTIES"
|
|
#Possible placements for these:
|
|
bl_region_type = "WINDOW"
|
|
|
|
|
|
#TODO: Make this much more intuitive for the user!
|
|
#Would be better if could define min[x,y,z] and max[x,y,z] and load between these point
|
|
# this would mean an edit in mineregion.py approx line 844: for z in range(pZ-loadRadius, pZ+loadRadius):
|
|
mcLoadAtCursor = False #Loads as if 3D cursor offset in viewport was the player (load) position.
|
|
mcLowLimit = 60 #The lowest depth layer to load. (High=256, Sea=64, Low=0)
|
|
mcHighLimit = 128 #The highest layer to load. (High=256, Sea=64, Low=0)
|
|
|
|
mcLoadRadius = 100 # 'Load Radius - The half-width of the load range around load-pos.
|
|
# e.g, 4 will load 9x9 chunks around the load centre
|
|
# WARNING! Above 10, this gets slow and eats LOTS of memory!
|
|
mcOmitStone = False # When True, do not import common blocks such as stone & dirt blocks (overworld) or netherrack (nether).
|
|
mcDimenSelectList = '0' #Which dimension should be loaded? - '0'=Overworld; '1'=Nether, '2'=The End
|
|
mcShowSlimeSpawns = False #'Display green markers showing slime-spawn locations
|
|
mcUseCyclesMats = False #Blender Setting: Set up default materials for use with Cycles Render Engine instead of Blender Internal
|
|
mcFasterViewport = False #Blender Setting: Disable display of common blocks (stone, dirt, etc.) in the viewport for better performance.
|
|
mcSurfaceOnly = False #Omit underground blocks. Significantly better viewing and rendering performance.
|
|
mcOmitMobs = True # When True, do not load mobs (creepers, skeletons, zombies, etc.) in world
|
|
#may need to define loadnether and loadend as operators...?
|
|
|
|
# omit Dirt toggle option.
|
|
|
|
# height-limit option (only load down to a specific height) -- could be semi-dynamic and delve deeper when air value for the
|
|
# column in question turns out to be lower than the loading threshold anyway.
|
|
|
|
#surfaceOnly ==> only load surface, discard underground areas. Doesn't count for nether.
|
|
# Load Nether is, obviously, only available if selected world has nether)
|
|
# Load End. Who has The End?! Not I!
|
|
|
|
#When specifying a property of type EnumProperty, ensure you call the constructing method correctly.
|
|
#Note that items is a set of (identifier, value, description) triples, and default is a string unless you switch on options=ENUM_FLAG in which case make default a set of 1 string.
|
|
#Need a better way to handle this variable: (possibly set it as a screen property)
|
|
|
|
# import mineregion
|
|
wlist = mineregion.getWorldSelectList()
|
|
if wlist is not None:
|
|
revwlist = wlist[::-1]
|
|
mcWorldSelectList = wlist[0][0] #Which Minecraft save should be loaded?
|
|
else:
|
|
mcWorldSelectList = 0 #Which Minecraft save should be loaded?
|
|
|
|
#TODO: on select, check presence of DIM-1 etc.
|
|
print("List of Worlds: wlist:: ", wlist)
|
|
|
|
netherWorlds = [w[0] for w in wlist if mineregion.hasNether(w[0])]
|
|
print("List of worlds with Nether: ", netherWorlds)
|
|
|
|
endWorlds = [e[0] for e in wlist if mineregion.hasEnd(e[0])]
|
|
print("List of worlds with The End: ", endWorlds)
|
|
|
|
#my_worldlist = bpy.props.EnumProperty(items=[('0', "A", "The A'th item"), ('1', 'B', "Bth item"), ('2', 'C', "Cth item"), ('3', 'D', "dth item"), ('4', 'E', 'Eth item')][::-1], default='2', name="World", description="Which Minecraft save should be loaded?")
|
|
|
|
# def execute(self, context):
|
|
#self.report({"INFO"}, "Loading world: " + str(self.mcWorldSelectList))
|
|
#thread.sleep(30)
|
|
#self.report({"WARNING"}, "Foo!")
|
|
|
|
#from . import mineregion
|
|
# scn = context.scene
|
|
|
|
mcLoadDimenNether = True if mcDimenSelectList=='1' else False
|
|
mcLoadDimenEnd = True if mcDimenSelectList=='2' else False
|
|
# FIXME - when omitmobs is false, mobs will sometimes still not be imported (related to reload issue?)
|
|
opts = {"omitstone": mcOmitStone, "showslimes": mcShowSlimeSpawns, "atcursor": mcLoadAtCursor,
|
|
"highlimit": mcHighLimit, "lowlimit": mcLowLimit,
|
|
"loadnether": mcLoadDimenNether, "loadend": mcLoadDimenEnd,
|
|
"usecycles": mcUseCyclesMats, "omitmobs": mcOmitMobs,
|
|
"fasterViewport": mcFasterViewport, "surfaceOnly": mcSurfaceOnly}
|
|
print(str(mcWorldSelectList))
|
|
print(str(opts))
|
|
#get selected world name instead via bpy.ops.mcraft.worldselected -- the enumeration as a property/operator...?
|
|
mineregion.readMinecraftWorld(str(mcWorldSelectList), mcLoadRadius, opts)
|
|
# for s in bpy.context.area.spaces: # iterate all space in the active area
|
|
# if s.type == "VIEW_3D": # check if space is a 3d-view
|
|
# space = s
|
|
# space.clip_end = 10000.0
|
|
#run minecraftLoadChunks
|
|
#if DEBUG_SCENE:
|
|
# createTestScene()
|
|
|
|
# return {'FINISHED'}
|
|
|
|
|
|
# def invoke(self, context, event):
|
|
# context.window_manager.invoke_props_dialog(self, width=350,height=250)
|
|
# return {'RUNNING_MODAL'}
|
|
#
|
|
#
|
|
# def draw(self, context):
|
|
# layout = self.layout
|
|
# col = layout.column()
|
|
# col.label(text="Choose import options")
|
|
#
|
|
# row = col.row()
|
|
# row.prop(self, "mcLoadAtCursor")
|
|
#
|
|
# row = col.row()
|
|
#
|
|
# sub = col.split(percentage=0.5)
|
|
# colL = sub.column(align=True)
|
|
# colL.prop(self, "mcShowSlimeSpawns")
|
|
#
|
|
# cycles = None
|
|
# if hasattr(bpy.context.scene, 'cycles'):
|
|
# cycles = bpy.context.scene.cycles
|
|
# row2 = col.row()
|
|
# if cycles is not None:
|
|
# row2.active = (cycles is not None)
|
|
# row2.prop(self, "mcUseCyclesMats")
|
|
#
|
|
# row3 = col.row()
|
|
# row3.prop(self, "mcOmitStone")
|
|
# row3.prop(self, "mcOmitMobs")
|
|
#
|
|
# row = col.row()
|
|
# row.prop(self,"mcFasterViewport")
|
|
# #row.prop(self,"mcSurfaceOnly")
|
|
#
|
|
# #if cycles:
|
|
# #like this from properties_data_mesh.py:
|
|
# ##layout = self.layout
|
|
# ##mesh = context.mesh
|
|
# ##split = layout.split()
|
|
# ##col = split.column()
|
|
# ##col.prop(mesh, "use_auto_smooth")
|
|
# ##sub = col.column()
|
|
# ##sub.active = mesh.use_auto_smooth
|
|
# ##sub.prop(mesh, "auto_smooth_angle", text="Angle")
|
|
# #row.operator(
|
|
# #row.prop(self, "mcLoadEnd") #detect folder first (per world...)
|
|
#
|
|
# #label: "loading limits"
|
|
# row = layout.row()
|
|
# row.prop(self, "mcLowLimit")
|
|
# row = layout.row()
|
|
# row.prop(self, "mcHighLimit")
|
|
# row = layout.row()
|
|
# row.prop(self, "mcLoadRadius")
|
|
#
|
|
# row = layout.row()
|
|
# row.prop(self, "mcDimenSelectList")
|
|
# #col = layout.column()
|
|
#
|
|
# row = layout.row()
|
|
# row.prop(self, "mcWorldSelectList")
|
|
# #row.operator("mcraft.worldlist", icon='')
|
|
# col = layout.column()
|
|
|
|
# def worldchange(self, context):
|
|
# ##UPDATE (ie read then write back the value of) the property in the panel
|
|
# #that needs to be updated. ensure it's in the scene so we can get it...
|
|
# #bpy.ops.mcraft.selectworld('INVOKE_DEFAULT')
|
|
# #if the new world selected has nether, then update the nether field...
|
|
# #in fact, maybe do that even if it doesn't.
|
|
# #context.scene['MCLoadNether'] = True
|
|
# return {'FINISHED'}
|
|
#
|
|
# class MineMenuItemOperator(bpy.types.Operator):
|
|
# bl_idname = "mcraft.launchselector"
|
|
# bl_label = "Needs label but label not used"
|
|
#
|
|
# def execute(self, context):
|
|
# bpy.ops.mcraft.selectworld('INVOKE_DEFAULT')
|
|
# return {'FINISHED'}
|
|
#
|
|
# bpy.utils.register_class(MinecraftWorldSelector)
|
|
# bpy.utils.register_class(MineMenuItemOperator)
|
|
#bpy.utils.register_class(MCraft_PT_worldlist)
|
|
|
|
#Forumsearch tip!! FINDME:
|
|
#Another way would be to update a property that is displayed in your panel via layout.prop(). AFAIK these are watched and cause a redraw on update.
|
|
#
|
|
# def mcraft_filemenu_func(self, context):
|
|
# self.layout.operator("mcraft.launchselector", text="Minecraft (.region)", icon='MESH_CUBE')
|
|
#
|
|
#
|
|
# def register():
|
|
# #bpy.utils.register_module(__name__)
|
|
# bpy.types.INFO_MT_file_import.append(mcraft_filemenu_func) # adds the operator action func to the filemenu
|
|
#
|
|
# def unregister():
|
|
# #bpy.utils.unregister_module(__name__)
|
|
# bpy.types.INFO_MT_file_import.remove(mcraft_filemenu_func) # removes the operator action func from the filemenu
|
|
#
|
|
# if __name__ == "__main__":
|
|
# register()
|