Image compressor for VRChat
It's a JPEG-Like format (Uses DCT, yuv space, and quantization), but differs in
the metadata, and entropy compression algorithm. The entropy compressor
was designed to run is as few instructions as possible, it is a fixed
code table with 4 operations. It still manages to get some OKAY ratios.
Image quality is fixed and is NOT GREAT, but OKAY.
Work is interleaved over frames and has a 'budget time' set in the inspector. This is by default 2 seconds. You can crank it down to as low as possible but you will notice the frames dissapearing.
NOTE: You should use this sparingly. (1-5 data stores per world)
NOTE: If you use the delta compression option, which saves some memory and speed, you only get one upload channel per person. It's not a hard requirement, but the compression ratios will become unpredictable.

Download: okaypeg0.1.unitypackage
Contains demo scene of how to use it
Example usage code:
0001 0002 0003 0004 0005 0006 0007 0008 0009 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 0080 0081 0082 0083 0084 0085 | using UdonSharp; using UnityEngine; using VRC.SDKBase; using VRC.Udon; /* * Demo program of okaypeg to sync the image of a camera. */ public class okaypeg_demo: UdonSharpBehaviour { /* Reference to the compressor */ public okaypeg_main _compressor; /* * Two store items (main holds the large chunks of data, delta holds a smaller * set of blocks when possible to try and save networking usage */ public okaypeg_store _store_main, _store_delta; /* * RT src is the texture we're reading from, so the camera's view in this demo. * RT dst is the destination texture we read the compressed image into. */ public RenderTexture _rt_src, _rt_dst; /* * Signal that there is new data in _rt_src */ bool _picture_taken = false; /* * Take picture button */ void OnPickupUseDown() { this.GetComponent<Camera>().Render(); /* * If we don't own it we use the claim function to take the data and reset * it */ if( Networking.GetOwner( _store_main.gameObject ) != Networking.LocalPlayer ) _compressor._opg_claim_and_clear( _store_main, _store_delta ); /* Signal to the update loop that we should compress a new image */ _picture_taken = true; } float _timer = 0.0f; const float k_timer_interval = 0.1f; void Update() { /* Use a simple timer to reduce pressure on Udon */ _timer -= Time.deltaTime; if( _timer < 0.0f ) { _timer = k_timer_interval; /* * Try compress and upload the texture if we're the owner */ if( Networking.GetOwner( _store_main.gameObject ) == Networking.LocalPlayer ) { if( _picture_taken ) { if( _compressor._opg_upload( _rt_src, 512, 256, _store_main, _store_delta ) ) { _picture_taken = false; } } } /* * Read back the compressed texture. We don't actually have to do this * at all if we own the object. But in the demo we use it to preview the * result. */ _compressor._opg_decode( _store_main, _store_delta, Color.white, _rt_dst); } } } |