Browse Source

First bits of entity logic: assign alias models, update transform and animation, remove entity. Still lots of problems to solve, but it's starting to do something.

console
Nico de Poel 5 years ago
parent
commit
e6fd7b83ae
  1. 5
      Assets/Scripts/Data/QExtensions.cs
  2. 62
      Assets/Scripts/Game/Entity.cs
  3. 3
      Assets/Scripts/Game/Entity.cs.meta
  4. 15
      Assets/Scripts/Game/GameAssets.cs
  5. 65
      Assets/Scripts/Game/GameState.cs
  6. 36
      Assets/Scripts/Modules/GameModule.Interop.cs
  7. 33
      Assets/Scripts/Modules/GameModule.cs
  8. 6
      Assets/Scripts/Modules/RenderModule.cs
  9. 2
      Assets/Scripts/Support/AliasModel.cs
  10. 2
      Assets/Scripts/UniQuake.Interop.cs
  11. 4
      Assets/Scripts/UniQuake.cs
  12. 3
      engine/Quake/cl_main.c
  13. 7
      engine/Quake/cl_parse.c
  14. 4
      engine/Quake/client.h
  15. 29
      engine/UniQuake/game_uniquake.c
  16. 3
      engine/UniQuake/uniquake.c
  17. 3
      engine/UniQuake/uniquake.h
  18. 1
      engine/Windows/VisualStudio/uniquake.vcxproj
  19. 3
      engine/Windows/VisualStudio/uniquake.vcxproj.filters

5
Assets/Scripts/Data/QExtensions.cs

