Rewrote SCAN asset handling + loading

This commit is contained in:
Aruki 2019-01-12 21:28:04 -08:00
parent a174548750
commit a1d94cc58f
37 changed files with 342 additions and 1296 deletions

View File

@ -119,7 +119,6 @@ HEADERS += \
Resource/CMaterialPass.h \
Resource/CMaterialSet.h \
Resource/CResource.h \
Resource/CScan.h \
Resource/CTexture.h \
Resource/CWorld.h \
Resource/EResType.h \
@ -252,7 +251,10 @@ HEADERS += \
Tweaks/CTweakManager.h \
Tweaks/CTweakData.h \
Tweaks/CTweakLoader.h \
Tweaks/CTweakCooker.h
Tweaks/CTweakCooker.h \
Resource/Scan/CScan.h \
Resource/Scan/SScanParametersMP1.h \
Resource/Scan/ELogbookCategory.h
# Source Files
SOURCES += \
@ -367,7 +369,8 @@ SOURCES += \
Resource/StringTable/CStringTable.cpp \
Tweaks/CTweakManager.cpp \
Tweaks/CTweakLoader.cpp \
Tweaks/CTweakCooker.cpp
Tweaks/CTweakCooker.cpp \
Resource/Scan/CScan.cpp
# Codegen
CODEGEN_DIR = $$EXTERNALS_DIR/CodeGen

View File

