Merge branch 'assimp:master' into master
commit
9c1d3b13a2
|
@ -16,7 +16,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
name: [ubuntu-latest-g++, macos-latest-clang++, windows-latest-cl.exe, ubuntu-latest-clang++, ubuntu-gcc-hunter, macos-clang-hunter, windows-msvc-hunter]
|
name: [ubuntu-latest-g++, macos-latest-clang++, windows-latest-cl.exe, ubuntu-latest-clang++]
|
||||||
# For Windows msvc, for Linux and macOS let's use the clang compiler, use gcc for Linux.
|
# For Windows msvc, for Linux and macOS let's use the clang compiler, use gcc for Linux.
|
||||||
include:
|
include:
|
||||||
- name: windows-latest-cl.exe
|
- name: windows-latest-cl.exe
|
||||||
|
@ -35,15 +35,6 @@ jobs:
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
cxx: g++
|
cxx: g++
|
||||||
cc: gcc
|
cc: gcc
|
||||||
- name: ubuntu-gcc-hunter
|
|
||||||
os: ubuntu-latest
|
|
||||||
toolchain: ninja-gcc-cxx17-fpic
|
|
||||||
- name: macos-clang-hunter
|
|
||||||
os: macos-latest
|
|
||||||
toolchain: ninja-clang-cxx17-fpic
|
|
||||||
- name: windows-msvc-hunter
|
|
||||||
os: windows-latest
|
|
||||||
toolchain: ninja-vs-win64-cxx17
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
@ -55,25 +46,11 @@ jobs:
|
||||||
- uses: ilammy/msvc-dev-cmd@v1
|
- uses: ilammy/msvc-dev-cmd@v1
|
||||||
|
|
||||||
- name: Set Compiler Environment
|
- name: Set Compiler Environment
|
||||||
if: "!endsWith(matrix.name, 'hunter')"
|
|
||||||
uses: lukka/set-shell-env@v1
|
uses: lukka/set-shell-env@v1
|
||||||
with:
|
with:
|
||||||
CXX: ${{ matrix.cxx }}
|
CXX: ${{ matrix.cxx }}
|
||||||
CC: ${{ matrix.cc }}
|
CC: ${{ matrix.cc }}
|
||||||
|
|
||||||
- name: Set Compiler Environment for Hunter on Windows
|
|
||||||
if: startsWith(matrix.name, 'windows') && endsWith(matrix.name, 'hunter')
|
|
||||||
uses: lukka/set-shell-env@v1
|
|
||||||
with:
|
|
||||||
VS160COMNTOOLS: C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools
|
|
||||||
|
|
||||||
- name: Checkout Hunter toolchains
|
|
||||||
if: endsWith(matrix.name, 'hunter')
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
repository: cpp-pm/polly
|
|
||||||
path: cmake/polly
|
|
||||||
|
|
||||||
- name: Cache DX SDK
|
- name: Cache DX SDK
|
||||||
id: dxcache
|
id: dxcache
|
||||||
if: contains(matrix.name, 'windows')
|
if: contains(matrix.name, 'windows')
|
||||||
|
|
1
Build.md
1
Build.md
|
@ -43,6 +43,7 @@ cd assimp
|
||||||
cmake CMakeLists.txt
|
cmake CMakeLists.txt
|
||||||
cmake --build .
|
cmake --build .
|
||||||
```
|
```
|
||||||
|
Note that by default this builds a shared library into the `bin` directory. If you want to build it as a static library see the build options at the bottom of this file.
|
||||||
|
|
||||||
### Build instructions for Windows with Visual-Studio
|
### Build instructions for Windows with Visual-Studio
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,7 @@ IF (WIN32)
|
||||||
ELSE()
|
ELSE()
|
||||||
OPTION( ASSIMP_BUILD_ZLIB
|
OPTION( ASSIMP_BUILD_ZLIB
|
||||||
"Build your own zlib"
|
"Build your own zlib"
|
||||||
ON
|
OFF
|
||||||
)
|
)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,10 @@ Open Asset Import Library is a library to load various 3d file formats into a sh
|
||||||
APIs are provided for C and C++. There are various bindings to other languages (C#, Java, Python, Delphi, D). Assimp also runs on Android and iOS.
|
APIs are provided for C and C++. There are various bindings to other languages (C#, Java, Python, Delphi, D). Assimp also runs on Android and iOS.
|
||||||
Additionally, assimp features various __mesh post-processing tools__: normals and tangent space generation, triangulation, vertex cache locality optimization, removal of degenerate primitives and duplicate vertices, sorting by primitive type, merging of redundant materials and many more.
|
Additionally, assimp features various __mesh post-processing tools__: normals and tangent space generation, triangulation, vertex cache locality optimization, removal of degenerate primitives and duplicate vertices, sorting by primitive type, merging of redundant materials and many more.
|
||||||
|
|
||||||
### Latest Doc's ###
|
### Documentation ###
|
||||||
Please check the latest documents at [Asset-Importer-Lib-Doc](https://assimp-docs.readthedocs.io/en/latest/).
|
Please check the latest documents at [Asset-Importer-Lib-Doc](https://assimp-docs.readthedocs.io/en/latest/).
|
||||||
|
|
||||||
### Prebuild binaries ###
|
### Pre-built binaries ###
|
||||||
Please check our [Itchi Projectspace](https://kimkulling.itch.io/the-asset-importer-lib)
|
Please check our [Itchi Projectspace](https://kimkulling.itch.io/the-asset-importer-lib)
|
||||||
|
|
||||||
If you want to check our Model-Database, use the following repo: https://github.com/assimp/assimp-mdb
|
If you want to check our Model-Database, use the following repo: https://github.com/assimp/assimp-mdb
|
||||||
|
@ -35,7 +35,7 @@ And we also have a Gitter-channel:Gitter [
|
You can find the complete list of supported file-formats [here](https://github.com/assimp/assimp/blob/master/doc/Fileformats.md)
|
||||||
|
|
||||||
### Building ###
|
### Building ###
|
||||||
Take a look into the https://github.com/assimp/assimp/blob/master/Build.md file. We are available in vcpkg, and our build system is CMake; if you used CMake before there is a good chance you know what to do.
|
Take a look [here](https://github.com/assimp/assimp/blob/master/Build.md) to get started. We are available in vcpkg, and our build system is CMake; if you used CMake before there is a good chance you know what to do.
|
||||||
|
|
||||||
### Ports ###
|
### Ports ###
|
||||||
* [Android](port/AndroidJNI/README.md)
|
* [Android](port/AndroidJNI/README.md)
|
||||||
|
@ -52,7 +52,7 @@ Take a look into the https://github.com/assimp/assimp/blob/master/Build.md file.
|
||||||
|
|
||||||
### Other tools ###
|
### Other tools ###
|
||||||
[open3mod](https://github.com/acgessler/open3mod) is a powerful 3D model viewer based on Assimp's import and export abilities.
|
[open3mod](https://github.com/acgessler/open3mod) is a powerful 3D model viewer based on Assimp's import and export abilities.
|
||||||
[Assimp-Viewer(]https://github.com/assimp/assimp_view) is an experimental implementation for an Asset-Viewer based on ImGUI and Assimp (experimental).
|
[Assimp-Viewer](https://github.com/assimp/assimp_view) is an experimental implementation for an Asset-Viewer based on ImGUI and Assimp (experimental).
|
||||||
|
|
||||||
#### Repository structure ####
|
#### Repository structure ####
|
||||||
Open Asset Import Library is implemented in C++. The directory structure looks like this:
|
Open Asset Import Library is implemented in C++. The directory structure looks like this:
|
||||||
|
|
|
@ -593,7 +593,7 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene *pcSOut, aiNode *pcOut,
|
||||||
|
|
||||||
// Cameras or lights define their transformation in their parent node and in the
|
// Cameras or lights define their transformation in their parent node and in the
|
||||||
// corresponding light or camera chunks. However, we read and process the latter
|
// corresponding light or camera chunks. However, we read and process the latter
|
||||||
// to to be able to return valid cameras/lights even if no scenegraph is given.
|
// to be able to return valid cameras/lights even if no scenegraph is given.
|
||||||
for (unsigned int n = 0; n < pcSOut->mNumCameras; ++n) {
|
for (unsigned int n = 0; n < pcSOut->mNumCameras; ++n) {
|
||||||
if (pcSOut->mCameras[n]->mName == pcOut->mName) {
|
if (pcSOut->mCameras[n]->mName == pcOut->mName) {
|
||||||
pcSOut->mCameras[n]->mLookAt = aiVector3D(0.f, 0.f, 1.f);
|
pcSOut->mCameras[n]->mLookAt = aiVector3D(0.f, 0.f, 1.f);
|
||||||
|
|
|
@ -365,7 +365,7 @@ void Discreet3DSImporter::ParseChunk(const char *name, unsigned int num) {
|
||||||
// IMPLEMENTATION NOTE;
|
// IMPLEMENTATION NOTE;
|
||||||
// Cameras or lights define their transformation in their parent node and in the
|
// Cameras or lights define their transformation in their parent node and in the
|
||||||
// corresponding light or camera chunks. However, we read and process the latter
|
// corresponding light or camera chunks. However, we read and process the latter
|
||||||
// to to be able to return valid cameras/lights even if no scenegraph is given.
|
// to be able to return valid cameras/lights even if no scenegraph is given.
|
||||||
|
|
||||||
// get chunk type
|
// get chunk type
|
||||||
switch (chunk.Flag) {
|
switch (chunk.Flag) {
|
||||||
|
|
|
@ -81,12 +81,17 @@ static constexpr aiImporterDesc desc = {
|
||||||
"3mf"
|
"3mf"
|
||||||
};
|
};
|
||||||
|
|
||||||
bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool /*checkSig*/) const {
|
bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool ) const {
|
||||||
if (!ZipArchiveIOSystem::isZipArchive(pIOHandler, filename)) {
|
if (!ZipArchiveIOSystem::isZipArchive(pIOHandler, filename)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename);
|
static const char *const ModelRef = "3D/3dmodel.model";
|
||||||
return opcPackage.validate();
|
ZipArchiveIOSystem archive(pIOHandler, filename);
|
||||||
|
if (!archive.Exists(ModelRef)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3MFImporter::SetupProperties(const Importer*) {
|
void D3MFImporter::SetupProperties(const Importer*) {
|
||||||
|
|
|
@ -904,7 +904,7 @@ void ASEImporter::ConvertMeshes(ASE::Mesh &mesh, std::vector<aiMesh *> &avOutMes
|
||||||
ASSIMP_LOG_WARN("Material index is out of range");
|
ASSIMP_LOG_WARN("Material index is out of range");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the material the mesh is assigned to is consisting of submeshes, split it
|
// If the material the mesh is assigned to consists of submeshes, split it
|
||||||
if (!mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials.empty()) {
|
if (!mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials.empty()) {
|
||||||
std::vector<ASE::Material> vSubMaterials = mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials;
|
std::vector<ASE::Material> vSubMaterials = mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials;
|
||||||
|
|
||||||
|
|
|
@ -50,11 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <assimp/version.h>
|
#include <assimp/version.h>
|
||||||
#include <assimp/IOStream.hpp>
|
#include <assimp/IOStream.hpp>
|
||||||
|
|
||||||
#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
|
#include "zlib.h"
|
||||||
#include <zlib.h>
|
|
||||||
#else
|
|
||||||
#include "../contrib/zlib/zlib.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
|
|
|
@ -283,6 +283,11 @@ void BlenderModifier_Subdivision ::DoIt(aiNode &out, ConversionData &conv_data,
|
||||||
if (conv_data.meshes->empty()) {
|
if (conv_data.meshes->empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const size_t meshIndex = conv_data.meshes->size() - out.mNumMeshes;
|
||||||
|
if (meshIndex >= conv_data.meshes->size()) {
|
||||||
|
ASSIMP_LOG_ERROR("Invalid index detected.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
aiMesh **const meshes = &conv_data.meshes[conv_data.meshes->size() - out.mNumMeshes];
|
aiMesh **const meshes = &conv_data.meshes[conv_data.meshes->size() - out.mNumMeshes];
|
||||||
std::unique_ptr<aiMesh *[]> tempmeshes(new aiMesh *[out.mNumMeshes]());
|
std::unique_ptr<aiMesh *[]> tempmeshes(new aiMesh *[out.mNumMeshes]());
|
||||||
|
|
||||||
|
|
|
@ -297,7 +297,7 @@ void Structure ::Convert<Base>(
|
||||||
const FileDatabase &db) const {
|
const FileDatabase &db) const {
|
||||||
// note: as per https://github.com/assimp/assimp/issues/128,
|
// note: as per https://github.com/assimp/assimp/issues/128,
|
||||||
// reading the Object linked list recursively is prone to stack overflow.
|
// reading the Object linked list recursively is prone to stack overflow.
|
||||||
// This structure converter is therefore an hand-written exception that
|
// This structure converter is therefore a hand-written exception that
|
||||||
// does it iteratively.
|
// does it iteratively.
|
||||||
|
|
||||||
const int initial_pos = db.reader->GetCurrentPos();
|
const int initial_pos = db.reader->GetCurrentPos();
|
||||||
|
|
|
@ -814,38 +814,38 @@ void ColladaParser::ReadImage(XmlNode &node, Collada::Image &pImage) {
|
||||||
if (!pImage.mFileName.length()) {
|
if (!pImage.mFileName.length()) {
|
||||||
pImage.mFileName = "unknown_texture";
|
pImage.mFileName = "unknown_texture";
|
||||||
}
|
}
|
||||||
}
|
} else if (mFormat == FV_1_5_n) {
|
||||||
} else if (mFormat == FV_1_5_n) {
|
std::string value;
|
||||||
std::string value;
|
XmlNode refChild = currentNode.child("ref");
|
||||||
XmlNode refChild = currentNode.child("ref");
|
XmlNode hexChild = currentNode.child("hex");
|
||||||
XmlNode hexChild = currentNode.child("hex");
|
if (refChild) {
|
||||||
if (refChild) {
|
// element content is filename - hopefully
|
||||||
// element content is filename - hopefully
|
if (XmlParser::getValueAsString(refChild, value)) {
|
||||||
if (XmlParser::getValueAsString(refChild, value)) {
|
aiString filepath(value);
|
||||||
aiString filepath(value);
|
UriDecodePath(filepath);
|
||||||
UriDecodePath(filepath);
|
pImage.mFileName = filepath.C_Str();
|
||||||
pImage.mFileName = filepath.C_Str();
|
}
|
||||||
}
|
} else if (hexChild && !pImage.mFileName.length()) {
|
||||||
} else if (hexChild && !pImage.mFileName.length()) {
|
// embedded image. get format
|
||||||
// embedded image. get format
|
pImage.mEmbeddedFormat = hexChild.attribute("format").as_string();
|
||||||
pImage.mEmbeddedFormat = hexChild.attribute("format").as_string();
|
if (pImage.mEmbeddedFormat.empty()) {
|
||||||
if (pImage.mEmbeddedFormat.empty()) {
|
ASSIMP_LOG_WARN("Collada: Unknown image file format");
|
||||||
ASSIMP_LOG_WARN("Collada: Unknown image file format");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
XmlParser::getValueAsString(hexChild, value);
|
XmlParser::getValueAsString(hexChild, value);
|
||||||
const char *data = value.c_str();
|
const char *data = value.c_str();
|
||||||
// hexadecimal-encoded binary octets. First of all, find the
|
// hexadecimal-encoded binary octets. First of all, find the
|
||||||
// required buffer size to reserve enough storage.
|
// required buffer size to reserve enough storage.
|
||||||
const char *cur = data;
|
const char *cur = data;
|
||||||
while (!IsSpaceOrNewLine(*cur)) {
|
while (!IsSpaceOrNewLine(*cur)) {
|
||||||
++cur;
|
++cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned int size = (unsigned int)(cur - data) * 2;
|
const unsigned int size = (unsigned int)(cur - data) * 2;
|
||||||
pImage.mImageData.resize(size);
|
pImage.mImageData.resize(size);
|
||||||
for (unsigned int i = 0; i < size; ++i) {
|
for (unsigned int i = 0; i < size; ++i) {
|
||||||
pImage.mImageData[i] = HexOctetToDecimal(data + (i << 1));
|
pImage.mImageData[i] = HexOctetToDecimal(data + (i << 1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1274,9 +1274,7 @@ void ColladaParser::ReadEffectParam(XmlNode &node, Collada::EffectParam &pParam)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode);
|
for (XmlNode ¤tNode : node.children()) {
|
||||||
XmlNode currentNode;
|
|
||||||
while (xmlIt.getNext(currentNode)) {
|
|
||||||
const std::string ¤tName = currentNode.name();
|
const std::string ¤tName = currentNode.name();
|
||||||
if (currentName == "surface") {
|
if (currentName == "surface") {
|
||||||
// image ID given inside <init_from> tags
|
// image ID given inside <init_from> tags
|
||||||
|
@ -1289,22 +1287,24 @@ void ColladaParser::ReadEffectParam(XmlNode &node, Collada::EffectParam &pParam)
|
||||||
}
|
}
|
||||||
} else if (currentName == "sampler2D" && (FV_1_4_n == mFormat || FV_1_3_n == mFormat)) {
|
} else if (currentName == "sampler2D" && (FV_1_4_n == mFormat || FV_1_3_n == mFormat)) {
|
||||||
// surface ID is given inside <source> tags
|
// surface ID is given inside <source> tags
|
||||||
const char *content = currentNode.value();
|
XmlNode source = currentNode.child("source");
|
||||||
pParam.mType = Param_Sampler;
|
if (source) {
|
||||||
pParam.mReference = content;
|
std::string v;
|
||||||
|
XmlParser::getValueAsString(source, v);
|
||||||
|
pParam.mType = Param_Sampler;
|
||||||
|
pParam.mReference = v.c_str();
|
||||||
|
}
|
||||||
} else if (currentName == "sampler2D") {
|
} else if (currentName == "sampler2D") {
|
||||||
// surface ID is given inside <instance_image> tags
|
// surface ID is given inside <instance_image> tags
|
||||||
std::string url;
|
XmlNode instance_image = currentNode.child("instance_image");
|
||||||
XmlParser::getStdStrAttribute(currentNode, "url", url);
|
if (instance_image) {
|
||||||
if (url[0] != '#') {
|
std::string url;
|
||||||
throw DeadlyImportError("Unsupported URL format in instance_image");
|
XmlParser::getStdStrAttribute(instance_image, "url", url);
|
||||||
}
|
if (url[0] != '#') {
|
||||||
pParam.mType = Param_Sampler;
|
throw DeadlyImportError("Unsupported URL format in instance_image");
|
||||||
pParam.mReference = url.c_str() + 1;
|
}
|
||||||
} else if (currentName == "source") {
|
pParam.mType = Param_Sampler;
|
||||||
const char *source = currentNode.child_value();
|
pParam.mReference = url.c_str() + 1;
|
||||||
if (nullptr != source) {
|
|
||||||
pParam.mReference = source;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1867,7 +1867,7 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<Inp
|
||||||
|
|
||||||
///@note This function won't work correctly if both PerIndex and PerVertex channels have same channels.
|
///@note This function won't work correctly if both PerIndex and PerVertex channels have same channels.
|
||||||
///For example if TEXCOORD present in both <vertices> and <polylist> tags this function will create wrong uv coordinates.
|
///For example if TEXCOORD present in both <vertices> and <polylist> tags this function will create wrong uv coordinates.
|
||||||
///It's not clear from COLLADA documentation is this allowed or not. For now only exporter fixed to avoid such behavior
|
///It's not clear from COLLADA documentation whether this is allowed or not. For now only exporter fixed to avoid such behavior
|
||||||
void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset, Mesh &pMesh,
|
void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset, Mesh &pMesh,
|
||||||
std::vector<InputChannel> &pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t> &indices) {
|
std::vector<InputChannel> &pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t> &indices) {
|
||||||
// calculate the base offset of the vertex whose attributes we ant to copy
|
// calculate the base offset of the vertex whose attributes we ant to copy
|
||||||
|
|
|
@ -55,9 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include <assimp/MathFunctions.h>
|
#include <assimp/MathFunctions.h>
|
||||||
#include <assimp/StringComparison.h>
|
#include <assimp/StringComparison.h>
|
||||||
|
|
||||||
#include <assimp/scene.h>
|
#include <assimp/scene.h>
|
||||||
|
|
||||||
#include <assimp/CreateAnimMesh.h>
|
#include <assimp/CreateAnimMesh.h>
|
||||||
#include <assimp/StringUtils.h>
|
#include <assimp/StringUtils.h>
|
||||||
#include <assimp/commonMetaData.h>
|
#include <assimp/commonMetaData.h>
|
||||||
|
@ -1206,7 +1204,7 @@ unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, c
|
||||||
const auto &curNormals = shapeGeometry->GetNormals();
|
const auto &curNormals = shapeGeometry->GetNormals();
|
||||||
const auto &curIndices = shapeGeometry->GetIndices();
|
const auto &curIndices = shapeGeometry->GetIndices();
|
||||||
//losing channel name if using shapeGeometry->Name()
|
//losing channel name if using shapeGeometry->Name()
|
||||||
// if blendShapeChannel Name is empty or don't have a ".", add geoMetryName;
|
// if blendShapeChannel Name is empty or doesn't have a ".", add geoMetryName;
|
||||||
auto aniName = FixAnimMeshName(blendShapeChannel->Name());
|
auto aniName = FixAnimMeshName(blendShapeChannel->Name());
|
||||||
auto geoMetryName = FixAnimMeshName(shapeGeometry->Name());
|
auto geoMetryName = FixAnimMeshName(shapeGeometry->Name());
|
||||||
if (aniName.empty()) {
|
if (aniName.empty()) {
|
||||||
|
@ -1604,7 +1602,7 @@ void FBXConverter::ConvertWeights(aiMesh *out, const MeshGeometry &geo, const ai
|
||||||
|
|
||||||
void FBXConverter::ConvertCluster(std::vector<aiBone*> &local_mesh_bones, const Cluster *cluster,
|
void FBXConverter::ConvertCluster(std::vector<aiBone*> &local_mesh_bones, const Cluster *cluster,
|
||||||
std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices,
|
std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices,
|
||||||
std::vector<size_t> &count_out_indices, const aiMatrix4x4 & /* absolute_transform*/,
|
std::vector<size_t> &count_out_indices, const aiMatrix4x4 &absolute_transform,
|
||||||
aiNode *) {
|
aiNode *) {
|
||||||
ai_assert(cluster != nullptr); // make sure cluster valid
|
ai_assert(cluster != nullptr); // make sure cluster valid
|
||||||
|
|
||||||
|
@ -1621,16 +1619,16 @@ void FBXConverter::ConvertCluster(std::vector<aiBone*> &local_mesh_bones, const
|
||||||
bone = new aiBone();
|
bone = new aiBone();
|
||||||
bone->mName = bone_name;
|
bone->mName = bone_name;
|
||||||
|
|
||||||
bone->mOffsetMatrix = cluster->Transform();
|
//bone->mOffsetMatrix = cluster->Transform();
|
||||||
// store local transform link for post processing
|
// store local transform link for post processing
|
||||||
/*
|
|
||||||
bone->mOffsetMatrix = cluster->TransformLink();
|
bone->mOffsetMatrix = cluster->TransformLink();
|
||||||
bone->mOffsetMatrix.Inverse();
|
bone->mOffsetMatrix.Inverse();
|
||||||
|
|
||||||
aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform;
|
const aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform;
|
||||||
|
|
||||||
bone->mOffsetMatrix = bone->mOffsetMatrix * matrix; // * mesh_offset
|
bone->mOffsetMatrix = bone->mOffsetMatrix * matrix; // * mesh_offset
|
||||||
*/
|
|
||||||
//
|
//
|
||||||
// Now calculate the aiVertexWeights
|
// Now calculate the aiVertexWeights
|
||||||
//
|
//
|
||||||
|
|
|
@ -84,7 +84,7 @@ Cluster::Cluster(uint64_t id, const Element& element, const Document& doc, const
|
||||||
transform = ReadMatrix(Transform);
|
transform = ReadMatrix(Transform);
|
||||||
transformLink = ReadMatrix(TransformLink);
|
transformLink = ReadMatrix(TransformLink);
|
||||||
|
|
||||||
// it is actually possible that there be Deformer's with no weights
|
// it is actually possible that there are Deformer's with no weights
|
||||||
if (!!Indexes != !!Weights) {
|
if (!!Indexes != !!Weights) {
|
||||||
DOMError("either Indexes or Weights are missing from Cluster",&element);
|
DOMError("either Indexes or Weights are missing from Cluster",&element);
|
||||||
}
|
}
|
||||||
|
|
|
@ -390,7 +390,7 @@ void Document::ReadObjects() {
|
||||||
const auto foundObject = objects.find(id);
|
const auto foundObject = objects.find(id);
|
||||||
if(foundObject != objects.end()) {
|
if(foundObject != objects.end()) {
|
||||||
DOMWarning("encountered duplicate object id, ignoring first occurrence",el.second);
|
DOMWarning("encountered duplicate object id, ignoring first occurrence",el.second);
|
||||||
delete foundObject->second;
|
delete_LazyObject(foundObject->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
objects[id] = new_LazyObject(id, *el.second, *this);
|
objects[id] = new_LazyObject(id, *el.second, *this);
|
||||||
|
|
|
@ -1061,14 +1061,14 @@ aiMatrix4x4 get_world_transform(const aiNode* node, const aiScene* scene)
|
||||||
return transform;
|
return transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t to_ktime(double ticks, const aiAnimation* anim) {
|
inline int64_t to_ktime(double ticks, const aiAnimation* anim) {
|
||||||
if (anim->mTicksPerSecond <= 0) {
|
if (anim->mTicksPerSecond <= 0) {
|
||||||
return static_cast<int64_t>(ticks) * FBX::SECOND;
|
return static_cast<int64_t>(ticks) * FBX::SECOND;
|
||||||
}
|
}
|
||||||
return (static_cast<int64_t>(ticks) / static_cast<int64_t>(anim->mTicksPerSecond)) * FBX::SECOND;
|
return (static_cast<int64_t>(ticks / anim->mTicksPerSecond)) * FBX::SECOND;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t to_ktime(double time) {
|
inline int64_t to_ktime(double time) {
|
||||||
return (static_cast<int64_t>(time * FBX::SECOND));
|
return (static_cast<int64_t>(time * FBX::SECOND));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2413,7 +2413,7 @@ void FBXExporter::WriteObjects ()
|
||||||
// position/translation
|
// position/translation
|
||||||
for (size_t ki = 0; ki < na->mNumPositionKeys; ++ki) {
|
for (size_t ki = 0; ki < na->mNumPositionKeys; ++ki) {
|
||||||
const aiVectorKey& k = na->mPositionKeys[ki];
|
const aiVectorKey& k = na->mPositionKeys[ki];
|
||||||
times.push_back(to_ktime(k.mTime));
|
times.push_back(to_ktime(k.mTime, anim));
|
||||||
xval.push_back(k.mValue.x);
|
xval.push_back(k.mValue.x);
|
||||||
yval.push_back(k.mValue.y);
|
yval.push_back(k.mValue.y);
|
||||||
zval.push_back(k.mValue.z);
|
zval.push_back(k.mValue.z);
|
||||||
|
@ -2427,7 +2427,7 @@ void FBXExporter::WriteObjects ()
|
||||||
times.clear(); xval.clear(); yval.clear(); zval.clear();
|
times.clear(); xval.clear(); yval.clear(); zval.clear();
|
||||||
for (size_t ki = 0; ki < na->mNumRotationKeys; ++ki) {
|
for (size_t ki = 0; ki < na->mNumRotationKeys; ++ki) {
|
||||||
const aiQuatKey& k = na->mRotationKeys[ki];
|
const aiQuatKey& k = na->mRotationKeys[ki];
|
||||||
times.push_back(to_ktime(k.mTime));
|
times.push_back(to_ktime(k.mTime, anim));
|
||||||
// TODO: aiQuaternion method to convert to Euler...
|
// TODO: aiQuaternion method to convert to Euler...
|
||||||
aiMatrix4x4 m(k.mValue.GetMatrix());
|
aiMatrix4x4 m(k.mValue.GetMatrix());
|
||||||
aiVector3D qs, qr, qt;
|
aiVector3D qs, qr, qt;
|
||||||
|
@ -2445,7 +2445,7 @@ void FBXExporter::WriteObjects ()
|
||||||
times.clear(); xval.clear(); yval.clear(); zval.clear();
|
times.clear(); xval.clear(); yval.clear(); zval.clear();
|
||||||
for (size_t ki = 0; ki < na->mNumScalingKeys; ++ki) {
|
for (size_t ki = 0; ki < na->mNumScalingKeys; ++ki) {
|
||||||
const aiVectorKey& k = na->mScalingKeys[ki];
|
const aiVectorKey& k = na->mScalingKeys[ki];
|
||||||
times.push_back(to_ktime(k.mTime));
|
times.push_back(to_ktime(k.mTime, anim));
|
||||||
xval.push_back(k.mValue.x);
|
xval.push_back(k.mValue.x);
|
||||||
yval.push_back(k.mValue.y);
|
yval.push_back(k.mValue.y);
|
||||||
zval.push_back(k.mValue.z);
|
zval.push_back(k.mValue.z);
|
||||||
|
|
|
@ -367,7 +367,9 @@ Video::Video(uint64_t id, const Element &element, const Document &doc, const std
|
||||||
}
|
}
|
||||||
|
|
||||||
Video::~Video() {
|
Video::~Video() {
|
||||||
delete[] content;
|
if (contentLength > 0) {
|
||||||
|
delete[] content;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} //!FBX
|
} //!FBX
|
||||||
|
|
|
@ -467,9 +467,9 @@ void ResolveVertexDataArray(std::vector<T>& data_out, const Scope& source,
|
||||||
std::vector<int> uvIndices;
|
std::vector<int> uvIndices;
|
||||||
ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName));
|
ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName));
|
||||||
|
|
||||||
if (uvIndices.size() != vertex_count) {
|
if (uvIndices.size() != mapping_offsets.size()) {
|
||||||
FBXImporter::LogError("length of input data unexpected for ByVertice mapping: ",
|
FBXImporter::LogError("length of input data unexpected for ByVertice mapping: ",
|
||||||
uvIndices.size(), ", expected ", vertex_count);
|
uvIndices.size(), ", expected ", mapping_offsets.size());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,12 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
|
||||||
|
|
||||||
//#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
|
|
||||||
#include "Common/Compression.h"
|
#include "Common/Compression.h"
|
||||||
//# include <zlib.h>
|
|
||||||
//#else
|
|
||||||
//# include "../contrib/zlib/zlib.h"
|
|
||||||
//#endif
|
|
||||||
|
|
||||||
#include "FBXTokenizer.h"
|
#include "FBXTokenizer.h"
|
||||||
#include "FBXParser.h"
|
#include "FBXParser.h"
|
||||||
|
|
|
@ -155,7 +155,7 @@ size_t DecodeBase64(const char* in, size_t inLength, uint8_t* out, size_t maxOut
|
||||||
const size_t realLength = inLength - size_t(in[inLength - 1] == '=') - size_t(in[inLength - 2] == '=');
|
const size_t realLength = inLength - size_t(in[inLength - 1] == '=') - size_t(in[inLength - 2] == '=');
|
||||||
size_t dst_offset = 0;
|
size_t dst_offset = 0;
|
||||||
int val = 0, valb = -8;
|
int val = 0, valb = -8;
|
||||||
for (size_t src_offset = 0; src_offset < realLength; ++src_offset)
|
for (size_t src_offset = 0; src_offset < realLength && dst_offset < maxOutLength; ++src_offset)
|
||||||
{
|
{
|
||||||
const uint8_t table_value = Util::DecodeBase64(in[src_offset]);
|
const uint8_t table_value = Util::DecodeBase64(in[src_offset]);
|
||||||
if (table_value == 255)
|
if (table_value == 255)
|
||||||
|
|
|
@ -1234,7 +1234,10 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
|
||||||
// Parse the XML
|
// Parse the XML
|
||||||
// Find the scene root from document root.
|
// Find the scene root from document root.
|
||||||
const pugi::xml_node &sceneRoot = documentRoot.child("irr_scene");
|
const pugi::xml_node &sceneRoot = documentRoot.child("irr_scene");
|
||||||
if (!sceneRoot) throw new DeadlyImportError("IRR: <irr_scene> not found in file");
|
if (!sceneRoot) {
|
||||||
|
delete root;
|
||||||
|
throw new DeadlyImportError("IRR: <irr_scene> not found in file");
|
||||||
|
}
|
||||||
for (pugi::xml_node &child : sceneRoot.children()) {
|
for (pugi::xml_node &child : sceneRoot.children()) {
|
||||||
// XML elements are either nodes, animators, attributes, or materials
|
// XML elements are either nodes, animators, attributes, or materials
|
||||||
if (!ASSIMP_stricmp(child.name(), "node")) {
|
if (!ASSIMP_stricmp(child.name(), "node")) {
|
||||||
|
|
|
@ -123,9 +123,8 @@ aiColor4D MDLImporter::ReplaceTextureWithColor(const aiTexture *pcTexture) {
|
||||||
// Read a texture from a MDL3 file
|
// Read a texture from a MDL3 file
|
||||||
void MDLImporter::CreateTextureARGB8_3DGS_MDL3(const unsigned char *szData) {
|
void MDLImporter::CreateTextureARGB8_3DGS_MDL3(const unsigned char *szData) {
|
||||||
const MDL::Header *pcHeader = (const MDL::Header *)mBuffer; //the endianness is already corrected in the InternReadFile_3DGS_MDL345 function
|
const MDL::Header *pcHeader = (const MDL::Header *)mBuffer; //the endianness is already corrected in the InternReadFile_3DGS_MDL345 function
|
||||||
|
const size_t len = pcHeader->skinwidth * pcHeader->skinheight;
|
||||||
VALIDATE_FILE_SIZE(szData + pcHeader->skinwidth *
|
VALIDATE_FILE_SIZE(szData + len);
|
||||||
pcHeader->skinheight);
|
|
||||||
|
|
||||||
// allocate a new texture object
|
// allocate a new texture object
|
||||||
aiTexture *pcNew = new aiTexture();
|
aiTexture *pcNew = new aiTexture();
|
||||||
|
|
|
@ -42,11 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include "MMDPmxParser.h"
|
#include "MMDPmxParser.h"
|
||||||
#include <assimp/StringUtils.h>
|
#include <assimp/StringUtils.h>
|
||||||
#ifdef ASSIMP_USE_HUNTER
|
#include "utf8.h"
|
||||||
# include <utf8.h>
|
|
||||||
#else
|
|
||||||
# include "../contrib/utf8cpp/source/utf8.h"
|
|
||||||
#endif
|
|
||||||
#include <assimp/Exceptional.h>
|
#include <assimp/Exceptional.h>
|
||||||
|
|
||||||
namespace pmx
|
namespace pmx
|
||||||
|
@ -93,7 +89,7 @@ namespace pmx
|
||||||
{
|
{
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
buffer.reserve(size);
|
buffer.resize(size);
|
||||||
stream->read((char*) buffer.data(), size);
|
stream->read((char*) buffer.data(), size);
|
||||||
if (encoding == 0)
|
if (encoding == 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -425,7 +425,8 @@ bool PLY::DOM::ParseHeader(IOStreamBuffer<char> &streamBuffer, std::vector<char>
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
// ignore unknown header elements
|
// ignore unknown header elements
|
||||||
streamBuffer.getNextLine(buffer);
|
if (!streamBuffer.getNextLine(buffer))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,11 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include <assimp/DefaultLogger.hpp>
|
#include <assimp/DefaultLogger.hpp>
|
||||||
|
|
||||||
#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
|
#include "zlib.h"
|
||||||
#include <zlib.h>
|
|
||||||
#else
|
|
||||||
#include "../contrib/zlib/zlib.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <assimp/DefaultIOSystem.h>
|
#include <assimp/DefaultIOSystem.h>
|
||||||
#include <assimp/StringComparison.h>
|
#include <assimp/StringComparison.h>
|
||||||
|
@ -399,7 +395,10 @@ void Q3BSPFileImporter::createTriangleTopology(const Q3BSP::Q3BSPModel *pModel,
|
||||||
m_pCurrentFace->mIndices = new unsigned int[3];
|
m_pCurrentFace->mIndices = new unsigned int[3];
|
||||||
m_pCurrentFace->mIndices[idx] = vertIdx;
|
m_pCurrentFace->mIndices[idx] = vertIdx;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
m_pCurrentFace->mIndices[idx] = vertIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pMesh->mVertices[vertIdx].Set(pVertex->vPosition.x, pVertex->vPosition.y, pVertex->vPosition.z);
|
pMesh->mVertices[vertIdx].Set(pVertex->vPosition.x, pVertex->vPosition.y, pVertex->vPosition.z);
|
||||||
pMesh->mNormals[vertIdx].Set(pVertex->vNormal.x, pVertex->vNormal.y, pVertex->vNormal.z);
|
pMesh->mNormals[vertIdx].Set(pVertex->vNormal.x, pVertex->vNormal.y, pVertex->vNormal.z);
|
||||||
|
|
|
@ -56,11 +56,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <assimp/ByteSwapper.h>
|
#include <assimp/ByteSwapper.h>
|
||||||
#include <assimp/StreamReader.h>
|
#include <assimp/StreamReader.h>
|
||||||
#include <assimp/TinyFormatter.h>
|
#include <assimp/TinyFormatter.h>
|
||||||
#ifdef ASSIMP_USE_HUNTER
|
#include "utf8.h"
|
||||||
#include <utf8.h>
|
|
||||||
#else
|
|
||||||
#include "../contrib/utf8cpp/source/utf8.h"
|
|
||||||
#endif
|
|
||||||
#include <assimp/importerdesc.h>
|
#include <assimp/importerdesc.h>
|
||||||
#include <assimp/scene.h>
|
#include <assimp/scene.h>
|
||||||
#include <assimp/DefaultLogger.hpp>
|
#include <assimp/DefaultLogger.hpp>
|
||||||
|
|
|
@ -45,11 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
#include "STEPFileEncoding.h"
|
#include "STEPFileEncoding.h"
|
||||||
#include <assimp/fast_atof.h>
|
#include <assimp/fast_atof.h>
|
||||||
#ifdef ASSIMP_USE_HUNTER
|
#include "utf8.h"
|
||||||
# include <utf8.h>
|
|
||||||
#else
|
|
||||||
# include <contrib/utf8cpp/source/utf8.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
|
|
@ -720,7 +720,7 @@ const vec4 defaultBaseColor = { 1, 1, 1, 1 };
|
||||||
const vec3 defaultEmissiveFactor = { 0, 0, 0 };
|
const vec3 defaultEmissiveFactor = { 0, 0, 0 };
|
||||||
const vec4 defaultDiffuseFactor = { 1, 1, 1, 1 };
|
const vec4 defaultDiffuseFactor = { 1, 1, 1, 1 };
|
||||||
const vec3 defaultSpecularFactor = { 1, 1, 1 };
|
const vec3 defaultSpecularFactor = { 1, 1, 1 };
|
||||||
const vec3 defaultSpecularColorFactor = { 0, 0, 0 };
|
const vec3 defaultSpecularColorFactor = { 1, 1, 1 };
|
||||||
const vec3 defaultSheenFactor = { 0, 0, 0 };
|
const vec3 defaultSheenFactor = { 0, 0, 0 };
|
||||||
const vec3 defaultAttenuationColor = { 1, 1, 1 };
|
const vec3 defaultAttenuationColor = { 1, 1, 1 };
|
||||||
|
|
||||||
|
|
|
@ -1392,7 +1392,7 @@ inline void PbrSpecularGlossiness::SetDefaults() {
|
||||||
inline void MaterialSpecular::SetDefaults() {
|
inline void MaterialSpecular::SetDefaults() {
|
||||||
//KHR_materials_specular properties
|
//KHR_materials_specular properties
|
||||||
SetVector(specularColorFactor, defaultSpecularColorFactor);
|
SetVector(specularColorFactor, defaultSpecularColorFactor);
|
||||||
specularFactor = 0.f;
|
specularFactor = 1.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void MaterialSheen::SetDefaults() {
|
inline void MaterialSheen::SetDefaults() {
|
||||||
|
@ -1552,6 +1552,22 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(this->targetNames.empty())
|
||||||
|
{
|
||||||
|
Value *curExtras = FindObject(primitive, "extras");
|
||||||
|
if (nullptr != curExtras) {
|
||||||
|
if (Value *curTargetNames = FindArray(*curExtras, "targetNames")) {
|
||||||
|
this->targetNames.resize(curTargetNames->Size());
|
||||||
|
for (unsigned int j = 0; j < curTargetNames->Size(); ++j) {
|
||||||
|
Value &targetNameValue = (*curTargetNames)[j];
|
||||||
|
if (targetNameValue.IsString()) {
|
||||||
|
this->targetNames[j] = targetNameValue.GetString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -426,13 +426,14 @@ namespace glTF2 {
|
||||||
|
|
||||||
if (specular.specularFactor != 0.0f) {
|
if (specular.specularFactor != 0.0f) {
|
||||||
WriteFloat(materialSpecular, specular.specularFactor, "specularFactor", w.mAl);
|
WriteFloat(materialSpecular, specular.specularFactor, "specularFactor", w.mAl);
|
||||||
WriteTex(materialSpecular, specular.specularTexture, "specularTexture", w.mAl);
|
|
||||||
}
|
}
|
||||||
if (specular.specularColorFactor[0] != defaultSpecularColorFactor[0] && specular.specularColorFactor[1] != defaultSpecularColorFactor[1] && specular.specularColorFactor[2] != defaultSpecularColorFactor[2]) {
|
if (specular.specularColorFactor[0] != defaultSpecularColorFactor[0] && specular.specularColorFactor[1] != defaultSpecularColorFactor[1] && specular.specularColorFactor[2] != defaultSpecularColorFactor[2]) {
|
||||||
WriteVec(materialSpecular, specular.specularColorFactor, "specularColorFactor", w.mAl);
|
WriteVec(materialSpecular, specular.specularColorFactor, "specularColorFactor", w.mAl);
|
||||||
WriteTex(materialSpecular, specular.specularColorTexture, "specularColorTexture", w.mAl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WriteTex(materialSpecular, specular.specularTexture, "specularTexture", w.mAl);
|
||||||
|
WriteTex(materialSpecular, specular.specularColorTexture, "specularColorTexture", w.mAl);
|
||||||
|
|
||||||
if (!materialSpecular.ObjectEmpty()) {
|
if (!materialSpecular.ObjectEmpty()) {
|
||||||
exts.AddMember("KHR_materials_specular", materialSpecular, w.mAl);
|
exts.AddMember("KHR_materials_specular", materialSpecular, w.mAl);
|
||||||
}
|
}
|
||||||
|
|
|
@ -730,8 +730,8 @@ bool glTF2Exporter::GetMatSpecular(const aiMaterial &mat, glTF2::MaterialSpecula
|
||||||
} else if (colorFactorIsZero) {
|
} else if (colorFactorIsZero) {
|
||||||
specular.specularColorFactor[0] = specular.specularColorFactor[1] = specular.specularColorFactor[2] = 1.0f;
|
specular.specularColorFactor[0] = specular.specularColorFactor[1] = specular.specularColorFactor[2] = 1.0f;
|
||||||
}
|
}
|
||||||
GetMatTex(mat, specular.specularColorTexture, aiTextureType_SPECULAR);
|
GetMatTex(mat, specular.specularTexture, aiTextureType_SPECULAR, 0);
|
||||||
GetMatTex(mat, specular.specularTexture, aiTextureType_SPECULAR);
|
GetMatTex(mat, specular.specularColorTexture, aiTextureType_SPECULAR, 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -288,8 +288,8 @@ static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, M
|
||||||
if (std::memcmp(specular.specularColorFactor, defaultSpecularColorFactor, sizeof(glTFCommon::vec3)) != 0 || specular.specularFactor != 0.0f) {
|
if (std::memcmp(specular.specularColorFactor, defaultSpecularColorFactor, sizeof(glTFCommon::vec3)) != 0 || specular.specularFactor != 0.0f) {
|
||||||
SetMaterialColorProperty(r, specular.specularColorFactor, aimat, AI_MATKEY_COLOR_SPECULAR);
|
SetMaterialColorProperty(r, specular.specularColorFactor, aimat, AI_MATKEY_COLOR_SPECULAR);
|
||||||
aimat->AddProperty(&specular.specularFactor, 1, AI_MATKEY_SPECULAR_FACTOR);
|
aimat->AddProperty(&specular.specularFactor, 1, AI_MATKEY_SPECULAR_FACTOR);
|
||||||
SetMaterialTextureProperty(embeddedTexIdxs, r, specular.specularTexture, aimat, aiTextureType_SPECULAR);
|
SetMaterialTextureProperty(embeddedTexIdxs, r, specular.specularTexture, aimat, aiTextureType_SPECULAR, 0);
|
||||||
SetMaterialTextureProperty(embeddedTexIdxs, r, specular.specularColorTexture, aimat, aiTextureType_SPECULAR);
|
SetMaterialTextureProperty(embeddedTexIdxs, r, specular.specularColorTexture, aimat, aiTextureType_SPECULAR, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// pbrSpecularGlossiness
|
// pbrSpecularGlossiness
|
||||||
|
|
|
@ -357,11 +357,7 @@ std::string BaseImporter::GetExtension(const std::string &pFile) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ASSIMP_USE_HUNTER
|
#include "utf8.h"
|
||||||
#include <utf8.h>
|
|
||||||
#else
|
|
||||||
#include "../contrib/utf8cpp/source/utf8.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Convert to UTF8 data
|
// Convert to UTF8 data
|
||||||
|
|
|
@ -41,11 +41,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
|
#include "zlib.h"
|
||||||
# include <zlib.h>
|
|
||||||
#else
|
|
||||||
# include "../contrib/zlib/zlib.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
|
|
|
@ -99,12 +99,12 @@ bool DefaultIOSystem::Exists(const char *pFile) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
FILE *file = ::fopen(pFile, "rb");
|
struct stat statbuf;
|
||||||
if (!file) {
|
stat(pFile, &statbuf);
|
||||||
|
// test for a regular file
|
||||||
|
if (!S_ISREG(statbuf.st_mode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
::fclose(file);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -116,6 +116,7 @@ IOStream *DefaultIOSystem::Open(const char *strFile, const char *strMode) {
|
||||||
ai_assert(strFile != nullptr);
|
ai_assert(strFile != nullptr);
|
||||||
ai_assert(strMode != nullptr);
|
ai_assert(strMode != nullptr);
|
||||||
FILE *file;
|
FILE *file;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
std::wstring name = Utf8ToWide(strFile);
|
std::wstring name = Utf8ToWide(strFile);
|
||||||
if (name.empty()) {
|
if (name.empty()) {
|
||||||
|
@ -126,6 +127,7 @@ IOStream *DefaultIOSystem::Open(const char *strFile, const char *strMode) {
|
||||||
#else
|
#else
|
||||||
file = ::fopen(strFile, strMode);
|
file = ::fopen(strFile, strMode);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,22 +89,27 @@ using namespace Assimp;
|
||||||
|
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
|
|
||||||
void ExportScenePbrt (
|
void ExportScenePbrt(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene,
|
||||||
const char* pFile,
|
const ExportProperties *) {
|
||||||
IOSystem* pIOSystem,
|
|
||||||
const aiScene* pScene,
|
|
||||||
const ExportProperties* /*pProperties*/
|
|
||||||
){
|
|
||||||
std::string path = DefaultIOSystem::absolutePath(std::string(pFile));
|
std::string path = DefaultIOSystem::absolutePath(std::string(pFile));
|
||||||
std::string file = DefaultIOSystem::completeBaseName(std::string(pFile));
|
std::string file = DefaultIOSystem::completeBaseName(std::string(pFile));
|
||||||
|
path = path + file + ".pbrt";
|
||||||
// initialize the exporter
|
// initialize the exporter
|
||||||
PbrtExporter exporter(pScene, pIOSystem, path, file);
|
PbrtExporter exporter(pScene, pIOSystem, path, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end of namespace Assimp
|
} // end of namespace Assimp
|
||||||
|
|
||||||
// Constructor
|
static void create_embedded_textures_folder(const aiScene *scene, IOSystem *pIOSystem) {
|
||||||
|
if (scene->mNumTextures > 0) {
|
||||||
|
if (!pIOSystem->Exists("textures")) {
|
||||||
|
if (!pIOSystem->CreateDirectory("textures")) {
|
||||||
|
throw DeadlyExportError("Could not create textures/ directory.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PbrtExporter::PbrtExporter(
|
PbrtExporter::PbrtExporter(
|
||||||
const aiScene *pScene, IOSystem *pIOSystem,
|
const aiScene *pScene, IOSystem *pIOSystem,
|
||||||
const std::string &path, const std::string &file) :
|
const std::string &path, const std::string &file) :
|
||||||
|
@ -127,10 +132,10 @@ PbrtExporter::PbrtExporter(
|
||||||
0.f, 0.f, 1.f, 0.f, //
|
0.f, 0.f, 1.f, 0.f, //
|
||||||
0.f, 0.f, 0.f, 1.f //
|
0.f, 0.f, 0.f, 1.f //
|
||||||
) * mRootTransform;
|
) * mRootTransform;
|
||||||
|
|
||||||
// Export embedded textures.
|
// Export embedded textures.
|
||||||
if (mScene->mNumTextures > 0)
|
create_embedded_textures_folder(mScene, mIOSystem);
|
||||||
if (!mIOSystem->CreateDirectory("textures"))
|
|
||||||
throw DeadlyExportError("Could not create textures/ directory.");
|
|
||||||
for (unsigned int i = 0; i < mScene->mNumTextures; ++i) {
|
for (unsigned int i = 0; i < mScene->mNumTextures; ++i) {
|
||||||
aiTexture* tex = mScene->mTextures[i];
|
aiTexture* tex = mScene->mTextures[i];
|
||||||
std::string fn = CleanTextureFilename(tex->mFilename, false);
|
std::string fn = CleanTextureFilename(tex->mFilename, false);
|
||||||
|
@ -176,9 +181,6 @@ PbrtExporter::PbrtExporter(
|
||||||
outfile->Write(mOutput.str().c_str(), mOutput.str().length(), 1);
|
outfile->Write(mOutput.str().c_str(), mOutput.str().length(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destructor
|
|
||||||
PbrtExporter::~PbrtExporter() = default;
|
|
||||||
|
|
||||||
void PbrtExporter::WriteMetaData() {
|
void PbrtExporter::WriteMetaData() {
|
||||||
mOutput << "#############################\n";
|
mOutput << "#############################\n";
|
||||||
mOutput << "# Scene metadata:\n";
|
mOutput << "# Scene metadata:\n";
|
||||||
|
|
|
@ -70,15 +70,33 @@ class ExportProperties;
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
/** Helper class to export a given scene to a Pbrt file. */
|
/** Helper class to export a given scene to a Pbrt file. */
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
class PbrtExporter
|
class PbrtExporter {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
/// Constructor for a specific scene to export
|
/// Constructor for a specific scene to export
|
||||||
PbrtExporter(const aiScene *pScene, IOSystem *pIOSystem,
|
PbrtExporter(const aiScene *pScene, IOSystem *pIOSystem,
|
||||||
const std::string &path, const std::string &file);
|
const std::string &path, const std::string &file);
|
||||||
|
|
||||||
/// Destructor
|
/// Destructor
|
||||||
virtual ~PbrtExporter();
|
virtual ~PbrtExporter() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
aiMatrix4x4 GetNodeTransform(const aiString &name) const;
|
||||||
|
static std::string TransformAsString(const aiMatrix4x4 &m);
|
||||||
|
static std::string RemoveSuffix(std::string filename);
|
||||||
|
std::string CleanTextureFilename(const aiString &f, bool rewriteExtension = true) const;
|
||||||
|
void WriteMetaData();
|
||||||
|
void WriteWorldDefinition();
|
||||||
|
void WriteCameras();
|
||||||
|
void WriteCamera(int i);
|
||||||
|
void WriteLights();
|
||||||
|
void WriteTextures();
|
||||||
|
static bool TextureHasAlphaMask(const std::string &filename);
|
||||||
|
void WriteMaterials();
|
||||||
|
void WriteMaterial(int i);
|
||||||
|
void WriteMesh(aiMesh *mesh);
|
||||||
|
void WriteInstanceDefinition(int i);
|
||||||
|
void WriteGeometricObjects(aiNode *node, aiMatrix4x4 parentTransform,
|
||||||
|
std::map<int, int> &meshUses);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// the scene to export
|
// the scene to export
|
||||||
|
@ -96,39 +114,11 @@ private:
|
||||||
/// Name of the file (without extension) where the scene will be exported
|
/// Name of the file (without extension) where the scene will be exported
|
||||||
const std::string mFile;
|
const std::string mFile;
|
||||||
|
|
||||||
private:
|
|
||||||
// A private set to keep track of which textures have been declared
|
// A private set to keep track of which textures have been declared
|
||||||
std::set<std::string> mTextureSet;
|
std::set<std::string> mTextureSet;
|
||||||
|
|
||||||
// Transform to apply to the root node and all root objects such as cameras, lights, etc.
|
// Transform to apply to the root node and all root objects such as cameras, lights, etc.
|
||||||
aiMatrix4x4 mRootTransform;
|
aiMatrix4x4 mRootTransform;
|
||||||
|
|
||||||
aiMatrix4x4 GetNodeTransform(const aiString& name) const;
|
|
||||||
static std::string TransformAsString(const aiMatrix4x4& m);
|
|
||||||
|
|
||||||
static std::string RemoveSuffix(std::string filename);
|
|
||||||
std::string CleanTextureFilename(const aiString &f, bool rewriteExtension = true) const;
|
|
||||||
|
|
||||||
void WriteMetaData();
|
|
||||||
|
|
||||||
void WriteWorldDefinition();
|
|
||||||
|
|
||||||
void WriteCameras();
|
|
||||||
void WriteCamera(int i);
|
|
||||||
|
|
||||||
void WriteLights();
|
|
||||||
|
|
||||||
void WriteTextures();
|
|
||||||
static bool TextureHasAlphaMask(const std::string &filename);
|
|
||||||
|
|
||||||
void WriteMaterials();
|
|
||||||
void WriteMaterial(int i);
|
|
||||||
|
|
||||||
void WriteMesh(aiMesh* mesh);
|
|
||||||
|
|
||||||
void WriteInstanceDefinition(int i);
|
|
||||||
void WriteGeometricObjects(aiNode* node, aiMatrix4x4 parentTransform,
|
|
||||||
std::map<int, int> &meshUses);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Assimp
|
} // namespace Assimp
|
||||||
|
|
|
@ -287,7 +287,7 @@ static AI_FORCE_INLINE bool isEndOfCache(size_t pos, size_t cacheSize) {
|
||||||
template <class T>
|
template <class T>
|
||||||
AI_FORCE_INLINE bool IOStreamBuffer<T>::getNextLine(std::vector<T> &buffer) {
|
AI_FORCE_INLINE bool IOStreamBuffer<T>::getNextLine(std::vector<T> &buffer) {
|
||||||
buffer.resize(m_cacheSize);
|
buffer.resize(m_cacheSize);
|
||||||
if (isEndOfCache(m_cachePos, m_cacheSize) || 0 == m_filePos) {
|
if (m_cachePos >= m_cacheSize || 0 == m_filePos) {
|
||||||
if (!readNextBlock()) {
|
if (!readNextBlock()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,7 +97,7 @@ public:
|
||||||
* Create an instance of your derived class and assign it to an
|
* Create an instance of your derived class and assign it to an
|
||||||
* #Assimp::Importer instance by calling Importer::SetIOHandler().
|
* #Assimp::Importer instance by calling Importer::SetIOHandler().
|
||||||
*/
|
*/
|
||||||
IOSystem() AI_NO_EXCEPT;
|
IOSystem() AI_NO_EXCEPT = default;
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** @brief Virtual destructor.
|
/** @brief Virtual destructor.
|
||||||
|
@ -105,7 +105,7 @@ public:
|
||||||
* It is safe to be called from within DLL Assimp, we're constructed
|
* It is safe to be called from within DLL Assimp, we're constructed
|
||||||
* on Assimp's heap.
|
* on Assimp's heap.
|
||||||
*/
|
*/
|
||||||
virtual ~IOSystem();
|
virtual ~IOSystem() = default;
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** @brief For backward compatibility
|
/** @brief For backward compatibility
|
||||||
|
@ -236,12 +236,6 @@ private:
|
||||||
std::vector<std::string> m_pathStack;
|
std::vector<std::string> m_pathStack;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
AI_FORCE_INLINE IOSystem::IOSystem() AI_NO_EXCEPT = default;
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
AI_FORCE_INLINE IOSystem::~IOSystem() = default;
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// For compatibility, the interface of some functions taking a std::string was
|
// For compatibility, the interface of some functions taking a std::string was
|
||||||
// changed to const char* to avoid crashes between binary incompatible STL
|
// changed to const char* to avoid crashes between binary incompatible STL
|
||||||
|
|
|
@ -165,6 +165,7 @@ SET( IMPORTERS
|
||||||
unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp
|
unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp
|
||||||
unit/ImportExport/RAW/utRAWImportExport.cpp
|
unit/ImportExport/RAW/utRAWImportExport.cpp
|
||||||
unit/ImportExport/Terragen/utTerragenImportExport.cpp
|
unit/ImportExport/Terragen/utTerragenImportExport.cpp
|
||||||
|
unit/ImportExport/Pbrt/utPbrtImportExport.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
SET( MATERIAL
|
SET( MATERIAL
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
Open Asset Import Library (assimp)
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2006-2020, assimp team
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use of this software in source and binary forms,
|
||||||
|
with or without modification, are permitted provided that the following
|
||||||
|
conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above
|
||||||
|
copyright notice, this list of conditions and the
|
||||||
|
following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the
|
||||||
|
following disclaimer in the documentation and/or other
|
||||||
|
materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the assimp team, nor the names of its
|
||||||
|
contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior
|
||||||
|
written permission of the assimp team.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "AbstractImportExportBase.h"
|
||||||
|
#include "UnitTestPCH.h"
|
||||||
|
#include <assimp/postprocess.h>
|
||||||
|
#include <assimp/scene.h>
|
||||||
|
#include <assimp/Importer.hpp>
|
||||||
|
#include <assimp/Exporter.hpp>
|
||||||
|
|
||||||
|
using namespace Assimp;
|
||||||
|
|
||||||
|
class utPbrtImportExport : public AbstractImportExportBase {
|
||||||
|
public:
|
||||||
|
#ifndef ASSIMP_BUILD_NO_EXPORT
|
||||||
|
bool exporterTest() override {
|
||||||
|
Assimp::Importer importer;
|
||||||
|
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure);
|
||||||
|
EXPECT_NE(scene, nullptr );
|
||||||
|
|
||||||
|
::Assimp::Exporter exporter;
|
||||||
|
return AI_SUCCESS == exporter.Export(scene, "pbrt", ASSIMP_TEST_MODELS_DIR "/OBJ/spider_out.pbrt");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef ASSIMP_BUILD_NO_EXPORT
|
||||||
|
|
||||||
|
TEST_F(utPbrtImportExport, exportTest_Success) {
|
||||||
|
EXPECT_TRUE(exporterTest());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ASSIMP_BUILD_NO_EXPORT
|
|
@ -196,3 +196,10 @@ TEST_F(utPLYImportExport, parseErrorTest) {
|
||||||
const aiScene *scene = importer.ReadFileFromMemory(test_file, strlen(test_file), 0);
|
const aiScene *scene = importer.ReadFileFromMemory(test_file, strlen(test_file), 0);
|
||||||
EXPECT_NE(nullptr, scene);
|
EXPECT_NE(nullptr, scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This file is invalid, we just want to ensure that the importer is not crashing
|
||||||
|
TEST_F(utPLYImportExport, parseInvalid) {
|
||||||
|
Assimp::Importer importer;
|
||||||
|
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/invalid/crash-30d6d0f7c529b3b66b4131700b7a4580cd7082df.ply", 0);
|
||||||
|
EXPECT_EQ(nullptr, scene);
|
||||||
|
}
|
||||||
|
|
|
@ -291,7 +291,7 @@ int LoadAsset() {
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------
|
||||||
// Delete the loaded asset
|
// Delete the loaded asset
|
||||||
// The function does nothing is no asset is loaded
|
// The function does nothing if no asset is loaded
|
||||||
//-------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------
|
||||||
int DeleteAsset(void) {
|
int DeleteAsset(void) {
|
||||||
if (!g_pcAsset) {
|
if (!g_pcAsset) {
|
||||||
|
|
Loading…
Reference in New Issue