diff options
Diffstat (limited to 'project/src/lib-kcl.c')
| -rw-r--r-- | project/src/lib-kcl.c | 8955 |
1 files changed, 8955 insertions, 0 deletions
diff --git a/project/src/lib-kcl.c b/project/src/lib-kcl.c new file mode 100644 index 0000000..090abc5 --- /dev/null +++ b/project/src/lib-kcl.c @@ -0,0 +1,8955 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens <wiimm@wiimm.de> * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-kcl.h" +#include "lib-szs.h" +#include "lib-image.h" +#include "lib-bzip2.h" +#include "kcl.inc" +#include "obj-mtl-bz2.inc" +#include <math.h> +#include <stddef.h> + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KCL action log /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool KCL_ACTION_LOG ( const char * format, ... ) +{ + #if HAVE_PRINT + { + char buf[200]; + snprintf(buf,sizeof(buf),">>>[KCL]<<< %s",format); + va_list arg; + va_start(arg,format); + PRINT_ARG_FUNC(buf,arg); + va_end(arg); + } + #endif + + if ( verbose > 2 || KCL_MODE & KCLMD_LOG ) + { + fflush(stdout); + fprintf(stdlog," %s>[KCL]%s ",colset->heading,colset->info); + va_list arg; + va_start(arg,format); + vfprintf(stdlog,format,arg); + va_end(arg); + fputs(colset->reset,stdlog); + fflush(stdlog); + return true; + } + + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kcl_mode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool drop_sort_triangles = false; + +int enable_kcl_drop_auto = 0; +kcl_mode_t KCL_MODE = KCLMD_M_DEFAULT; + +kcl_t * kcl_ref = 0; // NULL or reference KCL for calculations +kcl_t * kcl_ref_prio = 0; // NULL or prioreference KCL for calculations + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t opt_kcl_tab[] = +{ + { 0, "CLEAR", "RESET", KCLMD_M_ALL | KCLMD_F_HIDE }, + { KCLMD_M_DEFAULT, "DEFAULT", 0, KCLMD_M_ALL | KCLMD_F_HIDE }, + + { KCLMD_FAST, "FAST", 0, 0 }, + { KCLMD_ROUND, "ROUND", 0, 0 }, + { KCLMD_NORMALS, "NORMALS", 0, 0 }, + { KCLMD_MTL, "MTL", "MATERIALS", 0 }, + { KCLMD_WIIMM, "WIIMM", 0, 0 }, + { KCLMD_TRIANGLES, "TRIANGLES", 0, 0 }, + { KCLMD_OUT_SWAP, "OUT-SWAP", "OUTSWAP", 0 }, + { KCLMD_OUT_SWAP, "OSWAP", 0, 0 }, + + { KCLMD_G, "G", 0, 0 }, + { KCLMD_USEMTL, "USEMTL", 0, 0 }, + { KCLMD_CLIP, "CLIP", 0, 0 }, + { KCLMD_IN_SWAP, "IN-SWAP", "INSWAP", 0 }, + { KCLMD_IN_SWAP, "ISWAP", 0, 0 }, + { KCLMD_AUTO, "AUTO", 0, 0 }, + + { KCLMD_CENTER, "CENTER", 0, 0 }, + { KCLMD_ADD_ROAD, "ADD-ROAD", "ADDROAD", 0 }, + + { KCLMD_HEX4, "HEX4", "H4", 0 }, + { KCLMD_HEX23, "HEX23", "H23", 0 }, + { KCLMD_M_HEX, "HEX", 0, KCLMD_M_HEX | KCLMD_F_HIDE }, + { 0, "-HEX", 0, KCLMD_M_HEX | KCLMD_F_HIDE }, + + { KCLMD_CUBE, "CUBE", 0, 0 }, + + { KCLMD_SMALL, "SMALL", 0, KCLMD_M_PRESET }, + { KCLMD_CHARY, "CHARY", "NINTENDO", KCLMD_M_PRESET }, + { KCLMD_DRAW, "DRAW", 0, KCLMD_M_PRESET }, + { 0, "-SMALL", 0, KCLMD_SMALL | KCLMD_F_HIDE }, + { 0, "-CHARY", "-NINTENDO", KCLMD_CHARY | KCLMD_F_HIDE }, + { 0, "MEDIUM", 0, KCLMD_M_PRESET | KCLMD_F_HIDE }, + + { KCLMD_DROP_AUTO, "DROP-AUTO", "DROPAUTO", KCLMD_M_DROP }, + { 0, "-DROP-AUTO", "-DROPAUTO", KCLMD_DROP_AUTO|KCLMD_F_HIDE }, + { KCLMD_DROP_UNUSED + |KCLMD_DROP_AUTO, "DROP-UNUSED", "DROPUNUSED", 0 }, + { KCLMD_DROP_FIXED + |KCLMD_DROP_AUTO, "DROP-FIXED", "DROPFIXED", 0 }, + { KCLMD_DROP_INVALID + |KCLMD_DROP_AUTO, "DROP-INVALID", "DROPINVALID", 0 }, + { KCLMD_M_DROP, "DROP", "DROP-ALL", KCLMD_M_DROP | KCLMD_F_HIDE }, + { 0, "-DROP", "-DROP-ALL", KCLMD_M_DROP | KCLMD_F_HIDE }, + + { KCLMD_RM_FACEDOWN, "RM-FACEDOWN", "RMFACEDOWN", 0 }, + { KCLMD_RM_FACEUP, "RM-FACEUP", "RMFACEUP", 0 }, + { KCLMD_M_FIX_ALL, "FIX-ALL", "FIXALL", KCLMD_M_FIX_ALL | KCLMD_F_HIDE }, + + { KCLMD_CONV_FACEUP, "CONV-FACEUP", "CONVFACEUP", 0 }, + { KCLMD_WEAK_WALLS, "WEAK-WALLS", "WEAKWALLS", 0 }, + + { KCLMD_NEW, "NEW", 0, 0 }, + { KCLMD_SORT, "SORT", 0, 0 }, + + { KCLMD_TINY_0, "TINY-0", "TINY0", KCLMD_M_TINY }, + { KCLMD_TINY_1, "TINY-1", "TINY1", KCLMD_M_TINY }, + { KCLMD_TINY_2, "TINY-2", "TINY2", KCLMD_M_TINY }, + { KCLMD_TINY_3, "TINY-3", "TINY3", KCLMD_M_TINY }, + { KCLMD_TINY_4, "TINY-4", "TINY4", KCLMD_M_TINY }, + { KCLMD_TINY_5, "TINY-5", "TINY5", KCLMD_M_TINY }, + { KCLMD_TINY_6, "TINY-6", "TINY6", KCLMD_M_TINY }, + { KCLMD_TINY_7, "TINY-7", "TINY7", KCLMD_M_TINY }, + + { KCLMD_LOG, "LOG", 0, 0 }, + { KCLMD_SILENT, "SILENT", 0, 0 }, + { KCLMD_POSLEN, "POSLEN", 0, 0 }, + { KCLMD_INPLACE, "INPLACE", 0, 0 }, + + { KCLMD_F_SCRIPT, "SCRIPT", 0, 0 }, + { KCLMD_TEST, "TEST", "T", 0 }, + + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +void SetKclMode ( kcl_mode_t new_mode ) +{ + PRINT("KCL-MODE-IN: PC=%d, KPC=%d, DS=%d\n", + have_patch_count, have_kcl_patch_count, drop_sort_triangles ); + + // disable DROP_AUTO mode if any other DROP is set + if ( (new_mode & KCLMD_M_DROP ) != KCLMD_DROP_AUTO ) + new_mode &= ~KCLMD_DROP_AUTO; + + if (drop_sort_triangles) + have_patch_count--, have_kcl_patch_count--; + + KCL_MODE = new_mode & KCLMD_M_ALL; + + const kcl_mode_t tiny = KCL_MODE & KCLMD_M_TINY; + if ( tiny >= KCLMD_TINY_1 ) + { + KCL_MODE = KCL_MODE & ~KCLMD_M_PRESET | (KCLMD_SMALL|KCLMD_ROUND|KCLMD_SORT); + if ( tiny >= KCLMD_TINY_2 ) + KCL_MODE |= KCLMD_NEW; + } + + if ( PATCH_FILE_MODE & PFILE_F_LOG_ALL ) + KCL_MODE |= KCLMD_LOG; + + drop_sort_triangles = ( KCL_MODE & KCLMD_M_PATCH ) != 0; + if (drop_sort_triangles) + have_patch_count++, have_kcl_patch_count++; + + PRINT("KCL-MODE-OUT: PC=%d, KPC=%d, DS=%d\n", + have_patch_count, have_kcl_patch_count, drop_sort_triangles ); +} + +//----------------------------------------------------------------------------- + +int ScanOptKcl ( ccp arg ) +{ + if (!arg) + return 0; + + s64 stat = ScanKeywordList(arg,opt_kcl_tab,0,true,0,KCL_MODE, + "Option --kcl",ERR_SYNTAX); + if ( stat != -1 ) + { + SetKclMode(stat); + return 0; + } + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PrintKclMode ( char *buf, uint bufsize, kcl_mode_t mode ) +{ + DASSERT(buf); + DASSERT(bufsize>10); + char *dest = buf; + char *end = buf + bufsize - 1; + + mode = mode & KCLMD_M_ALL | KCLMD_F_HIDE; + kcl_mode_t mode1 = mode; + + const KeywordTab_t *ct; + for ( ct = opt_kcl_tab; ct->name1 && dest < end; ct++ ) + { + if ( ct->opt & KCLMD_F_HIDE ) + continue; + + if ( ct->opt ? (mode & ct->opt) == ct->id : mode & ct->id ) + { + if ( dest > buf ) + *dest++ = ','; + dest = StringCopyE(dest,end,ct->name1); + mode &= ~(ct->id|ct->opt); + } + } + + if ( mode1 == (KCLMD_M_DEFAULT|KCLMD_F_HIDE) ) + dest = StringCopyE(dest,end," (default)"); + else if (!mode1) + dest = StringCopyE(dest,end,"(none)"); + + *dest = 0; + return dest-buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetKclMode() +{ + static char buf[200] = {0}; + if (!*buf) + PrintKclMode(buf,sizeof(buf),KCL_MODE); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupKCL() +{ + if ( opt_tiny > 0 ) + { + const kcl_mode_t tiny + = ( (kcl_mode_t)opt_tiny << KCLMD_S_TINY ) & KCLMD_M_TINY; + if ( tiny > ( KCL_MODE & KCLMD_M_TINY )) + SetKclMode( KCL_MODE & ~KCLMD_M_TINY | tiny ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum CommandKCL_t +{ + KCMD_NONE = 0, // definitiv NULL + + KCMD_V, + KCMD_VN, + KCMD_F, + KCMD_G, + KCMD_USEMTL, + KCMD_TRI, + +} CommandKCL_t; + +/////////////////////////////////////////////////////////////////////////////// + +CommandKCL_t GetCommandKCL +( + ccp name // name to scan, upper case is assumed +) +{ + static FormatField_t cmdtab = {0}; + if (!cmdtab.used) + { + struct kcmdtab_t { ccp name; int id; }; + static const struct kcmdtab_t def_tab[] = + { + { "V", KCMD_V }, + { "VN", KCMD_VN }, + { "F", KCMD_F }, + { "G", KCMD_G }, + { "USEMTL", KCMD_USEMTL }, + { "TRI", KCMD_TRI }, + + {0,0} + }; + + const struct kcmdtab_t * cp; + for ( cp = def_tab; cp->name; cp++ ) + InsertFormatField(&cmdtab,cp->name,0,0,0)->num = cp->id; + } + + FormatFieldItem_t * item = FindFormatField(&cmdtab,name); + return item ? item->num : KCMD_NONE; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// const parameters /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// create octree params +uint KCL_BITS = 0; // 0 = generic +uint KCL_BLOW = 400; +uint KCL_MAX_DEPTH = 10; +uint KCL_MAX_TRI = 30; +uint KCL_MIN_SIZE = 512; +uint KCL_MAX_SIZE = 0x100000; +double3 KCL_MIN; // -inf +double3 KCL_MAX; // +inf +uint KCL_TRI_SPLIT = 1024; // >0: split triangles (recures) if side length ># + +// export obj param +static double3 KCL_CLIP; // = 1.0 + +/////////////////////////////////////////////////////////////////////////////// + +static bool DefineVector ( double3 *val, ccp name ) +{ + bool stat = false; + + const Var_t *var = FindConst(name); + if ( var && var->mode != VAR_UNSET ) + { + stat = true; + val->x = GetXDoubleV(var); + val->y = GetYDoubleV(var); + val->z = GetZDoubleV(var); + } + + char varname[100]; + snprintf(varname,sizeof(varname),"%s.X",name); + var = FindConst(varname); + if ( var && var->mode != VAR_UNSET ) + { + stat = true; + val->x = GetXDoubleV(var); + } + + snprintf(varname,sizeof(varname),"%s.Y",name); + var = FindConst(varname); + if ( var && var->mode != VAR_UNSET ) + { + stat = true; + val->y = GetYDoubleV(var); + } + + snprintf(varname,sizeof(varname),"%s.Z",name); + var = FindConst(varname); + if ( var && var->mode != VAR_UNSET ) + { + stat = true; + val->z = GetZDoubleV(var); + } + + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +void LoadParametersKCL +( + ccp log_prefix // not NULL: + // print log message with prefix +) +{ + static bool done = false; + if (done) + return; + done = true; + + + //--- --kcl small,chary,draw ==> change defaults + + SetKclMode(KCL_MODE); + kcl_mode_t preset = KCL_MODE & KCLMD_M_PRESET; + + if ( preset == KCLMD_DRAW ) + { + KCL_MODE = KCL_MODE | KCLMD_FAST | KCLMD_ROUND; + KCL_BLOW = 200; + KCL_MAX_TRI = 50; + } + else if ( preset == KCLMD_SMALL ) + { + KCL_MODE = KCL_MODE & ~KCLMD_FAST | KCLMD_ROUND; + KCL_BLOW = 200; + KCL_MAX_TRI = 40; + } + else if ( preset == KCLMD_CHARY ) + { + KCL_BLOW = 600; + KCL_MAX_TRI = 20; + } + + + //--- KCL_BITS + + const Var_t *var = FindConst("KCL_BITS"); + if (var) + { + const uint num = GetIntV(var); + if ( num >= 0 ) + KCL_BITS = num < 3 ? 3 : num > 20 ? 20 : num; + } + + + //--- KCL_BLOW + + var = FindConst("KCL_BLOW"); + if (var) + { + const uint num = GetIntV(var); + if ( num >= 0 ) + KCL_BLOW = num < 10000 ? num : 10000; + } + + + //--- KCL_CLIP + + KCL_CLIP.x = KCL_CLIP.y = KCL_CLIP.z = 1.0; + DefineVector(&KCL_CLIP,"KCL_CLIP"); + if ( KCL_CLIP.x <= 0.0 ) KCL_CLIP.x = 1.0; + if ( KCL_CLIP.y <= 0.0 ) KCL_CLIP.y = 1.0; + if ( KCL_CLIP.z <= 0.0 ) KCL_CLIP.z = 1.0; + + + //--- KCL_MAX_DEPTH + + var = FindConst("KCL_MAX_DEPTH"); + if (var) + { + const uint num = GetIntV(var); + if ( num > 0 ) + KCL_MAX_DEPTH = num; + } + + + //--- KCL_MAX_TRI + + var = FindConst("KCL_MAX_TRI"); + if (var) + { + const uint num = GetIntV(var); + if ( num > 0 ) + KCL_MAX_TRI = num < 5 ? 5 : num > 1000 ? 1000 : num; + } + + + //--- KCL_MAX_SIZE, KCL_MIN_SIZE + + var = FindConst("KCL_MAX_SIZE"); + if (var) + { + const uint num = GetIntV(var); + if ( num > 0 ) + KCL_MAX_SIZE = num < 0x100 ? 0x100 : num > 0x100000 ? 0x100000 : num; + } + + var = FindConst("KCL_MIN_SIZE"); + if (var) + { + const uint num = GetIntV(var); + if ( num > 0 ) + KCL_MIN_SIZE = num < KCL_MAX_SIZE ? num : KCL_MAX_SIZE; + } + + + //--- KCL_MIN, KCL_MAX + + KCL_MIN.x = KCL_MIN.y = KCL_MIN.z = -INFINITY; + DefineVector(&KCL_MIN,"KCL_MIN"); + + KCL_MAX.x = KCL_MAX.y = KCL_MAX.z = INFINITY; + DefineVector(&KCL_MAX,"KCL_MAX"); + + if ( KCL_MAX.x < KCL_MIN.x ) KCL_MAX.x = KCL_MIN.x; + if ( KCL_MAX.y < KCL_MIN.y ) KCL_MAX.y = KCL_MIN.y; + if ( KCL_MAX.z < KCL_MIN.z ) KCL_MAX.z = KCL_MIN.z; + + + //--- KCL_TRI_SPLIT, + + var = FindConst("KCL_TRI_SPLIT"); + if (var) + { + KCL_TRI_SPLIT = GetIntV(var); + if ( KCL_TRI_SPLIT && KCL_TRI_SPLIT < 512 ) + KCL_TRI_SPLIT = 512; + } + + + //--- logging + + if ( !KCL_ACTION_LOG("Global KCL Modes: %s\n",GetKclMode()) + && log_prefix + && KCL_MODE != KCLMD_M_DEFAULT + && !(KCL_MODE & KCLMD_SILENT) ) + { + fprintf(stdlog,"%sglobal kcl modes: %s\n",log_prefix,GetKclMode()); + fflush(stdlog); + } + + #if HAVE_PRINT + PRINT("KCL-PARAM: MODE= [%llx] %s\n",(u64)KCL_MODE,GetKclMode()); + + PRINT(" > BITS=%u, MAX_TRI=%u, MAX_DEPTH=%u, CUBE_SIZE=%u..%u\n", + KCL_BITS, KCL_MAX_TRI, KCL_MAX_DEPTH, KCL_MIN_SIZE, KCL_MAX_SIZE ); + PRINT(" > CLIP: %11.3f %11.3f %11.3f, enabled=%d \n", + KCL_CLIP.x, KCL_CLIP.y, KCL_CLIP.z, (KCL_MODE&KCLMD_CLIP)!=0 ); + PRINT(" > MIN: %11.3f %11.3f %11.3f\n", + KCL_MIN.x, KCL_MIN.y, KCL_MIN.z ); + PRINT(" > MAX: %11.3f %11.3f %11.3f\n", + KCL_MAX.x, KCL_MAX.y, KCL_MAX.z ); + + if ( KCL_MODE & KCLMD_CUBE ) + PRINT(" > Use CUBE mode (experimental), split triangles @ %u\n", + KCL_TRI_SPLIT ); + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KCL setup /////////////// +/////////////////////////////////////////////////////////////////////////////// + +Color_t user_color[N_KCL_USER_FLAGS]; +uint n_user_color = 0; + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeKCL ( kcl_t * kcl ) +{ + //--- one time setup + + static int done = 0; + if (!done) + { + ++done; + if ( opt_slot & (SLOTMD_ICE_TO_WATER|SLOTMD_WATER_TO_ICE) ) + { + SetupPatchKclFlag(); + const u16 search = opt_slot & SLOTMD_ICE_TO_WATER ? 0x0070 : 0x0030; + + uint i; + for ( i = 0; i < N_KCL_FLAG; i++ ) + if ( ( patch_kcl_flag[i] & 0x00ff ) == search ) + patch_kcl_flag[i] = patch_kcl_flag[i] ^ 0x0040; + + PurgePatchKclFlag(); + } + + if ( KCL_MODE & KCLMD_WEAK_WALLS ) + { + SetupPatchKclFlag(); + uint i; + for ( i = 0; i < N_KCL_FLAG; i++ ) + { + const int type = GET_KCL_TYPE(i); + if ( type >= 0 && kcl_type[type].attrib & KCLT_C_WALL ) + patch_kcl_flag[i] |= 0x8000; + } + PurgePatchKclFlag(); + } + } + + //--- object setup + + DASSERT(kcl); + memset(kcl,0,sizeof(*kcl)); + + kcl->fname = EmptyString; + kcl->revision = REVISION_NUM; + + InitializeList(&kcl->tridata,sizeof(kcl_tridata_t)); + InitializeFormatField(&kcl->flag_name); + InitializeFormatField(&kcl->flag_pattern); + InitializeFormatField(&kcl->flag_missing); + + // default header values + kcl->unknown_0x10 = 300.0; + kcl->unknown_0x38 = 250.0; + + kcl->tri_minval.x = -1e9; + kcl->tri_minval.y = -1e9; + kcl->tri_minval.z = -1e9; + + kcl->tri_maxval.x = 1e9; + kcl->tri_maxval.y = 1e9; + kcl->tri_maxval.z = 1e9; + + LoadParametersKCL( verbose > 0 ? " - " : 0 ); + + kcl->fast = ( KCL_MODE & KCLMD_FAST ) != 0; + kcl->accept_hex4 = ( KCL_MODE & KCLMD_HEX4 ) != 0; + kcl->accept_hex23 = ( KCL_MODE & KCLMD_HEX23 ) != 0; + + kcl->min_cube_size = KCL_MIN_SIZE; + kcl->max_cube_size = KCL_MAX_SIZE; + kcl->cube_blow = KCL_BLOW; + kcl->max_cube_triangles = KCL_MAX_TRI; + kcl->max_octree_depth = KCL_MAX_DEPTH; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetKCL ( kcl_t * kcl ) +{ + DASSERT(kcl); + + const file_format_t fform_outfile = kcl->fform_outfile; + + ResetStatisticsKCL(kcl); + FreeString(kcl->fname); + FreeString(kcl->flag_fname); + if (kcl->octree_alloced) + FREE(kcl->octree); + if (kcl->raw_data_alloced) + FREE(kcl->raw_data); + ResetList(&kcl->tridata); + ResetFormatField(&kcl->flag_name); + ResetFormatField(&kcl->flag_pattern); + ResetFormatField(&kcl->flag_missing); + + InitializeKCL(kcl); + kcl->fform_outfile = fform_outfile; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ClearKCL ( kcl_t * kcl, bool init_kcl ) +{ + DASSERT(kcl); + if (init_kcl) + InitializeKCL(kcl); + else + { + ccp fname = kcl->fname; + kcl->fname = 0; + ResetKCL(kcl); + kcl->fname = fname; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void CopyKCL ( kcl_t * kcl, bool init_kcl, const kcl_t * src ) +{ + DASSERT(kcl); + DASSERT(src); + + if ( !kcl || kcl == src ) + return; + + ClearKCL(kcl,init_kcl); + if (!src) + return; + + const uint n_tri = src->tridata.used; + kcl_tridata_t *td = (kcl_tridata_t*)src->tridata.list; + uint i; + for ( i = 0; i < n_tri; i++, td++ ) + AppendTriangleKCLp( kcl, td->cur_flag, td->pt, td->pt+1, td->pt+2 ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kcl_tridata_t helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CopyTriData_F2D ( kcl_tridata_dbl_t *dest, const kcl_tridata_flt_t *src ) +{ + DASSERT(src); + DASSERT(dest); + memcpy(dest->pt,src->pt,sizeof(dest->pt)); + + const int n = SIZEOFRANGE(kcl_tridata_dbl_t,length,normal)/sizeof(double); + const float *s = &src->length; + double *d = &dest->length; + int i; + for ( i = 0; i < n; i++ ) + *d++ = *s++; +} + +//----------------------------------------------------------------------------- + +void CopyTriData_D2F ( kcl_tridata_flt_t *dest, const kcl_tridata_dbl_t *src ) +{ + DASSERT(src); + DASSERT(dest); + memcpy(dest->pt,src->pt,sizeof(dest->pt)); + + const int n = SIZEOFRANGE(kcl_tridata_dbl_t,length,normal)/sizeof(double); + const double *s = &src->length; + float *d = &dest->length; + int i; + for ( i = 0; i < n; i++ ) + *d++ = *s++; +} + +/////////////////////////////////////////////////////////////////////////////// +// [[xtridata]] + +uint CheckMetricsTriData +( + // check length and minimal height of triangles + + kcl_tridata_t *td, // valid pointer to first record + uint n, // number of records to process + kcl_t *kcl // not NULL: store statistics +) +{ + DASSERT( td || !n ); + #if HAVE_PRINT + const u_usec_t start_time = GetTimerUSec(); + #endif + + uint invalid_count = 0; + kcl_tridata_t *end = td + n; + for ( ; td < end; td++ ) + { + if ( td->status & TD_INVALID ) + { + invalid_count++; + continue; + } + + if ( !( td->status & TD_INVALID_NORM ) && + ( !isnormal(td->length) || fabs(td->length) < opt_tri_height )) + { + PRINT0("CHECK-TRI/INVALID LENGTH %g\n",td->length); + td->status |= TD_INVALID|TD_INVALID_NORM; + invalid_count++; + continue; + } + + if ( opt_tri_area > 0 || opt_tri_height > 0 ) + { + tri_metrics_t met; + CalcTriMetricsByPoints(&met,td->pt,td->pt+1,td->pt+2); + if ( met.area < opt_tri_area || met.min_ht < opt_tri_height ) + { + #if HAVE_PRINT + { + ccp sep = " "; + char hbuf[30], abuf[30]; + if ( met.min_ht < opt_tri_height ) + { + snprintf(hbuf,sizeof(hbuf)," HEIGHT %g",met.min_ht); + sep = ", "; + } + else + *hbuf = 0; + + if ( met.area < opt_tri_area ) + snprintf(abuf,sizeof(abuf),"%sAREA %g",sep,met.area); + else + *abuf = 0; + + PRINT0("CHECK-TRI/INVALID%s%s\n",hbuf,abuf); + } + #endif + + td->status |= TD_INVALID; + invalid_count++; + continue; + } + } + } + + #if HAVE_PRINT + const u_usec_t duration = GetTimerUSec() - start_time; + #endif + + if (kcl) + { + PRINT("CheckMetricsTriData(%u), invalid: %u -> %u, done in %s\n", + n, kcl->tri_inv_count, invalid_count, + PrintTimerUSec(0,0,duration,6) ); + kcl->tri_inv_count = invalid_count; + } + else + PRINT("CheckMetricsTriData(%u), invalid: %u, done in %s\n", + n, invalid_count, PrintTimerUSec(0,0,duration,6) ); + return invalid_count; +} + +/////////////////////////////////////////////////////////////////////////////// +// [[xtridata]] + +uint CalcPointsTriData +( + // calculate 'pt' using 'length' & 'normal' + // returns the number of fixes + + kcl_tridata_t *td, // valid pointer to first record + uint n, // number of records to process + kcl_t *kcl, // not NULL: store statistics + bool use_minmax // true & 'kcl': use 'tri_minval' and 'tri_maxval' +) +{ + DASSERT( td || !n ); + + if (!kcl) + use_minmax = false; + + const double min_divisor = 1e-9; + uint fix_count = 0, invalid_count = 0; + + for ( ; n-- > 0; td++ ) + { + if ( td->status & TD_INVALID + || !isnormal(td->length) + || fabs(td->length) < opt_tri_height + ) + { + invalid: + PRINT0("TRI INVALID %g\n",td->length); + td->length = 0.0; +// memcpy(td->pt+1,td->pt,sizeof(*td->pt)); +// memcpy(td->pt+2,td->pt,sizeof(*td->pt)); + td->status |= TD_INVALID|TD_INVALID_NORM; + invalid_count++; + continue; + } + + double3 cross_1, cross_2; + double dot_1, dot_2; + + CrossProd3(cross_1,td->normal[1],td->normal[0]); + dot_1 = DotProd3(cross_1,td->normal[3]); + if (!isfinite(dot_1)) + goto invalid; + + if ( fabs(dot_1) >= min_divisor ) + { + const double factor_1 = td->length / dot_1; + double3 *d3 = td->pt + 2; + d3->x = td->pt[0].x + cross_1.x * factor_1; + d3->y = td->pt[0].y + cross_1.y * factor_1; + d3->z = td->pt[0].z + cross_1.z * factor_1; + + if ( use_minmax && + ( d3->x < kcl->tri_minval.x || d3->x > kcl->tri_maxval.x + || d3->y < kcl->tri_minval.y || d3->y > kcl->tri_maxval.y + || d3->z < kcl->tri_minval.z || d3->z > kcl->tri_maxval.z )) + { + TRACE("VERTEX-1: %12.6g %12.6g %12.6g\n", + d3->x, d3->y, d3->z ); + goto abort_1; + } + } + else + { + abort_1: + noPRINT("INVALID DOT_1\n"); + memcpy(td->pt+2,td->pt,sizeof(*td->pt)); + td->status |= TD_FIXED_PT|TD_INVALID_NORM; + } + + CrossProd3(cross_2,td->normal[2],td->normal[0]); + dot_2 = DotProd3(cross_2,td->normal[3]); + if (!isfinite(dot_2)) + goto invalid; + + if ( fabs(dot_2) >= min_divisor ) + { + const double factor_2 = td->length / dot_2; + double3 *d3 = td->pt + 1; + d3->x = td->pt[0].x + cross_2.x * factor_2; + d3->y = td->pt[0].y + cross_2.y * factor_2; + d3->z = td->pt[0].z + cross_2.z * factor_2; + + if ( use_minmax && + ( d3->x < kcl->tri_minval.x || d3->x > kcl->tri_maxval.x + || d3->y < kcl->tri_minval.y || d3->y > kcl->tri_maxval.y + || d3->z < kcl->tri_minval.z || d3->z > kcl->tri_maxval.z )) + { + TRACE("VERTEX-2: %12.6g %12.6g %12.6g\n", + d3->x, d3->y, d3->z ); + goto abort_2; + } + } + else + { + abort_2: + noPRINT("INVALID DOT_2\n"); + memcpy(td->pt+1,td->pt,sizeof(*td->pt)); + td->status |= TD_FIXED_PT|TD_INVALID_NORM; + } + + if ( td->status & TD_FIXED_PT ) + fix_count++; + } + + if (kcl) + { + kcl->tri_inv_count = invalid_count; + kcl->tri_fix_count = fix_count; + } + + return invalid_count + fix_count; +} + +/////////////////////////////////////////////////////////////////////////////// +// [[xtridata]] + +void CalcNormalsTriData +( + // calculate 'length' & 'normal' using 'pt' + // the calculation is only done, if 'TD_INVALID_NORM' + + kcl_t *kcl, // valid KCL + kcl_tridata_t *td, // valid pointer to first record + uint n // number of records to process +) +{ + DASSERT( td || !n ); + + #undef XPRINT + #define XPRINT noPRINT + + const bool pos_length = ( KCL_MODE & KCLMD_POSLEN ) != 0; + + for ( ; n-- > 0; td++ ) + { + if ( !( td->status & TD_INVALID_NORM ) || td->status & TD_REMOVED ) + continue; + + XPRINT("-----\n"); + XPRINT("PT0: %6.2f %6.2f %6.2f\n", td->pt[0].x, td->pt[0].y, td->pt[0].z ); + XPRINT("PT1: %6.2f %6.2f %6.2f\n", td->pt[1].x, td->pt[1].y, td->pt[1].z ); + XPRINT("PT2: %6.2f %6.2f %6.2f\n", td->pt[2].x, td->pt[2].y, td->pt[2].z ); + + double3 diff_2_0, diff_1_0, diff_1_2, normal[4]; + + Sub3(diff_2_0,td->pt[2],td->pt[0]); + Sub3(diff_1_0,td->pt[1],td->pt[0]); + Sub3(diff_1_2,td->pt[1],td->pt[2]); + XPRINT("D-10: %6.2f %6.2f %6.2f\n", diff_1_0.x, diff_1_0.y, diff_1_0.z ); + XPRINT("D-20: %6.2f %6.2f %6.2f\n", diff_2_0.x, diff_2_0.y, diff_2_0.z ); + XPRINT("D-12: %6.2f %6.2f %6.2f\n", diff_1_2.x, diff_1_2.y, diff_1_2.z ); + + CrossProd3( normal[0], diff_1_0, diff_2_0 ); Unit3(normal[0]); + CrossProd3( normal[1], normal[0], diff_2_0 ); Unit3(normal[1]); + CrossProd3( normal[2], diff_1_0, normal[0] ); Unit3(normal[2]); + CrossProd3( normal[3], normal[0], diff_1_2 ); Unit3(normal[3]); + + double length = DotProd3(diff_1_0,normal[3]); + if ( pos_length && length < 0.0 ) + { + length = -length; + Neg3(normal[3]); + } + + XPRINT("A: %6.2f %6.2f %6.2f\n",normal[1].x,normal[1].y,normal[1].z); + XPRINT("B: %6.2f %6.2f %6.2f\n",normal[2].x,normal[2].y,normal[2].z); + XPRINT("C: %6.2f %6.2f %6.2f\n",normal[3].x,normal[3].y,normal[3].z); + XPRINT("LEN: %6.2f\n",length); + + //--- now we assign the double values to the float members + + // need cast to avoid gcc warning 'above array bounds' + double *d = (double*)&normal->x; + float *f = td->normal->v; + float *f_end = (float*)( (u8*)f + sizeof(td->normal) ); + + if ( KCL_MODE & KCLMD_M_TINY ) + { + while ( f < f_end ) + *f++ = ldexp(round(ldexp(*d++,15)),-15); + } + else if ( KCL_MODE & KCLMD_ROUND ) + { + while ( f < f_end ) + *f++ = ldexp(round(ldexp(*d++,17)),-17); + } + else + while ( f < f_end ) + *f++ = *d++; + + td->length = length; + td->status &= ~TD_INVALID_NORM; + + if ( !isnormal(td->length) + || fabs(td->length) < opt_tri_height + || !isfinite(normal[0].x) + || !isfinite(normal[0].y) + || !isfinite(normal[0].z) + || !isfinite(normal[1].x) + || !isfinite(normal[1].y) + || !isfinite(normal[1].z) + || !isfinite(normal[2].x) + || !isfinite(normal[2].y) + || !isfinite(normal[2].z) + || !isfinite(normal[3].x) + || !isfinite(normal[3].y) + || !isfinite(normal[3].z) + ) + { + if (kcl) + { + PRINT("INVALID TRIANGLE #%u! [len=%5.3g]\n", + (int)( td - (kcl_tridata_t*)kcl->tridata.list ), td->length ); + if (!(td->status & TD_INVALID)) + kcl->tri_inv_count++; + } + td->status |= TD_INVALID; + td->length = 0.0; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void CalcNormalsKCL +( + // calculate 'length' & 'normal' using 'pt' + // the calculation is only done, if '!kcl->norm_valid' + + kcl_t *kcl, // valid KCL + bool force // true: ignore 'kcl->norm_valid' +) +{ + DASSERT(kcl); + + if ( !kcl->norm_valid || force ) + { + CalcNormalsTriData( kcl, (kcl_tridata_t*)kcl->tridata.list, + kcl->tridata.used ); + kcl->norm_valid = true; + + #ifdef TEST0 + // verify valid triangels + PRINT(" verify valid triangels\n"); + CalcPointsTriData( (kcl_tridata_t*)kcl->tridata.list, + kcl->tridata.used, 0,false ); + #endif + } +} + +/////////////////////////////////////////////////////////////////////////////// +// [[xtridata]] + +void RoundPointsKCL +( + // round the normal values + + kcl_t *kcl, // valid KCL + int round_pow2 // round to '1/(2^round_pow2)' +) +{ + DASSERT(kcl); + + uint n = kcl->tridata.used; + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + + for ( ; n-- > 0; td++ ) + { + double *d = &td->pt->x; + uint i; + for ( i = 0; i < sizeof(td->pt)/sizeof(*d); i++, d++ ) + *d = ldexp(round(ldexp(*d,round_pow2)),-round_pow2); + td->status |= TD_INVALID_NORM; + } + + CalcNormalsKCL(kcl,true); +} + +/////////////////////////////////////////////////////////////////////////////// + +void RoundNormalsKCL +( + // round the normal values + + kcl_t *kcl, // valid KCL + int round_pow2 // round to '1/(2^round_pow2)' +) +{ + DASSERT(kcl); + + uint n = kcl->tridata.used; + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + + if (!kcl->norm_valid) + { + CalcNormalsTriData(kcl,td,n); + kcl->norm_valid = true; + } + + for ( ; n-- > 0; td++ ) + { + float *f = &td->normal->x; + uint i; + for ( i = 0; i < sizeof(td->normal)/sizeof(*f); i++, f++ ) + *f = ldexpf(roundf(ldexpf(*f,round_pow2)),-round_pow2); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t *GetTridataKCL +( + kcl_t *kcl, // NULL or valid kcl + uint tri_index // triangle index +) +{ + return kcl && tri_index < kcl->tridata.used + ? (kcl_tridata_t*)kcl->tridata.list + tri_index + : 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// append triangles /////////////// +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t * PrepareAppendTrianglesKCL +( + // Grows the triangle field and the 'used' count. + // Returns a pointer to the first triangle. + // If to many triangles: + // => a warning is printed, but only once for a kcl. NULL is returned + + kcl_t *kcl, // valid KCL data structure + uint n_append // number of triangles to append +) +{ + DASSERT(kcl); + if ( kcl->no_limit || n_append + kcl->tridata.used <= KCL_MAX_TRIANGLES ) + { + kcl_tridata_t *td = GrowListSize(&kcl->tridata,n_append,1000); + DASSERT(td); + memset(td,0,n_append*sizeof(*td)); + return td; + } + + if (!kcl->limit_warning) + { + kcl->limit_warning = true; + ERROR0(ERR_SEMANTIC, + "Can't add new triangles, because limit (%u) reached!", + KCL_MAX_TRIANGLES ); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t *AppendTriangleKCLp +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *pt1, // pointer to point #1 + const double3 *pt2, // pointer to point #2 + const double3 *pt3 // pointer to point #3 +) +{ + DASSERT(kcl); + DASSERT(pt1); + DASSERT(pt2); + DASSERT(pt3); + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + return AppendTriangleKCL(&par,pt1,pt2,pt3); +} + +//----------------------------------------------------------------------------- + +kcl_tridata_t * AppendTriangleKCL +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_tri_param_t *par, // valid data structure + const double3 *pt1, // pointer to point #1 + const double3 *pt2, // pointer to point #2 + const double3 *pt3 // pointer to point #3 +) +{ + DASSERT(par); + DASSERT(par->kcl); + DASSERT(pt1); + DASSERT(pt2); + DASSERT(pt3); + + kcl_tridata_t *td = PrepareAppendTrianglesKCL(par->kcl,1); + if (td) + { + memcpy(td->pt+0,pt1,sizeof(*td->pt)); + memcpy(td->pt+1,pt2,sizeof(*td->pt)); + memcpy(td->pt+2,pt3,sizeof(*td->pt)); + td->in_flag = td->cur_flag = par->kcl_flag; + td->status = TD_ADDED|TD_INVALID_NORM; + par->kcl->model_modified = true; + + #if SUPPORT_KCL_DELTA + { + float d1 = LengthD(pt1->v,pt2->v); + float d2 = LengthD(pt2->v,pt3->v); + float d3 = LengthD(pt3->v,pt1->v); + float dmin, dmax; + if ( d1 < d2 ) + { + dmin = d1; + dmax = d2; + } + else + { + dmin = d2; + dmax = d1; + } + td->delta_min = dmin < d3 ? dmin : d3; + td->delta_max = dmax > d3 ? dmax : d3; + } + #endif + + if (par->norm) + { + const double norm_len = Length3(*par->norm); + if ( norm_len > 1e-3 ) + { + // first calc the normal of the direction + + double3 norm_unit; + Div3f(norm_unit,*par->norm,norm_len); + PRINT("NORM/DIR: %8.5f %8.5f %8.5f | %11.3f\n", + norm_unit.x, norm_unit.y, norm_unit.z, norm_len ); + + // now calc a normal by the points + + double3 diff_2_0, diff_1_0, pt_norm; + Sub3(diff_2_0,td->pt[2],td->pt[0]); + Sub3(diff_1_0,td->pt[1],td->pt[0]); + CrossProd3(pt_norm,diff_1_0,diff_2_0); + Unit3(pt_norm); + PRINT("NORM/PTS: %8.5f %8.5f %8.5f\n", + pt_norm.x, pt_norm.y, pt_norm.z ); + + // comparing + double dp = DotProd3(norm_unit,pt_norm); + if ( dp < -0.2 ) + { + PRINT(" -> SWAP POINTS, dp=%7.5f\n",dp); + memcpy(td->pt+1,pt3,sizeof(*td->pt)); + memcpy(td->pt+2,pt2,sizeof(*td->pt)); + par->swap_count++; + } + } + } + + #if SUPPORT_KCL_XTRIDATA + if (opt_xtridata) + { + CopyTriData_T2D(&td->dbl,td); + if (par->is_orig) + { + td->status |= TDX_ORIG_PT; + memcpy(&td->orig,&td->dbl,sizeof(td->orig)); + } + } + #endif + +// [[tridata]] [[2do]] ??? drop invalid triangles + } + return td; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t *AppendQuadrilateralKCLp +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *pt1, // pointer to point #1 + const double3 *pt2, // pointer to point #2 + const double3 *pt3, // pointer to point #3 + const double3 *pt4 // pointer to point #4 +) +{ + DASSERT(kcl); + DASSERT(pt1); + DASSERT(pt2); + DASSERT(pt3); + DASSERT(pt4); + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + return AppendQuadrilateralKCL(&par,pt1,pt2,pt3,pt4); +} + +//----------------------------------------------------------------------------- + +kcl_tridata_t *AppendQuadrilateralKCL +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_tri_param_t *par, // valid data structure + const double3 *pt1, // pointer to point #1 + const double3 *pt2, // pointer to point #2 + const double3 *pt3, // pointer to point #3 + const double3 *pt4 // pointer to point #4 +) +{ + DASSERT(par); + DASSERT(par->kcl); + DASSERT(pt1); + DASSERT(pt2); + DASSERT(pt3); + DASSERT(pt4); + + kcl_tridata_t *td = PrepareAppendTrianglesKCL(par->kcl,2); + if (!td) + return 0; + par->kcl->tridata.used -= 2; + + if ( Length2D(pt1->v,pt3->v) <= 1.1 * Length2D(pt2->v,pt4->v) ) + { + // this variant is prioritized to force + // 'pt1' as vertex at nearly same length + + AppendTriangleKCL( par, pt1, pt2, pt3 ); + AppendTriangleKCL( par, pt1, pt3, pt4 ); + } + else + { + AppendTriangleKCL( par, pt2, pt4, pt1 ); + AppendTriangleKCL( par, pt2, pt3, pt4 ); + } + return td; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t *AppendTrianglesKCLp +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *pt1, // pointer to first point + int pt_delta, // delta in bytes to next point, <0 possible + uint n_pt // number of points +) +{ + DASSERT(kcl); + DASSERT(pt1); + DASSERT( pt_delta >= sizeof(double3) || pt_delta <= -(int)sizeof(double3) ); + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + return AppendTrianglesKCL(&par,pt1,pt_delta,n_pt); +} + +//----------------------------------------------------------------------------- + +kcl_tridata_t * AppendTrianglesKCL +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_tri_param_t *par, // valid data structure + const double3 *pt1, // pointer to first point + int pt_delta, // delta in bytes to next point, <0 possible + uint n_pt // number of points +) +{ + DASSERT(par); + DASSERT(par->kcl); + DASSERT(pt1); + DASSERT( pt_delta >= sizeof(double3) || pt_delta <= -(int)sizeof(double3) ); + + if ( n_pt <= 4 ) + return n_pt == 3 + ? AppendTriangleKCL( par, + pt1, + (double3*)((u8*)pt1 + pt_delta), + (double3*)((u8*)pt1 + 2*pt_delta) ) + : n_pt == 4 + ? AppendQuadrilateralKCL( + par, + pt1, + (double3*)((u8*)pt1 + pt_delta), + (double3*)((u8*)pt1 + 2*pt_delta), + (double3*)((u8*)pt1 + 3*pt_delta) ) + : 0; + + kcl_tridata_t *td = PrepareAppendTrianglesKCL(par->kcl,n_pt-2); + if (!td) + return 0; + par->kcl->tridata.used -= n_pt-2; + + + //--- setup internal data structure + + struct tab_t + { + double3 *pt; // pointer to coordinates + double len; // distance to point+2 (square!) + }; + + struct tab_t *tab, tab_buf[KCL_MAX_PT_PER_TRI]; + if ( n_pt <= KCL_MAX_PT_PER_TRI ) + tab = tab_buf; + else + tab = MALLOC(n_pt*sizeof(*tab)); // this function allows more points + + int i; + for ( i = 0; i < n_pt; i++ ) + tab[i].pt = (double3*)( (u8*)pt1 + i * pt_delta ); + + + //--- polygons with >= 5 verteces + + DASSERT( n_pt >= 5 ); + // init differences + for ( i = 0; i < n_pt; i++ ) + tab[i].len = Length2D(tab[i].pt->v,tab[(i+2)%n_pt].pt->v); + + + //--- main loop + + while ( n_pt > 3 ) + { + // find min distance + uint found = 0; + double min = tab[0].len; + for ( i = 1; i < n_pt; i++ ) + if ( min > tab[i].len ) + { + min = tab[i].len; + found = i; + } + uint found1 = ( found + 1 ) % n_pt; + + AppendTriangleKCL(par, + tab[found].pt, + tab[found1].pt, + tab[(found+2)%n_pt].pt + ); + + //--- remove the mid point and fix the table + + n_pt--; + memmove( tab+found1, tab+found1+1, (n_pt-found1)*sizeof(*tab) ); + tab[found].len = Length2D(tab[found].pt->v,tab[(found+2)%n_pt].pt->v); + found = ( found + n_pt - 1 ) % n_pt; + tab[found].len = Length2D(tab[found].pt->v,tab[(found+2)%n_pt].pt->v); + } + + + //--- add the last triangle + + DASSERT( n_pt == 3 ); + AppendTriangleKCL(par, tab[0].pt, tab[1].pt, tab[2].pt ); + + + //--- terminate + + if ( tab != tab_buf ) + FREE(tab); + return td; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t *AppendOctahedronKCL +( + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + octahedron_t *oct // valid octahedron data +) +{ + DASSERT(kcl); + DASSERT(oct); + + kcl_tridata_t *td = PrepareAppendTrianglesKCL(kcl,8); + if (!td) + return 0; + kcl->tridata.used -= 8; + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + + AppendTriangleKCL( &par, &oct->x1, &oct->z1, &oct->y1 ); + AppendTriangleKCL( &par, &oct->z1, &oct->x2, &oct->y1 ); + AppendTriangleKCL( &par, &oct->x2, &oct->z2, &oct->y1 ); + AppendTriangleKCL( &par, &oct->z2, &oct->x1, &oct->y1 ); + + AppendTriangleKCL( &par, &oct->x1, &oct->z2, &oct->y2 ); + AppendTriangleKCL( &par, &oct->z2, &oct->x2, &oct->y2 ); + AppendTriangleKCL( &par, &oct->x2, &oct->z1, &oct->y2 ); + AppendTriangleKCL( &par, &oct->z1, &oct->x1, &oct->y2 ); + + return td; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t *AppendCuboidKCL +( + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + cuboid_t *c // valid cuboid data +) +{ + DASSERT(kcl); + DASSERT(c); + + kcl_tridata_t *td = PrepareAppendTrianglesKCL(kcl,12); + if (!td) + return 0; + kcl->tridata.used -= 12; + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + + AppendQuadrilateralKCL( &par, &c->p000, &c->p010, &c->p011, &c->p001 ); + AppendQuadrilateralKCL( &par, &c->p000, &c->p001, &c->p101, &c->p100 ); + AppendQuadrilateralKCL( &par, &c->p000, &c->p100, &c->p110, &c->p010 ); + AppendQuadrilateralKCL( &par, &c->p111, &c->p101, &c->p001, &c->p011 ); + AppendQuadrilateralKCL( &par, &c->p111, &c->p110, &c->p100, &c->p101 ); + AppendQuadrilateralKCL( &par, &c->p111, &c->p011, &c->p010, &c->p110 ); + return td; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t *AppendPyramidKCL +( + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *apex, // the apex of the pyramid + const double3 *pt1, // pointer to first point + uint pt_delta, // delta in bytes to next point + uint n_pt, // number of points + bool reverse // true: reverse face orientation +) +{ + DASSERT(kcl); + DASSERT(apex); + DASSERT(pt1); + DASSERT(n_pt>=2); + + kcl_tridata_t *td = PrepareAppendTrianglesKCL(kcl,n_pt); + if (!td) + return 0; + kcl->tridata.used -= n_pt; + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + + if (reverse) + { + int i; + const double3 *pt = pt1; + for ( i = n_pt-1; i >= 0; i-- ) + { + const double3 *next = (double3*)((u8*)pt1 + i*pt_delta); + AppendTriangleKCL( &par, apex, pt, next ); + pt = next; + } + } + else + { + int i; + const double3 *pt = (double3*)((u8*)pt1 + (n_pt-1)*pt_delta); + for ( i = 0; i < n_pt; i++ ) + { + const double3 *next = (double3*)((u8*)pt1 + i*pt_delta); + noPRINT("%6.1f %6.1f %6.1f / %6.1f %6.1f %6.1f / %6.1f %6.1f %6.1f\n", + apex->x, apex->y, apex->z, + pt->x, pt->y, pt->z, + next->x, next->y, next->z ); + AppendTriangleKCL( &par, apex, pt, next ); + pt = next; + } + } + return td; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t *AppendPrismKCL +( + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *axis1, // first axis point + const double3 *axis2, // second axis point + const double3 *dir, // NULL or direction of first edge + double r, // radius + uint n_side, // number of sides, max=100 + uint mode, // bitfield: 0=prism, 1=antiprism + u32 base_flag, // color of base, -1:no base + double base_height, // height of base, truncated to 40% of length + // if 0.0: use a plane, otherwise a pyramid + u32 arrow_flag, // color of arror peak at axis2, -1:no arrow + double arrow_size, // relative size of arrow (multiplied by *r), + // truncated to length. no arrow if <=0.0 + bool arrow_flip // true: flip direction of arrow +) +{ + DASSERT(kcl); + DASSERT(axis1); + DASSERT(axis2); + + const uint max_side = 100; + if ( n_side < 2 ) + n_side = 2; + else if ( n_side > max_side ) + n_side = max_side; + + const uint n_tri = n_side * ( base_flag == M1(base_flag) ? 2 : 4 ); + kcl_tridata_t *td = PrepareAppendTrianglesKCL(kcl,n_tri); + if (!td) + return 0; + kcl->tridata.used -= n_tri; + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + + //--- calculations + + double3 n[3]; + double prism_len = CalcNormals(n,axis1,axis2,dir,r); + if (!prism_len) + return 0; + + if ( base_height > 0.4 * prism_len ) + base_height = 0.4 * prism_len; + base_height /= r; + + const bool draw_arrow = arrow_flag != M1(arrow_flag) && arrow_size > 0.0; + double arrow_height; + if (draw_arrow) + { + arrow_height = 1.5; + const double max = prism_len / (arrow_height*r) - 1.0; + if ( arrow_size > max ) + arrow_size = max; + } + else + arrow_height = base_height; + + double3 base1, base2; + base1.x = axis1->x + n[0].x * base_height; + base1.y = axis1->y + n[0].y * base_height; + base1.z = axis1->z + n[0].z * base_height; + base2.x = axis2->x - n[0].x * arrow_height; + base2.y = axis2->y - n[0].y * arrow_height; + base2.z = axis2->z - n[0].z * arrow_height; + + double3 tab1[max_side+1], tab2[max_side+1]; + double3 *p1 = tab1, *p2 = tab2; + + + //--- calculate the points and print the faces + + if ( mode & 1 ) + { + // anti prism + + const double add_rad = M_PI / n_side; + double rad = 0.0; + + uint i; + for ( i = 0; i < n_side; i++ ) + { + double fcos, fsin; + + fcos = cos(rad); + fsin = sin(rad); + rad += add_rad; + p1->x = base1.x + fcos * n[1].x + fsin * n[2].x; + p1->y = base1.y + fcos * n[1].y + fsin * n[2].y; + p1->z = base1.z + fcos * n[1].z + fsin * n[2].z; + p1++; + + fcos = cos(rad); + fsin = sin(rad); + rad += add_rad; + p2->x = base2.x + fcos * n[1].x + fsin * n[2].x; + p2->y = base2.y + fcos * n[1].y + fsin * n[2].y; + p2->z = base2.z + fcos * n[1].z + fsin * n[2].z; + p2++; + } + *p1 = *tab1; + *p2 = *tab2; + + for ( i = 0, p1 = tab1, p2 = tab2; i < n_side; i++, p1++, p2++ ) + { + AppendTriangleKCL( &par, p1, p1+1, p2 ); + AppendTriangleKCL( &par, p2, p1+1, p2+1 ); + } + } + else + { + const double rad_f = 2*M_PI / n_side; + + uint i; + for ( i = 0; i < n_side; i++, p1++, p2++ ) + { + const double rad = i * rad_f; + const double fcos = cos(rad); + const double fsin = sin(rad); + noPRINT("ROT=%6.2f, COS=%6.3f, SIN=%6.3f\n", rad * (180/M_PI), fcos, fsin ); + double a; + a = fcos * n[1].x + fsin * n[2].x; p1->x = base1.x + a; p2->x = base2.x + a; + a = fcos * n[1].y + fsin * n[2].y; p1->y = base1.y + a; p2->y = base2.y + a; + a = fcos * n[1].z + fsin * n[2].z; p1->z = base1.z + a; p2->z = base2.z + a; + if ( i > 0 ) + AppendQuadrilateralKCL( &par, p1-1, p1, p2, p2-1 ); + } + AppendQuadrilateralKCL( &par, p1-1, tab1, tab2, p2-1 ); + } + + //--- print base pyramids + + if ( base_flag != M1(base_flag) ) + { + if ( fabs(base_height) < 1e-9 ) + { + AppendTrianglesKCL( &par, tab1 + n_side - 1, + -(int)sizeof(*tab1), n_side ); + if (!draw_arrow) + AppendTrianglesKCL( &par, tab2, sizeof(*tab2), n_side ); + } + else + { + AppendPyramidKCL(kcl,base_flag,axis1,tab1,sizeof(*tab1),n_side,true); + if (!draw_arrow) + AppendPyramidKCL(kcl,base_flag,axis2,tab2,sizeof(*tab2),n_side,false); + } + } + + if (draw_arrow) + { + if (arrow_flip) + { + // ??? + } + else + { + uint i; + for ( i = 0, p2 = tab2; i < n_side; i++, p2++ ) + { + p2->x += ( p2->x - axis2->x ) * arrow_size; + p2->y += ( p2->y - axis2->y ) * arrow_size; + p2->z += ( p2->z - axis2->z ) * arrow_size; + } + AppendPyramidKCL(kcl,arrow_flag,axis2,tab2,sizeof(*tab2),n_side,true); + AppendPyramidKCL(kcl,arrow_flag,axis2,tab2,sizeof(*tab2),n_side,false); + } + } + + return td; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t * AppendCylinderKCL +( + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *pos, // base point of cylinder + const double3 *p_scale, // scale. If NULL: assume v(1,1,1) + double scale_factor, // Scale is multiplied by this factor + const double3 *rotate, // NULL or rotation vector + uint n_side, // number of sides, max=100 + u32 base_flag // color of bases, -1:no base +) +{ + DASSERT(kcl); + DASSERT(pos); + + const uint max_side = 100; + if ( n_side < 2 ) + n_side = 2; + else if ( n_side > max_side ) + n_side = max_side; + + const uint n_tri = n_side * ( base_flag == M1(base_flag) ? 2 : 4 ); + + kcl_tridata_t *td = PrepareAppendTrianglesKCL(kcl,n_tri); + if (!td) + return 0; + kcl->tridata.used -= n_tri; + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + + + //--- setup pt list + + const uint n_used = 2*n_side+2; + double3 pt[2*max_side+2], scale; + double3 *tab0 = pt, *tab1 = tab0 + n_side+1; + + scale.x = scale.y = scale.z = scale_factor; + if (p_scale) + { + scale.x *= p_scale->x; + scale.y *= p_scale->y; + scale.z *= p_scale->z; + } + const double ht = 2*scale.y; + + double3 *p0 = tab0, *p1 = tab1; + const double add_rad = M_PI / n_side; + double rad = 0.0; + + uint i; + for ( i = 0; i < n_side; i++ ) + { + p0->x = scale.x * cos(rad); + p0->y = 0.0; + p0->z = scale.z * sin(rad); + p0++; + rad += add_rad; + + p1->x = scale.x * cos(rad); + p1->y = ht; + p1->z = scale.z * sin(rad); + p1++; + rad += add_rad; + } + *p0 = *tab0; + *p1 = *tab1; + + + //--- calculations + + if (rotate) + Rotate(0,rotate,pt,sizeof(*pt),n_used); + + Translate(pos,pt,sizeof(*pt),n_used); + + + //--- draw + + for ( i = 0, p0 = tab0, p1 = tab1; i < n_side; i++, p0++, p1++ ) + { + AppendTriangleKCL( &par, p0+1, p0, p1 ); + AppendTriangleKCL( &par, p0+1, p1, p1+1 ); + } + + if ( base_flag != M1(base_flag) ) + { + InitializeTriParam(&par,kcl,base_flag); + AppendTrianglesKCL(&par,tab0,sizeof(*tab0),n_side); + AppendTrianglesKCL(&par,tab1+n_side-1,-(int)sizeof(*tab1),n_side); + } + + return td; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t * AppendJoistKCL +( + kcl_t * kcl, // pointer to valid KCL + u32 kcl_flag, // parameter for AppendTrianglesKCLp() + double length, // length of corpus + const double3 *p1, // first point + const double3 *p2, // second point + const double3 *p3, // helper point to find direction + int n_mark, // >0: add marker representing the number (modulo) + u32 mark5_flag // >0: replace 5 marker by one of this color +) +{ + DASSERT(kcl); + DASSERT(p1); + DASSERT(p2); + DASSERT(p3); + + kcl_tridata_t *td = PrepareAppendTrianglesKCL(kcl,6); + if (!td) + return 0; + kcl->tridata.used -= 6; + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + + + //--- calculate joist + + double3 d13, n12, n13, nx; + Sub3(n12,*p2,*p1); + Unit3(n12); + Sub3(d13,*p3,*p1); + CrossProd3(nx,n12,d13); + Unit3(nx); + CrossProd3(n13,nx,n12); + + double3 p1a, p1b, p2a, p2b; + uint p; + for ( p = 0; p < 3; p++ ) + { + const double d12 = length * n12.v[p]; + const double d13 = length * n13.v[p]; + const double dx = length * nx.v[p] * 0.3; + p1a.v[p] = p1->v[p] + d12 + d13 - dx; + p1b.v[p] = p1->v[p] + d12 + d13 + dx; + p2a.v[p] = p2->v[p] - d12 + d13 - dx; + p2b.v[p] = p2->v[p] - d12 + d13 + dx; + } + + + //--- draw joist + + AppendQuadrilateralKCL( &par, p1, &p1a, &p2a, p2 ); + AppendQuadrilateralKCL( &par, &p1a, &p1b, &p2b, &p2a ); + AppendQuadrilateralKCL( &par, &p1b, p1, p2, &p2b ); + + if ( n_mark <= 0 ) + return td; + + + //--- calc marker counter + + const uint n_mark5 = mark5_flag > 0 ? n_mark/5 : 0; + n_mark -= n_mark5 * 4; + + + //--- calc marker basics + + length *= 2; + + double3 pt1, pt2, delta[4]; + for ( p = 0; p < 3; p++ ) + { + const double d12 = length * n12.v[p]; + const double d13 = length * n13.v[p]; + const double dx = length * nx.v[p]; + + pt1.v[p] = p1->v[p] + d12 + 0.25 * d13; + pt2.v[p] = p2->v[p] - d12 + 0.25 * d13; + + delta[0].v[p] = -d13 - dx; + delta[1].v[p] = -d13 + dx; + delta[2].v[p] = d13 + dx; + delta[3].v[p] = d13 - dx; + noPRINT("DELTA[%u]: %6.1f %6.1f %6.1f %6.1f\n", + p, delta[0].v[p], delta[1].v[p], delta[2].v[p], delta[3].v[p] ); + } + + + //--- calc axis positions + + const double factor = 2 * n_mark + 0.51; + + double dist = LengthD(pt1.v,pt2.v); + double need = length * factor; + if ( need <= dist ) + { + need = (1.0-need/dist)/2; + for ( p = 0; p < 3; p++ ) + { + double d = ( pt2.v[p] - pt1.v[p] ) * need; + pt1.v[p] += d; + pt2.v[p] -= d; + } + } + + double3 dx; + dx.x = ( pt2.x - pt1.x ) / factor; + dx.y = ( pt2.y - pt1.y ) / factor; + dx.z = ( pt2.z - pt1.z ) / factor; + + + //--- append markers + + uint i; + for ( i = 0; i < n_mark; i++ ) + { + if ( i < n_mark5 ) + { + double3 base1[4], base2[4]; + for ( p = 0; p < 3; p++ ) + { + pt2.v[p] = pt1.v[p] + 0.5 * dx.v[p]; + pt1.v[p] = pt2.v[p] + 1.5 * dx.v[p]; + + uint j; + for ( j = 0; j < 4; j++ ) + { + base1[j].v[p] = pt1.v[p] + delta[j].v[p]; + base2[j].v[p] = pt2.v[p] + delta[j].v[p]; + } + } + + AppendPyramidKCL(kcl,mark5_flag,&pt2,base1,sizeof(double3),4,true); + AppendPyramidKCL(kcl,mark5_flag,&pt1,base2,sizeof(double3),4,true); + } + else + { + double3 base[4]; + for ( p = 0; p < 3; p++ ) + { + pt2.v[p] = pt1.v[p] + 0.5 * dx.v[p]; + pt1.v[p] = pt2.v[p] + 1.5 * dx.v[p]; + + const double base1 = ( pt1.v[p] + pt2.v[p] ) / 2; + uint j; + for ( j = 0; j < 4; j++ ) + base[j].v[p] = base1 + delta[j].v[p]; + } + + AppendPyramidKCL(kcl,kcl_flag,&pt2,base,sizeof(double3),4,false); + AppendPyramidKCL(kcl,kcl_flag,&pt1,base,sizeof(double3),4,true); + } + } + + return td; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// statistics /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetStatisticsKCL ( kcl_t * kcl ) +{ + DASSERT(kcl); + + FREE(kcl->o_depth); + FREE(kcl->o_count); + FREE(kcl->l_count); + + u8 *stat_beg = (u8*)&kcl->stat_valid; + u8 *stat_end = (u8*)kcl + sizeof(*kcl); + memset(stat_beg,0,stat_end-stat_beg); + + DASSERT(!kcl->stat_valid); + DASSERT(!kcl->o_depth); + DASSERT(!kcl->o_count); + DASSERT(!kcl->l_count); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u32 calc_stat_oct +( + // returns next offset + + kcl_t *kcl, // valid KCL + u32 offset, // offset check + uint n // number of links to check +) +{ + DASSERT(kcl); + noPRINT("calc_stat_oct: off=%x, n=%x\n",offset,n); + + if ( offset >= kcl->octree_size ) + return kcl->octree_size; + const u32 baseoff = offset; + + const uint max_n = ( kcl->octree_size - offset ) / 4; + if ( n > max_n ) + n = max_n; + + while ( n-- > 0 ) + { + u32 val = be32(kcl->octree+offset); + offset += 4; + + if ( val & 0x80000000 ) + { + val = ( val & 0x7ffffff ) + baseoff + 2; + if ( kcl->min_l_offset > val ) + kcl->min_l_offset = val; + if ( kcl->max_l_offset < val ) + kcl->max_l_offset = val; + } + else + { + val += baseoff + 0x20; + if ( kcl->max_o_offset < val ) + kcl->max_o_offset = val; + } + } + return offset; +} + +/////////////////////////////////////////////////////////////////////////////// + +static u32 calc_stat_octlist +( + // returns next offset + + kcl_t *kcl, // valid KCL + u32 offset, // offset check + uint n, // number of links to check + uint depth // current depth +) +{ + DASSERT(kcl); + noPRINT("calc_stat_oct: off=%x, n=%x\n",offset,n); + + if ( offset >= kcl->octree_size ) + return kcl->octree_size; + const u32 baseoff = offset; + + const uint max_n = ( kcl->octree_size - offset ) / 4; + if ( n > max_n ) + n = max_n; + + while ( n-- > 0 ) + { + u32 val = be32(kcl->octree+offset); + offset += 4; + + if ( val & 0x80000000 ) + { + u32 off = ( val & 0x7ffffff ) + baseoff + 2; + val = ( off - kcl->min_l_offset ) / 2; + if ( val < kcl->n_trilist ) + { + u32 len = 0; + while (be16(kcl->octree+off)) + off+=2, len++; + if (len) + { + kcl->sum_tri_len += len; + if ( kcl->max_tri_len < len ) + kcl->max_tri_len = len; + if ( depth < KCL_MAX_STAT_DEPTH + && kcl->max_depth_tri_len[depth] < len ) + { + kcl->max_depth_tri_len[depth] = len; + } + kcl->n_tri_link++; + if (!kcl->l_count[val]++) + kcl->n_tri_list++; + } + else + { + kcl->n_0_link++; + if (!kcl->l_count[val]++) + kcl->n_0_list++; + } + } + } + else + { + val = ( val + baseoff - kcl->min_o_offset ) / 0x20; + if ( val < kcl->n_cube_nodes ) + { + kcl->o_count[val]++; + if ( kcl->o_depth[val] < depth ) + kcl->o_depth[val] = depth; + } + } + } + return offset; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CalcStatisticsKCL +( + kcl_t *kcl // valid KCL +) +{ + DASSERT(kcl); + + if (kcl->stat_valid) + return; + + DASSERT(kcl); + ResetStatisticsKCL(kcl); + KCL_ACTION_LOG("CalcStatisticsKCL()\n"); + CreateOctreeKCL(kcl); + + + //--- setup + + kcl->hash_bits = 0; + kcl->total_bcubes = 1; + kcl->bcube_width = 1 << kcl->coord_rshift; + + + //--- axis dependent calculations + + uint p; + for ( p = 0; p < 3; p++ ) + { + u32 val = kcl->mask[p]; + uint count; + if (!val) + count = 32; + else + { + count = 0; + for ( ; !(val&1); val >>= 1 ) + count++; + } + kcl->mask_bits[p] = count; + count = count > kcl->coord_rshift ? count - kcl->coord_rshift : 0; + kcl->bcube_bits[p] = count; + kcl->hash_bits += count; + kcl->n_bcubes[p] = 1 << count; + kcl->total_bcubes *= 1 << count; + kcl->max_octree.v[p] = kcl->min_octree.v[p] + + kcl->bcube_width * kcl->n_bcubes[p]; + } + + //--- logging + + noPRINT("KCL BASE STATS:\n" + "\t\tH.mask: 0x%08x 0x%08x 0x%08x\n" + "\t\tH.shift: %11u %11u %11u\n" + "\t\tS.mask_bits: %11u %11u %11u\n" + "\t\tS.bcube_bits: %11u %11u %11u =>%5u\n" + "\t\tS.n_bcubes: %11u %11u %11u =>%5u\n" + "\t\tS.bcube_width: %19u\n" + "\t\tH.min_octree: %11.3f %11.3f %11.3f\n" + "\t\tS.max_octree: %11.3f %11.3f %11.3f\n" + "\t\t---\n", + kcl->mask[0], kcl->mask[1], kcl->mask[2], + kcl->coord_rshift, kcl->y_lshift, kcl->z_lshift, + kcl->mask_bits[0], kcl->mask_bits[1], kcl->mask_bits[2], + kcl->bcube_bits[0], kcl->bcube_bits[1], kcl->bcube_bits[2], kcl->hash_bits, + kcl->n_bcubes[0], kcl->n_bcubes[1], kcl->n_bcubes[2], kcl->total_bcubes, + kcl->bcube_width, + kcl->min_octree.x, kcl->min_octree.y, kcl->min_octree.z, + kcl->max_octree.x, kcl->max_octree.y, kcl->max_octree.z + ); + + + //--- octree setup + + kcl->max_o_offset = kcl->total_bcubes * 4; + kcl->max_l_offset = 0; + kcl->min_l_offset = kcl->octree_size; + + u32 offset = calc_stat_oct(kcl,0,kcl->total_bcubes); + kcl->min_o_offset = offset; + while ( offset < kcl->max_o_offset && offset < kcl->octree_size ) + offset = calc_stat_oct(kcl,offset,8); + + kcl->n_cube_nodes = ( kcl->max_o_offset - kcl->min_o_offset ) / 0x20; + kcl->n_trilist = ( kcl->octree_size - kcl->min_l_offset ) / 2 + 1; + + + //--- fix max_l_offset to end of list + + offset = kcl->max_l_offset; + while ( offset < kcl->octree_size ) + { + const u16 val = be16(kcl->octree+offset); + offset += 2; + if (!val) + break; + } + kcl->max_l_offset = offset; + + + //--- logging + + noPRINT("KCL OCTREE STATS:\n" + "\t\tS.o_offset: %#9x .. %#9x, N:%8u\n" + "\t\tS.l_offset: %#9x .. %#9x, N:%8u\n" + "\t\t---\n", + kcl->min_o_offset, kcl->max_o_offset, kcl->n_cube_nodes, + kcl->min_l_offset, kcl->max_l_offset, kcl->n_trilist ); + + + //--- setup octree lists + + FREE(kcl->o_depth); + FREE(kcl->o_count); + FREE(kcl->l_count); + kcl->o_depth = CALLOC(kcl->n_cube_nodes,sizeof(*kcl->o_depth)); + kcl->o_count = CALLOC(kcl->n_cube_nodes,sizeof(*kcl->o_count)); + kcl->l_count = CALLOC(kcl->n_trilist,sizeof(*kcl->l_count)); + + kcl->n_0_list = 0; + kcl->n_0_link = 0; + kcl->n_tri_list = 0; + kcl->n_tri_link = 0; + kcl->max_tri_len = 0; + kcl->sum_tri_len = 0; + memset(kcl->max_depth_tri_len,0,sizeof(kcl->max_depth_tri_len)); + + + //--- fill octree lists + + uint max_depth = 0, sum_depth = 0; + offset = calc_stat_octlist(kcl,0,kcl->total_bcubes,1); + + while ( offset < kcl->max_o_offset && offset < kcl->octree_size ) + { + const uint depth = kcl->o_depth[(offset-kcl->min_o_offset)/0x20]; + sum_depth += 7*depth + 1; + if ( max_depth < depth ) + max_depth = depth; + offset = calc_stat_octlist(kcl,offset,8,depth+1); + } + kcl->max_depth = max_depth; + kcl->total_cubes = kcl->n_cube_nodes * 8 + kcl->total_bcubes; + kcl->ave_depth = (double)sum_depth/kcl->total_cubes; + + kcl->min_depth = 0; + uint d; + for ( d = 0; d < KCL_MAX_STAT_DEPTH; d++ ) + if ( kcl->max_depth_tri_len[d] ) + { + kcl->min_depth = d - 1; + break; + } + + + //--- logging + + PRINT("KCL OCTREE STATS: 0=%u,%u, tri=%u,%u, len=%u,%u, depth=%u..%u\n", + kcl->n_0_list, kcl->n_0_link, + kcl->n_tri_list, kcl->n_tri_link, + kcl->max_tri_len, kcl->sum_tri_len, + kcl->min_depth, kcl->max_depth ); + + + //--- setup triangle usage + + const int n_tri = kcl->tridata.used; + u8 *tri_used = CALLOC(n_tri,sizeof(*tri_used)); + kcl->n_invalid_tri_ref = 0; + kcl->n_tri_used = 0; + + + //--- calc triangle usage + + noPRINT("OFF: %x .. %x\n",kcl->min_l_offset,kcl->max_l_offset); + for ( offset = kcl->min_l_offset; offset < kcl->max_l_offset; offset += 2 ) + { + uint val = be16( kcl->octree + offset ); + if (val) + { + if ( --val >= n_tri ) + kcl->n_invalid_tri_ref++; + else if (!tri_used[val]) + { + tri_used[val] = 1; + kcl->n_tri_used++; + } + } + } + + + //--- set TD_UNUSED + + kcl->used_valid = true; + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + uint i; + for ( i = 0; i < n_tri; i++, td++ ) + if (tri_used[i]) + td->status &= ~TD_UNUSED; + else + td->status |= TD_UNUSED; + + FREE(tri_used); + + + //--- terminate & logging + + kcl->stat_valid = true; + noPRINT("KCL TRIANGLE STATS: used=%u/%u, invalid link=%u\n", + kcl->n_tri_used, n_tri, kcl->n_invalid_tri_ref ); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CalcMinMaxKCL +( + kcl_t * kcl // KCL data structure +) +{ + DASSERT(kcl); + + if (!kcl->min_max_valid) + { + KCL_ACTION_LOG("CalcMinMaxKCL()\n"); + kcl->min_max_valid = true; + + const uint n_tri = kcl->tridata.used; + if (n_tri) + { + const kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + const kcl_tridata_t *td_end = td + n_tri; + + memcpy(&kcl->min,td->pt,sizeof(kcl->min)); + memcpy(&kcl->max,td->pt,sizeof(kcl->max)); + double tmin = 1e99; + double tmax = 0.0; + double3 sum; + sum.x = sum.y = sum.z = 0.0; + + for ( ; td < td_end; td++ ) + { + const double3 *d3 = td->pt; + MinMax3(&kcl->min,&kcl->max,d3,3); + + sum.x += d3[0].x + d3[1].x + d3[2].x; + sum.y += d3[0].y + d3[1].y + d3[2].y; + sum.z += d3[0].z + d3[1].z + d3[2].z; + + double dist; + double3 diff; + + Sub3(diff,d3[0],d3[1]); + dist = LengthSqare3(diff); + if ( tmin > dist ) tmin = dist; + if ( tmax < dist ) tmax = dist; + + Sub3(diff,d3[0],d3[2]); + dist = LengthSqare3(diff); + if ( tmin > dist ) tmin = dist; + if ( tmax < dist ) tmax = dist; + + Sub3(diff,d3[1],d3[2]); + dist = LengthSqare3(diff); + if ( tmin > dist ) tmin = dist; + if ( tmax < dist ) tmax = dist; + } + kcl->tmin_dist = sqrt(tmin); + kcl->tmax_dist = sqrt(tmax); + + const double factor = 1 / ( n_tri * 3.0 ); + kcl->mean.x = sum.x * factor; + kcl->mean.y = sum.y * factor; + kcl->mean.z = sum.z * factor; + } + else + { + memset(&kcl->min,0,sizeof(kcl->min)); + memset(&kcl->max,0,sizeof(kcl->max)); + memset(&kcl->mean,0,sizeof(kcl->max)); + kcl->tmin_dist = 0.0; + kcl->tmax_dist = 0.0; + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CreateOctreeKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[oct_info_t]] + +typedef struct oct_info_t +{ + kcl_t *kcl; // current KCL + uint n_tri; // number of triangles + kcl_tri_t *tri; // triangle values + #if SUPPORT_KCL_CUBE + kcl_cube_list_t *cubelist; // NULL or a cube list for each 'tri' + #endif + + u32 *olist; // octree list + uint oused; // number of used elements in 'olist' + uint osize; // number of alloced elements in 'olist' + + u16 *tlist; // triangle list + uint tused; // number of used elements in 'tlist' + uint tsize; // number of alloced elements in 'tlist' + +} oct_info_t; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void setup_oct_tri +( + kcl_t *kcl, // valid kcl + kcl_tri_t *tri, // valid pointer -> dest + #if SUPPORT_KCL_CUBE + kcl_cube_list_t *cl, // NULL or pointer to a cube list + #endif + const kcl_tridata_t *td // valid data -> source +) +{ + DASSERT(kcl); + DASSERT(tri); + DASSERT(td); + + uint p; + const double3 *d3 = td->pt; + for ( p = 0; p < 3; p++ ) + { + s32 min = (u32)round( d3[0].v[p] - kcl->min_octree.v[p] ); + s32 max = min; + tri->pt[0][p] = min; + + s32 temp = (u32)round( d3[1].v[p] - kcl->min_octree.v[p] ); + tri->pt[1][p] = temp; + if ( min > temp ) + min = temp; + if ( max < temp ) + max = temp; + + temp = (u32)round( d3[2].v[p] - kcl->min_octree.v[p] ); + tri->pt[2][p] = temp; + + tri->cube.min[p] = min < temp ? min : temp; + tri->cube.max[p] = max > temp ? max : temp; + noPRINT(" %u: min=%6d, max=%6d, t=%6d %6d %6d\n", + p, tri->cube.min[p], tri->cube.max[p], + tri->pt[0][p], tri->pt[1][p], tri->pt[0][p] ); + + #if SUPPORT_KCL_CUBE + if (cl) + { + // [[2do]] [[cube]] KCL_TRI_SPLIT + + cl->n = 1; + cl->cube[0] = tri->cube; + } + #endif + } + tri->select = td->status & TD_INVALID ? M1(tri->select) : 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//======================== X-tests ======================== + +#define AXISTEST_X01(a, b, fa, fb) \ + p0 = a*v0.y - b*v0.z; \ + p2 = a*v2.y - b*v2.z; \ + if(p0<p2) {min=p0; max=p2;} else {min=p2; max=p0;} \ + rad = (fa+fb) * cubehalf; \ + if ( min > rad || max <- rad ) return 0; + +#define AXISTEST_X2(a, b, fa, fb) \ + p0 = a*v0.y - b*v0.z; \ + p1 = a*v1.y - b*v1.z; \ + if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \ + rad = (fa+fb) * cubehalf; \ + if(min>rad || max<-rad) return 0; + +//======================== Y-tests ======================== + +#define AXISTEST_Y02(a, b, fa, fb) \ + p0 = -a*v0.x + b*v0.z; \ + p2 = -a*v2.x + b*v2.z; \ + if(p0<p2) {min=p0; max=p2;} else {min=p2; max=p0;} \ + rad = (fa+fb) * cubehalf; \ + if(min>rad || max<-rad) return 0; + +#define AXISTEST_Y1(a, b, fa, fb) \ + p0 = -a*v0.x + b*v0.z; \ + p1 = -a*v1.x + b*v1.z; \ + if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \ + rad = (fa+fb) * cubehalf; \ + if(min>rad || max<-rad) return 0; + +//======================== Z-tests ======================== + +#define AXISTEST_Z12(a, b, fa, fb) \ + p1 = a*v1.x - b*v1.y; \ + p2 = a*v2.x - b*v2.y; \ + if(p2<p1) {min=p2; max=p1;} else {min=p1; max=p2;} \ + rad = (fa+fb) * cubehalf; \ + if(min>rad || max<-rad) return 0; + +#define AXISTEST_Z0(a, b, fa, fb) \ + p0 = a*v0.x - b*v0.y; \ + p1 = a*v1.x - b*v1.y; \ + if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \ + rad = (fa+fb) * cubehalf; \ + if(min>rad || max<-rad) return 0; + +//========================================================== + +static bool OctCubeTriangleOverlaped +( + kcl_cube_t *cube, // cube info + kcl_tri_t *tri // triangle info + #if SUPPORT_KCL_CUBE + , + kcl_cube_list_t *cl // NULL or cube list + #endif +) +{ + DASSERT( cube && tri ); + + //------------------------------------------------------------ + // we use this algorithm: + // http://jgt.akpeters.com/papers/AkenineMoller01/tribox.html + //------------------------------------------------------------ + + //--- Bullet 1: test the cube, that surrounds the triangle + + if ( tri->select == M1(tri->select) + || tri->cube.min[0] > cube->max[0] || tri->cube.max[0] < cube->min[0] + || tri->cube.min[1] > cube->max[1] || tri->cube.max[1] < cube->min[1] + || tri->cube.min[2] > cube->max[2] || tri->cube.max[2] < cube->min[2] ) + { + return false; + } + + + #if SUPPORT_KCL_CUBE // [[cube]] + if (cl) + { + if ( cl->n == 1 ) + return true; + + // use [[cube]] param? + } + #endif + + + //--- center cube and triangle + + const int cubehalf = ( cube->max[0] - cube->min[0] ) / 2; + DASSERT( cubehalf == ( cube->max[1] - cube->min[1] ) / 2 ); + DASSERT( cubehalf == ( cube->max[2] - cube->min[2] ) / 2 ); + + uint p; + double3 v0, v1, v2; + for ( p = 0; p < 3; p++ ) + { + const int cubemid = cube->min[p] + cubehalf; + v0.v[p] = tri->pt[0][p] - cubemid; + v1.v[p] = tri->pt[1][p] - cubemid; + v2.v[p] = tri->pt[2][p] - cubemid; + } + + double3 e0,e1,e2; + Sub3(e0,v1,v0); // tri edge 0 + Sub3(e1,v2,v1); // tri edge 1 + Sub3(e2,v0,v2); // tri edge 2 + + //--- Bullet 3: + + double min, max, p0,p1,p2, rad, fex,fey,fez; + + fex = fabs(e0.x); + fey = fabs(e0.y); + fez = fabs(e0.z); + AXISTEST_X01(e0.z, e0.y, fez, fey); + AXISTEST_Y02(e0.z, e0.x, fez, fex); + AXISTEST_Z12(e0.y, e0.x, fey, fex); + + fex = fabs(e1.x); + fey = fabs(e1.y); + fez = fabs(e1.z); + AXISTEST_X01(e1.z, e1.y, fez, fey); + AXISTEST_Y02(e1.z, e1.x, fez, fex); + AXISTEST_Z0 (e1.y, e1.x, fey, fex); + + fex = fabs(e2.x); + fey = fabs(e2.y); + fez = fabs(e2.z); + AXISTEST_X2 (e2.z, e2.y, fez, fey); + AXISTEST_Y1 (e2.z, e2.x, fez, fex); + AXISTEST_Z12(e2.y, e2.x, fey, fex); + + //--- Bullet 2: + // test if the box intersects the plane of the triangle + // compute plane equation of triangle: normal*x+d=0 + + double3 normal; + CrossProd3(normal,e0,e1); + + double3 vmin, vmax; + for ( p = 0; p < 3; p++ ) + { + double v = v0.v[p]; + if ( normal.v[p] > 0.0 ) + { + vmin.v[p] = -cubehalf - v; + vmax.v[p] = cubehalf - v; + } + else + { + vmin.v[p] = cubehalf - v; + vmax.v[p] = -cubehalf - v; + } + } + + return DotProd3(normal,vmin) <= 0.0 + && DotProd3(normal,vmax) >= 0.0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static u32 CalcCube +( + oct_info_t *oi, // base data + kcl_cube_t *cube, // cube info + uint depth // current depth, also used for select level +) +{ + DASSERT(oi); + DASSERT(oi->kcl); + DASSERT(cube); + + noPRINT("CUBE*: min=%6u,%6u,%6u, max=%6u,%6u,%6u\n", + cube->min[0], cube->min[1], cube->min[2], + cube->max[0], cube->max[1], cube->max[2] ); + + uint tri_count = 0; + + const uint cube_blow = oi->kcl->cube_blow; + cube->min[0] -= cube_blow; + cube->min[1] -= cube_blow; + cube->min[2] -= cube_blow; + cube->max[0] += cube_blow; + cube->max[1] += cube_blow; + cube->max[2] += cube_blow; + + #if SUPPORT_KCL_CUBE + kcl_tri_t *tri, *end = oi->tri + oi->n_tri; + kcl_cube_list_t *cl = oi->cubelist; + for ( tri = oi->tri ; tri < end; tri++ ) + { + if ( tri->select == depth && OctCubeTriangleOverlaped(cube,tri,cl) ) + { + tri->select++; + tri_count++; + } + if (cl) + cl++; + } + #else + kcl_tri_t *tri, *end = oi->tri + oi->n_tri; + for ( tri = oi->tri ; tri < end; tri++ ) + if ( tri->select == depth && OctCubeTriangleOverlaped(cube,tri) ) + { + tri->select++; + tri_count++; + } + #endif + + cube->min[0] += cube_blow; + cube->min[1] += cube_blow; + cube->min[2] += cube_blow; + cube->max[0] -= cube_blow; + cube->max[1] -= cube_blow; + cube->max[2] -= cube_blow; + + depth++; + uint cube_size = cube->max[0] - cube->min[0]; + + if ( cube_size <= oi->kcl->max_cube_size && tri_count <= oi->kcl->max_cube_triangles + || cube_size <= oi->kcl->min_cube_size + || depth > oi->kcl->max_octree_depth ) + { + noPRINT("--> TLIST: n-tri =%5u, width =%6u, base = %6u,%6u,%6u, depth=%u/%u\n", + tri_count, cube_size, + cube->min[0], cube->min[1], cube->min[2], + depth, kcl->max_octree_depth ); + + if (!tri_count) + return 0; + + if ( oi->tused + tri_count + 1 > oi->tsize ) + { + oi->tsize *= 2; + TRACE("REALLOC(tlist), new size = %u\n",oi->tsize); + oi->tlist = REALLOC( oi->tlist, oi->tsize * sizeof(*oi->tlist) ); + } + u32 result = 0x80000000 | oi->tused * sizeof(*oi->tlist); + + // create the list + u16 *tdata = oi->tlist + oi->tused; + u16 *tptr = tdata; + for ( tri = oi->tri ; tri < end; tri++ ) + if ( tri->select == depth ) + { + tri->select--; + write_be16( tptr++, tri - oi->tri + 1 ); + } + write_be16( tptr++, 0); + DASSERT( tptr - oi->tlist <= oi->tsize ); + DASSERT( tptr - oi->tlist == oi->tused + tri_count + 1 ); + + if (!oi->kcl->fast) + { + // try to find a duplicate + u16 *tmax = tdata - tri_count - 1; + const uint tsize = ( tri_count + 1 ) * sizeof(*oi->tlist); + for ( tptr = oi->tlist; tptr <= tmax; tptr++ ) + if (!memcmp(tptr,tdata,tsize)) + return ( tptr - oi->tlist ) * sizeof(*oi->tlist) | 0x80000000; + } + + // no duplicate found + oi->tused += tri_count + 1; + return result; + } + else + { + noPRINT("--> SUBCUBE: n-tri =%5u, width =%6u, base = %6u,%6u,%6u, depth=%u/%u\n", + tri_count, cube_size, + cube->min[0], cube->min[1], cube->min[2], + depth, kcl->max_octree_depth ); + + if ( oi->oused + 8 > oi->osize ) + { + oi->osize *= 2; + TRACE("REALLOC(olist), new size = %u\n",oi->osize); + oi->olist = REALLOC( oi->olist, oi->osize * sizeof(*oi->olist) ); + } + u32 result = oi->oused * sizeof(*oi->olist); + uint ores = oi->oused; + oi->oused += 8; + DASSERT( oi->oused <= oi->osize ); + + cube_size /= 2; + uint zi; + for ( zi = 0; zi < 2; zi++ ) + { + uint yi; + for ( yi = 0; yi < 2; yi++ ) + { + uint xi; + for ( xi = 0; xi < 2; xi++ ) + { + kcl_cube_t subcube; + subcube.min[0] = cube->min[0] + xi * cube_size; + subcube.max[0] = subcube.min[0] + cube_size; + subcube.min[1] = cube->min[1] + yi * cube_size; + subcube.max[1] = subcube.min[1] + cube_size; + subcube.min[2] = cube->min[2] + zi * cube_size; + subcube.max[2] = subcube.min[2] + cube_size; + + // 2 steps, because oi->olist may be realloced + const u32 result = CalcCube(oi,&subcube,depth); + oi->olist[ores++] = result; + } + } + } + + for ( tri = oi->tri ; tri < end; tri++ ) + if ( tri->select == depth ) + tri->select--; + return result; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateOctreeKCL +( + kcl_t * kcl // KCL data structure +) +{ + DASSERT(kcl); + if ( kcl->octree_valid && !kcl->recreate_octree ) + return ERR_NOTHING_TO_DO; + + TRACE_SIZEOF(kcl_mode_t); + TRACE_SIZEOF(kcl_cube_t); + #if SUPPORT_KCL_CUBE + TRACE_SIZEOF(kcl_cube_list_t); + #endif + TRACE_SIZEOF(kcl_tri_t); + TRACE_SIZEOF(oct_info_t); + + if (!kcl->silent_octree) + KCL_ACTION_LOG("Create KCL Octree [valid=%d,recreate=%d]\n", + kcl->octree_valid, kcl->recreate_octree ); + + CalcMinMaxKCL(kcl); + + if (kcl->octree_alloced) + FREE(kcl->octree); + kcl->octree = 0; + kcl->octree_size = 0; + kcl->octree_alloced = false; + kcl->recreate_octree = false; + + if (kcl->raw_data_alloced) + FREE(kcl->raw_data); + kcl->raw_data = 0; + kcl->raw_data_size = 0; + kcl->raw_data_alloced = false; + kcl->model_modified = true; + + + //--- calculate relevant bits + + uint p, relevant_bits[3], max_relevant = 0; + double max[3]; + for ( p = 0; p < 3; p++ ) + { + kcl->min_octree.v[p] + = ( IsNormalD(KCL_MIN.v[p]) ? KCL_MIN.v[p] : floor(kcl->min.v[p]) ) + - kcl->cube_blow; + + int exp; + max[p] = ( IsNormalD(KCL_MAX.v[p]) ? KCL_MAX.v[p] : kcl->max.v[p] ) + + kcl->cube_blow; + + frexp( max[p] - kcl->min_octree.v[p], &exp ); + relevant_bits[p] = exp < 0 ? 0 : exp; + kcl->mask[p] = ~(u32)0 << relevant_bits[p]; + if ( max_relevant < relevant_bits[p] ) + max_relevant = relevant_bits[p]; + } + noPRINT(" *MIN: %11.3f %11.3f %11.3f\n", kcl->min.x, kcl->min.y, kcl->min.z ); + noPRINT(" *MAX: %11.3f %11.3f %11.3f\n", kcl->max.x, kcl->max.y, kcl->max.z ); + noPRINT(" BASE: %11.3f %11.3f %11.3f\n", + kcl->min_octree.x, kcl->min_octree.y, kcl->min_octree.z ); + + + //--- maximum allowed bits in base cube selection + + int max_bits; + if (KCL_BITS) + max_bits = KCL_BITS; + else + { + max_bits = log(kcl->tridata.used)/log(2); + if ( max_bits < 5 ) + max_bits = 5; + #if 0 + else if ( max_bits >= 10 ) + max_bits = max_relevant > 10 + ? 10 + ( max_relevant - 10 ) / 2 + : 10; + #else + else if ( max_bits > 10 ) + max_bits = 10; + #endif + } + PRINT("KCL-PARAM: BITS=%u, MAX_TRI=%u, MAX_DEPTH=%u, CUBE_SIZE=%u..%u, BLOW=%u\n", + max_bits, kcl->max_cube_triangles, kcl->max_octree_depth, + kcl->min_cube_size, kcl->max_cube_size, kcl->cube_blow ); + + + //--- calculate shifts + + uint rshift = 1, nbits[3]; + while ( 1 << rshift < kcl->min_cube_size ) + rshift++; + for(;;) + { + uint sumbits = 0; + for ( p = 0; p < 3; p++ ) + { + nbits[p] = relevant_bits[p] > rshift ? relevant_bits[p] - rshift : 0; + sumbits += nbits[p]; + } + noPRINT("RSHIFT=%2u, NBITS= %2d + %2d + %2d = %2d / %2d\n", + rshift, + nbits[0], nbits[1], nbits[2], + sumbits, max_bits ); + if ( sumbits <= max_bits ) + break; + rshift++; + } + + const uint xn = 1 << nbits[0]; + const uint yn = 1 << nbits[1]; + const uint zn = 1 << nbits[2]; + const uint n_cubes = xn * yn * zn; + + PRINT("RSHIFT=%u, NBITS=%u,%u,%u, RELBITS=%u,%u,%u, N_CUBES=%u\n", + rshift, + nbits[0], nbits[1], nbits[2], + relevant_bits[0], relevant_bits[1], relevant_bits[2], + n_cubes ); + + kcl->coord_rshift = rshift; + kcl->y_lshift = nbits[0]; + kcl->z_lshift = nbits[0] + nbits[1]; + + + //--- adjust min_coord + + if ( KCL_MODE & KCLMD_CENTER ) + { + for ( p = 0; p < 3; p++ ) + { + const double space = ( 1 << rshift + nbits[p] ) + + kcl->min_octree.v[p] - max[p]; + noPRINT("ADJUST[%u] MIN=%11.3f, SPC=%11.3f\n",p,kcl->min_octree.v[p],space); + if ( space > 0.0 ) + { + PRINT("ADJUST[%u] %11.3f -> %11.3f\n", + p, kcl->min_octree.v[p], + kcl->min_octree.v[p] - floor(space/2) ); + kcl->min_octree.v[p] -= floor(space/2); + } + } + } + + + //--- setup data for octree calculations + + oct_info_t oi; + memset(&oi,0,sizeof(oi)); + oi.kcl = kcl; + oi.n_tri = kcl->tridata.used; + oi.tri = CALLOC(oi.n_tri,sizeof(*oi.tri)); + #ifdef TEST // [[cube]] + if ( KCL_MODE & KCLMD_CUBE ) + oi.cubelist = CALLOC(oi.n_tri,sizeof(*oi.cubelist)); + #endif + + oi.osize = oi.n_tri + n_cubes; + oi.oused = n_cubes; + oi.olist = CALLOC(oi.osize,sizeof(*oi.olist)); + uint ores = 0; + + oi.tsize = 2 + oi.n_tri; + oi.tused = 0; + oi.tlist = CALLOC(oi.tsize,sizeof(*oi.tlist)); + + if ( verbose >= 0 && !(KCL_MODE & KCLMD_SILENT) && !kcl->silent_octree ) + fprintf(stdlog, + " - create octree: rshift=%u, n_bcube=%u, cube_size=%u..%u, blow=%u," + " max_tri=%u, max_depth=%u, fast=%u\n", + rshift, n_cubes, + kcl->min_cube_size, kcl->max_cube_size, kcl->cube_blow, + kcl->max_cube_triangles, kcl->max_octree_depth, kcl->fast); + + uint t; + kcl_tri_t *tri = oi.tri; + const kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + for ( t = 0; t < oi.n_tri; t++, tri++, td++ ) + { + #if SUPPORT_KCL_CUBE + setup_oct_tri( kcl, tri, oi.cubelist ? oi.cubelist + t : 0 , td ); + #else + setup_oct_tri(kcl,tri,td); + #endif + noPRINT("%5u: min=%5d,%5d,%5d, max=%5d,%5d,%5d, select=%d\n", + t, + tri->min[0], tri->min[1], tri->min[2], + tri->max[0], tri->max[1], tri->max[2], + tri->select ); + } + + + //--- now create the octree (start recursion) + + const uint cube_size = 1 << rshift; + + uint zi; + for ( zi = 0; zi < zn; zi++ ) + { + uint yi; + for ( yi = 0; yi < yn; yi++ ) + { + uint xi; + for ( xi = 0; xi < xn; xi++ ) + { + kcl_cube_t cube; + cube.min[0] = xi * cube_size; + cube.max[0] = cube.min[0] + cube_size; + cube.min[1] = yi * cube_size; + cube.max[1] = cube.min[1] + cube_size; + cube.min[2] = zi * cube_size; + cube.max[2] = cube.min[2] + cube_size; + + // 2 steps, because oi.olist may be realloced + const u32 result = CalcCube(&oi,&cube,0); + oi.olist[ores++] = result; + } + } + } + + PRINT("*** O-USED = %u, T-USED = %u, n_CUBES = %u\n",oi.oused,oi.tused,n_cubes); + //HEXDUMP16(0,0,oi.olist,oi.oused*sizeof(*oi.olist)); putchar('\n'); + //HEXDUMP16(0,0,oi.tlist,oi.tused*sizeof(*oi.tlist)); putchar('\n'); + + + //--- concatenate both data areas + + if (!oi.tused) + { + // be sure that at least one 0 element is in the list + oi.tused++; + *oi.tlist = 0; + } + + u32 toffset = oi.oused * sizeof(*oi.olist); + u32 tsize = oi.tused * sizeof(*oi.tlist); + kcl->octree_size = toffset + tsize; + kcl->octree = REALLOC(oi.olist,kcl->octree_size); + kcl->octree_alloced = true; + oi.olist = (u32*)kcl->octree; + memcpy( kcl->octree + toffset, oi.tlist, tsize ); + + + //--- we have an absolute offset, but need big endian relative -> fix it + + u32 null_offset = tsize + toffset - 4 | 0x80000000; + toffset -= 2; // all tri offsets are 2 bytes to short! + + uint idx = 0, end = n_cubes; + while ( idx < oi.oused ) + { + u32 baseoff = idx * 4; + noPRINT("ADJUST %x..%x, baseoff=%x\n",idx,end,baseoff); + for ( ; idx < end; idx++ ) + { + u32 val = oi.olist[idx]; + if (!val) + val = null_offset - baseoff; + else if ( val & 0x80000000 ) + val += toffset - baseoff; + else + val -= baseoff; + write_be32( oi.olist + idx, val ); + } + end = idx + 8; + } + + //HEXDUMP16(0,0,kcl->octree,kcl->octree_size); putchar('\n'); + + + //--- clean up and terminate + + //FREE(oi.olist); don't free this. it's already realloced! + FREE(oi.tlist); + #if SUPPORT_KCL_CUBE + FREE(oi.cubelist); + #endif + kcl->octree_valid = true; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// flag support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanFlagFile +( + kcl_t * kcl, // pointer to valid KCL + ccp fname // filename to scan +) +{ + DASSERT(kcl); + DASSERT(fname); + + static u8 *data = 0; + static uint data_size = 0; + enumError err = OpenReadFILE(fname,0,true,&data,&data_size,0,0); + if (!err) + { + KCL_ACTION_LOG("Scan flag file: %s\n",fname); + FreeString(kcl->flag_fname); + kcl->flag_fname = STRDUP(fname); + + ScanInfo_t si; + InitializeSI(&si,(ccp)data,data_size,kcl->fname,kcl->revision); + si.predef = SetupVarsKCL(); + + while (NextCharSI(&si,true)) + { + //-- scan the group name + + ScanFile_t *sf = si.cur_file; + DASSERT(sf); + char *ptr = (char*)sf->ptr; + char *name = ptr; + bool have_wildcard = false; + while ( *ptr > ' ' && *ptr != '=' && *ptr != '#' ) + { + if ( !have_wildcard && strchr(PATTERN_WILDCARDS,*ptr) ) + have_wildcard = true; + *ptr = toupper((int)*ptr); + ptr++; + } + const uint namelen = ptr - name; + sf->ptr = ptr; + + if (!namelen) + { + CheckEolSI(&si); + continue; + } + + DEFINE_VAR(flag); + if ( CheckWarnSI(&si,'=',0) || ScanExprSI(&si,&flag) ) + { + GotoEolSI(&si); + continue; + } + CheckEolSI(&si); + int num = GetIntV(&flag); + if ( num < 0 ) + num = -1; // -1 is the only allowed negative value + + + //--- here we have all data! + + name[namelen] = 0; + noPRINT("FLAG-FOUND[%d]: |%s| = 0x%04x\n",have_wildcard,name,num); + + FormatFieldItem_t *it + = InsertFormatField(&kcl->flag_name,name,false,false,0); + DASSERT(it); + it->num = num; + + if (have_wildcard) + { + it = AppendFormatField(&kcl->flag_pattern,name,false,false); + DASSERT(it); + it->num = num; + } + } + + const Var_t *var = FindVarSI(&si,"HEX4",false); + if ( var && var->mode != VAR_UNSET ) + kcl->accept_hex4 = GetBoolV(var); + + var = FindVarSI(&si,"HEX23",false); + if ( var && var->mode != VAR_UNSET ) + kcl->accept_hex23 = GetBoolV(var); + + ResetSI(&si); + } + + FREE(data); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +void LoadFlagFileKCL +( + kcl_t * kcl // pointer to valid KCL +) +{ + DASSERT(kcl); + + if ( !kcl->flag_db_loaded && kcl->fname ) + { + kcl->flag_db_loaded = true; + if ( !opt_flag_file || *opt_flag_file ) + { + enumError err = ERR_WARNING; + if (opt_flag_file) + { + SubstDest(iobuf,sizeof(iobuf),kcl->fname,opt_flag_file, + 0,KCL_FLAG_EXT,false); + err = ScanFlagFile(kcl,iobuf); + } + + if (err) + { + SubstDest(iobuf,sizeof(iobuf),kcl->fname,KCL_FLAG_FILE1, + 0,KCL_FLAG_EXT,false); + err = ScanFlagFile(kcl,iobuf); + } + + if (err) + { + SubstDest(iobuf,sizeof(iobuf),kcl->fname,KCL_FLAG_FILE2, + 0,KCL_FLAG_EXT,false); + err = ScanFlagFile(kcl,iobuf); + } + + if (err) + { + FreeString(kcl->flag_fname); + kcl->flag_fname = 0; + KCL_ACTION_LOG("No flag file found for: %s\n",kcl->fname); + } + + PRINT("KCL.FLAGS loaded, N=%u+%u, hex4=%d, hex23=%d\n", + kcl->flag_name.used, kcl->flag_pattern.used, + kcl->accept_hex4, kcl->accept_hex23 ); + } + else + KCL_ACTION_LOG("No flag file searched for: %s\n",kcl->fname); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetFlagByNameKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp name, // upper case name to search + int new_value // return value if not found + // >0: used as new value + + // The name is searched in 4 steps: + // 1. Search literal in 'flag_name' (ignore case) + // 2. Search pattern in 'flag_pattern' (ignore case) + // 3. Search pattern in 'flag_missing' (ignore case) + // 4. Analyze the last 5 characters for '_ffff' + // 5. Analyze the last 7 characters for '_tt_vvv' + // 6. Use 'new_value' +) +{ + DASSERT(kcl); + DASSERT(name); + + if (!kcl->flag_db_loaded) + LoadFlagFileKCL(kcl); + + + //--- 1. Search literal in 'flag_name' (ignore case) + + if ( kcl->flag_name.used ) + { + FormatFieldItem_t *ptr = FindFormatField(&kcl->flag_name,name); + if (ptr) + { + noPRINT("GetFlagByNameKCL(%s) FOUND NAME -> %04x\n",name,ptr->num); + return (int)ptr->num; + } + } + + + //--- 2. Search pattern in 'flag_pattern' (ignore case) + + if ( kcl->flag_pattern.used ) + { + FormatFieldItem_t *ptr = MatchFormatField(&kcl->flag_pattern,name); + if (ptr) + { + noPRINT("GetFlagByNameKCL(%s) FOUND PATTERN-> %04x\n",name,ptr->num); + return (int)ptr->num; + } + } + + + //--- 3. Search literal in 'flag_missing' (ignore case) + + if ( kcl->flag_missing.used ) + { + FormatFieldItem_t *ptr = FindFormatField(&kcl->flag_missing,name); + if (ptr) + { + noPRINT("GetFlagByNameKCL(%s) FOUND MISSING -> %04x %s\n",name,ptr->num,ptr->key); + return (int)ptr->num; + } + } + + + //--- flag is missed in flag file + + FormatFieldItem_t *it = InsertFormatField(&kcl->flag_missing,name,false,false,0); + DASSERT(it); + it->num = new_value > 0 ? new_value : 0x10000; + PRINT("GetFlagByNameKCL(%s) INSERT INTO MISSING := %04x\n",name,it->num); + + + //--- 4. Analyze the last 5-6 characters for '_ffff' or '_Fffff + + const uint len = strlen(name); + if ( kcl->accept_hex4 && len >= 5 && name[len-5] == '_' + || len >= 6 && name[len-6] == '_' && name[len-5] == 'F' ) + { + char *end; + const uint num = strtoul(name+len-4,&end,16); + if (!*end) + { + noPRINT("GetFlagByNameKCL(%s) FOUND '_FFFF' -> %04x\n",name,num); + it->num = num; + return num; + } + } + + + //--- 5. Analyze the last 7 characters for '_tt_vvv' + + if ( kcl->accept_hex23 + && len >= 7 && name[len-7] == '_' && name[len-4] == '_' ) + { + char *end1, *end2; + const uint num1 = strtoul(name+len-6,&end1,16); + const uint num2 = strtoul(name+len-3,&end2,16); + if ( end1 == name+len-4 && !*end2 && num1 < 0x20 && num2 < 0x800 ) + { + noPRINT("GetFlagByNameKCL(%s) FOUND '_TT_VVV' -> %02x,%03x=%04x\n", + name, num1, num2, num1 | num2 << 5 ); + it->num = num1 | num2 << 5; + return it->num; + } + } + + + //--- 6. Use 'new_value' + + return new_value; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint * AllocFlagCount() +{ + return CALLOC(NNN_KCL_FLAG,sizeof(uint)); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CountFlagsKCL +( + // returns the total number of flag groups + + const kcl_t * kcl, // pointer to valid KCL + uint * count // pointer to field with NNN_KCL_FLAG elements +) +{ + DASSERT(kcl); + DASSERT(count); + + uint total = 0; + memset(count,0,N_KCL_FLAG*sizeof(*count)); + + const uint n = kcl->tridata.used; + const kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + const kcl_tridata_t *end = td + n; + for ( ; td < end; td++ ) + { + uint idx = td->cur_flag < NNN_KCL_FLAG ? td->cur_flag : KCL_FLAG_UNKNOWN; + if (!count[idx]++) + total++; + } + return total; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetGroupNameKCL ( uint kcl_flag, bool add_info ) +{ + if ( kcl_flag < N_KCL_FLAG ) + { + const uint bufsize = add_info ? 60 : 40; + char *buf = GetCircBuf(bufsize); + + const uint type = kcl_flag & 0x1f; + const kcl_class_t *cls = kcl_class + kcl_type[type].cls; + if (add_info) + snprintf(buf,bufsize,"%s_%02x_F%04x (%s)", + cls->name, type, kcl_flag, kcl_type[type].info ); + else + snprintf(buf,bufsize,"%s_%02x_F%04x", + cls->name, type, kcl_flag ); + return buf; + } + + if ( kcl_flag >= KCL_FLAG_AUTO_0 && kcl_flag <= KCL_FLAG_AUTO_MAX ) + { + const uint bufsize = 9; + char *buf = GetCircBuf(bufsize); + snprintf( buf, bufsize, "auto_%03u",kcl_flag-KCL_FLAG_AUTO_0); + return buf; + } + + if ( kcl_flag >= KCL_FLAG_USER_0 && kcl_flag <= KCL_FLAG_USER_MAX ) + { + Color_t col = user_color[kcl_flag-KCL_FLAG_USER_0]; + const uint bufsize = 14; + char *buf = GetCircBuf(bufsize); + snprintf( buf, bufsize, "user_%02x%02x%02x%02x", + col.a, col.r, col.g, col.b ); + return buf; + } + + return GetFlagGroupName(kcl_flag); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint GetExportFlagKCL ( uint kcl_flag ) +{ + if ( kcl_flag < NN_KCL_FLAG ) + return kcl_flag; + + if ( kcl_flag >= KCL_FLAG_USER_0 && kcl_flag <= KCL_FLAG_USER_MAX ) + return kcl_flag - KCL_FLAG_USER_0 + 0x1000000; + + if ( kcl_flag >= KCL_FLAG_AUTO_0 && kcl_flag <= KCL_FLAG_AUTO_MAX ) + return kcl_flag - KCL_FLAG_AUTO_0 + 0x2000000; + + return kcl_flag | 0x8000000; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTextKCL +( + kcl_t * kcl, // KCL data structure + bool init_kcl, // true: initialize 'kcl' first + const void * data, // data to scan + uint data_size, // size of 'data' + file_format_t fform // FF_UNKNOWN or file format +) +{ + TRACE("ScanTextKCL()\n"); + + DASSERT(kcl); + ClearKCL(kcl,init_kcl); +// [[analyse-magic]] + kcl->fform = fform != FF_UNKNOWN ? fform : GetByMagicFF(data,data_size,data_size); + + bool use_g = 0 != ( KCL_MODE & KCLMD_G ); + bool use_usemtl = 0 != ( KCL_MODE & KCLMD_USEMTL ); + if ( !use_g && !use_usemtl ) + { + if ( kcl->fform == FF_SKP_OBJ ) + use_usemtl = true; + else + use_g = true; + } + DASSERT( use_g || use_usemtl ); + KCL_ACTION_LOG("ScanTextKCL() %s [%s,%s]\n", + kcl->fname,GetNameFF(kcl->fform,0), + use_g && use_usemtl ? "'g'+'usemtl'" : use_g ? "'g'" : "'usemtl'" ); + + + //--- setup data + + double3List_t vertex, normal; + InitializeD3L(&vertex); + InitializeD3L(&normal); + + + //--- scan text file + + ScanInfo_t si; + InitializeSI(&si,data,data_size,kcl->fname,kcl->revision); + si.predef = SetupVarsKCL(); + enumError max_err = ERR_OK; + + int auto_flag = KCL_FLAG_AUTO_0; + char buf[200]; // for commands and group names + + struct tab_t + { + uint idx; + double3 pt; + } tab[KCL_MAX_PT_PER_TRI]; + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,0); + par.is_orig = true; + uint bad_count = 0; + + static const uint norm_assign_23[] = { 0, 1, 2 }; + static const uint norm_assign_32[] = { 0, 2, 1 }; + const uint *norm_assign = KCL_MODE & KCLMD_IN_SWAP + ? norm_assign_32 : norm_assign_23; + + while (NextCharSI(&si,true)) + { + if (!ScanNameSI(&si,buf,sizeof(buf),true,true,0)) + { + ccp eol = FindNextLineFeedSI(&si,true); + ScanFile_t *sf = si.cur_file; + DASSERT(sf); + ERROR0(ERR_WARNING, + "Missing name [%s @%u]: %.*s\n", + sf->name, sf->line, + (int)(eol - sf->prev_ptr), sf->prev_ptr ); + if ( max_err < ERR_WARNING ) + max_err = ERR_WARNING; + GotoEolSI(&si); + continue; + } + + enumError err = ERR_OK; + CommandKCL_t cmd = GetCommandKCL(buf); + switch (cmd) + { + case KCMD_V: + noPRINT("V\n"); + { + double3 temp; + err = ScanDoubleV3SI(&si,temp.v,1); + if (!err) + { + double3 *dest = AppendD3L(&vertex); + DASSERT(dest); + memcpy(dest,&temp,sizeof(*dest)); + } + } + break; + + case KCMD_VN: + noPRINT("Vn\n"); + { + double3 temp; + err = ScanDoubleV3SI(&si,temp.v,1); + if (!err) + { + double3 *dest = AppendD3L(&normal); + DASSERT(dest); + memcpy(dest,&temp,sizeof(*dest)); + } + } + break; + + case KCMD_F: + { + noPRINT("F\n"); + ScanFile_t *sf = si.cur_file; + DASSERT(sf); + + //--- scan points + + uint k, norm = ~0; + for ( k = 0; k < KCL_MAX_PT_PER_TRI; k++ ) + { + if ( k >= 3 && !NextCharSI(&si,false) ) + break; + + u32 idx; + err = ScanU32SI(&si,&idx,1,0); + if (err) + break; + // transform to 0-based + tab[ k < 3 ? norm_assign[k] : k ].idx = idx - 1; + + if ( *sf->ptr == '/' ) + { + // texture index found => ignore + sf->ptr++; + while ( *sf->ptr >= '0' && *sf->ptr <= '9' ) + sf->ptr++; + + if ( *sf->ptr == '/' ) + { + // normal index found + sf->ptr++; + if ( *sf->ptr >= '0' && *sf->ptr <= '9' + && !ScanU32SI(&si,&idx,1,0) + && !k + && (KCL_MODE & KCLMD_AUTO) ) + { + noPRINT("NORMAL FOUND: %u\n",idx-1); + norm = idx - 1; + } + } + } + } + + //--- add point + + if ( !err && par.kcl_flag >= 0 ) + { + bool valid = true; + uint i; + for ( i = 0; i < k; i++ ) + if ( tab[i].idx > vertex.used ) + { + PRINT("INVALID VERTEX INDEX\n"); + bad_count++; + valid = false; + break; + } + else + tab[i].pt = vertex.list[tab[i].idx]; + + if (valid) + { + par.norm = norm < normal.used ? normal.list + norm : 0; + AppendTrianglesKCL( &par, &tab[0].pt, sizeof(*tab), k ); + } + } + } + break; + + case KCMD_G: + PRINT("G %.10s\n",si.cur_file->ptr); + if (use_g) + { + scan_group: + ScanNameSI(&si,buf,sizeof(buf),true,true,0); + par.kcl_flag = GetFlagByNameKCL(kcl,buf,auto_flag); + if ( par.kcl_flag == auto_flag ) + { + if ( ++auto_flag > KCL_FLAG_AUTO_MAX ) + auto_flag = KCL_FLAG_AUTO_0; + PRINT("KCL-FLAG set to %04x: %s\n",par.kcl_flag,buf); + } + } + GotoEolSI(&si); + break; + + case KCMD_USEMTL: + noPRINT("USEMTL\n"); + if (use_usemtl) + goto scan_group; + GotoEolSI(&si); + break; + + case KCMD_TRI: + { + noPRINT("TRI\n"); + ScanFile_t *sf = si.cur_file; + DASSERT(sf); + + //--- scan flag + + u32 flag; + err = ScanU32SI(&si,&flag,1,0); + + + //--- scan points + + uint k; + for ( k = 0; k < KCL_MAX_PT_PER_TRI && !err; k++ ) + { + noPRINT(">> %u: %.8s\n",k,sf->ptr); + char ch = NextCharSI(&si,false); + if ( ch == '|' ) + { + sf->ptr++; + ch = NextCharSI(&si,false); + } + if (!ch) + break; + err = ScanDoubleSI(&si, tab[ k < 3 ? norm_assign[k] : k ].pt.v, 3 ); + } + + + //--- add point + + if ( !err && k >= 3 ) + { + par.norm = 0; + AppendTrianglesKCL( &par, &tab[0].pt, sizeof(*tab), k ); + } + } + break; + + default: + TRACE("IGNORE KCL/OBJ COMMAND %s\n",buf); + GotoEolSI(&si); + break; + } + + if (err) + { + if ( max_err < err ) + max_err = err; + GotoEolSI(&si); + } + else + CheckEolSI(&si); + } + + CheckLevelSI(&si); + if ( max_err < ERR_WARNING && si.total_err ) + max_err = ERR_WARNING; + PRINT("ERR(ScanTextKCL) = %u (errcount=%u)\n", max_err, si.total_err ); + ResetSI(&si); + + + //--- logging + + if ( !(KCL_MODE & KCLMD_SILENT) + && ( verbose > 0 || verbose >=0 && par.swap_count ) ) + { + fprintf(stdlog, + " - %u triangles read," + " points #2 and #3 are swapped for %u triangles.\n", + kcl->tridata.used, par.swap_count ); + } + + if (bad_count) + { + const uint total = bad_count + kcl->tridata.used; + ERROR0(ERR_WARNING, + "%u of %u (%4.2f%%) triangles ignored because of wrong indices: %s\n", + bad_count, total, + 100.0 * bad_count / total, + kcl->fname ); + if ( max_err < ERR_WARNING ) + max_err = ERR_WARNING; + } + + + //--- terminate + + ResetD3L(&vertex); + ResetD3L(&normal); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanRawKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawKCL +( + kcl_t * kcl, // KCL data structure + bool init_kcl, // true: initialize 'kcl' first + const void * data, // data to scan + uint data_size, // size of 'data' + bool use_data // true: data is valid on 'kcl' live time +) +{ + DASSERT(kcl); + ClearKCL(kcl,init_kcl); + + kcl_analyze_t ka; + if ( IsValidKCL(&ka,data,data_size,data_size,kcl->fname) >= VALID_ERROR ) + { + return ERROR0(ERR_INVALID_DATA, + "Invalid KCL file: %s\n", kcl->fname ? kcl->fname : "?"); + } + + KCL_ACTION_LOG("ScanRawKCL() %s\n",kcl->fname); + kcl->fform = FF_KCL; + + if (!ka.order_ok) + ERROR0(ERR_WARNING, + "KCL: Unusual section order: %d\n",ka.order_value); + + + //--- save raw data for optimized handling + + const bool store_raw_data + = ( KCL_MODE & (KCLMD_NEW|KCLMD_DROP_UNUSED) ) != KCLMD_NEW; + PRINT("STORE_RAW_DATA=%d\n",store_raw_data); + if (store_raw_data) + { + if (use_data) + { + kcl->raw_data = (u8*)data; + kcl->raw_data_alloced = false; + } + else + { + kcl->raw_data = MEMDUP(data,data_size); + kcl->raw_data_alloced = true; + } + kcl->raw_data_size = data_size; + } + + + //--- transfer header + + const kcl_head_t *kclhead = data; + kcl->min_octree.x = bef4(kclhead->min_octree+0); + kcl->min_octree.y = bef4(kclhead->min_octree+1); + kcl->min_octree.z = bef4(kclhead->min_octree+2); + kcl->mask[0] = be32(kclhead->mask+0); + kcl->mask[1] = be32(kclhead->mask+1); + kcl->mask[2] = be32(kclhead->mask+2); + kcl->coord_rshift = be32(&kclhead->coord_rshift); + kcl->y_lshift = be32(&kclhead->y_lshift); + kcl->z_lshift = be32(&kclhead->z_lshift); + kcl->unknown_0x10 = bef4(&kclhead->unknown_0x10); + kcl->unknown_0x38 = bef4(&kclhead->unknown_0x38); + + + //--- store triangles + + const uint n_vert = ka.n[0]; + const uint n_norm = ka.n[1]; + const uint n_tri = ka.n[2]; + PRINT("N(vert)=%u, N(norm)=%u, N(tri)=%u\n",n_vert,n_norm,n_tri); + + const float3 *vert = (float3*)( data + ka.off[0] ); + const float3 *norm = (float3*)( data + ka.off[1] ); + const kcl_triangle_t *tri = (kcl_triangle_t*)( data + ka.off[2] ); + + kcl_tridata_t *td = GrowListSize(&kcl->tridata,n_tri,100); + DASSERT( kcl->tridata.used == n_tri ); + memset(td,0,sizeof(*td)*n_tri); + + double3 min, max; // min and max values of all incomming vertex points + min.x = min.y = min.z = -1000.0; + max.x = min.y = max.z = 1000.0; + + uint ti; + for ( ti = 0; ti < n_tri; ti++, td++, tri++ ) + { + u16 in[6]; + be16n(in,&tri->idx_vertex,6); + + if ( in[0] < n_vert ) + { + const float *flt = vert[in[0]].v; + td->pt[0].x = bef4(flt++); + td->pt[0].y = bef4(flt++); + td->pt[0].z = bef4(flt); + MinMax3(&min,&max,td->pt,1); + // [[xtridata]] + } + + uint p; + for ( p = 0; p < 4; p++ ) + if ( in[p+1] < n_norm ) + bef4n(td->normal[p].v,norm[in[p+1]].v,3); + + td->length = bef4(&tri->length); + + td->in_flag = in[5]; + td->cur_flag = patch_kcl_flag ? patch_kcl_flag[in[5]] : in[5]; + PRINT_IF ( td->in_flag!=td->cur_flag, + "IN/FLAGS: %04x -> %04x\n",td->in_flag,td->cur_flag); + } + + + //--- calc 'tri_minval' and 'tri_maxval' + + const bool clip = ( KCL_MODE & KCLMD_CLIP ) != 0; + if (clip) + { + double temp; + temp = ( max.x - min.x ) * KCL_CLIP.x; + kcl->tri_minval.x = min.x - temp; + kcl->tri_maxval.x = max.x + temp; + + temp = ( max.y - min.y ) * KCL_CLIP.y; + kcl->tri_minval.y = min.y - temp; + kcl->tri_maxval.y = max.y + temp; + + temp = ( max.z - min.z ) * KCL_CLIP.z; + kcl->tri_minval.z = min.z - temp; + kcl->tri_maxval.z = max.z + temp; + + TRACE("VERTEX-MIN: %12.6g %12.6g %12.6g\n", + kcl->tri_minval.x, kcl->tri_minval.y, kcl->tri_minval.z ); + TRACE("VERTEX-MAX: %12.6g %12.6g %12.6g\n", + kcl->tri_maxval.x, kcl->tri_maxval.y, kcl->tri_maxval.z ); + } + + + //--- calc triangle points + + CalcPointsTriData( (kcl_tridata_t*)kcl->tridata.list, n_tri, kcl, clip ); + kcl->norm_valid = true; + + + //--- test signature + + { + ccp ptr = (ccp)&vert->x; + ccp end = ptr + 8; + for(;;) + { + char ch = *ptr++; + noPRINT("|%02x|\n",ch); + if ( ch <= 0x20 || ch >= 0x7f ) + break; + + if ( ptr == end ) + { + float sig = bef4(&vert->z); + if ( sig >= 0.01 && sig <= 1000.0 ) + { + kcl->signature = sig; + memcpy(kcl->signature_info,&vert->x,8); + } + break; + } + } + } + + + //--- store octree + + if ( store_raw_data && ka.off[3] < data_size ) + { + kcl->octree_size = data_size - ka.off[3]; + if (kcl->raw_data) + { + kcl->octree = kcl->raw_data + ka.off[3]; + kcl->octree_alloced = false; + } + else + { + kcl->octree = MEMDUP( data+ka.off[3], kcl->octree_size ); + kcl->octree_alloced = true; + } + kcl->octree_valid = true; + } + + + //--- terminate + + //PRINT("KCL-N: %u %u %u\n",n_vert,n_norm,kcl->triangle.used); + kcl->min_max_valid = false; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KCL: scan and load /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanKCL +( + kcl_t * kcl, // KCL data structure + bool init_kcl, // true: initialize 'kcl' first + const void * data, // data to scan + uint data_size, // size of 'data' + bool use_data, // true: data is valid on 'kcl' live time + CheckMode_t mode // not NULL: call CheckKCL(mode) +) +{ + DASSERT(kcl); + PRINT("ScanKCL(mode=%x) size=%u\n",mode,data_size); + //HEXDUMP16(0,0,data,16); + + enumError err; +// [[analyse-magic]] +// const file_format_t fform = GetByMagicFF(data,data_size,data_size); + const file_format_t fform = GetByMagicFF(data,data_size,data_size); + switch (fform) + { + case FF_KCL: + err = ScanRawKCL(kcl,init_kcl,data,data_size,use_data); + break; + + case FF_KCL_TXT: + case FF_WAV_OBJ: + case FF_SKP_OBJ: + err = ScanTextKCL(kcl,init_kcl,data,data_size,fform); + break; + + default: + ClearKCL(kcl,init_kcl); + return ERROR0(ERR_INVALID_DATA, + "No KCL or OBJ file: %s\n", kcl->fname ? kcl->fname : "?"); + } + kcl->fform = fform; + const bool rm_octree = kcl->octree_valid && KCL_MODE & KCLMD_NEW; + PRINT("NEW->RM(OCTREE): %u && %u => %u\n", + kcl->octree_valid, (KCL_MODE & KCLMD_NEW)!=0, rm_octree ); + + + CheckMetricsTriData( (kcl_tridata_t*)kcl->tridata.list, kcl->tridata.used, kcl ); + + if ( disable_patch_on_load <= 0 ) + PatchKCL(kcl); + + if ( err <= ERR_WARNING && mode ) + CheckKCL(kcl,mode); + + if (rm_octree) + { + KCL_ACTION_LOG("Remove Octree\n"); + + if (kcl->octree_alloced) + FREE(kcl->octree); + kcl->octree = 0; + kcl->octree_size = 0; + kcl->octree_alloced = false; + kcl->octree_valid = false; + } + kcl->recreate_octree = ( KCL_MODE & KCLMD_NEW ) != 0; + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawDataKCL +( + kcl_t * kcl, // KCL data structure + bool init_kcl, // true: initialize 'kcl' first + struct raw_data_t * raw, // valid raw data + bool move_data, // true: move 'raw.data' to 'kcl' + CheckMode_t mode // not NULL: call CheckKCL(mode) +) +{ + DASSERT(kcl); + DASSERT(raw); + if (init_kcl) + InitializeKCL(kcl); + else + ResetKCL(kcl); + + kcl->fname = raw->fname; + raw->fname = 0; + + if ( raw->is_0 ) + { + if ( disable_patch_on_load <= 0 ) + PatchKCL(kcl); + return ERR_OK; + } + + enumError err = ScanKCL(kcl,false,raw->data,raw->data_size,mode,move_data); + + if ( kcl->raw_data == raw->data ) + { + // move the data + kcl->raw_data_alloced = raw->data_alloced; + raw->data_alloced = false; + } + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadKCL +( + kcl_t * kcl, // KCL data structure + bool init_kcl, // true: initialize 'kcl' first + ccp fname, // valid pointer to filenname + bool ignore_no_file, // ignore if file does not exists + // and return warning ERR_NOT_EXISTS + CheckMode_t mode // not NULL: call CheckKCL(mode) +) +{ + DASSERT(kcl); + DASSERT(fname); + if (init_kcl) + InitializeKCL(kcl); + else + ResetKCL(kcl); + + //--- load and scan data + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,fname,0,ignore_no_file,0); + if (!err) + { + kcl->fname = raw.fname; + raw.fname = 0; + err = ScanKCL(kcl,false,raw.data,raw.data_size,mode,true); + if ( kcl->raw_data == raw.data ) + { + // move the data + kcl->raw_data_alloced = raw.data_alloced; + raw.data_alloced = false; + } + } + + ResetRawData(&raw); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Reference KCL /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp kcl_ref_fname = 0; +ccp kcl_auto_ref_fname = 0; + +int ScanOptLoadKcl ( ccp arg ) +{ + kcl_ref_fname = arg; + SetupReferenceKCL(0); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void UnloadAutoReferenceKCL() +{ + if ( !kcl_ref_fname && kcl_ref ) + { + // unload only auto loaded kcl + PRINT("UNLOAD AUTO KCL\n"); + ResetKCL(kcl_ref); + FREE(kcl_ref); + kcl_ref = 0; + } + + FreeString(kcl_auto_ref_fname); + kcl_auto_ref_fname = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void DefineAutoLoadReferenceKCL ( ccp fname, ccp ext ) +{ + DASSERT(fname); + if ( kcl_ref_fname || !fname ) + return; + + UnloadAutoReferenceKCL(); + + char buf[PATH_MAX]; + char *dest = StringCopyS(buf,sizeof(buf),fname); + bool done = false; + if (ext) + { + uint elen = strlen(ext); + if ( elen < dest - buf && !strcasecmp(dest-elen,ext) ) + { + dest -= elen; + done = true; + } + } + + if (!done) + { + char *pt = strrchr(buf,'.'); + if (pt) + dest = pt; + } + + StringCopyE(dest,buf+sizeof(buf),".kcl"); + kcl_auto_ref_fname = STRDUP(buf); + PRINT("NEW AUTO KCL: %s\n",kcl_auto_ref_fname); +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_t * LoadReferenceKCL() +{ + bool silent; + ccp path; + if (kcl_ref_fname) + { + path = kcl_ref_fname; + silent = false; + } + else + { + path = kcl_auto_ref_fname; + silent = true; + } + + if ( kcl_ref || !path || !*path ) + return kcl_ref; + + PRINT("GetReferenceKCL() %s\n",path); + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,path,"course.kcl",silent,0); + if (err) + { + ResetRawData(&raw); + FreeString(kcl_auto_ref_fname); + kcl_ref_fname = kcl_auto_ref_fname = 0; + return 0; + } + + kcl_t kcl; + InitializeKCL(&kcl); + //kcl.fform_outfile = FF_KCL; + err = ScanRawDataKCL(&kcl,false,&raw,0,false); + ResetRawData(&raw); + if (err) + { + ResetKCL(&kcl); + FreeString(kcl_auto_ref_fname); + kcl_ref_fname = kcl_auto_ref_fname = 0; + return 0; + } + + PRINT("REF-KCL loaded!\n"); + kcl_ref = MALLOC(sizeof(*kcl_ref)); + memcpy(kcl_ref,&kcl,sizeof(*kcl_ref)); + + return kcl_ref; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_t * CreateReferenceKCL() +{ + kcl_t *kcl = LoadReferenceKCL(); + if (kcl) + { + kcl_ref = 0; + return kcl; + } + + kcl = MALLOC(sizeof(*kcl)); + InitializeKCL(kcl); + return kcl; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int kcl_fall_flag = -1; + +static enumError F_kcl_fall +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + ToVectorV(param); + double y = param->y; + kcl_t *kcl = GetReferenceKCL(); + if ( !kcl && si ) + kcl = si->kcl; + if (kcl) + { + u32 typemask = n_param > 2 && param[2].mode != VAR_UNSET + ? GetIntV(param+2) + : M1(typemask); + double temp = FallKCL(kcl,0,param->d3, + GetDoubleV(param+1),0,typemask,&kcl_fall_flag); + if ( temp >= 0.0 ) + y = temp; + } + + res->x = param->x; + res->y = y; + res->z = param->z; + res->mode = VAR_VECTOR; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_kcl_fall_flag +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + res->i = kcl_fall_flag; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_isKCL +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + uint result = 0; + if ( si && si->kcl ) + { + result = 1; + + if (si->kcl->fname) + { + ccp fname = strrchr(si->kcl->fname,'/'); + if (fname) + fname++; + else + fname = si->kcl->fname; + + if ( !strcasecmp(fname,"course.kcl") + || !strcasecmp(fname,"course.txt") + || !strcasecmp(fname,"course.txt.kcl") ) + { + result = 2; + } + } + } + + res->i = result; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static const struct FuncTable_t kcl_ref_func_tab[] = +{ + { 2, 3, "KCL$FALL", F_kcl_fall, 0, + "vector", "kcl$fall(pt,width[,typemask])", + " If a reference KCL is defined, search the lowest point" + " below 'pt', that can be reached without collision." + " The point is expanded to a cube with edge length 'width'" + " for the collision tests." + " If 'typemask' is set, only KCL types with related bit number set" + " are recognized." + " Use kcl$fallFlag() to get the corespondent KCL flag." }, + + { 0, 0, "KCL$FALLFLAG", F_kcl_fall_flag, 0, + "int", "kcl$fallFlag()", + " This functions returns the corespondent KCL flag of the last" + " call of kcl$fall()." + " The return value is the KCL flag of the colliding triangle," + " or -1 if no collision was found." }, + + { 0, 0, "ISKCL", F_isKCL, 0, // replace only function call + "int", "isKCL()", 0 }, // but not info + + {0,0,0,0,0,0,0,0} +}; + +/////////////////////////////////////////////////////////////////////////////// + +void SetupReferenceKCL +( + VarMap_t *vm // not NULL: define KCL variables here +) +{ + static bool done = false; + if (!done) + { + done = true; + DefineParserFuncTab(kcl_ref_func_tab,0); + } + + if (vm) + { + char name[50]; + + const kcl_attrib_name_t *an; + for ( an = kcl_attrib_name; an->name; an++ ) + { + u32 typemask = 0; + const kcl_type_t *kt; + for ( kt = kcl_type; kt->info; kt++ ) + if ( kt->attrib & an->attrib ) + typemask |= 1ul << kt->type; + + StringCat2S(name,sizeof(name),"KCL$",an->name); + DefineIntVar(vm,name,typemask); + } + + uint i; + for ( i = N_KCL_FLAG; i < NN_KCL_FLAG; i++ ) + { + StringCat2S(name,sizeof(name),"FLAG$",GetFlagName(i)); + DefineIntVar(vm,name,i); + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CreateRawKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError FastCreateRawKCL +( + kcl_t * kcl, // pointer to valid KCL + bool add_sig // true: add signature +) +{ + DASSERT(kcl); + DASSERT(!kcl->model_modified); + DASSERT(kcl->raw_data); + DASSERT(kcl->raw_data_size); + + + //--- setup section offsets + + const kcl_head_t * kclhead = (kcl_head_t*)kcl->raw_data; + u32 sect_off[N_KCL_SECT]; + DASSERT( sizeof(sect_off) == sizeof(kclhead->sect_off) ); + + uint i; + for ( i = 0; i < N_KCL_SECT; i++ ) + sect_off[i] = be32(kclhead->sect_off+i); + sect_off[2] += 0x10; + + + //--- compare number triangles + + const uint n_tri = ( sect_off[3] - sect_off[2] ) / sizeof(kcl_triangle_t); + KCL_ACTION_LOG("FastCreateRawKCL() N=%u\n",n_tri); + if ( n_tri != kcl->tridata.used ) + { + PRINT("N-TRI: %u != %u\n",n_tri,kcl->tridata.used ); + kcl->model_modified = true; + return CreateRawKCL(kcl,add_sig); + } + + + //--- store modified flags + + kcl_triangle_t *tri = (kcl_triangle_t*)( kcl->raw_data + sect_off[2] ); + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *end = td + n_tri; + + for ( ; td < end; td++, tri++ ) + write_be16(&tri->flag,td->cur_flag); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AddSignature ( float3List_t *vertex, bool fast ) +{ + // the first normal is always a signature + float3 sig; + memcpy(&sig,KCL_SIGNATURE,8); + sig.x = bef4(&sig.x); + sig.y = bef4(&sig.y); + sig.z = strtod(VERSION_NUM,0); + FindInsertFloatF3L(vertex,&sig,fast); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint FindInsertFloatWithMask ( float3List_t *f3l, float3 *val, u32 mask ) +{ + float3 temp; + temp.u[0] = val->u[0] & mask; + temp.u[1] = val->u[1] & mask; + temp.u[2] = val->u[2] & mask; + return FindInsertFloatF3L(f3l,&temp,false); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateRawKCL +( + kcl_t * kcl, // pointer to valid KCL + bool add_sig // true: add signature +) +{ + DASSERT(kcl); + TRACE("CreateRawKCL()\n"); + + if ( !kcl->model_modified + && kcl->raw_data + && kcl->raw_data_size + && kcl->octree_valid ) + { + return FastCreateRawKCL(kcl,add_sig); + } + + if ( CreateOctreeKCL(kcl) == ERR_OK ) + add_sig = true; + + KCL_ACTION_LOG("CreateRawKCL()\n"); + + //--- calculate vetex and normal lists + + CalcNormalsKCL(kcl,false); + + static const int round_tab[] = { 17, 14, 11, 8, 6, 4, 3, 2, 1, 0 }; + const int *round_ptr = KCL_MODE & KCLMD_ROUND ? round_tab+1 : round_tab; + uint start_normals = 0, round_steps = 0; + + const int tiny_level = ( KCL_MODE & KCLMD_M_TINY ) >> KCLMD_S_TINY; + const tiny_param_t *tp = TinyParam + tiny_level; + const uint n_tri = kcl->tridata.used; + const bool fast = kcl->fast && n_tri < 0x4000 && !tp->kcl_round; + float3List_t vertex, normal; + + // vertex + 4*normal + flag + typedef u16 vertex_index_t; + vertex_index_t *index_list = CALLOC(6*n_tri,sizeof(*index_list)); + + kcl_tridata_t *td_base = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td, *td_end = td_base + n_tri; + + for(;;) + { + InitializeF3L(&vertex,n_tri); + InitializeF3L(&normal,n_tri*4); + + vertex_index_t *idx = index_list; + if (tp->kcl_mask) + { + for ( td = td_base; td < td_end; td++ ) + { + float3 temp; + temp.x = RoundF(td->pt->x,tp->kcl_round); + temp.y = RoundF(td->pt->y,tp->kcl_round); + temp.z = RoundF(td->pt->z,tp->kcl_round); + *idx++ = FindInsertFloatF3L(&vertex,&temp,false); + + *idx++ = FindInsertFloatWithMask(&normal,td->normal+0,tp->kcl_mask); + *idx++ = FindInsertFloatWithMask(&normal,td->normal+1,tp->kcl_mask); + *idx++ = FindInsertFloatWithMask(&normal,td->normal+2,tp->kcl_mask); + *idx++ = FindInsertFloatWithMask(&normal,td->normal+3,tp->kcl_mask); + + *idx++ = td->cur_flag; + } + } + else + { + if ( add_sig && !tiny_level ) + AddSignature(&vertex,fast); + + for ( td = td_base; td < td_end; td++ ) + { + float3 temp; + temp.x = td->pt->x; + temp.y = td->pt->y; + temp.z = td->pt->z; + *idx++ = FindInsertFloatF3L(&vertex,&temp,fast); + + *idx++ = FindInsertFloatF3L(&normal,td->normal+0,fast); + *idx++ = FindInsertFloatF3L(&normal,td->normal+1,fast); + *idx++ = FindInsertFloatF3L(&normal,td->normal+2,fast); + *idx++ = FindInsertFloatF3L(&normal,td->normal+3,fast); + + *idx++ = td->cur_flag; + } + } + + if ( normal.used < 0x10000 ) + { + if (start_normals) + { + KCL_ACTION_LOG("Number of normals reduced from %u to %u [steps=%u]\n", + start_normals, normal.used, round_steps ); + } + break; + } + + if (!start_normals) + start_normals = normal.used; + round_steps++; + + const int round = *round_ptr++; + if (!round) + { + return ERROR0(ERR_INVALID_DATA, + "Unable to limit normals list to <65536 elements (have %u): %s\n", + normal.used, kcl->fname ); + } + + #if defined(TEST) || 0 + KCL_ACTION_LOG("Have %u normals: try to reduce to 65535 (level %u)\n", + normal.used, round ); + #endif + + for ( td = td_base; td < td_end; td++ ) + { + // need cast to avoid gcc warning 'above array bounds' + float *f = (float*)&td->normal->x; + float *f_end = (float*)( (u8*)f + sizeof(td->normal) ); + while ( f < f_end ) + { + *f = ldexpf(truncf(ldexpf(*f,round)),-round); + f++; + } + } + } + + + //--- calculate data size + + const uint vertex_size = sizeof(float3) * vertex.used; + const uint normal_size = sizeof(float3) * normal.used; + const uint triangle_size = sizeof(kcl_triangle_t) * n_tri; + + uint data_size = sizeof(kcl_head_t) + + vertex_size + + normal_size + + triangle_size + + kcl->octree_size; + + + //--- alloc data + + u8 *old_data = 0; + if (kcl->raw_data_alloced) + old_data = kcl->raw_data; // octree may use this data + + kcl_head_t * kclhead = CALLOC(1,data_size); + kcl->raw_data = (u8*)kclhead; + kcl->raw_data_size = data_size; + kcl->raw_data_alloced = true; + + PRINT("CREATE KCL: size = %#x = %u\n",data_size,data_size); + + + //--- write header + + const uint vertex_offset = sizeof(kcl_head_t); + const uint normal_offset = vertex_offset + vertex_size; + const uint triangle_offset = normal_offset + normal_size; + const uint octree_offset = triangle_offset + triangle_size; + + write_be32(kclhead->sect_off+0, vertex_offset); + write_be32(kclhead->sect_off+1, normal_offset); + write_be32(kclhead->sect_off+2, triangle_offset - 0x10 ); + write_be32(kclhead->sect_off+3, octree_offset); + + write_bef4(kclhead->min_octree+0, kcl->min_octree.x); + write_bef4(kclhead->min_octree+1, kcl->min_octree.y); + write_bef4(kclhead->min_octree+2, kcl->min_octree.z); + write_be32(kclhead->mask+0, kcl->mask[0]); + write_be32(kclhead->mask+1, kcl->mask[1]); + write_be32(kclhead->mask+2, kcl->mask[2]); + write_be32(&kclhead->coord_rshift, kcl->coord_rshift); + write_be32(&kclhead->y_lshift, kcl->y_lshift); + write_be32(&kclhead->z_lshift, kcl->z_lshift); + write_bef4(&kclhead->unknown_0x10, kcl->unknown_0x10); + write_bef4(&kclhead->unknown_0x38, kcl->unknown_0x38); + + + //--- transfer verticies + + float *src = vertex.list->v; + float *dest = (float*)(kcl->raw_data + vertex_offset); + write_bef4n(dest,src,3*vertex.used); + + + //--- transfer normals + + src = normal.list->v; + dest = (float*)(kcl->raw_data + normal_offset); + write_bef4n(dest,src,3*normal.used); + + + //--- transfer triangles + + const vertex_index_t *idx = index_list; + kcl_triangle_t *tdest = (kcl_triangle_t*)( kcl->raw_data + triangle_offset ); + for ( td = td_base; td < td_end; td++, tdest++, idx+=6 ) + { + write_bef4(&tdest->length,td->length); + write_be16n(&tdest->idx_vertex,idx,6); + } + + + //--- transfer octree + + DASSERT( octree_offset + kcl->octree_size == kcl->raw_data_size ); + memcpy( kcl->raw_data+octree_offset, kcl->octree, kcl->octree_size ); + + + //--- terminate + + ResetF3L(&vertex); + ResetF3L(&normal); + FREE(index_list); + + kcl->model_modified = false; + + if (!kcl->octree_alloced) + { + // is part of 'old_data' + kcl->octree = 0; + kcl->octree_size = 0; + kcl->octree_alloced = false; + } + FREE(old_data); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveRawKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveRawKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + DASSERT(kcl); + DASSERT(fname); + PRINT("SaveRawKCL(%s,%d)\n",fname,set_time); + + //--- create raw data + + enumError err = CreateRawKCL(kcl,true); + if (err) + return err; + DASSERT(kcl->raw_data); + DASSERT(kcl->raw_data_size); + KCL_ACTION_LOG("SaveRawKCL(%s) N=%u, model_modified=%d\n", + fname, kcl->tridata.used, kcl->model_modified ); + + //--- write to file + + File_t F; + err = CreateFileOpt(&F,true,fname,testmode,kcl->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&kcl->fatt,0); + + if ( fwrite(kcl->raw_data,1,kcl->raw_data_size,F.f) != kcl->raw_data_size ) + err = FILEERROR1(&F,ERR_WRITE_FAILED,"Write failed: %s\n",fname); + return ResetFile(&F,set_time); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTextKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTextKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + // use DOS/Windows line format -> unix can handle it ;) + // obj file format: + // http://en.wikipedia.org/wiki/Wavefront_.obj_file + // http://www.martinreddy.net/gfx/3d/OBJ.spec + + DASSERT(kcl); + DASSERT(fname); + + KCL_ACTION_LOG("SaveTextKCL(%s) N=%u, model_modified=%d\n", + fname, kcl->tridata.used, kcl->model_modified ); + + static ccp sep = "#\f\r\n#-----------------------------------------------\r\n"; + static ccp head = "\r\n%s# %u %s\r\n\r\n"; + + if ( KCL_MODE & KCLMD_NORMALS ) + CalcNormalsKCL(kcl,false); + + const uint n_tri = kcl->tridata.used; + uint p, n_vert, n_norm, n_valid_tri = 0; + + uint *flag_count = AllocFlagCount(); + typedef u32 vertex_index_t; + vertex_index_t *index_list = CALLOC(4*n_tri,sizeof(*index_list)); + kcl_tridata_t *td_base = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td, *td_end = td_base + n_tri; + + float3List_t vertex, normal; + const bool print_tri = ( KCL_MODE & KCLMD_TRIANGLES ) != 0; + if ( print_tri || kcl->fast ) + { + InitializeF3L(&vertex,0); + InitializeF3L(&normal,0); + + // setup of 'index_list' is delayed; + + for ( td = td_base; td < td_end; td++ ) + if ( !(td->status & TD_INVALID) ) + n_valid_tri++; + + n_vert = print_tri ? 0 : 3 * n_valid_tri; + n_norm = !print_tri && KCL_MODE & KCLMD_NORMALS ? n_valid_tri : 0; + } + else + { + InitializeF3L(&vertex,n_tri*3); + InitializeF3L(&normal,n_tri); + + vertex_index_t *idx = index_list; + for ( td = td_base; td < td_end; td++ ) + { + if ( !(td->status & TD_INVALID) ) + { + n_valid_tri++; + for ( p = 0; p < 3; p++ ) + { + float3 temp; + temp.x = td->pt[p].x; + temp.y = td->pt[p].y; + temp.z = td->pt[p].z; + *idx++ = FindInsertFloatF3L(&vertex,&temp,false) + 1; + } + *idx++ = ( KCL_MODE & KCLMD_NORMALS ) + ? FindInsertFloatF3L(&normal,td->normal+0,false) + 1 + : 0; + } + } + n_vert = vertex.used; + n_norm = normal.used; + } + + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,kcl->fname); + if ( err > ERR_WARNING || !F.f ) + goto abort; + SetFileAttrib(&F.fatt,&kcl->fatt,0); + + const uint n_grp = CountFlagsKCL(kcl,flag_count); + PRINT("n_vert=%u, n_norm=%u, n_tri=%u/%u, n_grp=%u\n", + n_vert, n_norm, n_valid_tri, n_tri, n_grp ); + + + //--- print header + + fprintf(F.f, text_kcl_head_cr, + tool_name, SYSTEM, VERSION, REVISION_NUM, DATE, GetKclMode(), + n_vert, n_norm, n_valid_tri, n_grp ); + + uint i; + for ( i = 0; i <= N_KCL_TYPE; i++ ) + { + int j, j_max, j_inc; + if ( i < N_KCL_TYPE ) + { + j = i; + j_max = N_KCL_FLAG; + j_inc = N_KCL_TYPE; + } + else + { + j = N_KCL_FLAG; + j_max = KCL_FLAG_USER_0 + n_user_color; + j_inc = 1; + } + for ( ; j < j_max; j += j_inc ) + if (flag_count[j]) + fprintf(F.f,"# %8u triangles in group %s\r\n", + flag_count[j], GetGroupNameKCL(j,true) ); + } + + char mtl_fname[PATH_MAX]; + const bool have_mtl = !print_tri && !F.is_stdio + && KCL_MODE & (KCLMD_MTL|KCLMD_WIIMM); + if (have_mtl) + { + char *dest = StringCopyS(mtl_fname,sizeof(mtl_fname),fname); + if ( KCL_MODE & KCLMD_WIIMM ) + { + dest = strrchr(mtl_fname,'/'); + dest = dest ? dest+1 : mtl_fname; + StringCopyE(dest,mtl_fname+sizeof(mtl_fname),"wiimm" KCL_MTL_EXT); + fprintf(F.f,"\r\n\r\nmtllib %s\r\n",dest); + } + else + { + char *found = strrchr(mtl_fname,'.'); + if ( found && !strcasecmp(found,".obj") ) + dest = found; + StringCopyE(dest,mtl_fname+sizeof(mtl_fname),KCL_MTL_EXT); + + found = strrchr(mtl_fname,'/'); + found = found ? found+1 : mtl_fname; + fprintf(F.f,"\r\n\r\nmtllib %s\r\n",found); + } + } + + + //--- print vertices + + if (n_vert) + { + fprintf(F.f,head,sep,n_vert,"vertices"); + + if (kcl->fast) + { + vertex_index_t *idx = index_list; + uint vidx = 1, nidx = 1; + for ( td = td_base; td < td_end; td++ ) + { + if ( !(td->status & TD_INVALID) ) + { + fprintf(F.f,"\r\n" + "v %11.3f %11.3f %11.3f\r\n" + "v %11.3f %11.3f %11.3f\r\n" + "v %11.3f %11.3f %11.3f\r\n", + td->pt[0].x, td->pt[0].y, td->pt[0].z, + td->pt[1].x, td->pt[1].y, td->pt[1].z, + td->pt[2].x, td->pt[2].y, td->pt[2].z ); + *idx++ = vidx++; + *idx++ = vidx++; + *idx++ = vidx++; + *idx++ = nidx++; + } + } + } + else + { + float3 *f3 = vertex.list; + float3 *end = f3 + vertex.used; + for ( ; f3 < end; f3++ ) + fprintf(F.f, "v %11.3f %11.3f %11.3f\r\n", f3->x, f3->y, f3->z ); + } + } + + //--- print normals + + if (n_norm) + { + fprintf(F.f,head,sep,n_norm,"normals"); + + if (kcl->fast) + { + for ( td = td_base; td < td_end; td++ ) + if ( !(td->status & TD_INVALID) ) + fprintf(F.f, + "vn %11.3f %11.3f %11.3f\r\n", + td->normal->x, td->normal->y, td->normal->z ); + } + else + { + float3 *f3 = normal.list; + float3 *end = f3 + normal.used; + for ( ; f3 < end; f3++ ) + fprintf(F.f, + "vn %11.3f %11.3f %11.3f\r\n", + f3->x, f3->y, f3->z ); + } + } + + + //--- print groups and faces + + uint idx2, idx3; + if ( KCL_MODE & KCLMD_OUT_SWAP ) + idx2 = 2, idx3 = 1; + else + idx2 = 1, idx3 = 2; + + uint class_idx[N_KCL_CLASS] = {0}; + for ( i = 0; i <= N_KCL_TYPE; i++ ) + { + const kcl_class_t *cls = kcl_class + kcl_type[i].cls; + uint col_idx = class_idx[cls->cls]; + uint col_max = cls->ncol; + + int j, j_max, j_inc; + if ( i < N_KCL_TYPE ) + { + j = i; + j_max = N_KCL_FLAG; + j_inc = N_KCL_TYPE; + } + else + { + j = N_KCL_FLAG; + j_max = KCL_FLAG_USER_0 + n_user_color; + j_inc = 1; + } + + for ( ; j < j_max; j += j_inc ) + { + if (!flag_count[j]) + continue; + + ccp grp_name = GetGroupNameKCL(j,false); + fprintf(F.f, + "\r\n%s" + "# %u triangles in group '%s'\r\n" + "# -> flag 0x%04x => type 0x%02x, variant 0x%03x\r\n" + "\r\n" + "g %s\r\n" + "s off\r\n" + , sep + , flag_count[j], grp_name + , j, i < N_KCL_TYPE ? i : 0x100, i < N_KCL_TYPE ? j >> 5 : j & 0xffff + , grp_name + ); + + if (have_mtl) + { + if ( i < N_KCL_TYPE ) + { + fprintf(F.f,"usemtl %s_%u\r\n\r\n",cls->name,++col_idx); + if ( col_idx >= col_max ) + col_idx = 0; + } + else + fprintf(F.f,"usemtl %s\r\n\r\n",grp_name); + } + else + fputs("\r\n",F.f); + + if (print_tri) + { + for ( td = td_base; td < td_end; td++ ) + if ( td->cur_flag == j && !(td->status & TD_INVALID) ) + { + fprintf(F.f, + "TRI 0x%04x | %11.3f %11.3f %11.3f" + " | %11.3f %11.3f %11.3f" + " | %11.3f %11.3f %11.3f\r\n", + GetExportFlagKCL(td->cur_flag), + td->pt[0].x, td->pt[0].y, td->pt[0].z, + td->pt[1].x, td->pt[1].y, td->pt[1].z, + td->pt[2].x, td->pt[2].y, td->pt[2].z ); + } + #ifdef TEST0 + else + fprintf(F.f,"# flag=%x,%x, j=%x\n",td->in_flag,td->cur_flag,j); + #endif + } + else if (n_norm) + { + for ( td = td_base; td < td_end; td++ ) + if ( td->cur_flag == j && !(td->status & TD_INVALID) ) + { + vertex_index_t *idx = index_list + 4*(td-td_base); + fprintf(F.f, + "f %u//%u %u//%u %u//%u\r\n", + idx[0], idx[3], + idx[idx2], idx[3], + idx[idx3], idx[3] ); + } + } + else + { + for ( td = td_base; td < td_end; td++ ) + if ( td->cur_flag == j && !(td->status & TD_INVALID) ) + { + vertex_index_t *idx = index_list + 4*(td-td_base); + fprintf(F.f, + "f %u %u %u\r\n", + idx[0], idx[idx2], idx[idx3] ); + } + } + } + class_idx[cls->cls] = col_idx ; + } + + + //--- terminate + + fprintf(F.f,"\r\n%s# END\r\n" + "# define an empty last group as workaround for a SketchUp importer bug\r\n" + "g empty_group\r\n" + "\r\n" + ,sep); + ResetFile(&F,set_time); + + //--- create mtl file + + if (have_mtl) + { + enumError err = CreateFileOpt(&F,true,mtl_fname,testmode,0); + if ( err <= ERR_WARNING && F.f ) + { + SetFileAttrib(&F.fatt,&kcl->fatt,0); + #if 1 + u8 *data; + uint size; + if (!DecodeBZIP2(&data,&size,0,text_obj_mtl_bz2,sizeof(text_obj_mtl_bz2))) + fwrite(data,size,1,F.f); + FREE(data); + #else + fputs(kcl_mtl,F.f); + #endif + + uint ci; + for ( ci = 0; ci < n_user_color; ci++ ) + { + fprintf(F.f,"newmtl %s\r\n",GetGroupNameKCL(ci+KCL_FLAG_USER_0,0)); + Color_t col = user_color[ci]; + if ( col.a != 0xff ) + { + const float a = col.a / 255.0; + fprintf(F.f,"\td %5.3f\r\n\tTr %5.3f\r\n",a,1.0-a); + } + + const float r = col.r / 255.0; + const float g = col.g / 255.0; + const float b = col.b / 255.0; + fprintf(F.f, + "\tKa %5.3f %5.3f %5.3f\r\n" + "\tKd %5.3f %5.3f %5.3f\r\n" + "\tKs %5.3f %5.3f %5.3f\r\n" + "\r\n", + r, g, b, + r, g, b, + r, g, b ); + } + } + ResetFile(&F,set_time); + } + + abort: + FREE(flag_count); + FREE(index_list); + ResetF3L(&vertex); + ResetF3L(&normal); + + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PrintTriangles() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool PrintTriangles ( File_t *F, kcl_t *kcl, bool have_used ) +{ + DASSERT(F); + DASSERT(kcl); + + const uint n_tri = kcl->tridata.used; + if (!n_tri) + return false; + + FILE *f = F->f; + fprintf(f,text_kcl_dump_tridata_cr,n_tri); + + kcl_tridata_t *td_base = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td, *td_end = td_base + n_tri; + + for ( td = td_base; td < td_end; td++ ) + { + fprintf(f,"#%u\n",(int)(td-td_base)); + + const float3 *f3 = td->normal; + fprintf(f,"T.N0 %11.6f %11.6f %11.6f | 0x%04x -> 0x%04x\r\n", + f3->x, f3->y, f3->z, + td->in_flag, td->cur_flag ); + + f3++; + fprintf(f,"T.N1 %11.6f %11.6f %11.6f | %s\r\n", + f3->x, f3->y, f3->z, + td->status & TD_FIXED_PT ? "fixed" : "-" ); + + f3++; + fprintf(f,"T.N2 %11.6f %11.6f %11.6f | %svalid", + f3->x, f3->y, f3->z, + td->status & TD_INVALID ? "in" : "" ); + + if (have_used) + fprintf(f,", %sused", td->status & TD_UNUSED ? "un" : "" ); + + f3++; + fprintf(f,"\r\nT.N3 %11.6f %11.6f %11.6f |\r\n", + f3->x, f3->y, f3->z ); + + #if SUPPORT_KCL_DELTA + fprintf(f,"T.P1 %11.3f %11.3f %11.3f | len %g%s, delta %g/%g = %g\r\n", + td->pt[0].x, td->pt[0].y, td->pt[0].z, + td->length, + td->length, < opt_tri_height ? " (too small)" : "", + td->delta_max, td->delta_min, + td->delta_min > 0 ? td->delta_max / td->delta_min : 0.0 ); + #else + fprintf(f,"T.P1 %11.3f %11.3f %11.3f | len %g%s\r\n", + td->pt[0].x, td->pt[0].y, td->pt[0].z, + td->length, + td->length < opt_tri_height ? " (too small)" : "" ); + #endif + + int p; + for ( p = 1; p < 3; p++ ) + fprintf(f,"T.P%u %11.3f %11.3f %11.3f | %10.3f %10.3f %10.3f \r\n", + p+1, td->pt[p].x, td->pt[p].y, td->pt[p].z, + td->pt[p].x - td->pt[0].x, + td->pt[p].y - td->pt[0].y, + td->pt[p].z - td->pt[0].z ); + + tri_metrics_t met; + CalcTriMetricsByPoints(&met,td->pt,td->pt+1,td->pt+2); + fprintf(f,"T.LEN %11.3f %11.3f %11.3f | area %g%s\r\n", + met.l1, met.l2, met.l3, met.area, + met.area < opt_tri_area ? " (too small)" : "" ); + fprintf(f,"T.HT %11.3f %11.3f %11.3f | min %g%s, max %g\r\n", + met.h1, met.h2, met.h3, met.min_ht, + met.min_ht < opt_tri_height ? " (too small)" : "", + met.max_ht ); + + fputs("\r\n",f); + } + + return true; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DumpKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u32 dump_o_table +( + // returns next offset + + FILE *f, // output file + kcl_t *kcl, // valid kcl + u32 offset, // offset to print + u32 empty_offset, // offset of empty list + uint n, // number of elements to print + int count // >=0: print usage counter +) +{ + DASSERT(f); + DASSERT(kcl); + DASSERT(kcl->octree); + DASSERT(kcl->o_depth); + DASSERT(kcl->o_count); + DASSERT(kcl->l_count); + + noPRINT("dump_o_table: off=%x, n=%x\n",offset,n); + + if ( offset >= kcl->octree_size ) + return kcl->octree_size; + + const uint max_n = ( kcl->octree_size - offset ) / 4; + if ( n > max_n ) + n = max_n; + + const u32 baseoff = offset; + + while ( n > 0 ) + { + fprintf(f,"O.%06x:",offset); + + uint i, max = n < 8 ? n : 8; + n -= max; + for ( i = 0; i < max; i++ ) + { + if ( i == 4 ) + fputc(' ',f); + u32 val = be32(kcl->octree+offset); + offset += 4; + if ( val & 0x80000000 ) + { + val = ( val & 0x7ffffff ) + baseoff + 2; + if ( val == empty_offset ) + fprintf(f," -"); + else + fprintf(f," L.%06x",val); + } + else + { + val += baseoff; + fprintf(f," %8x",val & 0x7ffffff); + } + } + if ( count >= 0 ) + { + fprintf(f," # %2u*\r\n",count); + count = -1; + } + else + fputs("\r\n",f); + } + return offset; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DumpKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp fname, // filename of destination + bool print_tridata, // true: triangle list + uint print_octree // octree print level (0..2) +) +{ + // use DOS/Windows line format -> unix can handle it ;) + + DASSERT(kcl); + DASSERT(fname); + + CalcNormalsKCL(kcl,false); + CalcMinMaxKCL(kcl); + CreateOctreeKCL(kcl); + CalcStatisticsKCL(kcl); + CreateRawKCL(kcl,false); + //const uint blow_size = CalcBlowSizeKCL(kcl); + KCL_ACTION_LOG("DumpKCL(%s,%d,%d)\n",fname,print_tridata,print_octree); + + PRINT("DumpKCL(%s,PT=%d,PO=%d) N(td)=%u\n", + fname, print_tridata, print_octree, kcl->tridata.used ); + + + //--- calculate number of verteces and normals + + const uint n_tri = kcl->tridata.used; + uint n_vert, n_norm; + + kcl_tridata_t *td_base = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td, *td_end = td_base + n_tri; + + if (kcl->fast) + { + n_vert = 3 * n_tri; + n_norm = KCL_MODE & KCLMD_NORMALS ? n_tri : 0; + } + else + { + #if HAVE_PRINT + u_usec_t start_time = GetTimerUSec(); + #endif + float3List_t vertex, normal; + InitializeF3L(&vertex,n_tri); + InitializeF3L(&normal,n_tri*4); + //AddSignature(&vertex,false); + + for ( td = td_base; td < td_end; td++ ) + { + float3 temp; + temp.x = td->pt->x; + temp.y = td->pt->y; + temp.z = td->pt->z; + FindInsertFloatF3L(&vertex,&temp,false); + + FindInsertFloatF3L(&normal,td->normal+0,false); + FindInsertFloatF3L(&normal,td->normal+1,false); + FindInsertFloatF3L(&normal,td->normal+2,false); + FindInsertFloatF3L(&normal,td->normal+3,false); + } + n_vert = vertex.used; + n_norm = normal.used; + #if HAVE_PRINT + uint end_time = GetTimerUSec(); + PRINT(">> %u/%u/%u verteces and %u/%u/%u normals inserted in %6.4f sec.\n", + vertex.used, vertex.size, n_tri, + normal.used, normal.size, 4*n_tri, + 1e-6 * (end_time-start_time) ); + #endif + ResetF3L(&vertex); + ResetF3L(&normal); + } + + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,kcl->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&kcl->fatt,0); + + + //--- print header + + if (kcl->signature > 0.0 ) + fprintf(F.f,"\r\nH.signature = %s v%4.2f\r\n", + kcl->signature_info, kcl->signature ); + + fprintf(F.f,text_kcl_dump_head_cr, + n_vert, n_norm, n_tri, + n_tri - kcl->n_tri_used, kcl->tri_fix_count, kcl->tri_inv_count, + kcl->octree_size, kcl->raw_data_size, + + kcl->unknown_0x10, kcl->unknown_0x38, + + kcl->min_octree.x, kcl->min_octree.y, kcl->min_octree.z, + kcl->min.x, kcl->min.y, kcl->min.z, + kcl->mean.x, kcl->mean.y, kcl->mean.z, + kcl->max.x, kcl->max.y, kcl->max.z, + kcl->max_octree.x, kcl->max_octree.y, kcl->max_octree.z, + + kcl->mask[0], kcl->mask[1], kcl->mask[2], + kcl->coord_rshift, kcl->y_lshift, kcl->z_lshift, + kcl->mask_bits[0], kcl->mask_bits[1], kcl->mask_bits[2], + kcl->bcube_bits[0], kcl->bcube_bits[1], kcl->bcube_bits[2], + kcl->n_bcubes[0], kcl->n_bcubes[1], kcl->n_bcubes[2], + + kcl->tmin_dist, + kcl->tmax_dist, + + kcl->bcube_width, + kcl->bcube_width >> kcl->min_depth, + kcl->bcube_width >> kcl->max_depth, + //blow_size, + kcl->total_bcubes, + kcl->n_cube_nodes, + kcl->total_cubes, + kcl->max_depth, + kcl->ave_depth, + + kcl->n_0_list, + kcl->n_tri_list, + kcl->n_0_link, + kcl->n_tri_link + ); + + uint di; + //for ( di = 0; di < KCL_MAX_STAT_DEPTH && di <= kcl->max_depth; di++ ) + for ( di = 0; di < KCL_MAX_STAT_DEPTH; di++ ) + if (kcl->max_depth_tri_len[di]) + fprintf(F.f,"S.max_tri_size.%-2u= %8u" + " # max number of triangles in all lists of depth %u\r\n", + di-1, kcl->max_depth_tri_len[di], di-1 ); + + fprintf(F.f, + "S.max_tri_size = %8u # maximum number of triangles in all lists\r\n" + "S.ave_tri_size = %11.2f # average number of triangles in all lists\r\n\r\n", + kcl->max_tri_len, // max_tri_size + (double)kcl->sum_tri_len/kcl->n_tri_list // ave_tri_size + ); + + + //--- print triangles + + if (print_tridata) + { + fputs("#\f\r\n" + "########################################" + "#######################################\r\n",F.f); + + PrintTriangles(&F,kcl,true); + } + + + //--- print octree + + if ( print_octree && kcl->octree_valid && kcl->octree ) + { + fprintf(F.f,text_kcl_dump_octree_cr, + kcl->total_bcubes, kcl->total_cubes - kcl->total_bcubes, + kcl->n_tri_list ); + + + u32 empty_offset = kcl->octree_size >= 2 + && !be16(kcl->octree+kcl->octree_size-2) + ? kcl->octree_size - 2 : 0; + + u32 offset = dump_o_table(F.f,kcl,0,empty_offset,kcl->total_bcubes,-1); + fputs("\r\n",F.f); + + while ( offset < kcl->max_o_offset && offset < kcl->octree_size ) + offset = dump_o_table(F.f,kcl,offset,empty_offset,8, + kcl->o_count[(offset-kcl->min_o_offset)/0x20]); + + if ( print_octree > 1 ) + { + fprintf(F.f,text_kcl_dump_octlist_cr); + + uint li; + for ( li = 0; li < kcl->n_trilist; li++ ) + { + if ( kcl->l_count[li] ) + { + offset = li * 2 + kcl->min_l_offset; + fprintf(F.f,"L.%06x [%2u*]:",offset,kcl->l_count[li]); + for(;;) + { + const u16 tidx = be16(kcl->octree+offset); + if (!tidx) + break; + fprintf(F.f," %u",tidx-1); + offset += 2; + } + fputs("\r\n",F.f); + } + } + fputs("\r\n",F.f); + } + } + + + //--- terminate + + ResetFile(&F,false); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DumpTrianglesKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError DumpTrianglesKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp fname // filename of destination +) +{ + DASSERT(kcl); + DASSERT(fname); + + KCL_ACTION_LOG("DumpTrianglesKCL(%s)\n",fname); + PRINT("DumpTrianglesKCL(%s)\n",fname); + CalcNormalsKCL(kcl,false); + + if (kcl->octree_valid) + CalcStatisticsKCL(kcl); + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,kcl->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&kcl->fatt,0); + PrintTriangles(&F,kcl,kcl->octree_valid); + ResetFile(&F,false); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ListTrianglesKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ListTrianglesKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp fname, // filename of destination + bool print_normals, // true: print large tables + uint precision // floating point pre: 0=+0, .. 3=+6 +) +{ + // use DOS/Windows line format -> unix can handle it ;) + + DASSERT(kcl); + DASSERT(fname); + + CalcNormalsKCL(kcl,false); + CalcStatisticsKCL(kcl); + + KCL_ACTION_LOG("ListTrianglesKCL(%s,%d,%u)\n",fname,print_normals,precision); + + PRINT("ListTrianglesKCL(%s,PN=%d,PREC=%u) N=%u\n", + fname, print_normals, precision, kcl->tridata.used ); + + if ( precision > 5 ) + precision = 5; + const uint prec_pt = precision; + const uint prec_len = precision + 1; + const uint prec_norm = precision + 2; + const uint fw_pt = 7 + prec_pt + (prec_pt>0); + const uint fw_len = 6 + prec_len; + const uint fw_norm = 3 + prec_norm; + + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,kcl->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&kcl->fatt,0); + + + //--- print data + + const kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + uint i; + for ( i = 0; i < kcl->tridata.used; i++, td++ ) + { + fprintf(F.f, + "%04x %c %*.*f", + td->cur_flag, + !kcl->used_valid ? '?' : td->status & TD_UNUSED ? '-' : '+', + fw_len, prec_len, td->length + ); + + uint ti; + const double3 *d3 = td->pt; + for ( ti = 0; ti < 3; ti++, d3++ ) + fprintf(F.f,"%s %*.*f %*.*f %*.*f", + ti ? "," : " |", + fw_pt, prec_pt, d3->x, + fw_pt, prec_pt, d3->y, + fw_pt, prec_pt, d3->z ); + + if (print_normals) + { + uint ni; + const float3 *f3 = td->normal; + for ( ni = 0; ni < 4; ni++, f3++ ) + { + fprintf(F.f,"%s %*.*f %*.*f %*.*f", + ni ? "," : " |", + fw_norm, prec_norm, f3->x, + fw_norm, prec_norm, f3->y, + fw_norm, prec_norm, f3->z ); + } + } + fputs("\r\n",F.f); + } + + + //--- terminate + + ResetFile(&F,false); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TraverseOctreeKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int TraverseOctree +( + // returns -2: octree invalid + // -1: octree data invalid + // >=0: number of triangles + + kcl_t *kcl, // pointer to valid KCL + FILE *f, // log file + double3 pt, // point to analyze + int verbosity, // log verbose level + // <=0: silent + // >=1: print warnings + // >=2: print base calculations + // >=3: print step calculations + double3 *cube_min, // not NULL: store cube data here + uint *cube_width, // not NULL: store cube width here + u16 **tri_list // not NULL: store pointer to + // triangle list here +) +{ + DASSERT(kcl); + if ( !kcl->octree || !kcl->octree_valid ) + return -1; + + if (cube_min) + memset(cube_min,0,sizeof(*cube_min)); + if (cube_width) + *cube_width = 0.0; + if (tri_list) + *tri_list = 0; + + + //--- test 'min_octree' + + if ( pt.x < kcl->min_octree.x + || pt.y < kcl->min_octree.y + || pt.z < kcl->min_octree.z ) + { + if ( verbosity > 0 ) + fprintf(f, + "# No triangles: At least one coordinate is too small.\n" + "# * point: %11.3f %11.3f %11.3f\n" + "# * min border: %11.3f %11.3f %11.3f\n" + "#\n", + pt.x, pt.y, pt.z, + kcl->min_octree.x, kcl->min_octree.y, kcl->min_octree.z ); + return 0; + } + + double3 basept; + Sub3(basept,pt,kcl->min_octree); + + if ( basept.x > 2e9 || basept.y > 2e9 || basept.z > 2e9 ) + { + if ( verbosity > 0 ) + fprintf(f, + "# No triangles: At least one coordinate is too large (diff>10^9).\n" + "# * point: %11.3f %11.3f %11.3f\n" + "# * point - min: %11.3f %11.3f %11.3f\n" + "#\n", + pt.x, pt.y, pt.z, + basept.x, basept.y, basept.z ); + return 0; + } + + + //--- create and test integer coordinates + + u32 idx_mask = 1 << kcl->coord_rshift; // same as current cube width! + + double3 cmin; + uint p, failed = 0; + u32 base[3], relevant[3], nmask[3], nbits[3]; + for ( p = 0; p < 3; p++ ) + { + cmin.v[p] = trunc( basept.v[p] / idx_mask ) * idx_mask + kcl->min_octree.v[p]; + base[p] = round(basept.v[p]); + u32 mask = kcl->mask[p]; + relevant[p] = ( base[p] & ~mask ) >> kcl->coord_rshift << kcl->coord_rshift; + if ( base[p] & mask ) + failed++; + + if (!mask) + nmask[p] = 32; + else + { + nmask[p] = 0; + for ( ; !(mask&1); mask >>= 1 ) + nmask[p]++; + } + nbits[p] = nmask[p] > kcl->coord_rshift + ? nmask[p] - kcl->coord_rshift + : 0; + } + + if (failed) + { + if ( verbosity > 0 ) + fprintf(f, + "# No triangles: Mask test failed.\n" + "# * point: %11.3f %11.3f %11.3f\n" + "# * min border: %11.3f %11.3f %11.3f\n" + "# * relevant pt: %11u %11u %11u\n" + "# * relevant pt: %#11x %#11x %#11x\n" + "# * mask: %#11x %#11x %#11x\n" + "#\n", + pt.x, pt.y, pt.z, + kcl->min_octree.x, kcl->min_octree.y, kcl->min_octree.z, + base[0], base[1], base[2], + base[0], base[1], base[2], + kcl->mask[0], kcl->mask[1], kcl->mask[2] ); + return ERR_OK; + } + + u32 oct_index = base[0] >> kcl->coord_rshift + | base[1] >> kcl->coord_rshift << kcl->y_lshift + | base[2] >> kcl->coord_rshift << kcl->z_lshift; + + u32 baseoff = 0; + u32 offset = oct_index * 4; + + if ( verbosity > 1 ) + { + fprintf(f,"# Base values:\n" + "# * point: %11.3f %11.3f %11.3f\n" + "# * min border: %11.3f %11.3f %11.3f\n" + "# * relevant pt: %11u %11u %11u\n" + "# * relevant pt: %#11x %#11x %#11x\n" + "# * mask: %#11x %#11x %#11x\n" + "# * relevant bits: %11u %11u %11u\n" + "# * index bits: %11u %11u %11u (rshift=%u)\n" + "# * index pt: %#11x %#11x %#11x\n" + "# * start index: %#x | %#x | %#x == %#x => offset=%#x\n" + "# * cube min: %11.3f %11.3f %11.3f (width=%u)\n" + "# * cube max: %11.3f %11.3f %11.3f\n" + "#\n", + pt.x, pt.y, pt.z, + kcl->min_octree.x, kcl->min_octree.y, kcl->min_octree.z, + base[0], base[1], base[2], + base[0], base[1], base[2], + kcl->mask[0], kcl->mask[1], kcl->mask[2], + nmask[0], nmask[1], nmask[2], + nbits[0], nbits[1], nbits[2], kcl->coord_rshift, + relevant[0], relevant[1], relevant[2], + base[0] >> kcl->coord_rshift, + base[1] >> kcl->coord_rshift << kcl->y_lshift, + base[2] >> kcl->coord_rshift << kcl->z_lshift, + oct_index, offset, + cmin.x, cmin.y, cmin.z, idx_mask, + cmin.x+idx_mask, cmin.y+idx_mask, cmin.z+idx_mask ); + } + + + //--- traverse the octree + + uint step; + for ( step=1;; step++ ) + { + if ( baseoff + 0x20 > kcl->octree_size ) + { + if ( verbosity > 0 ) + fprintf(f,"# Abort, because octree index out of bounds!\n" + "# * point: %11.3f %11.3f %11.3f\n" + "# * Offset: %#10x\n" + "# * Size: %#10x\n" + "#\n", + pt.x, pt.y, pt.z, + offset, kcl->octree_size ); + return -1; + } + + if ( offset & 3 ) + { + if ( verbosity > 0 ) + fprintf(f,"# Abort, because invalid octree index!\n" + "# * point: %11.3f %11.3f %11.3f\n" + "# * Offset: %#10x\n" + "# * Low 2 bits: %#10x\n" + "#\n", + pt.x, pt.y, pt.z, + offset, offset&3 ); + return -1; + } + + u32 val = be32( kcl->octree + offset ); + if ( val & 0x80000000 ) + { + offset = baseoff + ( val & 0x7ffffff ) + 2; + if ( offset+2 > kcl->octree_size ) + { + if ( verbosity > 0 ) + fprintf(f,"# Abort, because triangle list index out of bounds!\n" + "# * point: %11.3f %11.3f %11.3f\n" + "# * Offset: %#10x\n" + "# * Size: %#10x\n" + "#\n", + pt.x, pt.y, pt.z, + offset, kcl->octree_size ); + return -1; + } + + if ( offset & 1 ) + { + if ( verbosity > 0 ) + fprintf(f,"# Abort, because invalid triangle list index!\n" + "# * point: %11.3f %11.3f %11.3f\n" + "# * Offset: %#10x\n" + "# * Low bit: %#10x\n" + "#\n", + pt.x, pt.y, pt.z, + offset, offset&1 ); + return -1; + } + + if (cube_min) + *cube_min = cmin; + if (cube_width) + *cube_width = idx_mask; + if (tri_list) + *tri_list = (u16*)( kcl->octree + offset ); + + u32 off; + for ( off = offset; off < kcl->octree_size; off += 2 ) + if (!be16( kcl->octree + off )) + break; + return ( off - offset ) / 2; + } + + uint next_index = 0; + idx_mask >>= 1; + if ( idx_mask & base[0] ) + { + next_index |= 1; + cmin.x += idx_mask; + } + if ( idx_mask & base[1] ) + { + next_index |= 2; + cmin.y += idx_mask; + } + if ( idx_mask & base[2] ) + { + next_index |= 4; + cmin.z += idx_mask; + } + baseoff += val; + offset = baseoff + 4*next_index; + + if ( verbosity > 2 ) + { + fprintf(f,"# Step #%u, next octree entry found, value %#x:\n" + "# * mask %#x, index %u+%u+%u = %u, offset %#x,%#x\n" + "# * cube min: %11.3f %11.3f %11.3f (width=%u)\n" + "# * cube max: %11.3f %11.3f %11.3f\n" + "#\n", + step, val, + idx_mask, + next_index & 1, next_index & 2, next_index & 4, + next_index, baseoff, offset, + cmin.x, cmin.y, cmin.z, idx_mask, + cmin.x+idx_mask, cmin.y+idx_mask, cmin.z+idx_mask ); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError TraverseOctreeKCL +( + kcl_t *kcl, // pointer to valid KCL + FILE *f, // output file, if NULL set verbosity:=0 + double3 pt, // point to analyze + bool print_tri_val, // true: print tri values instead of index + uint verbosity // verbose level + // >=1: print base calculations + // >=2: print step calculations +) +{ + DASSERT(kcl); + CreateOctreeKCL(kcl); + u16 *tri_ptr; + int stat = TraverseOctree(kcl,f,pt,verbosity+1,0,0,&tri_ptr); + if ( stat < 0 ) + return ERR_WARNING; + + const int fw_tlist = GetTermWidth(80,40) - 7; + + if (!f) + verbosity = 0; + else if (verbosity>0) + fputc('\n',f); + fprintf(f,"List with %u triangles found at offset %#x\n", + stat, (int)((u8*)tri_ptr - kcl->octree) ); + + if (!print_tri_val) + { + uint pcount = fprintf(f," - ");; + for ( ; stat > 0; stat--, tri_ptr++ ) + { + uint tidx = be16(tri_ptr); + if (!tidx--) + break; + if ( pcount > fw_tlist ) + pcount = fprintf(f,"\n "); + pcount += fprintf(f," %u%s", + tidx, + tidx <= kcl->tridata.used ? "" : "!" ); + } + } + else + { + fprintf(f,"\n * searched pt: %11.3f %11.3f %11.3f\n\n", + pt.x, pt.y, pt.z ); + + for ( ; stat > 0; stat--, tri_ptr++ ) + { + uint tidx = be16(tri_ptr); + if (!tidx--) + break; + if ( tidx < kcl->tridata.used ) + { + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list + tidx; + fprintf(f, + " * triangle pt 1: %11.3f %11.3f %11.3f (index %u)\n" + " * pt 2: %11.3f %11.3f %11.3f (flag 0x%04x)\n" + " * pt 3: %11.3f %11.3f %11.3f\n" + "\n", + td->pt[0].x, td->pt[0].y, td->pt[0].z, tidx, + td->pt[1].x, td->pt[1].y, td->pt[1].z, td->cur_flag, + td->pt[2].x, td->pt[2].y, td->pt[2].z ); + } + else + fprintf(f," * Invalid triangle, index = %u = %#x\n", + tidx, tidx ); + + } + } + fputs("\n\n",f); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// FallKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define N_FALL_LIST 10 + +typedef struct kcl_fall_t +{ + kcl_t * kcl; // related kcl + FILE *f; // log file + int verbose; // verbose level + u32 type_mask; // kcl type selector (bit field) + + kcl_cube_t cube; // cube to test + uint width; // width of cube + + u16 *tri_list[N_FALL_LIST]; // list to tri lists + uint n_tri_list; // number of valid 'tri_list' + + bool last_valid; // last values are valid + double3 last_min; // last traversed cube min + uint last_width; // last traversed cube width + +} kcl_fall_t; + +/////////////////////////////////////////////////////////////////////////////// + +static int Collide2 +( + kcl_fall_t *kf, // valid data + double3 pt // point for octree search +) +{ + DASSERT(kf); + noPRINT("Collide2() n=%u, v=%d, pt=%.0f %.0f %.0f\n", + kf->n_tri_list, kf->last_valid, pt.x, pt.y, pt.z ); + + + //--- test if point is in previous cube + + if ( kf->last_valid + && pt.x >= kf->last_min.x && pt.x <= kf->last_min.x + kf->last_width + && pt.y >= kf->last_min.y && pt.y <= kf->last_min.y + kf->last_width + && pt.z >= kf->last_min.z && pt.z <= kf->last_min.z + kf->last_width + ) + { + return -1; + } + + //--- find tri list + + u16 *tri_list; + int stat = TraverseOctree(kf->kcl,kf->f,pt, + kf->verbose-1, &kf->last_min,&kf->last_width,&tri_list); + if ( stat < 0 ) + return -1; + + kf->last_valid = true; + if ( !tri_list || !*tri_list ) + return -1; + + uint i; + for ( i = 0; i < kf->n_tri_list; i++ ) + if (kf->tri_list[i] == tri_list ) + return -1; + + if ( kf->n_tri_list < N_FALL_LIST ) + kf->tri_list[kf->n_tri_list++] = tri_list; + + uint ti; + for ( ti = 0; ti < stat; ti++ ) + { + DASSERT(tri_list); + uint tidx = be16(tri_list+ti); + if (!tidx--) + break; + if ( tidx >= kf->kcl->tridata.used ) + continue; + + const kcl_tridata_t *td = (kcl_tridata_t*)kf->kcl->tridata.list + tidx; + if ( td->cur_flag >= N_KCL_TYPE || ( 1u << (td->cur_flag&0x1f) & kf->type_mask ) ) + { + kcl_tri_t ot; + #if SUPPORT_KCL_CUBE + setup_oct_tri(kf->kcl,&ot,0,td); // use [[cube]] list param? + if (OctCubeTriangleOverlaped(&kf->cube,&ot,0)) // [use [[cube]] list param? + return td->cur_flag; + #else + setup_oct_tri(kf->kcl,&ot,td); + if (OctCubeTriangleOverlaped(&kf->cube,&ot)) + return td->cur_flag; + #endif + } + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int Collide1 +( + kcl_fall_t *kf, // valid data + double y // y point to test +) +{ + DASSERT(kf); + kf->n_tri_list = 0; + kf->last_valid = false; + + kf->cube.min[1] = round ( y - kf->kcl->min_octree.y ); + kf->cube.max[1] = kf->cube.min[1] + kf->width; + + noPRINT("OCT=%d,%d,%d .. %d,%d,%d\n", + kf->cube.min[0], kf->cube.min[1], kf->cube.min[2], + kf->cube.max[0], kf->cube.max[1], kf->cube.max[2] ); + + double3 pt; + double3 *min = &kf->kcl->min_octree; + pt.x = min->x + ( kf->cube.min[0] + kf->cube.max[0] ) / 2; + pt.y = min->y + kf->cube.min[1]; + pt.z = min->z + ( kf->cube.min[2] + kf->cube.max[2] ) / 2; + int stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.x = min->x + kf->cube.min[0]; + pt.z = min->z + kf->cube.min[2]; + stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.z = min->z + kf->cube.max[2]; + stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.x = min->x + kf->cube.max[0]; + stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.z = min->z + kf->cube.min[2]; + stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.y = min->y + kf->cube.max[1]; + stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.z = min->z + kf->cube.max[2]; + stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.x = min->y + kf->cube.min[0]; + stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.z = min->z + kf->cube.min[2]; + stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.x = min->x + ( kf->cube.min[0] + kf->cube.max[0] ) / 2; + pt.z = min->z + ( kf->cube.min[2] + kf->cube.max[2] ) / 2; + return Collide2(kf,pt); +} + +/////////////////////////////////////////////////////////////////////////////// + +double FallKCL +( + // returns the height or -1.0 on fail + + kcl_t *kcl, // pointer to valid KCL + FILE *f, // output file, if NULL: verbose:=0 + double3 pt, // mid point of cube to analyze + uint width, // width of cube + int verbosity, // verbose level + // <=0: silent + // >=1: log triangle search + // >=2: print base calculations + // >=3: print step calculations + u32 type_mask, // kcl type selector (bit field) + int *res_kcl_flag // not NULL: store KCL flag here +) +{ + DASSERT(kcl); + CreateOctreeKCL(kcl); + if ( !kcl->octree || !kcl->octree_valid || pt.y < 0.0 ) + return -1.0; + + if ( width < 1 ) + width = 1; + + kcl_fall_t kf; + memset(&kf,0,sizeof(kf)); + kf.kcl = kcl; + kf.f = f; + kf.verbose = f ? verbosity : 0; + kf.width = width; + kf.type_mask = type_mask; + + uint p; + for ( p = 0; p < 3; p++ ) + { + kf.cube.min[p] = round ( pt.v[p] - kcl->min_octree.v[p] - width/2); + kf.cube.max[p] = kf.cube.min[p] + width; + } + + if ( width > 1 ) + width--; + + // very first test + int stat = Collide1(&kf,pt.y); + if ( stat >= 0 ) + { + if (res_kcl_flag) + *res_kcl_flag = stat; + return pt.y; + } + + pt.y = ceil( pt.y - width ); // round only if really falling! + for(;;) + { + stat = Collide1(&kf,pt.y); + if ( stat >= 0 ) + break; + if ( pt.y <= 0.0 ) + { + if (res_kcl_flag) + *res_kcl_flag = -1; + return -1.0; + } + pt.y -= width; + if ( pt.y < 0.0 ) + pt.y = 0.0; + } + + pt.y += width; + while ( width > 1 ) + { + width = (width+1)/2; + pt.y -= width; + int stat2 = Collide1(&kf,pt.y); + if ( stat2 >= 0 ) + { + stat = stat2; + pt.y += width; + } + } + + if ( pt.y < 0.0 ) + { + pt.y = -1.0; + stat = -1; + } + + if (res_kcl_flag) + *res_kcl_flag = stat; + return pt.y; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// drop+sort unused triangles /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static kcl_t *kcl_sort = 0; + +static int sort_triangle ( const u16 *ia, const u16 *ib ) +{ + DASSERT(kcl_sort); + + kcl_tridata_t *ta = (kcl_tridata_t*)kcl_sort->tridata.list + *ia; + kcl_tridata_t *tb = (kcl_tridata_t*)kcl_sort->tridata.list + *ib; + if ( ta->length != tb->length ) + { + double absa = fabs(ta->length); + double absb = fabs(tb->length); + if ( absa != absb ) + return absa < absb ? -1 : +1; + return ta->length < tb->length ? -1 : +1; + } + + double *pa = ta->pt->v; + double *pb = tb->pt->v; + uint count; + for ( count = 0; count < 9; count++, pa++, pb++ ) + if ( *pa != *pb ) + return *pa < *pb ? -1 : +1; + + if ( ta->cur_flag != tb->cur_flag ) + return ta->cur_flag < tb->cur_flag ? -1 : +1; + + return ia < ib ? -1 : ia > ib; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool DropSortHelper +( + kcl_t *kcl, // valid KCL + uint drop_mask, // not 0: drop triangles with set bits + bool sort // true: sort triangles +) +{ + DASSERT(kcl); + if (sort) + CalcNormalsKCL(kcl,false); + KCL_ACTION_LOG("DropSortTriangles(,drop=%x,sort=%d)\n",drop_mask,sort); + + + //--- setup list + + const uint n_tri = kcl->tridata.used; + if (!n_tri) + return false; + u16 *list = CALLOC(n_tri,sizeof(*list)); + bool dirty = false; + + + //--- drop unreferenced triangles + + kcl_tridata_t *td_base = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td, *td_end = td_base + n_tri; + + uint n_list = 0; + if (drop_mask) + { + uint n_invalid = 0, n_fixed = 0; + for ( td = td_base; td < td_end; td++ ) + { + if ( !(td->status & drop_mask) ) + { + list[n_list++] = td - td_base; + if ( td->status & TD_INVALID ) + n_invalid++; + else if ( td->status & TD_FIXED_PT ) + n_fixed++; + } + else if ( !(td->status & TD_UNUSED) ) + kcl->octree_valid = false; // a used triangles was removed + } + kcl->tri_inv_count = n_invalid; + kcl->tri_fix_count = n_fixed; + if ( n_list < n_tri ) + { + dirty = true; + kcl->min_max_valid = false; + } + } + else + { + for ( ; n_list < n_tri; n_list++ ) + list[n_list] = n_list; + } + + #if HAVE_PRINT + { + uint i, max = n_list < 10 ? n_list : 10; + PRINT("INDEX-LIST [%u/%u/%u,dirty=%u]:",max,n_list,n_tri,dirty); + for ( i = 0; i < max; i++ ) + fprintf(stderr," %u",list[i]); + fputc('\n',stderr); + } + #endif + + + //--- sort triangles + + if ( sort && n_list > 1 ) + { + dirty = true; // assume that sort will change anything + kcl_sort = kcl; + qsort( list, n_list, sizeof(*list), (qsort_func)sort_triangle ); + kcl_sort = 0; + } + + + //--- fix kcl->tridata + + if (dirty) + { + kcl->model_modified = true; + + kcl_tridata_t *tsrc = (kcl_tridata_t*)kcl->tridata.list; + kcl->tridata.list = 0; + ResetList(&kcl->tridata); + kcl_tridata_t *tdest = GrowList(&kcl->tridata,n_list); + + uint idx; + for ( idx = 0; idx < n_list; idx++, tdest++ ) + memcpy(tdest,tsrc+list[idx],sizeof(*tdest)); + FREE(tsrc); + } + + + //--- fix kcl->octree + + if ( dirty && kcl->octree && kcl->octree_valid ) + { + // we need a reverse list! + u16 *rlist = CALLOC(n_tri+1,sizeof(*rlist)); + uint idx; + for ( idx = 0; idx < n_list; idx++ ) + rlist[list[idx]+1] = idx+1; + + // now we fix the octree + u16 *ptr = (u16*)(kcl->octree + kcl->min_l_offset); + u16 *end = (u16*)(kcl->octree + kcl->max_l_offset); + for ( ; ptr < end; ptr++ ) + write_be16(ptr,rlist[be16(ptr)]); + + FREE(rlist); + } + + + //--- terminate + + FREE(list); + if (dirty) + ResetStatisticsKCL(kcl); + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool DropSortTriangles ( kcl_t * kcl ) +{ + DASSERT(kcl); + if (!drop_sort_triangles) + return false; + + kcl_mode_t local_mode = KCL_MODE; + if ( enable_kcl_drop_auto > 0 && local_mode & KCLMD_DROP_AUTO ) + { + local_mode |= kcl->octree_valid && kcl->fform_outfile == FF_KCL + ? KCLMD_DROP_UNUSED + : KCLMD_M_DROP; + PRINT("DROP-AUTO[%d,%d] => %llx -> %llx\n", + kcl->octree_valid, kcl->fform_outfile == FF_KCL, + (u64)KCL_MODE, (u64)local_mode ); + } + + kcl_tridata_status_t mode = 0; + if ( local_mode & KCLMD_DROP_UNUSED ) mode |= TD_UNUSED; + if ( local_mode & KCLMD_DROP_FIXED ) mode |= TD_FIXED_PT; + if ( local_mode & KCLMD_DROP_INVALID ) mode |= TD_INVALID; + PRINT("DROP-MODE: %x\n",mode); + + bool sort_triangles = 0 != ( local_mode & KCLMD_SORT ); + PRINT("*** DropSortTriangles() mode=%x|%u=%u ***\n", + mode, sort_triangles, drop_sort_triangles ); + + if ( local_mode & KCLMD_DROP_UNUSED && kcl->octree_valid ) + CalcStatisticsKCL(kcl); + + if ( local_mode & (KCLMD_DROP_INVALID|KCLMD_RM_FACEDOWN + |KCLMD_RM_FACEUP|KCLMD_CONV_FACEUP) ) + { + CalcNormalsKCL(kcl,false); + if ( local_mode & (KCLMD_RM_FACEDOWN|KCLMD_RM_FACEUP|KCLMD_CONV_FACEUP) ) + { + mode |= TD_MARK; + + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td_end = td + kcl->tridata.used; + for ( ; td < td_end; td++ ) + td->status &= ~TD_MARK; + } + } + + if ( local_mode & KCLMD_RM_FACEDOWN ) + { + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td_end = td + kcl->tridata.used; + for ( ; td < td_end; td++ ) + { + if ( td->normal[0].y <= -0.5 ) + { + DASSERT(!(td->status&TD_INVALID_NORM)); + const int type = GET_KCL_TYPE(td->cur_flag); + if ( type >= 0 && kcl_type[type].attrib & KCLT_DRIVE ) + td->status |= TD_MARK; + } + } + } + + if ( local_mode & (KCLMD_RM_FACEUP|KCLMD_CONV_FACEUP) ) + { + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td_end = td + kcl->tridata.used; + for ( ; td < td_end; td++ ) + { + if ( td->normal[0].y > 0.985 ) + { + DASSERT(!(td->status&TD_INVALID_NORM)); + const int type = GET_KCL_TYPE(td->cur_flag); + if ( type >= 0 && kcl_type[type].attrib & KCLT_C_WALL ) + { + if ( local_mode & KCLMD_CONV_FACEUP ) + td->cur_flag = 0; + else + td->status |= TD_MARK; + } + } + } + } + + if ( local_mode & KCLMD_WEAK_WALLS ) + { + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td_end = td + kcl->tridata.used; + for ( ; td < td_end; td++ ) + { + const int type = GET_KCL_TYPE(td->cur_flag); + if ( type >= 0 && kcl_type[type].attrib & KCLT_C_WALL ) + td->cur_flag |= 0x8000; + } + } + + return DropSortHelper( kcl, mode, sort_triangles ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KCL Script /////////////// +/////////////////////////////////////////////////////////////////////////////// + +StringField_t kcl_script_list = {0}; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptKclScript ( ccp arg ) +{ + if ( arg && *arg ) + { + if (!kcl_script_list.used) + have_patch_count++, have_kcl_patch_count++; + InsertStringField(&kcl_script_list,arg,false); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool ScriptKCL +( + kcl_t *kcl, // valid KCL data + bool is_first // true: called at first place +) +{ + DASSERT(kcl); + + if ( (KCL_MODE & KCLMD_F_SCRIPT) ? !is_first : is_first ) + return false; + + PRINT("ScriptKCL(,%d)\n",is_first); + + //--- load source file once + + static uint used = 0; + static u8 **data = 0; + static uint *data_size = 0; + + if ( !used && kcl_script_list.used ) + { + used = kcl_script_list.used; + + // script files are only loaded once + data = CALLOC( kcl_script_list.used, sizeof(*data) ); + data_size = CALLOC( kcl_script_list.used, sizeof(*data_size) ); + + uint i, active = 0; + for ( i = 0; i < used; i++ ) + { + OpenReadFILE(kcl_script_list.field[i],0,false,data+i,data_size+i,0,0); + if ( data[i] && data_size[i] ) + active++; + } + + if (!active) + { + ResetStringField(&kcl_script_list); + have_patch_count--, have_kcl_patch_count--; + used = 0; + } + } + + if (!used) + return false; + + uint i; + for ( i = 0; i < used && i < kcl_script_list.used; i++ ) + { + if ( data[i] && data_size[i] ) + { + ccp fname = kcl_script_list.field[i]; + KCL_ACTION_LOG("ScriptKCL() %s\n",fname); + + ScanInfo_t si; + InitializeSI(&si,(ccp)data[i],data_size[i],fname,REVISION_NUM); + si.kcl = kcl; + si.predef = SetupVarsKCL(); + + kcl->script_tri_removed = false; + kcl->script_tri_added = false; + kcl->script_pt_modified = false; + + while (NextLineSI(&si,false,true)) + ; //--- scan source + + ResetSI(&si); + } + } + + //--- transfer results + + bool sort_triangles = false; + + if ( kcl->script_tri_added || kcl->script_pt_modified ) + { + kcl->model_modified = true; + CalcNormalsKCL(kcl,true); + kcl->octree_valid = kcl->min_max_valid = kcl->stat_valid = false; + if ( KCL_MODE & KCLMD_SORT ) + sort_triangles = true; + } + + if (kcl->script_tri_removed) + { + if ( KCL_MODE & KCLMD_SORT ) + sort_triangles = true; + kcl->octree_valid = false; + DropSortHelper(kcl,TD_REMOVED,sort_triangles); + sort_triangles = false; + } + else if (sort_triangles) + DropSortHelper(kcl,0,true); + + return true; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Transform KCL /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool TransformKCL +( + kcl_t * kcl // pointer to valid KMP +) +{ + DASSERT(kcl); + if ( have_patch_count <= 0 || !transform_active || disable_transformation > 0 ) + return false; + + KCL_ACTION_LOG("TransformKCL()\n"); + + const uint n_tri = kcl->tridata.used; + if (!n_tri) + return false; + + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + TransformPosDouble3D(td->pt[0].v,n_tri,sizeof(*td)); + TransformPosDouble3D(td->pt[1].v,n_tri,sizeof(*td)); + TransformPosDouble3D(td->pt[2].v,n_tri,sizeof(*td)); + // [[xtridata]] + + kcl_tridata_t *td_end = td + n_tri; + for ( ; td < td_end; td++ ) + td->status |= TD_INVALID_NORM; + + kcl->octree_valid = kcl->min_max_valid = kcl->stat_valid + = kcl->norm_valid = false; + + kcl->model_modified = true; + return true; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Patch KCL /////////////// +/////////////////////////////////////////////////////////////////////////////// + +u16 *patch_kcl_flag = 0; // NULL or 'N_KCL_FLAG' elements + +/////////////////////////////////////////////////////////////////////////////// + +void RemovePatchKclFlag() +{ + if (patch_kcl_flag) + { + have_patch_count--, have_kcl_patch_count--; + FREE(patch_kcl_flag); + patch_kcl_flag = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void PurgePatchKclFlag() +{ + if (patch_kcl_flag) + { + uint i; + bool have_patch = false; + for ( i = 0; i < N_KCL_FLAG; i++ ) + if ( patch_kcl_flag[i] != i ) + { + have_patch = true; + break; + } + if (!have_patch) + RemovePatchKclFlag(); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetPatchKclFlag() +{ + if (!patch_kcl_flag) + { + have_patch_count++, have_kcl_patch_count++; + patch_kcl_flag = MALLOC( N_KCL_FLAG * sizeof(*patch_kcl_flag) ); + } + + uint i; + for ( i = 0; i < N_KCL_FLAG; i++ ) + patch_kcl_flag[i] = i; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupPatchKclFlag() +{ + if (!patch_kcl_flag) + ResetPatchKclFlag(); +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptKclFlag ( ccp arg ) +{ + if (!arg) + { + RemovePatchKclFlag(); + return 0; + } + + ResetPatchKclFlag(); + for(;;) + { + //--- skip spaces & ',' + + while ( *arg && (uchar)*arg <= ' ' || *arg == ',' ) + arg++; + if (!*arg) + break; + + //--- find '=' + + ccp old = arg; + for ( ; *arg != '='; arg++ ) + { + if ( !*arg || *arg == ',' ) + { + ERROR0(ERR_SYNTAX, + "Missing '=' in parameter of --kcl-flag: %s\n",old); + goto abort; + } + } + DASSERT( *arg == '=' ); + arg++; + + //--- scan new value + + u32 new_val, valid; + arg = ScanNumU32(arg,&valid,&new_val,0,N_KCL_FLAG-1); + if (!valid) + { + ERROR0(ERR_SYNTAX, + "Missing number in parameter of --kcl-flag: %s\n",arg); + goto abort; + } + PRINT("NEW-VAL: - %04x\n",new_val); + + + //--- now scan old values (=index) and insert new value into table + + for(;;) + { + //--- skip spaces & '+' + + while ( *old && (uchar)*old <= ' ' || *old == '+' ) + old++; + if ( *old == '=' ) + break; + + + //--- test for 'T' prefix + + const bool have_t = *old == 't' || *old == 'T'; + if (have_t) + { + old++; + while ( *old && (uchar)*old <= ' ' ) + old++; + } + + + //--- scan range + + u32 old1, old2; + old = ScanRangeU32(old,&valid,&old1,&old2,0, + have_t ? N_KCL_TYPE-1 : N_KCL_FLAG-1 ); + if (!valid) + { + ERROR0(ERR_SYNTAX, + "Missing number in parameter of --kcl-flag: %s\n",old); + goto abort; + } + DASSERT( old1 >= 0 ); + DASSERT( old2 < N_KCL_FLAG ); + + while ( *old && (uchar)*old <= ' ' ) + old++; + + + //--- scan mask + + u32 mask = 0xffff; + if (have_t) + mask = N_KCL_TYPE-1; + else if ( *old == '/' ) + { + old++; + old = ScanNumU32(old,&valid,&mask,0,N_KCL_FLAG-1); + + while ( *old && (uchar)*old <= ' ' ) + old++; + } + + if ( !valid || *old != '+' && *old != '=' ) + { + ERROR0(ERR_SYNTAX, + "Missing '=' in parameter of --kcl-flag: %s\n",old); + goto abort; + } + + + //--- normalize params + + if (mask) + { + const u32 xmask = (u16)~mask; + u32 val = old1 & mask; + const u32 end = old2 | xmask; + const u32 newv = new_val & mask; + + PRINT("OLD-VAL: %s %04x..%04x / %04x => %04x..%04x := %04x\n", + have_t ? "T" : " ", old1, old2, mask, val, end, newv ); + + for ( ; val <= end; val++ ) + { + const u16 mval = val & mask; + if ( mval >= old1 && mval <= old2 ) + patch_kcl_flag[val] = val & xmask | newv; + } + } + } + } + PurgePatchKclFlag(); + + #if defined(DEBUG) && defined(TEST) + if (patch_kcl_flag) + { + PRINT("## HAVE KCL PATCH [%d,%d]\n",have_patch_count,have_kcl_patch_count); + #if 0 + for ( i = 0; i < N_KCL_FLAG; i++ ) + if ( patch_kcl_flag[i] != i ) + PRINT(" %04x := %04x\n", i, patch_kcl_flag[i] ); + #endif + } + #endif + + return 0; + + abort: + PRINT("ABORT KCL-FLAG\n"); + RemovePatchKclFlag(); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptTriArea ( ccp arg ) +{ + if (!arg) + return 0; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),"Option --tri-area",0); + si.float_div++; + + DEFINE_VAR(temp); + enumError err = ScanExprSI(&si,&temp); + if (!err) + { + const double d = GetDoubleV(&temp); + if ( d >= 0 ) + opt_tri_area = d; + CheckEolSI(&si); + ResetSI(&si); + } + return err != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptTriHeight ( ccp arg ) +{ + if (!arg) + return 0; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),"Option --tri-height",0); + si.float_div++; + + DEFINE_VAR(temp); + enumError err = ScanExprSI(&si,&temp); + if (!err) + { + const double d = GetDoubleV(&temp); + if ( d >= 0 ) + opt_tri_height = d; + CheckEolSI(&si); + ResetSI(&si); + } + return err != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool PatchRawDataKCL +( + void * data, // data to scan + uint data_size, // size of 'data' + ccp fname // file name for error messages +) +{ + if ( have_patch_count <= 0 || have_kcl_patch_count <= 0 && !transform_active ) + return false; + kcl_analyze_t ka; + if ( IsValidKCL(&ka,data,data_size,data_size,0) >= VALID_ERROR ) + return false; + + KCL_ACTION_LOG("PatchRawDataKCL: %s\n",fname); + PRINT("** PatchRawDataKCL() **\n"); + + kcl_t kcl; + InitializeKCL(&kcl); + kcl.fform_outfile = FF_KCL; + enumError err = ScanRawKCL(&kcl,false,data,data_size,true); + kcl.fast = false; + + bool stat = false; + if ( !err && PatchKCL(&kcl) ) + { + enumError err = CreateRawKCL(&kcl,false); + + if ( !err && kcl.raw_data_size > data_size ) + { + KCL_ACTION_LOG( + "New KCL is %u bytes too large => round normals.\n", + kcl.raw_data_size - data_size ); + + RoundNormalsKCL(&kcl,16); + kcl.model_modified = true; + err = CreateRawKCL(&kcl,false); + } + + #if 0 // this has sometimes negative effect (enlarge the data) + if ( !err && kcl.raw_data_size > data_size ) + { + KCL_ACTION_LOG( + "New KCL is %u bytes too large => round points.\n", + kcl.raw_data_size - data_size ); + + RoundPointsKCL(&kcl,1); + RoundNormalsKCL(&kcl,15); + kcl.model_modified = true; + err = CreateRawKCL(&kcl,false); + } + #endif + + if ( !err && kcl.raw_data_size > data_size ) + { + KCL_ACTION_LOG( + "New KCL is %u bytes too large => enlarge triangle lists.\n", + kcl.raw_data_size - data_size ); + + RoundNormalsKCL(&kcl,15); + if ( kcl.cube_blow > 400 ) + kcl.cube_blow = 400; + kcl.max_cube_triangles *= 2; + kcl.octree_valid = false; + kcl.model_modified = true; + err = CreateRawKCL(&kcl,false); + } + + if ( !err && kcl.raw_data_size > data_size ) + { + KCL_ACTION_LOG( + "New KCL is %u bytes too large => new octree param.\n", + kcl.raw_data_size - data_size ); + + if ( kcl.cube_blow > 250 ) + kcl.cube_blow = 250; + kcl.min_cube_size *= 2; + kcl.max_cube_triangles *= 2; + kcl.max_octree_depth -= 1; + kcl.octree_valid = false; + kcl.model_modified = true; + err = CreateRawKCL(&kcl,false); + } + + if ( !err && kcl.raw_data_size > data_size ) + { + KCL_ACTION_LOG( + "New KCL is %u bytes too large => new octree param again.\n", + kcl.raw_data_size - data_size ); + + RoundNormalsKCL(&kcl,14); + if ( kcl.cube_blow > 150 ) + kcl.cube_blow = 150; + kcl.octree_valid = false; + kcl.model_modified = true; + err = CreateRawKCL(&kcl,false); + } + + if (err) + ERROR0(err,"Creation of new KCL failed: %s\n",fname); + else if ( kcl.raw_data_size > data_size ) + ERROR0(ERR_WARNING, + "Creation of new KCL failed," + " because new KCL data is too large (%u>%u): %s\n", + kcl.raw_data_size, data_size, fname ); + else + { + KCL_ACTION_LOG("Replace KCL [inplace], size %u -> %u.\n", + data_size, kcl.raw_data_size ); + memcpy(data,kcl.raw_data,kcl.raw_data_size); + stat = true; + } + } + ResetKCL(&kcl); + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool PatchKCL +( + kcl_t * kcl // pointer to valid KCL +) +{ + DASSERT(kcl); + if ( have_patch_count <= 0 || have_kcl_patch_count <= 0 && !transform_active ) + return false; + + KCL_ACTION_LOG("PatchKCL() KCL=%d\n",kcl->fform_outfile == FF_KCL); + + const bool stat + = ScriptKCL(kcl,true) + | DropSortTriangles(kcl) + | TransformKCL(kcl) + | ScriptKCL(kcl,false) + | patch_kcl_flag != 0 + | ( !kcl->octree_valid && kcl->fform_outfile == FF_KCL ); + + CheckMetricsTriData( (kcl_tridata_t*)kcl->tridata.list, kcl->tridata.used, kcl ); + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// octree iterator /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct oct_iter_t +{ + kcl_t *kcl; // pointer to valid KCL + void *param; // user parameter + oct_func func; // call back function + +} oct_iter_t; + +/////////////////////////////////////////////////////////////////////////////// + +static int iterate_oct8 +( + const oct_iter_t *iter, // iteration parameter + u32 baseoff, // offset of current table + u32 offset, // offset to handle + const kcl_cube_t *cube // current octree +) +{ + DASSERT(iter); + DASSERT(iter->kcl); + DASSERT(iter->func); + DASSERT(cube); + + + if ( offset + 4 > iter->kcl->octree_size ) + return 0; + + u32 val = be32( iter->kcl->octree + offset ); + noPRINT("\t\t\t>>> %8x %8x [%08x]\n",baseoff,offset,val); + + if ( val & 0x80000000 ) + { + offset = baseoff + ( val & 0x7ffffff ) + 2; + if ( offset < iter->kcl->octree_size ) + { + const u16 *ptr, *list = (u16*)(iter->kcl->octree + offset); + for ( ptr = list; be16(ptr); ptr++ ) + ; + return iter->func(iter->kcl,iter->param,cube,list,ptr-list); + } + return 0; + } + + baseoff += val; + if ( val && baseoff + 0x20 <= iter->kcl->octree_size ) + { + const uint cube_size = ( cube->max[0] - cube->min[0] ) / 2; + + uint i; + for ( i = 0; i < 8; i++ ) + { + kcl_cube_t subcube; + subcube.min[0] = cube->min[0] + ( i&1 ? cube_size : 0 ); + subcube.max[0] = subcube.min[0] + cube_size; + subcube.min[1] = cube->min[1] + ( i&2 ? cube_size : 0 ); + subcube.max[1] = subcube.min[1] + cube_size; + subcube.min[2] = cube->min[2] + ( i&4 ? cube_size : 0 ); + subcube.max[2] = subcube.min[2] + cube_size; + const int stat = iterate_oct8(iter,baseoff,baseoff+i*4,&subcube); + if (stat) + return stat; + } + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int OctreeIteratorKCL +( + kcl_t *kcl, // pointer to valid KCL + oct_func func, // call back function + void *param // user parameter +) +{ + DASSERT(kcl); + DASSERT(func); + + CalcStatisticsKCL(kcl); + if (!kcl->octree_valid) + return -1; + + const uint cube_size = 1 << kcl->coord_rshift; + + const uint xn = 1 << kcl->bcube_bits[0]; + const uint yn = 1 << kcl->bcube_bits[1]; + const uint zn = 1 << kcl->bcube_bits[2]; + + oct_iter_t iter; + iter.kcl = kcl; + iter.func = func; + iter.param = param; + + u32 offset = 0; + + uint zi; + for ( zi = 0; zi < zn; zi++ ) + { + uint yi; + for ( yi = 0; yi < yn; yi++ ) + { + uint xi; + for ( xi = 0; xi < xn; xi++ ) + { + kcl_cube_t cube; + cube.min[0] = xi * cube_size; + cube.max[0] = cube.min[0] + cube_size; + cube.min[1] = yi * cube_size; + cube.max[1] = cube.min[1] + cube_size; + cube.min[2] = zi * cube_size; + cube.max[2] = cube.min[2] + cube_size; + + iterate_oct8(&iter,0,offset,&cube); + offset += 4; + } + } + } + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// calculate blow level /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct kcl_blow_t +{ + uint min_blow; // minimum blow value + + uint n_tri; // number of triangles + kcl_tri_t *tlist; // list of triangles + +} kcl_blow_t; + +/////////////////////////////////////////////////////////////////////////////// + +static int calc_blow +( + kcl_t *kcl, // pointer to valid KCL + void *param, // user parameter + const kcl_cube_t *cube, // pointer to current cube geometry + // relative to kcl->min_octree; + const u16 *tri_list, // NULL or pointer to triangle index list + uint n_list // number of elements in 'tri_list' +) +{ + DASSERT(kcl); + DASSERT(cube); + DASSERT( tri_list || !n_list ); + + kcl_blow_t *blow = param; + DASSERT(blow); + + noPRINT("** %7u,%7u,%7u .. %7u,%7u,%7u WD=%7u, N=%5u, D=%6zx, BLOW=%u\n", + cube->min[0], cube->min[1], cube->min[2], + cube->max[0], cube->max[1], cube->max[2], + cube->max[0] - cube->min[0], n_list, + tri_list ? (u8*)tri_list - kcl->octree : 0, blow->min_blow ); + + kcl_cube_t xcube; + xcube.min[0] = cube->min[0] - blow->min_blow; + xcube.min[1] = cube->min[1] - blow->min_blow; + xcube.min[2] = cube->min[2] - blow->min_blow; + xcube.max[0] = cube->max[0] + blow->min_blow; + xcube.max[1] = cube->max[1] + blow->min_blow; + xcube.max[2] = cube->max[2] + blow->min_blow; + + while ( n_list-- > 0 ) + { + const uint tidx = be16(tri_list++) - 1; + + #if SUPPORT_KCL_CUBE + // use [[cube]] list param? + noPRINT("%d/%u -> %d\n",tidx,blow->n_tri, + OctCubeTriangleOverlaped(&xcube,blow->tlist+tidx,0)); + + // use [[cube]] list param? + if ( tidx >= blow->n_tri + || OctCubeTriangleOverlaped(&xcube,blow->tlist+tidx,0) ) + { + continue; + } + #else + noPRINT("%d/%u -> %d\n",tidx,blow->n_tri, + OctCubeTriangleOverlaped(&xcube,blow->tlist+tidx)); + if ( tidx >= blow->n_tri + || OctCubeTriangleOverlaped(&xcube,blow->tlist+tidx) ) + { + continue; + } + + #endif + + + //--- enlarge the box until all triangles are inside + + const uint grow_size = 512; + const int max_grow = 99999; + + uint grow, good_grow = blow->min_blow; + for ( grow = grow_size; ; grow *= 2 ) + { + uint grow_val = blow->min_blow + grow; + if ( grow_val > max_grow ) + grow_val = max_grow; + noPRINT("%5u : %6u + %6u = %6u\n", n_list, blow->min_blow, grow, grow_val ); + + kcl_cube_t ycube; + ycube.min[0] = cube->min[0] - grow_val; + ycube.min[1] = cube->min[1] - grow_val; + ycube.min[2] = cube->min[2] - grow_val; + ycube.max[0] = cube->max[0] + grow_val; + ycube.max[1] = cube->max[1] + grow_val; + ycube.max[2] = cube->max[2] + grow_val; + + #if SUPPORT_KCL_CUBE + // use [[cube]] list param? + if (OctCubeTriangleOverlaped(&ycube,blow->tlist+tidx,0)) + break; + #else + if (OctCubeTriangleOverlaped(&ycube,blow->tlist+tidx)) + break; + #endif + + if ( grow_val >= 99999 ) + { + blow->min_blow = 99999; + return 1; + } + good_grow = grow_val; + } + noPRINT("GROW (A) TO %u\n",blow->min_blow); + blow->min_blow = good_grow; + + + //--- now use a binary method to find the border + + for ( grow = grow_size / 2; grow; grow /= 2 ) + { + const uint grow_val = blow->min_blow + grow; + + kcl_cube_t ycube; + ycube.min[0] = cube->min[0] - grow_val; + ycube.min[1] = cube->min[1] - grow_val; + ycube.min[2] = cube->min[2] - grow_val; + ycube.max[0] = cube->max[0] + grow_val; + ycube.max[1] = cube->max[1] + grow_val; + ycube.max[2] = cube->max[2] + grow_val; + + #if SUPPORT_KCL_CUBE + // use [[cube]] list param? + if (!OctCubeTriangleOverlaped(&ycube,blow->tlist+tidx,0)) + blow->min_blow = grow_val; + #else + if (!OctCubeTriangleOverlaped(&ycube,blow->tlist+tidx)) + blow->min_blow = grow_val; + #endif + } + noPRINT("GROW (B) TO %u\n",blow->min_blow); + + + //--- fix the xcube + + xcube.min[0] = cube->min[0] - blow->min_blow; + xcube.min[1] = cube->min[1] - blow->min_blow; + xcube.min[2] = cube->min[2] - blow->min_blow; + xcube.max[0] = cube->max[0] + blow->min_blow; + xcube.max[1] = cube->max[1] + blow->min_blow; + xcube.max[2] = cube->max[2] + blow->min_blow; + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CalcBlowSizeKCL ( kcl_t *kcl ) +{ + DASSERT(kcl); + + KCL_ACTION_LOG("CalcBlowSizeKCL()\n"); + + kcl_blow_t blow; + memset(&blow,0,sizeof(blow)); + blow.n_tri = kcl->tridata.used; + blow.tlist = CALLOC(blow.n_tri,sizeof(*blow.tlist)); + + uint t; + kcl_tri_t *tri = blow.tlist; + const kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + for ( t = 0; t < blow.n_tri; t++, tri++, td++ ) + #if SUPPORT_KCL_CUBE + setup_oct_tri(kcl,tri,0,td); // use [[cube]] list param? + #else + setup_oct_tri(kcl,tri,td); + #endif + + OctreeIteratorKCL(kcl,calc_blow,&blow); + + FREE(blow.tlist); + return blow.min_blow; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CkeckKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct check_kcl_t +{ + //--- param + + const kcl_t * kcl; // pointer to valid KCL + CheckMode_t mode; // print mode + + //--- status + + int warn_count; // number of warnings + int hint_count; // number of hints + int info_count; // number of infos + bool head_printed; // true: header line printed + bool sig_printed; // true: signature line printed + + //--- misc data + + ColorSet_t col; // color setup + +} check_kcl_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[xtridata]] + +// predefine function to avoid a warning because of __attribute__ usage +static void print_check_error +( + check_kcl_t * chk, // valid data structure + CheckMode_t print_mode, // NULL | CMOD_WARNING | CMOD_HINT | CMOD_INFO + ccp format, // format of message + ... // arguments + +) __attribute__ ((__format__(__printf__,3,4))); + +static void print_check_error +( + check_kcl_t * chk, // valid data structure + CheckMode_t print_mode, // NULL | CMOD_WARNING | CMOD_HINT | CMOD_INFO + ccp format, // format of message + ... // arguments + +) +{ + DASSERT(chk); + DASSERT(chk->kcl); + + ccp col; // message color + ccp bol; // begin of line + switch (print_mode) + { + case CMOD_WARNING: + if (!( chk->mode & CMOD_WARNING )) + return; + col = chk->col.warn; + bol = check_bowl; + chk->warn_count++; + break; + + case CMOD_HINT: + if (!( chk->mode & CMOD_HINT )) + return; + col = chk->col.hint; + bol = check_bohl; + chk->hint_count++; + break; + + case CMOD_INFO: + if (!( chk->mode & CMOD_INFO )) + return; + col = chk->col.info; + bol = check_boil; + chk->info_count++; + break; + + default: + col = 0; + bol = 0; // print head only + break; + } + + if (!chk->head_printed) + { + chk->head_printed = true; + if (chk->mode & (CMOD_HEADER|CMOD_FORCE_HEADER) ) + fprintf(stdlog,"%s* CHECK %s:%s%s\n", + chk->col.heading, GetNameFF(chk->kcl->fform,0), + chk->kcl->fname, chk->col.reset ); + } + + if ( !chk->sig_printed && chk->kcl->signature > 0.0 ) + { + chk->sig_printed = true; + chk->info_count++; + fprintf(stdlog,"%s%s%sSignature: %s v%4.2f\n", + chk->col.info, check_boil, chk->col.reset, + chk->kcl->signature_info, chk->kcl->signature ); + } + + if (!bol) + return; + + if (format) + { + fputs(col,stdlog); + fputs(bol,stdlog); + fputs(chk->col.reset,stdlog); + + va_list arg; + va_start(arg,format); + vfprintf(stdlog,format,arg); + va_end(arg); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[xtridata]] + +int CheckKCL +( + // returns number of errors + + kcl_t * kcl, // pointer to valid KCL + CheckMode_t mode // print mode +) +{ + DASSERT(kcl); + PRINT("CheckKCL(%d)\n",mode); + + + //--- setup + + check_kcl_t chk; + memset(&chk,0,sizeof(chk)); + SetupColorSet(&chk.col,stdlog); + chk.kcl = kcl; + chk.mode = mode; + + if ( mode & CMOD_FORCE_HEADER || kcl->signature > 0.0 ) + print_check_error(&chk,0,0); + + + //--- special statistics + + uint count_0 = 0, count_30 = 0, total = 0, n = kcl->tridata.used; + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + + for ( ; n-- > 0; td++ ) + if ( td->cur_flag < N_KCL_FLAG + && kcl_type[ td->cur_flag & 0x1f ].attrib & KCLT_DRIVE ) + { + total++; + if ( td->status & TD_INVALID_NORM ) + CalcNormalsTriData(kcl,td,1); + if ( td->normal[0].y < 0.0 ) + { + count_0++; + if ( td->normal[0].y < -0.5 ) + count_30++; + } + } + if (count_0) + print_check_error(&chk,CMOD_HINT, + "%u of %u drivable triangle%s %s face down" + " => --kcl=RM-FACEDOWN\n", + count_0, total, + total == 1 ? "" : "s", + count_0 == 1 ? "is" : "are" ); + if (count_30) + print_check_error(&chk,CMOD_HINT, + "%u of %u drivable triangle%s %s face down (>30°).\n", + count_30, total, + total == 1 ? "" : "s", + count_30 == 1 ? "is" : "are" ); + + + //--- analyze statistics + + if (kcl->octree_valid) + { + CalcStatisticsKCL(kcl); + if ( kcl->n_tri_used < kcl->tridata.used ) + { + uint unused = kcl->tridata.used - kcl->n_tri_used; + print_check_error(&chk,CMOD_HINT, + "%u of %u triangle%s obsolete" + " (not referenced by the octree)" + " => --kcl=DROP-UNUSED\n", + unused, kcl->tridata.used, + unused == 1 ? " is" : "s are" ); + } + } + + if (kcl->tri_fix_count) + print_check_error(&chk,CMOD_HINT, + "%u of %u triangle%s had extreme values" + " => --kcl=DROP-FIXED\n", + kcl->tri_fix_count, kcl->tridata.used, + kcl->tridata.used == 1 ? "" : "s" ); + + if (kcl->tri_inv_count) + print_check_error(&chk,CMOD_WARNING, + "%u of %u triangle%s invalid values" + " => --kcl=DROP-INVALID\n", + kcl->tri_inv_count, kcl->tridata.used, + kcl->tridata.used == 1 ? " has" : "s have" ); + + if (kcl->n_invalid_tri_ref) + print_check_error(&chk,CMOD_WARNING, + "The octree contains %u invalid triangle reference%s!\n", + kcl->n_invalid_tri_ref, + kcl->n_invalid_tri_ref == 1 ? "" : "s" ); + + + //--- terminate + + if ( mode & CMOD_GLOBAL_STAT ) + { + global_warn_count += chk.warn_count; + global_hint_count += chk.hint_count; + global_info_count += chk.info_count; + } + + if ( mode & (CMOD_FOOTER|CMOD_FORCE_FOOTER) ) + { + static ccp hint + = " => see https://szs.wiimm.de/cmd/wkclt/check#desc for more info."; + + ccp bind; + char hbuf[60], ibuf[60]; + if (chk.info_count) + { + snprintf(ibuf,sizeof(ibuf)," and %s%u info%s%s", + chk.col.info, + chk.info_count, chk.info_count>1 ? "s" : "", + chk.col.heading ); + bind = ","; + } + else + { + *ibuf = 0; + bind = " and"; + } + + if (chk.hint_count) + snprintf(hbuf,sizeof(hbuf),"%s %s%u hint%s%s", + bind, chk.col.hint, + chk.hint_count, chk.hint_count>1 ? "s" : "", + chk.col.heading ); + else + *hbuf = 0; + + if (chk.warn_count) + fprintf(stdlog, + " %s=> %s%u warning%s%s%s%s for %s:%s\n%s%s%s\n\n", + chk.col.heading, chk.col.warn, + chk.warn_count, chk.warn_count == 1 ? "" : "s", + chk.col.heading, + hbuf, ibuf, GetNameFF(kcl->fform,0), kcl->fname, + chk.col.heading, hint, chk.col.reset ); + else if ( mode & CMOD_FORCE_FOOTER || chk.hint_count ) + fprintf(stdlog," %s=> No warnings%s%s for %s:%s\n%s%s%s\n\n", + chk.col.heading, hbuf, ibuf, GetNameFF(kcl->fform,0), kcl->fname, + chk.col.heading, hint, chk.col.reset ); + } + + return chk.warn_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kcl parser functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_n +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + res->i = si && si->kcl ? si->kcl->tridata.used : 0; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_pt +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + if (si) + { + const int pt_index = GetIntV(param+1); + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if ( td && pt_index >= 0 && pt_index <= 2 ) + { + AssignVectorV(res,td->pt+pt_index); + return ERR_OK; + } + } + + res->mode = VAR_UNSET; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_length +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + if ( td->status & TD_INVALID_NORM ) + CalcNormalsTriData(si->kcl,td,1); + res->d = td->length; + res->mode = VAR_DOUBLE; + return ERR_OK; + } + } + + res->mode = VAR_UNSET; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_normal +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + if (si) + { + const int pt_index = GetIntV(param+1); + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if ( td && pt_index >= 0 && pt_index <= 3 ) + { + if ( td->status & TD_INVALID_NORM ) + CalcNormalsTriData(si->kcl,td,1); + float *f = td->normal[pt_index].v; + res->x = *f++; + res->y = *f++; + res->z = *f; + res->mode = VAR_VECTOR; + return ERR_OK; + } + } + + res->mode = VAR_UNSET; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_setPt +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=3); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + if ( n_param > 3 ) + { + td->pt[0] = GetVectorV(param+1); + td->pt[1] = GetVectorV(param+2); + td->pt[2] = GetVectorV(param+3); + status = 1; + } + else + { + const int pt_index = GetIntV(param+1); + if ( pt_index >= 0 && pt_index <= 2 ) + { + td->pt[pt_index] = GetVectorV(param+2); + status = 1; + } + } + if (status>0) + { + td->status |= TD_INVALID_NORM; + si->kcl->script_pt_modified = true; + } + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_scale +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + Var_t *v = ToVectorV(param+1); + if ( n_param > 2 ) + { + Var_t *origin = ToVectorV(param+2); + uint p; + for ( p = 0 ; p < 3; p++ ) + { + td->pt[p].x = ( td->pt[p].x - origin->x ) * v->x + origin->x; + td->pt[p].y = ( td->pt[p].y - origin->y ) * v->y + origin->y; + td->pt[p].z = ( td->pt[p].z - origin->z ) * v->z + origin->z; + } + } + else + { + uint p; + for ( p = 0 ; p < 3; p++ ) + { + td->pt[p].x *= v->x; + td->pt[p].y *= v->y; + td->pt[p].z *= v->z; + } + } + td->status |= TD_INVALID_NORM; + si->kcl->script_pt_modified = true; + status = 1; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_shift +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + Var_t *v = ToVectorV(param+1); + uint p; + for ( p = 0 ; p < 3; p++ ) + { + td->pt[p].x += v->x; + td->pt[p].y += v->y; + td->pt[p].z += v->z; + } + td->status |= TD_INVALID_NORM; + si->kcl->script_pt_modified = true; + status = 1; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_hRot +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + const double hrot_rad = GetDoubleV(param+1) * (M_PI/180.0); + + if ( n_param > 2 ) + { + Var_t *origin = ToVectorV(param+2); + + uint p; + double3 *d3 = td->pt; + for ( p = 0 ; p < 3; p++, d3++ ) + { + const double dx = d3->x - origin->x; + const double dz = d3->z - origin->z; + const double rad = atan2(dx,dz) + hrot_rad; + const double len = sqrt(dx*dx+dz*dz); + + d3->x = sin(rad) * len; + d3->z = cos(rad) * len; + } + } + else + { + + uint p; + double3 *d3 = td->pt; + for ( p = 0 ; p < 3; p++, d3++ ) + { + const double rad = atan2(d3->x,d3->z) + hrot_rad; + const double len = sqrt(d3->x*d3->x+d3->z*d3->z); + + d3->x = sin(rad) * len; + d3->z = cos(rad) * len; + } + } + td->status |= TD_INVALID_NORM; + si->kcl->script_pt_modified = true; + status = 1; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_remove +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + status = ( td->status & TD_REMOVED ) != 0; + td->status |= TD_REMOVED; + si->kcl->script_tri_removed = true; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_unremove +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + status = ( td->status & TD_REMOVED ) != 0; + td->status &= ~TD_REMOVED; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_isRemoved +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + status = ( td->status & TD_REMOVED ) != 0; + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_create +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=4); + + int status = -1; + if ( si && si->kcl ) + { + uint i; + for ( i = 1; i < n_param; i++ ) + ToVectorV(param+i); + + kcl_tridata_t *td = AppendTrianglesKCLp( + si->kcl, + GetIntV(param), + ¶m[1].d3, + sizeof(*param), + n_param-1 ); + if (td) + { + si->kcl->script_tri_added = true; + status = td - (kcl_tridata_t*)si->kcl->tridata.list; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_createOctahedron +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=3); + + int status = -1; + if ( si && si->kcl ) + { + ToVectorV(param+1); + ToVectorV(param+2); + octahedron_t oct; + if ( n_param > 3 ) + { + ToVectorV(param+3); + CreateOctahedron( &oct, ¶m[2].d3, ¶m[3].d3, ¶m[1].d3 ); + } + else + CreateOctahedron( &oct, ¶m[2].d3, 0, ¶m[1].d3 ); + + kcl_tridata_t *td = AppendOctahedronKCL( si->kcl, GetIntV(param), &oct ); + if (td) + { + si->kcl->script_tri_added = true; + status = td - (kcl_tridata_t*)si->kcl->tridata.list; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_createCuboid +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=3); + + int status = -1; + if ( si && si->kcl ) + { + ToVectorV(param+1); + ToVectorV(param+2); + cuboid_t cub; + if ( n_param > 3 ) + { + ToVectorV(param+3); + CreateCuboid( &cub, ¶m[2].d3, ¶m[3].d3, ¶m[1].d3 ); + } + else + CreateCuboid( &cub, ¶m[2].d3, 0, ¶m[1].d3 ); + + kcl_tridata_t *td = AppendCuboidKCL( si->kcl, GetIntV(param), &cub ); + if (td) + { + si->kcl->script_tri_added = true; + status = td - (kcl_tridata_t*)si->kcl->tridata.list; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_createPyramid +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=4); + + int status = -1; + if ( si && si->kcl ) + { + uint i; + for ( i = 1; i < n_param; i++ ) + ToVectorV(param+i); + + kcl_tridata_t *td = AppendPyramidKCL( + si->kcl, + GetIntV(param), + ¶m[1].d3, + ¶m[2].d3, + sizeof(*param), + n_param-2, + false ); + if (td) + { + si->kcl->script_tri_added = true; + status = td - (kcl_tridata_t*)si->kcl->tridata.list; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_createPrism +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter + // * fpar->user_id (bit field) + // bit 0: anti prism + // bit 1: arrow +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=6); + DASSERT(fpar); + + int status = -1; + if ( si && si->kcl ) + { + + // tri$createPrism( kcl_flag, pt1,pt2, ptX, r,n [,base_flag] [,base_height] ) + // tri$createArrow( kcl_flag, pt1,pt2, ptX, r,n, + // arrow_flag [,arrow_size] [,base_flag] [,base_height] ) + + ToVectorV(param+1); + ToVectorV(param+2); + ToVectorV(param+3); + + int base_flag, arrow_flag; + double base_height, arrow_size; + if ( fpar->user_id & 2 ) + { + DASSERT( n_param >= 7 ); + arrow_flag = GetIntV(param+6); + arrow_size = n_param > 7 ? GetDoubleV(param+7) : 1.0; + base_flag = n_param > 8 ? GetIntV(param+8) : -1; + base_height = n_param > 9 ? GetDoubleV(param+9) : 0.0; + } + else + { + arrow_flag = -1; + arrow_size = 0.0; + base_flag = n_param > 6 ? GetIntV(param+6) : -1; + base_height = n_param > 7 ? GetDoubleV(param+7) : 0.0; + } + + kcl_tridata_t *td + = AppendPrismKCL( si->kcl, + GetIntV(param), + ¶m[1].d3, + ¶m[2].d3, + ¶m[3].d3, + GetDoubleV(param+4), + GetIntV(param+5), + fpar->user_id & 1, + base_flag, + base_height, + arrow_flag, + arrow_size, + false ); + if (td) + { + si->kcl->script_tri_added = true; + status = td - (kcl_tridata_t*)si->kcl->tridata.list; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_createJoist +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=5); + + int status = -1; + if ( si && si->kcl ) + { + ToVectorV(param+2); + ToVectorV(param+3); + ToVectorV(param+4); + + kcl_tridata_t *td = AppendJoistKCL( + si->kcl, + GetIntV(param), + GetDoubleV(param+1), + ¶m[2].d3, + ¶m[3].d3, + ¶m[4].d3, + n_param > 5 ? GetIntV(param+5) : 0, + n_param > 6 ? GetIntV(param+6) : 0 ); + if (td) + { + si->kcl->script_tri_added = true; + status = td - (kcl_tridata_t*)si->kcl->tridata.list; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_ptsInCuboid +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=3); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + status = 0; + ToVectorV(param+1); + ToVectorV(param+2); + + double3 *d3 = td->pt; + uint p; + for ( p = 0; p < 3; p++, d3++ ) + { + if ( d3->x >= param[1].x && d3->x <= param[2].x + && d3->y >= param[1].y && d3->y <= param[2].y + && d3->z >= param[1].z && d3->z <= param[2].z + ) + { + status++; + } + } + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_OverlapCube +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=3); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + status = OverlapCubeTri( + &ToVectorV(param+1)->d3, + GetDoubleV(param+2), + td->pt+0, + td->pt+1, + td->pt+2 ); + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_flag +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + DASSERT(fpar); + + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + res->i = td->cur_flag; + if ( n_param > 1 ) + { + const int mode = GetIntV(param+1); + if ( mode == 2 && patch_kcl_flag ) + res->i = patch_kcl_flag[td->in_flag]; + else if ( mode == 1 || mode == 2 ) + res->i = td->in_flag; + } + if (fpar->user_id) + res->i = GET_KCL_TYPE(res->i); + res->mode = VAR_INT; + return ERR_OK; + } + } + + res->mode = VAR_UNSET; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_setFlag +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + status = td->cur_flag; + td->cur_flag = GetIntV(param+1); + // no 'flag changed' status needed + } + } + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_defColor +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + const u32 col0 = GetIntV(param); + Color_t col; + col.a = col0 >> 24; + col.r = col0 >> 16; + col.g = col0 >> 8; + col.b = col0; + + switch (n_param) + { + case 1: + if (!col.a) + col.a = 0xff; + break; + + case 2: + col.a = GetIntV(param+1); + break; + + case 4: + col.a = GetIntV(param+3); + case 3: + col.r = GetIntV(param+0); + col.g = GetIntV(param+1); + col.b = GetIntV(param+2); + break; + } + + int ci; + for ( ci = 0; ci < n_user_color; ci++ ) + if ( user_color[ci].val == col.val ) + break; + + if ( ci >= N_KCL_USER_FLAGS ) + res->i = -1; + else + { + if ( ci == n_user_color ) + user_color[n_user_color++] = col; + res->i = ci + KCL_FLAG_USER_0; + } + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_getColor +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + const uint idx = GetIntV(param); + if ( idx >= KCL_FLAG_USER_0 && idx <= KCL_FLAG_USER_MAX ) + { + Color_t col = user_color[ idx - KCL_FLAG_USER_0]; + res->i = col.a << 24 | col.r << 16 | col.g << 8 | col.b; + res->mode = VAR_INT; + } + else + res->mode = VAR_UNSET; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const struct FuncTable_t func_tab[] = +{ + { 0, 0, "TRI$N", F_tri_n, 0, + "vector", "tri$n()", + "This function returns the number of triangles." }, + + { 2, 2, "TRI$PT", F_tri_pt, 0, + "vector", "tri$pt(tri_index,pt_index)", + "This function returns the point with index 'pt_index' (0..2)" + " of triangle 'tri_index' (0..)." }, + + { 1, 1, "TRI$LENGTH", F_tri_length, 0, + "float", "tri$length(tri_index)", + "This function returns the length of triangle 'tri_index' (0..)." }, + + { 2, 2, "TRI$NORMAL", F_tri_normal, 0, + "vector", "tri$normal(tri_index,norm_index)", + "This function returns the normal with index 'norm_index' (0..3)" + " of triangle 'tri_index' (0..)." }, + + { 3, 4, "TRI$SETPT", F_tri_setPt, 0, + "int", "tri$setPt(tri_index,pt0,pt1,pt2)", + "This function sets the coordinates of all 3 points" + " of the triangle 'tri_index' (0..) to the vectors 'pt*'." }, + { 0, 0, 0, F_tri_setPt, 0, + "int", "tri$setPt(tri_index,pt_index,pt)", + "This function sets the coordinates of the single point" + " with index 'pt_index' (0..2) of triangle 'tri_index' (0..)." + " to the vector 'pt'." }, + + { 2, 3, "TRI$SCALE", F_tri_scale, 0, + "int", "tri$scale(tri_index,scale[,origin])", + "This function scales the triangle 'tri_index' (0..)" + " by the vector 'scale' relative to point 'origin'." + " If the origin is not set, v(0,0,0) is used instead." }, + + { 2, 2, "TRI$SHIFT", F_tri_shift, 0, + "int", "tri$shift(tri_index,shift)", + "This function adds the vector 'shift' to all 3 points" + " of the triangle 'tri_index' (0..)." }, + + { 2, 3, "TRI$HROT", F_tri_hRot, 0, + "int", "tri$hRot(tri_index,degree[,origin])", + "This function rotates the triangle 'tri_index' (0..)" + " horizontal counterclockwise by the angle 'degree'" + " around the point 'origin'." + " If the origin is not set, v(0,0,0) is used instead." }, + + { 1, 1, "TRI$REMOVE", F_tri_remove, 0, + "int", "tri$remove(tri_index)", + "This function marks the triangle 'tri_index' (0..) as 'REMOVED'." + " The return value is -1 for an invalid index," + " 1 if the triangle was already removed or 0 if not." }, + + { 1, 1, "TRI$UNREMOVE", F_tri_unremove, 0, + "int", "tri$unremove(tri_index)", + "This function clears the 'REMOVED' marker of the triangle 'tri_index' (0..)." + " The return value is -1 for an invalid index," + " 1 if the triangle was removed or 0 if not." }, + + { 1, 1, "TRI$ISREMOVED", F_tri_isRemoved, 0, + "int", "tri$isRemoved(tri_index)", + "This returns the status of the 'REMOVED' marker" + " of the triangle 'tri_index' (0..)." + " The return value is -1 for an invalid index," + " 1 if the triangle is marked as removed or 0 if not." }, + + { 4, MAX_FUNC_PARAM, "TRI$CREATE", F_tri_create, 0, + "int", "tri$create(kcl_flag,pt0,pt1,pt2,...)", + "This function appends new triangles at the end of the triangle list" + " with the entered parameters." + " If more than 3 points are entered, all points must be in the same plane" + " and the polygon is split into multiple (N-2) triangles." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 3, 4, "TRI$CREATEOCTAHEDRON", F_tri_createOctahedron, 0, + "int", "tri$createOctahedron(kcl_flag,pos,radius[,rot])", + "This function creates a octahedron by adding 8 triangles." + " If 'radius' is a scalar, a regular octahedron created." + " Otherwise it is a vector and define different radiuses for each axis." + " If 'rot' is set, the octahedron is rotated." + " As last operation the center (origin) of the cube is shifted to 'pos'." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 3, 4, "TRI$CREATECUBOID", F_tri_createCuboid, 0, + "int", "tri$createCuboid(kcl_flag,pos,size[,rot])", + "This function creates a cuboid by adding 12 triangles." + " If 'size' is a scalar, a cube with the entered edge length is created." + " If 'rot' is set, the cube is rotated." + " As last operation the center (origin) of the cube is shifted to 'pos'." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 4, MAX_FUNC_PARAM, "TRI$CREATEPYRAMID", F_tri_createPyramid, 0, + "int", "tri$createPyramid(kcl_flag,apex,pt1,pt2,...)", + "This function creates a pyramid by adding N triangles." + " 'pt1'..'ptN' descibribes the base polygon, which is not created." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 6, 8, "TRI$CREATEPRISM", F_tri_createPrism, 0, + "int", "tri$createPrism(kcl_flag,pt1,pt2,ptX,r,n[,base_flag][,base_height])", + "This function creates a prism with radius 'r' and N faces" + " from 'pt1' to 'pt2' by adding 2*N triangles." + " The point 'ptX' is used for the orientation (direction of first edge)." + " If 'base_flag' is set and not @-1@, the base is printed with ths kcl flag value." + " If 'base_height' is set, a pyramid is drawn as base." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 6, 8, "TRI$CREATEANTIPRISM", F_tri_createPrism, 1, + "int", "tri$createAntiPrism(kcl_flag,pt1,pt2,ptX,r,n[,base_flag][,base_height])", + "This function creates an antiprism with radius 'r' and N faces" + " from 'pt1' to 'pt2' by adding 2*N triangles." + " The point 'ptX' is used for the orientation (direction of first edge)." + " If 'base_flag' is set and not @-1@, the base is printed with ths kcl flag value." + " If 'base_height' is set, a pyramid is drawn as base." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 7, 10, "TRI$CREATEARROW", F_tri_createPrism, 2, + "int", "tri$createArrow(flag,pt1,pt2,ptX,r,n,a_flag[,a_size][,b_flag][,b_ht])", + "This function creates a prism based arrow with radius 'r' and N faces" + " from 'pt1' to 'pt2', where 'pt2' is the place of the arrowhead." + " The point 'ptX' is used for the orientation (direction of first edge)." + " 'flag', 'a_flag' and 'b_flag' are KCL flags to define the color." + " 'a_size' is the addition size factor of the arrowhead, the default is 1.0." + " If 'b_flag' is not set or @-1@, no base is drawn." + " If 'base_height' is set, a pyramid is drawn as base." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 7, 10, "TRI$CREATEANTIARROW", F_tri_createPrism, 3, + "int", "tri$createAntiArrow(flag,pt1,pt2,ptX,r,n,a_flag[,a_size][,b_flag][,b_ht])", + "This function creates an antiprism based arrow with radius 'r' and N faces" + " from 'pt1' to 'pt2', where 'pt2' is the place of the arrowhead." + " The point 'ptX' is used for the orientation (direction of first edge)." + " 'flag', 'a_flag' and 'b_flag' are KCL flags to define the color." + " 'a_size' is the addition size factor of the arrowhead, the default is 1.0." + " If 'b_flag' is not set or @-1@, no base is drawn." + " If 'base_height' is set, a pyramid is drawn as base." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 5, 6, "TRI$CREATEJOIST", F_tri_createJoist, 0, + "int", "tri$createJoist(kcl_flag,length,pt1,pt2,pt_dir[,n_marker][,mark5_flag])", + "This function appends a triangular joist from 'p1' to 'p2'" + " to at the end of the triangle list." + " 'length' the length of the body" + " and 'pt_dir' is a helper point for its direction." + " If 'n_marker' is >0, markers representing the number are added." + " If 'mark5_flag' is >0, every 5 markers are replaced by one of the entered flag." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 3, 3, "TRI$PTSINCUBOID", F_tri_ptsInCuboid, 0, + "int", "tri$ptsInCuboid(tri_index,cube_min,cube_max)", + " Parameters 'cube_*' are converted to vectors." + " They describe 2 diagonal corners of a rectangular cuboid," + " assuming that 'cube_min<=cube_max' is true for each coordinate." + " The functions returns the number of points" + " of the triangle 'tri_index' (0..)," + " that are inside of the cube including the border." + " If 'tri_index' is invalid, -1 is returned." }, + + #ifdef TEST // [[2do]] + { 3, 3, "TRI$OVERLAPCUBE", F_tri_OverlapCube, 1, + "int", "tri$OverlapCube(tri_index,cube_mid,cube_width)", + " 'tri_index' (0..) is a triangle index." + " 'cube_mid' is the middle point of a cube" + " and 'cube_width' the length of its edges." + " The function returns -1 for an invalid triangle index," + " or 1, if the cube and the triangle are overlapped." + " Otherwise 0 is returned." }, + #endif + + { 1, 2, "TRI$FLAG", F_tri_flag, 0, + "int", "tri$flag(tri_index[,mode])", + "This function returns the KCL flag of the triangle 'tri_index' (0..)." + " If mode is '1', the original source value is returned." + " If mode is '2', the original source value " + " transformed by --kcl-flag is returned;" + " if --kcl-flag is not set, it is the same as mode '1'." + " Otherwise the current value is returned." + " The current value is initialized by the value of mode '2'." + " Values >0xffff are possible and represent internal drawings." }, + + { 1, 2, "TRI$TYPE", F_tri_flag, 1, + "int", "tri$type(tri_index[,mode])", + "This function returns the KCL type of the triangle 'tri_index' (0..)." + " It works like tri$flag(), but the flag is transformed to a type." + " The return value is in the range of 0x00..0x1f (5 lowest bits of the flag)," + " if it is a real KCL flag (<0x10000), or -1 otherwise (special type)." }, + + { 2, 2, "TRI$SETFLAG", F_tri_setFlag, 0, + "int", "tri$setFlag(tri_index,new_flag)", + "This function sets the new KCL flag of triangle 'tri_index' (0..)." + " The function returns -1 for an invalid index" + " or the previous value of the KCL flag." }, + + { 1, 4, "TRI$DEFCOLOR", F_tri_defColor, 0, + "int", "tri$defColor(argb[,alpha])", + "Define a new color." + " 'ARGB' is a bytes coded color: alpha,red,green,blue." + " If 'ALPHA' is not set, the highest byte of 'ARGB' is used." + " If this highest byte is null, 0xff (no transparency) is used." + " The function returns -1, if already 200 colors defined." + " Otherwise it returns the virtual KCL flag for the user defined color." }, + { 0, 0, 0, F_tri_defColor, 0, + "int", "tri$defColor(red,green,blue[,alpha])", + "Define a new color." + " If ALPHA is not set, 0xff (no transparency) is used." + " For the 3 colors, values between 0x00 and 0xff are expected." + " The function returns -1, if already 200 colors defined." + " Otherwise it returns the virtual KCL flag for the user defined color." }, + + { 1, 1, "TRI$GETCOLOR", F_tri_getColor, 0, + "int", "tri$getColor(index)", + "This function returns the color defined by a previous tri$defColor() call." + " INDEX is the virtual KCL flag for the user defined color." + " On error, the value NONE (type 'undefined') is returned." }, + + {0,0,0,0,0,0,0,0} +}; + +// [[2do]] ideas: +// tri$isAdded(tindex) : true, if triangle is marked as 'added' +// tri$isFixed(tindex) : true, if triangle is marked as 'fixed' +// tri$isUsed(tindex) : true, if triangle is marked as 'used' + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// setup kcl parser /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const VarMap_t * SetupVarsKCL() +{ + static VarMap_t vm = {0}; + if (!vm.used) + { + //---- define functions + + DefineParserFuncTab(func_tab,FF_KCL); + SetupReferenceKCL(&vm); + + #if 0 + //--- setup integer variables + + struct inttab_t { ccp name; int val; }; + static const struct inttab_t inttab[] = + { + {0,0} + }; + + const struct inttab_t * ip; + for ( ip = inttab; ip->name; ip++ ) + DefineIntVar(&vm,ip->name,ip->val); + #endif + + //--- setup basic parser variables + + DefineParserVars(&vm); + } + + return &vm; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + |
