mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-09-09 12:25:17 -04:00
ESM4 landscape textures
This commit is contained in:
parent
b160cee0b7
commit
0b5c8271e0
@ -1,6 +1,8 @@
|
|||||||
#include "terrainstorage.hpp"
|
#include "terrainstorage.hpp"
|
||||||
|
|
||||||
#include <components/esm3/loadland.hpp>
|
#include <components/esm3/loadland.hpp>
|
||||||
|
#include <components/esm4/loadltex.hpp>
|
||||||
|
#include <components/esm4/loadtxst.hpp>
|
||||||
#include <components/esm4/loadwrld.hpp>
|
#include <components/esm4/loadwrld.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
@ -111,4 +113,15 @@ namespace MWRender
|
|||||||
return esmStore.get<ESM::LandTexture>().search(index, plugin);
|
return esmStore.get<ESM::LandTexture>().search(index, plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ESM4::LandTexture* TerrainStorage::getEsm4LandTexture(ESM::RefId ltexId) const
|
||||||
|
{
|
||||||
|
const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore();
|
||||||
|
return esmStore.get<ESM4::LandTexture>().search(ltexId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ESM4::TextureSet* TerrainStorage::getEsm4TextureSet(ESM::RefId txstId) const
|
||||||
|
{
|
||||||
|
const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore();
|
||||||
|
return esmStore.get<ESM4::TextureSet>().search(txstId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,9 @@ namespace MWRender
|
|||||||
osg::ref_ptr<const ESMTerrain::LandObject> getLand(ESM::ExteriorCellLocation cellLocation) override;
|
osg::ref_ptr<const ESMTerrain::LandObject> getLand(ESM::ExteriorCellLocation cellLocation) override;
|
||||||
const std::string* getLandTexture(std::uint16_t index, int plugin) override;
|
const std::string* getLandTexture(std::uint16_t index, int plugin) override;
|
||||||
|
|
||||||
|
const ESM4::LandTexture* getEsm4LandTexture(ESM::RefId ltexId) const override;
|
||||||
|
const ESM4::TextureSet* getEsm4TextureSet(ESM::RefId txstId) const override;
|
||||||
|
|
||||||
bool hasData(ESM::ExteriorCellLocation cellLocation) override;
|
bool hasData(ESM::ExteriorCellLocation cellLocation) override;
|
||||||
|
|
||||||
/// Get bounds of the whole terrain in cell units
|
/// Get bounds of the whole terrain in cell units
|
||||||
|
@ -110,6 +110,7 @@ namespace ESM4
|
|||||||
struct Static;
|
struct Static;
|
||||||
struct StaticCollection;
|
struct StaticCollection;
|
||||||
struct Terminal;
|
struct Terminal;
|
||||||
|
struct TextureSet;
|
||||||
struct Tree;
|
struct Tree;
|
||||||
struct Weapon;
|
struct Weapon;
|
||||||
struct World;
|
struct World;
|
||||||
@ -149,7 +150,7 @@ namespace MWWorld
|
|||||||
Store<ESM4::LevelledNpc>, Store<ESM4::Light>, Store<ESM4::MiscItem>, Store<ESM4::MovableStatic>,
|
Store<ESM4::LevelledNpc>, Store<ESM4::Light>, Store<ESM4::MiscItem>, Store<ESM4::MovableStatic>,
|
||||||
Store<ESM4::Npc>, Store<ESM4::Outfit>, Store<ESM4::Potion>, Store<ESM4::Race>, Store<ESM4::Reference>,
|
Store<ESM4::Npc>, Store<ESM4::Outfit>, Store<ESM4::Potion>, Store<ESM4::Race>, Store<ESM4::Reference>,
|
||||||
Store<ESM4::Sound>, Store<ESM4::SoundReference>, Store<ESM4::Static>, Store<ESM4::StaticCollection>,
|
Store<ESM4::Sound>, Store<ESM4::SoundReference>, Store<ESM4::Static>, Store<ESM4::StaticCollection>,
|
||||||
Store<ESM4::Terminal>, Store<ESM4::Tree>, Store<ESM4::Weapon>, Store<ESM4::World>>;
|
Store<ESM4::Terminal>, Store<ESM4::TextureSet>, Store<ESM4::Tree>, Store<ESM4::Weapon>, Store<ESM4::World>>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -1354,6 +1354,7 @@ template class MWWorld::TypedDynamicStore<ESM4::SoundReference>;
|
|||||||
template class MWWorld::TypedDynamicStore<ESM4::Static>;
|
template class MWWorld::TypedDynamicStore<ESM4::Static>;
|
||||||
template class MWWorld::TypedDynamicStore<ESM4::StaticCollection>;
|
template class MWWorld::TypedDynamicStore<ESM4::StaticCollection>;
|
||||||
template class MWWorld::TypedDynamicStore<ESM4::Terminal>;
|
template class MWWorld::TypedDynamicStore<ESM4::Terminal>;
|
||||||
|
template class MWWorld::TypedDynamicStore<ESM4::TextureSet>;
|
||||||
template class MWWorld::TypedDynamicStore<ESM4::Tree>;
|
template class MWWorld::TypedDynamicStore<ESM4::Tree>;
|
||||||
template class MWWorld::TypedDynamicStore<ESM4::Weapon>;
|
template class MWWorld::TypedDynamicStore<ESM4::Weapon>;
|
||||||
template class MWWorld::TypedDynamicStore<ESM4::World>;
|
template class MWWorld::TypedDynamicStore<ESM4::World>;
|
||||||
|
@ -31,6 +31,7 @@ namespace ESM
|
|||||||
VER_134 = 0x3fab851f, // FONV, GunRunnersArsenal, LonesomeRoad, OldWorldBlues
|
VER_134 = 0x3fab851f, // FONV, GunRunnersArsenal, LonesomeRoad, OldWorldBlues
|
||||||
VER_094 = 0x3f70a3d7, // TES5/FO3
|
VER_094 = 0x3f70a3d7, // TES5/FO3
|
||||||
VER_170 = 0x3fd9999a, // TES5
|
VER_170 = 0x3fd9999a, // TES5
|
||||||
|
VER_171 = 0x3fdae148, // TES5
|
||||||
VER_095 = 0x3f733333, // FO4
|
VER_095 = 0x3f733333, // FO4
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ ESM::LandData::LandData(const ESM::Land& land, int loadFlags)
|
|||||||
, mNormals(mData->mNormals)
|
, mNormals(mData->mNormals)
|
||||||
, mColors(mData->mColours)
|
, mColors(mData->mColours)
|
||||||
, mTextures(mData->mTextures)
|
, mTextures(mData->mTextures)
|
||||||
|
, mIsEsm4(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,9 +44,11 @@ ESM::LandData::LandData(const ESM4::Land& land, int /*loadFlags*/)
|
|||||||
, mMaxHeight(std::numeric_limits<float>::lowest())
|
, mMaxHeight(std::numeric_limits<float>::lowest())
|
||||||
, mSize(Constants::ESM4CellSizeInUnits)
|
, mSize(Constants::ESM4CellSizeInUnits)
|
||||||
, mLandSize(ESM4::Land::sVertsPerSide)
|
, mLandSize(ESM4::Land::sVertsPerSide)
|
||||||
|
, mPlugin(land.mId.mContentFile)
|
||||||
, mNormals(land.mVertNorm)
|
, mNormals(land.mVertNorm)
|
||||||
, mColors(land.mVertColr)
|
, mColors(land.mVertColr)
|
||||||
, mTextures(textures)
|
, mTextures(textures)
|
||||||
|
, mIsEsm4(true)
|
||||||
{
|
{
|
||||||
float rowOffset = land.mHeightMap.heightOffset;
|
float rowOffset = land.mHeightMap.heightOffset;
|
||||||
for (int y = 0; y < mLandSize; y++)
|
for (int y = 0; y < mLandSize; y++)
|
||||||
@ -69,6 +72,9 @@ ESM::LandData::LandData(const ESM4::Land& land, int /*loadFlags*/)
|
|||||||
}
|
}
|
||||||
|
|
||||||
mHeights = mHeightsData;
|
mHeights = mHeightsData;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
mEsm4Textures[i] = land.mTextures[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
#ifndef COMPONENTS_ESM_ESMTERRAIN
|
#ifndef COMPONENTS_ESM_ESMTERRAIN
|
||||||
#define COMPONENTS_ESM_ESMTERRAIN
|
#define COMPONENTS_ESM_ESMTERRAIN
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace ESM4
|
#include <components/esm4/loadland.hpp>
|
||||||
{
|
|
||||||
struct Land;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
@ -28,7 +26,6 @@ namespace ESM
|
|||||||
std::span<const float> getHeights() const { return mHeights; }
|
std::span<const float> getHeights() const { return mHeights; }
|
||||||
std::span<const std::int8_t> getNormals() const { return mNormals; }
|
std::span<const std::int8_t> getNormals() const { return mNormals; }
|
||||||
std::span<const std::uint8_t> getColors() const { return mColors; }
|
std::span<const std::uint8_t> getColors() const { return mColors; }
|
||||||
std::span<const std::uint16_t> getTextures() const { return mTextures; }
|
|
||||||
float getSize() const { return mSize; }
|
float getSize() const { return mSize; }
|
||||||
float getMinHeight() const { return mMinHeight; }
|
float getMinHeight() const { return mMinHeight; }
|
||||||
float getMaxHeight() const { return mMaxHeight; }
|
float getMaxHeight() const { return mMaxHeight; }
|
||||||
@ -36,6 +33,22 @@ namespace ESM
|
|||||||
int getLoadFlags() const { return mLoadFlags; }
|
int getLoadFlags() const { return mLoadFlags; }
|
||||||
int getPlugin() const { return mPlugin; }
|
int getPlugin() const { return mPlugin; }
|
||||||
|
|
||||||
|
bool isEsm4() const { return mIsEsm4; }
|
||||||
|
|
||||||
|
std::span<const std::uint16_t> getTextures() const
|
||||||
|
{
|
||||||
|
if (mIsEsm4)
|
||||||
|
throw std::logic_error("ESM3 textures requested from ESM4 LandData");
|
||||||
|
return mTextures;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ESM4::Land::Texture& getEsm4Texture(std::size_t quad) const
|
||||||
|
{
|
||||||
|
if (!mIsEsm4)
|
||||||
|
throw std::logic_error("ESM4 texture requested from ESM3 LandData");
|
||||||
|
return mEsm4Textures[quad];
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<const ESM::LandRecordData> mData;
|
std::unique_ptr<const ESM::LandRecordData> mData;
|
||||||
int mLoadFlags = 0;
|
int mLoadFlags = 0;
|
||||||
@ -49,6 +62,8 @@ namespace ESM
|
|||||||
std::span<const std::int8_t> mNormals;
|
std::span<const std::int8_t> mNormals;
|
||||||
std::span<const std::uint8_t> mColors;
|
std::span<const std::uint8_t> mColors;
|
||||||
std::span<const std::uint16_t> mTextures;
|
std::span<const std::uint16_t> mTextures;
|
||||||
|
std::array<ESM4::Land::Texture, 4> mEsm4Textures;
|
||||||
|
bool mIsEsm4;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,7 @@
|
|||||||
#include <components/esm4/loadstat.hpp>
|
#include <components/esm4/loadstat.hpp>
|
||||||
#include <components/esm4/loadterm.hpp>
|
#include <components/esm4/loadterm.hpp>
|
||||||
#include <components/esm4/loadtree.hpp>
|
#include <components/esm4/loadtree.hpp>
|
||||||
|
#include <components/esm4/loadtxst.hpp>
|
||||||
#include <components/esm4/loadweap.hpp>
|
#include <components/esm4/loadweap.hpp>
|
||||||
#include <components/esm4/loadwrld.hpp>
|
#include <components/esm4/loadwrld.hpp>
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (C) 2015-2016, 2018, 2020-2021 cc9cii
|
Copyright (C) 2015 - 2024 cc9cii
|
||||||
|
|
||||||
This software is provided 'as-is', without any express or implied
|
This software is provided 'as-is', without any express or implied
|
||||||
warranty. In no event will the authors be held liable for any damages
|
warranty. In no event will the authors be held liable for any damages
|
||||||
@ -17,7 +17,7 @@
|
|||||||
misrepresented as being the original software.
|
misrepresented as being the original software.
|
||||||
3. This notice may not be removed or altered from any source distribution.
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
|
||||||
cc9cii cc9c@iinet.net.au
|
cc9cii cc9cii@hotmail.com
|
||||||
|
|
||||||
Much of the information on the data structures are based on the information
|
Much of the information on the data structures are based on the information
|
||||||
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
|
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
|
||||||
@ -26,13 +26,51 @@
|
|||||||
*/
|
*/
|
||||||
#include "loadland.hpp"
|
#include "loadland.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
#include "reader.hpp"
|
#include "reader.hpp"
|
||||||
// #include "writer.hpp"
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void assignDefaultTextures(ESM4::Land& land, ESM4::Reader& reader)
|
||||||
|
{
|
||||||
|
std::uint32_t esmVer = reader.esmVersion();
|
||||||
|
|
||||||
|
// Note: in games after TES4 it can be configured in ini file (sDefaultLandDiffuseTexture)
|
||||||
|
if (!reader.hasFormVersion() && (esmVer == ESM::VER_080 || esmVer == ESM::VER_100)) // TES4
|
||||||
|
{
|
||||||
|
land.mDefaultDiffuseMap = VFS::Path::NormalizedView("textures/landscape/terrainhddirt01.dds");
|
||||||
|
land.mDefaultNormalMap = VFS::Path::NormalizedView("textures/landscape/terrainhddirt01_n.dds");
|
||||||
|
}
|
||||||
|
else if (reader.hasFormVersion() && reader.formVersion() >= 16
|
||||||
|
&& (esmVer == ESM::VER_094 || esmVer == ESM::VER_170 || esmVer == ESM::VER_171)) // TES5
|
||||||
|
{
|
||||||
|
land.mDefaultDiffuseMap = VFS::Path::NormalizedView("textures/landscape/dirt02.dds");
|
||||||
|
land.mDefaultNormalMap = VFS::Path::NormalizedView("textures/landscape/dirt02_n.dds");
|
||||||
|
}
|
||||||
|
else if (esmVer == ESM::VER_095 || esmVer == ESM::VER_100) // FO4
|
||||||
|
{
|
||||||
|
land.mDefaultDiffuseMap
|
||||||
|
= VFS::Path::NormalizedView("textures/landscape/ground/commonwealthdefault01_d.dds");
|
||||||
|
land.mDefaultNormalMap = VFS::Path::NormalizedView("textures/landscape/ground/commonwealthdefault01_n.dds");
|
||||||
|
}
|
||||||
|
else if (esmVer == ESM::VER_094 || esmVer == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::VER_134)
|
||||||
|
{ // FO3, FONV
|
||||||
|
land.mDefaultDiffuseMap = VFS::Path::NormalizedView("textures/landscape/dirtwasteland01.dds");
|
||||||
|
land.mDefaultNormalMap = VFS::Path::NormalizedView("textures/landscape/dirtwasteland01_n.dds");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Nothing especially bad happens if default texture is not set (except of the missing texture of course),
|
||||||
|
// but we throw an error because this case is unexpected and detection logic needs to be updated.
|
||||||
|
throw std::runtime_error("ESM4::Land unknown ESM version");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// overlap north
|
// overlap north
|
||||||
//
|
//
|
||||||
@ -53,12 +91,16 @@ void ESM4::Land::load(ESM4::Reader& reader)
|
|||||||
{
|
{
|
||||||
mId = reader.getFormIdFromHeader();
|
mId = reader.getFormIdFromHeader();
|
||||||
mFlags = reader.hdr().record.flags;
|
mFlags = reader.hdr().record.flags;
|
||||||
|
|
||||||
mDataTypes = 0;
|
mDataTypes = 0;
|
||||||
mCell = reader.currCell();
|
mCell = reader.currCell();
|
||||||
TxtLayer layer;
|
TxtLayer layer;
|
||||||
std::int8_t currentAddQuad = -1; // for VTXT following ATXT
|
std::int8_t currentAddQuad = -1; // for VTXT following ATXT
|
||||||
|
assignDefaultTextures(*this, reader);
|
||||||
|
|
||||||
// std::map<FormId, int> uniqueTextures; // FIXME: for temp testing only
|
layer.texture.formId = 0;
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
mTextures[i].base.formId = 0;
|
||||||
|
|
||||||
while (reader.getSubRecordHeader())
|
while (reader.getSubRecordHeader())
|
||||||
{
|
{
|
||||||
@ -78,12 +120,6 @@ void ESM4::Land::load(ESM4::Reader& reader)
|
|||||||
}
|
}
|
||||||
case ESM::fourCC("VHGT"): // vertex height gradient, 4+33x33+3 = 4+1089+3 = 1096
|
case ESM::fourCC("VHGT"): // vertex height gradient, 4+33x33+3 = 4+1089+3 = 1096
|
||||||
{
|
{
|
||||||
#if 0
|
|
||||||
reader.get(mHeightMap.heightOffset);
|
|
||||||
reader.get(mHeightMap.gradientData);
|
|
||||||
reader.get(mHeightMap.unknown1);
|
|
||||||
reader.get(mHeightMap.unknown2);
|
|
||||||
#endif
|
|
||||||
reader.get(mHeightMap);
|
reader.get(mHeightMap);
|
||||||
mDataTypes |= LAND_VHGT;
|
mDataTypes |= LAND_VHGT;
|
||||||
break;
|
break;
|
||||||
@ -102,13 +138,9 @@ void ESM4::Land::load(ESM4::Reader& reader)
|
|||||||
if (base.quadrant >= 4)
|
if (base.quadrant >= 4)
|
||||||
throw std::runtime_error("base texture quadrant index error");
|
throw std::runtime_error("base texture quadrant index error");
|
||||||
|
|
||||||
reader.adjustFormId(base.formId);
|
if (base.formId != 0)
|
||||||
mTextures[base.quadrant].base = std::move(base);
|
reader.adjustFormId(base.formId);
|
||||||
#if 0
|
mTextures[base.quadrant].base = base;
|
||||||
std::cout << "Base Texture formid: 0x"
|
|
||||||
<< std::hex << mTextures[base.quadrant].base.formId
|
|
||||||
<< ", quad " << std::dec << (int)base.quadrant << std::endl;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -116,31 +148,23 @@ void ESM4::Land::load(ESM4::Reader& reader)
|
|||||||
{
|
{
|
||||||
if (currentAddQuad != -1)
|
if (currentAddQuad != -1)
|
||||||
{
|
{
|
||||||
// FIXME: sometimes there are no VTXT following an ATXT? Just add a dummy one for now
|
// NOTE: sometimes there are no VTXT following an ATXT
|
||||||
Log(Debug::Verbose) << "ESM4::Land VTXT empty layer " << layer.texture.layerIndex;
|
layer.data.resize(1); // just one spot
|
||||||
|
layer.data.back().position = 0; // this corner
|
||||||
|
layer.data.back().opacity = 0.f; // transparent
|
||||||
|
|
||||||
|
if (layer.texture.layerIndex != mTextures[currentAddQuad].layers.size())
|
||||||
|
throw std::runtime_error("ESM4::LAND additional texture skipping layer");
|
||||||
|
|
||||||
mTextures[currentAddQuad].layers.push_back(layer);
|
mTextures[currentAddQuad].layers.push_back(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.get(layer.texture);
|
reader.get(layer.texture);
|
||||||
reader.adjustFormId(layer.texture.formId);
|
if (layer.texture.formId != 0)
|
||||||
|
reader.adjustFormId(layer.texture.formId);
|
||||||
if (layer.texture.quadrant >= 4)
|
if (layer.texture.quadrant >= 4)
|
||||||
throw std::runtime_error("additional texture quadrant index error");
|
throw std::runtime_error("ESM4::LAND additional texture quadrant index error");
|
||||||
#if 0
|
|
||||||
FormId txt = layer.texture.formId;
|
|
||||||
std::map<FormId, int>::iterator lb = uniqueTextures.lower_bound(txt);
|
|
||||||
if (lb != uniqueTextures.end() && !(uniqueTextures.key_comp()(txt, lb->first)))
|
|
||||||
{
|
|
||||||
lb->second += 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
uniqueTextures.insert(lb, std::make_pair(txt, 1));
|
|
||||||
#endif
|
|
||||||
#if 0
|
|
||||||
std::cout << "Additional Texture formId: 0x"
|
|
||||||
<< std::hex << layer.texture.formId
|
|
||||||
<< ", quad " << std::dec << (int)layer.texture.quadrant << std::endl;
|
|
||||||
std::cout << "Additional Texture layer: "
|
|
||||||
<< std::dec << (int)layer.texture.layerIndex << std::endl;
|
|
||||||
#endif
|
|
||||||
currentAddQuad = layer.texture.quadrant;
|
currentAddQuad = layer.texture.quadrant;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -156,25 +180,17 @@ void ESM4::Land::load(ESM4::Reader& reader)
|
|||||||
if (count)
|
if (count)
|
||||||
{
|
{
|
||||||
layer.data.resize(count);
|
layer.data.resize(count);
|
||||||
std::vector<ESM4::Land::VTXT>::iterator it = layer.data.begin();
|
for (ESM4::Land::VTXT& vtxt : layer.data)
|
||||||
for (; it != layer.data.end(); ++it)
|
reader.get(vtxt);
|
||||||
{
|
|
||||||
reader.get(*it);
|
|
||||||
// FIXME: debug only
|
|
||||||
// std::cout << "pos: " << std::dec << (int)(*it).position << std::endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mTextures[currentAddQuad].layers.push_back(layer);
|
|
||||||
|
|
||||||
// Assumed that the layers are added in the correct sequence
|
if (layer.texture.layerIndex != mTextures[currentAddQuad].layers.size())
|
||||||
// FIXME: Knights.esp doesn't seem to observe this - investigate more
|
throw std::runtime_error("ESM4::LAND additional texture skipping layer");
|
||||||
// assert(layer.texture.layerIndex == mTextures[currentAddQuad].layers.size()-1
|
|
||||||
//&& "additional texture layer index error");
|
mTextures[currentAddQuad].layers.push_back(layer);
|
||||||
|
|
||||||
currentAddQuad = -1;
|
currentAddQuad = -1;
|
||||||
layer.data.clear();
|
layer.data.clear();
|
||||||
// FIXME: debug only
|
|
||||||
// std::cout << "VTXT: count " << std::dec << count << std::endl;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESM::fourCC("VTEX"): // only in Oblivion?
|
case ESM::fourCC("VTEX"): // only in Oblivion?
|
||||||
@ -195,44 +211,14 @@ void ESM4::Land::load(ESM4::Reader& reader)
|
|||||||
reader.skipSubRecordData();
|
reader.skipSubRecordData();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("ESM4::LAND::load - Unknown subrecord " + ESM::printName(subHdr.typeId));
|
throw std::runtime_error("ESM4::LAND - Unknown subrecord " + ESM::printName(subHdr.typeId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentAddQuad != -1)
|
if (currentAddQuad != -1)
|
||||||
{
|
{
|
||||||
// FIXME: not sure if it happens here as well
|
// not sure if it happens here as well, if so just ignore
|
||||||
Log(Debug::Verbose) << "ESM4::Land VTXT empty layer " << layer.texture.layerIndex << " quad "
|
Log(Debug::Verbose) << "ESM4::Land VTXT empty layer " << layer.texture.layerIndex << " quad "
|
||||||
<< static_cast<unsigned>(layer.texture.quadrant);
|
<< static_cast<unsigned>(layer.texture.quadrant);
|
||||||
mTextures[currentAddQuad].layers.push_back(layer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool missing = false;
|
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
{
|
|
||||||
if (mTextures[i].base.formId == 0)
|
|
||||||
{
|
|
||||||
// std::cout << "ESM4::LAND " << ESM4::formIdToString(mFormId) << " missing base, quad " << i << std::endl;
|
|
||||||
// std::cout << "layers " << mTextures[i].layers.size() << std::endl;
|
|
||||||
// NOTE: can't set the default here since FO3/FONV may have different defaults
|
|
||||||
// mTextures[i].base.formId = 0x000008C0; // TerrainHDDirt01.dds
|
|
||||||
missing = true;
|
|
||||||
}
|
|
||||||
// else
|
|
||||||
//{
|
|
||||||
// std::cout << "ESM4::LAND " << ESM4::formIdToString(mFormId) << " base, quad " << i << std::endl;
|
|
||||||
// std::cout << "layers " << mTextures[i].layers.size() << std::endl;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
// at least one of the quadrants do not have a base texture, return without setting the flag
|
|
||||||
if (!missing)
|
|
||||||
mDataTypes |= LAND_VTEX;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// void ESM4::Land::save(ESM4::Writer& writer) const
|
|
||||||
//{
|
|
||||||
// }
|
|
||||||
|
|
||||||
// void ESM4::Land::blank()
|
|
||||||
//{
|
|
||||||
// }
|
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include <components/esm/defs.hpp>
|
#include <components/esm/defs.hpp>
|
||||||
#include <components/esm/formid.hpp>
|
#include <components/esm/formid.hpp>
|
||||||
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
namespace ESM4
|
namespace ESM4
|
||||||
{
|
{
|
||||||
@ -124,6 +125,8 @@ namespace ESM4
|
|||||||
Texture mTextures[4]; // 0 = bottom left, 1 = bottom right, 2 = top left, 3 = top right
|
Texture mTextures[4]; // 0 = bottom left, 1 = bottom right, 2 = top left, 3 = top right
|
||||||
std::vector<ESM::FormId> mIds; // land texture (LTEX) formids
|
std::vector<ESM::FormId> mIds; // land texture (LTEX) formids
|
||||||
ESM::FormId mCell;
|
ESM::FormId mCell;
|
||||||
|
VFS::Path::NormalizedView mDefaultDiffuseMap;
|
||||||
|
VFS::Path::NormalizedView mDefaultNormalMap;
|
||||||
|
|
||||||
void load(Reader& reader);
|
void load(Reader& reader);
|
||||||
|
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
#include <components/esm/util.hpp>
|
#include <components/esm/util.hpp>
|
||||||
#include <components/esm3/loadland.hpp>
|
#include <components/esm3/loadland.hpp>
|
||||||
#include <components/esm4/loadland.hpp>
|
#include <components/esm4/loadland.hpp>
|
||||||
|
#include <components/esm4/loadltex.hpp>
|
||||||
|
#include <components/esm4/loadtxst.hpp>
|
||||||
#include <components/misc/resourcehelpers.hpp>
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
#include <components/misc/strings/algorithm.hpp>
|
#include <components/misc/strings/algorithm.hpp>
|
||||||
#include <components/vfs/manager.hpp>
|
#include <components/vfs/manager.hpp>
|
||||||
@ -89,6 +91,8 @@ namespace ESMTerrain
|
|||||||
LandObject::LandObject(const ESM4::Land& land, int loadFlags)
|
LandObject::LandObject(const ESM4::Land& land, int loadFlags)
|
||||||
: mData(land, loadFlags)
|
: mData(land, loadFlags)
|
||||||
{
|
{
|
||||||
|
mEsm4DefaultLayerInfo.mDiffuseMap = land.mDefaultDiffuseMap;
|
||||||
|
mEsm4DefaultLayerInfo.mNormalMap = land.mDefaultNormalMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
LandObject::LandObject(const ESM::Land& land, int loadFlags)
|
LandObject::LandObject(const ESM::Land& land, int loadFlags)
|
||||||
@ -385,9 +389,105 @@ namespace ESMTerrain
|
|||||||
return Misc::ResourceHelpers::correctTexturePath(texture, mVFS);
|
return Misc::ResourceHelpers::correctTexturePath(texture, mVFS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Storage::getEsm4Blendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,
|
||||||
|
std::vector<Terrain::LayerInfo>& layerList, ESM::RefId worldspace)
|
||||||
|
{
|
||||||
|
const osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize - 1, chunkSize + 1) * 0.5f;
|
||||||
|
const int startCellX = static_cast<int>(std::floor(origin.x()));
|
||||||
|
const int startCellY = static_cast<int>(std::floor(origin.y()));
|
||||||
|
|
||||||
|
constexpr int quadsPerCell = 2;
|
||||||
|
constexpr int quadSize = ESM4::Land::sVertsPerSide / quadsPerCell;
|
||||||
|
const int quadCount = static_cast<int>(chunkSize * quadsPerCell);
|
||||||
|
assert(quadCount > 0);
|
||||||
|
|
||||||
|
const int blendmapSize = quadCount * quadSize + 1;
|
||||||
|
|
||||||
|
LandCache cache(startCellX - 1, startCellY - 1, static_cast<std::size_t>(std::ceil(chunkSize)) + 2);
|
||||||
|
std::pair lastCell{ startCellX, startCellY };
|
||||||
|
const LandObject* land = getLand(ESM::ExteriorCellLocation(startCellX, startCellY, worldspace), cache);
|
||||||
|
|
||||||
|
std::map<ESM::FormId, std::size_t> textureIndicesMap;
|
||||||
|
|
||||||
|
auto getOrCreateBlendmap = [&](ESM::FormId texId) -> unsigned char* {
|
||||||
|
auto found = textureIndicesMap.find(texId);
|
||||||
|
if (found != textureIndicesMap.end())
|
||||||
|
return blendmaps[found->second]->data();
|
||||||
|
Terrain::LayerInfo info
|
||||||
|
= texId.isZeroOrUnset() ? land->getEsm4DefaultLayerInfo() : getLandTextureLayerInfo(texId);
|
||||||
|
osg::ref_ptr<osg::Image> image(new osg::Image);
|
||||||
|
image->allocateImage(blendmapSize, blendmapSize, 1, GL_ALPHA, GL_UNSIGNED_BYTE);
|
||||||
|
std::memset(image->data(), 0, image->getTotalDataSize());
|
||||||
|
textureIndicesMap.emplace(texId, blendmaps.size());
|
||||||
|
blendmaps.push_back(std::move(image));
|
||||||
|
layerList.push_back(std::move(info));
|
||||||
|
return blendmaps.back()->data();
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto handleSample = [&](const CellSample& sample) {
|
||||||
|
const std::pair cell{ sample.mCellX, sample.mCellY };
|
||||||
|
if (lastCell != cell)
|
||||||
|
{
|
||||||
|
land = getLand(ESM::ExteriorCellLocation(sample.mCellX, sample.mCellY, worldspace), cache);
|
||||||
|
lastCell = cell;
|
||||||
|
}
|
||||||
|
if (!land)
|
||||||
|
return;
|
||||||
|
const ESM::LandData* ldata = land->getData(0);
|
||||||
|
if (!ldata)
|
||||||
|
return;
|
||||||
|
int quad;
|
||||||
|
if (sample.mSrcRow == 0)
|
||||||
|
quad = sample.mSrcCol == 0 ? 0 : 2;
|
||||||
|
else
|
||||||
|
quad = sample.mSrcCol == 0 ? 1 : 3;
|
||||||
|
const ESM4::Land::Texture& ltex = ldata->getEsm4Texture(quad);
|
||||||
|
|
||||||
|
unsigned char* const baseBlendmap = getOrCreateBlendmap(ESM::FormId::fromUint32(ltex.base.formId));
|
||||||
|
int starty = (static_cast<int>(sample.mDstCol) - 1) * quadSize;
|
||||||
|
int startx = sample.mDstRow * quadSize;
|
||||||
|
for (int y = std::max(0, starty + 1); y <= starty + quadSize && y < blendmapSize; ++y)
|
||||||
|
{
|
||||||
|
unsigned char* const row = baseBlendmap + (blendmapSize - y - 1) * blendmapSize;
|
||||||
|
for (int x = startx; x < startx + quadSize && x < blendmapSize; ++x)
|
||||||
|
row[x] = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& layer : ltex.layers)
|
||||||
|
{
|
||||||
|
unsigned char* const layerBlendmap = getOrCreateBlendmap(ESM::FormId::fromUint32(layer.texture.formId));
|
||||||
|
for (const ESM4::Land::VTXT& v : layer.data)
|
||||||
|
{
|
||||||
|
int y = v.position / (quadSize + 1);
|
||||||
|
int x = v.position % (quadSize + 1);
|
||||||
|
if (x == quadSize || startx + x >= blendmapSize || y == 0 || starty + y >= blendmapSize
|
||||||
|
|| starty + y < 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int index = (blendmapSize - starty - y - 1) * blendmapSize + startx + x;
|
||||||
|
int delta = std::clamp(static_cast<int>(v.opacity * 255), 0, 255);
|
||||||
|
baseBlendmap[index] = std::max(0, baseBlendmap[index] - delta);
|
||||||
|
layerBlendmap[index] = delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
sampleBlendmaps(chunkSize, origin.x(), origin.y(), quadsPerCell, handleSample);
|
||||||
|
|
||||||
|
if (blendmaps.size() == 1)
|
||||||
|
blendmaps.clear(); // If a single texture fills the whole terrain, there is no need to blend
|
||||||
|
}
|
||||||
|
|
||||||
void Storage::getBlendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,
|
void Storage::getBlendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,
|
||||||
std::vector<Terrain::LayerInfo>& layerList, ESM::RefId worldspace)
|
std::vector<Terrain::LayerInfo>& layerList, ESM::RefId worldspace)
|
||||||
{
|
{
|
||||||
|
if (ESM::isEsm4Ext(worldspace))
|
||||||
|
{
|
||||||
|
getEsm4Blendmaps(chunkSize, chunkCenter, blendmaps, layerList, worldspace);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize, chunkSize) * 0.5f;
|
const osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize, chunkSize) * 0.5f;
|
||||||
const int startCellX = static_cast<int>(std::floor(origin.x()));
|
const int startCellX = static_cast<int>(std::floor(origin.x()));
|
||||||
const int startCellY = static_cast<int>(std::floor(origin.y()));
|
const int startCellY = static_cast<int>(std::floor(origin.y()));
|
||||||
@ -613,6 +713,47 @@ namespace ESMTerrain
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Terrain::LayerInfo Storage::getLandTextureLayerInfo(ESM::FormId id)
|
||||||
|
{
|
||||||
|
if (const ESM4::LandTexture* ltex = getEsm4LandTexture(id))
|
||||||
|
{
|
||||||
|
if (!ltex->mTextureFile.empty())
|
||||||
|
return getLayerInfo("textures/landscape/" + ltex->mTextureFile); // TES4
|
||||||
|
if (const ESM4::TextureSet* txst = getEsm4TextureSet(ltex->mTexture))
|
||||||
|
return getTextureSetLayerInfo(*txst); // TES5
|
||||||
|
else
|
||||||
|
Log(Debug::Warning) << "TextureSet not found: " << ltex->mTexture.toString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Log(Debug::Warning) << "LandTexture not found: " << id.toString();
|
||||||
|
return getLayerInfo("");
|
||||||
|
}
|
||||||
|
|
||||||
|
Terrain::LayerInfo Storage::getTextureSetLayerInfo(const ESM4::TextureSet& txst)
|
||||||
|
{
|
||||||
|
Terrain::LayerInfo info;
|
||||||
|
|
||||||
|
assert(!txst.mDiffuse.empty() && "getlayerInfo: empty diffuse map");
|
||||||
|
info.mDiffuseMap = "textures/" + txst.mDiffuse;
|
||||||
|
|
||||||
|
if (!txst.mNormalMap.empty())
|
||||||
|
info.mNormalMap = "textures/" + txst.mNormalMap;
|
||||||
|
|
||||||
|
// FIXME: this flag indicates height info in alpha channel of normal map
|
||||||
|
// but the normal map alpha channel has specular info instead
|
||||||
|
// (probably needs some flag in the terrain shader to fix)
|
||||||
|
info.mParallax = false;
|
||||||
|
// FIXME: this flag indicates specular info in alpha channel of diffuse
|
||||||
|
// but the diffuse alpha channel has transparency data instead
|
||||||
|
// (probably needs some flag in the terrain shader to fix)
|
||||||
|
info.mSpecular = false;
|
||||||
|
|
||||||
|
// FIXME: should support other features of ESM4::TextureSet
|
||||||
|
// probably need corresponding support in the terrain shader
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
float Storage::getCellWorldSize(ESM::RefId worldspace)
|
float Storage::getCellWorldSize(ESM::RefId worldspace)
|
||||||
{
|
{
|
||||||
return static_cast<float>(ESM::getCellSize(worldspace));
|
return static_cast<float>(ESM::getCellSize(worldspace));
|
||||||
@ -623,9 +764,12 @@ namespace ESMTerrain
|
|||||||
return ESM::getLandSize(worldspace);
|
return ESM::getLandSize(worldspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Storage::getBlendmapScale(float chunkSize)
|
int Storage::getTextureTileCount(float chunkSize, ESM::RefId worldspace)
|
||||||
{
|
{
|
||||||
return ESM::Land::LAND_TEXTURE_SIZE * chunkSize;
|
if (ESM::isEsm4Ext(worldspace))
|
||||||
|
return static_cast<int>(2 * ESM4::Land::sQuadTexturePerSide * chunkSize);
|
||||||
|
else
|
||||||
|
return static_cast<int>(ESM::Land::LAND_TEXTURE_SIZE * chunkSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <components/terrain/defs.hpp>
|
||||||
#include <components/terrain/storage.hpp>
|
#include <components/terrain/storage.hpp>
|
||||||
|
|
||||||
#include <components/esm/esmterrain.hpp>
|
#include <components/esm/esmterrain.hpp>
|
||||||
@ -13,6 +14,8 @@
|
|||||||
namespace ESM4
|
namespace ESM4
|
||||||
{
|
{
|
||||||
struct Land;
|
struct Land;
|
||||||
|
struct LandTexture;
|
||||||
|
struct TextureSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
@ -51,9 +54,13 @@ namespace ESMTerrain
|
|||||||
|
|
||||||
int getPlugin() const { return mData.getPlugin(); }
|
int getPlugin() const { return mData.getPlugin(); }
|
||||||
|
|
||||||
|
const Terrain::LayerInfo& getEsm4DefaultLayerInfo() const { return mEsm4DefaultLayerInfo; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ESM::LandData mData;
|
ESM::LandData mData;
|
||||||
|
|
||||||
|
Terrain::LayerInfo mEsm4DefaultLayerInfo;
|
||||||
|
|
||||||
LandObject(const LandObject& copy, const osg::CopyOp& copyOp);
|
LandObject(const LandObject& copy, const osg::CopyOp& copyOp);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -74,6 +81,11 @@ namespace ESMTerrain
|
|||||||
// Not implemented in this class, because we need different Store implementations for game and editor
|
// Not implemented in this class, because we need different Store implementations for game and editor
|
||||||
virtual osg::ref_ptr<const LandObject> getLand(ESM::ExteriorCellLocation cellLocation) = 0;
|
virtual osg::ref_ptr<const LandObject> getLand(ESM::ExteriorCellLocation cellLocation) = 0;
|
||||||
virtual const std::string* getLandTexture(std::uint16_t index, int plugin) = 0;
|
virtual const std::string* getLandTexture(std::uint16_t index, int plugin) = 0;
|
||||||
|
|
||||||
|
// Not implemented in this class because requires ESMStore
|
||||||
|
virtual const ESM4::LandTexture* getEsm4LandTexture(ESM::RefId ltexId) const { return nullptr; }
|
||||||
|
virtual const ESM4::TextureSet* getEsm4TextureSet(ESM::RefId txstId) const { return nullptr; }
|
||||||
|
|
||||||
/// Get bounds of the whole terrain in cell units
|
/// Get bounds of the whole terrain in cell units
|
||||||
void getBounds(float& minX, float& maxX, float& minY, float& maxY, ESM::RefId worldspace) override = 0;
|
void getBounds(float& minX, float& maxX, float& minY, float& maxY, ESM::RefId worldspace) override = 0;
|
||||||
|
|
||||||
@ -120,7 +132,7 @@ namespace ESMTerrain
|
|||||||
/// Get the number of vertices on one side for each cell. Should be (power of two)+1
|
/// Get the number of vertices on one side for each cell. Should be (power of two)+1
|
||||||
int getCellVertices(ESM::RefId worldspace) override;
|
int getCellVertices(ESM::RefId worldspace) override;
|
||||||
|
|
||||||
int getBlendmapScale(float chunkSize) override;
|
int getTextureTileCount(float chunkSize, ESM::RefId worldspace) override;
|
||||||
|
|
||||||
float getVertexHeight(const ESM::LandData* data, int x, int y)
|
float getVertexHeight(const ESM::LandData* data, int x, int y)
|
||||||
{
|
{
|
||||||
@ -159,6 +171,11 @@ namespace ESMTerrain
|
|||||||
bool mAutoUseSpecularMaps;
|
bool mAutoUseSpecularMaps;
|
||||||
|
|
||||||
Terrain::LayerInfo getLayerInfo(const std::string& texture);
|
Terrain::LayerInfo getLayerInfo(const std::string& texture);
|
||||||
|
Terrain::LayerInfo getTextureSetLayerInfo(const ESM4::TextureSet& txst);
|
||||||
|
Terrain::LayerInfo getLandTextureLayerInfo(ESM::FormId id);
|
||||||
|
|
||||||
|
void getEsm4Blendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,
|
||||||
|
std::vector<Terrain::LayerInfo>& layerList, ESM::RefId worldspace);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <osgUtil/IncrementalCompileOperation>
|
#include <osgUtil/IncrementalCompileOperation>
|
||||||
|
|
||||||
|
#include <components/esm/util.hpp>
|
||||||
#include <components/resource/objectcache.hpp>
|
#include <components/resource/objectcache.hpp>
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
|
|
||||||
@ -205,10 +206,10 @@ namespace Terrain
|
|||||||
blendmapTextures.push_back(texture);
|
blendmapTextures.push_back(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
float blendmapScale = mStorage->getBlendmapScale(chunkSize);
|
float tileCount = mStorage->getTextureTileCount(chunkSize, mWorldspace);
|
||||||
|
|
||||||
return ::Terrain::createPasses(
|
return ::Terrain::createPasses(
|
||||||
useShaders, mSceneManager, layers, blendmapTextures, blendmapScale, blendmapScale);
|
useShaders, mSceneManager, layers, blendmapTextures, tileCount, tileCount, ESM::isEsm4Ext(mWorldspace));
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> ChunkManager::createChunk(float chunkSize, const osg::Vec2f& chunkCenter, unsigned char lod,
|
osg::ref_ptr<osg::Node> ChunkManager::createChunk(float chunkSize, const osg::Vec2f& chunkCenter, unsigned char lod,
|
||||||
|
@ -224,7 +224,7 @@ namespace Terrain
|
|||||||
{
|
{
|
||||||
std::vector<osg::ref_ptr<osg::StateSet>> createPasses(bool useShaders, Resource::SceneManager* sceneManager,
|
std::vector<osg::ref_ptr<osg::StateSet>> createPasses(bool useShaders, Resource::SceneManager* sceneManager,
|
||||||
const std::vector<TextureLayer>& layers, const std::vector<osg::ref_ptr<osg::Texture2D>>& blendmaps,
|
const std::vector<TextureLayer>& layers, const std::vector<osg::ref_ptr<osg::Texture2D>>& blendmaps,
|
||||||
int blendmapScale, float layerTileSize)
|
int blendmapScale, float layerTileSize, bool esm4terrain)
|
||||||
{
|
{
|
||||||
auto& shaderManager = sceneManager->getShaderManager();
|
auto& shaderManager = sceneManager->getShaderManager();
|
||||||
std::vector<osg::ref_ptr<osg::StateSet>> passes;
|
std::vector<osg::ref_ptr<osg::StateSet>> passes;
|
||||||
@ -269,7 +269,8 @@ namespace Terrain
|
|||||||
osg::ref_ptr<osg::Texture2D> blendmap = blendmaps.at(blendmapIndex++);
|
osg::ref_ptr<osg::Texture2D> blendmap = blendmaps.at(blendmapIndex++);
|
||||||
|
|
||||||
stateset->setTextureAttributeAndModes(1, blendmap.get());
|
stateset->setTextureAttributeAndModes(1, blendmap.get());
|
||||||
stateset->setTextureAttributeAndModes(1, BlendmapTexMat::value(blendmapScale));
|
if (!esm4terrain)
|
||||||
|
stateset->setTextureAttributeAndModes(1, BlendmapTexMat::value(blendmapScale));
|
||||||
stateset->addUniform(UniformCollection::value().mBlendMap);
|
stateset->addUniform(UniformCollection::value().mBlendMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,7 +330,8 @@ namespace Terrain
|
|||||||
stateset->setTextureAttributeAndModes(1, blendmap.get());
|
stateset->setTextureAttributeAndModes(1, blendmap.get());
|
||||||
|
|
||||||
// This is to map corner vertices directly to the center of a blendmap texel.
|
// This is to map corner vertices directly to the center of a blendmap texel.
|
||||||
stateset->setTextureAttributeAndModes(1, BlendmapTexMat::value(blendmapScale));
|
if (!esm4terrain)
|
||||||
|
stateset->setTextureAttributeAndModes(1, BlendmapTexMat::value(blendmapScale));
|
||||||
stateset->setTextureAttributeAndModes(1, TexEnvCombine::value(), osg::StateAttribute::ON);
|
stateset->setTextureAttributeAndModes(1, TexEnvCombine::value(), osg::StateAttribute::ON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,14 +20,13 @@ namespace Terrain
|
|||||||
{
|
{
|
||||||
osg::ref_ptr<osg::Texture2D> mDiffuseMap;
|
osg::ref_ptr<osg::Texture2D> mDiffuseMap;
|
||||||
osg::ref_ptr<osg::Texture2D> mNormalMap; // optional
|
osg::ref_ptr<osg::Texture2D> mNormalMap; // optional
|
||||||
bool mParallax;
|
bool mParallax = false;
|
||||||
bool mSpecular;
|
bool mSpecular = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<osg::ref_ptr<osg::StateSet>> createPasses(bool useShaders, Resource::SceneManager* sceneManager,
|
std::vector<osg::ref_ptr<osg::StateSet>> createPasses(bool useShaders, Resource::SceneManager* sceneManager,
|
||||||
const std::vector<TextureLayer>& layers, const std::vector<osg::ref_ptr<osg::Texture2D>>& blendmaps,
|
const std::vector<TextureLayer>& layers, const std::vector<osg::ref_ptr<osg::Texture2D>>& blendmaps,
|
||||||
int blendmapScale, float layerTileSize);
|
int blendmapScale, float layerTileSize, bool esm4terrain = false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -289,11 +289,12 @@ namespace Terrain
|
|||||||
, mLodFactor(lodFactor)
|
, mLodFactor(lodFactor)
|
||||||
, mVertexLodMod(vertexLodMod)
|
, mVertexLodMod(vertexLodMod)
|
||||||
, mViewDistance(std::numeric_limits<float>::max())
|
, mViewDistance(std::numeric_limits<float>::max())
|
||||||
, mMinSize(ESM::isEsm4Ext(worldspace) ? 1 / 4.f : 1 / 8.f)
|
, mMinSize(ESM::isEsm4Ext(worldspace) ? 1 / 2.f : 1 / 8.f)
|
||||||
, mDebugTerrainChunks(debugChunks)
|
, mDebugTerrainChunks(debugChunks)
|
||||||
{
|
{
|
||||||
mChunkManager->setCompositeMapSize(compMapResolution);
|
mChunkManager->setCompositeMapSize(compMapResolution);
|
||||||
mChunkManager->setCompositeMapLevel(compMapLevel);
|
mChunkManager->setCompositeMapLevel(
|
||||||
|
ESM::isEsm4Ext(worldspace) ? compMapLevel * 2 /*because cells are twice smaller*/ : compMapLevel);
|
||||||
mChunkManager->setMaxCompositeGeometrySize(maxCompGeometrySize);
|
mChunkManager->setMaxCompositeGeometrySize(maxCompGeometrySize);
|
||||||
mChunkManagers.push_back(mChunkManager.get());
|
mChunkManagers.push_back(mChunkManager.get());
|
||||||
|
|
||||||
|
@ -88,7 +88,8 @@ namespace Terrain
|
|||||||
/// Get the number of vertices on one side for each cell. Should be (power of two)+1
|
/// Get the number of vertices on one side for each cell. Should be (power of two)+1
|
||||||
virtual int getCellVertices(ESM::RefId worldspace) = 0;
|
virtual int getCellVertices(ESM::RefId worldspace) = 0;
|
||||||
|
|
||||||
virtual int getBlendmapScale(float chunkSize) = 0;
|
/// Get the number of texture tiles on one side per chunk (chunkSize 1.0 = 1 cell).
|
||||||
|
virtual int getTextureTileCount(float chunkSize, ESM::RefId worldspace) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
#include "heightcull.hpp"
|
#include "heightcull.hpp"
|
||||||
#include "storage.hpp"
|
#include "storage.hpp"
|
||||||
#include "view.hpp"
|
#include "view.hpp"
|
||||||
|
|
||||||
|
#include <components/esm/util.hpp>
|
||||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||||
|
|
||||||
namespace Terrain
|
namespace Terrain
|
||||||
@ -27,13 +29,13 @@ namespace Terrain
|
|||||||
unsigned int borderMask)
|
unsigned int borderMask)
|
||||||
: Terrain::World(
|
: Terrain::World(
|
||||||
parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask, worldspace, expiryDelay)
|
parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask, worldspace, expiryDelay)
|
||||||
, mNumSplits(4)
|
, mNumSplits(ESM::isEsm4Ext(worldspace) ? 2 : 4)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TerrainGrid::TerrainGrid(osg::Group* parent, Storage* storage, ESM::RefId worldspace, unsigned int nodeMask)
|
TerrainGrid::TerrainGrid(osg::Group* parent, Storage* storage, ESM::RefId worldspace, unsigned int nodeMask)
|
||||||
: Terrain::World(parent, storage, nodeMask, worldspace)
|
: Terrain::World(parent, storage, nodeMask, worldspace)
|
||||||
, mNumSplits(4)
|
, mNumSplits(ESM::isEsm4Ext(worldspace) ? 2 : 4)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user