Skip to content

Commit

Permalink
Merge pull request #251 from GothicVRProject/feature/vob-particlefx
Browse files Browse the repository at this point in the history
Feature/vob particlefx
  • Loading branch information
JaXt0r authored Nov 7, 2023
2 parents e7684d8 + c260df6 commit 6683333
Show file tree
Hide file tree
Showing 13 changed files with 5,140 additions and 15 deletions.
4,901 changes: 4,901 additions & 0 deletions Assets/GothicVR/Resources/Prefabs/Vobs/vobPfx.prefab

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions Assets/GothicVR/Resources/Prefabs/Vobs/vobPfx.prefab.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Assets/GothicVR/Scenes/Bootstrap.unity
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ MonoBehaviour:
vobCullingLarge:
maxObjectSize: 100
cullingDistance: 200
enableVobParticles: 0
enableFineGrainedWorldMeshCreation: 0
enableWorldCulling: 0
--- !u!1 &394991343
Expand Down
8 changes: 8 additions & 0 deletions Assets/GothicVR/Scripts/Bootstrap/PhoenixBootstrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public void BootGothicVR(string g1Dir)
SetLanguage();
LoadGothicVM(g1Dir);
LoadSfxVM(g1Dir);
LoadPfxVm(g1Dir);
LoadMusicVM(g1Dir);
LoadMusic();
LoadFonts();
Expand Down Expand Up @@ -147,6 +148,13 @@ private void LoadSfxVM(string G1Dir)
GameData.VmSfxPtr = vmPtr;
}

private static void LoadPfxVm(string g1Dir)
{
var fullPath = Path.GetFullPath(Path.Join(g1Dir, "/_work/DATA/scripts/_compiled/PARTICLEFX.DAT"));
var vmPtr = VmGothicExternals.LoadVm(fullPath);
GameData.VmPfxPtr = vmPtr;
}

private void LoadMusicVM(string G1Dir)
{
var fullPath = Path.GetFullPath(Path.Join(G1Dir, "/_work/DATA/scripts/_compiled/MUSIC.DAT"));
Expand Down
16 changes: 16 additions & 0 deletions Assets/GothicVR/Scripts/Caches/AssetCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public static class AssetCache
private static Dictionary<string, PxVmItemData> itemDataCache = new();
private static Dictionary<string, PxVmMusicData> musicDataCache = new();
private static Dictionary<string, PxVmSfxData> sfxDataCache = new();
private static Dictionary<string, PxVmPfxData> pfxDataCache = new();
private static Dictionary<string, PxSoundData<float>> soundCache = new();
private static Dictionary<string, PxFontData> fontCache = new();

Expand Down Expand Up @@ -270,6 +271,21 @@ public static PxVmSfxData TryGetSfxData(string key)
return newData;
}

/// <summary>
/// Hint: Instances only need to be initialized once on phoenix and don't need to be deleted during runtime.
/// </summary>
public static PxVmPfxData TryGetPfxData(string key)
{
var preparedKey = GetPreparedKey(key);
if (pfxDataCache.TryGetValue(preparedKey, out var data))
return data;

var newData = PxVm.InitializePfx(GameData.VmPfxPtr, preparedKey);
pfxDataCache[preparedKey] = newData;

return newData;
}