@ -3,9 +3,10 @@
#include "CResourceIterator.h"
#include "Core/Resource/CAudioMacro.h"
#include "Core/Resource/CFont.h"
#include "Core/Resource/CScan.h"
#include "Core/Resource/CWorld.h"
#include "Core/Resource/Animation/CAnimSet.h"
#include "Core/Resource/Scan/CScan.h"
#include "Core/Resource/Scan/SScanParametersMP1.h"
#include "Core/Resource/Script/CScriptLayer.h"
#include <Common/Math/MathUtil.h>
@ -311,10 +312,15 @@ void GenerateAssetNames(CGameProject *pProj)
ApplyGeneratedName(pEntry, pEntry->DirectoryPath(), ScanName);
CScan *pScan = (CScan*) pEntry->Load();
if (pScan && pScan->ScanText())
if (pScan)
{
CResourceEntry *pStringEntry = pScan->ScanText()->Entry();
ApplyGeneratedName(pStringEntry, pStringEntry->DirectoryPath(), ScanName);
CAssetID StringID = pScan->ScanStringPropertyRef();
CResourceEntry* pStringEntry = gpResourceStore->FindEntry(StringID);
if (pStringEntry)
{
ApplyGeneratedName(pStringEntry, pStringEntry->DirectoryPath(), ScanName);
}
}
}
}
@ -609,16 +615,10 @@ void GenerateAssetNames(CGameProject *pProj)
CScan *pScan = (CScan*) It->Load();
TString ScanName;
if (pProj->Game() >= EGame::EchoesDemo)
{
CAssetID DisplayAsset = pScan->LogbookDisplayAssetID();
CResourceEntry *pEntry = pStore->FindEntry(DisplayAsset);
if (pEntry && pEntry->IsNamed()) ScanName = pEntry->Name();
}
if (ScanName.IsEmpty())
{
CStringTable *pString = pScan->ScanText();
CAssetID StringID = pScan->ScanStringPropertyRef().Get();
CStringTable *pString = (CStringTable*) gpResourceStore->LoadResource(StringID, EResourceType::StringTable);
if (pString) ScanName = pString->Entry()->Name();
}
@ -626,13 +626,14 @@ void GenerateAssetNames(CGameProject *pProj)
if (!ScanName.IsEmpty() && pProj->Game() <= EGame::Prime)
{
CAssetID FrameID = pScan->GuiFrame();
CResourceEntry *pEntry = pStore->FindEntry(FrameID);
const SScanParametersMP1& kParms = *static_cast<SScanParametersMP1*>(pScan->ScanData().DataPointer());
CResourceEntry *pEntry = pStore->FindEntry(kParms.GuiFrame);
if (pEntry) ApplyGeneratedName(pEntry, pEntry->DirectoryPath(), "ScanFrame");
for (uint32 iImg = 0; iImg < 4; iImg++)
{
CAssetID ImageID = pScan->ScanImage(iImg);
CAssetID ImageID = kParms.ScanImages[iImg].Texture;
CResourceEntry *pImgEntry = pStore->FindEntry(ImageID);
if (pImgEntry) ApplyGeneratedName(pImgEntry, pImgEntry->DirectoryPath(), TString::Format("%s_Image%d", *ScanName, iImg));
}

View File

@ -30,6 +30,70 @@ void IDependencyNode::GetAllResourceReferences(std::set<CAssetID>& rOutSet) cons
mChildren[iChild]->GetAllResourceReferences(rOutSet);
}
void IDependencyNode::ParseProperties(CResourceEntry* pParentEntry, CStructProperty* pProperties, void* pData)
{
// Recursive function for parsing dependencies in properties
for (uint32 PropertyIdx = 0; PropertyIdx < pProperties->NumChildren(); PropertyIdx++)
{
IProperty* pProp = pProperties->ChildByIndex(PropertyIdx);
EPropertyType Type = pProp->Type();
// Technically we aren't parsing array children, but it's not really worth refactoring this function
// to support it when there aren't any array properties that contain any asset references anyway...
if (Type == EPropertyType::Struct)
ParseProperties( pParentEntry, TPropCast<CStructProperty>(pProp), pData );
else if (Type == EPropertyType::Sound)
{
uint32 SoundID = TPropCast<CSoundProperty>(pProp)->Value(pData);
if (SoundID != -1)
{
CGameProject* pProj = pParentEntry->Project();
SSoundInfo Info = pProj->AudioManager()->GetSoundInfo(SoundID);
if (Info.pAudioGroup)
{
CPropertyDependency *pDep = new CPropertyDependency(pProp->IDString(true), Info.pAudioGroup->ID());
mChildren.push_back(pDep);
}
}
}
else if (Type == EPropertyType::Asset)
{
CAssetID ID = TPropCast<CAssetProperty>(pProp)->Value(pData);
if (ID.IsValid())
{
CPropertyDependency *pDep = new CPropertyDependency(pProp->IDString(true), ID);
mChildren.push_back(pDep);
}
}
else if (Type == EPropertyType::AnimationSet)
{
CAnimationParameters Params = TPropCast<CAnimationSetProperty>(pProp)->Value(pData);
CAssetID ID = Params.ID();
if (ID.IsValid())
{
// Character sets are removed starting in MP3, so we only need char property dependencies in Echoes and earlier
if (pProperties->Game() <= EGame::Echoes)
{
CCharPropertyDependency *pDep = new CCharPropertyDependency(pProp->IDString(true), ID, Params.CharacterIndex());
mChildren.push_back(pDep);
}
else
{
CPropertyDependency *pDep = new CPropertyDependency(pProp->IDString(true), ID);
mChildren.push_back(pDep);
}
}
}
}
}
// Serialization constructor
IDependencyNode* IDependencyNode::ArchiveConstructor(EDependencyNodeType Type)
{
@ -149,76 +213,10 @@ CScriptInstanceDependency* CScriptInstanceDependency::BuildTree(CScriptObject *p
{
CScriptInstanceDependency *pInst = new CScriptInstanceDependency();
pInst->mObjectType = pInstance->ObjectTypeID();
ParseStructDependencies(pInst, pInstance, pInstance->Template()->Properties());
pInst->ParseProperties(pInstance->Area()->Entry(), pInstance->Template()->Properties(), pInstance->PropertyData());
return pInst;
}
void CScriptInstanceDependency::ParseStructDependencies(CScriptInstanceDependency* pInst, CScriptObject* pInstance, CStructProperty *pStruct)
{
// Recursive function for parsing script dependencies and loading them into the script instance dependency
void* pPropertyData = pInstance->PropertyData();
for (uint32 PropertyIdx = 0; PropertyIdx < pStruct->NumChildren(); PropertyIdx++)
{
IProperty *pProp = pStruct->ChildByIndex(PropertyIdx);
EPropertyType Type = pProp->Type();
// Technically we aren't parsing array children, but it's not really worth refactoring this function
// to support it when there aren't any array properties that contain any asset references anyway...
if (Type == EPropertyType::Struct)
ParseStructDependencies(pInst, pInstance, TPropCast<CStructProperty>(pProp));
else if (Type == EPropertyType::Sound)
{
uint32 SoundID = TPropCast<CSoundProperty>(pProp)->Value(pPropertyData);
if (SoundID != -1)
{
CGameProject *pProj = pInstance->Area()->Entry()->Project();
SSoundInfo Info = pProj->AudioManager()->GetSoundInfo(SoundID);
if (Info.pAudioGroup)
{
CPropertyDependency *pDep = new CPropertyDependency(pProp->IDString(true), Info.pAudioGroup->ID());
pInst->mChildren.push_back(pDep);
}
}
}
else if (Type == EPropertyType::Asset)
{
CAssetID ID = TPropCast<CAssetProperty>(pProp)->Value(pPropertyData);
if (ID.IsValid())
{
CPropertyDependency *pDep = new CPropertyDependency(pProp->IDString(true), ID);
pInst->mChildren.push_back(pDep);
}
}
else if (Type == EPropertyType::AnimationSet)
{
CAnimationParameters Params = TPropCast<CAnimationSetProperty>(pProp)->Value(pPropertyData);
CAssetID ID = Params.ID();
if (ID.IsValid())
{
// Character sets are removed starting in MP3, so we only need char property dependencies in Echoes and earlier
if (pStruct->Game() <= EGame::Echoes)
{
CCharPropertyDependency *pDep = new CCharPropertyDependency(pProp->IDString(true), ID, Params.CharacterIndex());
pInst->mChildren.push_back(pDep);
}
else
{
CPropertyDependency *pDep = new CPropertyDependency(pProp->IDString(true), ID);
pInst->mChildren.push_back(pDep);
}
}
}
}
}
// ************ CSetCharacterDependency ************
EDependencyNodeType CSetCharacterDependency::Type() const
{

View File

@ -39,6 +39,7 @@ public:
virtual void Serialize(IArchive& rArc) = 0;
virtual void GetAllResourceReferences(std::set<CAssetID>& rOutSet) const;
virtual bool HasDependency(const CAssetID& rkID) const;
void ParseProperties(CResourceEntry* pParentEntry, CStructProperty* pProperties, void* pData);
// Serialization constructor
static IDependencyNode* ArchiveConstructor(EDependencyNodeType Type);
@ -144,8 +145,6 @@ public:
// Static
static CScriptInstanceDependency* BuildTree(CScriptObject *pInstance);
protected:
static void ParseStructDependencies(CScriptInstanceDependency *pTree, CScriptObject* pInstance, CStructProperty *pStruct);
};
// Node representing an animset character. Indicates what index the character is within the animset.

View File

@ -2,9 +2,10 @@
#define CANIMATIONPARAMETERS_H
#include "Core/Resource/TResPtr.h"
#include "Core/Resource/Model/CModel.h"
#include <Common/EGame.h>
class CModel;
class CAnimationParameters
{
EGame mGame;

View File

@ -381,12 +381,12 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes()
{
CResTypeInfo *pType = new CResTypeInfo(EResourceType::StringTable, "String Table", "strg");
AddExtension(pType, "STRG", EGame::PrimeDemo, EGame::DKCReturns);
pType->mCanBeSerialized = true;
}
{
CResTypeInfo *pType = new CResTypeInfo(EResourceType::Texture, "Texture", "txtr");
AddExtension(pType, "TXTR", EGame::PrimeDemo, EGame::DKCReturns);
pType->mCanHaveDependencies = false;
pType->mCanBeSerialized = true;
}
{
CResTypeInfo *pType = new CResTypeInfo(EResourceType::Tweaks, "Tweak Data", "ctwk");

View File

@ -1,122 +0,0 @@
#ifndef CSCAN_H
#define CSCAN_H
#include "CResource.h"
#include "TResPtr.h"
#include "Core/Resource/Animation/CAnimationParameters.h"
#include "Core/Resource/StringTable/CStringTable.h"
#include <Common/EGame.h>
class CScan : public CResource
{
DECLARE_RESOURCE_TYPE(Scan)
friend class CScanLoader;
public:
// This likely needs revising when MP2/MP3 support is added
enum class ELogbookCategory
{
None,
PirateData,
ChozoLore,
Creatures,
Research
};
struct SScanInfoSecondaryModel
{
CAssetID ModelID;
CAnimationParameters AnimParams;
TString AttachBoneName;
};
private:
// Common
TResPtr<CStringTable> mpStringTable;
bool mIsSlow;
bool mIsImportant;
ELogbookCategory mCategory;
// MP1
CAssetID mFrameID;
CAssetID mScanImageTextures[4];
// MP2/3
bool mUseLogbookModelPostScan;
CAssetID mPostOverrideTexture;
float mLogbookDefaultRotX;
float mLogbookDefaultRotZ;
float mLogbookScale;
CAssetID mLogbookModel;
CAnimationParameters mLogbookAnimParams;
CAnimationParameters mUnknownAnimParams;
std::vector<SScanInfoSecondaryModel> mSecondaryModels;
// MP3
std::vector<CAssetID> mDependencyList;
public:
CScan(CResourceEntry *pEntry = 0)
: CResource(pEntry)
, mpStringTable(nullptr)
, mIsSlow(false)
, mIsImportant(false)
, mCategory(ELogbookCategory::None)
{}
CDependencyTree* BuildDependencyTree() const
{
CDependencyTree *pTree = new CDependencyTree();
// Corruption's SCAN has a list of all assets - just grab that
if (Game() >= EGame::CorruptionProto)
{
for (uint32 iDep = 0; iDep < mDependencyList.size(); iDep++)
{
pTree->AddDependency(mDependencyList[iDep]);
}
return pTree;
}
// Otherwise add all the dependencies we need from the properties
if (Game() <= EGame::Prime)
pTree->AddDependency(mFrameID);
pTree->AddDependency(mpStringTable);
if (Game() <= EGame::Prime)
{
for (uint32 iImg = 0; iImg < 4; iImg++)
pTree->AddDependency(mScanImageTextures[iImg]);
}
else if (Game() <= EGame::Echoes)
{
pTree->AddDependency(mPostOverrideTexture);
pTree->AddDependency(mLogbookModel);
pTree->AddCharacterDependency(mLogbookAnimParams);
pTree->AddCharacterDependency(mUnknownAnimParams);
for (uint32 iSec = 0; iSec < mSecondaryModels.size(); iSec++)
{
const SScanInfoSecondaryModel& rkSecModel = mSecondaryModels[iSec];
pTree->AddDependency(rkSecModel.ModelID);
pTree->AddCharacterDependency(rkSecModel.AnimParams);
}
}
return pTree;
}
// Accessors
inline CStringTable* ScanText() const { return mpStringTable; }
inline bool IsImportant() const { return mIsImportant; }
inline bool IsSlow() const { return mIsSlow; }
inline ELogbookCategory LogbookCategory() const { return mCategory; }
inline CAssetID GuiFrame() const { return mFrameID; }
inline CAssetID ScanImage(uint32 ImgIndex) const { return mScanImageTextures[ImgIndex]; }
inline CAssetID LogbookDisplayAssetID() const { return (mLogbookAnimParams.ID().IsValid() ? mLogbookAnimParams.ID() : mLogbookModel); }
};
#endif // CSCAN_H

View File

@ -1,306 +1,83 @@
#include "CScanLoader.h"
#include "Core/GameProject/CResourceStore.h"
#include "CScriptLoader.h"
#include <Common/Log.h>
CScanLoader::CScanLoader()
CScan* CScanLoader::LoadScanMP1(IInputStream& SCAN, CResourceEntry* pEntry)
{
}
CScan* CScanLoader::LoadScanMP1(IInputStream& rSCAN)
{
// Basic support at the moment - don't read animation/scan image data
mpScan->mFrameID = CAssetID(rSCAN, k32Bit);
mpScan->mpStringTable = gpResourceStore->LoadResource(rSCAN.ReadLong(), EResourceType::StringTable);
mpScan->mIsSlow = (rSCAN.ReadLong() != 0);
mpScan->mCategory = (CScan::ELogbookCategory) rSCAN.ReadLong();
mpScan->mIsImportant = (rSCAN.ReadByte() == 1);
for (uint32 iImg = 0; iImg < 4; iImg++)
{
mpScan->mScanImageTextures[iImg] = CAssetID(rSCAN, k32Bit);
rSCAN.Seek(0x18, SEEK_CUR);
}
return mpScan;
}
CScan* CScanLoader::LoadScanMP2(IInputStream& rSCAN)
{
// The SCAN format in MP2 embeds a SNFO object using the same format as SCLY
// However since the contents of the file are consistent there's no need to delegate to CScriptLoader
rSCAN.Skip(0x1);
uint32 NumInstances = rSCAN.ReadLong();
if (NumInstances != 1)
{
errorf("%s: SCAN has multiple instances", *rSCAN.GetSourceString());
return nullptr;
}
uint32 ScanInfoStart = rSCAN.Tell();
CFourCC SNFO(rSCAN);
if (SNFO != FOURCC('SNFO'))
{
errorf("%s [0x%X]: Unrecognized SCAN object type: %s", *rSCAN.GetSourceString(), ScanInfoStart, *SNFO.ToString());
return nullptr;
}
uint16 InstanceSize = rSCAN.ReadShort();
uint32 InstanceEnd = rSCAN.Tell() + InstanceSize;
rSCAN.Skip(0x4);
uint16 NumConnections = rSCAN.ReadShort();
if (NumConnections > 0)
{
warnf("%s [0x%X]: SNFO object in SCAN has connections", *rSCAN.GetSourceString(), ScanInfoStart);
rSCAN.Skip(NumConnections * 0xC);
}
uint32 BasePropID = rSCAN.ReadLong();
if (BasePropID != 0xFFFFFFFF)
{
errorf("%s [0x%X]: Invalid base property ID: 0x%08X", *rSCAN.GetSourceString(), rSCAN.Tell() - 4, BasePropID);
return nullptr;
}
rSCAN.Skip(0x2);
uint16 NumProperties = rSCAN.ReadShort();
switch (NumProperties)
{
case 0x14:
case 0xB:
mpScan = new CScan(mpEntry);
LoadParamsMP2(rSCAN, NumProperties);
break;
case 0x12:
case 0x15:
case 0x16:
mpScan = new CScan(mpEntry);
LoadParamsMP3(rSCAN, NumProperties);
break;
default:
errorf("%s [0x%X]: Invalid SNFO property count: 0x%X", *rSCAN.GetSourceString(), rSCAN.Tell() - 2, NumProperties);
return nullptr;
}
// Load MP3 dependency list
if (mpScan->Game() == EGame::Corruption)
{
rSCAN.GoTo(InstanceEnd);
uint32 NumDeps = rSCAN.ReadLong();
for (uint32 DepIdx = 0; DepIdx < NumDeps; DepIdx++)
{
rSCAN.Skip(4);
CAssetID ID(rSCAN, mpScan->Game());
mpScan->mDependencyList.push_back(ID);
}
}
return mpScan;
}
void CScanLoader::LoadParamsMP2(IInputStream& rSCAN, uint16 NumProperties)
{
// Function begins after the SNFO property count
mpScan->mSecondaryModels.resize(9);
for (uint32 iProp = 0; iProp < NumProperties; iProp++)
{
uint32 PropertyID = rSCAN.ReadLong();
uint16 PropertySize = rSCAN.ReadShort();
uint32 Next = rSCAN.Tell() + PropertySize;
switch (PropertyID)
{
case 0x2F5B6423:
mpScan->mpStringTable = gpResourceStore->LoadResource(rSCAN.ReadLong(), EResourceType::StringTable);
break;
case 0xC308A322:
mpScan->mIsSlow = (rSCAN.ReadLong() != 0);
break;
case 0x7B714814:
mpScan->mIsImportant = rSCAN.ReadBool();
break;
case 0x1733B1EC:
mpScan->mUseLogbookModelPostScan = rSCAN.ReadBool();
break;
case 0x53336141:
mpScan->mPostOverrideTexture = CAssetID(rSCAN, mVersion);
break;
case 0x3DE0BA64:
mpScan->mLogbookDefaultRotX = rSCAN.ReadFloat();
break;
case 0x2ADD6628:
mpScan->mLogbookDefaultRotZ = rSCAN.ReadFloat();
break;
case 0xD0C15066:
mpScan->mLogbookScale = rSCAN.ReadFloat();
break;
case 0xB7ADC418:
mpScan->mLogbookModel = CAssetID(rSCAN, mVersion);
break;
case 0x15694EE1:
mpScan->mLogbookAnimParams = CAnimationParameters(rSCAN, mVersion);
break;
case 0x58F9FE99:
mpScan->mUnknownAnimParams = CAnimationParameters(rSCAN, mVersion);
break;
case 0x1C5B4A3A:
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[0] );
break;
case 0x8728A0EE:
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[1] );
break;
case 0xF1CD99D3:
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[2] );
break;
case 0x6ABE7307:
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[3] );
break;
case 0x1C07EBA9:
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[4] );
break;
case 0x8774017D:
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[5] );
break;
case 0xF1913840:
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[6] );
break;
case 0x6AE2D294:
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[7] );
break;
case 0x1CE2091C:
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[8] );
break;
}
rSCAN.GoTo(Next);
}
mpScan->mCategory = CScan::ELogbookCategory::None;
}
void CScanLoader::LoadParamsMP3(IInputStream& rSCAN, uint16 NumProperties)
{
// Function begins after the SNFO property count
for (uint32 iProp = 0; iProp < NumProperties; iProp++)
{
uint32 PropertyID = rSCAN.ReadLong();
uint16 PropertySize = rSCAN.ReadShort();
uint32 Next = rSCAN.Tell() + PropertySize;
switch (PropertyID)
{
case 0x2F5B6423:
mpScan->mpStringTable = gpResourceStore->LoadResource(rSCAN.ReadLongLong(), EResourceType::StringTable);
break;
case 0xC308A322:
mpScan->mIsSlow = (rSCAN.ReadLong() != 0);
break;
case 0x7B714814:
mpScan->mIsImportant = (rSCAN.ReadByte() != 0);
break;
}
rSCAN.GoTo(Next);
}
mpScan->mCategory = CScan::ELogbookCategory::None;
}
void CScanLoader::LoadScanInfoSecondaryModel(IInputStream& rSCAN, CScan::SScanInfoSecondaryModel& rSecondaryModel)
{
uint16 NumProperties = rSCAN.ReadShort();
for (uint32 iProp = 0; iProp < NumProperties; iProp++)
{
uint32 PropertyID = rSCAN.ReadLong();
uint16 PropertySize = rSCAN.ReadShort();
uint32 Next = rSCAN.Tell() + PropertySize;
switch (PropertyID)
{
case 0x1F7921BC:
rSecondaryModel.ModelID = CAssetID(rSCAN, mVersion);
break;
case 0xCDD202D1:
rSecondaryModel.AnimParams = CAnimationParameters(rSCAN, mVersion);
break;
case 0x3EA2BED8:
rSecondaryModel.AttachBoneName = rSCAN.ReadString();
break;
}
rSCAN.GoTo(Next);
}
}
// ************ STATIC/PUBLIC ************
CScan* CScanLoader::LoadSCAN(IInputStream& rSCAN, CResourceEntry *pEntry)
{
if (!rSCAN.IsValid()) return nullptr;
/* Switching to EGame enum here isn't really useful unfortunately
* because the MP1 demo can be 1, 2, or 3, while MP1 is 5 and MP2+ is 2
* MP1 is the only one that starts with 5 so that is a consistent check for now
* Better version checks will be implemented when the other versions are
* better-understood. */
uint32 FileVersion = rSCAN.ReadLong();
uint32 Magic = rSCAN.ReadLong();
// Echoes+
if (FileVersion == FOURCC('SCAN'))
{
// The MP2 load function will check for MP3
CScanLoader Loader;
Loader.mVersion = EGame::Echoes;
Loader.mpEntry = pEntry;
if (Magic == 0x01000000) rSCAN.Seek(-4, SEEK_CUR); // The version number isn't present in the Echoes demo
return Loader.LoadScanMP2(rSCAN);
}
// Validate magic
uint Magic = SCAN.ReadLong();
if (Magic != 0x0BADBEEF)
{
errorf("%s: Invalid SCAN magic: 0x%08X", *rSCAN.GetSourceString(), Magic);
errorf("Invalid magic in SCAN asset: 0x%08X", Magic);
return nullptr;
}
if (FileVersion != 5)
{
errorf("%s: Unsupported SCAN version: 0x%X", *rSCAN.GetSourceString(), FileVersion);
return nullptr;
}
// MP1 SCAN - read the file!
CScanLoader Loader;
Loader.mVersion = EGame::Prime;
Loader.mpScan = new CScan(pEntry);
Loader.mpEntry = pEntry;
return Loader.LoadScanMP1(rSCAN);
// The SCAN format in MP2 and later games uses the script loader to load SCAN parameters.
// The MP1 format is not loaded the same way, as far as I'm aware, and is loaded the same
// way as a normal file format... however, since we support all games, we need to support
// the script object method for proper MP2/3 support (including dealing with property names/IDs).
// So, it's simplest to use the script loader to load the MP1 SCAN format as well... that enables
// us to just create one class for all SCAN assets that works for every game.
mpScan = new CScan(pEntry);
CScriptLoader::LoadStructData(SCAN, mpScan->ScanData());
return mpScan;
}
CScan* CScanLoader::LoadScanMP2(IInputStream& SCAN, CResourceEntry* pEntry)
{
// Validate version
uint Version = SCAN.ReadLong();
if (Version != 2)
{
errorf("Unrecognized SCAN version: %d", Version);
return nullptr;
}
// The SCAN format in MP2 embeds a ScannableObjectInfo script object using the same file format as SCLY.
// As such we use CScriptLoader to load parameters, but since we don't actually want to create a script
// object, we will skip past the script object/layer header and just load the properties directly.
SCAN.Skip(0x17);
mpScan = new CScan(pEntry);
CScriptLoader::LoadStructData(SCAN, mpScan->ScanData());
return mpScan;
}
// ************ STATIC/PUBLIC ************
CScan* CScanLoader::LoadSCAN(IInputStream& SCAN, CResourceEntry *pEntry)
{
if (!SCAN.IsValid()) return nullptr;
// MP1 SCAN format starts with a version number and then follows with a magic.
// The demo can be 1, 2, or 3, while the final build is 5.
// The MP2 SCAN format starts with a 'SCAN' magic.
uint VersionCheck = SCAN.ReadLong();
// Echoes+
if (VersionCheck == FOURCC('SCAN'))
{
CScanLoader Loader;
return Loader.LoadScanMP2(SCAN, pEntry);
}
// MP1
else if (VersionCheck <= 5)
{
if (VersionCheck == 5)
{
CScanLoader Loader;
return Loader.LoadScanMP1(SCAN, pEntry);
}
else
{
errorf("%s: Unsupported SCAN version: %d", VersionCheck);
return nullptr;
}
}
else
{
errorf("Failed to identify SCAN version: 0x%X", VersionCheck);
return nullptr;
}
}

