diff --git a/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/RayMarch.mat b/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/RayMarch.mat new file mode 100644 index 0000000..2506c9e --- /dev/null +++ b/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/RayMarch.mat @@ -0,0 +1,80 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: RayMarch + m_Shader: {fileID: 4800000, guid: 61a027af60b3b3a40bd0bdf19edb1d0c, type: 3} + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/RayMarch.mat.meta b/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/RayMarch.mat.meta new file mode 100644 index 0000000..19c4f16 --- /dev/null +++ b/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/RayMarch.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 304ea58732802c24fb827b0f19dddfc8 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/RayMarch.shader b/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/RayMarch.shader new file mode 100644 index 0000000..542f87d --- /dev/null +++ b/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/RayMarch.shader @@ -0,0 +1,323 @@ +/* +* Ray Marching shader +* https://www.youtube.com/watch?v=S8AWd66hoCo +*/ +Shader "Unlit/RayMarch" +{ + Properties + { + _MainTex ("Texture", 2D) = "white" {} + } + SubShader + { + Tags { "RenderType"="Opaque"} + LOD 100 + + + + Pass + { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + + #include "UnityCG.cginc" + + struct CellInfo{ + int current_state; + int next_state; + int last_frame_modified; + }; + +#define MAX_STEPS 10000 +#define MAX_DIST 10000 +#define SURF_DIST 1e-3 +#define ELEMENT_EMPTY 0 +#define ELEMENT_STONE 1 +#define ELEMENT_SAND 2 +#define ELEMENT_WATER 3 +#define ELEMENT_LAVA 4 +#define ELEMENT_STEAM 5 + + + struct appdata + { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + }; + + struct v2f // vertex to fragment + { + float2 uv : TEXCOORD0; + float4 vertex : SV_POSITION; + float3 camera_origin : TEXCOORD1; + float3 hit_position : TEXCOORD2; + float3 light_position : TEXCOORD3; + }; + + sampler2D _MainTex; + float4 _MainTex_ST; + StructuredBuffer _voxel_data; + int _voxel_data_length; + int AUTOMATA_SIZE_X; + int AUTOMATA_SIZE_Y; + int AUTOMATA_SIZE_Z; + float light_position_X; + float light_position_Y; + float light_position_Z; + int3 step; + float tMaxX; + float tMaxY; + float tMaxZ; + float tDeltaX; + float tDeltaY; + float tDeltaZ; + + v2f vert (appdata v) + { + v2f o; + o.vertex = UnityObjectToClipPos(v.vertex); + o.uv = TRANSFORM_TEX(v.uv, _MainTex); + // object space + //o.ray_origin = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos,1)); + //o.hit_position = v.vertex; + //world space + o.camera_origin = _WorldSpaceCameraPos; + o.light_position = float3(light_position_X,light_position_Y, light_position_Z); + o.hit_position = mul(unity_ObjectToWorld, v.vertex); + return o; + } + + int FlatIndexFromCoordinates(int x, int y, int z) + { + return x + AUTOMATA_SIZE_X * y + AUTOMATA_SIZE_X * AUTOMATA_SIZE_Y * z; + } + + int FlatIndexFromVector3(int3 index) + { + return FlatIndexFromCoordinates(index.x, index.y, index.z); + } + + float4 GetVoxelColor(int element){ + + if(element == ELEMENT_STONE){ + return float4(0.3568628,0.3568628,0.3568628,1.0); + }else if(element == ELEMENT_SAND){ + return float4(0.9352227,1,0.4669811,1.0); + }else if(element == ELEMENT_WATER){ + return float4(0, 0.170235,0.9716981,0.5686275); + }else if(element == ELEMENT_LAVA){ + return float4(1, 0.1527778,0,1.0); + }else if(element == ELEMENT_STEAM){ + return float4(0, 0.919493,1,0.1254902); + }else{ + return float4(0,0,0,0); + } + } + + bool IsOutOfAutomataBounds(int3 index){ + return (index.x < 0 || index.x >= AUTOMATA_SIZE_X || index.y < 0 || index.y >= AUTOMATA_SIZE_Y || index.z < 0 || index.z >= AUTOMATA_SIZE_Z); + } + + bool IsGas(int element){ + if(element == ELEMENT_STEAM) return true; + return false; + } + + + // float3 GetNormal(float3 p){ + // float2 e = float2(1e-2, 0); + // float3 n = GetDist(p) - float3( + // GetDist(p - e.xyy), + // GetDist(p - e.yxy), + // GetDist(p - e.yyx) + // ); + // return normalize(n); + // } + + void VoxelMarchInitialization(float3 ray_origin, float3 ray_direction){ + + //the direction of a voxel step, according to the raycast direction + step.x = 0; + if(ray_direction.x > 0){ + step.x = 1; + }else if(ray_direction.x < 0){ + step.x = -1; + }else if(ray_direction.x == 0){ + step.x = 0; + } + + step.y = 0; + if(ray_direction.y > 0){ + step.y = 1; + }else if(ray_direction.y < 0){ + step.y = -1; + }else if(ray_direction.z == 0){ + step.y = 0; + } + + step.z = 0; + if(ray_direction.z > 0){ + step.z = 1; + }else if(ray_direction.z < 0){ + step.z = -1; + }else if(ray_direction.z == 0){ + step.z = 0; + } + + + // the origin voxel + int voxelX = (int)ray_origin.x; + int voxelY = (int)ray_origin.y; + int voxelZ = (int)ray_origin.z; + //difference between point and voxel border + float diffX = -1 * step.x * ray_origin.x + step.x * voxelX + ((1+step.x)/2.0); + float diffY = -1 * step.y * ray_origin.y + step.y * voxelY + ((1+step.y)/2.0); + float diffZ = -1 * step.z * ray_origin.z + step.z * voxelZ + ((1+step.z)/2.0); + + // magnitude of distance, how far along the ray must be traversed to traverse 1 voxel in a given dimension + tDeltaX = abs(1 / ray_direction.x); + tDeltaY = abs(1 / ray_direction.y); + tDeltaZ = abs(1 / ray_direction.z); + + // These store the overall distance travelled to the current hit voxel in each dimension + tMaxX = tDeltaX*diffX; + tMaxY = tDeltaY*diffY; + tMaxZ = tDeltaZ*diffZ; + + } + + int3 NextVoxel(int X, int Y, int Z){ + if(tMaxX < tMaxY) { + if(tMaxX < tMaxZ) { + X = X + step.x; + tMaxX= tMaxX + tDeltaX; // traverse to next X voxel + } else { + Z = Z + step.z; + tMaxZ= tMaxZ + tDeltaZ; // traverse to next Z voxel + } + } else { + if(tMaxY < tMaxZ) { + Y = Y + step.y; + tMaxY= tMaxY + tDeltaY; // traverse to next Y voxel + } else { + Z = Z + step.z; + tMaxZ= tMaxZ + tDeltaZ; // traverse to next Z voxel + } + } + + return int3(X, Y, Z); + } + + int3 NextVoxel(int3 index){ + return NextVoxel(index[0], index[1], index[2]); + } + + // returns -1 if no non-empty voxel hit, returns the index of the hit voxel otherwise + int3 VoxelMarch(int X, int Y,int Z){ + int start_flat = FlatIndexFromCoordinates(X,Y,Z); + int z = 0; + int flat_voxel_index; + do{ + int3 next_index = NextVoxel(X,Y,Z); + X = next_index[0]; + Y = next_index[1]; + Z = next_index[2]; + if(X < 0 || X >= AUTOMATA_SIZE_X || Y < 0 || Y >= AUTOMATA_SIZE_Y || Z < 0 || Z >= AUTOMATA_SIZE_Z){ + flat_voxel_index = -1; + break; + } + flat_voxel_index = FlatIndexFromCoordinates(X,Y,Z); + + z++; + }while(_voxel_data[flat_voxel_index].next_state == ELEMENT_EMPTY && z < MAX_STEPS); + if(z >= MAX_STEPS || _voxel_data[flat_voxel_index].next_state == ELEMENT_EMPTY){ + flat_voxel_index = -1; + } + + if(flat_voxel_index == -1) return int3(-1,-1,-1); + return int3(X, Y, Z); + } + + + + float4 BlendColors(float4 col1, float4 col2){ + return lerp(col1, col2, 0.5); + } + + + // main function + fixed4 frag (v2f i) : SV_Target + { + + float3 ray_origin = i.camera_origin; + float3 ray_direction = normalize(i.hit_position - ray_origin); // from camera center to outward ray + + VoxelMarchInitialization(ray_origin, ray_direction); + int3 voxel_index = VoxelMarch((int)ray_origin.x, (int)ray_origin.y, (int)ray_origin.z); + + + if(IsOutOfAutomataBounds(voxel_index)){ + discard; + } + + // surface was hit + + float3 light_position = float3(light_position_X, light_position_Y, light_position_Z); + int flat_voxel_index = FlatIndexFromVector3(voxel_index); + + // draw pixel to texture + int element = _voxel_data[flat_voxel_index].next_state; + float4 rgba = GetVoxelColor(element); + + // make gas transparent + if(IsGas(element)){ + // keep marching to next voxel + + ray_origin = NextVoxel(voxel_index); + + + int3 behind_voxel_index = VoxelMarch(ray_origin.x, ray_origin.y, ray_origin.z); + int flat_behind_voxel_index = FlatIndexFromVector3(behind_voxel_index); + if(!IsOutOfAutomataBounds(voxel_index)){ + int behind_element = _voxel_data[flat_behind_voxel_index].next_state; + rgba = BlendColors(rgba,GetVoxelColor(behind_element)); + } + }else{ + + //march to shadow + ray_origin = voxel_index; + ray_direction = normalize(light_position - ray_origin); // from origin to light position + + VoxelMarchInitialization(ray_origin, ray_direction); + ray_origin = NextVoxel(voxel_index); + ray_origin = NextVoxel(ray_origin); + int3 light_voxel_index = VoxelMarch((int)ray_origin.x, (int)ray_origin.y, (int)ray_origin.z); + + if(light_voxel_index[0] != -1){ // voxel was hit + int light_voxel_flat_index = FlatIndexFromVector3(light_voxel_index); + bool same_voxel = (light_voxel_flat_index == flat_voxel_index); + if(!same_voxel){ + rgba[0] *= 0.5; // shade color + rgba[1] *= 0.5; // shade color + rgba[2] *= 0.5; // shade color + + } + + } + //rgba = float4(abs(ray_direction.x),abs(ray_direction.y),abs(ray_direction.z),1); + //rgba = float4(abs(light_position.x - ,ray_direction.y,ray_direction.z,1); + // rgba = float4(ray_origin[0] / (float)AUTOMATA_SIZE_X, ray_origin[1] / (float)AUTOMATA_SIZE_Y, ray_origin[2] / (float)AUTOMATA_SIZE_Z,1); + + } + + fixed4 col = 0; + col.rgba = rgba; + + + return col; + } + ENDCG + } + } +} diff --git a/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/RayMarch.shader.meta b/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/RayMarch.shader.meta new file mode 100644 index 0000000..9a4ee3e --- /dev/null +++ b/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/RayMarch.shader.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 61a027af60b3b3a40bd0bdf19edb1d0c +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/WorldAutomatonGPU.cs b/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/WorldAutomatonGPU.cs new file mode 100644 index 0000000..195ab9c --- /dev/null +++ b/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/WorldAutomatonGPU.cs @@ -0,0 +1,343 @@ +using System.Runtime.InteropServices; +using Unity.Mathematics; +using UnityEngine; +using static WorldAutomaton.Elemental; +using WorldCellInfo = CellInfo; + +/// +/// Computes and renders the voxel automaton using GPU +/// +public class WorldAutomatonGPU : WorldAutomaton +{ + // compute automaton in compute shader + public ComputeShader compute_shader; + + + // Material for fragment shader, to do raymarching rendering + Material raymarch_shader_material; + + // buffers to share info between C# and GPU + public ComputeBuffer cell_grid_buffer; // stores data for each cell. x is current state, y is the modified flag z is the computed next state + public ComputeBuffer debug_buffer; // stores data for each cell. x is current state, y is the modified flag z is the computed next state + public ComputeBuffer block_grid_buffer; // stores index for corner of each 2x2x2 Margolus block (unshifted blocks) + + public int main_kernel; + + WorldCellInfo[] cell_grid_data; + int last_data_get_frame = -1; + + + //const int MAX_NUM_GPU_THREADS_IN_GROUP = 65535; + + + public override void Setup(WorldCellInfo[] cell_grid, int3[] block_grid) + { + + // initialize + this.cell_grid_buffer = new(this.automaton_size, Marshal.SizeOf(typeof(WorldCellInfo))); + this.block_grid_buffer = new(block_grid.Length, Marshal.SizeOf(typeof(int3))); + this.debug_buffer = new(1, Marshal.SizeOf(typeof(int3))); + + // set compute buffer on GPU shader + raymarch_shader_material = GetComponent().material; + + raymarch_shader_material.SetInteger("_voxel_data_length", this.automaton_size); + raymarch_shader_material.SetInteger("AUTOMATA_SIZE_X", GlobalConfig.WORLD_AUTOMATON_DIMENSIONS.x); + raymarch_shader_material.SetInteger("AUTOMATA_SIZE_Y", GlobalConfig.WORLD_AUTOMATON_DIMENSIONS.y); + raymarch_shader_material.SetInteger("AUTOMATA_SIZE_Z", GlobalConfig.WORLD_AUTOMATON_DIMENSIONS.z); + + raymarch_shader_material.SetFloat("light_position_X", this.light.transform.position.x); + raymarch_shader_material.SetFloat("light_position_Y", this.light.transform.position.y); + raymarch_shader_material.SetFloat("light_position_Z", this.light.transform.position.z); + + compute_shader.SetInt("AUTOMATA_SIZE_X", GlobalConfig.WORLD_AUTOMATON_DIMENSIONS.x); + compute_shader.SetInt("AUTOMATA_SIZE_Y", GlobalConfig.WORLD_AUTOMATON_DIMENSIONS.y); + compute_shader.SetInt("AUTOMATA_SIZE_Z", GlobalConfig.WORLD_AUTOMATON_DIMENSIONS.z); + + // + this.main_kernel = compute_shader.FindKernel("CSMain"); + compute_shader.SetBuffer(this.main_kernel, "cell_grid", this.cell_grid_buffer); + compute_shader.SetBuffer(this.main_kernel, "block_grid", this.block_grid_buffer); + compute_shader.SetBuffer(this.main_kernel, "debug_buffer", this.debug_buffer); + raymarch_shader_material.SetBuffer("_voxel_data", this.cell_grid_buffer); + uint threads_x, threads_y, threads_z; + compute_shader.GetKernelThreadGroupSizes(this.main_kernel, out threads_x, out threads_y, out threads_z); + + // initial setting of buffers + this.cell_grid_buffer.SetData(cell_grid); + this.block_grid_buffer.SetData(block_grid); + + this.cell_grid_data = new WorldCellInfo[this.cell_grid_buffer.count]; + } + + + + + public void UpdateLightPosition() + { + raymarch_shader_material.SetFloat("light_position_X", this.light.transform.position.x); + raymarch_shader_material.SetFloat("light_position_Y", this.light.transform.position.y); + raymarch_shader_material.SetFloat("light_position_Z", this.light.transform.position.z); + } + + + + public override void RunMouseButtonBehaviors(Element state, int brush_size) + { + float distance = 2.0f; + Vector3 point = Camera.main.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, distance)); + int3 index = new int3(Mathf.RoundToInt(point.x), Mathf.RoundToInt(point.y), Mathf.RoundToInt(point.z)); + + WorldCellInfo[] data = null; + + //check all downward neighbors + for (int nx = index.x - brush_size; nx <= index.x + brush_size; nx++) + { + for (int ny = index.y - brush_size; ny <= index.y + brush_size; ny++) + { + for (int nz = index.z - brush_size; nz <= index.z + brush_size; nz++) + { + if (VoxelUtils.IsOutOfBounds(nx, ny, nz, GlobalConfig.world_automaton.automaton_dimensions)) continue; + if (data == null) + { + data = new WorldCellInfo[this.cell_grid_buffer.count]; + this.cell_grid_buffer.GetData(data); + } + int i = VoxelUtils.Index_FlatFromint3(nx, ny, nz, this.automaton_dimensions); + data[i].current_state = state; + data[i].next_state = state; + } + + } + } + + + this.cell_grid_buffer.SetData(data); + } + + + + /// + /// Calculate the next state of the automata + /// + public override void CalculateAndRenderNextGridState() + { + compute_shader.SetInt("frame", this.frame); + compute_shader.SetInt("frame_mod2", this.frame % 2); + + + int remaining_blocks = this.block_grid_buffer.count; + + int i = 0; + int max_blocks_processed_per_dispatch = GlobalConfig.MAX_NUM_OF_THREAD_GROUPS * GlobalConfig.NUM_OF_GPU_THREADS; + while (remaining_blocks > 0) + { + compute_shader.SetInt("index_offset", i * max_blocks_processed_per_dispatch); + if (remaining_blocks < max_blocks_processed_per_dispatch) + { + compute_shader.Dispatch(this.main_kernel, Mathf.CeilToInt(remaining_blocks / GlobalConfig.NUM_OF_GPU_THREADS), 1, 1); + remaining_blocks = 0; + break; + } + else + { + compute_shader.Dispatch(this.main_kernel, GlobalConfig.MAX_NUM_OF_THREAD_GROUPS, 1, 1); + remaining_blocks -= max_blocks_processed_per_dispatch; + } + i++; + } + } + + + + + + private void OnApplicationQuit() + { + + try + { + this.cell_grid_buffer.Dispose(); + } + catch + { + + } + + + try + { + this.block_grid_buffer.Dispose(); + } + catch + { + + } + try + { + this.debug_buffer.Dispose(); + } + catch + { + + } + + } + + /** + * Helpers + * + */ + + public void RefreshCellInfoData() + { + if (last_data_get_frame != this.frame) + { + cell_grid_buffer.GetData(this.cell_grid_data); + last_data_get_frame = this.frame; + } + } + + + + //===================== + + /// + /// Get the current state of a cell + /// + /// + /// + /// + /// + public override Element GetCellNextState(int i) + { + RefreshCellInfoData(); + return (Element)this.cell_grid_data[i].next_state; + } + + /// + /// Get the current state of a cell + /// + /// + /// + /// + /// + public override Element GetCellCurrentState(int i) + { + RefreshCellInfoData(); + return (Element)this.cell_grid_data[i].current_state; + } + + + /// + /// Returns a vector 3 where: + /// x is the current state + /// y is whether this cell was modified this frame + /// z is the previous state + /// + /// + /// + /// + /// + public override WorldCellInfo GetCellInfo(int i) + { + RefreshCellInfoData(); + return this.cell_grid_data[i]; + } + + /// + /// Set the current state of a cell. Also flags the cell as modified during this frame. + /// + /// + /// + public override void SetCellNextState(int i, Element state) + { + WorldCellInfo[] data = new WorldCellInfo[this.cell_grid_buffer.count]; + this.cell_grid_buffer.GetData(data); + WorldCellInfo info = data[i]; + info.current_state = state; + info.next_state = state; + info.last_frame_modified = this.frame; + data[i] = info; + this.cell_grid_buffer.SetData(data); + } + + + //===================== + + + /// + /// Get the current state of a cell + /// + /// + /// + /// + /// + public override Element GetCellNextState(int x, int y, int z) + { + int i = VoxelUtils.Index_FlatFromint3(x, y, z, this.automaton_dimensions); + return GetCellNextState(i); + } + + /// + /// Get the current state of a cell + /// + /// + /// + /// + /// + public override Element GetCellCurrentState(int x, int y, int z) + { + int i = VoxelUtils.Index_FlatFromint3(x, y, z, this.automaton_dimensions); + return GetCellCurrentState(i); + } + + + /// + /// Returns a vector 3 where: + /// x is the current state + /// y is whether this cell was modified this frame + /// z is the previous state + /// + /// + /// + /// + /// + public override WorldCellInfo GetCellInfo(int x, int y, int z) + { + int i = VoxelUtils.Index_FlatFromint3(x, y, z, this.automaton_dimensions); + return GetCellInfo(i); + } + + /// + /// Set the current state of a cell. Also flags the cell as modified during this frame. + /// + /// + /// + /// + /// + public override void SetCellNextState(int x, int y, int z, Element state) + { + int i = VoxelUtils.Index_FlatFromint3(x, y, z, this.automaton_dimensions); + SetCellNextState(i, state); + } + + //===================== + + /// + /// Returns a vector 3 where: + /// x is the current state + /// y is whether this cell was modified this frame + /// z is the previous state + /// + /// + /// + /// + /// + public override WorldCellInfo GetCellInfo(int3 index) + { + return GetCellInfo(index.x, index.y, index.z); + } + + +} diff --git a/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/WorldAutomatonGPU.cs.meta b/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/WorldAutomatonGPU.cs.meta new file mode 100644 index 0000000..858e9a7 --- /dev/null +++ b/Assets/Scripts/Automata/Rendering/GPU (RayMarching)/WorldAutomatonGPU.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3afe84a3a03d01c4cbb7131d759aeee8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: