|
|
@@ -0,0 +1,324 @@
|
|
|
+// Portions Copyright (c) 2008-2015 the Urho3D project.
|
|
|
+//
|
|
|
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
|
|
|
+// LICENSE: Atomic Game Engine Editor and Tools EULA
|
|
|
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
|
|
|
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
|
|
|
+//
|
|
|
+
|
|
|
+#include <Atomic/Core/Context.h>
|
|
|
+#include <Atomic/Core/CoreEvents.h>
|
|
|
+
|
|
|
+#include <Atomic/IO/Log.h>
|
|
|
+#include <Atomic/IO/FileSystem.h>
|
|
|
+#include <Atomic/IO/File.h>
|
|
|
+
|
|
|
+#include <Atomic/Graphics/Graphics.h>
|
|
|
+#include <Atomic/Graphics/Camera.h>
|
|
|
+#include <Atomic/Graphics/Viewport.h>
|
|
|
+#include <Atomic/Graphics/Texture2D.h>
|
|
|
+#include <Atomic/Graphics/TextureCube.h>
|
|
|
+
|
|
|
+#include <Atomic/Resource/ResourceCache.h>
|
|
|
+#include <Atomic/Resource/XMLFile.h>
|
|
|
+
|
|
|
+#include <Atomic/Scene/Node.h>
|
|
|
+
|
|
|
+#include <ToolCore/ToolSystem.h>
|
|
|
+#include <ToolCore/Project/Project.h>
|
|
|
+
|
|
|
+#include "../Editors/SceneEditor3D/SceneEditor3DEvents.h"
|
|
|
+#include "../Editors/SceneEditor3D/SceneEditor3D.h"
|
|
|
+
|
|
|
+#include "CubemapGenerator.h"
|
|
|
+
|
|
|
+namespace AtomicEditor
|
|
|
+{
|
|
|
+
|
|
|
+CubemapGenerator::CubemapGenerator(Context *context) : EditorComponent(context),
|
|
|
+ updateCycle_(0),
|
|
|
+ imageSize_(512),
|
|
|
+ namePrefix_("Cubemap")
|
|
|
+{
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+CubemapGenerator::~CubemapGenerator()
|
|
|
+{
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+bool CubemapGenerator::Render()
|
|
|
+{
|
|
|
+
|
|
|
+ if(!InitRender())
|
|
|
+ {
|
|
|
+ LOGERRORF("Unable to init render");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ GetScene()->SendEvent(E_CUBEMAPRENDERBEGIN);
|
|
|
+ SubscribeToEvent(E_BEGINFRAME, HANDLER(CubemapGenerator, HandleBeginFrame));
|
|
|
+ SubscribeToEvent(E_ENDFRAME, HANDLER(CubemapGenerator, HandleEndFrame));
|
|
|
+
|
|
|
+ return true;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+bool CubemapGenerator::InitPaths()
|
|
|
+{
|
|
|
+
|
|
|
+ String scenePath = sceneEditor_->GetFullPath();
|
|
|
+
|
|
|
+ String pathName;
|
|
|
+ String fileName;
|
|
|
+ String ext;
|
|
|
+
|
|
|
+ SplitPath(scenePath, pathName, fileName, ext);
|
|
|
+
|
|
|
+ outputPathAbsolute_ = pathName + "Cubemaps/" + fileName + "/";
|
|
|
+
|
|
|
+ FileSystem* fileSystem = GetSubsystem<FileSystem>();
|
|
|
+
|
|
|
+ if (!fileSystem->DirExists(outputPathAbsolute_))
|
|
|
+ {
|
|
|
+ if (!fileSystem->CreateDirs(pathName, "Cubemaps/" + fileName + "/"))
|
|
|
+ {
|
|
|
+ LOGERRORF("CubemapGenerator::InitRender - Unable to create path: %s", outputPathAbsolute_.CString());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO: There should be a better way of getting the resource path
|
|
|
+ ToolSystem* tsystem = GetSubsystem<ToolSystem>();
|
|
|
+ Project* project = tsystem->GetProject();
|
|
|
+
|
|
|
+ resourcePath_ = outputPathAbsolute_;
|
|
|
+ resourcePath_.Replace(project->GetResourcePath(), "");
|
|
|
+ resourcePath_ = AddTrailingSlash(resourcePath_);
|
|
|
+
|
|
|
+ return true;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+bool CubemapGenerator::InitRender()
|
|
|
+{
|
|
|
+
|
|
|
+ sceneEditor_ = GetSceneEditor();
|
|
|
+
|
|
|
+ if (sceneEditor_.Null())
|
|
|
+ {
|
|
|
+ LOGERROR("CubemapGenerator::InitRender - unable to get scene editor");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!InitPaths())
|
|
|
+ return false;
|
|
|
+
|
|
|
+ cameraNode_ = node_->CreateChild("CubeMapRenderCamera");
|
|
|
+ cameraNode_->SetTemporary(true);
|
|
|
+
|
|
|
+ camera_ = cameraNode_->CreateComponent<Camera>();
|
|
|
+ camera_->SetTemporary(true);
|
|
|
+ camera_->SetFov(90.0f);
|
|
|
+ camera_->SetNearClip(0.0001f);
|
|
|
+ camera_->SetAspectRatio(1.0f);
|
|
|
+
|
|
|
+ RenderPath* renderPath = sceneEditor_->GetSceneView3D()->GetViewport()->GetRenderPath();
|
|
|
+ viewport_ = new Viewport(context_, GetScene(), camera_, renderPath);
|
|
|
+
|
|
|
+ renderImage_ = new Texture2D(context_);
|
|
|
+ renderImage_->SetSize(imageSize_, imageSize_, Graphics::GetRGBAFormat(), TEXTURE_RENDERTARGET);
|
|
|
+
|
|
|
+ renderSurface_ = renderImage_->GetRenderSurface();
|
|
|
+ renderSurface_->SetViewport(0, viewport_);
|
|
|
+ renderSurface_->SetUpdateMode(SURFACE_UPDATEALWAYS);
|
|
|
+
|
|
|
+ return true;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+void CubemapGenerator::SaveCubemapXML()
|
|
|
+{
|
|
|
+ SharedPtr<XMLFile> xmlFile(new XMLFile(context_));
|
|
|
+ XMLElement rootElem = xmlFile->CreateRoot("cubemap");
|
|
|
+
|
|
|
+ String prefix = resourcePath_ + namePrefix_ + "_";
|
|
|
+
|
|
|
+ String name = prefix + GetFaceName(FACE_POSITIVE_X) + ".png";
|
|
|
+ rootElem.CreateChild("face").SetAttribute("name", name);
|
|
|
+ name = prefix + GetFaceName(FACE_NEGATIVE_X) + ".png";
|
|
|
+ rootElem.CreateChild("face").SetAttribute("name", name);
|
|
|
+ name = prefix + GetFaceName(FACE_POSITIVE_Y) + ".png";
|
|
|
+ rootElem.CreateChild("face").SetAttribute("name", name);
|
|
|
+ name = prefix + GetFaceName(FACE_NEGATIVE_Y) + ".png";
|
|
|
+ rootElem.CreateChild("face").SetAttribute("name", name);
|
|
|
+ name = prefix + GetFaceName(FACE_POSITIVE_Z) + ".png";
|
|
|
+ rootElem.CreateChild("face").SetAttribute("name", name);
|
|
|
+ name = prefix + GetFaceName(FACE_NEGATIVE_Z) + ".png";
|
|
|
+ rootElem.CreateChild("face").SetAttribute("name", name);
|
|
|
+
|
|
|
+ String xmlPath = outputPathAbsolute_ + namePrefix_ + ".xml";
|
|
|
+
|
|
|
+ SharedPtr<File> file(new File(context_, xmlPath, FILE_WRITE));
|
|
|
+ xmlFile->Save(*file, " ");
|
|
|
+ file->Close();
|
|
|
+
|
|
|
+ ResourceCache* cache = GetSubsystem<ResourceCache>();
|
|
|
+ TextureCube* texcube = cache->GetResource<TextureCube>(resourcePath_ + namePrefix_ + ".xml");
|
|
|
+ if (texcube)
|
|
|
+ cache->ReloadResource(texcube);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+void CubemapGenerator::EndRender()
|
|
|
+{
|
|
|
+ UnsubscribeFromEvent(E_BEGINFRAME);
|
|
|
+ UnsubscribeFromEvent(E_ENDFRAME);
|
|
|
+
|
|
|
+ SaveCubemapXML();
|
|
|
+
|
|
|
+ cameraNode_->Remove();
|
|
|
+
|
|
|
+ cameraNode_ = 0;
|
|
|
+ camera_ = 0;
|
|
|
+ viewport_ = 0;
|
|
|
+ renderImage_ = 0;
|
|
|
+ assert(renderSurface_->Refs() == 1);
|
|
|
+ renderSurface_ = 0;
|
|
|
+ updateCycle_ = 0;
|
|
|
+
|
|
|
+ GetScene()->SendEvent(E_CUBEMAPRENDEREND);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+void CubemapGenerator::HandleBeginFrame(StringHash eventType, VariantMap& eventData)
|
|
|
+{
|
|
|
+ updateCycle_++;
|
|
|
+ if (updateCycle_ < 7)
|
|
|
+ cameraNode_->SetWorldRotation(RotationOf(GetFaceForCycle(updateCycle_)));
|
|
|
+ else
|
|
|
+ {
|
|
|
+ EndRender();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CubemapGenerator::HandleEndFrame(StringHash eventType, VariantMap& eventData)
|
|
|
+{
|
|
|
+ SharedPtr<Image> image(GetImage(renderImage_));
|
|
|
+
|
|
|
+ String path = outputPathAbsolute_;
|
|
|
+
|
|
|
+ if (namePrefix_.Length())
|
|
|
+ path += namePrefix_;
|
|
|
+
|
|
|
+ path.AppendWithFormat("_%s.png", GetFaceName(GetFaceForCycle(updateCycle_)).CString());
|
|
|
+
|
|
|
+ image->SavePNG(path);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+SharedPtr<Image> CubemapGenerator::GetImage(Texture2D* tex2d)
|
|
|
+{
|
|
|
+ Image* rawImage = new Image(tex2d->GetContext());
|
|
|
+
|
|
|
+ const unsigned format = tex2d->GetFormat();
|
|
|
+
|
|
|
+ if (format == Graphics::GetRGBAFormat() || format == Graphics::GetRGBA16Format() || format == Graphics::GetRGBAFloat32Format())
|
|
|
+ rawImage->SetSize(tex2d->GetWidth(), tex2d->GetHeight(), 4);
|
|
|
+ else if (format == Graphics::GetRGBFormat())
|
|
|
+ rawImage->SetSize(tex2d->GetWidth(), tex2d->GetHeight(), 3);
|
|
|
+ else
|
|
|
+ return SharedPtr<Image>();
|
|
|
+
|
|
|
+ tex2d->GetData(0, rawImage->GetData());
|
|
|
+
|
|
|
+ return SharedPtr<Image>(rawImage);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+CubeMapFace CubemapGenerator::GetFaceForCycle(int cycle)
|
|
|
+{
|
|
|
+ switch (cycle)
|
|
|
+ {
|
|
|
+ case 1:
|
|
|
+ return FACE_POSITIVE_X;
|
|
|
+ case 2:
|
|
|
+ return FACE_POSITIVE_Y;
|
|
|
+ case 3:
|
|
|
+ return FACE_POSITIVE_Z;
|
|
|
+ case 4:
|
|
|
+ return FACE_NEGATIVE_X;
|
|
|
+ case 5:
|
|
|
+ return FACE_NEGATIVE_Y;
|
|
|
+ case 6:
|
|
|
+ return FACE_NEGATIVE_Z;
|
|
|
+ }
|
|
|
+ return FACE_POSITIVE_X;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+String CubemapGenerator::GetFaceName(CubeMapFace face)
|
|
|
+{
|
|
|
+ switch (face)
|
|
|
+ {
|
|
|
+ case FACE_POSITIVE_X:
|
|
|
+ return "PosX";
|
|
|
+ case FACE_POSITIVE_Y:
|
|
|
+ return "PosY";
|
|
|
+ case FACE_POSITIVE_Z:
|
|
|
+ return "PosZ";
|
|
|
+ case FACE_NEGATIVE_X:
|
|
|
+ return "NegX";
|
|
|
+ case FACE_NEGATIVE_Y:
|
|
|
+ return "NegY";
|
|
|
+ case FACE_NEGATIVE_Z:
|
|
|
+ return "NegZ";
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+ return "PosX";
|
|
|
+}
|
|
|
+
|
|
|
+Quaternion CubemapGenerator::RotationOf(CubeMapFace face)
|
|
|
+{
|
|
|
+ Quaternion result;
|
|
|
+ switch (face)
|
|
|
+ {
|
|
|
+ // Rotate camera according to probe rotation
|
|
|
+ case FACE_POSITIVE_X:
|
|
|
+ result = Quaternion(0, 90, 0);
|
|
|
+ break;
|
|
|
+ case FACE_NEGATIVE_X:
|
|
|
+ result = Quaternion(0, -90, 0);
|
|
|
+ break;
|
|
|
+ case FACE_POSITIVE_Y:
|
|
|
+ result = Quaternion(-90, 0, 0);
|
|
|
+ break;
|
|
|
+ case FACE_NEGATIVE_Y:
|
|
|
+ result = Quaternion(90, 0, 0);
|
|
|
+ break;
|
|
|
+ case FACE_POSITIVE_Z:
|
|
|
+ result = Quaternion(0, 0, 0);
|
|
|
+ break;
|
|
|
+ case FACE_NEGATIVE_Z:
|
|
|
+ result = Quaternion(0, 180, 0);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+void CubemapGenerator::RegisterObject(Context* context)
|
|
|
+{
|
|
|
+ context->RegisterFactory<CubemapGenerator>();
|
|
|
+
|
|
|
+ ATTRIBUTE("Name Prefix", String, namePrefix_, "Cubemap", AM_DEFAULT);
|
|
|
+ ATTRIBUTE("Image Size", int, imageSize_, 512, AM_DEFAULT);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+}
|