1#!perl -w -I \temp\1\bin 2# 3#this module handles ogf models with OGF_format_version 4, from build 1893 to 3456+. Last Edit: 26.06.2011 4################################################################################################################### 5package stkutils::ogf; 6use strict; 7#use diagnostics; 8use stkutils::debug; 9use stkutils::data_packet; 10use stkutils::chunked_file; 11use POSIX qw(ctime); 12use constant esmFX => 0x1; 13use constant esmStopAtEnd => 0x2; 14use constant esmNoMix => 0x4; 15use constant esmSyncPart => 0x8; 16use constant esmHasMotionMarks => 0x10; # guessed name 17# vertex constants 18use constant OGF_VERTEXFORMAT_FVF_OLD => 0x112; 19use constant OGF_VERTEXFORMAT_FVF_1L => 0x12071980; 20use constant OGF_VERTEXFORMAT_FVF_2L => 0x240e3300; # skeletonX_pm 21use constant OGF_VERTEXFORMAT_FVF_NL => 0x36154c80; 22use constant OGF_VERTEXFORMAT_FVF_1_CS => 1; 23use constant OGF_VERTEXFORMAT_FVF_2_CS => 2; 24use constant OGF_VERTEXFORMAT_FVF_3_CS => 3; 25use constant OGF_VERTEXFORMAT_FVF_4_CS => 4; 26# loddef constants 27use constant OGF3_HOPPE_HEADER => 1; 28use constant OGF3_HOPPE_VERT_SPLITS => 2; 29use constant OGF3_HOPPE_FIX_FACES => 3; 30# motion constants 31use constant KPF_T_PRESENT => 0x01; 32use constant KPF_R_ABSENT => 0x02; 33use constant KPF_T_HQ => 0x04; 34use constant S_POINTS => 0x01; 35use constant S_NORMALS => 0x02; 36use constant S_TEXCOORDS => 0x04; 37use constant S_LIGHTMAPS => 0x08; 38use constant S_INFLUENCES => 0x10; 39use constant S_COLORS => 0x20; 40use constant UINT32_MAX => 0xffffffff; 41my %chunks_loaded = ( 423 => {'OGF_HEADER' => 0x1, 43 'OGF_TEXTURE' => 0x2, 44 'OGF_TEXTURE_L' => 0x4, 45 'OGF_CHILD_REFS' => 0x8, 46 'OGF_BBOX' => 0x10, 47 'OGF_VERTICES' => 0x20, 48 'OGF_INDICES' => 0x40, 49 'OGF_LODDATA' => 0x80, 50 'OGF_VCONTAINER' => 0x100, 51 'OGF_BSPHERE' => 0x200, 52 'OGF_CHILDREN_L' => 0x400, 53 'OGF_S_BONE_NAMES' => 0x800, 54 'OGF_S_MOTIONS_0' => 0x1000, 55 'OGF_DPATCH' => 0x2000, 56 'OGF_S_LODS' => 0x4000, 57 'OGF_CHILDREN' => 0x8000, 58 'OGF_S_SMPARAMS_0' => 0x10000, 59 'OGF_ICONTAINER' => 0x20000, 60 'OGF_S_SMPARAMS_1' => 0x40000, 61 'OGF_LODDEF2' => 0x80000, 62 'OGF_TREEDEF2' => 0x100000, 63 'OGF_S_IKDATA_0' => 0x200000, 64 'OGF_S_USERDATA' => 0x400000, 65 'OGF_S_IKDATA_1' => 0x800000, 66 'OGF_S_MOTIONS_1' => 0x1000000, 67 'OGF_S_DESC' => 0x2000000, 68 'OGF_S_IKDATA_2' => 0x4000000, 69 'OGF_S_MOTION_REFS_0' => 0x8000000}, 704 => {'OGF_HEADER' => 0x1, 71 'OGF_TEXTURE' => 0x2, 72 'OGF_VERTICES' => 0x4, 73 'OGF_INDICES' => 0x8, 74 'OGF_P_MAP' => 0x10, 75 'OGF_SWIDATA' => 0x20, 76 'OGF_VCONTAINER' => 0x40, 77 'OGF_ICONTAINER' => 0x80, 78 'OGF_CHILDREN' => 0x100, 79 'OGF_CHILDREN_L' => 0x200, 80 'OGF_LODDEF2' => 0x400, 81 'OGF_TREEDEF2' => 0x800, 82 'OGF_S_BONE_NAMES' => 0x1000, 83 'OGF_S_MOTIONS_1' => 0x2000, 84 'OGF_S_SMPARAMS_1' => 0x4000, 85 'OGF_S_IKDATA_2' => 0x8000, 86 'OGF_S_USERDATA' => 0x10000, 87 'OGF_S_DESC' => 0x20000, 88 'OGF_S_MOTION_REFS_0' => 0x40000, 89 'OGF_SWICONTAINER' => 0x80000, 90 'OGF_GCONTAINER' => 0x100000, 91 'OGF_FASTPATH' => 0x200000, 92 'OGF_S_LODS' => 0x400000, 93 'OGF_S_MOTION_REFS_1' => 0x800000, 94 'OGF_TEXTURE_L' => 0x1000000, 95 'OGF_CHILD_REFS' => 0x2000000, 96 'OGF_BBOX' => 0x4000000, 97 'OGF_LODDATA' => 0x8000000, 98 'OGF_BSPHERE' => 0x10000000, 99 'OGF_DPATCH' => 0x20000000, 100 'OGF_S_LODS_CSKY' => 0x40000000,}, 101); 102my %mt_names = ( #names of appropriate engine classes 1033 => { 104 0x0 => 'MT_NORMAL', #FVisual 105 0x1 => 'MT_HIERRARHY', #FHierrarhyVisual 106 0x2 => 'MT_PROGRESSIVE', #FProgressiveFixedVisual 107 0x3 => 'MT_SKELETON_GEOMDEF_PM', #CSkeletonX_PM 108 0x4 => 'MT_SKELETON_ANIM', #CKinematics //CKinematicsAnimated since build 1510 109 0x6 => 'MT_DETAIL_PATCH', #FDetailPatch 110 0x7 => 'MT_SKELETON_GEOMDEF_ST', #CSkeletonX_ST 111 0x8 => 'MT_CACHED', #FCached 112 0x9 => 'MT_PARTICLE', #CPSVisual 113 0xa => 'MT_PROGRESSIVE2', #FProgressive 114 0xb => 'MT_LOD', #FLod 115 0xc => 'MT_TREE', #FTreeVisual 116 0xd => 'MT_PARTICLE_EFFECT', #PS::CParticleEffect, not used //introduced in build 1510 117 0xe => 'MT_PARTICLE_GROUP', #PS::CParticleGroup, not used //introduced in build 1510 118 0xf => 'MT_SKELETON_RIGID', #CKinematics //introduced in build 1510 119 }, 1204 => { 121 0x0 => 'MT_NORMAL', #FVisual 122 0x1 => 'MT_HIERRARHY', #FHierrarhy_Visual 123 0x2 => 'MT_PROGRESSIVE', #FProgressive 124 0x3 => 'MT_SKELETON_ANIM', #CKinematics_Animated #CSkeletonAnimated before 2205 125 0x4 => 'MT_SKELETON_GEOMDEF_PM', #CSkeletonX_PM 126 0x5 => 'MT_SKELETON_GEOMDEF_ST', #CSkeletonX_ST 127 0x6 => 'MT_LOD', #FLod 128 0x7 => 'MT_TREE_ST', #FTreeVisual_ST 129 0x8 => 'MT_PARTICLE_EFFECT', #CParticleEffect, not used 130 0x9 => 'MT_PARTICLE_GROUP', #CParticleGroup, not used 131 0xa => 'MT_SKELETON_RIGID', #CKinematics 132 0xb => 'MT_TREE_PM', #FTreeVisual_PM #introduced in build 1957 133 }, 134); 135my %chunk_names = ( 1363 => { 137 'OGF_HEADER' => 0x1, 138 'OGF_TEXTURE' => 0x2, 139 'OGF_TEXTURE_L' => 0x3, 140 'OGF_CHILD_REFS' => 0x5, 141 'OGF_BBOX' => 0x6, 142 'OGF_VERTICES' => 0x7, 143 'OGF_INDICES' => 0x8, 144 'OGF_LODDATA' => 0x9, 145 'OGF_VCONTAINER' => 0xa, 146 'OGF_BSPHERE' => 0xb, 147 'OGF_CHILDREN_L' => 0xc, 148 'OGF_S_BONE_NAMES' => 0xd, 149 'OGF_S_MOTIONS_0' => 0xe, 150 'OGF_DPATCH' => 0xf, 151 'OGF_S_LODS' => 0x10, 152 'OGF_CHILDREN' => 0x11, 153 'OGF_S_SMPARAMS_0' => 0x12, 154 'OGF_ICONTAINER' => 0x13, 155 'OGF_S_SMPARAMS_1' => 0x14, 156 'OGF_LODDEF2' => 0x15, 157 'OGF_TREEDEF2' => 0x16, 158 'OGF_S_IKDATA_0' => 0x17, 159 'OGF_S_USERDATA' => 0x18, 160 'OGF_S_IKDATA_1' => 0x19, 161 'OGF_S_MOTIONS_1' => 0x1A, 162 'OGF_S_DESC' => 0x1B, 163 'OGF_S_IKDATA_2' => 0x1C, 164 'OGF_S_MOTION_REFS_0' => 0x1D, 165 }, 1664 => { 167 'OGF_HEADER' => 0x1, 168 'OGF_TEXTURE' => 0x2, 169 'OGF_VERTICES' => 0x3, 170 'OGF_INDICES' => 0x4, 171 'OGF_P_MAP' => 0x5, #used before build 1925 172 'OGF_SWIDATA' => 0x6, 173 'OGF_VCONTAINER' => 0x7, #used before build 2205 174 'OGF_ICONTAINER' => 0x8, #used before build 2205 175 'OGF_CHILDREN' => 0x9, 176 'OGF_CHILDREN_L' => 0xa, 177 'OGF_LODDEF2' => 0xb, 178 'OGF_TREEDEF2' => 0xc, 179 'OGF_S_BONE_NAMES' => 0xd, 180 'OGF_S_MOTIONS_1' => 0xe, #used before build 2205 181 'OGF_S_SMPARAMS_1' => 0xf, #used before build 2205 182 'OGF_S_IKDATA_2' => 0x10, 183 'OGF_S_USERDATA' => 0x11, 184 'OGF_S_DESC' => 0x12, 185 'OGF_S_MOTION_REFS_0' => 0x13, 186 'OGF_SWICONTAINER' => 0x14, #introduced in build 1957 187 'OGF_GCONTAINER' => 0x15, #introduced in build 2205 188 'OGF_FASTPATH' => 0x16, #introduced in build 2205 189 'OGF_S_LODS' => 0x17, #introduced in build ???? 190 'OGF_S_MOTION_REFS_1' => 0x18, #introduced in Clear Sky 191 }, 192); 193sub new { 194 my $class = shift; 195 my $self = {}; 196 $self->{bbox} = {}; 197 $self->{bsphere} = {}; 198 $self->{c_scale} = {}; 199 $self->{c_bias} = {}; 200 $self->{fast_swidata} = {}; 201 $self->{loaded_chunks} = 0; 202 bless $self, $class; 203 return $self; 204} 205sub read { 206 my $self = shift; 207 my ($cf) = @_; 208 if (!($cf->find_chunk(0x1))) { 209 stkutils::debug::fail(__PACKAGE__.'::read', __LINE__, '$cf->find_chunk(OGF_HEADER)', 'cannot find OGF_HEADER'); 210 } 211 $self->read_header($cf); 212 $cf->close_found_chunk(); 213 SWITCH: { 214 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_NORMAL' && do { $self->read_visual($cf); last SWITCH; }; 215 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_HIERRARHY' && do { $self->read_hierrarhy_visual($cf); last SWITCH; }; 216 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_PROGRESSIVE' && do { $self->read_progressive($cf); last SWITCH; }; 217 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_SKELETON_ANIM' && do { $self->read_kinematics_animated($cf); last SWITCH; }; 218 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_SKELETON_GEOMDEF_PM' && do { $self->read_skeletonx_pm($cf); last SWITCH; }; 219 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_SKELETON_GEOMDEF_ST' && do { $self->read_skeletonx_st($cf); last SWITCH; }; 220 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_PROGRESSIVE2' && do { $self->read_progressive2($cf); last SWITCH; }; 221 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_LOD' && do { $self->read_lod($cf); last SWITCH; }; 222 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_TREE' && do { $self->read_tree_visual_st($cf); last SWITCH; }; 223 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_TREE_ST' && do { $self->read_tree_visual_st($cf); last SWITCH; }; 224 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_SKELETON_RIGID' && do { $self->read_kinematics($cf); last SWITCH; }; 225 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_TREE_PM' && do { $self->read_tree_visual_pm($cf); last SWITCH; }; 226 stkutils::debug::fail(__PACKAGE__.'::read', __LINE__, '', 'unexpected model type '.$self->{type}); 227 } 228} 229sub read_header { 230 my $self = shift; 231 my ($cf) = @_; 232 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 233 ($self->{format_version}, $self->{type}, $self->{shader_id}) = $packet->unpack('CCv'); 234 if ($self->{format_version} == 4) { 235 $self->read_bbox($packet); 236 $self->read_bsphere($packet); 237 } 238 $self->set_loaded('OGF_HEADER'); 239} 240sub read_render_visual { 241 my $self = shift; 242 my ($cf) = @_; 243 if ($self->{format_version} == 3) { 244 if ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_BBOX'})) { 245 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 246 $self->read_bbox($packet); 247 $cf->close_found_chunk(); 248 } else { 249 stkutils::debug::fail(__PACKAGE__.'::read_render_visual', __LINE__, '$cf->find_chunk(OGF_BBOX)', 'cannot find OGF_BBOX'); 250 } 251 } 252 if ($self->{format_version} == 3 && $cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_BSPHERE'})) { 253 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 254 $self->read_bsphere($packet); 255 $cf->close_found_chunk(); 256 } 257 if ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_S_DESC'})) { 258 $self->read_s_desc($cf); 259 $cf->close_found_chunk(); 260 } 261 if ($self->{format_version} == 3 && $cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_TEXTURE_L'})) { 262 $self->read_texture_l($cf); 263 $cf->close_found_chunk(); 264 } elsif ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_TEXTURE'})) { 265 $self->read_texture($cf); 266 $cf->close_found_chunk(); 267 } 268}; 269sub read_visual { 270 my $self = shift; 271 my ($cf) = @_; 272 $self->read_render_visual($cf); 273 if ($self->{format_version} == 4 && $cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_GCONTAINER'})) { 274 $self->read_gcontainer($cf); 275 $cf->close_found_chunk(); 276 if ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_FASTPATH'})) { 277 $self->read_fastpath($cf) ; 278 $cf->close_found_chunk(); 279 } 280 } 281 if ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_VCONTAINER'})) { 282 $self->read_vcontainer($cf); 283 $cf->close_found_chunk(); 284 } elsif ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_VERTICES'})) { 285 $self->read_vertices($cf); 286 $cf->close_found_chunk(); 287 } 288 if ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_ICONTAINER'})) { 289 $self->read_icontainer($cf); 290 $cf->close_found_chunk(); 291 } elsif ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_INDICES'})) { 292 $self->read_indices($cf); 293 $cf->close_found_chunk(); 294 } 295} 296sub read_hierrarhy_visual { 297 my $self = shift; 298 my ($cf) = @_; 299 $self->read_render_visual($cf); 300 if ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_CHILDREN_L'})) { 301 $self->read_children_l($cf); 302 } elsif ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_CHILDREN'})) { 303 $self->read_children($cf); 304 } elsif ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_CHILD_REFS'})) { 305 $self->read_child_refs($cf); 306 } else { 307 stkutils::debug::fail(__PACKAGE__.'::read_hierrarhy_visual', __LINE__, '', 'Invalid visual, no children'); 308 } 309 $cf->close_found_chunk(); 310}; 311sub read_progressive { 312 my $self = shift; 313 my ($cf) = @_; 314 $self->read_visual($cf); 315 if ($self->{format_version} == 4 && $cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_SWIDATA'})) { 316 $self->read_swidata($cf); 317 $cf->close_found_chunk(); 318 } else { 319 if ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_LODDATA'})) { 320 $self->read_loddata($cf); 321 $cf->close_found_chunk(); 322 } else { 323 stkutils::debug::fail(__PACKAGE__.'::read_progressive', __LINE__, '', 'Invalid visual, no loddata'); 324 } 325 } 326} 327sub read_kinematics { 328 my $self = shift; 329 my ($cf) = @_; 330 $self->read_hierrarhy_visual($cf); 331 if ($self->{format_version} == 4) { 332 my $size = $cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_S_LODS'}); 333 if ($size) { 334 if ($size < 0x100) { 335 $self->read_s_lods_csky($cf); 336 } else { 337 $self->read_s_lods($cf); 338 } 339 $cf->close_found_chunk(); 340 } 341 } 342 if ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_S_USERDATA'})) { 343 $self->read_s_userdata($cf); 344 $cf->close_found_chunk(); 345 } 346 if ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_S_BONE_NAMES'})) { 347 $self->read_s_bone_names($cf); 348 $cf->close_found_chunk(); 349 } else { 350 stkutils::debug::fail(__PACKAGE__.'::read_kinematics', __LINE__, '$cf->find_chunk('.$chunk_names{$self->{format_version}}{'OGF_S_BONE_NAMES'}.')', 'cannot find OGF_S_BONE_NAMES'); 351 } 352 if ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_S_IKDATA_2'})) { 353 $self->read_s_ikdata($cf, 2); 354 $cf->close_found_chunk(); 355 } elsif ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_S_IKDATA_1'})) { 356 $self->read_s_ikdata($cf, 1); 357 $cf->close_found_chunk(); 358 } elsif ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_S_IKDATA_0'})) { 359 stkutils::debug::fail(__PACKAGE__.'::read_kinematics', __LINE__, '', 'sorry, ik data of builds 15xx not implemented yet'); 360 $self->read_s_ikdata($cf, 0); 361 $cf->close_found_chunk(); 362 } 363} 364sub read_kinematics_animated { 365 my $self = shift; 366 my ($cf) = @_; 367 $self->read_kinematics($cf); 368 if ($self->{format_version} == 4 && $cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_S_MOTION_REFS_1'})) { 369 $self->read_smotion_refs_1($cf); 370 $cf->close_found_chunk(); 371 return; 372 } elsif ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_S_MOTION_REFS_0'})) { 373 $self->read_smotion_refs_0($cf); 374 $cf->close_found_chunk(); 375 return; 376 } elsif ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_S_SMPARAMS_1'})) { 377 $self->read_s_smparams($cf, 1); 378 $cf->close_found_chunk(); 379 } elsif ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_S_SMPARAMS_0'})) { 380 $self->read_s_smparams($cf, 0); 381 $cf->close_found_chunk(); 382 } 383 if ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_S_MOTIONS_1'})) { 384 $self->read_smotions($cf, 1); 385 $cf->close_found_chunk(); 386 } elsif ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_S_MOTIONS_0'})) { 387 $self->read_smotions($cf, 0); 388 $cf->close_found_chunk(); 389 } else { 390 stkutils::debug::fail(__PACKAGE__.'::read_kinematics_animated', __LINE__, '', 'Invalid visual, no motions'); 391 } 392} 393sub read_skeletonx { 394 my $self = shift; 395 my ($cf) = @_; 396 if ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_VERTICES'})) { 397 $cf->close_found_chunk(); 398 return; 399 } else { 400 stkutils::debug::fail(__PACKAGE__.'::read_skeletonx', __LINE__, '$cf->find_chunk('.$chunk_names{$self->{format_version}}{'OGF_VERTICES'}.')', 'cannot find OGF_VERTICES'); 401 } 402} 403sub read_skeletonx_pm { 404 my $self = shift; 405 my ($cf) = @_; 406 $self->read_skeletonx($cf); 407 $self->read_progressive($cf); 408} 409sub read_skeletonx_st { 410 my $self = shift; 411 my ($cf) = @_; 412 $self->read_skeletonx($cf); 413 $self->read_visual($cf); 414} 415sub read_progressive2 { 416 my $self = shift; 417 my ($cf) = @_; 418 $self->read_render_visual($cf); 419 if ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_LODS'})) { 420 $self->read_s_lods($cf); 421 $cf->close_found_chunk(); 422 } else { 423 stkutils::debug::fail('ogf::read_progressive', 417, 'Invalid visual, no lods'); 424 } 425} 426sub read_lod { 427 my $self = shift; 428 my ($cf) = @_; 429 $self->read_hierrarhy_visual($cf); 430 if ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_LODDEF2'})) { 431 $self->read_loddef2($cf); 432 $cf->close_found_chunk(); 433 } else { 434 stkutils::debug::fail('ogf::read_lod', 428, 'cannot find chunk OGF_LODDEF2'); 435 } 436} 437sub read_tree_visual { 438 my $self = shift; 439 my ($cf) = @_; 440 $self->read_visual($cf); 441 if ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_TREEDEF2'})) { 442 $self->read_treedef2($cf); 443 $cf->close_found_chunk(); 444 } else { 445 stkutils::debug::fail(__PACKAGE__.'::read_tree_visual', __LINE__, '!$cf->find_chunk(OGF_TREEDEF2)', 'cannot find OGF_TREEDEF2'); 446 } 447} 448sub read_tree_visual_st { 449 my $self = shift; 450 my ($cf) = @_; 451 $self->read_tree_visual($cf); 452} 453sub read_tree_visual_pm { 454 my $self = shift; 455 my ($cf) = @_; 456 $self->read_tree_visual($cf); 457 if ($cf->find_chunk($chunk_names{$self->{format_version}}{'OGF_SWICONTAINER'})) { 458 $self->read_swicontainer($cf); 459 $cf->close_found_chunk(); 460 } else { 461 stkutils::debug::fail('ogf::read_tree_visual_pm', 457, 'cannot find OGF_SWICONTAINER'); 462 } 463} 464sub read_bbox { 465 my $self = shift; 466 my ($packet) = @_; 467 @{$self->{bbox}->{min}} = $packet->unpack('f3'); 468 @{$self->{bbox}->{max}} = $packet->unpack('f3'); 469 $self->set_loaded('OGF_BBOX'); 470} 471sub read_bsphere { 472 my $self = shift; 473 my ($packet) = @_; 474 @{$self->{bsphere}->{c}} = $packet->unpack('f3'); 475 ($self->{bsphere}->{r}) = $packet->unpack('f'); 476 $self->set_loaded('OGF_BSPHERE'); 477} 478sub read_s_desc { 479 my $self = shift; 480 my ($cf) = @_; 481 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 482 ($self->{ogf_object}, $self->{ogf_creator}, 483 $self->{unk}, 484 $self->{creator}, $self->{create_time}, 485 $self->{editor}, $self->{edit_time}) = $packet->unpack('Z*Z*VZ*VZ*V'); 486 stkutils::debug::fail('ogf::read_s_desc', 486, 'there some data in packet left: '.$packet->length()) unless $packet->length() == 0; 487 $self->set_loaded('OGF_S_DESC'); 488} 489sub read_texture_l { 490 my $self = shift; 491 my ($cf) = @_; 492 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 493 ($self->{texture_id}, $self->{shader_id}) = $packet->unpack('VV'); 494 $self->set_loaded('OGF_TEXTURE_L'); 495 stkutils::debug::fail('ogf::read_texture_l', 495, 'there some data in packet left: '.$packet->length()) unless $packet->length() == 0; 496} 497sub read_texture { 498 my $self = shift; 499 my ($cf) = @_; 500 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 501 ($self->{texture_name}, $self->{shader_name}) = $packet->unpack('Z*Z*'); 502 $self->set_loaded('OGF_TEXTURE'); 503 stkutils::debug::fail('ogf::read_texture', __LINE__, 'there some data in packet left: '.$packet->length()) unless $packet->length() == 0; 504} 505sub read_gcontainer { 506 my $self = shift; 507 my ($cf) = @_; 508 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 509 ($self->{ext_vb_index}, $self->{ext_vb_offset}, $self->{ext_vb_size}, 510 $self->{ext_ib_index}, $self->{ext_ib_offset}, $self->{ext_ib_size}) = $packet->unpack('VVVVVV'); 511 $self->set_loaded('OGF_GCONTAINER'); 512} 513sub read_fastpath { 514 my $self = shift; 515 my ($cf) = @_; 516 $self->{m_fast} = $cf->r_chunk_data(); 517# $cf->find_chunk(0x15); 518# my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 519# @{$self->{fast_gcontainer}} = $packet->unpack('V6'); 520# $cf->close_found_chunk(); 521# if ($cf->find_chunk(0x6)) { 522# read_swidata($self->{fast_swidata}, $cf); 523# $cf->close_found_chunk(); 524# } 525 $self->set_loaded('OGF_FASTPATH'); 526} 527sub read_vcontainer { 528 my $self = shift; 529 my ($cf) = @_; 530 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 531 ($self->{ext_vb_index}, $self->{ext_vb_offset}, $self->{ext_vb_size}) = $packet->unpack('VVV'); 532 $self->set_loaded('OGF_VCONTAINER'); 533} 534sub read_vertices { 535 my $self = shift; 536 my ($cf) = @_; 537 my $packet = stkutils::data_packet->new($cf->r_chunk_data(8)); 538 ($self->{vertex_format}, $self->{vertex_count}) = $packet->unpack('VV'); 539 if ($self->{vertex_format} == OGF_VERTEXFORMAT_FVF_OLD) { 540 for (my $i = 0; $i < $self->{vertex_count}; $i++) { 541 my $packet = stkutils::data_packet->new($cf->r_chunk_data(32)); 542 my $vertice = {}; 543 @{$vertice->{point}} = $packet->unpack('f3'); 544 @{$vertice->{normal}} = $packet->unpack('f3'); 545 @{$vertice->{textcoords}} = $packet->unpack('f2'); 546 push @{$self->{vertices}}, $vertice; 547 } 548 } elsif ($self->{format_version} == 3 && $self->{vertex_format} == OGF_VERTEXFORMAT_FVF_1L) { 549 for (my $i = 0; $i < $self->{vertex_count}; $i++) { 550 my $packet = stkutils::data_packet->new($cf->r_chunk_data(36)); 551 my $vertice = {}; 552 @{$vertice->{point}} = $packet->unpack('f3'); 553 @{$vertice->{normal}} = $packet->unpack('f3'); 554 @{$vertice->{textcoords}} = $packet->unpack('f2'); 555 ($vertice->{matrix}) = $packet->unpack('V'); 556 push @{$self->{vertices}}, $vertice; 557 } 558 } elsif ($self->{vertex_format} == OGF_VERTEXFORMAT_FVF_1L or $self->{vertex_format} == OGF_VERTEXFORMAT_FVF_1_CS) { 559 for (my $i = 0; $i < $self->{vertex_count}; $i++) { 560 my $packet = stkutils::data_packet->new($cf->r_chunk_data(60)); 561 my $vertice = {}; 562 @{$vertice->{point}} = $packet->unpack('f3'); 563 @{$vertice->{normal}} = $packet->unpack('f3'); 564 @{$vertice->{t}} = $packet->unpack('f3'); 565 @{$vertice->{b}} = $packet->unpack('f3'); 566 @{$vertice->{textcoords}} = $packet->unpack('f2'); 567 ($vertice->{matrix}) = $packet->unpack('V'); 568 push @{$self->{vertices}}, $vertice; 569 } 570 } elsif ($self->{vertex_format} == OGF_VERTEXFORMAT_FVF_2L or $self->{vertex_format} == OGF_VERTEXFORMAT_FVF_2_CS) { 571 for (my $i = 0; $i < $self->{vertex_count}; $i++) { 572 my $packet = stkutils::data_packet->new($cf->r_chunk_data(64)); 573 my $vertice = {}; 574 ($vertice->{matrix0}, $vertice->{matrix1}) = $packet->unpack('vv'); 575 @{$vertice->{point}} = $packet->unpack('f3'); 576 @{$vertice->{normal}} = $packet->unpack('f3'); 577 @{$vertice->{t}} = $packet->unpack('f3'); 578 @{$vertice->{b}} = $packet->unpack('f3'); 579 ($vertice->{w}) = $packet->unpack('f'); 580 @{$vertice->{textcoords}} = $packet->unpack('f2'); 581 push @{$self->{vertices}}, $vertice; 582 } 583 } elsif ($self->{vertex_format} == OGF_VERTEXFORMAT_FVF_3_CS) { 584 for (my $i = 0; $i < $self->{vertex_count}; $i++) { 585 my $packet = stkutils::data_packet->new($cf->r_chunk_data(70)); 586 my $vertice = {}; 587 ($vertice->{matrix0}, $vertice->{matrix1}, $vertice->{matrix2}) = $packet->unpack('vvv'); 588 @{$vertice->{point}} = $packet->unpack('f3'); 589 @{$vertice->{normal}} = $packet->unpack('f3'); 590 @{$vertice->{t}} = $packet->unpack('f3'); 591 @{$vertice->{b}} = $packet->unpack('f3'); 592 ($vertice->{w0}, $vertice->{w1}) = $packet->unpack('ff'); 593 @{$vertice->{textcoords}} = $packet->unpack('f2'); 594 push @{$self->{vertices}}, $vertice; 595 } 596 } elsif ($self->{vertex_format} == OGF_VERTEXFORMAT_FVF_4_CS) { 597 for (my $i = 0; $i < $self->{vertex_count}; $i++) { 598 my $packet = stkutils::data_packet->new($cf->r_chunk_data(76)); 599 my $vertice = {}; 600 ($vertice->{matrix0}, $vertice->{matrix1}, $vertice->{matrix2}, $vertice->{matrix3}) = $packet->unpack('vvvv'); 601 @{$vertice->{point}} = $packet->unpack('f3'); 602 @{$vertice->{normal}} = $packet->unpack('f3'); 603 @{$vertice->{t}} = $packet->unpack('f3'); 604 @{$vertice->{b}} = $packet->unpack('f3'); 605 ($vertice->{w0}, $vertice->{w1}, $vertice->{w2}) = $packet->unpack('fff'); 606 @{$vertice->{textcoords}} = $packet->unpack('f2'); 607 push @{$self->{vertices}}, $vertice; 608 } 609 } else { 610 stkutils::debug::fail('ogf::read_vertices', 605, 'unsupported FVF'); 611 } 612 stkutils::debug::fail('ogf::read_vertices', 607, 'there some data in packet left: '.($cf->{end_offsets}[$#{$cf->{end_offsets}}] - $cf->{offset})) unless $cf->{offset} == $cf->{end_offsets}[$#{$cf->{end_offsets}}]; 613 $self->set_loaded('OGF_VERTICES'); 614} 615sub read_icontainer { 616 my $self = shift; 617 my ($cf) = @_; 618 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 619 ($self->{ext_ib_index}, $self->{ext_ib_offset}, $self->{ext_ib_size}) = $packet->unpack('VVV'); 620 $self->set_loaded('OGF_ICONTAINER'); 621} 622sub read_indices { 623 my $self = shift; 624 my ($cf) = @_; 625 my $packet = stkutils::data_packet->new($cf->r_chunk_data(4)); 626 ($self->{indices_count}) = $packet->unpack('V'); 627 for (my $i = 0; $i < $self->{indices_count}; $i++) { 628 my $packet = stkutils::data_packet->new($cf->r_chunk_data(2)); 629 my ($index) = $packet->unpack('v'); 630 push @{$self->{indices}}, $index; 631 } 632 stkutils::debug::fail('ogf::read_indices', 627, 'there some data in packet left: '.($cf->{end_offsets}[$#{$cf->{end_offsets}}] - $cf->{offset})) unless $cf->{offset} == $cf->{end_offsets}[$#{$cf->{end_offsets}}]; 633 $self->set_loaded('OGF_INDICES'); 634} 635sub read_children_l { 636 my $self = shift; 637 my ($cf) = @_; 638 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 639 @{$self->{children_l}} = $packet->unpack('V/V'); 640 stkutils::debug::fail('ogf::read_children_l', 635, 'there some data in packet left: '.$packet->length()) unless $packet->length() == 0; 641 $self->set_loaded('OGF_CHILDREN_L'); 642} 643sub read_children { 644 my $self = shift; 645 my ($cf) = @_; 646 for (my $expected_id = 0;; $expected_id++) { 647 my ($nnnn, $size) = $cf->r_chunk_open(); 648 defined $nnnn or last; 649 if ($nnnn != $expected_id) { 650 stkutils::debug::fail('ogf::read_children', 645, 'unexpected chunk $nnnn'); 651 } 652 my $child = stkutils::ogf->new(); 653 $child->read($cf); 654 $cf->r_chunk_close(); 655 push @{$self->{children}}, $child; 656 } 657 $self->set_loaded('OGF_CHILDREN'); 658} 659sub read_child_refs { 660 my $self = shift; 661 my ($cf) = @_; 662 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 663 my ($count) = $packet->unpack('V'); 664 for (my $i = 0; $i < $count; $i++) { 665 my ($ref) = $packet->unpack('Z*'); 666 push @{$self->{child_refs}}, $ref; 667 } 668 stkutils::debug::fail('ogf::read_child_refs', 664, 'there some data in packet left: '.$packet->length()) unless $packet->length() == 0; 669 $self->set_loaded('OGF_CHILD_REFS'); 670} 671sub read_swidata { 672 my $self = shift; 673 my ($cf) = @_; 674 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 675 @{$self->{swi_reserved}} = $packet->unpack('V4'); 676 ($self->{swi_count}) = $packet->unpack('V'); 677 for (my $i = 0; $i < $self->{swi_count}; $i++) { 678 my $swi = {}; 679 ($swi->{offset}, 680 $swi->{num_tris}, 681 $swi->{num_verts}) = $packet->unpack('lvv'); 682 push @{$self->{swi_data}}, $swi; 683 } 684 stkutils::debug::fail('ogf::read_swidata', 675, 'there some data in packet left: '.$packet->length()) unless $packet->length() == 0; 685 set_loaded($self, 'OGF_SWIDATA'); 686} 687sub read_loddata { 688 my $self = shift; 689 my ($cf) = @_; 690 my $loddata = {}; 691 while (1) { 692 my ($id, $size) = $cf->r_chunk_open(); 693 defined $id or last; 694 SWITCH: { 695 $id == OGF3_HOPPE_HEADER && do { read_hoppe_header($loddata, $cf); last SWITCH; }; 696 $id == OGF3_HOPPE_VERT_SPLITS && do {read_hoppe_vertsplits($loddata, $self, $cf); last SWITCH; }; 697 $id == OGF3_HOPPE_FIX_FACES && do {read_hoppe_fix_faces($loddata, $cf); last SWITCH; }; 698 stkutils::debug::fail('ogf::read_loddata', 694, "unexpected chunk $id"); 699 } 700 $cf->r_chunk_close(); 701 } 702 $cf->r_chunk_close(); 703 push @{$self->{loddata}}, $loddata; 704 $self->set_loaded('OGF_LODDATA'); 705} 706sub read_hoppe_header { 707 my $self = shift; 708 my ($cf) = @_; 709 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 710 ($self->{min_vertices}, $self->{max_vertices}) = $packet->unpack('VV'); 711} 712sub read_hoppe_vertsplits { 713 my $self = shift; 714 my ($global, $cf) = @_; 715 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 716 $self->{num_vertsplits} = $global->{vertex_count} - $self->{min_vertices}; 717 for (my $i = 0; $i < $self->{num_vertsplits}; $i++) { 718 my $split = {}; 719 ($split->{vert}, $split->{num_tris}, $split->{num_verts}) = $packet->unpack('vCC'); 720 push @{$self->{vertsplits}}, $split; 721 } 722} 723sub read_hoppe_fix_faces { 724 my $self = shift; 725 my ($cf) = @_; 726 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 727 ($self->{num_fix_faces}) = $packet->unpack('V'); 728 @{$self->{fix_faces}} = $packet->unpack("(v)$self->{num_fix_faces}"); 729} 730sub read_s_lods_csky { 731 my $self = shift; 732 my ($cf) = @_; 733 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 734 ($self->{s_lods_ref}) = $packet->unpack('Z*'); 735 $self->set_loaded('OGF_S_LODS_CSKY'); 736} 737sub read_s_lods { 738 my $self = shift; 739 my ($cf) = @_; 740 for (my $expected_id = 0;; $expected_id++) { 741 my ($nnnn, $size) = $cf->r_chunk_open(); 742 defined $nnnn or last; 743 if ($nnnn != $expected_id) { 744 stkutils::debug::fail('ogf::read_s_lods', 741, 'unexpected chunk $nnnn'); 745 } 746 my $lod = stkutils::ogf->new(); 747 $lod->read($cf); 748 $cf->r_chunk_close(); 749 push @{$self->{s_lods}}, $lod; 750 } 751 $self->set_loaded('OGF_S_LODS'); 752} 753sub read_s_userdata { 754 my $self = shift; 755 my ($cf) = @_; 756 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 757 my $n = $packet->length(); 758 ($self->{userdata}) = $packet->unpack("a$n"); 759 stkutils::debug::fail('ogf::read_s_userdata', 757, 'there some data in packet left: '.$packet->length()) unless $packet->length() == 0; 760 $self->set_loaded('OGF_S_USERDATA'); 761} 762sub read_s_bone_names { 763 my $self = shift; 764 my ($cf) = @_; 765 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 766 my ($count) = $packet->unpack('V'); 767 for (my $i = 0; $i < $count; $i++) { 768 my $bone_name_obj = {}; 769 ($bone_name_obj->{name}, $bone_name_obj->{parent_name}) = $packet->unpack('Z*Z*'); 770 read_obb($bone_name_obj, $packet); 771 push @{$self->{bone_names}}, $bone_name_obj; 772 } 773 $self->set_loaded('OGF_S_BONE_NAMES'); 774 stkutils::debug::fail('ogf::read_s_bone_names', 772, 'there some data in packet left: '.$packet->length()) unless $packet->length() == 0; 775} 776sub read_obb { 777 my $self = $_[0]; 778 @{$self->{rotate}} = $_[1]->unpack('f9'); 779 @{$self->{translate}} = $_[1]->unpack('f3'); 780 @{$self->{halfsize}} = $_[1]->unpack('f3'); 781} 782sub read_s_ikdata { 783#loc_44C45A: 784#mov ecx, [esi+4] 785#mov edx, [esp+1B8h+var_1AC] 786#mov esi, [ecx+edx*4] 787#lea eax, [esi+0E8h] 788#push eax 789#mov ecx, edi 790#call ds:?r_stringZ@IReader@@QAEXPAD@Z ; IReader::r_stringZ(char *) 791#push 70h 792#lea ecx, [esi+78h] 793#push ecx 794#mov ecx, edi 795#call ds:?r@IReader@@QAEXPAXH@Z ; IReader::r(void *,int) 796#push 3Ch 797#lea edx, [esi+128h] 798#push edx 799#mov ecx, edi 800#call ds:?r@IReader@@QAEXPAXH@Z ; IReader::r(void *,int) 801#push 0Ch 802#lea eax, [esi+60h] 803#push eax 804#mov ecx, edi 805#call ds:?r@IReader@@QAEXPAXH@Z ; IReader::r(void *,int) 806#push 0Ch 807#lea ecx, [esi+6Ch] 808#push ecx 809#mov ecx, edi 810#call ds:?r@IReader@@QAEXPAXH@Z ; IReader::r(void *,int) 811#push 4 812#lea edx, [esp+1BCh+var_190] 813#push edx 814#mov ecx, edi 815#call ds:?r@IReader@@QAEXPAXH@Z ; IReader::r(void *,int) 816#mov eax, [esp+1B8h+var_190] 817#mov [esi+164h], eax 818#push 0Ch 819#add esi, 168h 820#push esi 821#mov ecx, edi 822#call ds:?r@IReader@@QAEXPAXH@Z ; IReader::r(void *,int) 823#add [esp+1B8h+var_1AC], 1 824#mov edx, [esp+1B8h+var_1AC] 825#jmp loc_44C410 826 my $self = shift; 827 my ($cf, $mode) = @_; 828 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 829 while ($packet->length()) { 830 my $ik = {}; 831 $ik->{bone_shape} = {}; 832 $ik->{joint_data} = {}; 833 if ($mode == 2) { 834 ($ik->{version}, $ik->{game_mtl_name}) = $packet->unpack('VZ*'); 835 } else { 836 ($ik->{game_mtl_name}) = $packet->unpack('Z*'); 837 } 838 read_s_bone_shape($ik->{bone_shape}, $packet); 839 read_s_joint_ik_data($ik->{joint_data}, $packet, $ik->{version}); 840 @{$ik->{bind_rotation}} = $packet->unpack('f3'); 841 @{$ik->{bind_position}} = $packet->unpack('f3'); 842 ($ik->{mass}, @{$ik->{center_of_mass}}) = $packet->unpack('ff3'); 843 push @{$self->{ik_data}}, $ik; 844 } 845 $self->set_loaded('OGF_S_IKDATA_'.$mode); 846 stkutils::debug::fail('ogf::read_s_ikdata', 801, 'there some data in packet left: '.$packet->length()) unless $packet->length() == 0; 847} 848sub read_s_bone_shape { 849 my $self = $_[0]; 850 ($self->{type}, $self->{flags}) = $_[1]->unpack('vv'); 851 $self->{box} = {}; 852 $self->{sphere} = {}; 853 $self->{cylinder} = {}; 854 read_obb($self->{box}, $_[1]); 855 read_sphere($self->{sphere}, $_[1]); 856 read_cylinder($self->{cylinder}, $_[1]); 857} 858sub read_sphere { 859 my $self = $_[0]; 860 @{$self->{p}} = $_[1]->unpack('f3'); 861 ($self->{r}) = $_[1]->unpack('f'); 862} 863sub read_cylinder { 864 my $self = $_[0]; 865 @{$self->{center}} = $_[1]->unpack('f3'); 866 @{$self->{direction}} = $_[1]->unpack('f3'); 867 ($self->{height}) = $_[1]->unpack('f'); 868 ($self->{radius}) = $_[1]->unpack('f'); 869} 870sub read_s_joint_ik_data { 871 my $self = shift; 872 my ($packet, $version) = @_; 873 ($self->{type}) = $packet->unpack('V'); 874 @{$self->{limits}} = []; 875 ${$self->{limits}}[0] = {}; 876 read_s_joint_limit(${$self->{limits}}[0], $packet); 877 ${$self->{limits}}[1] = {}; 878 read_s_joint_limit(${$self->{limits}}[1], $packet); 879 ${$self->{limits}}[2] = {}; 880 read_s_joint_limit(${$self->{limits}}[2], $packet); 881 ($self->{spring_factor}, 882 $self->{damping_factor}, 883 $self->{ik_flags}, 884 $self->{break_force}, 885 $self->{break_torque}) = $packet->unpack('ffVff'); 886 ($self->{friction}) = $packet->unpack('f') if defined $version; 887} 888sub read_s_joint_limit { 889 my $self = $_[0]; 890 @{$self->{limit}} = $_[1]->unpack('f2'); 891 ($self->{spring_factor}, $self->{damping_factor}) = $_[1]->unpack('ff'); 892} 893sub read_smotion_refs_1 { 894 my $self = shift; 895 my ($cf) = @_; 896 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 897 @{$self->{motion_refs_1}} = $packet->unpack('V/(Z*)'); 898 stkutils::debug::fail('ogf::read_smotion_refs_1', 853, 'there some data in packet left: '.$packet->length()) unless $packet->length() == 0; 899 $self->set_loaded('OGF_S_MOTION_REFS_1'); 900} 901sub read_smotion_refs_0 { 902 my $self = shift; 903 my ($cf) = @_; 904 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 905 ($self->{motion_refs_0}) = $packet->unpack('Z*'); 906 stkutils::debug::fail('ogf::read_smotion_refs_0', 854, 'there some data in packet left: '.$packet->length()) unless $packet->length() == 0; 907 $self->set_loaded('OGF_S_MOTION_REFS_0'); 908} 909sub read_s_smparams { 910 my $self = shift; 911 my ($cf, $mode) = @_; 912 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 913 ($self->{sm_params_version}) = $packet->unpack('v') if $mode == 1; ### build 1865 - exists!!! 914 my ($partition_count) = $packet->unpack('v'); 915 for (my $i = 0; $i < $partition_count; $i++) { 916 my $part = {}; 917 ($part->{name}, $part->{bone_count}) = $packet->unpack('Z*v'); 918 for (my $i = 0; $i < $part->{bone_count}; $i++) { 919 my $bone = {}; 920 if ($mode == 0 || $self->{sm_params_version} == 1) { 921 ($bone->{bone_id}) = $packet->unpack('V'); 922 } elsif ($self->{sm_params_version} == 2) { 923 ($bone->{bone_name}) = $packet->unpack('Z*'); 924 } elsif ($self->{sm_params_version} == 3 || $self->{sm_params_version} == 4) { 925 ($bone->{bone_name}, $bone->{bone_id}) = $packet->unpack('Z*V'); 926 } 927 push @{$part->{bones}}, $bone; 928 } 929 push @{$self->{s_smparams_partitions}}, $part; 930 } 931 my ($motion_count) = $packet->unpack('v'); 932 for (my $i = 0; $i < $motion_count; $i++) { 933 my $mot = {}; 934 if ($mode == 1) { 935 ($mot->{name}, $mot->{flags}) = $packet->unpack('Z*V'); 936 read_motion_def($mot, $packet); 937 if ($self->{sm_params_version} == 4) { 938 my ($num_marks) = $packet->unpack('V'); 939 for (my $j = 0; $j < $num_marks; ++$j) { 940 my $mmark = {}; 941 read_motion_mark($mmark, $packet); 942 push @{$mot->{mmarks}}, $mmark; 943 } 944 } 945 } else { 946 ($mot->{name}, $mot->{flags}) = $packet->unpack('Z*C'); 947 read_motion_def($mot, $packet); 948 my $flag = $packet->unpack('C'); 949 $mot->{flags} += 0x2 if $flag != 1; 950 } 951 push @{$self->{s_smparams_motions}}, $mot; 952 } 953 stkutils::debug::fail('ogf::read_s_smparams', 908, 'there some data in packet left: '.$packet->length()) unless $packet->length() == 0; 954 $self->set_loaded('OGF_S_SMPARAMS_'.$mode); 955} 956sub read_motion_def { 957 my $self = $_[0]; 958 ($self->{bone_or_part}, 959 $self->{motion}, 960 $self->{speed}, 961 $self->{power}, 962 $self->{accrue}, 963 $self->{falloff}) = $_[1]->unpack('vvffff'); 964} 965sub read_motion_mark { 966 my $self = $_[0]; 967 $self->{name} = ''; 968 my $c; 969 while (1) { 970 ($c) = $_[1]->unpack('a'); 971 last if ($c eq "\n" || $c eq "\r"); 972 $self->{name} .= $c; 973 } 974 ($c) = $_[1]->unpack('a'); 975 die unless $c eq "\n"; 976 my ($count) = $_[1]->unpack('V'); 977 for (my $i = 0; $i < $count; $i++) { 978 my $int = {}; 979 ($int->{min}, $int->{max}) = $_[1]->unpack('ff'); 980 push @{$self->{intervals}}, $int; 981 } 982} 983sub read_smotions { 984 my $self = shift; 985 my ($cf, $mode) = @_; 986 for (my $expected_id = 0;; $expected_id++) { 987 my ($nnnn, $size) = $cf->r_chunk_open(); 988 defined $nnnn or last; 989 if ($nnnn != $expected_id) { 990 stkutils::debug::fail('ogf::read_smotions', 945, 'unexpected chunk $nnnn'); 991 } 992 if ($nnnn == 0) { 993 $self->{motions_count} = unpack('V', $cf->r_chunk_data()); 994 } else { 995 my $motion = {}; 996 read_motion($motion, $cf, $mode); 997 push @{$self->{motions}}, $motion; 998 } 999 $cf->r_chunk_close(); 1000 } 1001 $self->set_loaded('OGF_S_MOTIONS_'.$mode); 1002} 1003sub read_motion { 1004 my $self = shift; 1005 my ($cf, $mode) = @_; 1006 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 1007 ($self->{name}, $self->{keys_count}) = $packet->unpack('Z*V'); 1008 if ($self->{keys_count} == 0) { 1009 stkutils::debug::fail('ogf::read_motion', 965, 'there some data in packet left: '.$packet->length()) unless $packet->length() == 0; 1010 return; 1011 } 1012 if ($mode == 1) { 1013 for (my $i = 0; $packet->length() > 0; $i++) { 1014 my @keyst; 1015 my $bone = {}; 1016 ($bone->{flags}) = $packet->unpack('C'); 1017 die unless ($bone->{flags} & ~0x07) == 0; 1018 if ($bone->{flags} & KPF_R_ABSENT) { 1019 @{$bone->{keysr}} = $packet->unpack('s4'); 1020 } else { 1021 ($bone->{crc_keysr}, @{$bone->{keysr}}) = $packet->unpack("V(s4)$self->{keys_count}"); 1022 } 1023 # dequantize_qr(\@{$bone->{keysr}}); 1024 if ($bone->{flags} & KPF_T_PRESENT) { 1025 ($bone->{crc_keyst}) = $packet->unpack('V'); 1026 if ($bone->{flags} & KPF_T_HQ) { 1027 @{$bone->{keyst}} = $packet->unpack("(s3)$self->{keys_count}"); 1028 } else { 1029 @{$bone->{keyst}} = $packet->unpack("(c3)$self->{keys_count}"); 1030 } 1031 @{$bone->{sizet}} = $packet->unpack('f3'); 1032 } else { 1033 die unless ($bone->{flags} & KPF_T_HQ) == 0; 1034 } 1035 @{$bone->{initt}} = $packet->unpack('f3'); 1036 push @{$self->{bones}}, $bone; 1037 } 1038 } else { 1039 for (my $i = 0; $packet->length() > 0; $i++) { 1040 my $bone = {}; 1041 @{$bone->{keys}} = $packet->unpack("(s4f3)$self->{keys_count}"); 1042 # dequantize_qr(\@{$bone->{keysr}}); 1043 push @{$self->{bones}}, $bone; 1044 } 1045 } 1046 stkutils::debug::fail('ogf::read_motion', 1002, 'there some data in packet left: '.$packet->length()) unless $packet->length() == 0; 1047} 1048sub read_loddef { 1049# if ( !IReader__find_chunk(a3, 21, 0) ) 1050# xrDebug__fail(Debug, "data->find_chunk(OGF_LODDEF)", ".\\FLOD.cpp", 10); 1051# v6 = (int)((char *)v5 + 144); 1052# v69 = 8; 1053# do 1054# { 1055 # v7 = v6 - 56; 1056# IReader__r(v4, v6 - 56, 96); 1057# v8 = *(float *)(v6 - 32) - *(float *)(v6 - 56); 1058# v9 = *(float *)(v6 - 28) - *(float *)(v6 - 52); 1059# v10 = *(float *)(v6 - 24) - *(float *)(v6 - 48); 1060# v11 = *(float *)(v6 - 8) - *(float *)(v6 - 32); 1061# v12 = *(float *)(v6 - 4) - *(float *)(v6 - 28); 1062# v13 = *(float *)v6 - *(float *)(v6 - 24); 1063# v48 = v13 * v9 - v12 * v10; 1064# v52 = v10 * v11 - v13 * v8; 1065# v56 = v12 * v8 - v9 * v11; 1066# v70 = v56 * v56 + v52 * v52 + v48 * v48; 1067# if ( std__numeric_limits_float___min() >= v70 ) 1068# { 1069# v14 = v56; 1070# } 1071# else 1072# { 1073# v15 = sqrt(1.0 / v70); 1074# v48 = v48 * v15; 1075# v52 = v52 * v15; 1076# v14 = v15 * v56; 1077# } 1078# v66 = v14; 1079# v16 = *(float *)(v6 - 8) - *(float *)(v6 - 32); 1080# v60 = v48; 1081# v63 = v52; 1082# v17 = *(float *)(v6 - 4) - *(float *)(v6 - 28); 1083# v18 = *(float *)v6 - *(float *)(v6 - 24); 1084# v19 = *(float *)(v6 + 16) - *(float *)(v6 - 8); 1085# v20 = *(float *)(v6 + 20) - *(float *)(v6 - 4); 1086# v21 = *(float *)(v6 + 24) - *(float *)v6; 1087# v49 = v21 * v17 - v20 * v18; 1088# v53 = v18 * v19 - v21 * v16; 1089# v57 = v20 * v16 - v17 * v19; 1090# v71 = v57 * v57 + v53 * v53 + v49 * v49; 1091# if ( std__numeric_limits_float___min() >= v71 ) 1092# { 1093# v22 = v57; 1094# } 1095# else 1096# { 1097# v23 = sqrt(1.0 / v71); 1098# v49 = v49 * v23; 1099# v53 = v53 * v23; 1100# v22 = v23 * v57; 1101# } 1102# v61 = v49 + v60; 1103# v64 = v53 + v63; 1104# v67 = v22 + v66; 1105# v24 = *(float *)(v6 + 16) - *(float *)(v6 - 8); 1106# v25 = *(float *)(v6 + 20) - *(float *)(v6 - 4); 1107# v26 = *(float *)(v6 + 24) - *(float *)v6; 1108# v27 = *(float *)v7 - *(float *)(v6 + 16); 1109# v28 = *(float *)(v6 - 52) - *(float *)(v6 + 20); 1110# v29 = *(float *)(v6 - 48) - *(float *)(v6 + 24); 1111# v50 = v29 * v25 - v28 * v26; 1112# v54 = v26 * v27 - v29 * v24; 1113# v58 = v28 * v24 - v25 * v27; 1114# v72 = v58 * v58 + v54 * v54 + v50 * v50; 1115# if ( std__numeric_limits_float___min() >= v72 ) 1116# { 1117# v30 = v58; 1118# } 1119# else 1120# { 1121# v31 = sqrt(1.0 / v72); 1122# v50 = v50 * v31; 1123# v54 = v54 * v31; 1124# v30 = v31 * v58; 1125# } 1126# v62 = v50 + v61; 1127# v65 = v54 + v64; 1128# v68 = v30 + v67; 1129# v32 = *(float *)v7 - *(float *)(v6 + 16); 1130# v33 = *(float *)(v6 - 52) - *(float *)(v6 + 20); 1131# v34 = *(float *)(v6 - 48) - *(float *)(v6 + 24); 1132# v35 = *(float *)(v6 - 32) - *(float *)v7; 1133# v36 = *(float *)(v6 - 28) - *(float *)(v6 - 52); 1134# v37 = *(float *)(v6 - 24) - *(float *)(v6 - 48); 1135# v51 = v37 * v33 - v36 * v34; 1136# v55 = v34 * v35 - v37 * v32; 1137# v59 = v36 * v32 - v33 * v35; 1138# v73 = v59 * v59 + v55 * v55 + v51 * v51; 1139# if ( std__numeric_limits_float___min() >= v73 ) 1140# { 1141# v38 = v59; 1142# } 1143# else 1144# { 1145# v39 = sqrt(1.0 / v73); 1146# v51 = v51 * v39; 1147# v55 = v55 * v39; 1148# v38 = v39 * v59; 1149# } 1150# v6 += 108; 1151# v40 = v69-- == 1; 1152# v41 = v38 + v68; 1153# v42 = (v51 + v62) * 0.25; 1154# v43 = (v55 + v65) * 0.25; 1155# v44 = v41 * 0.25; 1156# v45 = sqrt(1.0 / (v44 * v44 + v43 * v43 + v42 * v42)); 1157# *(float *)(v6 - 68) = v42 * v45; 1158# *(float *)(v6 - 64) = v43 * v45; 1159# *(float *)(v6 - 60) = v44 * v45; 1160# *(float *)(v6 - 68) = -*(float *)(v6 - 68); 1161# *(float *)(v6 - 64) = -*(float *)(v6 - 64); 1162# *(float *)(v6 - 60) = -*(float *)(v6 - 60); 1163# } 1164# while ( !v40 ); 1165} 1166sub read_loddef2 { 1167 my $self = shift; 1168 my ($cf) = @_; 1169 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 1170 for (my $i = 0; $i < 8; $i++) { 1171 my $lod_face = {}; 1172 for (my $j = 0; $j < 4; $j++) { 1173 my $vertex = {}; 1174 @{$vertex->{v}} = $packet->unpack('f3'); 1175 @{$vertex->{t}} = $packet->unpack('f2'); 1176 ($vertex->{c_rgb_hemi}, $vertex->{c_sun}) = $packet->unpack('VC'); 1177 @{$vertex->{pad}} = $packet->unpack('C3'); 1178 push @{$lod_face->{vertices}}, $vertex; 1179 } 1180 push @{$self->{lod_faces}}, $lod_face; 1181 } 1182 $self->set_loaded('OGF_LODDEF2'); 1183} 1184sub read_treedef { 1185# IReader__r(v5, (char *)v4 + 132, 64); 1186# IReader__r(v5, (char *)v4 + 196, 16); 1187# IReader__r(v5, (char *)v4 + 212, 16); 1188# *((_DWORD *)v4 + 16) = CShaderManager__CreateGeom(v25, *((_DWORD *)v4 + 17), *((_DWORD *)v4 + 20)); 1189# *((_DWORD *)v4 + 24) = sub_599BF0("consts"); 1190# *((_DWORD *)v4 + 25) = sub_599BF0("wave"); 1191# *((_DWORD *)v4 + 26) = sub_599BF0("wind"); 1192# *((_DWORD *)v4 + 27) = sub_599BF0("c_bias"); 1193# *((_DWORD *)v4 + 28) = sub_599BF0("c_scale"); 1194# *((_DWORD *)v4 + 29) = sub_599BF0("m_m2w"); 1195# *((_DWORD *)v4 + 30) = sub_599BF0("m_w2v2p"); 1196# *((_DWORD *)v4 + 31) = sub_599BF0("v_eye"); 1197 1198} 1199sub read_treedef2 { 1200 my $self = shift; 1201 my ($cf) = @_; 1202 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 1203 @{$self->{tree_xform}} = $packet->unpack('f16'); 1204 read_ogf_color($self->{c_scale}, $packet); 1205 read_ogf_color($self->{c_bias}, $packet); 1206 $self->set_loaded('OGF_TREEDEF2'); 1207} 1208sub read_ogf_color { 1209 my $self = shift; 1210 my ($packet) = @_; 1211 @{$self->{rgb}} = $packet->unpack('f3'); 1212 ($self->{hemi}, $self->{sun}) = $packet->unpack('ff'); 1213} 1214sub read_swicontainer { 1215 my $self = shift; 1216 my ($cf) = @_; 1217 my $packet = stkutils::data_packet->new($cf->r_chunk_data()); 1218 ($self->{ext_swib_index}) = $packet->unpack('V'); 1219 1220} 1221sub write { 1222 my $self = shift; 1223 my ($cf, $subversion) = @_; 1224 $self->{subversion} = $subversion; 1225 $self->write_header($cf); 1226 SWITCH: { 1227 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_NORMAL' && do { $self->write_visual($cf); last SWITCH; }; 1228 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_HIERRARHY' && do { $self->write_hierrarhy_visual($cf); last SWITCH; }; 1229 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_PROGRESSIVE' && do { $self->write_progressive($cf); last SWITCH; }; 1230 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_SKELETON_ANIM' && do { $self->write_kinematics_animated($cf); last SWITCH; }; 1231 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_SKELETON_GEOMDEF_PM' && do { $self->write_skeletonx_pm($cf); last SWITCH; }; 1232 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_SKELETON_GEOMDEF_ST' && do { $self->write_skeletonx_st($cf); last SWITCH; }; 1233 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_PROGRESSIVE2' && do { $self->write_progressive2($cf); last SWITCH; }; 1234 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_LOD' && do { $self->write_lod($cf); last SWITCH; }; 1235 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_TREE' && do { $self->write_tree_visual_st($cf); last SWITCH; }; 1236 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_TREE_ST' && do { $self->write_tree_visual_st($cf); last SWITCH; }; 1237 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_SKELETON_RIGID' && do { $self->write_kinematics($cf); last SWITCH; }; 1238 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_TREE_PM' && do { $self->write_tree_visual_pm($cf); last SWITCH; }; 1239 stkutils::debug::fail(__PACKAGE__.'::write', __LINE__, '', "unexpected model type $self->{type}"); 1240 } 1241} 1242sub write_header { 1243 my $self = shift; 1244 my ($cf) = @_; 1245 $cf->w_chunk_open(0x1); 1246 $cf->w_chunk_data(pack('CCv', $self->{format_version}, $self->{type}, $self->{shader_id})); 1247 if ($self->{format_version} == 4) { 1248 $self->write_bbox($cf); 1249 $self->write_bsphere($cf); 1250 } 1251 $cf->w_chunk_close(); 1252} 1253sub write_render_visual { 1254 my $self = shift; 1255 my ($cf) = @_; 1256 if ($self->{format_version} == 3 && $self->check_loaded('OGF_BBOX')) { 1257 $cf->w_chunk_open(0x6); 1258 $self->write_bbox($cf); 1259 $cf->w_chunk_close(); 1260 } 1261 if ($self->{format_version} == 3 && $self->check_loaded('OGF_BSPHERE')) { 1262 $cf->w_chunk_open(0xb); 1263 $self->write_bsphere($cf); 1264 $cf->w_chunk_close(); 1265 } 1266 if ($self->check_loaded('OGF_S_DESC')) { 1267 $self->write_s_desc($cf); 1268 } 1269 if ($self->{format_version} == 3 && $self->check_loaded('OGF_TEXTURE_L')) { 1270 $self->write_texture_l($cf); 1271 } 1272 if ($self->check_loaded('OGF_TEXTURE')) { 1273 $self->write_texture($cf); 1274 } 1275}; 1276sub write_visual { 1277 my $self = shift; 1278 my ($cf) = @_; 1279 $self->write_render_visual($cf); 1280 if ($self->{format_version} == 4 && $self->check_loaded('OGF_GCONTAINER')) { 1281 $self->write_gcontainer($cf); 1282 if ($self->check_loaded('OGF_FASTPATH')) { 1283 $self->write_fastpath($cf) ; 1284 } 1285 } 1286 if ($self->check_loaded('OGF_VCONTAINER')) { 1287 $self->write_vcontainer($cf); 1288 } elsif ($self->check_loaded('OGF_VERTICES')) { 1289 $self->write_vertices($cf); 1290 } 1291 if ($self->check_loaded('OGF_ICONTAINER')) { 1292 $self->write_icontainer($cf); 1293 } elsif ($self->check_loaded('OGF_INDICES')) { 1294 $self->write_indices($cf); 1295 } 1296} 1297sub write_hierrarhy_visual { 1298 my $self = shift; 1299 my ($cf) = @_; 1300 $self->write_render_visual($cf); 1301 if ($self->check_loaded('OGF_CHILDREN_L')) { 1302 $self->write_children_l($cf); 1303 } elsif ($self->check_loaded('OGF_CHILDREN')) { 1304 $self->write_children($cf); 1305 } elsif ($self->check_loaded('OGF_CHILD_REFS')) { 1306 $self->write_child_refs($cf); 1307 } 1308}; 1309sub write_progressive { 1310 my $self = shift; 1311 my ($cf) = @_; 1312 $self->write_visual($cf); 1313 if ($self->{format_version} == 4) { 1314 $self->write_swidata($cf); 1315 } else { 1316 $self->write_loddata($cf); 1317 } 1318} 1319sub write_kinematics { 1320 my $self = shift; 1321 my ($cf) = @_; 1322 $self->write_hierrarhy_visual($cf); 1323 if ($self->{format_version} == 4) { 1324 if ($self->check_loaded('OGF_S_LODS_CSKY')) { 1325 $self->write_s_lods_csky($cf); 1326 } elsif ($self->check_loaded('OGF_S_LODS')) { 1327 $self->write_s_lods($cf); 1328 } 1329 } 1330 if ($self->check_loaded('OGF_S_USERDATA')) { 1331 $self->write_s_userdata($cf); 1332 } 1333 if ($self->check_loaded('OGF_S_BONE_NAMES')) { 1334 $self->write_s_bone_names($cf); 1335 } 1336 if ($self->check_loaded('OGF_S_IKDATA_2')) { 1337 $self->write_s_ikdata($cf, 2); 1338 } elsif ($self->check_loaded('OGF_S_IKDATA_1')) { 1339 $self->write_s_ikdata($cf, 1); 1340 } 1341} 1342sub write_kinematics_animated { 1343 my $self = shift; 1344 my ($cf) = @_; 1345 $self->write_kinematics($cf); 1346 if ($self->{format_version} == 4 && $self->check_loaded('OGF_S_MOTION_REFS_1')) { 1347 $self->write_smotion_refs_1($cf); 1348 return; 1349 } elsif ($self->check_loaded('OGF_S_MOTION_REFS_0')) { 1350 $self->write_smotion_refs_0($cf); 1351 return; 1352 } elsif ($self->check_loaded('OGF_S_SMPARAMS_1')) { 1353 $self->write_s_smparams($cf, 1); 1354 } elsif ($self->{format_version} == 3 && $self->check_loaded('OGF_S_SMPARAMS_0')) { 1355 $self->write_s_smparams($cf, 2); 1356 } 1357 if ($self->check_loaded('OGF_S_MOTIONS_1')) { 1358 $self->write_smotions($cf, 1); 1359 } elsif ($self->{format_version} == 3 && $self->check_loaded('OGF_S_MOTIONS_0')) { 1360 $self->write_smotions($cf, 0); 1361 } else { 1362 stkutils::debug::fail('ogf::write_kinematics_animated', 1184, 'no motions to write'); 1363 } 1364} 1365sub write_skeletonx_pm { 1366 my $self = shift; 1367 my ($cf) = @_; 1368 $self->write_progressive($cf); 1369} 1370sub write_skeletonx_st { 1371 my $self = shift; 1372 my ($cf) = @_; 1373 $self->write_visual($cf); 1374} 1375sub write_progressive2 { 1376 my $self = shift; 1377 my ($cf) = @_; 1378 $self->write_render_visual($cf); 1379 $self->write_s_lods($cf); 1380} 1381sub write_lod { 1382 my $self = shift; 1383 my ($cf) = @_; 1384 $self->write_hierrarhy_visual($cf); 1385 if ($self->check_loaded('OGF_LODDEF2')) { 1386 $self->write_loddef2($cf); 1387 } 1388} 1389sub write_tree_visual { 1390 my $self = shift; 1391 my ($cf) = @_; 1392 $self->write_visual($cf); 1393 $self->write_treedef2($cf); 1394} 1395sub write_tree_visual_st { 1396 my $self = shift; 1397 my ($cf) = @_; 1398 $self->write_tree_visual($cf); 1399} 1400sub write_tree_visual_pm { 1401 my $self = shift; 1402 my ($cf) = @_; 1403 $self->write_tree_visual($cf); 1404 $self->write_swicontainer($cf); 1405} 1406sub write_bbox { 1407 my $self = shift; 1408 my ($cf) = @_; 1409 $cf->w_chunk_data(pack('f3f3', @{$self->{bbox}->{min}}, @{$self->{bbox}->{max}})); 1410} 1411sub write_bsphere { 1412 my $self = shift; 1413 my ($cf) = @_; 1414 $cf->w_chunk_data(pack('f3f', @{$self->{bsphere}->{c}}, $self->{bsphere}->{r})); 1415} 1416sub write_s_desc { 1417 my $self = shift; 1418 my ($cf) = @_; 1419 $cf->w_chunk($chunk_names{$self->{format_version}}{'OGF_S_DESC'}, pack('Z*Z*VZ*VZ*V', $self->{ogf_object}, $self->{ogf_creator}, $self->{unk}, $self->{creator}, $self->{create_time}, $self->{editor}, $self->{edit_time})); 1420} 1421sub write_texture_l { 1422 my $self = shift; 1423 my ($cf) = @_; 1424 $cf->w_chunk($chunk_names{$self->{format_version}}{'OGF_TEXTURE_L'}, pack('VV', $self->{texture_id}, $self->{shader_id})); 1425} 1426sub write_texture { 1427 my $self = shift; 1428 my ($cf) = @_; 1429 $cf->w_chunk($chunk_names{$self->{format_version}}{'OGF_TEXTURE'}, pack('Z*Z*', $self->{texture_name}, $self->{shader_name})); 1430} 1431sub write_vcontainer { 1432 my $self = shift; 1433 my ($cf) = @_; 1434 $cf->w_chunk($chunk_names{$self->{format_version}}{'OGF_VCONTAINER'}, pack('VVV', $self->{ext_vb_index}, $self->{ext_vb_offset}, $self->{ext_vb_size})); 1435} 1436sub write_vertices { 1437 my $self = shift; 1438 my ($cf) = @_; 1439 $cf->w_chunk_open($chunk_names{$self->{format_version}}{'OGF_VERTICES'}); 1440 $cf->w_chunk_data(pack('VV', $self->{vertex_format}, $self->{vertex_count})); 1441 if ($self->{vertex_format} == OGF_VERTEXFORMAT_FVF_OLD) { 1442 foreach my $vertice (@{$self->{vertices}}) { 1443 $cf->w_chunk_data(pack('f3f3f2', @{$vertice->{point}}, @{$vertice->{normal}}, @{$vertice->{textcoords}})); 1444 } 1445 } elsif ($self->{format_version} == 3 && $self->{vertex_format} == OGF_VERTEXFORMAT_FVF_1L) { 1446 foreach my $vertice (@{$self->{vertices}}) { 1447 $cf->w_chunk_data(pack('f3f3f2l', @{$vertice->{point}}, @{$vertice->{normal}}, @{$vertice->{textcoords}}, $vertice->{matrix})); 1448 } 1449 } elsif ($self->{vertex_format} == OGF_VERTEXFORMAT_FVF_1L or $self->{vertex_format} == OGF_VERTEXFORMAT_FVF_1_CS) { 1450 foreach my $vertice (@{$self->{vertices}}) { 1451 $cf->w_chunk_data(pack('f3f3f3f3f2l', @{$vertice->{point}}, @{$vertice->{normal}}, @{$vertice->{t}}, @{$vertice->{b}}, @{$vertice->{textcoords}}, $vertice->{matrix})); 1452 } 1453 } elsif ($self->{vertex_format} == OGF_VERTEXFORMAT_FVF_2L or $self->{vertex_format} == OGF_VERTEXFORMAT_FVF_2_CS) { 1454 foreach my $vertice (@{$self->{vertices}}) { 1455 $cf->w_chunk_data(pack('vvf3f3f3f3ff2', $vertice->{matrix0}, $vertice->{matrix1}, @{$vertice->{point}}, @{$vertice->{normal}}, @{$vertice->{t}}, @{$vertice->{b}}, $vertice->{w}, @{$vertice->{textcoords}})); 1456 } 1457 } elsif ($self->{vertex_format} == OGF_VERTEXFORMAT_FVF_3_CS) { 1458 foreach my $vertice (@{$self->{vertices}}) { 1459 $cf->w_chunk_data(pack('vvvf3f3f3f3fff2', $vertice->{matrix0}, $vertice->{matrix1}, $vertice->{matrix2}, @{$vertice->{point}}, @{$vertice->{normal}}, @{$vertice->{t}}, @{$vertice->{b}}, $vertice->{w0}, $vertice->{w1}, @{$vertice->{textcoords}})); 1460 } 1461 } elsif ($self->{vertex_format} == OGF_VERTEXFORMAT_FVF_4_CS) { 1462 foreach my $vertice (@{$self->{vertices}}) { 1463 $cf->w_chunk_data(pack('vvvvf3f3f3f3ffff2', $vertice->{matrix0}, $vertice->{matrix1}, $vertice->{matrix2}, $vertice->{matrix3}, @{$vertice->{point}}, @{$vertice->{normal}}, @{$vertice->{t}}, @{$vertice->{b}}, $vertice->{w0}, $vertice->{w1}, $vertice->{w2}, @{$vertice->{textcoords}})); 1464 } 1465 } 1466 $cf->w_chunk_close(); 1467} 1468sub write_icontainer { 1469 my $self = shift; 1470 my ($cf) = @_; 1471 $cf->w_chunk($chunk_names{$self->{format_version}}{'OGF_ICONTAINER'}, pack('VVV', $self->{ext_ib_index}, $self->{ext_ib_offset}, $self->{ext_ib_size})); 1472} 1473sub write_indices { 1474 my $self = shift; 1475 my ($cf) = @_; 1476 $cf->w_chunk_open($chunk_names{$self->{format_version}}{'OGF_INDICES'}); 1477 $cf->w_chunk_data(pack('V', $self->{indices_count})); 1478 foreach my $index (@{$self->{indices}}) { 1479 $cf->w_chunk_data(pack('v', $index)); 1480 } 1481 $cf->w_chunk_close(); 1482} 1483sub write_children_l { 1484 my $self = shift; 1485 my ($cf) = @_; 1486 $cf->w_chunk_open($chunk_names{$self->{format_version}}{'OGF_CHILDREN_L'}); 1487 $cf->w_chunk_data(pack('V', $#{$self->{children_l}} + 1)); 1488 foreach my $child (@{$self->{children_l}}) { 1489 $cf->w_chunk_data(pack('V', $child)); 1490 } 1491 $cf->w_chunk_close(); 1492} 1493sub write_children { 1494 my $self = shift; 1495 my ($cf) = @_; 1496 $cf->w_chunk_open($chunk_names{$self->{format_version}}{'OGF_CHILDREN'}); 1497 my $id = 0; 1498 foreach my $child (@{$self->{children}}) { 1499 $cf->w_chunk_open($id); 1500 $child->write($cf); 1501 $cf->w_chunk_close(); 1502 $id++; 1503 } 1504 $cf->w_chunk_close(); 1505} 1506sub write_child_refs { 1507 my $self = shift; 1508 my ($cf) = @_; 1509 $cf->w_chunk_open($chunk_names{$self->{format_version}}{'OGF_CHILD_REFS'}); 1510 $cf->w_chunk_data(pack('V', $#{$self->{children_l}} + 1)); 1511 foreach my $child (@{$self->{child_refs}}) { 1512 $cf->w_chunk_data(pack('Z*', $child)); 1513 } 1514 $cf->w_chunk_close(); 1515} 1516sub write_swidata { 1517 my $self = shift; 1518 my ($cf) = @_; 1519 $cf->w_chunk_open($chunk_names{$self->{format_version}}{'OGF_SWIDATA'}); 1520 $cf->w_chunk_data(pack('V4V', @{$self->{swi_reserved}}, $self->{swi_count})); 1521 foreach my $swi (@{$self->{swi_data}}) { 1522 $cf->w_chunk_data(pack('lvv', $swi->{offset}, $swi->{num_tris}, $swi->{num_verts})); 1523 } 1524 $cf->w_chunk_close(); 1525} 1526sub write_loddata { 1527 my $self = shift; 1528 my ($cf) = @_; 1529 $cf->w_chunk_open($chunk_names{$self->{format_version}}{'OGF_LODDATA'}); 1530 foreach my $lod (@{$self->{loddata}}) { 1531 write_hoppe_header($lod, $cf); 1532 write_hoppe_vertsplits($lod, $cf); 1533 write_hoppe_fix_faces($lod, $cf); 1534 } 1535 $cf->w_chunk_close(); 1536} 1537sub write_hoppe_header { 1538 my $self = shift; 1539 my ($cf) = @_; 1540 $cf->w_chunk(OGF3_HOPPE_HEADER, pack('VV', $self->{min_vertices}, $self->{max_vertices})); 1541} 1542sub write_hoppe_vertsplits { 1543 my $self = shift; 1544 my ($cf) = @_; 1545 $cf->w_chunk_open(OGF3_HOPPE_VERT_SPLITS); 1546 foreach my $vertsplit (@{$self->{vertsplits}}) { 1547 $cf->w_chunk_data(pack('vCC', $vertsplit->{vert}, $vertsplit->{num_tris}, $vertsplit->{num_verts})); 1548 } 1549 $cf->w_chunk_close(); 1550} 1551sub write_hoppe_fix_faces { 1552 my $self = shift; 1553 my ($cf) = @_; 1554 $cf->w_chunk_open(OGF3_HOPPE_FIX_FACES); 1555 $cf->w_chunk_data(pack('V', $self->{num_fix_faces})); 1556 $cf->w_chunk_data(pack("(v)$self->{num_fix_faces}", @{$self->{fix_faces}})); 1557 $cf->w_chunk_close(); 1558} 1559sub write_s_lods_csky { 1560 my $self = shift; 1561 my ($cf) = @_; 1562 $cf->w_chunk($chunk_names{$self->{format_version}}{'OGF_S_LODS'}, substr(pack('Z*', $self->{s_lods_ref}), 0, -1)); 1563} 1564sub write_s_lods { 1565 my $self = shift; 1566 my ($cf) = @_; 1567 $cf->w_chunk_open($chunk_names{$self->{format_version}}{'OGF_S_LODS'}); 1568 my $id = 0; 1569 foreach my $lod (@{$self->{s_lods}}) { 1570 $cf->w_chunk_open($id); 1571 $lod->write($cf); 1572 $cf->w_chunk_close(); 1573 $id++; 1574 } 1575 $cf->w_chunk_close(); 1576} 1577sub write_s_userdata { 1578 my $self = shift; 1579 my ($cf) = @_; 1580 my $len = length($self->{userdata}); 1581 $cf->w_chunk($chunk_names{$self->{format_version}}{'OGF_S_USERDATA'}, pack("a$len", $self->{userdata})); 1582} 1583sub write_s_bone_names { 1584 my $self = shift; 1585 my ($cf) = @_; 1586 $cf->w_chunk_open($chunk_names{$self->{format_version}}{'OGF_S_BONE_NAMES'}); 1587 $cf->w_chunk_data(pack('V', $#{$self->{bone_names}} + 1)); 1588 foreach my $bone_name_obj (@{$self->{bone_names}}) { 1589 $cf->w_chunk_data(pack('Z*Z*', $bone_name_obj->{name}, $bone_name_obj->{parent_name})); 1590 write_obb($bone_name_obj, $cf); 1591 } 1592 $cf->w_chunk_close(); 1593} 1594sub write_obb { 1595 my $self = shift; 1596 my ($cf) = @_; 1597 $cf->w_chunk_data(pack('f9f3f3', @{$self->{rotate}}, @{$self->{translate}}, @{$self->{halfsize}})); 1598} 1599sub write_sphere { 1600 my $self = $_[0]; 1601 $_[1]->w_chunk_data(pack('f3f', @{$self->{p}}, $self->{r})); 1602} 1603sub write_cylinder { 1604 my $self = $_[0]; 1605 $_[1]->w_chunk_data(pack('f3f3ff', @{$self->{center}}, @{$self->{direction}}, $self->{height}, $self->{radius})); 1606} 1607sub write_s_ikdata { 1608 my $self = shift; 1609 my ($cf, $mode) = @_; 1610 $cf->w_chunk_open($chunk_names{$self->{format_version}}{'OGF_S_IKDATA_'.$mode}); 1611 foreach my $ik (@{$self->{ik_data}}) { 1612 if ($mode == 2) { 1613 $cf->w_chunk_data(pack('VZ*', $ik->{version}, $ik->{game_mtl_name})); 1614 } else { 1615 $cf->w_chunk_data(pack('Z*', $ik->{game_mtl_name})); 1616 } 1617 write_s_bone_shape($ik->{bone_shape}, $cf); 1618 write_s_joint_ik_data($ik->{joint_data}, $cf); 1619 if ($mode > 0) { 1620 $cf->w_chunk_data(pack('f3f3', @{$ik->{bind_rotation}}, @{$ik->{bind_position}})); 1621 } 1622 $cf->w_chunk_data(pack('ff3', $ik->{mass}, @{$ik->{center_of_mass}})); 1623 } 1624 $cf->w_chunk_close(); 1625} 1626sub write_s_bone_shape { 1627 my $self = $_[0]; 1628 $_[1]->w_chunk_data(pack('vv', $self->{type}, $self->{flags})); 1629 write_obb($self->{box}, $_[1]); 1630 write_sphere($self->{sphere}, $_[1]); 1631 write_cylinder($self->{cylinder}, $_[1]); 1632} 1633sub write_s_joint_ik_data { 1634 my $self = $_[0]; 1635 $_[1]->w_chunk_data(pack('V', $self->{type})); 1636 write_s_joint_limit(${$self->{limits}}[0], $_[1]); 1637 write_s_joint_limit(${$self->{limits}}[1], $_[1]); 1638 write_s_joint_limit(${$self->{limits}}[2], $_[1]); 1639 $_[1]->w_chunk_data(pack('ffVff', $self->{spring_factor}, $self->{damping_factor}, $self->{ik_flags}, $self->{break_force}, $self->{break_torque})); 1640 $_[1]->w_chunk_data(pack('f', $self->{friction})) if defined $self->{friction}; 1641} 1642sub write_s_joint_limit { 1643 my $self = $_[0]; 1644 $_[1]->w_chunk_data(pack('f2ff', @{$self->{limit}}, $self->{spring_factor}, $self->{damping_factor})); 1645} 1646sub write_smotion_refs_1 { 1647 my $self = shift; 1648 my ($cf) = @_; 1649 $cf->w_chunk_open($chunk_names{$self->{format_version}}{'OGF_S_MOTION_REFS_1'}); 1650 $cf->w_chunk_data(pack('V', $#{$self->{motion_refs_1}} + 1)); 1651 foreach my $ref (@{$self->{motion_refs_1}}) { 1652 $cf->w_chunk_data(pack('Z*', $ref)); 1653 } 1654 $cf->w_chunk_close(); 1655} 1656sub write_smotion_refs_0 { 1657 my $self = shift; 1658 my ($cf) = @_; 1659 $cf->w_chunk($chunk_names{$self->{format_version}}{'OGF_S_MOTION_REFS_0'}, pack('Z*', $self->{motion_refs_0})); 1660} 1661sub write_s_smparams { 1662 my $self = shift; 1663 my ($cf, $mode) = @_; 1664 $cf->w_chunk_open($chunk_names{$self->{format_version}}{'OGF_S_SMPARAMS_'.$mode}); 1665 $cf->w_chunk_data(pack('vv', $self->{sm_params_version}, $#{$self->{s_smparams_partitions}} + 1)); 1666 foreach my $part (@{$self->{s_smparams_partitions}}) { 1667 $cf->w_chunk_data(pack('Z*v', $part->{name}, $part->{bone_count})); 1668 foreach my $bone (@{$part->{bones}}) { 1669 if ($mode == 0 || $self->{sm_params_version} == 1) { 1670 $cf->w_chunk_data(pack('V', $bone->{bone_id})); 1671 } elsif ($self->{sm_params_version} == 2) { 1672 $cf->w_chunk_data(pack('Z*', $bone->{bone_name})); 1673 } elsif ($self->{sm_params_version} == 3 || $self->{sm_params_version} == 4) { 1674 $cf->w_chunk_data(pack('Z*V', $bone->{bone_name}, $bone->{bone_id})); 1675 } 1676 } 1677 } 1678 $cf->w_chunk_data(pack('v', $#{$self->{s_smparams_motions}} + 1)); 1679 foreach my $mot (@{$self->{s_smparams_motions}}) { 1680 if ($mode == 1) { 1681 $cf->w_chunk_data(pack('Z*V', $mot->{name}, $mot->{flags})); 1682 write_motion_def($mot, $cf); 1683 if ($self->{sm_params_version} == 4) { 1684 $cf->w_chunk_data(pack('V', $#{$mot->{mmarks}} + 1)); 1685 foreach my $nmark (@{$mot->{mmarks}}) { 1686 write_motion_mark($nmark, $cf); 1687 } 1688 } 1689 } else { 1690 my $flag = 0; 1691 if ($mot->{flags} & 0x2) { 1692 $mot->{flags} -= 0x2; 1693 $flag = 1; 1694 } 1695 $cf->w_chunk_data(pack('Z*C', $mot->{name}, $mot->{flags})); 1696 write_motion_def($mot, $cf); 1697 $cf->w_chunk_data(pack('C', $flag)); 1698 } 1699 } 1700 $cf->w_chunk_close(); 1701} 1702sub write_motion_def { 1703 my $self = shift; 1704 my ($cf) = @_; 1705 $cf->w_chunk_data(pack('vvffff', $self->{bone_or_part}, $self->{motion}, $self->{speed}, $self->{power}, $self->{accrue}, $self->{falloff})); 1706} 1707sub write_motion_mark { 1708 my $self = shift; 1709 my ($cf) = @_; 1710 my $len = length($self->{name}); 1711 $cf->w_chunk_data(pack("(a)$len", $self->{name})); 1712 $cf->w_chunk_data(pack('V', $#{$self->{intervals}} + 1)); 1713 foreach my $int (@{$self->{intervals}}) { 1714 $cf->w_chunk_data(pack('ff', $int->{min}, $int->{max})); 1715 } 1716} 1717sub write_smotions { 1718 my $self = shift; 1719 my ($cf, $mode) = @_; 1720 $cf->w_chunk_open($chunk_names{$self->{format_version}}{'OGF_S_MOTIONS_'.$mode}); 1721 $cf->w_chunk_open(0); 1722 $cf->w_chunk_data(pack('V', $self->{motions_count})); 1723 $cf->w_chunk_close(); 1724 my $id = 1; 1725 foreach my $motion (@{$self->{motions}}) { 1726 $cf->w_chunk_open($id++); 1727 write_motion($motion, $cf, $mode); 1728 $cf->w_chunk_close(); 1729 } 1730 $cf->w_chunk_close(); 1731} 1732sub write_motion { 1733 my $self = shift; 1734 my ($cf, $mode) = @_; 1735 $cf->w_chunk_data(pack('Z*V', $self->{name}, $self->{keys_count})); 1736 if ($self->{keys_count} == 0) { 1737 return; 1738 } 1739 if ($mode == 1) { 1740 foreach my $bone (@{$self->{bones}}) { 1741 $cf->w_chunk_data(pack('C', $bone->{flags})); 1742 if ($bone->{flags} & KPF_R_ABSENT) { 1743 $cf->w_chunk_data(pack('s4', @{$bone->{keysr}})); 1744 } else { 1745 $cf->w_chunk_data(pack("V(s4)$self->{keys_count}", $bone->{crc_keysr}, @{$bone->{keysr}})); 1746 } 1747 if ($bone->{flags} & KPF_T_PRESENT) { 1748 $cf->w_chunk_data(pack('V', $bone->{crc_keyst})); 1749 if ($bone->{flags} & KPF_T_HQ) { 1750 for (my $j = 0; $j < $self->{keys_count}; $j++) { 1751 $cf->w_chunk_data(pack('s3', @{$bone->{keyst}}[$j*3..$j*3+2])); 1752 } 1753 } else { 1754 for (my $j = 0; $j < $self->{keys_count}; $j++) { 1755 $cf->w_chunk_data(pack('c3', @{$bone->{keyst}}[$j*3..$j*3+2])); 1756 } 1757 } 1758 $cf->w_chunk_data(pack('f3', @{$bone->{sizet}})); 1759 } 1760 $cf->w_chunk_data(pack('f3', @{$bone->{initt}})); 1761 } 1762 } else { 1763 foreach my $bone (@{$self->{bones}}) { 1764 for (my $n = 0; $n < $self->{keys_count}; $n++) { 1765 $cf->w_chunk_data(pack('s4f3', @{$bone->{keys}}[$n*7..$n*7+6])); 1766 } 1767 } 1768 } 1769} 1770sub write_loddef2 { 1771 my $self = shift; 1772 my ($cf) = @_; 1773 $cf->w_chunk_open($chunk_names{$self->{format_version}}{'OGF_LODDEF2'}); 1774 foreach my $lod_face (@{$self->{lod_faces}}) { 1775 foreach my $vertex (@{$lod_face->{vertices}}) { 1776 $cf->w_chunk_data(pack('f3f2VCC3', @{$vertex->{v}}, @{$vertex->{t}}, $vertex->{c_rgb_hemi}, $vertex->{c_sun}, @{$vertex->{pad}})); 1777 } 1778 } 1779 $cf->w_chunk_close(); 1780} 1781sub write_treedef2 { 1782 my $self = shift; 1783 my ($cf) = @_; 1784 $cf->w_chunk_open($chunk_names{$self->{format_version}}{'OGF_TREEDEF2'}); 1785 $cf->w_chunk_data(pack('f16', @{$self->{tree_xform}})); 1786 write_ogf_color($self->{c_scale}, $cf); 1787 write_ogf_color($self->{c_bias}, $cf); 1788 $cf->w_chunk_close(); 1789} 1790sub write_ogf_color { 1791 my $self = shift; 1792 my ($cf) = @_; 1793 $cf->w_chunk_data(pack('f3ff', @{$self->{rgb}}, $self->{hemi}, $self->{sun})); 1794} 1795sub write_swicontainer { 1796 my $self = shift; 1797 my ($cf) = @_; 1798 $cf->w_chunk($chunk_names{$self->{format_version}}{'OGF_SWICONTAINER'}, pack('V', $self->{ext_swib_index})); 1799} 1800sub convert { 1801 my $self = shift; 1802 my ($nVer, $subversion) = @_; 1803 if ($nVer == $self->{format_version}) { 1804 if ($nVer == 3) { 1805 $self->subver_3($subversion); 1806 } else { 1807 $self->subver_4($subversion); 1808 } 1809 } elsif ($nVer == 3) { 1810 $self->conv_to_3(); 1811 } else { 1812 $self->conv_to_4(); 1813 } 1814} 1815sub subver_4 { 1816 my $self = shift; 1817 my ($subversion) = @_; 1818 ### Attention! Now it support only FSL_VISUALS converting! ### 1819 if ($subversion == 1 || $subversion == 2 || $subversion == 3) { 1820 if ($self->check_loaded('OGF_GCONTAINER')) { 1821 print "yes, type = $self->{type}\n"; 1822 $self->set_loaded('OGF_ICONTAINER'); 1823 $self->set_loaded('OGF_VCONTAINER'); 1824 $self->{loaded_chunks} -= $chunks_loaded{$self->{format_version}}{'OGF_GCONTAINER'}; 1825 die if $self->check_loaded('OGF_GCONTAINER'); 1826 die unless $self->check_loaded('OGF_VCONTAINER'); 1827 } 1828 if ($self->{type} == 11) { 1829 delete $self->{ext_swib_index}; 1830 $self->{type} = 7; 1831 $self->{loaded_chunks} -= $chunks_loaded{$self->{format_version}}{'OGF_SWICONTAINER'} if $self->check_loaded('OGF_SWICONTAINER'); 1832 die if $self->check_loaded('OGF_GCONTAINER'); 1833 die unless $self->check_loaded('OGF_VCONTAINER'); 1834 } 1835 if ($self->{type} == 2) { 1836 delete $self->{swi_data}; 1837 $self->{type} = 0; 1838 $self->{loaded_chunks} -= $chunks_loaded{$self->{format_version}}{'OGF_SWIDATA'} if $self->check_loaded('OGF_SWIDATA'); 1839 die if $self->check_loaded('OGF_GCONTAINER'); 1840 die unless $self->check_loaded('OGF_VCONTAINER'); 1841 } 1842 } elsif ($subversion == 4) { 1843 if ($self->check_loaded('OGF_GCONTAINER')) { 1844 $self->set_loaded('OGF_ICONTAINER'); 1845 $self->set_loaded('OGF_VCONTAINER'); 1846 $self->{loaded_chunks} -= $chunks_loaded{$self->{format_version}}{'OGF_GCONTAINER'}; 1847 } 1848 } else { 1849 if ($self->check_loaded('OGF_VCONTAINER')) { 1850 $self->set_loaded('OGF_GCONTAINER'); 1851 $self->{loaded_chunks} -= $chunks_loaded{$self->{format_version}}{'OGF_VCONTAINER'}; 1852 $self->{loaded_chunks} -= $chunks_loaded{$self->{format_version}}{'OGF_ICONTAINER'}; 1853 } 1854 } 1855} 1856sub conv_to_3 { 1857 my $self = shift; 1858 SWITCH: { 1859 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_NORMAL' && do {last SWITCH; }; 1860 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_HIERRARHY' && do {last SWITCH; }; #FIXME: childeren may vary in different builds 1861 #MT_PROGRESSIVE ??? 1862 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_PROGRESSIVE' && do { $self->swi_to_loddata(); last SWITCH; }; 1863 #MT_SKELETON_ANIM ??? 1864 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_SKELETON_ANIM' && do { die; last SWITCH; }; 1865 #MT_SKELETON_GEOMDEF_PM ??? 1866 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_SKELETON_GEOMDEF_PM' && do { $self->swi_to_loddata(); $self->{type} = 0x3; last SWITCH; }; 1867 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_SKELETON_GEOMDEF_ST' && do { $self->{type} = 0x7; last SWITCH; }; 1868 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_LOD' && do { $self->{type} = 0xb; last SWITCH; }; 1869 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_TREE_ST' && do { $self->{type} = 0xc; last SWITCH; }; 1870 #MT_SKELETON_RIGID ??? 1871 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_SKELETON_RIGID' && do { die; last SWITCH; }; 1872 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_TREE_PM' && do { $self->{type} = 0xc; last SWITCH; }; 1873 stkutils::debug::fail('ogf::conv_to_3', __LINE__, 'unexpected model type'); 1874 } 1875} 1876sub conv_to_4 { 1877 my $self = shift; 1878 SWITCH: { 1879 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_NORMAL' && do {last SWITCH; }; 1880 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_HIERRARHY' && do {last SWITCH; }; #FIXME: children may vary in different builds 1881 #MT_PROGRESSIVE ??? 1882 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_PROGRESSIVE' && do { $self->loddata_to_swi(); last SWITCH; }; 1883 #MT_SKELETON_ANIM ??? 1884 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_SKELETON_ANIM' && do { die; last SWITCH; }; 1885 #MT_SKELETON_GEOMDEF_PM ??? 1886 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_SKELETON_GEOMDEF_PM' && do { $self->loddata_to_swi(); $self->{type} = 0x4; last SWITCH; }; 1887 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_SKELETON_GEOMDEF_ST' && do { $self->{type} = 0x5; last SWITCH; }; 1888 #MT_PROGRESSIVE2 ??? 1889 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_PROGRESSIVE2' && do { die; last SWITCH; }; 1890 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_LOD' && do {$self->{type} = 0x6; last SWITCH; }; 1891 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_TREE' && do { $self->{type} = 0x7; last SWITCH; }; 1892 #MT_SKELETON_RIGID ??? 1893 $mt_names{$self->{format_version}}{$self->{type}} eq 'MT_SKELETON_RIGID' && do { die; last SWITCH; }; 1894 stkutils::debug::fail('ogf::conv_to_4', __LINE__, 'unexpected model type'); 1895 } 1896} 1897sub swi_to_loddata { 1898 my $self = shift; 1899 stkutils::debug::fail('ogf::swi_to_loddata', __LINE__, "not implemented yet"); 1900} 1901sub loddata_to_swi { 1902 my $self = shift; 1903 stkutils::debug::fail('ogf::loddata_to_swi', __LINE__, "not implemented yet"); 1904} 1905sub make_signature { 1906 my $self = shift; 1907 $self->{signature} |= S_POINTS; 1908 $self->{signature} |= S_NORMALS; 1909 $self->{signature} |= S_TEXCOORDS; 1910 if ($self->{vertex_format} != OGF_VERTEXFORMAT_FVF_OLD) { 1911 $self->{signature} |= S_INFLUENCES; 1912 } 1913} 1914sub to_object { 1915 my $self = shift; 1916 my $mesh = {}; 1917 if ($self->hierarhical()) { 1918 my $shader_id = 0; 1919 foreach my $child (@{$self->{children}}) { 1920 $child->make_signature(); 1921 $mesh->{signature} |= $child->{signature}; 1922 $child->push_to_mesh($mesh, $shader_id, $shader_id); 1923 $shader_id++; 1924 } 1925 } else { 1926 $self->push_to_mesh($mesh, 0, 0); 1927 } 1928} 1929sub push_to_mesh { 1930 my $self = shift; 1931 my ($mesh, $texture, $eshader) = @_; 1932 my $vb_offset = $self->{vertex_count} & UINT32_MAX; 1933 for (my $i = 0; $i < $self->{indices_count}; $i += 3) { 1934 my $v0 = @{$self->{indices}}[$i + 0]; 1935 my $v1 = @{$self->{indices}}[$i + 1]; 1936 my $v2 = @{$self->{indices}}[$i + 2]; 1937 my @p0 = @{$self->{vertices}[$v0]->{point}}; 1938 my @p1 = @{$self->{vertices}[$v1]->{point}}; 1939 my @p2 = @{$self->{vertices}[$v2]->{point}}; 1940 if ((join ('', @p0) cmp join ('', @p1)) || 1941 (join ('', @p1) cmp join ('', @p2)) || 1942 (join ('', @p2) cmp join ('', @p0))) { 1943 next; 1944 } 1945 my $area = calc_area(@p0, @p1, @p2); 1946 if ($area <= 0.000001) { 1947 next; 1948 } 1949 my $perim = calc_perimeter(@{$self->{vertices}[$v0]->{textcoords}}, @{$self->{vertices}[$v1]->{textcoords}}, @{$self->{vertices}[$v2]->{textcoords}}); 1950 if ($perim <= 1/8192.) { 1951 next; 1952 } 1953 my $face = set_face($self->{signature}, $vb_offset + $v0, $vb_offset + $v1, $vb_offset + $v2); 1954 push @{$mesh->{faces}}, $face; 1955 } 1956 if ($#{$mesh->{faces}} != -1) { 1957 for (my $i = $self->{vertex_count}; $i != 0; $i--) { 1958# my $ref = b_proxy($vb_offset); 1959# push @{$mesh->{refs}}, $ref; 1960 $vb_offset++; 1961 } 1962# m_vb.push(vb, &ib, xform); 1963 } 1964} 1965sub set_face { 1966 my $face = {}; 1967 my $sign = shift; 1968 if ($sign & S_POINTS) { 1969 @{$face->{v}} = @_; 1970 } 1971 if ($sign & S_NORMALS) { 1972 @{$face->{n}} = @_; 1973 } 1974 if ($sign & S_TEXCOORDS) { 1975 @{$face->{tc}} = @_; 1976 } 1977 return $face; 1978} 1979sub calc_area { 1980 my $tr1 = ($_[4] - $_[3])*($_[8] - $_[6]) - ($_[5] - $_[3])*($_[7] - $_[6]); 1981 my $tr2 = ($_[1] - $_[0])*($_[8] - $_[6]) - ($_[2] - $_[0])*($_[7] - $_[6]); 1982 my $tr3 = ($_[1] - $_[0])*($_[5] - $_[3]) - ($_[2] - $_[0])*($_[4] - $_[3]); 1983 return vector_length($tr1, $tr2, $tr3); 1984} 1985sub calc_perimeter { 1986 return (vector_length(@_[0..2]) + vector_length(@_[3..5]) + vector_length(@_[6..8])); 1987} 1988sub vector_length { 1989 return sqrt($_[0]*$_[0] + $_[1]*$_[1] + $_[2]*$_[2]); 1990} 1991sub hierrarhical { 1992 my $self = shift; 1993 my $type = $mt_names{$self->{format_version}}{$self->{type}}; 1994 if ($type eq 'MT_NORMAL' || 1995 $type eq 'MT_HIERRARHY' || 1996 $type eq 'MT_SKELETON_ANIM' || 1997 $type eq 'MT_SKELETON_RIGID') { 1998 return 1; 1999 } else {return 0;} 2000} 2001sub set_loaded { 2002 my $self = shift; 2003 my ($chunk) = @_; 2004 $self->{loaded_chunks} += $chunks_loaded{$self->{format_version}}{$chunk}; 2005} 2006sub check_loaded { 2007 my $self = shift; 2008 my ($chunk) = @_; 2009 return ($self->{loaded_chunks} & $chunks_loaded{$self->{format_version}}{$chunk}); 2010} 2011sub check_unhandled_chunks { 2012 my $self = shift; 2013 my ($cf, $fn) = @_; 2014 my %rev_names = reverse %{$chunk_names{$self->{format_version}}}; 2015 $cf->{fh}->seek(0, 0); 2016 my $fsize = -s $fn; 2017 for (my $offset = 0; $offset < $fsize;) { 2018 my $data = ''; 2019 $cf->{fh}->read($data, 8); 2020 my ($index, $size) = unpack('VV', $data); 2021 if ($index == 0 && $size ==0) { 2022 last; 2023 } 2024 if (!($self->check_loaded($rev_names{$index}))) { 2025 my $name = $rev_names{$index}; 2026 if ($name) { 2027 stkutils::debug::fail(__PACKAGE__.'::check_unhandled_chunks', __LINE__, '$self->check_loaded('.$rev_names{$index}.')', "chunk $name is unhandled"); 2028 } else { 2029 stkutils::debug::fail(__PACKAGE__.'::check_unhandled_chunks', __LINE__, '$self->check_loaded('.$index.')', "chunk $index is unhandled"); 2030 } 2031 } 2032 $cf->{fh}->seek($size, 1); 2033 $offset = tell $cf->{fh}; 2034 } 2035} 20361;