Page 1 of 3 123 LastLast
Results 1 to 10 of 22

Thread: Periphery Online / MMORTS

  1. #1

    Periphery Online / MMORTS

    Hi
    Try my mmorts game - Periphery Online.
    The client is written in Delphi, the server in Lazarus.

    Short description:
    A refugee capsule lands on an uninhabited planet.
    You have to survive, rebuild civilization and, if possible, regain control over the Earth.

    One big universe, one endless session for all players.

    I'm preparing a publication on Steam, so the test version is not available right now.
    https://store.steampowered.com/app/1873750/Periphery/










    Screenshots:









    Interface:
    Last edited by rts111; 08-08-2022 at 07:28 AM. Reason: update screenshots

  2. #2
    do tell more on how you built the game, what graphics library do you use?
    This is my game project - Top Down City:
    http://www.pascalgamedevelopment.com...y-Topic-Reboot

    My OpenAL audio wrapper with Intelligent Source Manager to use unlimited:
    http://www.pascalgamedevelopment.com...source+manager

  3. #3
    Graphic API: OpenGL, GLSL.
    Sound: DirectSound.

    All plants and houses are made with the Flora3D tree generator.
    For those who use this program, in the folder "../Periphery/Data/F3d" you can find additional collections of trees and bushes that you can freely use in your projects.

    Models and animations from Mixamo.
    First I import them into Blender, then export them in my own format.

    No other libraries are used, game engine is self-written.

    The server is also self-written, Linux/Posix, only native low-level functions: listen, send, recv, etc., no other lib.
    Last edited by rts111; 02-08-2022 at 04:18 PM.

  4. #4
    Looks very interesting.

  5. #5
    Quote Originally Posted by SilverWarior View Post
    Looks very interesting.
    Thanks.

    Landscape screenshots:



    Last edited by rts111; 27-11-2021 at 03:26 PM. Reason: update screenshots

  6. #6
    Could you do a writeup on how you did model animations? this is something i still have a problem with in my own game.
    This is my game project - Top Down City:
    http://www.pascalgamedevelopment.com...y-Topic-Reboot

    My OpenAL audio wrapper with Intelligent Source Manager to use unlimited:
    http://www.pascalgamedevelopment.com...source+manager

  7. #7
    Unfortunately, I don't know an easy way.

    My path was like this:
    - Learn how skeletal animation works in general.
    - Learn Blender, and learn how to import models and animations from Mixamo.
    - Learn Python, and learn how to write Python scripts in Blender.
    - Write a script that exports these model and animation to my own format.
    - In the game itself, you need to write functions for loading this format, for converting to format understandable GPU ...

    If you're a beginner, all of this can take over a year. I think my answer will not help you much.

  8. #8
    I know the principles behind the skinning but the issue is loading up the data in a consistient and useful way in pascal and getting it to opengl & shaders (or a decent CPU method to deform mesh).
    There is no useful example that works with modern file formats in pascal, there are ancient quake file format examples in glscene and some GLTF examples which are super hard to integrate because they rely on certain way their shaders are done and are hard to just integrate into a custom engine pipeline, and most of these don't even have support to share animations between different meshes, if you want multiple characters you usually need to export animations for each model separatedly.

    If you could make a minimal example of this, all of pascal community could benefit from it.
    This is my game project - Top Down City:
    http://www.pascalgamedevelopment.com...y-Topic-Reboot

    My OpenAL audio wrapper with Intelligent Source Manager to use unlimited:
    http://www.pascalgamedevelopment.com...source+manager

  9. #9
    The animation is too deeply integrated with the game engine. The code is written for internal useage only, it contains many strange and specific structures and names, it will not be understandable to the general public without detailed comments.

    Right now It's too hard and not realistic to quickly make a simple animation example without the game engine itself. Sorry.


    If anyone is interested, here is the Python code I use to export an animation model from Blender to my own binary format:
    Code:
    import os
    import bpy
    import mathutils
    from struct import pack
    from mathutils import Matrix, Vector, Color
    from bpy_extras import io_utils, node_shader_utils
    
    
    
    print('*********************************************************')
    
    oo = bpy.context.scene.objects
    
    
    
    VV = []
    TT = []
    NN = []
    WW = []
    
    II = []
    MM = []
    GG = []
    
    BB = []
    KK = []
    
    ML = []
    MI = []
    MB = []
    MG = []
    
    
    arm_name = 'Armature'
    icon_a = 8
    icon_k = 0
     
     
     
    if bpy.data.objects.find(arm_name) != -1:
        armature = bpy.data.objects[arm_name]
        BB = armature.data.bones[:] 
    
    
    def bone_index( b ):
        for i in range(len(BB)):
            if b == BB[i]:
                return i
        return -1
    
    
    def get_MG( i ):
        M = ML[i] @ MB[i] @ MI[i]
        if KK[i] < 0:
            return M
        else:
            return get_MG(KK[i]) @ M
    
    
    def root_path( _f_name = '' ):
        s = os.path.dirname(bpy.data.filepath)
        return s + '\\' + _f_name
    
    
    def name_compat(name):
        if name is None:
            return 'None'
        else:
            return name.replace(' ', '_')
    
    
    def mesh_triangulate(me):
        import bmesh
        bm = bmesh.new()
        bm.from_mesh(me)
        bmesh.ops.triangulate( bm , faces = bm.faces )
        bm.to_mesh( me )
        bm.free()
    
    
    def aa_add(aa,a):
        for i in range(len(aa)):
            if a == aa[i]:
                return i
        aa.append(a)
        return len(aa)-1
    
    
    def get_mat_tex(m):
        mat_wrap = node_shader_utils.PrincipledBSDFWrapper(m)
        tex_wrap = getattr( mat_wrap , "base_color_texture" , None )
        if tex_wrap:
            image = tex_wrap.image
            if image:
                d0 = os.path.dirname(bpy.data.filepath)
                d1 = os.path.normpath( d0 + image.filepath )
                d2 = os.path.relpath( d1 , start = d0 )
                return d2
        return 'none'
    
    
    def get_w2(v):
        gg = v.groups[:]
        if len(gg) == 0:
            return ( 0 , 1.0 ) , ( 0 , 0.0 )
        if len(gg) == 1:
            return ( gg[0].group , 1.0 ) , ( 0 , 0.0 )
        if len(gg) > 2:
            gg.sort( key = lambda g: -g.weight )
        w1 = gg[0].weight
        w2 = gg[1].weight
        ww = w1 + w2
        return ( gg[0].group , w1/ww ) , ( gg[1].group , w2/ww )
    
    
    def read_mesh(o):
    
        mesh_triangulate(o)
        o.calc_normals_split()
    
        vv_count = len(VV)
        LL = o.loops
        tt = o.uv_layers.active.data[:]
        
        def loop_to_ii(L):
            return LL[L].vertex_index + vv_count , aa_add( TT , tt[L].uv ) , aa_add( NN , LL[L].normal )
        
        gg = [ [[],m,0] for m in o.materials ]
        for f in o.polygons:
            f_ii = tuple( loop_to_ii(L) for L in f.loop_indices )
            gg[f.material_index][0].append( f_ii )
        
        VV.extend( o.vertices[:] )
        
        for g in gg:
            if len(g[0]) > 0:
                g[2] = aa_add( MM , g[1] )
                GG.append(g)
    
    
    def mesh_to_aa():
        for o in oo:
            if o.type == "MESH":
                read_mesh(o.to_mesh())
        for i in range(len(MM)):
            tex_name  = get_mat_tex(MM[i])
            tex_index = aa_add( II , tex_name )
            MM[i] = MM[i] , tex_index
    
    
    def write_arm( fw ):
    
        def write_m( M ):
            t = M.to_translation()
            e = M.to_euler()
            fw( pack('3f', M[0][3] , M[1][3] , M[2][3] ) )
            fw( pack('3f', e[0] , e[1] , e[2] ) )
        
        # icon pos
        fw( pack( '2B' , icon_a , icon_k ) )
        
        # BB
        fw( pack( 'i' , len(BB) ) )
        for b in BB:
            i = bone_index( b.parent )
            KK.append(i)
            fw( pack('i', i ) )
        
        # t_pose
        for i in range(len(BB)):
            b = BB[i]
            M = b.matrix_local.copy()
            ML.append( M.copy() )
            MI.append( M.inverted().copy() )
            MB.append( None )
            write_m(M)
        
        # t_anim
        fw( pack( 'i' , len(bpy.data.actions)-1 ) )
        for i in range( 1 , len(bpy.data.actions) ):
            
            a = bpy.data.actions[i]
            cc = a.fcurves
            a_len = len( cc[0].keyframe_points )
            
            fw( pack('B',len(a.name)) )
            fw( bytes( a.name , 'ansi' ) )
            
            fw( pack( 'i' , a_len ) )   
            
            for j in range( a_len ):
                for k in range(len(BB)):
                    b = BB[k]
                    path_p = 'pose.bones["' + b.name + '"].location'
                    path_q = 'pose.bones["' + b.name + '"].rotation_quaternion'
                    pp = []
                    qq = []
                    for c in cc:
                        if c.data_path == path_p:
                            pp.append(c)
                        if c.data_path == path_q:
                            qq.append(c)
                    v = [c.keyframe_points[j].co[1] for c in pp]
                    p = mathutils.Matrix.Translation( ( v[0] , v[1] , v[2] ) )
                    v = [c.keyframe_points[j].co[1] for c in qq]
                    q = mathutils.Quaternion( ( v[0] , v[1] , v[2] , v[3] ) ) 
                    MB[k] = p @ q.to_matrix().to_4x4()
                for k in range(len(BB)):
                    M = get_MG(k)
                    write_m(M)
    
    
    
    def save_arm( f_name ):
        f = open( f_name , 'bw' )
        fw = f.write
        write_arm( fw )
        f.close()
    
    
    
    def save_to_b2m( f_name , s_format ):
    
        mesh_to_aa()
        
        f = open(f_name,'bw')
        fw = f.write
    
        # format
        fw( pack( 'B' , s_format ) )
    
        # images
        fw( pack( 'i' , len(II) ) )
        for I in II:
            fw( pack('B',len(I)) )
            fw( bytes( I , 'ansi' ) )
    
        # mat
        fw( pack( 'i' , len(MM) ) )
        for M in MM:
            fw( pack( 'i' , M[1] ) )
            s = M[0].name
            fw( pack( 'B' , len(s) ) )
            fw( bytes( s , 'ansi' ) )
    
        # VV
        fw( pack( 'i' , len(VV) ) )
        for v in VV:
            fw( pack('3f', v.co[0] , v.co[1] , v.co[2] ) )
    
        # TT
        fw( pack( 'i' , len(TT) ) )
        for t in TT:
            fw( pack('2f', t[0] , t[1] ) )
    
        # NN
        fw( pack( 'i' , len(NN) ) )
        for n in NN:
            fw( pack('3f', n[0] , n[1] , n[2] ) )
    
        # GG
        fw( pack( 'i' , len(GG) ) )
        for G in GG:
            fw( pack( 'i' , G[2] ) )
            fw( pack( 'i' , len(G[0]) ) )
            for g in G[0]:
                #fw( pack( '9i', g[0][0],g[0][1],g[0][2], g[1][0],g[1][1],g[1][2], g[2][0],g[2][1],g[2][2]  ) )
                fw( pack( '9H', g[0][0],g[0][1],g[0][2], g[1][0],g[1][1],g[1][2], g[2][0],g[2][1],g[2][2]  ) )
        
        # animation ...
        if s_format < 1:
            f.close()
            return
            
        # WW
        for v in VV:
            w = get_w2(v)
            fw( pack('<BfBf', w[0][0] , w[0][1] , w[1][0] , w[1][1] ) )
        
        if s_format == 2:
            write_arm( fw )
    
        f.close()
    
        
    save_to_b2m( root_path('m1.b2m') , 2 )
    #save_arm( 'm1.arm' )
    Last edited by rts111; 11-12-2021 at 02:44 PM.

  10. #10
    Thanks, i will try to make sense of that, it looks like a simple exporter with clearly separated structures, t-pose is also exported so each model has its own t-pose (does this cause you issues if you want to share animations between multiple modems? )
    This is my game project - Top Down City:
    http://www.pascalgamedevelopment.com...y-Topic-Reboot

    My OpenAL audio wrapper with Intelligent Source Manager to use unlimited:
    http://www.pascalgamedevelopment.com...source+manager

Page 1 of 3 123 LastLast

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •