mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-30 01:15:35 +00:00
Extremely early handling for BGSM/BGEM files
This commit is contained in:
parent
fe1cb3a5ae
commit
1a961f3021
11 changed files with 151 additions and 17 deletions
|
@ -12,6 +12,7 @@
|
|||
#include <components/files/multidircollection.hpp>
|
||||
#include <components/misc/strings/conversion.hpp>
|
||||
#include <components/platform/platform.hpp>
|
||||
#include <components/resource/bgsmfilemanager.hpp>
|
||||
#include <components/resource/bulletshape.hpp>
|
||||
#include <components/resource/bulletshapemanager.hpp>
|
||||
#include <components/resource/foreachbulletobject.hpp>
|
||||
|
@ -173,7 +174,8 @@ namespace
|
|||
constexpr double expiryDelay = 0;
|
||||
Resource::ImageManager imageManager(&vfs, expiryDelay);
|
||||
Resource::NifFileManager nifFileManager(&vfs, &encoder.getStatelessEncoder());
|
||||
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, expiryDelay);
|
||||
Resource::BgsmFileManager bgsmFileManager(&vfs, expiryDelay);
|
||||
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, &bgsmFileManager, expiryDelay);
|
||||
Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager, expiryDelay);
|
||||
|
||||
Resource::forEachBulletObject(
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <components/files/conversion.hpp>
|
||||
#include <components/files/multidircollection.hpp>
|
||||
#include <components/platform/platform.hpp>
|
||||
#include <components/resource/bgsmfilemanager.hpp>
|
||||
#include <components/resource/bulletshapemanager.hpp>
|
||||
#include <components/resource/imagemanager.hpp>
|
||||
#include <components/resource/niffilemanager.hpp>
|
||||
|
@ -220,7 +221,8 @@ namespace NavMeshTool
|
|||
|
||||
Resource::ImageManager imageManager(&vfs, expiryDelay);
|
||||
Resource::NifFileManager nifFileManager(&vfs, &encoder.getStatelessEncoder());
|
||||
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, expiryDelay);
|
||||
Resource::BgsmFileManager bgsmFileManager(&vfs, expiryDelay);
|
||||
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, &bgsmFileManager, expiryDelay);
|
||||
Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager, expiryDelay);
|
||||
DetourNavigator::RecastGlobalAllocator::init();
|
||||
DetourNavigator::Settings navigatorSettings = DetourNavigator::makeSettingsFromSettingsManager();
|
||||
|
|
|
@ -70,7 +70,7 @@ namespace
|
|||
init(node);
|
||||
Nif::NIFFile file("test.nif");
|
||||
file.mRoots.push_back(&node);
|
||||
auto result = Loader::load(file, &mImageManager);
|
||||
auto result = Loader::load(file, &mImageManager, nullptr);
|
||||
EXPECT_EQ(serialize(*result), R"(
|
||||
osg::Group {
|
||||
UniqueID 1
|
||||
|
@ -259,7 +259,7 @@ osg::Group {
|
|||
node.mProperties.push_back(Nif::RecordPtrT<Nif::NiProperty>(&property));
|
||||
Nif::NIFFile file("test.nif");
|
||||
file.mRoots.push_back(&node);
|
||||
auto result = Loader::load(file, &mImageManager);
|
||||
auto result = Loader::load(file, &mImageManager, nullptr);
|
||||
EXPECT_EQ(serialize(*result), formatOsgNodeForBSShaderProperty(GetParam().mExpectedShaderPrefix));
|
||||
}
|
||||
|
||||
|
@ -289,7 +289,7 @@ osg::Group {
|
|||
node.mProperties.push_back(Nif::RecordPtrT<Nif::NiProperty>(&property));
|
||||
Nif::NIFFile file("test.nif");
|
||||
file.mRoots.push_back(&node);
|
||||
auto result = Loader::load(file, &mImageManager);
|
||||
auto result = Loader::load(file, &mImageManager, nullptr);
|
||||
EXPECT_EQ(serialize(*result), formatOsgNodeForBSLightingShaderProperty(GetParam().mExpectedShaderPrefix));
|
||||
}
|
||||
|
||||
|
|
|
@ -173,6 +173,11 @@ std::string Misc::ResourceHelpers::correctActorModelPath(const std::string& resP
|
|||
return mdlname;
|
||||
}
|
||||
|
||||
std::string Misc::ResourceHelpers::correctMaterialPath(std::string_view resPath, const VFS::Manager* vfs)
|
||||
{
|
||||
return correctResourcePath({ { "materials" } }, resPath, vfs);
|
||||
}
|
||||
|
||||
std::string Misc::ResourceHelpers::correctMeshPath(std::string_view resPath)
|
||||
{
|
||||
std::string res = "meshes\\";
|
||||
|
|
|
@ -34,6 +34,7 @@ namespace Misc
|
|||
/// Use "xfoo.nif" instead of "foo.nif" if "xfoo.kf" is available
|
||||
/// Note that if "xfoo.nif" is actually unavailable, we can't fall back to "foo.nif". :(
|
||||
std::string correctActorModelPath(const std::string& resPath, const VFS::Manager* vfs);
|
||||
std::string correctMaterialPath(std::string_view resPath, const VFS::Manager* vfs);
|
||||
|
||||
// Adds "meshes\\".
|
||||
std::string correctMeshPath(std::string_view resPath);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <components/misc/strings/algorithm.hpp>
|
||||
#include <components/misc/strings/lower.hpp>
|
||||
#include <components/nif/parent.hpp>
|
||||
#include <components/resource/bgsmfilemanager.hpp>
|
||||
#include <components/resource/imagemanager.hpp>
|
||||
|
||||
// particle
|
||||
|
@ -42,6 +43,7 @@
|
|||
#include <osg/TexEnvCombine>
|
||||
#include <osg/Texture2D>
|
||||
|
||||
#include <components/bgsm/file.hpp>
|
||||
#include <components/nif/effect.hpp>
|
||||
#include <components/nif/exception.hpp>
|
||||
#include <components/nif/extra.hpp>
|
||||
|
@ -238,15 +240,17 @@ namespace NifOsg
|
|||
{
|
||||
public:
|
||||
/// @param filename used for warning messages.
|
||||
LoaderImpl(const std::filesystem::path& filename, unsigned int ver, unsigned int userver, unsigned int bethver)
|
||||
LoaderImpl(const std::filesystem::path& filename, unsigned int ver, unsigned int userver, unsigned int bethver, Resource::BgsmFileManager* materialMgr)
|
||||
: mFilename(filename)
|
||||
, mVersion(ver)
|
||||
, mUserVersion(userver)
|
||||
, mBethVersion(bethver)
|
||||
, mMaterialMgr(materialMgr)
|
||||
{
|
||||
}
|
||||
std::filesystem::path mFilename;
|
||||
unsigned int mVersion, mUserVersion, mBethVersion;
|
||||
Resource::BgsmFileManager* mMaterialMgr;
|
||||
|
||||
size_t mFirstRootTextureIndex{ ~0u };
|
||||
bool mFoundFirstRootTexturingProperty = false;
|
||||
|
@ -2155,6 +2159,98 @@ namespace NifOsg
|
|||
handleTextureControllers(texprop, composite, imageManager, stateset, animflags);
|
||||
}
|
||||
|
||||
void handleShaderMaterial(const std::string& path, osg::StateSet* stateset, Resource::ImageManager* imageManager,
|
||||
std::vector<unsigned int>& boundTextures)
|
||||
{
|
||||
if (!mMaterialMgr)
|
||||
return;
|
||||
|
||||
Bgsm::MaterialFilePtr material = mMaterialMgr->get(VFS::Path::Normalized(path));
|
||||
if (!material)
|
||||
return;
|
||||
|
||||
if (material->mShaderType == Bgsm::ShaderType::Lighting)
|
||||
{
|
||||
const Bgsm::BGSMFile* bgsm = static_cast<const Bgsm::BGSMFile*>(material.get());
|
||||
|
||||
if (!boundTextures.empty())
|
||||
{
|
||||
for (unsigned int i = 0; i < boundTextures.size(); ++i)
|
||||
stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF);
|
||||
boundTextures.clear();
|
||||
}
|
||||
|
||||
const unsigned int uvSet = 0;
|
||||
if (!bgsm->mDiffuseMap.empty())
|
||||
{
|
||||
std::string filename
|
||||
= Misc::ResourceHelpers::correctTexturePath(bgsm->mDiffuseMap, imageManager->getVFS());
|
||||
osg::ref_ptr<osg::Image> image = imageManager->getImage(filename);
|
||||
osg::ref_ptr<osg::Texture2D> texture2d = new osg::Texture2D(image);
|
||||
if (image)
|
||||
texture2d->setTextureSize(image->s(), image->t());
|
||||
handleTextureWrapping(texture2d, (bgsm->mClamp >> 1) & 0x1, bgsm->mClamp & 0x1);
|
||||
unsigned int texUnit = boundTextures.size();
|
||||
stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON);
|
||||
texture2d->setName("diffuseMap");
|
||||
boundTextures.emplace_back(uvSet);
|
||||
}
|
||||
|
||||
if (!bgsm->mNormalMap.empty())
|
||||
{
|
||||
std::string filename
|
||||
= Misc::ResourceHelpers::correctTexturePath(bgsm->mNormalMap, imageManager->getVFS());
|
||||
osg::ref_ptr<osg::Image> image = imageManager->getImage(filename);
|
||||
osg::ref_ptr<osg::Texture2D> texture2d = new osg::Texture2D(image);
|
||||
if (image)
|
||||
texture2d->setTextureSize(image->s(), image->t());
|
||||
handleTextureWrapping(texture2d, (bgsm->mClamp >> 1) & 0x1, bgsm->mClamp & 0x1);
|
||||
unsigned int texUnit = boundTextures.size();
|
||||
stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON);
|
||||
texture2d->setName("normalMap");
|
||||
boundTextures.emplace_back(uvSet);
|
||||
}
|
||||
|
||||
if (bgsm->mTwoSided)
|
||||
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
|
||||
if (bgsm->mTree)
|
||||
stateset->addUniform(new osg::Uniform("useTreeAnim", true));
|
||||
|
||||
handleDepthFlags(stateset, bgsm->mDepthTest, bgsm->mDepthWrite);
|
||||
}
|
||||
else
|
||||
{
|
||||
const Bgsm::BGEMFile* bgem = static_cast<const Bgsm::BGEMFile*>(material.get());
|
||||
if (!bgem->mBaseMap.empty())
|
||||
{
|
||||
if (!boundTextures.empty())
|
||||
{
|
||||
for (unsigned int i = 0; i < boundTextures.size(); ++i)
|
||||
stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF);
|
||||
boundTextures.clear();
|
||||
}
|
||||
std::string filename = Misc::ResourceHelpers::correctTexturePath(
|
||||
bgem->mBaseMap, imageManager->getVFS());
|
||||
osg::ref_ptr<osg::Image> image = imageManager->getImage(filename);
|
||||
osg::ref_ptr<osg::Texture2D> texture2d = new osg::Texture2D(image);
|
||||
texture2d->setName("diffuseMap");
|
||||
if (image)
|
||||
texture2d->setTextureSize(image->s(), image->t());
|
||||
handleTextureWrapping(texture2d, (bgem->mClamp >> 1) & 0x1, bgem->mClamp & 0x1);
|
||||
const unsigned int texUnit = 0;
|
||||
const unsigned int uvSet = 0;
|
||||
stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON);
|
||||
boundTextures.push_back(uvSet);
|
||||
}
|
||||
|
||||
bool useFalloff = bgem->mFalloff;
|
||||
stateset->addUniform(new osg::Uniform("useFalloff", useFalloff));
|
||||
if (useFalloff)
|
||||
stateset->addUniform(new osg::Uniform("falloffParams", bgem->mFalloffParams));
|
||||
handleDepthFlags(stateset, bgem->mDepthTest, bgem->mDepthWrite);
|
||||
}
|
||||
}
|
||||
|
||||
void handleTextureSet(const Nif::BSShaderTextureSet* textureSet, unsigned int clamp,
|
||||
const std::string& nodeName, osg::StateSet* stateset, Resource::ImageManager* imageManager,
|
||||
std::vector<unsigned int>& boundTextures)
|
||||
|
@ -2421,6 +2517,12 @@ namespace NifOsg
|
|||
node->setUserValue("shaderPrefix", std::string(getBSLightingShaderPrefix(texprop->mType)));
|
||||
node->setUserValue("shaderRequired", shaderRequired);
|
||||
osg::StateSet* stateset = node->getOrCreateStateSet();
|
||||
std::string normalizedName = Misc::ResourceHelpers::correctMaterialPath(texprop->mName, mMaterialMgr->getVFS());
|
||||
if (normalizedName.ends_with(".bgsm"))
|
||||
{
|
||||
handleShaderMaterial(normalizedName, stateset, imageManager, boundTextures);
|
||||
break;
|
||||
}
|
||||
if (!texprop->mTextureSet.empty())
|
||||
handleTextureSet(texprop->mTextureSet.getPtr(), texprop->mClamp, node->getName(), stateset,
|
||||
imageManager, boundTextures);
|
||||
|
@ -2442,6 +2544,12 @@ namespace NifOsg
|
|||
node->setUserValue("shaderPrefix", std::string("bs/nolighting"));
|
||||
node->setUserValue("shaderRequired", shaderRequired);
|
||||
osg::StateSet* stateset = node->getOrCreateStateSet();
|
||||
std::string normalizedName = Misc::ResourceHelpers::correctMaterialPath(texprop->mName, mMaterialMgr->getVFS());
|
||||
if (normalizedName.ends_with(".bgem"))
|
||||
{
|
||||
handleShaderMaterial(normalizedName, stateset, imageManager, boundTextures);
|
||||
break;
|
||||
}
|
||||
if (!texprop->mSourceTexture.empty())
|
||||
{
|
||||
if (!boundTextures.empty())
|
||||
|
@ -2860,15 +2968,15 @@ namespace NifOsg
|
|||
}
|
||||
};
|
||||
|
||||
osg::ref_ptr<osg::Node> Loader::load(Nif::FileView file, Resource::ImageManager* imageManager)
|
||||
osg::ref_ptr<osg::Node> Loader::load(Nif::FileView file, Resource::ImageManager* imageManager, Resource::BgsmFileManager* materialMgr)
|
||||
{
|
||||
LoaderImpl impl(file.getFilename(), file.getVersion(), file.getUserVersion(), file.getBethVersion());
|
||||
LoaderImpl impl(file.getFilename(), file.getVersion(), file.getUserVersion(), file.getBethVersion(), materialMgr);
|
||||
return impl.load(file, imageManager);
|
||||
}
|
||||
|
||||
void Loader::loadKf(Nif::FileView kf, SceneUtil::KeyframeHolder& target)
|
||||
{
|
||||
LoaderImpl impl(kf.getFilename(), kf.getVersion(), kf.getUserVersion(), kf.getBethVersion());
|
||||
LoaderImpl impl(kf.getFilename(), kf.getVersion(), kf.getUserVersion(), kf.getBethVersion(), nullptr);
|
||||
impl.loadKf(kf, target);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace osg
|
|||
namespace Resource
|
||||
{
|
||||
class ImageManager;
|
||||
class BgsmFileManager;
|
||||
}
|
||||
|
||||
namespace NifOsg
|
||||
|
@ -30,7 +31,7 @@ namespace NifOsg
|
|||
public:
|
||||
/// Create a scene graph for the given NIF. Auto-detects when skinning is used and wraps the graph in a Skeleton
|
||||
/// if so.
|
||||
static osg::ref_ptr<osg::Node> load(Nif::FileView file, Resource::ImageManager* imageManager);
|
||||
static osg::ref_ptr<osg::Node> load(Nif::FileView file, Resource::ImageManager* imageManager, Resource::BgsmFileManager* materialManager);
|
||||
|
||||
/// Load keyframe controllers from the given kf file.
|
||||
static void loadKf(Nif::FileView kf, SceneUtil::KeyframeHolder& target);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#include "bgsmfilemanager.hpp"
|
||||
#include "imagemanager.hpp"
|
||||
#include "keyframemanager.hpp"
|
||||
#include "niffilemanager.hpp"
|
||||
|
@ -15,11 +16,13 @@ namespace Resource
|
|||
: mVFS(vfs)
|
||||
{
|
||||
mNifFileManager = std::make_unique<NifFileManager>(vfs, encoder);
|
||||
mBgsmFileManager = std::make_unique<BgsmFileManager>(vfs, expiryDelay);
|
||||
mImageManager = std::make_unique<ImageManager>(vfs, expiryDelay);
|
||||
mSceneManager = std::make_unique<SceneManager>(vfs, mImageManager.get(), mNifFileManager.get(), expiryDelay);
|
||||
mSceneManager = std::make_unique<SceneManager>(vfs, mImageManager.get(), mNifFileManager.get(), mBgsmFileManager.get(), expiryDelay);
|
||||
mKeyframeManager = std::make_unique<KeyframeManager>(vfs, mSceneManager.get(), expiryDelay, encoder);
|
||||
|
||||
addResourceManager(mNifFileManager.get());
|
||||
addResourceManager(mBgsmFileManager.get());
|
||||
addResourceManager(mKeyframeManager.get());
|
||||
// note, scene references images so add images afterwards for correct implementation of updateCache()
|
||||
addResourceManager(mSceneManager.get());
|
||||
|
@ -43,6 +46,11 @@ namespace Resource
|
|||
return mImageManager.get();
|
||||
}
|
||||
|
||||
BgsmFileManager* ResourceSystem::getBgsmFileManager()
|
||||
{
|
||||
return mBgsmFileManager.get();
|
||||
}
|
||||
|
||||
NifFileManager* ResourceSystem::getNifFileManager()
|
||||
{
|
||||
return mNifFileManager.get();
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace Resource
|
|||
|
||||
class SceneManager;
|
||||
class ImageManager;
|
||||
class BgsmFileManager;
|
||||
class NifFileManager;
|
||||
class KeyframeManager;
|
||||
class BaseResourceManager;
|
||||
|
@ -41,6 +42,7 @@ namespace Resource
|
|||
|
||||
SceneManager* getSceneManager();
|
||||
ImageManager* getImageManager();
|
||||
BgsmFileManager* getBgsmFileManager();
|
||||
NifFileManager* getNifFileManager();
|
||||
KeyframeManager* getKeyframeManager();
|
||||
|
||||
|
@ -74,6 +76,7 @@ namespace Resource
|
|||
private:
|
||||
std::unique_ptr<SceneManager> mSceneManager;
|
||||
std::unique_ptr<ImageManager> mImageManager;
|
||||
std::unique_ptr<BgsmFileManager> mBgsmFileManager;
|
||||
std::unique_ptr<NifFileManager> mNifFileManager;
|
||||
std::unique_ptr<KeyframeManager> mKeyframeManager;
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#include <components/files/hash.hpp>
|
||||
#include <components/files/memorystream.hpp>
|
||||
|
||||
#include "bgsmfilemanager.hpp"
|
||||
#include "errormarker.hpp"
|
||||
#include "imagemanager.hpp"
|
||||
#include "niffilemanager.hpp"
|
||||
|
@ -409,7 +410,7 @@ namespace Resource
|
|||
};
|
||||
|
||||
SceneManager::SceneManager(const VFS::Manager* vfs, Resource::ImageManager* imageManager,
|
||||
Resource::NifFileManager* nifFileManager, double expiryDelay)
|
||||
Resource::NifFileManager* nifFileManager, Resource::BgsmFileManager* bgsmFileManager, double expiryDelay)
|
||||
: ResourceManager(vfs, expiryDelay)
|
||||
, mShaderManager(new Shader::ShaderManager)
|
||||
, mForceShaders(false)
|
||||
|
@ -424,6 +425,7 @@ namespace Resource
|
|||
, mSharedStateManager(new SharedStateManager)
|
||||
, mImageManager(imageManager)
|
||||
, mNifFileManager(nifFileManager)
|
||||
, mBgsmFileManager(bgsmFileManager)
|
||||
, mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR)
|
||||
, mMagFilter(osg::Texture::LINEAR)
|
||||
, mMaxAnisotropy(1)
|
||||
|
@ -795,11 +797,11 @@ namespace Resource
|
|||
}
|
||||
|
||||
osg::ref_ptr<osg::Node> load(VFS::Path::NormalizedView normalizedFilename, const VFS::Manager* vfs,
|
||||
Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager)
|
||||
Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager, Resource::BgsmFileManager* materialMgr)
|
||||
{
|
||||
const std::string_view ext = Misc::getFileExtension(normalizedFilename.value());
|
||||
if (ext == "nif")
|
||||
return NifOsg::Loader::load(*nifFileManager->get(normalizedFilename), imageManager);
|
||||
return NifOsg::Loader::load(*nifFileManager->get(normalizedFilename), imageManager, materialMgr);
|
||||
else if (ext == "spt")
|
||||
{
|
||||
Log(Debug::Warning) << "Ignoring SpeedTree data file " << normalizedFilename;
|
||||
|
@ -921,7 +923,7 @@ namespace Resource
|
|||
{
|
||||
path.changeExtension(meshType);
|
||||
if (mVFS->exists(path))
|
||||
return load(path, mVFS, mImageManager, mNifFileManager);
|
||||
return load(path, mVFS, mImageManager, mNifFileManager, mBgsmFileManager);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
|
@ -953,7 +955,7 @@ namespace Resource
|
|||
osg::ref_ptr<osg::Node> loaded;
|
||||
try
|
||||
{
|
||||
loaded = load(normalized, mVFS, mImageManager, mNifFileManager);
|
||||
loaded = load(normalized, mVFS, mImageManager, mNifFileManager, mBgsmFileManager);
|
||||
|
||||
SceneUtil::ProcessExtraDataVisitor extraDataVisitor(this);
|
||||
loaded->accept(extraDataVisitor);
|
||||
|
|
|
@ -32,6 +32,7 @@ namespace Resource
|
|||
{
|
||||
class ImageManager;
|
||||
class NifFileManager;
|
||||
class BgsmFileManager;
|
||||
class SharedStateManager;
|
||||
}
|
||||
|
||||
|
@ -90,7 +91,7 @@ namespace Resource
|
|||
{
|
||||
public:
|
||||
explicit SceneManager(const VFS::Manager* vfs, Resource::ImageManager* imageManager,
|
||||
Resource::NifFileManager* nifFileManager, double expiryDelay);
|
||||
Resource::NifFileManager* nifFileManager, Resource::BgsmFileManager* bgsmFileManager, double expiryDelay);
|
||||
~SceneManager();
|
||||
|
||||
Shader::ShaderManager& getShaderManager();
|
||||
|
@ -259,6 +260,7 @@ namespace Resource
|
|||
|
||||
Resource::ImageManager* mImageManager;
|
||||
Resource::NifFileManager* mNifFileManager;
|
||||
Resource::BgsmFileManager* mBgsmFileManager;
|
||||
|
||||
osg::Texture::FilterMode mMinFilter;
|
||||
osg::Texture::FilterMode mMagFilter;
|
||||
|
|
Loading…
Reference in a new issue