View File

@ -7,15 +7,10 @@
class CScanLoader
{
TResPtr<CScan> mpScan;
CResourceEntry *mpEntry;
EGame mVersion;
CScanLoader();
CScan* LoadScanMP1(IInputStream& rSCAN);
CScan* LoadScanMP2(IInputStream& rSCAN);
void LoadParamsMP2(IInputStream& rSCAN, uint16 NumProperties);
void LoadParamsMP3(IInputStream& rSCAN, uint16 NumProperties);
void LoadScanInfoSecondaryModel(IInputStream& rSCAN, CScan::SScanInfoSecondaryModel& rSecondaryModel);
CScanLoader() {}
CScan* LoadScanMP1(IInputStream& SCAN, CResourceEntry* pEntry);
CScan* LoadScanMP2(IInputStream& SCAN, CResourceEntry* pEntry);
public:
static CScan* LoadSCAN(IInputStream& rSCAN, CResourceEntry *pEntry);

View File

@ -0,0 +1,51 @@
#include "CScan.h"
CScan::CScan(CResourceEntry* pEntry /*= 0*/)
: CResource(pEntry)
{
CGameTemplate* pGameTemplate = NGameList::GetGameTemplate( Game() );
mpTemplate = pGameTemplate->FindMiscTemplate("ScannableObjectInfo");
ASSERT( mpTemplate != nullptr );
CStructProperty* pProperties = mpTemplate->Properties();
mPropertyData.resize( pProperties->DataSize() );
pProperties->Construct( mPropertyData.data() );
}
CStructRef CScan::ScanData() const
{
return CStructRef((void*) mPropertyData.data(), mpTemplate->Properties());
}
/** Convenience property accessors */
CAssetRef CScan::ScanStringPropertyRef() const
{
const uint kStringIdMP1 = 0x1;
const uint kStringIdMP2 = 0x2F5B6423;
IProperty* pProperty = mpTemplate->Properties()->ChildByID(
Game() <= EGame::Prime ? kStringIdMP1 : kStringIdMP2
);
return CAssetRef( (void*) mPropertyData.data(), pProperty );
}
CBoolRef CScan::IsCriticalPropertyRef() const
{
const uint kIsCriticalIdMP1 = 0x4;
const uint kIsCriticalIdMP2 = 0x7B714814;
IProperty* pProperty = mpTemplate->Properties()->ChildByID(
Game() <= EGame::Prime ? kIsCriticalIdMP1 : kIsCriticalIdMP2
);
return CBoolRef( (void*) mPropertyData.data(), pProperty );
}
/** CResource interface */
CDependencyTree* CScan::BuildDependencyTree() const
{
CDependencyTree* pTree = new CDependencyTree();
pTree->ParseProperties(Entry(), ScanData().Property(), ScanData().DataPointer());
return pTree;
}

View File

@ -0,0 +1,34 @@
#ifndef CSCAN_H
#define CSCAN_H
#include <Common/Common.h>
#include "Core/Resource/Animation/CAnimationParameters.h"
#include "Core/Resource/Script/CGameTemplate.h"
#include "Core/Resource/Script/NGameList.h"
/** Scannable object parameters from SCAN assets */
class CScan : public CResource
{
DECLARE_RESOURCE_TYPE(Scan)
friend class CScanLoader;
friend class CScanCooker;
/** Script template specifying scan data layout */
CScriptTemplate* mpTemplate;
/** Scan property data */
std::vector<uint8> mPropertyData;
public:
CScan(CResourceEntry* pEntry = 0);
CStructRef ScanData() const;
/** Convenience property accessors */
CAssetRef ScanStringPropertyRef() const;
CBoolRef IsCriticalPropertyRef() const;
/** CResource interface */
virtual CDependencyTree* BuildDependencyTree() const override;
};
#endif // CSCAN_H

View File

@ -0,0 +1,14 @@
#ifndef ELOGBOOKCATEGORY_H
#define ELOGBOOKCATEGORY_H
/** Logbook category for scannable objects in MP1, used in SCAN and SAVW */
enum class ELogbookCategory
{
None = 0,
SpacePirateData = 1,
ChozoLore = 2,
Creatures = 3,
Research = 4
};
#endif // ELOGBOOKCATEGORY_H

View File

@ -0,0 +1,60 @@
#ifndef SSCANPARAMETERSMP1_H
#define SSCANPARAMETERSMP1_H
#include "ELogbookCategory.h"
#include <Common/Common.h>
/** Struct mapping to SCAN property layout in MP1 */
enum class EScanSpeed
{
Normal = 0,
Slow = 1,
};
enum class EScanImagePane
{
Pane0 = 0,
Pane1 = 1,
Pane2 = 2,
Pane3 = 3,
Pane01 = 4,
Pane12 = 5,
Pane23 = 6,
Pane012 = 7,
Pane123 = 8,
Pane0123 = 9,
Pane4 = 10,
Pane5 = 11,
Pane6 = 12,
Pane7 = 13,
Pane45 = 14,
Pane56 = 15,
Pane67 = 16,
Pane456 = 17,
Pane567 = 18,
Pane4567 = 19,
None = -1
};
struct SScanImage
{
CAssetID Texture;
float AppearPercentage;
EScanImagePane Pane;
int32 AnimationCellWidth;
int32 AnimationCellHeight;
float AnimationSwapInterval;
float FadeDuration;
};
struct SScanParametersMP1
{
CAssetID GuiFrame;
CAssetID String;
EScanSpeed Speed;
ELogbookCategory LogbookCategory;
bool IsCritical;
SScanImage ScanImages[4];
};
#endif // SSCANPARAMETERSMP1_H

View File

@ -2,6 +2,7 @@
#define CANIMATIONSETPROPERTY_H
#include "IProperty.h"
#include "Core/Resource/Animation/CAnimationParameters.h"
class CAnimationSetProperty : public TSerializeableTypedProperty< CAnimationParameters, EPropertyType::AnimationSet >
{

View File

@ -1,7 +1,6 @@
#ifndef IPROPERTY_H
#define IPROPERTY_H
#include "Core/Resource/Animation/CAnimationParameters.h"
#include <Common/Common.h>
#include <Common/CFourCC.h>
#include <Common/Math/CVector3f.h>

View File

@ -1,5 +1,6 @@
#include "CPointOfInterestExtra.h"
//@todo pull these values from tweaks instead of hardcoding them
const CColor CPointOfInterestExtra::skRegularColor = CColor::Integral(0xFF,0x70,0x00);
const CColor CPointOfInterestExtra::skImportantColor = CColor::Integral(0xFF,0x00,0x00);
@ -19,14 +20,17 @@ CPointOfInterestExtra::CPointOfInterestExtra(CScriptObject *pInstance, CScene *p
void CPointOfInterestExtra::PropertyModified(IProperty* pProperty)
{
if (mScanProperty.Property() == pProperty)
{
mpScanData = gpResourceStore->LoadResource<CScan>( mScanProperty.Get() );
mScanIsCritical = (mpScanData ? mpScanData->IsCriticalPropertyRef() : CBoolRef());
}
}
void CPointOfInterestExtra::ModifyTintColor(CColor& Color)
{
if (mpScanData)
{
if (mpScanData->IsImportant()) Color *= skImportantColor;
if (mScanIsCritical) Color *= skImportantColor;
else Color *= skRegularColor;
}
}

View File

@ -2,7 +2,7 @@
#define CPOINTOFINTERESTEXTRA_H
#include "CScriptExtra.h"
#include "Core/Resource/CScan.h"
#include "Core/Resource/Scan/CScan.h"
#include <Common/CColor.h>
class CPointOfInterestExtra : public CScriptExtra
@ -10,6 +10,7 @@ class CPointOfInterestExtra : public CScriptExtra
// Tint POI billboard orange/red depending on scan importance
CAssetRef mScanProperty;
TResPtr<CScan> mpScanData;
CBoolRef mScanIsCritical;
public:
explicit CPointOfInterestExtra(CScriptObject *pInstance, CScene *pScene, CScriptNode *pParent = 0);

View File

@ -97,14 +97,9 @@ HEADERS += \
Undo/CTranslateNodeCommand.h \
Undo/EUndoCommand.h \
Undo/UndoCommands.h \
Widgets/IPreviewPanel.h \
Widgets/WColorPicker.h \
Widgets/WDraggableSpinBox.h \
Widgets/WIntegralSpinBox.h \
Widgets/WScanPreviewPanel.h \
Widgets/WStringPreviewPanel.h \
Widgets/WTextureGLWidget.h \
Widgets/WTexturePreviewPanel.h \
Widgets/WVectorEditor.h \
WorldEditor/CLayerEditor.h \
WorldEditor/CLayerModel.h \
@ -216,14 +211,9 @@ SOURCES += \
Undo/CRotateNodeCommand.cpp \
Undo/CScaleNodeCommand.cpp \
Undo/CTranslateNodeCommand.cpp \
Widgets/IPreviewPanel.cpp \
Widgets/WColorPicker.cpp \
Widgets/WDraggableSpinBox.cpp \
Widgets/WIntegralSpinBox.cpp \
Widgets/WScanPreviewPanel.cpp \
Widgets/WStringPreviewPanel.cpp \
Widgets/WTextureGLWidget.cpp \
Widgets/WTexturePreviewPanel.cpp \
Widgets/WVectorEditor.cpp \
WorldEditor/CLayerEditor.cpp \
WorldEditor/CLayerModel.cpp \
@ -293,8 +283,6 @@ SOURCES += \
FORMS += \
TestDialog.ui \
ModelEditor/CModelEditorWindow.ui \
Widgets/WScanPreviewPanel.ui \
Widgets/WTexturePreviewPanel.ui \
WorldEditor/CLayerEditor.ui \
WorldEditor/CWorldEditor.ui \
WorldEditor/WCreateTab.ui \

View File

@ -1,24 +0,0 @@
#include "IPreviewPanel.h"
#include "WScanPreviewPanel.h"
#include "WStringPreviewPanel.h"
#include "WTexturePreviewPanel.h"
IPreviewPanel::IPreviewPanel(QWidget *parent) : QFrame(parent)
{
setFrameShape(QFrame::StyledPanel);
setFrameShadow(QFrame::Plain);
setLineWidth(2);
}
// Can add more if more preview types are implemented
// Not every resource type is really suitable for this though unfortunately
IPreviewPanel* IPreviewPanel::CreatePanel(EResourceType Type, QWidget *pParent)
{
switch (Type)
{
case EResourceType::Texture: return new WTexturePreviewPanel(pParent);
case EResourceType::StringTable: return new WStringPreviewPanel(pParent);
case EResourceType::Scan: return new WScanPreviewPanel(pParent);
default: return nullptr;
}
}

View File

@ -1,18 +0,0 @@
#ifndef IPREVIEWPANEL_H
#define IPREVIEWPANEL_H
#include <Core/Resource/CResource.h>
#include <Core/Resource/EResType.h>
#include <QFrame>
class IPreviewPanel : public QFrame
{
public:
explicit IPreviewPanel(QWidget *parent = 0);
virtual EResourceType ResType() = 0;
virtual void SetResource(CResource *pRes) = 0;
static IPreviewPanel* CreatePanel(EResourceType Type, QWidget *pParent = 0);
};
#endif // IPREVIEWPANEL_H

View File

@ -1,99 +0,0 @@
#include "WScanPreviewPanel.h"
#include "ui_WScanPreviewPanel.h"
#include "WStringPreviewPanel.h"
#include <Core/Resource/CScan.h>
WScanPreviewPanel::WScanPreviewPanel(QWidget *pParent)
: IPreviewPanel(pParent)
, ui(new Ui::WScanPreviewPanel)
{
ui->setupUi(this);
ui->ScanTextWidget->setFrameShape(QFrame::NoFrame);
ui->ScanTextWidget->layout()->setContentsMargins(9,0,9,9);
}
WScanPreviewPanel::~WScanPreviewPanel()
{
delete ui;
}
QSize WScanPreviewPanel::sizeHint() const
{
return QSize(400, 0);
}
EResourceType WScanPreviewPanel::ResType()
{
return EResourceType::Scan;
}
void WScanPreviewPanel::SetResource(CResource *pRes)
{
// Clear existing UI
ui->ScanTypeLabel->clear();
ui->ScanSpeedLabel->clear();
ui->ScanCategoryLabel->clear();
// Set up new UI
if (pRes && (pRes->Type() == EResourceType::Scan))
{
CScan *pScan = static_cast<CScan*>(pRes);
// Scan type
if (pScan->IsImportant())
ui->ScanTypeLabel->setText("<b><font color=\"red\">Important</font></b>");
else
{
if (pScan->Game() <= EGame::Prime)
ui->ScanTypeLabel->setText("<b><font color=\"#FF9030\">Normal</font></b>");
else
ui->ScanTypeLabel->setText("<b><font color=\"#A0A0FF\">Normal</font></b>");
}
// Scan speed
if (pScan->IsSlow())
ui->ScanSpeedLabel->setText("<b><font color=\"blue\">Slow</font></b>");
else
ui->ScanSpeedLabel->setText("<b><font color=\"green\">Fast</font></b>");
// Scan category
switch (pScan->LogbookCategory())
{
case CScan::ELogbookCategory::None:
ui->ScanCategoryLabel->setText("<b>None</b>");
break;
case CScan::ELogbookCategory::ChozoLore:
ui->ScanCategoryLabel->setText("<b>Chozo Lore</b>");
break;
case CScan::ELogbookCategory::PirateData:
ui->ScanCategoryLabel->setText("<b>Pirate Data</b>");
break;
case CScan::ELogbookCategory::Creatures:
ui->ScanCategoryLabel->setText("<b>Creatures</b>");
break;
case CScan::ELogbookCategory::Research:
ui->ScanCategoryLabel->setText("<b>Research</b>");
break;
}
// Scan text
ui->ScanTextWidget->SetResource(pScan->ScanText());
// Show logbook category? (Yes on MP1, no on MP2+)
if (pScan->Game() <= EGame::Prime)
{
ui->CategoryInfoLabel->show();
ui->ScanCategoryLabel->show();
}
else
{
ui->CategoryInfoLabel->hide();
ui->ScanCategoryLabel->hide();
}
}
else
ui->ScanTextWidget->SetResource(nullptr);
}

View File

@ -1,25 +0,0 @@
#ifndef WSCANPREVIEWPANEL_H
#define WSCANPREVIEWPANEL_H
#include "IPreviewPanel.h"
namespace Ui {
class WScanPreviewPanel;
}
class WScanPreviewPanel : public IPreviewPanel
{
Q_OBJECT
public:
explicit WScanPreviewPanel(QWidget *pParent = 0);
~WScanPreviewPanel();
QSize sizeHint() const;
EResourceType ResType();
void SetResource(CResource *pRes);
private:
Ui::WScanPreviewPanel *ui;
};
#endif // WSCANPREVIEWPANEL_H

View File

@ -1,159 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WScanPreviewPanel</class>
<widget class="QWidget" name="WScanPreviewPanel">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>38</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>400</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="ScanInfoLayout">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="ScanTypeInfoLayout">
<item>
<widget class="QLabel" name="TypeInfoLabel">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="ScanTypeLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="HorizSpacer1">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="ScanCategoryInfoLayout">
<item>
<widget class="QLabel" name="CategoryInfoLabel">
<property name="text">
<string>Logbook:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="ScanCategoryLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="HorizSpacer2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="ScanSpeedInfoLayout">
<item>
<widget class="QLabel" name="SpeedInfoLabel">
<property name="text">
<string>Speed:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="ScanSpeedLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="WStringPreviewPanel" name="ScanTextWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>WStringPreviewPanel</class>
<extends>QWidget</extends>
<header>Editor/Widgets/WStringPreviewPanel.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -1,53 +0,0 @@
#include "WStringPreviewPanel.h"
#include "Editor/UICommon.h"
#include <Core/Resource/StringTable/CStringTable.h>
#include <QFontMetrics>
#include <QTextLayout>
WStringPreviewPanel::WStringPreviewPanel(QWidget *pParent)
: IPreviewPanel(pParent)
{
mpLayout = new QVBoxLayout(this);
mpLayout->setAlignment(Qt::AlignTop);
mpLayout->setSpacing(0);
setLayout(mpLayout);
}
WStringPreviewPanel::~WStringPreviewPanel()
{
}
QSize WStringPreviewPanel::sizeHint() const
{
return QSize(400, 0);
}
EResourceType WStringPreviewPanel::ResType()
{
return EResourceType::StringTable;
}
void WStringPreviewPanel::SetResource(CResource *pRes)
{
foreach(const QLabel *pLabel, mLabels)
delete pLabel;
mLabels.clear();
if (pRes && (pRes->Type() == EResourceType::StringTable))
{
CStringTable *pString = static_cast<CStringTable*>(pRes);
mLabels.reserve(pString->NumStrings());
for (uint32 iStr = 0; iStr < pString->NumStrings(); iStr++)
{
QString text = TO_QSTRING(pString->GetString(ELanguage::English, iStr));
QLabel *pLabel = new QLabel(text, this);
pLabel->setWordWrap(true);
pLabel->setFrameStyle(QFrame::Plain | QFrame::Box);
pLabel->setMargin(3);
mLabels.push_back(pLabel);
mpLayout->addWidget(pLabel);
}
}
}

View File

@ -1,24 +0,0 @@
#ifndef WSTRINGPREVIEWPANEL_H
#define WSTRINGPREVIEWPANEL_H
#include "IPreviewPanel.h"
#include <QLabel>
#include <QVBoxLayout>
#include <QVector>
class WStringPreviewPanel : public IPreviewPanel
{
Q_OBJECT
QVector<QLabel*> mLabels;
QVBoxLayout *mpLayout;
public:
explicit WStringPreviewPanel(QWidget *pParent = 0);
~WStringPreviewPanel();
QSize sizeHint() const;
EResourceType ResType();
void SetResource(CResource *pRes);
};
#endif // WSTRINGPREVIEWPANEL_H

View File

@ -1,155 +0,0 @@
#include "WTextureGLWidget.h"
#include <Common/Math/CTransform4f.h>
#include <Core/GameProject/CResourceStore.h>
#include <Core/Render/CDrawUtil.h>
#include <Core/Render/CGraphics.h>
#include <iostream>
#include <iomanip>
WTextureGLWidget::WTextureGLWidget(QWidget *pParent, CTexture *pTex)
: QOpenGLWidget(pParent)
{
SetTexture(pTex);
mInitialized = false;
}
WTextureGLWidget::~WTextureGLWidget()
{
if (mInitialized) CGraphics::ReleaseContext(mContextID);
}
void WTextureGLWidget::initializeGL()
{
CGraphics::Initialize();
glEnable(GL_BLEND);
mContextID = CGraphics::GetContextIndex();
mInitialized = true;
}
void WTextureGLWidget::paintGL()
{
CGraphics::SetActiveContext(mContextID);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(1.f, 0.f, 0.f, 0.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
// Set matrices to identity
CGraphics::sMVPBlock.ModelMatrix = CMatrix4f::skIdentity;
CGraphics::sMVPBlock.ViewMatrix = CMatrix4f::skIdentity;
CGraphics::sMVPBlock.ProjectionMatrix = CMatrix4f::skIdentity;
CGraphics::UpdateMVPBlock();
// Draw checkerboard background
CDrawUtil::UseTextureShader();
glDepthMask(GL_FALSE);
CDrawUtil::LoadCheckerboardTexture(0);
CDrawUtil::DrawSquare(&mCheckerCoords[0].X);
// Make it darker
CDrawUtil::UseColorShader(CColor::Integral(0.0f, 0.0f, 0.0f, 0.5f));
glDepthMask(GL_FALSE);
CDrawUtil::DrawSquare();
// Leave it at just the checkerboard if there's no texture
if (!mpTexture) return;
// Draw texture
CDrawUtil::UseTextureShader();
mpTexture->Bind(0);
CGraphics::sMVPBlock.ModelMatrix = mTexTransform;
CGraphics::UpdateMVPBlock();
CDrawUtil::DrawSquare();
glEnable(GL_DEPTH_TEST);
}
void WTextureGLWidget::resizeGL(int Width, int Height)
{
mAspectRatio = (float) Width / (float) Height;
glViewport(0, 0, Width, Height);
CalcTexTransform();
CalcCheckerCoords();
update();
}
void WTextureGLWidget::SetTexture(CTexture *pTex)
{
mpTexture = pTex;
if (pTex) mTexAspectRatio = (float) pTex->Width() / (float) pTex->Height();
else mTexAspectRatio = 0.f;
CalcTexTransform();
CalcCheckerCoords();
update();
}
void WTextureGLWidget::CalcTexTransform()
{
// This is a simple scale based on the dimensions of the viewport, in order to
// avoid stretching the texture if it doesn't match the viewport aspect ratio.
mTexTransform = CTransform4f::skIdentity;
float Diff = mTexAspectRatio / mAspectRatio;
if (mAspectRatio >= mTexAspectRatio)
mTexTransform.Scale(Diff, 1.f, 1.f);
else
mTexTransform.Scale(1.f, 1.f / Diff, 1.f);
}
void WTextureGLWidget::CalcCheckerCoords()
{
// The translation vector is set up so the checkerboard stays centered on the screen
// rather than expanding from the bottom-left corner. This makes it look more natural.
CVector2f Trans;
float InvAspect = (mAspectRatio == 0.f) ? 0.f : 1.f / mAspectRatio;
float InvTexAspect = (mTexAspectRatio == 0.f) ? 0.f : 1.f / mTexAspectRatio;
float XBase, YBase, XScale, YScale;
// Horizontal texture
if ((mpTexture != nullptr) && (mpTexture->Width() > mpTexture->Height()))
{
XBase = 1.f;
YBase = InvTexAspect;
XScale = InvTexAspect;
YScale = 1.f;
}
// Vertical texture
else
{
XBase = mTexAspectRatio;
YBase = 1.f;
XScale = 1.f;
YScale = mTexAspectRatio;
}
// Space on left/right
if (mAspectRatio > mTexAspectRatio)
{
Trans = CVector2f(mAspectRatio / 2.f, 0.5f) * -XScale;
mCheckerCoords[0] = CVector2f(0.f, YBase);
mCheckerCoords[1] = CVector2f(mAspectRatio * XScale, YBase);
mCheckerCoords[2] = CVector2f(mAspectRatio * XScale, 0.f);
mCheckerCoords[3] = CVector2f(0.f, 0.f);
}
// Space on top/bottom
else
{
Trans = CVector2f(0.5f, InvAspect / 2.f) * -YScale;
mCheckerCoords[0] = CVector2f(0.f, InvAspect * YScale);
mCheckerCoords[1] = CVector2f(XBase, InvAspect * YScale);
mCheckerCoords[2] = CVector2f(XBase, 0.f);
mCheckerCoords[3] = CVector2f(0.f, 0.f);
}
// Finally, apply translation/scale
for (uint32 iCoord = 0; iCoord < 4; iCoord++)
{
mCheckerCoords[iCoord] += Trans;
mCheckerCoords[iCoord] *= 10.f;
}
}

View File

@ -1,38 +0,0 @@
#ifndef WTEXTUREGLWIDGET_H
#define WTEXTUREGLWIDGET_H
#include <Common/Math/CTransform4f.h>
#include <Common/Math/CVector2f.h>
#include <Core/Resource/TResPtr.h>
#include <Core/Resource/CTexture.h>
#include <QOpenGLWidget>
#include <QTimer>
#include <GL/glew.h>
class WTextureGLWidget : public QOpenGLWidget
{
Q_OBJECT
float mAspectRatio;
TResPtr<CTexture> mpTexture;
float mTexAspectRatio;
CTransform4f mTexTransform;
CVector2f mCheckerCoords[4];
uint32 mContextID;
bool mInitialized;
public:
explicit WTextureGLWidget(QWidget *pParent = 0, CTexture *pTex = 0);
~WTextureGLWidget();
void initializeGL();
void paintGL();
void resizeGL(int Width, int Height);
void SetTexture(CTexture *pTex);
private:
void CalcTexTransform();
void CalcCheckerCoords();
};
#endif // WTEXTUREGLWIDGET_H

View File

@ -1,64 +0,0 @@
#include "WTexturePreviewPanel.h"
#include "ui_WTexturePreviewPanel.h"
#include "WTextureGLWidget.h"
#include "Editor/UICommon.h"
WTexturePreviewPanel::WTexturePreviewPanel(QWidget *parent, CTexture *pTexture)
: IPreviewPanel(parent)
, ui(new Ui::WTexturePreviewPanel)
{
ui->setupUi(this);
SetResource(pTexture);
}
WTexturePreviewPanel::~WTexturePreviewPanel()
{
delete ui;
}
EResourceType WTexturePreviewPanel::ResType()
{
return EResourceType::Texture;
}
void WTexturePreviewPanel::SetResource(CResource *pRes)
{
CTexture *pTexture = (CTexture*) pRes;
ui->TextureGLWidget->SetTexture(pTexture);
if (pTexture)
{
TString Name = pTexture->Source();
ui->TextureNameLabel->setText( TO_QSTRING(Name) );
QString TexInfo;
TexInfo += QString::number(pTexture->Width()) + "x" + QString::number(pTexture->Height());
TexInfo += " ";
switch (pTexture->SourceTexelFormat())
{
case ETexelFormat::GX_I4: TexInfo += "I4"; break;
case ETexelFormat::GX_I8: TexInfo += "I8"; break;
case ETexelFormat::GX_IA4: TexInfo += "IA4"; break;
case ETexelFormat::GX_IA8: TexInfo += "IA8"; break;
case ETexelFormat::GX_C4: TexInfo += "C4"; break;
case ETexelFormat::GX_C8: TexInfo += "C8"; break;
case ETexelFormat::GX_C14x2: TexInfo += "C14x2"; break;
case ETexelFormat::GX_RGB565: TexInfo += "RGB565"; break;
case ETexelFormat::GX_RGB5A3: TexInfo += "RGB5A3"; break;
case ETexelFormat::GX_RGBA8: TexInfo += "RGBA8"; break;
case ETexelFormat::GX_CMPR: TexInfo += "CMPR"; break;
default: TexInfo += "Invalid Format"; break;
}
TexInfo += " / ";
TexInfo += QString::number(pTexture->NumMipMaps()) + " mipmaps";
ui->TextureInfoLabel->setText(TexInfo);
}
else
{
ui->TextureNameLabel->setText("No texture");
ui->TextureInfoLabel->setText("");
}
}

View File

@ -1,25 +0,0 @@
#ifndef WTEXTUREPREVIEWPANEL_H
#define WTEXTUREPREVIEWPANEL_H
#include "IPreviewPanel.h"
#include <Core/Resource/CTexture.h>
namespace Ui {
class WTexturePreviewPanel;
}
class WTexturePreviewPanel : public IPreviewPanel
{
Q_OBJECT
public:
explicit WTexturePreviewPanel(QWidget *pParent = 0, CTexture *pTexture = 0);
~WTexturePreviewPanel();
EResourceType ResType();
void SetResource(CResource *pRes);
private:
Ui::WTexturePreviewPanel *ui;
};
#endif // WTEXTUREPREVIEWPANEL_H

View File

@ -1,77 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WTexturePreviewPanel</class>
<widget class="QWidget" name="WTexturePreviewPanel">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>260</width>
<height>305</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<item>
<widget class="WTextureGLWidget" name="TextureGLWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>256</width>
<height>256</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="TextureNameLabel">
<property name="text">
<string>TextureName</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="TextureInfoLabel">
<property name="text">
<string>TextureInfo</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>WTextureGLWidget</class>
<extends>QWidget</extends>
<header>Editor/Widgets/WTextureGLWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -4,7 +4,7 @@
#include "CPoiMapModel.h"
#include "Editor/UICommon.h"
#include <Core/Resource/CScan.h>
#include <Core/Resource/Scan/CScan.h>
#include <Core/Resource/Script/CScriptTemplate.h>
#include <Core/Scene/CScene.h>
#include <Core/ScriptExtra/CPointOfInterestExtra.h>
@ -57,7 +57,7 @@ public:
{
CScriptNode *pNode = mObjList[rkIndex.row()];
CScan *pScan = static_cast<CPointOfInterestExtra*>(pNode->Extra())->GetScan();
bool IsImportant = (pScan ? pScan->IsImportant() : false);
bool IsImportant = (pScan ? pScan->IsCriticalPropertyRef() : false);
if (IsImportant)
return QIcon(":/icons/POI Important.png");

View File

@ -52,7 +52,7 @@ QVariant CPoiMapModel::data(const QModelIndex& rkIndex, int Role) const
CScan *pScan = static_cast<CPointOfInterestExtra*>(pNode->Extra())->GetScan();
if (pScan)
IsImportant = pScan->IsImportant();
IsImportant = pScan->IsCriticalPropertyRef();
}
if (IsImportant)

View File

@ -3,8 +3,8 @@
#include "CWorldEditor.h"
#include "Editor/UICommon.h"
#include <Core/Resource/CScan.h>
#include <Core/Resource/Cooker/CPoiToWorldCooker.h>
#include <Core/Resource/Scan/CScan.h>
#include <Core/Resource/Script/CGameTemplate.h>
#include <Core/Resource/Script/NGameList.h>
#include <Core/ScriptExtra/CPointOfInterestExtra.h>
@ -171,7 +171,7 @@ bool CPoiMapSidebar::IsImportant(const QModelIndex& rkIndex)
TResPtr<CScan> pScan = static_cast<CPointOfInterestExtra*>(pPOI->Extra())->GetScan();
if (pScan)
Important = pScan->IsImportant();
Important = pScan->IsCriticalPropertyRef();
return Important;
}

View File

@ -23,6 +23,7 @@
<Element Name="Pane456" ID="0x11"/>
<Element Name="Pane567" ID="0x12"/>
<Element Name="Pane4567" ID="0x13"/>
<Element Name="None" ID="0xFFFFFFFF"/>
</Values>
</PropertyArchetype>
</PropertyTemplate>

View File

@ -10,7 +10,7 @@
<Element>FRME</Element>
</TypeFilter>
</Element>
<Element Type="Asset" ID="0x0">
<Element Type="Asset" ID="0x1">
<Name>String</Name>
<TypeFilter>
<Element>STRG</Element>

View File

@ -24,6 +24,8 @@
<Element>CMDL</Element>
</TypeFilter>
</Element>
<Element Type="AnimationSet" ID="0x15694EE1"/>
<Element Type="AnimationSet" ID="0x58F9FE99"/>
<Element Type="Struct" ID="0x1C5B4A3A" Archetype="ScanInfoSecondaryModel"/>
<Element Type="Struct" ID="0x8728A0EE" Archetype="ScanInfoSecondaryModel"/>
<Element Type="Struct" ID="0xF1CD99D3" Archetype="ScanInfoSecondaryModel"/>

View File

@ -8,7 +8,7 @@
<Element>CMDL</Element>
</TypeFilter>
</Element>
<Element Type="AnimationSet" ID="0x48B192CF"/>
<Element Type="AnimationSet" ID="0xCDD202D1"/>
<Element Type="String" ID="0x3EA2BED8"/>
</SubProperties>
</PropertyArchetype>