From deffbca3ee209f6aea3358692d9ca6dc7090e748 Mon Sep 17 00:00:00 2001
From: Dave Parks <davep@lindenlab.com>
Date: Thu, 28 Apr 2022 15:51:31 +0000
Subject: [PATCH] SL-17281 environment map prototype (stupid slow and hacky,
 but provides a way to get a "real" environment map for shader development)

---
 indra/llrender/llcubemap.cpp                  | 218 +++---------------
 indra/llrender/llcubemap.h                    |  16 +-
 indra/newview/CMakeLists.txt                  |   2 +
 indra/newview/llenvironmentmap.cpp            | 116 ++++++++++
 indra/newview/llenvironmentmap.h              |  48 ++++
 indra/newview/llviewermenu.cpp                |   8 +
 indra/newview/pipeline.cpp                    |   7 +-
 indra/newview/pipeline.h                      |   4 +
 .../skins/default/xui/en/menu_viewer.xml      |   7 +
 9 files changed, 231 insertions(+), 195 deletions(-)
 create mode 100644 indra/newview/llenvironmentmap.cpp
 create mode 100644 indra/newview/llenvironmentmap.h

diff --git a/indra/llrender/llcubemap.cpp b/indra/llrender/llcubemap.cpp
index 834084674e0..40d15280d30 100644
--- a/indra/llrender/llcubemap.cpp
+++ b/indra/llrender/llcubemap.cpp
@@ -166,6 +166,39 @@ void LLCubeMap::init(const std::vector<LLPointer<LLImageRaw> >& rawimages)
 	}
 }
 
+void LLCubeMap::initEnvironmentMap(const std::vector<LLPointer<LLImageRaw> >& rawimages)
+{
+    llassert(rawimages.size() == 6);
+
+    U32 texname = 0;
+
+    LLImageGL::generateTextures(1, &texname);
+
+    U32 resolution = rawimages[0]->getWidth();
+    U32 components = rawimages[0]->getComponents();
+
+    for (int i = 0; i < 6; i++)
+    {
+        llassert(rawimages[i]->getWidth() == resolution);
+        llassert(rawimages[i]->getHeight() == resolution);
+        llassert(rawimages[i]->getComponents() == components);
+
+
+        mImages[i] = new LLImageGL(resolution, resolution, components, FALSE);
+        mImages[i]->setTarget(mTargets[i], LLTexUnit::TT_CUBE_MAP);
+        mRawImages[i] = rawimages[i];
+        mImages[i]->createGLTexture(0, mRawImages[i], texname);
+
+        gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_CUBE_MAP, texname);
+        mImages[i]->setAddressMode(LLTexUnit::TAM_CLAMP);
+        stop_glerror();
+
+        mImages[i]->setSubImage(mRawImages[i], 0, 0, resolution, resolution);
+    }
+    gGL.getTexUnit(0)->disable();
+    disable();
+}
+
 GLuint LLCubeMap::getGLName()
 {
 	return mImages[0]->getTexName();
@@ -256,191 +289,6 @@ void LLCubeMap::restoreMatrix()
 	}*/
 }
 
