I read a a file and create entities, there are 4000 of them, so not a big deal, fps is stable. I have cinemachine setup for orbiting around the objects. As soon as I move the camera fps drops to 40-50. I want to read protein file and display all atoms that are in the file. Here is the code (read file from MonoBehaviour, access system and trigger it through MonoBehaviour):
using System;
using System.IO;
using Core.Dots.Systems;
using Cysharp.Threading.Tasks;
using NaughtyAttributes;
using Unity.Collections;
using Unity.Entities;
using UnityEngine;
namespace Core.Reader
{
public class PdbReader : MonoBehaviour
{
public TextAsset PdfFile;
private Protein _protein;
[Button("Load")]
public void Load()
{
if (string.IsNullOrEmpty(PdfFile.text)) return;
_ = LoadPdpFile(PdfFile.text, () =>
{
CreateProtein();
});
}
private void CreateProtein()
{
var world = World.DefaultGameObjectInjectionWorld;
var systemHandle = world.Unmanaged.GetExistingSystemState<PdbEntityBuilderSystem>();
// Collect parsed atom data
var atoms = new NativeArray<AtomAuthoring.AtomComponent>(_protein.Atoms.Count, Allocator.Temp);
var index = 0;
foreach (var atom in _protein.Atoms)
{
atoms[index++] = new AtomAuthoring.AtomComponent
{
AtomNumber = atom.Key,
AtomType = atom.Value.AtomType,
Position = atom.Value.Position,
Size = GetAtomSize(atom.Value.AtomType)
};
}
world.Unmanaged.GetUnsafeSystemRef<PdbEntityBuilderSystem>(systemHandle.SystemHandle).LoadAtoms(atoms);
}
async UniTaskVoid LoadPdpFile(string data, Action onComplete)
{
await UniTask.RunOnThreadPool(() =>
{
ParsePDBFile(data);
});
Debug.Log(_protein.ToString());
onComplete?.Invoke();
}
void ParsePDBFile(string data)
{
_protein = new Protein("Protein");
using StringReader reader = new StringReader(data);
while (reader.ReadLine() is { } line)
{
if (line.StartsWith("ATOM") || line.StartsWith("HETATM"))
{
var atom = ParseAtomLine(line);
if (atom != null)
{
_protein.AddAtom(atom.AtomNumber, atom.AtomType, atom.Position);
}
}
}
}
Atom ParseAtomLine(string line)
{
try
{
int atomNumber = int.Parse(line.Substring(6, 5).Trim());
string atomType = line.Substring(76, 2).Trim(); // Atom type
float x = float.Parse(line.Substring(30, 8));
float y = float.Parse(line.Substring(38, 8));
float z = float.Parse(line.Substring(46, 8));
return new Atom(atomNumber, atomType, new Vector3(x, y, z));
}
catch
{
Debug.LogWarning("Failed to parse line: " + line);
return null;
}
}
private float GetAtomSize(string atomType, float scaleFactor = 1.5f)
{
float baseSize = atomType switch
{
"H" => 1.2f, // Hydrogen
"C" => 1.7f, // Carbon
"N" => 1.55f, // Nitrogen
"O" => 1.52f, // Oxygen
"P" => 1.8f, // Phosphorus
"S" => 1.8f, // Sulfur
"Cl" => 1.75f, // Chlorine
_ => 1.5f // Default size for unknown atoms
};
// Apply scaling factor
return baseSize * scaleFactor;
}
}
}
using Core.Dots.Components;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Rendering;
using Unity.Transforms;
using UnityEngine;
namespace Core.Dots.Systems
{
[BurstCompile]
public partial struct PdbEntityBuilderSystem : ISystem
{
private NativeList<AtomAuthoring.AtomComponent> _atoms;
private bool _dataLoaded;
private EntityManager _entityManager;
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<PdbCreatorAuthoring.PdbCreatorComponent>();
_dataLoaded = false;
_atoms = new NativeList<AtomAuthoring.AtomComponent>(Allocator.Persistent);
_entityManager = state.EntityManager;
}
public void OnDestroy(ref SystemState state)
{
if(_atoms.IsCreated)
_atoms.Dispose();
}
public void OnUpdate(ref SystemState state)
{
if(!_dataLoaded || !_atoms.IsCreated) return;
var pdbCreator = SystemAPI.GetSingleton<PdbCreatorAuthoring.PdbCreatorComponent>();
var ecb = new EntityCommandBuffer(Allocator.Temp);
foreach (var atom in _atoms)
{
var entity = ecb.Instantiate(pdbCreator.AtomPrefab);
// ecb.AddComponent(entity, atom);
ecb.SetComponent(entity,new LocalTransform()
{
Position = atom.Position,
Rotation = Quaternion.identity,
Scale = atom.Size
});
ecb.AddComponent(entity, new URPMaterialPropertyBaseColor
{
Value = GetColorFromAtomType(atom.AtomType)
});
}
ecb.Playback(state.EntityManager);
ecb.Dispose();
_atoms.Dispose();
_dataLoaded = false;
// state.Enabled = false;
}
public void LoadAtoms(NativeArray<AtomAuthoring.AtomComponent> atoms)
{
if (_atoms.IsCreated)
_atoms.Dispose();
_atoms = new NativeList<AtomAuthoring.AtomComponent>(atoms.Length, Allocator.Persistent);
_atoms.AddRange(atoms);
Debug.Log($"{atoms.Length} {_atoms.Length}");
NativeArray<AtomAuthoring.AtomComponent>.Copy(atoms, _atoms);
_dataLoaded = true;
}
private static float4 GetColorFromAtomType(FixedString32Bytes atomType)
{
return atomType.Value switch
{
"C" => new float4(0.3f, 0.3f, 0.3f, 1f), // Carbon: Grey
"H" => new float4(1.0f, 1.0f, 1.0f, 1f), // Hydrogen: White
"O" => new float4(1.0f, 0.0f, 0.0f, 1f), // Oxygen: Red
"N" => new float4(0.0f, 0.0f, 1.0f, 1f), // Nitrogen: Blue
"S" => new float4(1.0f, 1.0f, 0.0f, 1f), // Sulfur: Yellow
"P" => new float4(1.0f, 0.5f, 0.0f, 1f), // Phosphorus: Orange
"Cl" => new float4(0.0f, 1.0f, 0.0f, 1f), // Chlorine: Green
"F" => new float4(0.5f, 1.0f, 0.5f, 1f), // Fluorine: Pale Green
"Br" => new float4(0.65f, 0.16f, 0.16f, 1f), // Bromine: Brown
"I" => new float4(0.58f, 0.0f, 0.83f, 1f), // Iodine: Violet
_ => new float4(0.5f, 0.5f, 0.5f, 1f) // Default: Grey
};
}
}
}