1
+ // Fill out your copyright notice in the Description page of Project Settings.
2
+
3
+
4
+ #include " CameraCaptureManager.h"
5
+
6
+ // #include "Engine.h"
7
+ #include " Runtime/Engine/Classes/Engine/Engine.h"
8
+
9
+ #include " Engine/SceneCapture2D.h"
10
+ #include " Components/SceneCaptureComponent2D.h"
11
+ #include " Engine/TextureRenderTarget2D.h"
12
+ #include " Kismet/GameplayStatics.h"
13
+ #include " ShowFlags.h"
14
+
15
+ #include " Materials/Material.h"
16
+
17
+ #include " RHICommandList.h"
18
+
19
+ #include " ImageWrapper/Public/IImageWrapper.h"
20
+ #include " ImageWrapper/Public/IImageWrapperModule.h"
21
+
22
+ #include " ImageUtils.h"
23
+
24
+ #include " Modules/ModuleManager.h"
25
+
26
+ #include " Misc/FileHelper.h"
27
+
28
+ // Sets default values
29
+ ACameraCaptureManager::ACameraCaptureManager ()
30
+ {
31
+ // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
32
+ PrimaryActorTick.bCanEverTick = true ;
33
+
34
+ }
35
+
36
+ // Called when the game starts or when spawned
37
+ void ACameraCaptureManager::BeginPlay ()
38
+ {
39
+ Super::BeginPlay ();
40
+
41
+ if (CaptureComponent ){ // nullptr check
42
+ SetupCaptureComponent ();
43
+ } else {
44
+ UE_LOG (LogTemp, Error, TEXT (" No CaptureComponent set!" ));
45
+ }
46
+
47
+ }
48
+
49
+ // Called every frame
50
+ void ACameraCaptureManager::Tick (float DeltaTime)
51
+ {
52
+ Super::Tick (DeltaTime);
53
+
54
+ // Read pixels once RenderFence is completed
55
+ if (!RenderRequestQueue.IsEmpty ()){
56
+ // Peek the next RenderRequest from queue
57
+ FRenderRequestStruct* nextRenderRequest = nullptr ;
58
+ RenderRequestQueue.Peek (nextRenderRequest);
59
+
60
+ // int32 frameWidht = 640;
61
+ // int32 frameHeight = 480;
62
+
63
+ if (nextRenderRequest){ // nullptr check
64
+ if (nextRenderRequest->RenderFence .IsFenceComplete ()){ // Check if rendering is done, indicated by RenderFence
65
+
66
+ // Load the image wrapper module
67
+ IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName (" ImageWrapper" ));
68
+
69
+ // Decide storing of data, either jpeg or png
70
+ FString fileName = " " ;
71
+ if (UsePNG){
72
+ // Generate image name
73
+ fileName = FPaths::ProjectSavedDir () + SubDirectoryName + " /img" + " _" + ToStringWithLeadingZeros (ImgCounter, NumDigits);
74
+ fileName += " .png" ; // Add file ending
75
+
76
+ // Prepare data to be written to disk
77
+ static TSharedPtr<IImageWrapper> imageWrapper = ImageWrapperModule.CreateImageWrapper (EImageFormat::PNG); // EImageFormat::PNG //EImageFormat::JPEG
78
+ imageWrapper->SetRaw (nextRenderRequest->Image .GetData (), nextRenderRequest->Image .GetAllocatedSize (), FrameWidth, FrameHeight, ERGBFormat::BGRA, 8 );
79
+ const TArray<uint8>& ImgData = imageWrapper->GetCompressed (5 );
80
+ RunAsyncImageSaveTask (ImgData, fileName);
81
+ } else {
82
+ // Generate image name
83
+ fileName = FPaths::ProjectSavedDir () + SubDirectoryName + " /img" + " _" + ToStringWithLeadingZeros (ImgCounter, NumDigits);
84
+ fileName += " .jpeg" ; // Add file ending
85
+
86
+ // Prepare data to be written to disk
87
+ static TSharedPtr<IImageWrapper> imageWrapper = ImageWrapperModule.CreateImageWrapper (EImageFormat::JPEG); // EImageFormat::PNG //EImageFormat::JPEG
88
+ imageWrapper->SetRaw (nextRenderRequest->Image .GetData (), nextRenderRequest->Image .GetAllocatedSize (), FrameWidth, FrameHeight, ERGBFormat::BGRA, 8 );
89
+ const TArray<uint8>& ImgData = imageWrapper->GetCompressed (0 );
90
+ RunAsyncImageSaveTask (ImgData, fileName);
91
+ }
92
+
93
+ if (VerboseLogging && !fileName.IsEmpty ()){
94
+ UE_LOG (LogTemp, Warning, TEXT (" %f" ), *fileName);
95
+ }
96
+
97
+ ImgCounter += 1 ;
98
+
99
+ // Delete the first element from RenderQueue
100
+ RenderRequestQueue.Pop ();
101
+ delete nextRenderRequest;
102
+
103
+ UE_LOG (LogTemp, Log, TEXT (" Done..." ));
104
+ }
105
+ }
106
+ }
107
+
108
+ }
109
+
110
+ void ACameraCaptureManager::SetupCaptureComponent (){
111
+ if (!IsValid (CaptureComponent )){
112
+ UE_LOG (LogTemp, Error, TEXT (" SetupCaptureComponent: CaptureComponent is not valid!" ));
113
+ return ;
114
+ }
115
+
116
+ // Create RenderTargets
117
+ UTextureRenderTarget2D* renderTarget2D = NewObject<UTextureRenderTarget2D>();
118
+
119
+ // Set FrameWidth and FrameHeight
120
+ renderTarget2D->TargetGamma = GEngine->GetDisplayGamma (); // 1.2f; // for Vulkan //GEngine->GetDisplayGamma(); // for DX11/12
121
+
122
+ // Setup the RenderTarget capture format
123
+ renderTarget2D->InitAutoFormat (256 , 256 ); // some random format, got crashing otherwise
124
+ // int32 frameWidht = 640;
125
+ // int32 frameHeight = 480;
126
+ renderTarget2D->InitCustomFormat (FrameWidth, FrameHeight, PF_B8G8R8A8, true ); // PF_B8G8R8A8 disables HDR which will boost storing to disk due to less image information
127
+ renderTarget2D->RenderTargetFormat = ETextureRenderTargetFormat::RTF_RGBA8;
128
+ renderTarget2D->bGPUSharedFlag = true ; // demand buffer on GPU
129
+
130
+ // Assign RenderTarget
131
+ CaptureComponent ->GetCaptureComponent2D ()->TextureTarget = renderTarget2D;
132
+
133
+ // Set Camera Properties
134
+ CaptureComponent ->GetCaptureComponent2D ()->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;
135
+ CaptureComponent ->GetCaptureComponent2D ()->ShowFlags .SetTemporalAA (true );
136
+ // lookup more showflags in the UE4 documentation..
137
+
138
+ // Assign PostProcess Material if assigned
139
+ if (PostProcessMaterial){ // check nullptr
140
+ CaptureComponent ->GetCaptureComponent2D ()->AddOrUpdateBlendable (PostProcessMaterial);
141
+ } else {
142
+ UE_LOG (LogTemp, Log, TEXT (" No PostProcessMaterial is assigend" ));
143
+ }
144
+
145
+ }
146
+
147
+ void ACameraCaptureManager::CaptureNonBlocking (){
148
+ if (!IsValid (CaptureComponent )){
149
+ UE_LOG (LogTemp, Error, TEXT (" CaptureColorNonBlocking: CaptureComponent was not valid!" ));
150
+ return ;
151
+ }
152
+
153
+ CaptureComponent ->GetCaptureComponent2D ()->TextureTarget ->TargetGamma = GEngine->GetDisplayGamma ();
154
+
155
+ // Get RenderConterxt
156
+ FTextureRenderTargetResource* renderTargetResource = CaptureComponent ->GetCaptureComponent2D ()->TextureTarget ->GameThread_GetRenderTargetResource ();
157
+
158
+ struct FReadSurfaceContext {
159
+ FRenderTarget* SrcRenderTarget;
160
+ TArray<FColor>* OutData;
161
+ FIntRect Rect ;
162
+ FReadSurfaceDataFlags Flags;
163
+ };
164
+
165
+ // Init new RenderRequest
166
+ FRenderRequestStruct* renderRequest = new FRenderRequestStruct ();
167
+
168
+ // Setup GPU command
169
+ FReadSurfaceContext readSurfaceContext = {
170
+ renderTargetResource,
171
+ &(renderRequest->Image ),
172
+ FIntRect (0 ,0 ,renderTargetResource->GetSizeXY ().X , renderTargetResource->GetSizeXY ().Y ),
173
+ FReadSurfaceDataFlags (RCM_UNorm, CubeFace_MAX)
174
+ };
175
+
176
+ // Send command to GPU
177
+ /* Up to version 4.22 use this
178
+ ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
179
+ SceneDrawCompletion,//ReadSurfaceCommand,
180
+ FReadSurfaceContext, Context, readSurfaceContext,
181
+ {
182
+ RHICmdList.ReadSurfaceData(
183
+ Context.SrcRenderTarget->GetRenderTargetTexture(),
184
+ Context.Rect,
185
+ *Context.OutData,
186
+ Context.Flags
187
+ );
188
+ });
189
+ */
190
+ // Above 4.22 use this
191
+ ENQUEUE_RENDER_COMMAND (SceneDrawCompletion)(
192
+ [readSurfaceContext](FRHICommandListImmediate& RHICmdList){
193
+ RHICmdList.ReadSurfaceData (
194
+ readSurfaceContext.SrcRenderTarget ->GetRenderTargetTexture (),
195
+ readSurfaceContext.Rect ,
196
+ *readSurfaceContext.OutData ,
197
+ readSurfaceContext.Flags
198
+ );
199
+ });
200
+
201
+ // Notifiy new task in RenderQueue
202
+ RenderRequestQueue.Enqueue (renderRequest);
203
+
204
+ // Set RenderCommandFence
205
+ renderRequest->RenderFence .BeginFence ();
206
+ }
207
+
208
+
209
+ /*
210
+ void ACameraCaptureManager::SpawnSegmentationCaptureComponent(ASceneCapture2D* ColorCapture){
211
+ if(!IsValid(ColorCapture)){
212
+ UE_LOG(LogTemp, Error, TEXT("CaptureColorNonBlocking: CaptureComponent was not valid!"));
213
+ return;
214
+ }
215
+
216
+ // Spawning a new SceneCaptureComponent
217
+ ASceneCapture2D* newSegmentationCapture = (ASceneCapture2D*) GetWorld()->SpawnActor<ASceneCapture2D>(ASceneCapture2D::StaticClass());
218
+ if(!newSegmentationCapture){ // nullptr check
219
+ UE_LOG(LogTemp, Error, TEXT("Failed to spawn SegmentationComponent"));
220
+ return;
221
+ }
222
+ // Register new CaptureComponent to game
223
+ newSegmentationCapture->GetCaptureComponent2D()->RegisterComponent();
224
+ // Attach SegmentationCaptureComponent to match ColorCaptureComponent
225
+ newSegmentationCapture->AttachToActor(ColorCapture, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
226
+
227
+ // Get values from "parent" ColorCaptureComponent
228
+ newSegmentationCapture->GetCaptureComponent2D()->FOVAngle = ColorCapture->GetCaptureComponent2D()->FOVAngle;
229
+
230
+ // Set pointer to new segmentation capture component
231
+ SegmentationCapture = newSegmentationCapture;
232
+
233
+ UE_LOG(LogTemp, Warning, TEXT("Done..."));
234
+ }
235
+ */
236
+ /*
237
+ void ACameraCaptureManager::SetupSegmentationCaptureComponent(ASceneCapture2D* ColorCapture){
238
+ if(!IsValid(ColorCapture)){
239
+ UE_LOG(LogTemp, Error, TEXT("CaptureColorNonBlocking: CaptureComponent was not valid!"));
240
+ return;
241
+ }
242
+
243
+ // Spawn SegmentaitonCaptureComponents
244
+ SpawnSegmentationCaptureComponent(ColorCapture);
245
+
246
+ // Setup SegmentationCaptureComponent
247
+ SetupColorCaptureComponent(SegmentationCapture);
248
+
249
+ // Assign PostProcess Material
250
+ if(PostProcessMaterial){ // check nullptr
251
+ SegmentationCapture->GetCaptureComponent2D()->AddOrUpdateBlendable(PostProcessMaterial);
252
+ } else {
253
+ UE_LOG(LogTemp, Error, TEXT("PostProcessMaterial was nullptr!"));
254
+ }
255
+ }
256
+ */
257
+
258
+ FString ACameraCaptureManager::ToStringWithLeadingZeros (int32 Integer, int32 MaxDigits){
259
+ FString result = FString::FromInt (Integer);
260
+ int32 stringSize = result.Len ();
261
+ int32 stringDelta = MaxDigits - stringSize;
262
+ if (stringDelta < 0 ){
263
+ UE_LOG (LogTemp, Error, TEXT (" MaxDigits of ImageCounter Overflow!" ));
264
+ return result;
265
+ }
266
+ // FIXME: Smarter function for this..
267
+ FString leadingZeros = " " ;
268
+ for (size_t i=0 ;i<stringDelta;i++){
269
+ leadingZeros += " 0" ;
270
+ }
271
+ result = leadingZeros + result;
272
+
273
+ return result;
274
+ }
275
+
276
+
277
+
278
+
279
+
280
+ void ACameraCaptureManager::RunAsyncImageSaveTask (TArray<uint8> Image, FString ImageName){
281
+ (new FAutoDeleteAsyncTask<AsyncSaveImageToDiskTask>(Image, ImageName))->StartBackgroundTask ();
282
+ }
283
+
284
+
285
+
286
+ /*
287
+ *******************************************************************
288
+ */
289
+
290
+ AsyncSaveImageToDiskTask::AsyncSaveImageToDiskTask (TArray<uint8> Image, FString ImageName){
291
+ ImageCopy = Image;
292
+ FileName = ImageName;
293
+ }
294
+
295
+ AsyncSaveImageToDiskTask::~AsyncSaveImageToDiskTask (){
296
+ // UE_LOG(LogTemp, Warning, TEXT("AsyncTaskDone"));
297
+ }
298
+
299
+ void AsyncSaveImageToDiskTask::DoWork (){
300
+ FFileHelper::SaveArrayToFile (ImageCopy, *FileName);
301
+ UE_LOG (LogTemp, Log, TEXT (" Stored Image: %s" ), *FileName);
302
+ }
0 commit comments