Search Unity

Tomb Raider Level Viewer Development In Unity3D

Discussion in 'Works In Progress' started by suruz, Jan 15, 2014.

  1. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    I started developing Tomb Raider 2 level viewer as a hobby project. My intention was to learn Unity3D rendering pipeline through comparative conversion of original Tomb Raider 2 rendering system.Which is mostly fixed function and procedural.It helped me to get deeper into mesh and animation representation structure of Unity3D. I represented Tomb Raider 2 data structure in C#. Which helped me to understand difference between managed and undamaged programing behavior.This cross platform level viewer can load any Tomb Raider II format file.It can play cut scene.It is able to export level / model in obj file.
    I will happily share my experience of this development. After some cleanups I have a plane to make it open source.Meanwhile creative feed backs are highly welcome :)

    So far I have implemented following features:

    It parses Tomb Raider 2 level data like models, animation, textures.Then it renders them back in unity3D.

    Tomb Raider's original rendering pipe line was fixed function. I converted it to modern data driven shader model. I used vertex buffer instead of separate geometry commands.Original UV mapping was texture Id based.That would required lots of texture load/unload to render triangles.

    Texture UV mapping was completely redone in Unity for vertex buffer support. I have used single texture to render every thing in the level.Which is way faster the original rendering process.

    In original file models were not defined in complete mesh.Model parts are stored separately.They were rearranged procedurally using attached position and rotation information. I have represented model's skeletal transformation hierarchy in unity3D.

    Original animation system was key frame based. I represented it to unity3D animation curve.You can watch it in http://www.youtube.com/watch?v=qmSC4v

    P.S. I titled this thread Tomb Raider Level Viewer Development In Unity3D instead of 'Tomb Raider 2 Parser Development In Unity3D'. I thought 'Level Viewer ' would be more friendlier then 'Parser'. I am going to upload my updated work demo.
     

    Attached Files:

    Last edited: Jan 15, 2014
    jasperbrooks79 and victorkin11 like this.
  2. bubbalovesponge

    bubbalovesponge

    Joined:
    Nov 8, 2012
    Posts:
    69
    Count me in if you will allow other to use your code. So very interesting in learning.
     
    jasperbrooks79 likes this.
  3. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Hi, thanks for your interest in this development :) I will try to keep this thread updated with informative materials. In fact I am new to forum activities. It will be great if you guys suggest me how I should proceed to improve it :D
     
    jasperbrooks79 likes this.
  4. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Now level and static objects are loaded with animation. Applied transparent material with alpha cutoff to windows and other transparent objects. Added real time shadows.

    Now that level is populated with objects it is time to have some control. I have plan to form a development structure with a series of problem solving tasks.Which includes:

    1.0 Control system for Lara
    2.0 Collision detection and physics
    3.0 Shader development
    4.0 Environmental Effects development
    This is not rigid list. Just rough sketch of TO DO list. Please let me know if you have any ideas :D

    $TR2 v.10 with trans parency.png
    [video=youtube_share;1zgjhpivJJo]http://youtu.be/1zgjhpivJJo
     
    Last edited: Jan 17, 2014
    jasperbrooks79 likes this.
  5. princeWIGUAN

    princeWIGUAN

    Joined:
    Aug 14, 2013
    Posts:
    337
    I never play the Tomb Raider 2.
    My mind keep remind me of the beautiful Tomb Raider 2013.
    But nice work there.

    If you can make a side by side picture comparison will be more interesting to see.

    Btw, this remind me also how GTA IV being refresh with the MODS that make it more realistic than the original shaders.
     
    jasperbrooks79 likes this.
  6. jonmalave_

    jonmalave_

    Joined:
    Aug 19, 2012
    Posts:
    1,669
    ah the good ol playstation days! long live PS One! lol

    Nice Work man!
     
    jasperbrooks79 likes this.
  7. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Tomb Raider 2 level file is practically a relational resource data base. For example, their is a table of unique mesh. Then their is a table for all instantiated models in the level. Each instantiated model has index into the table of unique model mesh.It efficiently reduces resource data duplication.Lately I learned I miss that point of data duplication. While I was procedurally instantiating models I was making a copy of mesh data from mesh data table, where I could just make a reference of required mesh data from mesh data table. What an waste :(

    $Mesh Data Duplication.png

    As you can see there five models of tree. They are same kind. They could share same mesh data. But current implementation has practically five duplicated meshes. I am going to handle that in next update...
     
    Last edited: Jan 19, 2014
    jasperbrooks79 likes this.
  8. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Comparison side by side would be really nice. I will keep it that in mind Yes shading sometime makes a whole lots of deference... at least for aesthetic pleasure.
     
    jasperbrooks79 likes this.
  9. Fishypants

    Fishypants

    Joined:
    Jan 25, 2009
    Posts:
    444
    This is super awesome! Great job, and very interesting!
     
    jasperbrooks79 likes this.
  10. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    I am using System.Runtime.InteropService Marshaling to convert byte buffer that lays out C Structure into C# Structure. In C, casting byte buffer with required struct type would be enough. Structure members are layid out sequentially and contiguously.But case is not same for C#. C# structure is not represented that simply in memory. C# Structure members are stored along with member type information. I'm not sure block data assignment is possible that way in C#. C# structure seems picky in nature. InteropService Marshaling do that picking for you whenever you want to represent a buffer into C# Structure. Please correct me if I'm wrong here. And intuitively you know it needs field information, for example field size and offset, to pick data for that field from byte buffer.

    Does System.Runtime.InteropService works in Unity Web Player?

    I have my own problem solving strategies.I am solving the problem bottom up. I am determining what causing Web Player stopping Marshaling structure. So I will follow my confidence hierarchy. When I know bottom level issues are not causing problem I will move to upper level issues in dependency. I never investigate issues arbitrary way- investigate low level issue, test, if success go for higher level. That way I ensure there is no loose end behind.

    I suspect marshaling causing problem.
    First I am going check simple IO operation in Web Player, like stream read, that will possibly succeed.
    Result: Yes, it succeed.

    Then I am going check simple marshaling related code. I want to know size of a C# structure through Marshal.SizeOf().
    Lets see if that return expected result in web player.

    Here is my structure:

    [StructLayout(LayoutKind.Explicit,Pack = 1,Size=16)]
    struct Room_Info { // 16 bytes
    [FieldOffset(0)]
    public int x; // X-offset of room (world coordinates)
    [FieldOffset(4)]
    public int z; // Z-offset of room (world coordinates)
    [FieldOffset(8)]
    public int yBottom; // Y-offset of lowest point in room (world coordinates) (actually highest value)
    [FieldOffset(12)]
    public int yTop; // Y-offset of highest point in room (world coordinates) (actually lowest value)
    }

    Result: failed, it returns 0 in Web Player.

    Result In Editor:
    $Marshal Test Editor .png

    Result In Web Player:
    $Marshal Test Web Player.png

    So how should I represent byte buffer into C# Structure When I can't determine size of Structure in run time ? Without using System.Runtime.InteropService? Another way around is providing size on my own. Cause, i am handling structure in managed space.

    Please let me know how I can solve the problem.

    If you are interested in Mastering C# Structure here is a great article by mikejames
    http://www.developerfusion.com/article/84519/mastering-structs-in-c/
     
    Last edited: Jan 22, 2014
    jasperbrooks79 likes this.
  11. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    I am replacing run time struct size calculation with explicit struct size...plus point is , I had already provided size information in C# StructLayout attribute settings :) I am replacing it in macro definition style... its saving a lot of coding at least..
     
    jasperbrooks79 likes this.
  12. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Last edited: Jan 27, 2014
    jasperbrooks79 likes this.
  13. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Now I am going to implement animation state control and transition of Lara. Her initial state is 'Falling Down'. State change is controlled by state id. state id is selected by user control or by level environment. State transition is more like animation cross fading. Transition in tr2 is not interpolation based, rather tr2 engine selects animation chunk from a list of prefabricated tiny animation. Here I am trying to illustrate the system,

    [Current State Running]
    --[If Run Change To animation Stop] -----------> [Select Intermediate animation From prefabricated animation list to Achieve state Stop]
    --[If Run Change To animation Jump] ----------> [Select Intermediate animation From prefabricated animation list to Achieve state Jump]
    --[If Run Change to animation Flip back]-------> [Select Intermediate animation From prefabricated animation listto Achieve state Back]

     
    jasperbrooks79 likes this.
  14. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    First move with Lara :) Implemented Laras IDLE - > RUN -> IDLE states with keyboard control.Implemented Queue based animation cross fading for state transition in Unity3D. It required some update in animation curve building process. Until now unity animation state timing was guided by key frame selector script.But it was not suitable for implementation of queued animation play. Animation curve between key frames was not stable because of error in-out tangent calculation of key frames. As a result direct animation play was jittery.Which would effect cross fade animation play.

    $lara running_.png

    $lara running2_.png
     
    jasperbrooks79 likes this.
  15. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    A simple state control algorithm:
    --Define basic state ID for each key control <Left><Right><Up><Down>
    --Set initial State
    --In update phase check for state change
    --if state change found go through intermediate states to achieve Target State
    --set it as current state


    2/9/2014
    Working with compound state id.
    --Develop key mapper to map key code with performed action
    --Develop state code generator

    2/11/2014
    Generating state map with key code. This is three layer mapping system. Outer layer, interface and an inner layer. Interface is core layer. Outer layer stores key codes generated by Unity. Inner layer stores game state machine code.Number of codes are same in each layer. Layers are constructed with three integer arrays of same size. Interface layer translates unity key codes into its own set of interface codes. Inner layer's state machine codes are then map to selected interface codes. Benefit of such layering is that, when user wants to change Unity key code for a specific action, he only needs to change unity layer - interface layer code mapping, Still inner layer - interface layer mapping works just fine.Thus state machine code is insulated from outer layer unity code. Other benefit is one to one code mapping.So there is no need of conditional branching in inner layer for state code selection.That makes selection procedure neat and simple.

    Now TR animation - state machine code finding is really hair pulling task. I had gone through all possible animations for an expected state.Selected one of them for experiment, watched if it work right by playing it.If satisfied, picked up it's state ID which will later be used for further processing, like cross fading control, delayed play etc.Plugged that state ID to a preselected index of inner layer state code array. That selected index will be used to trigger state ID to fire associated TR animation.

    Few problems I faced while state transition and looping:
    Delayed play seems troublesome...at least for now :) I want sate play loop by single key press. Which will include some amount of delay in-between state execution. Will I use queue for this mechanism? Until now I'm playing animation with direct state change detection. But that is kinda one shot, have problem with auto state transition. How about let state select it self on animation play done event?

    A note in Animation State control in Unity
    ------------------------------------------------------------
    When working with Unity3D Animation State class make sure your are playing clip with name of that Animation State. Otherwise Animation State time control will give wrong result.

    Refined Objective:

    2/13/2014
    States Run Loop Design: Which state will be played and how many times as a action? Designed a action class with a list of states. On action change new state and animation will be set for state player.At the end frame of each state play, an event will be fired where next state of current action will be selected, this state change rhythm will continue to run until new action is set. Core of this system is:

    Current Action: Current Active Action.
    Current State: Current State of Action.

    Implemented State Map for state transition

    $state map.png

    Key Code Interface Code State Code

    [UP] [0] [RUN]
    [SHIFT] + [UP] [1] [Walk]
    [SPACE] [2] [JUMP UP]
    [CTRL] + [UP] [3] [JUMP FORWARD]
     
    Last edited: May 2, 2014
    jasperbrooks79 likes this.
  16. indiegamemodels

    indiegamemodels

    Joined:
    Jan 25, 2012
    Posts:
    269
    Tomb Raider 2 was my favorite episode, so I am clearly interested in this project!
     
    jasperbrooks79 likes this.
  17. FlorianSchmoldt

    FlorianSchmoldt

    Joined:
    Jul 2, 2012
    Posts:
    334
    Very nice work ! Looks already much better in unity :)

    I don't think square enix would care but even "fan work" might be a bit tricky if it includes original content.
    Anyway...the visuals are so outdated that it doesn't really compete with anything and Lara was always very open and mod friendly.
    Lets hope the best and good luck :)
     
    jasperbrooks79 likes this.
  18. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Thank you :) I have so much enjoyed playing this game... and feeling same while I'm developing this project...
     
    jasperbrooks79 likes this.
  19. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Hi, I liked how you point out 'fan work'. Really nice! I never thought that way. Now I realized exposing original content can be a potential spoiler. Even though I did that to checkout this project's capability. As a TR fan I will definitely not do any thing that spoil TR :)
     
  20. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    2/17/2014
    Developing Jump Physics for Lara :)

    What is Jump in perspective of physics? At simple form, working against gravitational acceleration g. At the
    moment anybody lefts ground, his applied force on body generates initial velocity u. if there would no g in
    vacuum, everybody would continue to go up with velocity u. But, in real world u decays in fixed rate by constant
    negative velocity generated by g (ignoring air drag resistance). at time t effective negative velocity is g*t.

    So anybody can expect to go up till u > g*t. when u = g*t body gets still, two equal and oposit velocities act on
    body. Negative velocity g*t continues to increase because of constant g. At some point of time t, g*t starts to
    dominate, where u < g*t and body begins to fall down, because u is unable to help body to get up further :)

    So, if we want to know, maximum jump displacement hmax, we actually want to know time when body gets still
    in air. It happens in time Thmax, when

    u = g * Thmax -----(1)
    or Thmax = u / g--------(2)

    Benefit of knowing Thmax, is that now we can calculate hmax , with

    hmax = u * Thmax - 0.5 * g * Thmax * Thmax --------------(3)

    or, hmax = u * (u/g) - 0.5 * g *(u/g) * (u/g) //replacing Thmax with (u/g)
    or, hmax = u^2/g - 0.5 * g *(u^2)/(g^2)
    or, hmax = u^2/g - 0.5 * (u^2)/g
    or, hmax = 0.5 * u^2/g

    I want to know a initial velocity u, that will continue to dominate at hmax
    0 = u^2 - 2 * g * hmax
    or, u^2 = 2 * g * hmax
    or, u = sqrt(2 * g * hmax ) // here hmax is magnitude of displacement, which is always positive

    Sudo code for calculating height displacement as a function of time:
    public float UpdateJump(float deltatime)
    {
    return u * deltatime - 0.5f * g * deltatime * deltatime;
    }

    param deltatime is difference between current time( actually, Time.time in Unity)
    and time she started to jump. UpdateJump() returns current height displcement h.

    Now we can make Lara jump up applying returned value of UpdateJump() to her root transform,
    transform.position.y = UpdateJump(deltatime);

    Integration method I used here is called Ballistic Integration. It is much more stable than
    Euler's deferential Integration.

    What about horizontal displacement calculation :) if we consider initial horizontal velocity
    ux, there will be no change in ux, because there is no effective force in horizontal direction.
    So, at time t horizontal displacement,

    dx = ux * t -----------(4)

    So, how much distance will Lara cross when she is at maximum height displacement hmax? To reach
    hmax she takes Thmax time. From equation (4), within Thmax her crossed horizontal distance will be,

    dThmax = ux * Thmax

    With horizontal displacement dx and vertical displacement dh at hand we can create 3D position vector p
    at time t . Unity code for this vector should look like:

    Vector3 p = new Vector3( dx, dh ,0 ) // origin of p is Lara's initial Jump position

    p initially lies in xy plane where world space forward vector is normal of this plane. We have to transform
    it with Lara's current world space rotation matrix to get rotated version of this vector.Again unity code for
    transformed position vector:

    Vector3 pT = transform.rotation * p // origin of pT is Lara's initial Jump position

    using relative movement vector pT and Lara's initial jump position, we can get her world
    space jump position:

    transform.position = initialJumpPosition + pT

    Physics is not so hard if we have natural intuition about what is happening under the hood :)

    Here is web player version of the viewer
    including physics developed with above method.



    Here is the class I developed for jump physics :)

    using UnityEngine;
    using System.Collections;
    public class Physic3D {
    public float g = 9.8f; //gravitational acceleration
    float Tymax = 0.0f; //Time will be taken for maximum vertical displacement
    float Txmax = 0.0f; //Time will be taken for maximum horizontal displacement
    float Hv = 0.0f; //Initial horizontal Velocity
    float Vv = 0.0f; //Initial vertical Velocity
    float Vd = 0.0f; //Maximum vertical displacement
    float Hd = 0.0f; //Maximum horizontal displacement
    Quaternion rotation ; //Current world space rotation of parent transform
    Vector3 Start = Vector3.zero; //Jump start position
    bool HasValidCurve = false;

    public Physic3D(Vector3 startpos)
    {
    Start = startpos;
    }

    /*UpdateJump returns world space jump position.Call it in Unity Update()
    input:
    deltatime: difference between current time ( actually, Time.time in Unity) and jump start time
    */

    public Vector3 UpdateJump(float deltatime)
    {
    if(!HasValidCurve) return Start ;

    float dx = deltatime * Hv ;
    float dy = Vv * deltatime - (0.5f * g * deltatime * deltatime);
    Vector3 posvec = new Vector3(0.0f,dy,dx);
    return Start + rotation * posvec;
    }

    /*CalculateCurve Calculates initial vertical velocity Vv and horizontal velocity Hv. Call this when starting to
    jump.
    Inputs:
    From: Jump start position
    To: Direction vector of jump
    rot: Current rotation of transform
    */

    public bool CalculateCurve(Vector3 From, Vector3 To, Quaternion rot)
    {
    if(rot == null)
    {
    HasValidCurve = false;
    return false;
    }
    rotation = rot;

    Vector3 Forward = To ;
    float distance = new Vector3(Forward.x,0.0f, Forward.z).magnitude;
    Hd = distance;
    Vd = Mathf.Abs(Forward.y);

    //I want maximum initial velocity Vv, that will continue to dominate at maximum height Vd
    Vv = 2 * g * Vd;
    Vv = Mathf.Sqrt(Vv);

    if(Vv !=0.0f)
    {
    Tymax = Vv / g;
    Txmax = 2.0f * Tymax; //maximum horizontal displacement time is twice of Tymax
    Hv = Hd / Txmax ;
    Start = From;
    HasValidCurve = true;
    }
    else
    {
    HasValidCurve = false;
    }

    return HasValidCurve;
    }
    }

    Example implementation of Physic3D in Unity:

    using UnityEngine;
    using System.Collections;
    public class Jumper : MonoBehaviour
    {
    Physic3D physics = null;
    float JumpStartTime = 0.0f;

    void Start()
    {
    physics = new Physic3D(transform.position);
    }

    void Update()
    {
    transform.position = physics.UpdateJump(Time.time - JumpStartTime );
    }

    //Call this on press jump button
    void Jump()
    {
    JumpStartTime= Time.time;
    Vector3 direction = (transform.forward + transform.up * 0.5f).normalized * 10.0f;
    physics.CalculateCurve(transform.position, direction , transform.rotation);
    }
    }

    2/22/2014
    Fix landing height level after jump

    2/24/2014
    Basic collision detection with RayCasting....
    Added meshcollider for all objects but Lara. Thus avoided self raycast collision.
    TODO: Calculate shared vertex normal for Lara... for smooth look.
     
    Last edited: May 2, 2014
  21. BrunoR

    BrunoR

    Joined:
    Jan 7, 2013
    Posts:
    3
    Great Project:)
     
  22. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    2/26/2014
    Optimizing key mapper.Replacing conditional key input selection with dynamic iterative input selection.

    Key press event generation: When there is a key down event for a key, there must be a key up event
    for that last key. A key down and key up is conjugate event which clears each other.Actually, no two keys
    can be pressed simultaneously. There must be time gap between two key presses. Time gap can be used as
    key press classifier.

    Experiment:
    -- Key press response time...
    -- Generate new state code by key press ordering...

    I have to handle following cases:

    1.0 There may be two type of keys in sequence.
    2.0 Keys can be pressed in any order.
    3.0 Specific key order defines specific action. For example, run after jump, jump while running...
    4.0 key sequence is delayed with fixed response time.
    5.0 Timeout defines specific action for a key.
    6.0 Handle either single or double keys.
    8.0 specific key has specific key code.

    Define problem:
    1.0 Detect key order
    2.0 Implement key press response timeout

    Possible solution for order detection:
    1.0 Key code shifting and hashing.
    2.0 Key push-pop with queue.
    3.0 Commutative/comparative arithmetic coding.

    Implemented solution is actually none of them :) While key press handling I detect status of other key type,
    weather other key is pressed or not. If other key is not pressed, this is new key and I wait for a fixed amount
    of time for other key. On wait timeout if still there is no other key, I handle single key, else I handle double key.

    3/2/2014
    Note: Key press ordering and timing.

    I'm heavily biased to name spacing. Or you can say I'm space oriented programmer :) I consider statement
    execution time as a space. Which is constant. According to my general sense, when something is executed, let
    say printed or stated, can never be reverted at the point of time when it was executed.

    Every entity has a space, and every space governs it's entity. This sense give me kind of comfort and confidence
    while I structure things.Being in a constant space I can be sure that it can not be other space. I can take
    advantage of this knowledge when ordering or comparing a event space from other event space. Conditional
    branching is a spacing attached with constant statement.

    I'm handling key press on one conditional statement: Only IF Key Press Is New

    Being in this conditional branch I'm sure at least the key I'm currently handling is fresh. Then I mark it as handled.
    What about other key? If other key is handled than certainly this is not first key.thus spacing give me ordering
    information for free.
     
    Last edited: Mar 2, 2014
  23. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    3/04/2014
    Fix flat shed: I used triangles with shared vertex and recalculated shared normal. But this caused UV distortion.
    So, I restored original triangles and distributed calculated normals to duplicate vertices. Now Lara looks much smooth and curvy :)

    Lara's look before vertex sharing:

    $original trianlges + original normal.png

    Distorted uv with vertex shared triangles + calculated shared normal

    $vertexshared triangles + sharednormal + original uv.png

    Fix uv distortion with original triangles + distributed previously calculated normals

    $original triangles + distributed shared normals + original uv.png
    $smooth lara2.png
     
    Last edited: Mar 5, 2014
  24. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    I have to slow down TR2 viewer development a bit. Because of call of professional duty :) Anyway... followings
    are my next development plan.

    --Develop algorithm for surface model analysis....Actually I have two options here, (a) going with original TR way,
    data driven surface properties estimation, (b) more generic ray cast based procedural surface modeling. Luckily,
    my current official project dealing with Digital Surface Modelling.Hopefully, I can bring that experience here.

    --Develop Lara's Interactions with terrain , for example walkable, grab able surface, by implementing floor data.
     
    Last edited: Mar 12, 2014
  25. Fishypants

    Fishypants

    Joined:
    Jan 25, 2009
    Posts:
    444
    I can't believe how far you've gotten already. This is really impressive!
     
  26. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Thank you :) and stay tuned...
     
  27. tswalk

    tswalk

    Joined:
    Jul 27, 2013
    Posts:
    1,106
    wow, i'm really amazed.. so much work, great job!

    you have an almost inside knowledge of what how it was made :)
     
  28. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Thank you. Its so much encouraging! I also like to thank enormous dedicated tr community who made unofficial tr 2 data format available to us... I will resume developing as soon as I get enough free time :)
     
  29. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35

    Hi, folks.Hoping you are all well. I'm back in development. There was no update for long time. I did few developments in last few months.Here is summary. I did try to rebuild software architecture enforcing MVC pattern. I developed climbable surface detection algorithm exploiting terrain mesh. Also I developed custom TR2 formatted test level, which can be loaded by both original TR2 engine and this viewer.


    7/15/2014
    Parser class Modularization through dependency analysis and unit test.

    Discarding externally dependent members...

    7/16/2014

    Performing CRC analysis with class Level, MeshBuilder, TextureUV.
    Level:
    Level Manages prefab and instantiated game objects, rooms, actor, static objects. This class uses helper class MeshBuilder, TextureUV e,t,c
    MeshBuilder:
    Generates unity mesh from tr2 mesh.
    TextureUV:
    An utility class for texture manipulation.
    CRC using existing class:
    When analyzing mesh builder member function input, main input should correspond to an existing tr2 mesh. Function should be helper in nature that will serve an entity type, not an ID. ID is not finite in nature and context sensitive. Example:
    CreateStaticObject(Mesh mesh, Vector3 position, Quaternion rotation, string name)
    Here, Parameter should be Mesh type, not an ID type. ID type would restrict wide use of CreateStaticObject() ID is bound to a specific context, such as an valid array of mesh. Here, CreateStaticObject() is not sensitive to such array bound.

    7/19/2014
    Noticed object self collision while ray casing. Ignore attached collider to player.
    TODO:
    Handle out of index error while calculating animation clip count for a specific movable tr2 object.
    7/21/2014
    Attach AI with State Machine to NPC Object Tiger( Test purpose)
    7/23/2014
    Extending tr2 base class...
    Attaching custom properties to cloned unity object. One way to do this is assigning a object containing custom properties as a variable to MonoBehaviour script of that cloned object and then initialize the cloned object and it's component with that variable.

    Example:
    public class GameObjectEx: MonoBehaviour {
    public ClassData data; // data holding custom properties
    void Start()
    {
    //perform runtime initialization like animation clip setup
    //with data
    }
    }
    Learned: Direct instantiation of MonoBehaviour is not allowed!
    8/04/2014:
    An Note On CRC:
    A class does not need to know about another class, unless it’s task requires it. It will enforce strict responsibility of a class. For example, an event system does not need to know how the event will be handled and where.
    An object based approach for game development
    GameObject* objects[]
    GUIElement* elements[]
    Player* player
    Window* window
    while (true)
    {
    // read player input
    if (key == DOWN) {
    update player's position
    }
    if (joystick is moved) {
    update player's position
    }
    // update each object belonging to the game objects
    for (each game object) {
    do physics simulation
    do AI
    }
    // render each object belonging to the game objects
    for (each game object in objects[]) {
    Window->render(object)
    }
    // render each GUI element
    for (each game element in elements[]) {
    if (element has been updated) {
    Window->render(element)
    }



    }
    }
    What we got is a God object, a well known anti-pattern. As we add more features to our game, maintaining such all-knowing objects becomes a bottleneck of the project. It is also worth noting that when doing rendering, the renderer needs to go through all the game objects to know which objects have been updated. The same applies to the GUI elements. We don't use Observer pattern, so we can not notify the renderer about individual updates as soon as they happen.
    More on http://en.wikipedia.org/wiki/God_object

    Learned: It takes time to make an update which has measurable cost!

    8/28/2014
    Writting data on SDCard could be potential source of error! When SDCard is write protected.
    Does the shader ‘Transparent/cutout/diffuse’ work for unity android free? Objects with this shader does not render in unity android. Diffuse shader works fine.
    9/1/2014
    Developing custom TR2 formatted TR2 level

    9/3/2014
    Handle SFX with animation. Mapping TR2 SFX.
    9/8/2014
    Developing custom TR2 formatted level
    Custom level.png
    9/16/2014
    Realize engine architecture.
    To me, elegant way to using Game Engine is to use its query mechanism. Engine maintains a list of objects with component. Why not let engine handle them.

    • Push Object to engine and If anything to know ask it for that.
    • Extend room objects with MonoBehabiour component holding important room info.
    • Convert RoomEx class into Monobehabiour.
    • Limit Responsibility of Level class as component object factory and passing them to engine.
    Learned: Direct instantiation of MonoBehaviour is not allowed!
    Updating player class...
    Define Player Events:
    OnPositionChanged()
    OnSectorChanged()
    OnRoomChanged()
    CanHandlePrimaryAction() //Smartly determine possibility (Grab Ledge, Pull self up, Pull Push block e.g.) analyzing surface geometry.

    9/20/2014
    Add physics Generated events:
    OnCollision()
    During flying, jumping, landing general handled case is going to idle state.

    9/21/2014
    Updating AnimationStatePlayer!
    Mostly cleanup tasks! All Physics handlers are moved to Player Class. Now AnimationStatePlayer is just a view of Player class. Added Event for Animation states that Player class will handle. For example, OnJump event. Collision checks are mostly done on movement events. That’s a great saving against continuous collision check!
    FIXED: Lara is initially in negative height position in some level. but mistakenly her initial setup height position was 0. So while landing (without ground collision check!) Lara was trying to reach 0 position (her assumed landing position!). Which is higher than her initial jump position? This was giving floating on air error.
    FIXED: Every transform that gets hit in room by Lara is not room transform.


    9/22/2014
    Implement Lara’s self pull up action.
    Pull up movement state should be generated with DPAD + Action key combination

    9/23/2014
    Write algorithm to detect ladder step (platform) and find highest step from terrain triangle network.

    9/27/2014
    FIXED: Get back to free position bug. Actually last free position must be recorded unconditionally after every movement processing, weather collision happens or not. It must be unconditional. Free position must also be checked in idle state and start game.

    10/1/2014
    FIXED: Ladder Step Detection algorithm

    10/3/2014
    FIXED: Stand position after pulling self up on a Block. Unfortunately, I added Block height with last hanging height (of foot). In fact it will be hanging height (of foot) + body height, so that stand position is exactly on top of block.

    Note: Pivot point is Lara's foot.
    New Bug: Flickering after height adjustment!!
    When pull up animation going there is difference between physical foot position and actual pivot position. Difference remains at the end of animation. When I try to reposition pivot to expected position, model body is translated to new height including foot-pivot difference. When animation is reset or new animation is applied, pivot gets back to actual position. This back and forth translation causes Flickering.
















     
    Last edited: Oct 24, 2014
  30. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    This is custom level I'm developing. It is also playable with original Tomb Raider2 engine...

     
    2dchaos likes this.
  31. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Hi, everybody! Finally, this project has reached in a development stage where I think it is OK to release : ) I'm publishing in hope that it will be somehow helpful to our developer community. You can download it from https://github.com/suruz/TR2-Level-Viewer

    Project was mainly developed in Unity3D 3.5. Meanwhile a former animation system used in Unity3D 3.5 has gone legacy in Unity3D 5. I performed compatibility test of the project in latest Unity3D 5 Beta. Fortunately, it still works well. I'll try to post noticeable differences since Unity3D 3.5 in next posts.

    Title BG.png
     
    Stardog likes this.
  32. joeri_07

    joeri_07

    Joined:
    Dec 18, 2015
    Posts:
    36
    Suruz@kento great work u did here !
    I am really amazed by your project.

    I have a question though... i loaded WALL.TR2 in your level-viewer and I noticed that the water isn't being properly animated or collored (its just static transparant)... Are you planning on completing the level-viewer so it can properly display this? as it is in the game? I would like to recreate this map as it is in the game... How is this information parsed? i I know it is a animated texture, but there is also something tricky going on with the lighting... is this information included in the .TR2 files ? Did you come across this info somewhere and ignored it?

    Otherwise: could u give me a tip on how to create this effect in Unity?
    U can see the effect here :


    Once again i'm a big fan of the work you did here, great job !
     
  33. twinspectre021090

    twinspectre021090

    Joined:
    Mar 9, 2016
    Posts:
    7
    I admire what you did here, great job
    I hope you're still focused on this amazing project
     
  34. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35

    Thanks joeri_07

    Texture animation of water is in my TODO list. I postponed it because, water and other transparent
    room sections are not defined as separate mesh. Rather, separate data such as floor data is used to
    mask part of room mesh. At first I thought it little complicated :p. Currently I'm mostly concerned about improvising
    light and shade and I'm upgrading it for latest build target WebGL.

    Watching your liked video, I guess following is going on in water section,

    1. Separate mesh that contains water surface, wall and floor.
    2. Use transparent material in water surface.
    3. To animate water surface you can perform uv animation of main texture using script.
    4. For refraction effect in water, you can slightly distort uv of the floor material and wall material
    using math function like Sin()

    That's what I can say right now :)
     
  35. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Hi twinspectre021090,

    This project is still alive. I'm working on it to make it more approachable ;) I have added WebGL support in latest version. You can find recent changes here https://github.com/suruz/TR2-Level-Viewer.
     
  36. aeroson

    aeroson

    Joined:
    Jul 20, 2013
    Posts:
    22
    This is amazing, super cool, great job !
    Tomb Raider 2 was the favorite game of my childhood.
    Few screenshots: http://imgur.com/a/iXbOF
     
  37. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Nice shoot aeroson! I'm happy to learn that tomb raider level viewer can ignite imagination such way. Thanks once again :)
     
  38. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Right now I'm developing water effect! Actually there are few tasks involved, here,
    1. Development of water effect.
    2. Development of swimming behavior of water.
    3. Updating camera for underwater movement.
    Development of water effect:

    Here, I found few basic problems such as separating water surface from terrain/water holder (room bellow terrain), coloring water holder as part of underwater effect, texture animation of water surface. BTW, room not necessarily means generic room in TR2, it can be open terrain like area with water surface.

    Separation of water surface was done using room vertex attribute information that defines which vertices of room that are in water surface.

    Coloring for water surface was rather easy when separation of water surface was done. Different material was used for it. But, I was facing hard time in defining material for water holder and terrain above it:



    Because, both contain water surface. Terran contains front facing water surface and water holder contains back facing water surface.

    Here comes, another helpful information, room flag. In general, room flag defines room type, such as water filled room (e.g. water filled caves), under water room. Using this information I differentiated materials for water holder and water excluded terrain.



    As for texture animation in water, I used same texture atlas that is used for other rooms. Texture atlas contains frames for animation, but frames were located at different UV location in atlas. So I updated Mesh Builder to generate second UV set for next frame of animation. I developed a surface shader with second UV support. I simply toggled UV set using a Water Effect script to select animation frame from atlas.

    Please stay tuned for next parts of development info :)
     
    Eringo and Stardog like this.
  39. Eringo

    Eringo

    Joined:
    Jun 2, 2017
    Posts:
    2
    So happy to see you are still continuing this project!!!
    Looking forward to see how it develops.
     
  40. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Thank you : )
     
  41. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Here is second part of development.

    Developing caustic effect:
    Underwater light variation /caustic effect can be developed by vertex color modulation if you don't want to use light, or, by vertex normal modulation if you want to use light. Original tomb raider engine uses first method. But this viewer has day night system, so I developed it using second method.

    We need calculate displacement vector and then add this vector with vertex normal to get modulated vertex normal.

    if displacement vector is Vd,
    vertex normal is Vn,
    and modulated vertex normal Vm, Then

    Vm = Vn + Vd

    Here is simplified shader to calculate modulated vertex normal Vm, I used custom lighting with hard coded light direction as I don’t want actual lighting here.


    void vert (inout appdata_full v)
    {

    float3 Vn = v.normal;

    //Convert model space vertex position to world space vertex position
    float4 vertex = mul(unity_ObjectToWorld, v.vertex);

    //Get world space model center
    float3 _Center = half3(_CenterX, _CenterY, _CenterZ);

    //Calculate radial length of position vector
    float distance = length(vertex.xyz - _Center);

    //Use distance to calculate parameter of sin and cos function
    float param = 3.14 * distance * _Time.y * 0.25;

    //Calculate displacement vector and it's component
    float3 Vd = float3(sin(param), 0, cos(param));

    //Modulate vertex normal with displacement vector
    float3 Vm = Vn + Vd;

    //Use modulate vertex normal as final normal
    v.normal = Vm;

    }

    half4 LightingSimpleLambert(SurfaceOutput s, half3 lightDir, half atten)
    {
    //Override light direction as we dont need actual lighting
    lightDir = half3(1, 0.5, 1);
    half NdotL = pow(dot(s.Normal, lightDir), 4) * 0.25;
    half4 c;
    c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten);
    c.a = s.Alpha;
    return c;
    }

    We need convert position vector of each vertex into parameter space where parameter is length of position vector.



    In this 2D illustration I'm calculating position vector for each vertex and corresponding length in world space. As for origin, we can use model center origin or world zero. Model center origin will give better modulation

    Then I calculated components of displacement vector Vd, where x and z component of vector is calculated using Sin(length) and Cos(length) respectively, y component is left zero, as I don't want to modulate y component.



    This image illustrates radial wave function, here we plot height value( height represents Sine/Cosine function of distance from origin) at vertex's xz position.Along with increment of radial distance height value is periodically rises and drops as following image.



    As we are using this function to calculate component of our displacement vector, so displacement vector changes periodically. At the end changing displacement vector modulates vertex normal.

    We get caustic effect when modulated normal is used in light calculation.

    Caustic effect.jpg

    Your questions, suggestions and reviews are most welcome. Thank you for reading this post : )
     
    Last edited: Feb 10, 2018
    Eringo and Stardog like this.
  42. timsoret

    timsoret

    Joined:
    Apr 9, 2015
    Posts:
    14
    Hello suruz. Are you still maintaining this project? I'd love to get involved & help you revive Tomb Raider. As of right now, I can't make the project run properly in Unity 2017.
     
  43. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Hi timsoret, I'm maintaining it in my own pace : ) You are most welcome to this project. Together we can do many interesting things. Let me know what you are thinking. BTW, I'm excited to know what problem you are facing with this project in Unity 2017.
     
  44. Morphy

    Morphy

    Joined:
    Jan 27, 2014
    Posts:
    1
    how a mazying
     
  45. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Hello everybody! How are you doing? I was so caught by life maintenance, it was almost impacting my passion. I managed to steal few hours though. Highly talented Tomb Raider fans out there. They are developing amazing custom levels for classic game engine. I decided to mess with one of them in Unity level viewer. I thought I could share with you. Keep well, have fun!

     
    tspk91 and Peter77 like this.
  46. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Hi, I planed to adapt tr4 level data in this tr2 level viewer. I'm planing implement adaption method that will make maximum use of existing level parser. Here is progress so far. Here I loaded mesh of trle level Beyond of Gobi from http://www.trle.net/sc/search.php I have plan to make video explaining adaptation details.

    Adapting Tr4 level in viewer.jpg
     
  47. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Wish you all a very Happy New Year 2020 ! I'm trying to adapt Tomb Raider 2 level viewer to support TR4 file. Here is progress so far with texture mapping.

    Adapting Tr4 level viewer_ .jpg
     
    Peter77 likes this.
  48. Develax

    Develax

    Joined:
    Nov 14, 2017
    Posts:
    11
    This is really cool, man!
    I'd like to have time for this too.
    Hope I will.
     
  49. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    35
    Thank you :D Let me know If I could be any help for you.
     
  50. warthos3399

    warthos3399

    Joined:
    May 11, 2019
    Posts:
    511
    If this is ever finished or close, you let me know, i can make this (gfx/rendering/texture/PPE/resolution/etc.) look like a million bucks. Note:
    • The conversion/export capabilities seem pretty good.
    • Remember, your importing (textures/meshes/data) a PS1/PS2 type title into Unity. Once all data is imported for the game, now its time to update gfx, big time, to be up to todays standard. People would love/want a gfx update to a remake of Tomb Raider 2.
    • Many possibilities for improvements on all elements/systems.
    Great idea, i see your passionate, this goes as far as YOU want it to. Want inspiration? Check out my thread (in signature), Trial and tribulation, look what im dealing with daily, lol. In the end its what YOU want...
     
unityunity