-void LLCubeMap::setReflection (void)
-{
-	gGL.getTexUnit(mTextureStage)->bindManual(LLTexUnit::TT_CUBE_MAP, getGLName());
-	mImages[0]->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
-	mImages[0]->setAddressMode(LLTexUnit::TAM_CLAMP);
-}
-
-LLVector3 LLCubeMap::map(U8 side, U16 v_val, U16 h_val) const
-{
-	LLVector3 dir;
-
-	const U8 curr_coef = side >> 1; // 0/1 = X axis, 2/3 = Y, 4/5 = Z
-	const S8 side_dir = (((side & 1) << 1) - 1);  // even = -1, odd = 1
-	const U8 i_coef = (curr_coef + 1) % 3;
-	const U8 j_coef = (i_coef + 1) % 3;
-
-	dir.mV[curr_coef] = side_dir;
-
-	switch (side)
-	{
-	case 0: // negative X
-		dir.mV[i_coef] = -F32((v_val<<1) + 1) / RESOLUTION + 1;
-		dir.mV[j_coef] = F32((h_val<<1) + 1) / RESOLUTION - 1;
-		break;
-	case 1: // positive X
-		dir.mV[i_coef] = -F32((v_val<<1) + 1) / RESOLUTION + 1;
-		dir.mV[j_coef] = -F32((h_val<<1) + 1) / RESOLUTION + 1;
-		break;
-	case 2:	// negative Y
-		dir.mV[i_coef] = -F32((v_val<<1) + 1) / RESOLUTION + 1;
-		dir.mV[j_coef] = F32((h_val<<1) + 1) / RESOLUTION - 1;
-		break;
-	case 3:	// positive Y
-		dir.mV[i_coef] = F32((v_val<<1) + 1) / RESOLUTION - 1;
-		dir.mV[j_coef] = F32((h_val<<1) + 1) / RESOLUTION - 1;
-		break;
-	case 4:	// negative Z
-		dir.mV[i_coef] = -F32((h_val<<1) + 1) / RESOLUTION + 1;
-		dir.mV[j_coef] = -F32((v_val<<1) + 1) / RESOLUTION + 1;
-		break;
-	case 5: // positive Z
-		dir.mV[i_coef] = -F32((h_val<<1) + 1) / RESOLUTION + 1;
-		dir.mV[j_coef] = F32((v_val<<1) + 1) / RESOLUTION - 1;
-		break;
-	default:
-		dir.mV[i_coef] = F32((v_val<<1) + 1) / RESOLUTION - 1;
-		dir.mV[j_coef] = F32((h_val<<1) + 1) / RESOLUTION - 1;
-	}
-
-	dir.normVec();
-	return dir;
-}
-
-
-BOOL LLCubeMap::project(F32& v_val, F32& h_val, BOOL& outside,
-						U8 side, const LLVector3& dir) const
-{
-	const U8 curr_coef = side >> 1; // 0/1 = X axis, 2/3 = Y, 4/5 = Z
-	const S8 side_dir = (((side & 1) << 1) - 1);  // even = -1, odd = 1
-	const U8 i_coef = (curr_coef + 1) % 3;
-	const U8 j_coef = (i_coef + 1) % 3;
-
-	outside = TRUE;
-	if (side_dir * dir.mV[curr_coef] < 0)
-		return FALSE;
-
-	LLVector3 ray;
-
-	F32 norm_val = fabs(dir.mV[curr_coef]);
-
-	if (norm_val < epsilon)
-		norm_val = 1e-5f;
-
-	ray.mV[curr_coef] = side_dir;
-	ray.mV[i_coef] = dir.mV[i_coef] / norm_val;
-	ray.mV[j_coef] = dir.mV[j_coef] / norm_val;
-
-
-	const F32 i_val = (ray.mV[i_coef] + 1) * 0.5f * RESOLUTION;
-	const F32 j_val = (ray.mV[j_coef] + 1) * 0.5f * RESOLUTION;
-
-	switch (side)
-	{
-	case 0: // negative X
-		v_val = RESOLUTION - i_val;
-		h_val = j_val;
-		break;
-	case 1: // positive X
-		v_val = RESOLUTION - i_val;
-		h_val = RESOLUTION - j_val;
-		break;
-	case 2:	// negative Y
-		v_val = RESOLUTION - i_val;
-		h_val = j_val;
-		break;
-	case 3:	// positive Y
-		v_val = i_val;
-		h_val = j_val;
-		break;
-	case 4:	// negative Z
-		v_val = RESOLUTION - j_val;
-		h_val = RESOLUTION - i_val;
-		break;
-	case 5: // positive Z
-		v_val = RESOLUTION - j_val;
-		h_val = i_val;
-		break;
-	default:
-		v_val = i_val;
-		h_val = j_val;
-	}
-
-	outside =  ((v_val < 0) || (v_val > RESOLUTION) ||
-		(h_val < 0) || (h_val > RESOLUTION));
-
-	return TRUE;
-}
-
-BOOL LLCubeMap::project(F32& v_min, F32& v_max, F32& h_min, F32& h_max, 
-						U8 side, LLVector3 dir[4]) const
-{
-	v_min = h_min = RESOLUTION;
-	v_max = h_max = 0;
-
-	BOOL fully_outside = TRUE;
-	for (U8 vtx = 0; vtx < 4; ++vtx)
-	{
-		F32 v_val, h_val;
-		BOOL outside;
-		BOOL consider = project(v_val, h_val, outside, side, dir[vtx]);
-		if (!outside)
-			fully_outside = FALSE;
-		if (consider)
-		{
-			if (v_val < v_min) v_min = v_val;
-			if (v_val > v_max) v_max = v_val;
-			if (h_val < h_min) h_min = h_val;
-			if (h_val > h_max) h_max = h_val;
-		}
-	}
-
-	v_min = llmax(0.0f, v_min);
-	v_max = llmin(RESOLUTION - epsilon, v_max);
-	h_min = llmax(0.0f, h_min);
-	h_max = llmin(RESOLUTION - epsilon, h_max);
-
-	return !fully_outside;
-}
-
-
-void LLCubeMap::paintIn(LLVector3 dir[4], const LLColor4U& col)
-{
-    LL_PROFILE_ZONE_SCOPED;
-	F32 v_min, v_max, h_min, h_max;
-	LLVector3 center = dir[0] + dir[1] + dir[2] + dir[3];
-	center.normVec();
-
-	for (U8 side = 0; side < 6; ++side)
-	{
-		if (!project(v_min, v_max, h_min, h_max, side, dir))
-			continue;
-
-		U8 *td = mRawImages[side]->getData();
-		
-		U16 v_minu = (U16) v_min;
-		U16 v_maxu = (U16) (ceil(v_max) + 0.5);
-		U16 h_minu = (U16) h_min;
-		U16 h_maxu = (U16) (ceil(h_max) + 0.5);
-
-		for (U16 v = v_minu; v < v_maxu; ++v)
-			for (U16 h = h_minu; h < h_maxu; ++h)
-		//for (U16 v = 0; v < RESOLUTION; ++v)
-		//	for (U16 h = 0; h < RESOLUTION; ++h)
-			{
-				const LLVector3 ray = map(side, v, h);
-				if (ray * center > 0.999)
-				{
-					const U32 offset = (RESOLUTION * v + h) * 4;
-					for (U8 cc = 0; cc < 3; ++cc)
-						td[offset + cc] = U8((td[offset + cc] + col.mV[cc]) * 0.5);
-				}
-			}
-		mImages[side]->setSubImage(mRawImages[side], 0, 0, RESOLUTION, RESOLUTION);
-	}
-}
 
 void LLCubeMap::destroyGL()
 {
diff --git a/indra/llrender/llcubemap.h b/indra/llrender/llcubemap.h
index a01636d8d41..9ce4a94bcae 100644
--- a/indra/llrender/llcubemap.h
+++ b/indra/llrender/llcubemap.h
@@ -40,6 +40,13 @@ class LLCubeMap : public LLRefCount
 public:
 	LLCubeMap(bool init_as_srgb);
 	void init(const std::vector<LLPointer<LLImageRaw> >& rawimages);
+
+    // init from environment map images
+    // Similar to init, but takes ownership of rawimages and makes this cubemap
+    // respect the resolution of rawimages
+    // Raw images must point to array of six square images that are all the same resolution
+    // For example usage, see LLEnvironmentMap
+    void initEnvironmentMap(const std::vector<LLPointer<LLImageRaw> >& rawimages);
 	void initGL();
 	void initRawData(const std::vector<LLPointer<LLImageRaw> >& rawimages);
 	void initGLData();
@@ -54,18 +61,9 @@ class LLCubeMap : public LLRefCount
 	void disableTexture(void);
 	void setMatrix(S32 stage);
 	void restoreMatrix();
-	void setReflection (void);
-
-	void finishPaint();
 
 	GLuint getGLName();
 
-	LLVector3 map(U8 side, U16 v_val, U16 h_val) const;
-	BOOL project(F32& v_val, F32& h_val, BOOL& outside,
-						U8 side, const LLVector3& dir) const;
-	BOOL project(F32& v_min, F32& v_max, F32& h_min, F32& h_max, 
-						U8 side, LLVector3 dir[4]) const;
-	void paintIn(LLVector3 dir[4], const LLColor4U& col);
 	void destroyGL();
 
 public:
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 964615320db..5e50f585953 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -196,6 +196,7 @@ set(viewer_SOURCE_FILES
     lldynamictexture.cpp
     llemote.cpp
     llenvironment.cpp
+    llenvironmentmap.cpp
     llestateinfomodel.cpp
     lleventnotifier.cpp
     lleventpoll.cpp
@@ -832,6 +833,7 @@ set(viewer_HEADER_FILES
     lldynamictexture.h
     llemote.h
     llenvironment.h
+    llenvironmentmap.h
     llestateinfomodel.h
     lleventnotifier.h
     lleventpoll.h
diff --git a/indra/newview/llenvironmentmap.cpp b/indra/newview/llenvironmentmap.cpp
new file mode 100644
index 00000000000..7f23c630f19
--- /dev/null
+++ b/indra/newview/llenvironmentmap.cpp
@@ -0,0 +1,116 @@
+/**
+ * @file llenvironmentmap.cpp
+ * @brief LLEnvironmentMap class implementation
+ *
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2022, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llenvironmentmap.h"
+#include "pipeline.h"
+#include "llviewerwindow.h"
+
+LLEnvironmentMap::LLEnvironmentMap()
+{
+    mOrigin.setVec(0, 0, 0);
+}
+
+void LLEnvironmentMap::update(const LLVector3& origin, U32 resolution)
+{
+    LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
+
+    mOrigin = origin;
+
+    // allocate images
+    std::vector<LLPointer<LLImageRaw> > rawimages;
+    rawimages.reserve(6);
+
+    for (int i = 0; i < 6; ++i)
+    {
+        rawimages.push_back(new LLImageRaw(resolution, resolution, 3));
+    }
+
+    // ============== modified copy/paste of LLFloater360Capture::capture360Images() follows ==============
+
+    // these are the 6 directions we will point the camera, see LLCubeMap::mTargets
+    LLVector3 look_dirs[6] = {
+        LLVector3(-1, 0, 0),
+        LLVector3(1, 0, 0),
+        LLVector3(0, -1, 0),
+        LLVector3(0, 1, 0),
+        LLVector3(0, 0, -1),
+        LLVector3(0, 0, 1)
+    };
+
+    LLVector3 look_upvecs[6] = { 
+        LLVector3(0, -1, 0), 
+        LLVector3(0, -1, 0), 
+        LLVector3(0, 0, -1), 
+        LLVector3(0, 0, 1), 
+        LLVector3(0, 1, 0), 
+        LLVector3(0, -1, 0) 
+    };
+
+    // save current view/camera settings so we can restore them afterwards
+    S32 old_occlusion = LLPipeline::sUseOcclusion;
+
+    // set new parameters specific to the 360 requirements
+    LLPipeline::sUseOcclusion = 0;
+    LLViewerCamera* camera = LLViewerCamera::getInstance();
+    LLVector3 old_origin = camera->getOrigin();
+    F32 old_fov = camera->getView();
+    F32 old_aspect = camera->getAspect();
+    F32 old_yaw = camera->getYaw();
+
+    // camera constants for the square, cube map capture image
+    camera->setAspect(1.0); // must set aspect ratio first to avoid undesirable clamping of vertical FoV
+    camera->setView(F_PI_BY_TWO);
+    camera->yaw(0.0);
+    camera->setOrigin(mOrigin);
+
+    // for each of the 6 directions we shoot...
+    for (int i = 0; i < 6; i++)
+    {
+        // set up camera to look in each direction
+        camera->lookDir(look_dirs[i], look_upvecs[i]);
+
+        // call the (very) simplified snapshot code that simply deals
+        // with a single image, no sub-images etc. but is very fast
+        gViewerWindow->simpleSnapshot(rawimages[i],
+            resolution, resolution, 1);
+    }
+
+    // restore original view/camera/avatar settings settings
+    camera->setAspect(old_aspect);
+    camera->setView(old_fov);
+    camera->yaw(old_yaw);
+    camera->setOrigin(old_origin);
+
+    LLPipeline::sUseOcclusion = old_occlusion;
+
+    // ====================================================
+    
+    mCubeMap = new LLCubeMap(false);
+    mCubeMap->initEnvironmentMap(rawimages);
+}
+
diff --git a/indra/newview/llenvironmentmap.h b/indra/newview/llenvironmentmap.h
new file mode 100644
index 00000000000..7d951eb678c
--- /dev/null
+++ b/indra/newview/llenvironmentmap.h
@@ -0,0 +1,48 @@
+/**
+ * @file llenvironmentmap.h
+ * @brief LLEnvironmentMap class declaration
+ *
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2022, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#pragma once
+
+#include "llcubemap.h"
+
+class LLEnvironmentMap
+{
+public:
+    // allocate an environment map of the given resolution 
+    LLEnvironmentMap();
+
+    // update this environment map
+    // origin - position in agent space to generate environment map from in agent space
+    // resolution - size of cube map to generate
+    void update(const LLVector3& origin, U32 resolution);
+    
+    // cube map used to sample this environment map
+    LLPointer<LLCubeMap> mCubeMap;
+
+    // point at which environment map was generated from (in agent space)
+    LLVector3 mOrigin;
+};
+
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index e3de4267dc4..f5ea060e82f 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -8304,6 +8304,12 @@ void handle_cache_clear_immediately()
 	LLNotificationsUtil::add("ConfirmClearCache", LLSD(), LLSD(), callback_clear_cache_immediately);
 }
 
+void handle_override_environment_map()
+{
+    gPipeline.overrideEnvironmentMap();
+}
+
+
 void handle_web_content_test(const LLSD& param)
 {
 	std::string url = param.asString();
@@ -9402,6 +9408,8 @@ void initialize_menus()
 	view_listener_t::addMenu(new LLDevelopTextureFetchDebugger(), "Develop.SetTexFetchDebugger");
 	//Develop (clear cache immediately)
 	commit.add("Develop.ClearCache", boost::bind(&handle_cache_clear_immediately) );
+    //Develop (override environment map)
+    commit.add("Develop.OverrideEnvironmentMap", boost::bind(&handle_override_environment_map));
 
 	// Admin >Object
 	view_listener_t::addMenu(new LLAdminForceTakeCopy(), "Admin.ForceTakeCopy");
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 20d6fe39e36..09437473d97 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -8191,7 +8191,7 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, LLRenderTarget* light_
 	channel = shader.enableTexture(LLShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP);
 	if (channel > -1)
 	{
-		LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL;
+		LLCubeMap* cube_map = mEnvironmentMap.mCubeMap.notNull() ? mEnvironmentMap.mCubeMap : gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL;
 		if (cube_map)
 		{
 			cube_map->enable(channel);
@@ -11467,3 +11467,8 @@ void LLPipeline::restoreHiddenObject( const LLUUID& id )
 	}
 }
 
+void LLPipeline::overrideEnvironmentMap()
+{
+    mEnvironmentMap.update(LLViewerCamera::instance().getOrigin(), 1024);
+}
+
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index 62d3ae7a398..150d3c7d58f 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -38,6 +38,7 @@
 #include "llgl.h"
 #include "lldrawable.h"
 #include "llrendertarget.h"
+#include "llenvironmentmap.h"
 
 #include <stack>
 
@@ -426,6 +427,9 @@ class LLPipeline
 	void hideObject( const LLUUID& id );
 	void restoreHiddenObject( const LLUUID& id );
 
+    LLEnvironmentMap mEnvironmentMap;
+    void overrideEnvironmentMap();
+
 private:
 	void unloadShaders();
 	void addToQuickLookup( LLDrawPool* new_poolp );
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 8d7cfe1116f..db4e794ed47 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -3250,6 +3250,13 @@ function="World.EnvPreset"
                  function="ToggleControl"
                  parameter="RenderHoverGlowEnable" />
             </menu_item_check>
+            <menu_item_call
+              enabled="true"
+              label="Override Environment Map"
+              name="Override Environment Map">
+              <menu_item_call.on_click
+               function="Develop.OverrideEnvironmentMap" />
+            </menu_item_call>
           <menu_item_separator />
           
           <menu_item_call
-- 
GitLab