public static PxSoundData<float> TryGetSound(string key)
{
var preparedKey = GetPreparedKey(key);
Expand Down
2 changes: 2 additions & 0 deletions Assets/GothicVR/Scripts/Caches/PrefabCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public enum PrefabType
VobItem,
VobInteractable,
VobSpot,
VobPfx,
VobMusic,
VobSound,
VobSoundDaytime,
Expand All @@ -26,6 +27,7 @@ private static string GetPath(PrefabType type)
PrefabType.VobItem => "Prefabs/Vobs/oCItem",
PrefabType.VobInteractable => "Prefabs/Vobs/Interactable",
PrefabType.VobSpot => "Prefabs/Vobs/zCVobSpot",
PrefabType.VobPfx => "Prefabs/Vobs/vobPfx",
PrefabType.VobMusic => "Prefabs/Vobs/oCZoneMusic",
PrefabType.VobSound => "Prefabs/Vobs/zCVobSound",
PrefabType.VobSoundDaytime => "Prefabs/Vobs/zCVobSoundDaytime",
Expand Down
173 changes: 162 additions & 11 deletions Assets/GothicVR/Scripts/Creator/VobCreator.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using GothicVR.Vob;
Expand All @@ -23,6 +24,7 @@
using PxCs.Data.Vob;
using PxCs.Interface;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.XR.Interaction.Toolkit;
using Vector3 = System.Numerics.Vector3;

Expand Down Expand Up @@ -57,7 +59,7 @@ private static void PostWorldLoaded()
return;

var loc = Camera.main!.transform.position;
foreach (var sound in LookupCache.vobSoundsAndDayTime)
foreach (var sound in LookupCache.vobSoundsAndDayTime.Where(i => i != null))
{
var soundLoc = sound.transform.position;
var soundDist = sound.GetComponent<AudioSource>().maxDistance;
Expand Down Expand Up @@ -179,11 +181,18 @@ public static async Task CreateAsync(GameObject rootTeleport, GameObject rootNon
case PxWorld.PxVobType.PxVob_zCVob:
{
GameObject obj;
if (vob.visualType == PxWorld.PxVobVisualType.PxVobVisualDecal)
obj = CreateDecal(vob);
else
obj = CreateDefaultMesh(vob);

switch (vob.visualType)
{
case PxWorld.PxVobVisualType.PxVobVisualDecal:
obj = CreateDecal(vob);
break;
case PxWorld.PxVobVisualType.PxVobVisualParticleSystem:
obj = CreatePfx(vob);
break;
default:
obj = CreateDefaultMesh(vob);
break;
}
cullingVobObjects.Add(obj);
break;
}
Expand Down Expand Up @@ -591,23 +600,165 @@ private static GameObject CreateDecal(PxVobData vob)
if (!FeatureFlags.I.EnableDecals)
return null;


var parent = parentGosTeleport[vob.type];

return VobMeshCreator.CreateDecal(vob, parent);
}

/// <summary>
/// Please check description at worldofgothic for more details:
/// https://www.worldofgothic.de/modifikation/index.php?go=particelfx
/// </summary>
private static GameObject CreatePfx(PxVobData vob)
{
if (!FeatureFlags.I.enableVobParticles)
return null;

// FIXME - move to non-teleport
var parent = parentGosTeleport[vob.type];

var pfxGo = PrefabCache.TryGetObject(PrefabCache.PrefabType.VobPfx);
pfxGo.name = vob.visualName;
SetPosAndRot(pfxGo, vob.position, vob.rotation);
pfxGo.SetParent(parent);

var pfx = AssetCache.TryGetPfxData(vob.visualName);
var particleSystem = pfxGo.GetComponent<ParticleSystem>();

pfxGo.GetComponent<VobPfxProperties>().pfxData = pfx;

particleSystem.Stop();

var gravity = pfx.flyGravity.Split();
float gravityX = 1f, gravityY = 1f, gravityZ = 1f;
if (gravity.Length == 3)
{
// Gravity seems too low. Therefore *10k.
gravityX = float.Parse(gravity[0], CultureInfo.InvariantCulture) * 10000;
gravityY = float.Parse(gravity[1], CultureInfo.InvariantCulture) * 10000;
gravityZ = float.Parse(gravity[2], CultureInfo.InvariantCulture) * 10000;
}

// Main module
{
var mainModule = particleSystem.main;
var minLifeTime = (pfx.lspPartAvg - pfx.lspPartVar) / 1000; // I assume we need to change milliseconds to seconds.
var maxLifeTime = (pfx.lspPartAvg + pfx.lspPartVar) / 1000;
mainModule.duration = 1f; // I assume pfx data wants a cycle being 1 second long.
mainModule.startLifetime = new (minLifeTime, maxLifeTime);
mainModule.loop = pfx.ppsIsLooping;

var minSpeed = (pfx.velAvg - pfx.velVar) / 1000;
var maxSpeed = (pfx.velAvg + pfx.velVar) / 1000;
mainModule.startSpeed = new(minSpeed, maxSpeed);
}

// Emission module
{
var emissionModule = particleSystem.emission;
emissionModule.rateOverTime = new ParticleSystem.MinMaxCurve(pfx.ppsValue);
}

// Force over Lifetime module
{
var forceModule = particleSystem.forceOverLifetime;
if (gravity.Length == 3)
{
forceModule.enabled = true;
forceModule.x = gravityX;
forceModule.y = gravityY;
forceModule.z = gravityZ;
}
}

// Renderer module
{
var rendererModule = pfxGo.GetComponent<ParticleSystemRenderer>();
// FIXME - Move to a cached constant value
var standardShader = Shader.Find("Universal Render Pipeline/Particles/Unlit");
var material = new Material(standardShader);
rendererModule.material = material;
TextureManager.I.SetTexture(pfx.visName, rendererModule.material);
// renderer.material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest; // First check with no change.

switch (pfx.visAlphaFunc.ToUpper())
{
case "BLEND":
rendererModule.material.ToTransparentMode(); // e.g. leaves.pfx.
break;
case "ADD":
rendererModule.material.ToAdditiveMode();
break;
default:
Debug.LogWarning($"Particle AlphaFunc {pfx.visAlphaFunc} not yet handled.");
break;
}
// makes the material render both faces
rendererModule.material.SetInt("_Cull", (int)CullMode.Off);

switch (pfx.visOrientation)
{
case "NONE":
rendererModule.alignment = ParticleSystemRenderSpace.View;
break;
case "WORLD":
rendererModule.alignment = ParticleSystemRenderSpace.World;
break;
case "VELO":
rendererModule.alignment = ParticleSystemRenderSpace.Velocity;
break;
default:
Debug.LogWarning($"visOrientation {pfx.visOrientation} not yet handled.");
break;
}
}

// Shape module
{
var shapeModule = particleSystem.shape;
switch (pfx.shpType.ToUpper())
{
case "SPHERE":
shapeModule.shapeType = ParticleSystemShapeType.Sphere;
break;
case "CIRCLE":
shapeModule.shapeType = ParticleSystemShapeType.Circle;
break;
case "MESH":
shapeModule.shapeType = ParticleSystemShapeType.Mesh;
break;
default:
Debug.LogWarning($"Particle ShapeType {pfx.shpType} not yet handled.");
break;
}

var shapeDimensions = pfx.shpDim.Split();
switch (shapeDimensions.Length)
{
case 1:
shapeModule.radius = float.Parse(shapeDimensions[0], CultureInfo.InvariantCulture) / 100; // cm in m
break;
default:
Debug.LogWarning($"shpDim >{pfx.shpDim}< not yet handled");
break;
}

shapeModule.rotation = new(pfx.dirAngleElev, 0, 0);
shapeModule.alignToDirection = true;
}

particleSystem.Play();

return pfxGo;
}

private static GameObject CreateDefaultMesh(PxVobData vob, bool nonTeleport = false)
{
var parent = nonTeleport ? parentGosNonTeleport[vob.type] : parentGosTeleport[vob.type];
var meshName = vob.showVisual ? vob.visualName : vob.vobName;

if (meshName == string.Empty)
return null;

// FIXME - PFX effects not yet implemented
if (meshName.ToLower().EndsWith(".pfx"))
return null;

// MDL
var mdl = AssetCache.TryGetMdl(meshName);
Expand Down
2 changes: 2 additions & 0 deletions Assets/GothicVR/Scripts/Debugging/FeatureFlags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public class VobCullingGroupSetting
public VobCullingGroupSetting vobCullingLarge;

[Header("__________Experimental / Do not use in Production__________")]
[Tooltip("Looks already quite good for leaves.pfy in the forest, but fire is awkward.")]
public bool enableVobParticles;
[Tooltip("The current implementation costs more frames than it saves. But it's a potential starting point for further enhancements like gluing small related objects together. Stored here for future use.")]
public bool enableFineGrainedWorldMeshCreation;
[Tooltip("Experimental. Looks weird without proper distance shadow. Could save some frames if combined with well looking distance shadow.")]
Expand Down
18 changes: 16 additions & 2 deletions Assets/GothicVR/Scripts/Extensions/MaterialExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,23 @@ public static void ToTransparentMode(this Material material)
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
material.SetInt("_ZWrite", 0);
material.DisableKeyword("_ALPHATEST_ON");
material.SetFloat("_SurfaceType", 1f);
material.EnableKeyword("_ALPHATEST_ON");
material.EnableKeyword("_ALPHABLEND_ON");
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
material.EnableKeyword("_ALPHAPREMULTIPLY_ON");
material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
}

public static void ToAdditiveMode(this Material material)
{
material.SetOverrideTag("RenderType", "Transparent");
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_ZWrite", 0);
material.SetFloat("_SurfaceType", 1f);
material.EnableKeyword("_ALPHATEST_ON");
material.EnableKeyword("_ALPHABLEND_ON");
material.EnableKeyword("_ALPHAPREMULTIPLY_ON");
material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
}
}
Expand Down
11 changes: 9 additions & 2 deletions Assets/GothicVR/Scripts/Manager/GameData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public static class GameData
{
public static IntPtr VfsPtr;
public static IntPtr VmGothicPtr;
public static IntPtr VmSfxPtr;
public static IntPtr VmSfxPtr; // Sound FX
public static IntPtr VmPfxPtr; // Particle FX
public static IntPtr VmMusicPtr;

private static WorldData worldInternal;
Expand Down Expand Up @@ -67,6 +68,12 @@ public static void Dispose()
VmSfxPtr = IntPtr.Zero;
}

if (VmPfxPtr != IntPtr.Zero)
{
PxVm.pxVmDestroy(VmPfxPtr);
VmPfxPtr = IntPtr.Zero;
}

if (VmMusicPtr != IntPtr.Zero)
{
PxVm.pxVmDestroy(VmMusicPtr);
Expand All @@ -87,4 +94,4 @@ private static void SetWayPointData(PxWayPointData[] wayPoints)
}
}
}
}
}
12 changes: 12 additions & 0 deletions Assets/GothicVR/Scripts/Vob/VobPfxProperties.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using GVR.Properties;
using PxCs.Data.Vm;
using UnityEngine;

namespace GVR.Vob
{
public class VobPfxProperties : VobProperties
{
[SerializeField]
public PxVmPfxData pfxData;
}
}
3 changes: 3 additions & 0 deletions Assets/GothicVR/Scripts/Vob/VobPfxProperties.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ProjectSettings/GraphicsSettings.asset
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ GraphicsSettings:
m_VideoShadersIncludeMode: 2
m_AlwaysIncludedShaders:
- {fileID: 4800000, guid: 650dd9526735d5b46b79224bc6e94025, type: 3}
- {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
- {fileID: -6465566751694194690, guid: 9b4e681081e2b4c469111bb649e2f7ee, type: 3}
- {fileID: -6465566751694194690, guid: 6a56f4d49ccc01c4caeafc424f9766ed, type: 3}
- {fileID: 4800000, guid: 6177a3f8311d19a48b15a5fda3907388, type: 3}
Expand Down

0 comments on commit 6683333

Please sign in to comment.