@ -51,6 +51,11 @@ public static class QExtensions
return new Vector3(vec.x, vec.z, vec.y);
}
public static Vector3 ToUnityPosition(this QVec3 origin)
{
return new Vector3(origin.x, origin.z, origin.y);
}
public static Quaternion ToUnityRotation(this QVec3 angles)
{
return Quaternion.Euler(angles.x, 90 - angles.y, -angles.z);

62
Assets/Scripts/Game/Entity.cs

@ -0,0 +1,62 @@
using UnityEngine;
using UnityEngine.Rendering;
public class Entity
{
private readonly int entityNum;
private GameObject gameObject;
private SkinnedMeshRenderer meshRenderer;
private AliasModel aliasModel;
public Entity(int entityNum)
{
this.entityNum = entityNum;
CreateGameObject();
}
private void CreateGameObject()
{
gameObject = new GameObject($"Entity_{entityNum}");
// DEBUG - we'll want to instantiate a prefab and assign materials from a preset collection
meshRenderer = gameObject.AddComponent<SkinnedMeshRenderer>();
meshRenderer.material = new Material(Shader.Find("Universal Render Pipeline/Simple Lit"));
meshRenderer.shadowCastingMode = ShadowCastingMode.Off;
meshRenderer.receiveShadows = false;
meshRenderer.lightProbeUsage = LightProbeUsage.Off;
meshRenderer.reflectionProbeUsage = ReflectionProbeUsage.Off;
}
public void Destroy()
{
Object.Destroy(gameObject);
}
public void SetAliasModel(AliasModel model)
{
aliasModel = model;
// Set a default pose based on the first animation frame
UpdateAnimation(0);
}
public void UpdateAnimation(int frameNum)
{
if (aliasModel != null)
{
aliasModel.Animate(frameNum, out Mesh mesh, out float blendWeight);
meshRenderer.sharedMesh = mesh;
if (mesh.blendShapeCount > 0)
meshRenderer.SetBlendShapeWeight(0, blendWeight);
}
}
public void SetTransform(Vector3 position, Quaternion rotation)
{
gameObject.transform.position = position;
gameObject.transform.rotation = rotation;
}
}

3
Assets/Scripts/Game/Entity.cs.meta

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e876769ea29c4d10bfa6b7eea48009a3
timeCreated: 1625824889

15
Assets/Scripts/Game/GameAssets.cs

@ -45,6 +45,21 @@ public class GameAssets
aliasModels.Add(aliasModel);
}
public bool TryGetAliasModel(string name, out AliasModel aliasModel)
{
foreach (var model in aliasModels)
{
if (model.Name == name)
{
aliasModel = model;
return true;
}
}
aliasModel = null;
return false;
}
public void AddBrushModel(BrushModel brushModel)
{
brushModels.Add(brushModel);

65
Assets/Scripts/Game/GameState.cs

@ -9,6 +9,8 @@ public class GameState
private GameObject worldGameObject;
private readonly Dictionary<int, Entity> entities = new Dictionary<int, Entity>();
public GameState(UniQuake uniQuake)
{
uq = uniQuake;
@ -16,16 +18,18 @@ public class GameState
public void Destroy()
{
DestroyEntities();
DestroyWorld();
}
public void NewMap(BrushModel worldModel)
{
DestroyWorld();
Destroy();
// DEBUG - we'll want to instantiate prefabs for each submodel and assign materials from a preset collection
worldGameObject = new GameObject(worldModel.Name);
for (int i = 0; i < worldModel.SubModelCount; ++i)
//for (int i = 0; i < worldModel.SubModelCount; ++i)
for (int i = 0; i < 1; ++i)
{
var subModel = worldModel.GetSubModel(i);
var subModelGO = new GameObject($"SubModel_{i}");
@ -64,4 +68,61 @@ public class GameState
worldGameObject = null;
}
}
public void SetEntityAliasModel(int entityNum, string modelName)
{
if (!entities.TryGetValue(entityNum, out var entity))
{
entity = new Entity(entityNum);
entities.Add(entityNum, entity);
}
if (!uq.GameAssets.TryGetAliasModel(modelName, out var aliasModel))
{
Debug.LogWarning($"Unknown alias model name: {aliasModel}");
return;
}
entity.SetAliasModel(aliasModel);
}
public void SetEntityWorldModel(int entityNum, int subModelNum)
{
// TODO: obtain Mesh from brush submodel and assign it to MeshRenderer
}
public void UpdateEntityAnimation(int entityNum, int frameNum)
{
if (entities.TryGetValue(entityNum, out var entity))
{
entity.UpdateAnimation(frameNum);
}
}
public void SetEntityTransform(int entityNum, Vector3 position, Quaternion rotation)
{
if (entities.TryGetValue(entityNum, out var entity))
{
entity.SetTransform(position, rotation);
}
}
public void RemoveEntity(int entityNum)
{
if (entities.TryGetValue(entityNum, out var entity))
{
entity.Destroy();
entities.Remove(entityNum);
}
}
private void DestroyEntities()
{
foreach (var entity in entities.Values)
{
entity.Destroy();
}
entities.Clear();
}
}

36
Assets/Scripts/Modules/GameModule.Interop.cs

@ -2,6 +2,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using AOT;
using UnityEngine;
public partial class GameModule : CallbackHandler<GameModule>
@ -11,6 +12,10 @@ public partial class GameModule : CallbackHandler<GameModule>
var callbacks = new Callbacks
{
target = TargetPtr,
SetEntityModel = CreateCallback<GameSetEntityModelCallback>(Callback_GameSetEntityModel),
SetEntityTransform = CreateCallback<GameSetEntityTransformCallback>(Callback_GameSetEntityTransform),
RemoveEntity = CreateCallback<GameRemoveEntityCallback>(Callback_GameRemoveEntity),
};
RegisterCallbacks(callbacks);
@ -23,5 +28,36 @@ public partial class GameModule : CallbackHandler<GameModule>
private class Callbacks
{
public IntPtr target;
public IntPtr SetEntityModel;
public IntPtr SetEntityTransform;
public IntPtr RemoveEntity;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void GameSetEntityModelCallback(IntPtr target, int entityNum, [MarshalAs(UnmanagedType.LPStr)] string modelName, int frame);
[MonoPInvokeCallback(typeof(GameSetEntityModelCallback))]
private static void Callback_GameSetEntityModel(IntPtr target, int entityNum, string modelName, int frame)
{
GetSelf(target).SetEntityModel(entityNum, modelName, frame);
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void GameSetEntityTransformCallback(IntPtr target, int entityNum, QVec3 origin, QVec3 angles);
[MonoPInvokeCallback(typeof(GameSetEntityTransformCallback))]
private static void Callback_GameSetEntityTransform(IntPtr target, int entityNum, QVec3 origin, QVec3 angles)
{
GetSelf(target).SetEntityTransform(entityNum, origin, angles);
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void GameRemoveEntityCallback(IntPtr target, int entityNum);
[MonoPInvokeCallback(typeof(GameRemoveEntityCallback))]
private static void Callback_GameRemoveEntity(IntPtr target, int entityNum)
{
GetSelf(target).RemoveEntity(entityNum);
}
}

33
Assets/Scripts/Modules/GameModule.cs

@ -11,4 +11,37 @@ public partial class GameModule
uq = uniQuake;
BuildCallbacks();
}
private void SetEntityModel(int entityNum, string modelName, int frame)
{
if (modelName != null)
{
if (modelName.StartsWith("*"))
{
if (!int.TryParse(modelName.Substring(1), out int subModelNum))
{
Debug.LogWarning($"Invalid world submodel index: {modelName}");
return;
}
uq.GameState.SetEntityWorldModel(entityNum, subModelNum);
return;
}
// TODO: identify non-world brush model names
uq.GameState.SetEntityAliasModel(entityNum, modelName);
}
uq.GameState.UpdateEntityAnimation(entityNum, frame);
}
private void SetEntityTransform(int entityNum, QVec3 origin, QVec3 angles)
{
uq.GameState.SetEntityTransform(entityNum, origin.ToUnityPosition(), angles.ToUnityRotation());
}
private void RemoveEntity(int entityNum)
{
uq.GameState.RemoveEntity(entityNum);
}
}

6
Assets/Scripts/Modules/RenderModule.cs

@ -31,12 +31,12 @@ public partial class RenderModule
Debug.Log($"Alias model '{name}' (frame type {frameType}) with {header.numVerts} vertices, {header.numTriangles} triangles, {header.numFrames} frame(s), {header.numPoses} pose(s):\n{sb}");
string modelName = System.IO.Path.GetFileNameWithoutExtension(name);
AliasModel aliasModel = new AliasModel(modelName, frameType);
AliasModel aliasModel = new AliasModel(name, frameType);
aliasModel.ImportMeshData(header, poseVertices, triangles, stVertices);
uq.GameAssets.AddAliasModel(aliasModel);
string modelName = System.IO.Path.GetFileNameWithoutExtension(name);
var go = new GameObject(modelName);
go.transform.SetPositionAndRotation(new Vector3(xPos, 0, zPos), Quaternion.identity);
@ -138,7 +138,7 @@ public partial class RenderModule
if (cam == null)
return;
cam.transform.position = origin.ToVector3().ToUnity();
cam.transform.position = origin.ToUnityPosition();
cam.transform.rotation = angles.ToUnityRotation();
}
}

