Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 03/24/2018 in all areas

  1. Shadowblaze

    Nick Nuwe - Nice Day Out

    Heyyy, it's been a while since I've done anything! Better get back on track with a fully produced track with real recorders! As I mentioned in the description of the video, this has been laying around for a pretty dang long time. While I still think this track is way too silly for me to take 100% seriously, it still is pretty important to me, so I felt I needed to publish it and move on with it. Hope you'll enjoy!
    1 point
  2. I'm developing a Blender import script, but I've not been able to find source code or documentation of any kind for Will Kirby's LibLR2, which is used in the model viewer. I've done my research and reverse-engineered the model files to the point where I can do what the viewer can do, but I wouldn't want to reinvent the wheel any more if the work is already done and available. Edit: Here's the WIP script that imported the above mesh. It works with most models. It's for testing, so you're supposed to use it from the text editor. Change the strings at the bottom of the script to your own file locations. Image tutorial below. from struct import unpack from sys import argv from os.path import split, splitext, join import bpy, bmesh def readnulltermstring(input,skip=False): if type(input) in (str, bytes): #else it's a file object null = '\0' if type(input)==str else b'\0' return input[:input.index(null)] if null in input else input if skip: while 1: c = input.read(1) if not c or c == b'\0': return buildstring = bytearray() while 1: c = input.read(1) if not c or c == b'\0': return buildstring buildstring += c # All models use the .md2 extension, but the actual file format within is discernible by the first 4 bytes. # MDL2 and MDL1 are chunking formats, but MDL0 is not. A chunk's name is a 4-byte int, followed by a length also represented in a 4-byte int. # .bsb files are skeletons, pointing to associated animation .bsa files # .mip textures are .tga files. def import_discern(filepath): with open(filepath,'rb') as f: first4 = f.read(4) if first4 == b'MDL2': return import_mdl2(filepath) elif first4 == b'MDL1': return import_mdl1(filepath) elif first4 == b'MDL0': return import_mdl0(filepath) else: raise AssertionError('Input file is not of type MDL2, MDL1, or MDL0.') def import_mdl2(filepath): f = open(filepath,'rb') open_textures = True try: rootpath = filepath[:filepath.lower().index('game data')] except ValueError: open_textures = False print('Could not trace back to root directory "GAME DATA".') offset = 0 texture_paths = [] objects = [] while 1: chunk_name = f.read(4) if chunk_name == b'\0\0\0\0' or not chunk_name: break chunk_size = unpack('I', f.read(4))[0] chunk_base = f.tell() print('%s: %s' % (chunk_name.decode('ascii'), hex(chunk_size))) if chunk_name == b'MDL2': ints = tuple(f.read(4) for x in range(32)) mdl2_texturecount = unpack('I', f.read(4))[0] mdl2_texturebase = f.tell() print(' Textures:', mdl2_texturecount) for texture_id in range(mdl2_texturecount): texture_path = readnulltermstring(f.read(256)).decode('ascii') texture_type ,\ texture_index = unpack('2I', f.read(8)) print(' ' + texture_path) texture_paths += [texture_path] materials = [bpy.data.materials.new(splitext(split(x)[1])[0]) for x in texture_paths] elif chunk_name == b'P2G0': # weights? pass elif chunk_name == b'GEO1': geo1_unknown0,\ geo1_unknown1,\ geo1_unknown2,\ geo1_meshes = unpack('2IfI', f.read(16)) geo1_unknown4 = unpack('I', f.read(4))[0] geo1_unknown5 = unpack('f', f.read(4))[0] print(' Split models: %i' % geo1_meshes) # meshes are split because textures are defined per mesh for model in range(geo1_meshes): print(' Model %i' % model) md2_bmesh = bmesh.new() geo1_mesh_unknown21 = unpack('H', f.read(2))[0] geo1_mesh_unknown22 = unpack('H', f.read(2))[0] geo1_mesh_unknown0 = unpack('f', f.read(4))[0] geo1_mesh_unknown1 = unpack('f', f.read(4))[0] geo1_mesh_unknown2 = unpack('f', f.read(4))[0] geo1_mesh_unknown3 = unpack('f', f.read(4))[0] geo1_mesh_unknown4 = unpack('I', f.read(4))[0] geo1_mesh_unknown5 = unpack('I', f.read(4))[0] geo1_mesh_unknown6 = unpack('I', f.read(4))[0] geo1_mesh_texture = unpack('H', f.read(2))[0] geo1_mesh_unknown7 = unpack('H', f.read(2))[0] geo1_mesh_unknown8 = unpack('I', f.read(4))[0] geo1_mesh_unknown9 = unpack('I', f.read(4))[0] geo1_mesh_unknown10 = unpack('I', f.read(4))[0] geo1_mesh_unknown11 = unpack('I', f.read(4))[0] geo1_mesh_unknown12 = unpack('I', f.read(4))[0] geo1_mesh_unknown13 = unpack('I', f.read(4))[0] geo1_mesh_unknown14 = unpack('I', f.read(4))[0] geo1_mesh_unknown15 = unpack('I', f.read(4))[0] geo1_mesh_unknown16 = unpack('I', f.read(4))[0] geo1_mesh_unknown17 = unpack('I', f.read(4))[0] geo1_mesh_vertexsize= unpack('I', f.read(4))[0] geo1_mesh_unknown19 = unpack('I', f.read(4))[0] geo1_mesh_unknown20 = unpack('H', f.read(2))[0] print(' Texture: %s' % texture_paths[geo1_mesh_texture]) #print(' Start of vertices:', hex(f.tell())) geo1_mesh_vertices = unpack('H', f.read(2))[0] geo1_mesh_unknownxyz = unpack('3f', f.read(4*3)) #print(' Vertices:', geo1_mesh_vertices) geo1_mesh_vertexbase = f.tell() print(' Vertices: %i' % geo1_mesh_vertices) uvs = [] for vertex in range(geo1_mesh_vertices): md2_bmesh.verts.new() md2_bmesh.verts.ensure_lookup_table() print(' Vertex format: 0x%04x' % geo1_mesh_unknown20) for vertex in range(geo1_mesh_vertices): vertex_xyz = unpack('3f', f.read(4*3)) if geo1_mesh_vertexsize == 0x20: pass elif geo1_mesh_vertexsize == 0x24: geo1_mesh_unknown20_f = unpack('f', f.read(4))[0] elif geo1_mesh_vertexsize == 0x28: geo1_mesh_unknown20_f = unpack('2f', f.read(8)) else: raise AssertionError('Unexpected vertex struct size (%s).' % hex(geo1_mesh_vertexsize)) vertex_normal = unpack('3f', f.read(4*3)) vertex_uv = unpack('2f', f.read(4*2)) md2_bmesh.verts[vertex].co = vertex_xyz md2_bmesh.verts[vertex].normal = vertex_normal uvs += [vertex_uv] print(' End of vertices:', hex(f.tell())) geo1_mesh_unknown19 = unpack('I', f.read(4))[0] geo1_mesh_unknown20 = unpack('I', f.read(4))[0] #print(' Start of polygons:', hex(f.tell())) geo1_mesh_polygons = unpack('I', f.read(4))[0] // 3 print(' Polygons: %i' % geo1_mesh_polygons) for polygon in range(geo1_mesh_polygons): md2_bmesh.faces.new((md2_bmesh.verts[x] for x in unpack('3H', f.read(2*3))))#.material_index = geo1_mesh_texture #print(' End of polygons:', hex(f.tell())) # set uv maps md2_bmesh.verts.ensure_lookup_table() md2_bmesh.faces.ensure_lookup_table() md2_bmesh.verts.index_update() uv_layer = md2_bmesh.loops.layers.uv.new() for face in md2_bmesh.faces: for loop in face.loops: loop[uv_layer].uv = uvs[loop.vert.index] md2_mesh = bpy.data.meshes.new(str(model)) md2_bmesh.to_mesh(md2_mesh) md2_obj = bpy.data.objects.new(str(model), md2_mesh) md2_obj.data.materials.append(materials[geo1_mesh_texture]) bpy.context.scene.objects.link(md2_obj) objects += [md2_obj] elif chunk_name == b'COLD': pass offset += 8 + chunk_size f.seek(offset) f.close() for object in objects: object.select = True bpy.context.scene.objects.active = objects[0] bpy.ops.object.join() objects[0].rotation_euler = (__import__('math').pi / 2, 0, 0) if open_textures: for texturepath in texture_paths: bpy.ops.image.open(filepath = join(rootpath, splitext(texturepath)[0] + '.mip')) def export_mdl2(filepath): pass import_discern( r'C:\Users\Irup\Desktop\lr2\GAMEDATA\GAME DATA\LOM\OBJECTS\MODELS\REJOOV_WORKGODAMNYOU.MD2' ) r'C:\Users\Irup\Desktop\lr2\GAMEDATA\GAME DATA\ADVENTURERS\OBJECTS\MODELS\WATERFALL_01.MD2' r'C:\Users\Irup\Desktop\lr2\GAMEDATA\GAME DATA\CHARACTERS\HEADS\MODELS\PLAYER1.MD2' r'C:\Users\Irup\Desktop\lr2\GAMEDATA\GAME DATA\ANIMATION\ALBATROS\ALBATROS.MD2' r'C:\Users\Irup\Desktop\lr2\GAMEDATA\GAME DATA\CHARACTERS\HEADS\MODELS\SPARKY.MD2' r'C:\Users\Irup\Desktop\lr2\GAMEDATA\GAME DATA\LOM\OBJECTS\MODELS\REJOOV_WORKGODAMNYOU.MD2' r'C:\Users\Irup\Desktop\lr2\GAMEDATA\GAME DATA\OBJECTS\BRICKS\SQUARE\1X1 RED hacked.MD2' r'C:\Users\Irup\Desktop\lr2\GAMEDATA\GAME DATA\OBJECTS\BRICKS\SQUARE\1X1 RED.MD2'
    1 point
  3. lol username

    Rocket Racers Car

    Again, other way around... Copy from Rocket Racer to Player One (or whichever player you want to replace). And copy, don't cut or delete the originals.
    1 point
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.