// Icon Studio - Mathew Simon

#pragma once

#include "CoreMinimal.h"
#include "UObject/GCObject.h"
#include "IconStudioTypes.h"

class FPreviewScene;
class UDirectionalLightComponent;
class UIconStudioPreset;
class UMaterialInterface;
class USceneCaptureComponent2D;
class USkyLightComponent;
class UStaticMesh;
class UStaticMeshComponent;
class UTexture2D;
class UTextureCube;
class UTextureRenderTarget2D;

/**
 * Le moteur de rendu du studio : une scene isolee, trois lumieres et une
 * capture, reutilisees d'un objet a l'autre.
 *
 * Rien de tout cela ne touche au niveau ouvert dans l'editeur. FPreviewScene
 * cree son propre UWorld, invisible et non tique, comme celui de l'editeur de
 * Static Mesh -- on peut donc generer un lot entier pendant qu'une carte est
 * chargee, sans y poser le moindre acteur. La scene et ses composants sont
 * construits une seule fois puis reconfigures a chaque rendu : recreer un UWorld
 * par icone couterait bien plus cher que tout le reste du travail.
 *
 * COMMENT LA TRANSPARENCE EST OBTENUE
 *
 * Une capture de scene ne fournit pas de canal alpha exploitable : elle efface
 * toujours sa cible en noir opaque, sans tenir compte de la couleur de fond du
 * render target, et l'alpha qui ressort du post-process ne vaut quelque chose
 * qu'avec le support alpha active pour le projet entier -- un reglage de rendu
 * global qu'un outil d'icones n'a pas a imposer.
 *
 * D'ou deux captures du meme point de vue : une de couleur, une de PROFONDEUR.
 * La profondeur dit exactement ou se trouve de la geometrie, et c'est elle qui
 * sert de masque. Comme l'antialiasing du moteur est desactive, chaque pixel de
 * rendu est franc : soit objet, soit vide. La douceur des bords vient de la
 * suranalyse -- on rend a quatre fois la taille finale et on moyenne, ce qui
 * donne un alpha a seize niveaux et une couleur de bord juste.
 *
 * Limite assumee : un materiau TRANSLUCIDE n'ecrit pas dans le tampon de
 * profondeur, il sera donc absent du masque. Pour du verre ou du feuillage
 * translucide, passer le materiau en Masked le temps du rendu.
 */
class ICONSTUDIO_API FIconStudioRenderer : public FGCObject
{
public:
	FIconStudioRenderer();
	virtual ~FIconStudioRenderer();

	//~ FGCObject
	virtual void AddReferencedObjects(FReferenceCollector& Collector) override;
	virtual FString GetReferencerName() const override { return TEXT("FIconStudioRenderer"); }
	//~ End FGCObject

	/**
	 * Rend un mesh et renvoie l'image composee, alpha compris.
	 * MaterialOverride ne remplace que le premier slot, comme le fait le jeu.
	 */
	bool Render(UStaticMesh* Mesh, UMaterialInterface* MaterialOverride, const UIconStudioPreset* Preset, EIconStudioPass Pass, FIconStudioImage& OutImage);

	/**
	 * Recopie une image dans la texture de previsualisation et renvoie celle-ci.
	 *
	 * Une UTexture2D transitoire, et non un render target : l'image finale est
	 * assemblee sur le CPU (masque, reduction, etalement des bords), donc le seul
	 * moyen de montrer exactement ce qui sera exporte est de reinjecter ces
	 * pixels-la dans une texture.
	 */
	UTexture2D* UpdatePreviewTexture(const FIconStudioImage& Image);

	UTexture2D* GetPreviewTexture() const { return PreviewTexture; }

private:
	void EnsureScene();
	void ApplyLighting(const UIconStudioPreset* Preset, const FRotator& CameraRotation);
	void ApplyCamera(const UIconStudioPreset* Preset, const FRotator& CameraRotation, const FBoxSphereBounds& Bounds);
	void ApplyPostProcess(const UIconStudioPreset* Preset);

	UTextureRenderTarget2D* EnsureTarget(TObjectPtr<UTextureRenderTarget2D>& Target, int32 Size, bool bFloatingPoint);

	/** Une capture sur la cible donnee. Le mode de capture change ce qui est ecrit dans les pixels. */
	bool CaptureTo(UTextureRenderTarget2D* Target, uint8 CaptureSource);

	TUniquePtr<FPreviewScene> Scene;

	TObjectPtr<UStaticMeshComponent> MeshComponent;
	TObjectPtr<USceneCaptureComponent2D> CaptureComponent;
	TObjectPtr<UDirectionalLightComponent> KeyLightComponent;
	TObjectPtr<UDirectionalLightComponent> FillLightComponent;
	TObjectPtr<UDirectionalLightComponent> RimLightComponent;
	TObjectPtr<USkyLightComponent> SkyLightComponent;

	/** Couleur en 8 bits sRGB, profondeur en virgule flottante : deux formats, deux cibles. */
	TObjectPtr<UTextureRenderTarget2D> ColorTarget;
	TObjectPtr<UTextureRenderTarget2D> DepthTarget;

	TObjectPtr<UTexture2D> PreviewTexture;

	/** Cubemap deja appliquee, pour ne pas recapturer le ciel a chaque rendu. */
	TObjectPtr<UTextureCube> AppliedSkyCubemap;
	float AppliedSkyIntensity = -1.f;

	/** Au-dela de cette distance, un pixel est du vide. Recalcule a chaque cadrage. */
	float DepthThreshold = 0.f;
};