2
Assets/Scripts/Support/AliasModel.cs

@ -11,6 +11,8 @@ public class AliasModel
private readonly QAliasFrameType frameType;
private readonly List<(int, Mesh)> animationMeshes = new List<(int, Mesh)>();
public string Name => name;
public AliasModel(string name, QAliasFrameType frameType)
{
this.name = name;

2
Assets/Scripts/UniQuake.Interop.cs

@ -18,7 +18,7 @@ public partial class UniQuake
private IntPtr libraryHandle;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void UniQuake_InitFunc(IntPtr parms, IntPtr sysCalls, IntPtr glCalls);
private delegate void UniQuake_InitFunc(IntPtr parms, IntPtr sysCalls, IntPtr glCalls, IntPtr gameCalls);
private UniQuake_InitFunc UniQuake_Init;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]

4
Assets/Scripts/UniQuake.cs

@ -96,7 +96,9 @@ public partial class UniQuake: MonoBehaviour
try
{
UniQuake_SetFmodSystem(AudioManager.Instance.FmodSystem.handle);
UniQuake_Init(quakeParms.ToNativePtr(), systemModule.CallbacksPtr, renderModule.CallbacksPtr);
UniQuake_Init(quakeParms.ToNativePtr(),
systemModule.CallbacksPtr, renderModule.CallbacksPtr, gameModule.CallbacksPtr);
initialized = true;
}
catch (QuakeException ex)

3
engine/Quake/cl_main.c

@ -459,6 +459,7 @@ void CL_RelinkEntities (void)
{
ent->model = NULL;
ent->lerpflags |= LERP_RESETMOVE|LERP_RESETANIM; //johnfitz -- next time this entity slot is reused, the lerp will need to be reset
UQ_Game_RemoveEntity(i);
continue;
}
@ -580,6 +581,8 @@ void CL_RelinkEntities (void)
cl_visedicts[cl_numvisedicts] = ent;
cl_numvisedicts++;
}
UQ_Game_SetEntityTransform(i, ent->origin, ent->angles);
}
}

7
engine/Quake/cl_parse.c

