/// <summary> /// Cleanup any lingering blender data from the scene (viewport cameras, meshes, etc) /// </summary> private void OnDisconnectFromBlender() { Debug.Log("Disconnected from Blender"); IsConnected = false; // Clear any messages still queued to send messages.ClearQueue(); // Reset Blender state information blenderState = new InteropBlenderState(); // Clear the scene of any synced data from Blender Clear(); }
/// <summary> /// Connect to a shared memory space hosted by Unity and sync scene data /// </summary> /// <param name="connectionName">Common name for the shared memory space between Blender and Unity</param> public bool Connect(string connectionName, string versionInfo) { blenderState = new InteropBlenderState { version = versionInfo }; try { InteropLogger.Debug($"Connecting to `{connectionName + VIEWPORT_IMAGE_BUFFER}`"); // Buffer for render data coming from Unity (consume-only) pixelsConsumer = new CircularBuffer(connectionName + VIEWPORT_IMAGE_BUFFER); InteropLogger.Debug($"Connecting to `{connectionName + UNITY_MESSAGES_BUFFER}` and `{connectionName + BLENDER_MESSAGES_BUFFER}`"); // Two-way channel between Blender and Unity messages = new InteropMessenger(); messages.ConnectAsSlave( connectionName + UNITY_MESSAGES_BUFFER, connectionName + BLENDER_MESSAGES_BUFFER ); } catch (System.IO.FileNotFoundException) { // Shared memory space is not valid - Unity may not have started it. // This is an error that should be gracefully handled by the UI. IsConnectedToSharedMemory = false; return(false); } IsConnectedToSharedMemory = true; // Send an initial connect message to let Unity know we're in messages.Queue(RpcRequest.Connect, versionInfo, ref blenderState); return(true); }
/// <summary> /// Consume a single message off the interop message queue /// </summary> /// <returns>Number of bytes read</returns> private int ConsumeMessage() { var disconnected = false; var bytesRead = messages.Read((target, header, ptr) => { // While not connected - only accept connection requests if (!IsConnected) { if (header.type != RpcRequest.Connect) { Debug.LogWarning($"Unhandled request type {header.type} for {target} - expected RpcRequest.Connect"); } blenderState = FastStructure.PtrToStructure <InteropBlenderState>(ptr); OnConnectToBlender(); return(0); } switch (header.type) { case RpcRequest.UpdateBlenderState: blenderState = FastStructure.PtrToStructure <InteropBlenderState>(ptr); break; case RpcRequest.Disconnect: // Don't call OnDisconnectFromBlender() from within // the read handler - we want to release the read node // safely first before disposing the connection. disconnected = true; break; // Viewport messages case RpcRequest.AddViewport: AddViewport( target, FastStructure.PtrToStructure <InteropViewport>(ptr) ); break; case RpcRequest.RemoveViewport: RemoveViewport(target); break; case RpcRequest.UpdateViewport: GetViewport(target).UpdateFromInterop( FastStructure.PtrToStructure <InteropViewport>(ptr) ); break; case RpcRequest.UpdateVisibleObjects: var visibleObjectIds = new int[header.count]; FastStructure.ReadArray(visibleObjectIds, ptr, 0, header.count); GetViewport(target).SetVisibleObjects( visibleObjectIds ); break; // Object messages case RpcRequest.AddObjectToScene: AddObject( target, FastStructure.PtrToStructure <InteropSceneObject>(ptr) ); break; case RpcRequest.RemoveObjectFromScene: RemoveObject(target); break; case RpcRequest.UpdateSceneObject: UpdateObject( target, FastStructure.PtrToStructure <InteropSceneObject>(ptr) ); break; // Mesh messages case RpcRequest.UpdateTriangles: GetOrCreateMesh(target) .triangles .Resize(header.length) .CopyFrom(ptr, header.index, header.count); break; case RpcRequest.UpdateVertices: GetOrCreateMesh(target) .vertices .Resize(header.length) .CopyFrom(ptr, header.index, header.count); break; case RpcRequest.UpdateNormals: GetOrCreateMesh(target) .normals .Resize(header.length) .CopyFrom(ptr, header.index, header.count); break; case RpcRequest.UpdateUV: GetOrCreateMesh(target) .uv .Resize(header.length) .CopyFrom(ptr, header.index, header.count); break; case RpcRequest.UpdateUV2: GetOrCreateMesh(target) .uv2 .Resize(header.length) .CopyFrom(ptr, header.index, header.count); break; case RpcRequest.UpdateUV3: GetOrCreateMesh(target) .uv3 .Resize(header.length) .CopyFrom(ptr, header.index, header.count); break; case RpcRequest.UpdateUV4: GetOrCreateMesh(target) .uv4 .Resize(header.length) .CopyFrom(ptr, header.index, header.count); break; case RpcRequest.UpdateVertexColors: GetOrCreateMesh(target) .colors .Resize(header.length) .CopyFrom(ptr, header.index, header.count); break; // TODO: ... and so on for weights/bones/etc case RpcRequest.UpdateMesh: GetOrCreateMesh(target).UpdateFromInterop( FastStructure.PtrToStructure <InteropMesh>(ptr) ); break; // Texture messages case RpcRequest.UpdateTexture: GetTexture(target).UpdateFromInterop( FastStructure.PtrToStructure <InteropTexture>(ptr) ); break; case RpcRequest.UpdateTextureData: GetTexture(target).CopyFrom( ptr, header.index, header.count, header.length ); break; default: Debug.LogWarning($"Unhandled request type {header.type} for {target}"); break; } // TODO: Necessary to count bytes? We won't read anything off this // buffer at this point so it's safe to drop the whole thing. // bytesRead will count the header size (indicating a message *was* read) return(0); }); // Handle any disconnects that may have occured during the read if (disconnected) { OnDisconnectFromBlender(); } return(bytesRead); }
/// <summary> /// Handle any messages coming from Blender /// </summary> private void ConsumeMessages() { Profiler.BeginSample("Consume Message"); var disconnected = false; // TODO: Some messages should be skipped if !IsConnected. // Otherwise we may get a bad state. E.g. we see a disconnect // from Blender and THEN some other viewport/object data messages. messages.Read((target, header, ptr) => { ObjectController obj; switch (header.type) { case RpcRequest.Connect: blenderState = FastStructure.PtrToStructure <InteropBlenderState>(ptr); OnConnectToBlender(); break; case RpcRequest.UpdateBlenderState: blenderState = FastStructure.PtrToStructure <InteropBlenderState>(ptr); break; case RpcRequest.Disconnect: // Don't call OnDisconnectFromBlender() from within // the read handler - we want to release the read node // safely first before disposing the connection. disconnected = true; break; case RpcRequest.AddViewport: AddViewport( target, FastStructure.PtrToStructure <InteropViewport>(ptr) ); break; case RpcRequest.RemoveViewport: RemoveViewport(target); break; case RpcRequest.UpdateViewport: GetViewport(target).UpdateFromInterop( FastStructure.PtrToStructure <InteropViewport>(ptr) ); break; case RpcRequest.UpdateVisibleObjects: var visibleObjectIds = new int[header.count]; FastStructure.ReadArray(visibleObjectIds, ptr, 0, header.count); GetViewport(target).SetVisibleObjects( visibleObjectIds ); break; case RpcRequest.AddObjectToScene: AddObject( target, FastStructure.PtrToStructure <InteropSceneObject>(ptr) ); break; case RpcRequest.RemoveObjectFromScene: RemoveObject(target); break; case RpcRequest.UpdateSceneObject: GetObject(target).UpdateFromInterop( FastStructure.PtrToStructure <InteropSceneObject>(ptr) ); break; case RpcRequest.UpdateTriangles: obj = GetObject(target); FastStructure.ReadArray(obj.GetOrCreateTriangleBuffer(), ptr, header.index, header.count); obj.OnUpdateTriangleRange(header.index, header.count); break; case RpcRequest.UpdateVertices: obj = GetObject(target); FastStructure.ReadArray(obj.GetOrCreateVertexBuffer(), ptr, header.index, header.count); obj.OnUpdateVertexRange(header.index, header.count); break; case RpcRequest.UpdateNormals: obj = GetObject(target); FastStructure.ReadArray(obj.GetOrCreateNormalBuffer(), ptr, header.index, header.count); obj.OnUpdateNormalRange(header.index, header.count); break; // TODO: ... and so on for UV/weights default: Debug.LogWarning($"Unhandled request type {header.type} for {target}"); break; } // TODO: Necessary to count bytes? We won't read anything off this // buffer at this point so it's safe to drop the whole thing. return(0); }); // Handle any disconnects that may have occured during the read if (disconnected) { OnDisconnectFromBlender(); } Profiler.EndSample(); }