Skip to content

Commit

Permalink
Merge pull request #52 from L05/master
Browse files Browse the repository at this point in the history
Updated to 4.23, fixed bugs
  • Loading branch information
AleDel committed Nov 6, 2019
2 parents 8740fc1 + fd807cc commit 242778b
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 130 deletions.
93 changes: 80 additions & 13 deletions README.md
@@ -1,33 +1,100 @@
*Modification of original plugin by [AleDel](https://github.com/AleDel/Spout-UE4) with UE versions 4.19+.*

# Spout-UE4
[Spout](http://spout.zeal.co/) Plugin for Unreal Engine
This is a [Spout](http://spout.zeal.co/) Plugin for Unreal Engine. It allows you to send and receive textures using Spout framework.

Sender and Receiver only DirectX 11.

* [Installation and Use](#installation-and-use)
* [Sending Spout](#sending-spout)
* [Install Example](#install-example)
* [Packaged game](#packaged-game)

**This was tested with:**
* 4.19
* 4.20
* 4.21
* 4.22
* 4.23


# Installation and Use

Put code in folder Plugins (for example "yourproject/Plugins/SpoutUE4")
1. Open up Epic Games Launcher (make sure it's up to date).

2. Create a new C++ First person Project.

![Image 1](https://L05.github.io/Spout-UE4/images/SpoutExample_Instructions_01.png)

3. You should see it Generating code...

![Image 2](https://L05.github.io/Spout-UE4/images/SpoutExample_Instructions_02.png)

4. The Unreal project will open in the editor, and a Visual Studio project will also open.

![Image 3](https://L05.github.io/Spout-UE4/images/SpoutExample_Instructions_03.png)

5. Close the Unreal project.

6. In the project directory, create a Plugins folder.

![Image 4](https://L05.github.io/Spout-UE4/images/SpoutExample_Instructions_04.png)

7. Download the Spout-UE4 repository (zip file) and put it in the Plugins directory.

# Info
![Image 5](https://L05.github.io/Spout-UE4/images/SpoutExample_Instructions_05.png)

the "spout sender" has two options:
* "Game Viewport" that send the image of the viewport (no work in standalone game)
* or use a "TextureRenderTarget2D" in this case you should create along with a "SceneCaptureComponent2D"
8. *(Optional)* Download ExampleSpout.zip (from the project GitHub page). Unzip the contents into the Content folder.

use "spout close" blueprint to close spouts
![Image 6](https://L05.github.io/Spout-UE4/images/SpoutExample_Instructions_06.png)

![CaptureSpout2](http://aledel.github.io/Spout-UE4/images/10senders.jpg)
test sending 10 sender to Touchdesigner 1024x768 either one, the performance is good.
9. In the project directory, open the .uproject file. Press Yes when asked if you'd like to rebuild the SpoutPlugin module.

# Install Example
![Image 7](https://L05.github.io/Spout-UE4/images/SpoutExample_Instructions_07.png)

* Create new c++ First Person project
* unzip example in the "Content" folder of your project
It will start to build.

![Image 8](https://L05.github.io/Spout-UE4/images/SpoutExample_Instructions_08.png)

10. Once the project opens, go to Settings > Plugins.

![Image 9](https://L05.github.io/Spout-UE4/images/SpoutExample_Instructions_09.png)

11. Make sure the Spout Plugin is enabled. If necessary, restart.

![Image 10](https://L05.github.io/Spout-UE4/images/SpoutExample_Instructions_10.png)

12. *(Optional)* Open the ExampleSpout > Spout project and press Play. Configure the Sender and Receiver names to work with other software.

![Image 11](https://L05.github.io/Spout-UE4/images/SpoutExample_Instructions_11.png)

For video instructions, please refer to [Unreal Engine 4 and Lightact Video Tutorials](https://www.youtube.com/playlist?list=PLcNPGta1d2XDcSsz8zcW0f2lPSawnW3mR), which provides a good step-by-Image walkthrough of how to set up your project for use with the plugin.

## Sending Spout

This is done with the **Spout sender** node which has can send texture either from the Game viewport or from a Render Targert 2D:
* **Game Viewport** sends the image of the viewport, but please note that it doesn't work in standalone or packaged game.
* **TextureRenderTarget2D** in which case you should create a _SceneCaptureComponent2D_ and a *Render target 2D* which you should reference in the node.

use **Close Sender** node to close Spouts. The best way is to connect it to **Event EndPlay** node.

## Install Example

* Create new C++ *First Person* project
* unzip ExampleSpout.zip in the "Content" folder of your project
* unzip code plugin in folder "Plugins" as mentioned above, if there is no "Plugins" folder, create it
* restart project
* load Spout scene
* if you encounter compile errors you have to delete and re-insert identical nodes

[ExampleSpout.zip](http://aledel.github.io/Spout-UE4/exampleSpoutUE4/ExampleSpout.zip)
[ExampleSpout.zip](http://L05.github.io/Spout-UE4/exampleSpoutUE4/ExampleSpout.zip) *(Updated on 9/22/2019)*

![CaptureSpout2](http://aledel.github.io/Spout-UE4/images/spout2.jpg)
This image corresponds to the "Spout" scene.

## Packaged game
To make this plugin work in a packaged game you have to disable using 'pak' files. You do that by:
1. going to File->Package project->Packaging settings
2. once there uncheck 'Use Pak File' checkbox

This is only necessary if you are using the *Mat* pin on the *Spout Receiver* node.
77 changes: 39 additions & 38 deletions Source/SpoutPlugin/Private/SpoutBPFunctionLibrary.cpp
Expand Up @@ -200,7 +200,7 @@ FSenderStruct* RegisterReceiver(FName spoutName){
newFSenderStruc->texTemp = NULL;

//TextureColor = new
UE_LOG(SpoutLog, Warning, TEXT("No material intance, creating...//////"));
UE_LOG(SpoutLog, Warning, TEXT("No material instance, creating...//////"));
// Prepara Textura, Set the texture update region
newFSenderStruc->UpdateRegions = new FUpdateTextureRegion2D(0, 0, 0, 0, newFSenderStruc->w, newFSenderStruc->h);
ResetTexture(newFSenderStruc->TextureColor, newFSenderStruc->MaterialInstanceColor, newFSenderStruc);
Expand Down Expand Up @@ -239,7 +239,7 @@ FSenderStruct* RegisterReceiver(FName spoutName){
description.ArraySize = 1;


UE_LOG(SpoutLog, Warning, TEXT("--- Creando d3d11 Texture 2D---"));
UE_LOG(SpoutLog, Warning, TEXT("--- Creating d3d11 Texture 2D---"));

HRESULT hr = g_D3D11Device->CreateTexture2D(&description, NULL, &newFSenderStruc->texTemp);

Expand All @@ -249,7 +249,7 @@ FSenderStruct* RegisterReceiver(FName spoutName){
ss << " Error code = 0x" << std::hex << hr << std::endl;
std::cout << ss.str() << std::endl;
std::string TestString = ss.str();
UE_LOG(SpoutLog, Error, TEXT("Failed Create Texture: ----> %s"), *FString(TestString.c_str()));
UE_LOG(SpoutLog, Error, TEXT("Failed to create texture of name: ----> %s"), *FString(TestString.c_str()));

if (hr == E_OUTOFMEMORY) {
UE_LOG(SpoutLog, Error, TEXT("OUT OF MEMORY"));
Expand All @@ -259,7 +259,7 @@ FSenderStruct* RegisterReceiver(FName spoutName){
newFSenderStruc->texTemp->Release();
newFSenderStruc->texTemp = NULL;
}
UE_LOG(SpoutLog, Error, TEXT("error creating temporal textura"));
UE_LOG(SpoutLog, Error, TEXT("Error creating temporal textura"));
return false;

}
Expand All @@ -282,7 +282,7 @@ bool USpoutBPFunctionLibrary::SpoutInfoFrom(FName spoutName, FSenderStruct& Send
FSenderStruct* EncontradoSenderStruct = FSenders.FindByPredicate(MyPredicate);

if (EncontradoSenderStruct == nullptr){
UE_LOG(SpoutLog, Warning, TEXT("No Encontrado sender con nombre : %s"), *spoutName.GetPlainNameString());
UE_LOG(SpoutLog, Warning, TEXT("No Sender was found with the name : %s"), *spoutName.GetPlainNameString());
return false;
}
else
Expand Down Expand Up @@ -311,7 +311,7 @@ bool USpoutBPFunctionLibrary::CreateRegisterSender(FName spoutName, ID3D11Textur
baseTexture->GetDesc(&desc);
ID3D11Texture2D * sendingTexture;

UE_LOG(SpoutLog, Warning, TEXT("ID3D11Texture2D Info : ancho_%i, alto_%i"), desc.Width, desc.Height);
UE_LOG(SpoutLog, Warning, TEXT("ID3D11Texture2D Info : width_%i, height_%i"), desc.Width, desc.Height);
UE_LOG(SpoutLog, Warning, TEXT("ID3D11Texture2D Info : Format is %i"), int(desc.Format));

//use the pixel format from basetexture (the native texture textureRenderTarget2D)
Expand Down Expand Up @@ -434,12 +434,12 @@ bool USpoutBPFunctionLibrary::SpoutSender(FName spoutName, ESpoutSendTextureFrom
ESpoutState state = CheckSenderState(spoutName);

if (state == ESpoutState::noEnoR || state == ESpoutState::noER) {
UE_LOG(SpoutLog, Warning, TEXT("New Sender creating, registering..."));
UE_LOG(SpoutLog, Warning, TEXT("Creating and registering new Sender..."));
CreateRegisterSender(spoutName, baseTexture);
return false;
}
if (state == ESpoutState::EnoR) {
UE_LOG(SpoutLog, Warning, TEXT("Already exist a Sender with the name %s"), *spoutName.GetPlainNameString());
UE_LOG(SpoutLog, Warning, TEXT("A Sender with the name %s already exists"), *spoutName.GetPlainNameString());
return false;
}
if (state == ESpoutState::ER) {
Expand All @@ -455,7 +455,7 @@ bool USpoutBPFunctionLibrary::SpoutSender(FName spoutName, ESpoutSendTextureFrom
}
}
if (SenderStruct->spoutType == ESpoutType::Receiver) {
UE_LOG(SpoutLog, Warning, TEXT("Already exist a Sender with the name %s and you have a receiver in unreal receiving"), *spoutName.GetPlainNameString());
UE_LOG(SpoutLog, Warning, TEXT("A Sender with the name %s already exists, and you have a receiver in Unreal that is receiving"), *spoutName.GetPlainNameString());

return false;
}
Expand All @@ -478,16 +478,10 @@ bool USpoutBPFunctionLibrary::SpoutSender(FName spoutName, ESpoutSendTextureFrom
return false;
}

//Sincroniza el thread del render y la copia de la textura
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
void,
ID3D11Texture2D*, tt, targetTex,
ID3D11Texture2D*, bt, baseTexture,
{

g_pImmediateContext->CopyResource(tt, bt);
g_pImmediateContext->Flush();

ENQUEUE_RENDER_COMMAND(void)(
[targetTex, baseTexture](FRHICommandListImmediate& RHICmdList) {
g_pImmediateContext->CopyResource(targetTex, baseTexture);
g_pImmediateContext->Flush();
});

D3D11_TEXTURE2D_DESC td;
Expand Down Expand Up @@ -522,20 +516,20 @@ bool USpoutBPFunctionLibrary::SpoutReceiver(const FName spoutName, UMaterialInst
ESpoutState state = CheckSenderState(spoutName);

if (state == ESpoutState::noEnoR) {
UE_LOG(SpoutLog, Warning, TEXT("Not found any sender and no registred with the name %s"), *spoutName.GetPlainNameString());
UE_LOG(SpoutLog, Warning, TEXT("try to rename it, or resend %s"), *SenderNameString);
UE_LOG(SpoutLog, Warning, TEXT("No sender found registered with the name %s"), *spoutName.GetPlainNameString());
UE_LOG(SpoutLog, Warning, TEXT("Try to rename it, or resend %s"), *SenderNameString);
return false;
}

if(state == ESpoutState::noER) {
UE_LOG(SpoutLog, Warning, TEXT("why are you registred??, unregistre, best close it"));
UE_LOG(SpoutLog, Warning, TEXT("Why are you registered??, unregister, best to close it"));
//UnregisterSpout(spoutName);
CloseSender(spoutName);
return false;
}

if (state == ESpoutState::EnoR) {
UE_LOG(SpoutLog, Warning, TEXT("Sender %s found, registring, receiving..."), *spoutName.GetPlainNameString());
UE_LOG(SpoutLog, Warning, TEXT("Sender %s found, registering, receiving..."), *spoutName.GetPlainNameString());
RegisterReceiver(spoutName);
return false;
}
Expand All @@ -553,14 +547,15 @@ bool USpoutBPFunctionLibrary::SpoutReceiver(const FName spoutName, UMaterialInst

//communication between the two threads (rendering thread and game thread)
// copy pixels from shared resource texture to texture temporal and update
ENQUEUE_UNIQUE_RENDER_COMMAND_FOURPARAMETER(
void,
ID3D11Texture2D*, t_texTemp, SenderStruct->texTemp,
ID3D11Texture2D*, t_tex, (ID3D11Texture2D*)SenderStruct->sharedResource,
int32, Stride, SenderStruct->w * 4,
FSenderStruct*, Params, SenderStruct,
int32 Stride = SenderStruct->w * 4;

ENQUEUE_RENDER_COMMAND(void)(
[SenderStruct, Stride](FRHICommandListImmediate& RHICmdList)
{
if (Params == nullptr) {
ID3D11Texture2D* t_texTemp = SenderStruct->texTemp;
ID3D11Texture2D* t_tex = (ID3D11Texture2D*)SenderStruct->sharedResource;

if (SenderStruct == nullptr) {
return;
}

Expand All @@ -581,7 +576,13 @@ bool USpoutBPFunctionLibrary::SpoutReceiver(const FName spoutName, UMaterialInst


//Update Texture
RHIUpdateTexture2D(Params->Texture2DResource->GetTexture2DRHI(), 0, *Params->UpdateRegions, mapped.RowPitch, (uint8*)pixel);
RHIUpdateTexture2D(
SenderStruct->Texture2DResource->GetTexture2DRHI(),
0,
*SenderStruct->UpdateRegions,
mapped.RowPitch,
(uint8*)pixel
);


});
Expand All @@ -597,7 +598,7 @@ bool USpoutBPFunctionLibrary::SpoutReceiver(const FName spoutName, UMaterialInst
void USpoutBPFunctionLibrary::CloseSender(FName spoutName)
{

UE_LOG(SpoutLog, Warning, TEXT("Closing... sender %s "), *spoutName.GetPlainNameString());
UE_LOG(SpoutLog, Warning, TEXT("Closing... sender %s"), *spoutName.GetPlainNameString());

if (sender == nullptr)
{
Expand All @@ -612,7 +613,7 @@ void USpoutBPFunctionLibrary::CloseSender(FName spoutName)
ESpoutState state = CheckSenderState(spoutName);

if (state == ESpoutState::noEnoR) {
UE_LOG(SpoutLog, Warning, TEXT("already %s closed, there is nothing to close!! check the name"), *spoutName.GetPlainNameString());
UE_LOG(SpoutLog, Warning, TEXT("%s is already closed; there is nothing to close!! Please check the name."), *spoutName.GetPlainNameString());
//return;
}
if (state == ESpoutState::noER) {
Expand All @@ -621,7 +622,7 @@ void USpoutBPFunctionLibrary::CloseSender(FName spoutName)
//return;
}
if (state == ESpoutState::EnoR) {
UE_LOG(SpoutLog, Warning, TEXT("Already exist a Sender with the name %s, You can not close a sender that is not yours.??"), *spoutName.GetPlainNameString());
UE_LOG(SpoutLog, Warning, TEXT("A Sender with the name %s already exists. You can not close a sender that is not yours??"), *spoutName.GetPlainNameString());
//return;
}
if (state == ESpoutState::ER) {
Expand All @@ -630,14 +631,14 @@ void USpoutBPFunctionLibrary::CloseSender(FName spoutName)
GetSpoutRegistred(spoutName, tempSenderStruct);

if (tempSenderStruct->spoutType == ESpoutType::Sender) {
UE_LOG(SpoutLog, Warning, TEXT("releasing sender %s"), *spoutName.GetPlainNameString());
UE_LOG(SpoutLog, Warning, TEXT("Releasing sender %s"), *spoutName.GetPlainNameString());
// here really release the sender
sender->ReleaseSenderName(TCHAR_TO_ANSI(*spoutName.ToString()));
UE_LOG(SpoutLog, Warning, TEXT("sender %s released"), *spoutName.GetPlainNameString());
UE_LOG(SpoutLog, Warning, TEXT("Sender %s released"), *spoutName.GetPlainNameString());

}
else {
UE_LOG(SpoutLog, Warning, TEXT("receiver always listening"));
UE_LOG(SpoutLog, Warning, TEXT("Receiver always listening"));

FlushRenderingCommands();
if (tempSenderStruct->sharedResource != nullptr) {
Expand All @@ -659,7 +660,7 @@ void USpoutBPFunctionLibrary::CloseSender(FName spoutName)
UnregisterSpout(spoutName);
}

UE_LOG(SpoutLog, Warning, TEXT("There are now %i senders remaining "), FSenders.Num());
UE_LOG(SpoutLog, Warning, TEXT("There are now %i senders remaining"), FSenders.Num());


}
Expand Down

0 comments on commit 242778b

Please sign in to comment.