From 4644474135317fd92858523a2225ba839652e423 Mon Sep 17 00:00:00 2001 From: codereader Date: Sun, 4 Apr 2021 20:16:34 +0200 Subject: [PATCH] #5576: Implement the triangle submission code and remove tons of unused code. --- radiantcore/model/import/AseModel.cpp | 417 +++++--------------------- 1 file changed, 68 insertions(+), 349 deletions(-) diff --git a/radiantcore/model/import/AseModel.cpp b/radiantcore/model/import/AseModel.cpp index d1eaf9a982..f28a390939 100644 --- a/radiantcore/model/import/AseModel.cpp +++ b/radiantcore/model/import/AseModel.cpp @@ -8,6 +8,8 @@ /* ----------------------------------------------------------------------------- +ASE Loading Code based on the original PicoModel ASE parser (licence as follows) + PicoModel Library Copyright (c) 2002, Randy Reddig & seaw0lf @@ -64,7 +66,7 @@ struct AseMaterial vOffset(0), uTiling(1), vTiling(1), - angle(0) + uvAngle(0) {} std::string materialName; // *MATERIAL_NAME @@ -74,362 +76,78 @@ struct AseMaterial float vOffset; // * UVW_V_OFFSET float uTiling; // * UVW_U_TILING float vTiling; // * UVW_V_TILING - float angle; // * UVW_ANGLE + float uvAngle; // * UVW_ANGLE }; -} - -/* dependencies */ -#include "../picomodel/lib/picointernal.h" - -/* plain white */ -static picoColor_t white = { 255, 255, 255, 255 }; - -/* jhefty - multi-subobject material support */ - -/* Material/SubMaterial management */ -/* A material should have 1..n submaterials assigned to it */ - -typedef struct aseSubMaterial_s -{ - struct aseSubMaterial_s* next; - int subMtlId; - picoShader_t* shader; - - float uOffset; /* UVW_U_OFFSET */ - float vOffset; /* UVW_V_OFFSET */ - float uScale; /* UVW_U_TILING */ - float vScale; /* UVW_V_TILING */ - float uvAngle; /* UVW_ANGLE */ -} aseSubMaterial_t; - -typedef struct aseMaterial_s -{ - struct aseMaterial_s* next; - struct aseSubMaterial_s* subMtls; - int mtlId; -} aseMaterial_t; - -/* Material/SubMaterial management functions */ -static aseMaterial_t* _ase_get_material ( aseMaterial_t* list , int mtlIdParent ) -{ - aseMaterial_t* mtl = list; - - while ( mtl ) - { - if ( mtlIdParent == mtl->mtlId ) - { - break; - } - mtl = mtl->next; - } - return mtl; -} - -static aseSubMaterial_t* _ase_get_submaterial ( aseMaterial_t* list, int mtlIdParent , int subMtlId ) -{ - aseMaterial_t* parent = _ase_get_material ( list , mtlIdParent ); - aseSubMaterial_t* subMtl = NULL; - - if ( !parent ) - { - _pico_printf ( PICO_ERROR , "No ASE material exists with id %i\n" , mtlIdParent ); - return NULL; - } - - subMtl = parent->subMtls; - while ( subMtl ) - { - if ( subMtlId == subMtl->subMtlId ) - { - break; - } - subMtl = subMtl->next; - } - return subMtl; -} - -aseSubMaterial_t* _ase_get_submaterial_or_default ( aseMaterial_t* materials, int mtlIdParent , int subMtlId ) -{ - aseSubMaterial_t* subMtl = _ase_get_submaterial( materials, mtlIdParent, subMtlId ); - if(subMtl != NULL) - { - return subMtl; - } - - /* ydnar: trying default submaterial */ - subMtl = _ase_get_submaterial( materials, mtlIdParent, 0 ); - if( subMtl != NULL ) - { - return subMtl; - } - - _pico_printf( PICO_ERROR, "Could not find material/submaterial for id %d/%d\n", mtlIdParent, subMtlId ); - return NULL; -} - - - - -static aseMaterial_t* _ase_add_material( aseMaterial_t **list, int mtlIdParent ) -{ - aseMaterial_t *mtl = (aseMaterial_t*)_pico_calloc( 1, sizeof( aseMaterial_t ) ); - mtl->mtlId = mtlIdParent; - mtl->subMtls = NULL; - mtl->next = *list; - *list = mtl; - - return mtl; -} - -static aseSubMaterial_t* _ase_add_submaterial( aseMaterial_t **list, int mtlIdParent, int subMtlId, picoShader_t* shader ) -{ - aseMaterial_t *parent = _ase_get_material( *list, mtlIdParent ); - aseSubMaterial_t *subMtl = (aseSubMaterial_t*)_pico_calloc( 1, sizeof ( aseSubMaterial_t ) ); - - /* Initialise some values */ - subMtl->uOffset = 0.0f; - subMtl->vOffset = 0.0f; - subMtl->uScale = 1.0f; - subMtl->vScale = 1.0f; - subMtl->uvAngle = 0.0f; - - if ( !parent ) - { - parent = _ase_add_material ( list , mtlIdParent ); - } - - subMtl->shader = shader; - subMtl->subMtlId = subMtlId; - subMtl->next = parent->subMtls; - parent->subMtls = subMtl; - - return subMtl; -} - -static void _ase_free_materials( aseMaterial_t **list ) +void _ase_submit_triangles(model::AseModel& model, + std::vector& materials, std::size_t materialIndex, + std::vector& vertices, std::vector& normals, std::vector& texcoords, + std::vector& colours, std::vector& faces) { - aseMaterial_t* mtl = *list; - aseSubMaterial_t* subMtl = NULL; - - aseMaterial_t* mtlTemp = NULL; - aseSubMaterial_t* subMtlTemp = NULL; - - while ( mtl ) - { - subMtl = mtl->subMtls; - while ( subMtl ) - { - subMtlTemp = subMtl->next; - _pico_free ( subMtl ); - subMtl = subMtlTemp; - } - mtlTemp = mtl->next; - _pico_free ( mtl ); - mtl = mtlTemp; - } - (*list) = NULL; -} - -/* todo: - * - apply material specific uv offsets to uv coordinates - */ - -/* _ase_canload: - * validates a 3dsmax ase model file. - */ -static int _ase_canload( PM_PARAMS_CANLOAD ) -{ - picoParser_t *p; - - - /* quick data length validation */ - if( bufSize < 80 ) - return PICO_PMV_ERROR_SIZE; - - /* keep the friggin compiler happy */ - *fileName = *fileName; + assert(vertices.size() == normals.size()); - /* create pico parser */ - p = _pico_new_parser( (picoByte_t*) buffer, bufSize ); - if( p == NULL ) - return PICO_PMV_ERROR_MEMORY; - - /* get first token */ - if( _pico_parse_first( p ) == NULL) - { - return PICO_PMV_ERROR_IDENT; - } - - /* check first token */ - if( _pico_stricmp( p->token, "*3dsmax_asciiexport" ) ) - { - _pico_free_parser( p ); - return PICO_PMV_ERROR_IDENT; - } + if (materialIndex >= materials.size()) + { + throw parser::ParseException(fmt::format("Cannot submit triangles, material index {0} is out of range", materialIndex)); + } - /* free the pico parser object */ - _pico_free_parser( p ); + const auto& material = materials[materialIndex]; - /* file seems to be a valid ase file */ - return PICO_PMV_OK; -} + // submit the triangle to the model + auto& surface = model.ensureSurface(material.diffuseBitmap); -typedef struct aseVertex_s aseVertex_t; -struct aseVertex_s -{ - picoVec3_t xyz; - picoVec3_t normal; - picoIndex_t id; -}; - -typedef struct aseTexCoord_s aseTexCoord_t; -struct aseTexCoord_s -{ - picoVec2_t texcoord; -}; + surface.vertices.reserve(surface.vertices.size() + vertices.size()); + surface.indices.reserve(surface.indices.size() + faces.size() * 3); -typedef struct aseColor_s aseColor_t; -struct aseColor_s -{ - picoColor_t color; -}; + double materialSin = sin(material.uvAngle); + double materialCos = cos(material.uvAngle); -typedef struct aseFace_s aseFace_t; -struct aseFace_s -{ - picoIndex_t indices[9]; - picoIndex_t smoothingGroup; - picoIndex_t materialId; - picoIndex_t subMaterialId; -}; -typedef aseFace_t* aseFacesIter_t; + for (const auto& face : faces) + { + /* we pull the data from the vertex, color and texcoord arrays using the face index data */ + for (int j = 0 ; j < 3 ; j ++ ) + { + auto nextIndex = static_cast(surface.indices.size()); -picoSurface_t* PicoModelFindOrAddSurface( picoModel_t *model, picoShader_t* shader ) -{ - /* see if a surface already has the shader */ - int i = 0; - for ( ; i < model->numSurfaces ; i++ ) - { - picoSurface_t* workSurface = model->surface[i]; - if ( workSurface->shader == shader ) - { - return workSurface; - } - } - - /* no surface uses this shader yet, so create a new surface */ - - { - /* create a new surface in the model for the unique shader */ - picoSurface_t* workSurface = PicoNewSurface(model); - if ( !workSurface ) - { - _pico_printf ( PICO_ERROR , "Could not allocate a new surface!\n" ); - return 0; - } - - /* do surface setup */ - PicoSetSurfaceType( workSurface, PICO_TRIANGLES ); - PicoSetSurfaceName( workSurface, shader->name ); - PicoSetSurfaceShader( workSurface, shader ); - - return workSurface; - } -} + auto& vertex = vertices[face.vertexIndices[j]]; + auto& normal = normals[face.vertexIndices[j]]; -void _ase_submit_triangles(model::AseModel& model, std::vector& materials, - std::vector& vertices, std::vector& texcoords, - std::vector& colors, std::vector& faces) -{ -#if 0 - aseFacesIter_t i = faces, end = faces + numFaces; - for(; i != end; ++i) - { - /* look up the shader for the material/submaterial pair */ - aseSubMaterial_t* subMtl = _ase_get_submaterial_or_default( materials, (*i).materialId, (*i).subMaterialId ); - if( subMtl == NULL ) - { - return; - } - - { - Vertex3f* xyz[3]; - Normal3f* normal[3]; - picoVec2_t* stRef[3]; - picoVec2_t st[3]; - picoColor_t* color[3]; - //picoIndex_t smooth[3]; - double u,v; - - double materialSin = sin(subMtl->uvAngle); - double materialCos = cos(subMtl->uvAngle); - - int j; - /* we pull the data from the vertex, color and texcoord arrays using the face index data */ - for ( j = 0 ; j < 3 ; j ++ ) - { - xyz[j] = &vertices[(*i).indices[j]].vertex; - normal[j] = &vertices[(*i).indices[j]].normal; - - /* greebo: Apply shift, scale and rotation */ - /* Also check for NULL texcoords pointer, some models surfaces don't have any tverts */ - u = texcoords != NULL ? texcoords[(*i).indices[j + 3]].texcoord[0] * subMtl->uScale + subMtl->uOffset : 0.0; - v = texcoords != NULL ? texcoords[(*i).indices[j + 3]].texcoord[1] * subMtl->vScale + subMtl->vOffset : 0.0; - - st[j][0] = u * materialCos + v * materialSin; - st[j][1] = u * -materialSin + v * materialCos; - - stRef[j] = &st[j]; - - if( colors != NULL && (*i).indices[j + 6] >= 0 ) - { - color[j] = &colors[(*i).indices[j + 6]].color; - } - else - { - color[j] = &white; - } - - //smooth[j] = (vertices[(*i).indices[j]].id * (1 << 16)) + (*i).smoothingGroup; /* don't merge vertices */ - - } - - /* submit the triangle to the model */ - auto& surface = model.ensureSurface(subMtl->shader->mapName ? subMtl->shader->mapName : ""); + double u, v; - auto nextIndex = static_cast(surface.indices.size()); + /* greebo: Apply shift, scale and rotation */ + /* Also check for empty texcoords, some models surfaces don't have any tverts */ + if (!texcoords.empty()) + { + u = texcoords[face.texcoordIndices[j]].x() * material.uTiling + material.uOffset; + v = texcoords[face.texcoordIndices[j]].y() * material.vTiling + material.vOffset; + } + else + { + u = 0; + v = 0; + } - surface.vertices.emplace_back(ArbitraryMeshVertex{ Vertex3f(*(xyz[0])), Normal3f(*(normal[0])), TexCoord2f(st[0]) }); - surface.vertices.emplace_back(ArbitraryMeshVertex{ Vertex3f(*(xyz[1])), Normal3f(*(normal[1])), TexCoord2f(st[1]) }); - surface.vertices.emplace_back(ArbitraryMeshVertex{ Vertex3f(*(xyz[2])), Normal3f(*(normal[2])), TexCoord2f(st[2]) }); + auto& meshVertex = surface.vertices.emplace_back(ArbitraryMeshVertex + { + vertex, + normal, + TexCoord2f(u * materialCos + v * materialSin, u * -materialSin + v * materialCos) + }); surface.indices.emplace_back(nextIndex++); - surface.indices.emplace_back(nextIndex++); - surface.indices.emplace_back(nextIndex++); - - // TODO PicoAddTriangleToModel ( model , xyz , normal , 1 , stRef, 1 , color , subMtl->shader, smooth ); - } - } -#endif -} -static void shadername_convert(char* shaderName) -{ - /* unix-style path separators */ - char* s = shaderName; - for(; *s != '\0'; ++s) - { - if(*s == '\\') - { - *s = '/'; + if (!colours.empty()) + { + meshVertex.colour = colours[face.colourIndices[j]]; + } + else + { + meshVertex.colour.set(1, 1, 1); + } + } } - } } -namespace model -{ - AseModel::Surface& AseModel::addSurface(const std::string& name) { return _surfaces.emplace_back(Surface{name}); @@ -460,10 +178,12 @@ std::shared_ptr AseModel::CreateFromStream(std::istream& stream) parser::BasicDefTokeniser tokeniser(stream); std::vector materials; - std::vector vertices; + std::vector vertices; + std::vector normals; std::vector faces; std::vector texcoords; - std::vector colours; + std::vector colours; + std::size_t materialIndex; while (tokeniser.hasMoreTokens()) { @@ -482,7 +202,7 @@ std::shared_ptr AseModel::CreateFromStream(std::istream& stream) if (token == "*mesh") { /* finish existing surface */ - _ase_submit_triangles(*model, materials, vertices, texcoords, colours, faces); + _ase_submit_triangles(*model, materials, materialIndex, vertices, normals, texcoords, colours, faces); colours.clear(); texcoords.clear(); faces.clear(); @@ -493,6 +213,7 @@ std::shared_ptr AseModel::CreateFromStream(std::istream& stream) // Parse the number to allocate space in the vertex vector auto numVertices = string::convert(tokeniser.nextToken()); vertices.resize(numVertices); + normals.resize(numVertices); } else if (token == "*mesh_numfaces") { @@ -515,9 +236,9 @@ std::shared_ptr AseModel::CreateFromStream(std::istream& stream) * the material reference id (shader index) now. */ else if (token == "*material_ref") { - auto index = string::convert(tokeniser.nextToken()); + materialIndex = string::convert(tokeniser.nextToken()); - if (index >= materials.size()) throw parser::ParseException("MATERIAL_REF index out of bounds >= MATERIAL_COUNT"); + if (materialIndex >= materials.size()) throw parser::ParseException("MATERIAL_REF index out of bounds >= MATERIAL_COUNT"); } /* model mesh vertex */ else if (token == "*mesh_vertex") @@ -526,7 +247,7 @@ std::shared_ptr AseModel::CreateFromStream(std::istream& stream) if (index >= vertices.size()) throw parser::ParseException("MESH_VERTEX index out of bounds >= MESH_NUMVERTEX"); - auto& vertex = vertices[index].vertex; + auto& vertex = vertices[index]; vertex.x() = string::convert(tokeniser.nextToken()); vertex.y() = string::convert(tokeniser.nextToken()); vertex.z() = string::convert(tokeniser.nextToken()); @@ -538,7 +259,7 @@ std::shared_ptr AseModel::CreateFromStream(std::istream& stream) if (index >= vertices.size()) throw parser::ParseException("MESH_VERTEXNORMAL index out of bounds >= MESH_NUMVERTEX"); - auto& normal = vertices[index].normal; + auto& normal = normals[index]; normal.x() = string::convert(tokeniser.nextToken()); normal.y() = string::convert(tokeniser.nextToken()); normal.z() = string::convert(tokeniser.nextToken()); @@ -619,8 +340,6 @@ std::shared_ptr AseModel::CreateFromStream(std::istream& stream) colour.x() = string::convert(tokeniser.nextToken()); colour.y() = string::convert(tokeniser.nextToken()); colour.z() = string::convert(tokeniser.nextToken()); - /* leave alpha alone since we don't get any data from the ASE format */ - colour.w() = 1.0; } /* model color face */ else if (token == "*mesh_cface") @@ -717,7 +436,7 @@ std::shared_ptr AseModel::CreateFromStream(std::istream& stream) } else if (token == "*uvw_angle") { - materials[index].angle = string::convert(tokeniser.nextToken()); + materials[index].uvAngle = string::convert(tokeniser.nextToken()); } } } /* end map_diffuse block */ @@ -726,7 +445,7 @@ std::shared_ptr AseModel::CreateFromStream(std::istream& stream) } /* ydnar: finish existing surface */ - _ase_submit_triangles(*model, materials, vertices, texcoords, colours, faces); + _ase_submit_triangles(*model, materials, materialIndex, vertices, normals, texcoords, colours, faces); return model; }