@ -617,6 +617,13 @@ void CL_ParseUpdate (int bits)
R_TranslateNewPlayerSkin (num - 1); //johnfitz -- was R_TranslatePlayerSkin
ent->lerpflags |= LERP_RESETANIM; //johnfitz -- don't lerp animation across model changes
UQ_Game_SetEntityModel(num, model->name, ent->frame);
}
else
{
// Model hasn't changed, just update the animation info
UQ_Game_SetEntityModel(num, NULL, ent->frame);
}
//johnfitz

4
engine/Quake/client.h

@ -372,5 +372,9 @@ void TraceLine (vec3_t start, vec3_t end, vec3_t impact);
void Chase_UpdateForClient (void); //johnfitz
void Chase_UpdateForDrawing (void); //johnfitz
void UQ_Game_SetEntityModel(int entityNum, const char *modelName, int frame);
void UQ_Game_SetEntityTransform(int entityNum, vec3_t origin, vec3_t angles);
void UQ_Game_RemoveEntity(int entityNum);
#endif /* _CLIENT_H_ */

29
engine/UniQuake/game_uniquake.c

@ -0,0 +1,29 @@
#include "uniquake.h"
#include "../Quake/quakedef.h"
typedef struct unity_gamecalls_s
{
void *target;
void(*SetEntityModel)(void *target, int entityNum, const char *modelName, int frame);
void(*SetEntityTransform)(void *target, int entityNum, vec3_t origin, vec3_t angles);
void(*RemoveEntity)(void *target, int entityNum);
} unity_gamecalls_t;
const unity_gamecalls_t *unity_gamecalls;
void UQ_Game_SetEntityModel(int entityNum, const char *modelName, int frame)
{
unity_gamecalls->SetEntityModel(unity_gamecalls->target, entityNum, modelName, frame);
}
void UQ_Game_SetEntityTransform(int entityNum, vec3_t origin, vec3_t angles)
{
unity_gamecalls->SetEntityTransform(unity_gamecalls->target, entityNum, origin, angles);
}
void UQ_Game_RemoveEntity(int entityNum)
{
unity_gamecalls->RemoveEntity(unity_gamecalls->target, entityNum);
}

3
engine/UniQuake/uniquake.c

@ -14,12 +14,13 @@ UNIQUAKE_API void UniQuake_SetFmodSystem(FMOD_SYSTEM *system, int playernumber)
}
#endif
UNIQUAKE_API void UniQuake_Init(quakeparms_t *parms, const unity_syscalls_t *syscalls, const unity_glcalls_t *glcalls)
UNIQUAKE_API void UniQuake_Init(quakeparms_t *parms, const unity_syscalls_t *syscalls, const unity_glcalls_t *glcalls, const unity_gamecalls_t *gamecalls)
{
host_parms = parms;
unity_syscalls = syscalls;
unity_glcalls = glcalls;
unity_gamecalls = gamecalls;
COM_InitArgv(parms->argc, parms->argv);
parms->argc = com_argc;

3
engine/UniQuake/uniquake.h

@ -14,3 +14,6 @@ extern const unity_syscalls_t *unity_syscalls;
typedef struct unity_glcalls_s unity_glcalls_t;
extern const unity_glcalls_t *unity_glcalls;
typedef struct unity_gamecalls_s unity_gamecalls_t;
extern const unity_gamecalls_t *unity_gamecalls;

1
engine/Windows/VisualStudio/uniquake.vcxproj

@ -359,6 +359,7 @@ copy "$(SolutionDir)\..\SDL2\lib64\*.dll" "$(TargetDir)"</Command>
<ClCompile Include="..\..\Quake\wad.c" />
<ClCompile Include="..\..\Quake\world.c" />
<ClCompile Include="..\..\Quake\zone.c" />
<ClCompile Include="..\..\UniQuake\game_uniquake.c" />
<ClCompile Include="..\..\UniQuake\gl_uniquake.c" />
<ClCompile Include="..\..\UniQuake\stub\stub_opengl.c" />
<ClCompile Include="..\..\UniQuake\in_uniquake.c" />

3
engine/Windows/VisualStudio/uniquake.vcxproj.filters

@ -241,6 +241,9 @@
<ClCompile Include="..\..\UniQuake\vid_uniquake.c">
<Filter>uniquake</Filter>
</ClCompile>
<ClCompile Include="..\..\UniQuake\game_uniquake.c">
<Filter>uniquake</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\Quake\anorm_dots.h">

Loading…
Cancel
Save