00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "eggGroup.h"
00020 #include "eggMiscFuncs.h"
00021 #include "eggVertexPool.h"
00022 #include "eggBin.h"
00023 #include "lexerDefs.h"
00024
00025 #include "indent.h"
00026 #include "string_utils.h"
00027 #include "lmatrix.h"
00028
00029
00030 TypeHandle EggGroup::_type_handle;
00031
00032
00033
00034
00035
00036
00037 EggGroup::
00038 EggGroup(const string &name) : EggGroupNode(name) {
00039 _flags = 0;
00040 _flags2 = 0;
00041 _fps = 0.0;
00042 }
00043
00044
00045
00046
00047
00048
00049 EggGroup::
00050 EggGroup(const EggGroup ©) {
00051 (*this) = copy;
00052 }
00053
00054
00055
00056
00057
00058
00059 EggGroup &EggGroup::
00060 operator = (const EggGroup ©) {
00061 EggTransform3d::operator = (copy);
00062 _flags = copy._flags;
00063 _flags2 = copy._flags2;
00064 _object_types = copy._object_types;
00065 _collision_name = copy._collision_name;
00066 _fps = copy._fps;
00067
00068 unref_all_vertices();
00069 _vref = copy._vref;
00070
00071
00072
00073 VertexRef::iterator vri;
00074 for (vri = _vref.begin(); vri != _vref.end(); ++vri) {
00075 EggVertex *vert = (*vri).first;
00076
00077 bool inserted = vert->_gref.insert(this).second;
00078
00079
00080
00081 nassertr(inserted, *this);
00082 }
00083
00084
00085
00086
00087 EggGroupNode::operator = (copy);
00088 EggRenderMode::operator = (copy);
00089
00090 return *this;
00091 }
00092
00093
00094
00095
00096
00097
00098
00099 EggGroup::
00100 ~EggGroup() {
00101 unref_all_vertices();
00102 }
00103
00104
00105
00106
00107
00108
00109 void EggGroup::
00110 set_group_type(GroupType type) {
00111 if (type != get_group_type()) {
00112
00113 nassertv((type & ~F_group_type)==0);
00114 _flags = (_flags & ~F_group_type) | type;
00115
00116
00117
00118 update_under(0);
00119 }
00120 }
00121
00122
00123
00124
00125
00126
00127
00128 bool EggGroup::
00129 has_object_type(const string &object_type) const {
00130 vector_string::const_iterator oi;
00131 for (oi = _object_types.begin(); oi != _object_types.end(); ++oi) {
00132 if (cmp_nocase_uh((*oi), object_type) == 0) {
00133 return true;
00134 }
00135 }
00136 return false;
00137 }
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147 bool EggGroup::
00148 remove_object_type(const string &object_type) {
00149 vector_string::iterator oi;
00150 for (oi = _object_types.begin(); oi != _object_types.end(); ++oi) {
00151 if (cmp_nocase_uh((*oi), object_type) == 0) {
00152 _object_types.erase(oi);
00153 return true;
00154 }
00155 }
00156 return false;
00157 }
00158
00159
00160
00161
00162
00163
00164
00165 void EggGroup::
00166 write(ostream &out, int indent_level) const {
00167 test_under_integrity();
00168
00169 switch (get_group_type()) {
00170 case GT_group:
00171 write_header(out, indent_level, "<Group>");
00172 break;
00173
00174 case GT_instance:
00175 write_header(out, indent_level, "<Instance>");
00176 break;
00177
00178 case GT_joint:
00179 write_header(out, indent_level, "<Joint>");
00180 break;
00181
00182 default:
00183
00184 nassertv(false);
00185 }
00186
00187 if (is_of_type(EggBin::get_class_type())) {
00188 indent(out, indent_level + 2)
00189 << "// Bin " << DCAST(EggBin, this)->get_bin_number() << "\n";
00190 }
00191
00192 if (has_lod()) {
00193 get_lod().write(out, indent_level + 2);
00194 }
00195
00196 if (get_billboard_type() != BT_none) {
00197 indent(out, indent_level + 2)
00198 << "<Billboard> { " << get_billboard_type() << " }\n";
00199 }
00200
00201 if (has_billboard_center()) {
00202 indent(out, indent_level + 2)
00203 << "<BillboardCenter> { " << get_billboard_center() << " }\n";
00204 }
00205
00206 if (get_cs_type() != CST_none) {
00207 indent(out, indent_level + 2) << "<Collide> ";
00208 if (has_collision_name()) {
00209 enquote_string(out, get_collision_name()) << " ";
00210 }
00211 out << "{ " << get_cs_type();
00212 if (get_collide_flags() != CF_none) {
00213 out << " " << get_collide_flags();
00214 }
00215 out << " }\n";
00216 }
00217
00218 if (has_collide_mask()) {
00219 indent(out, indent_level + 2)
00220 << "<Scalar> collide-mask { 0x";
00221 get_collide_mask().output_hex(out, 0);
00222 out << " }\n";
00223 }
00224
00225 if (has_from_collide_mask()) {
00226 indent(out, indent_level + 2)
00227 << "<Scalar> from-collide-mask { 0x";
00228 get_from_collide_mask().output_hex(out, 0);
00229 out << " }\n";
00230 }
00231
00232 if (has_into_collide_mask()) {
00233 indent(out, indent_level + 2)
00234 << "<Scalar> into-collide-mask { 0x";
00235 get_into_collide_mask().output_hex(out, 0);
00236 out << " }\n";
00237 }
00238
00239 if (get_dcs_type() != DC_none) {
00240 indent(out, indent_level + 2)
00241 << "<DCS> { " << get_dcs_type() << " }\n";
00242 }
00243
00244 if (get_dart_type() != DT_none) {
00245 indent(out, indent_level + 2)
00246 << "<Dart> { " << get_dart_type() << " }\n";
00247 }
00248
00249 if (get_switch_flag()) {
00250 indent(out, indent_level + 2) << "<Switch> { 1 }\n";
00251 if (get_switch_fps() != 0.0) {
00252 indent(out, indent_level + 2)
00253 << "<Scalar> fps { " << get_switch_fps() << " }\n";
00254 }
00255 }
00256
00257 if (has_transform()) {
00258 EggTransform3d::write(out, indent_level + 2);
00259 }
00260
00261 vector_string::const_iterator oi;
00262 for (oi = _object_types.begin(); oi != _object_types.end(); ++oi) {
00263 indent(out, indent_level + 2)
00264 << "<ObjectType> { ";
00265 enquote_string(out, (*oi)) << " }\n";
00266 }
00267
00268 if (get_model_flag()) {
00269 indent(out, indent_level + 2) << "<Model> { 1 }\n";
00270 }
00271
00272 if (get_texlist_flag()) {
00273 indent(out, indent_level + 2) << "<TexList> { 1 }\n";
00274 }
00275
00276 if (get_nofog_flag()) {
00277 indent(out, indent_level + 2) << "<Scalar> no-fog { 1 }\n";
00278 }
00279
00280 if (get_decal_flag()) {
00281 indent(out, indent_level + 2) << "<Scalar> decal { 1 }\n";
00282 }
00283
00284 if (get_direct_flag()) {
00285 indent(out, indent_level + 2) << "<Scalar> direct { 1 }\n";
00286 }
00287
00288 EggRenderMode::write(out, indent_level + 2);
00289
00290 EggGroupNode::write(out, indent_level + 2);
00291
00292 write_vertex_ref(out, indent_level + 2);
00293
00294 indent(out, indent_level) << "}\n";
00295 }
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306 EggRenderMode *EggGroup::
00307 determine_alpha_mode() {
00308 if (get_alpha_mode() != AM_unspecified) {
00309 return this;
00310 }
00311 return EggGroupNode::determine_alpha_mode();
00312 }
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323 EggRenderMode *EggGroup::
00324 determine_depth_write_mode() {
00325 if (get_depth_write_mode() != DWM_unspecified) {
00326 return this;
00327 }
00328 return EggGroupNode::determine_depth_write_mode();
00329 }
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340 EggRenderMode *EggGroup::
00341 determine_depth_test_mode() {
00342 if (get_depth_test_mode() != DTM_unspecified) {
00343 return this;
00344 }
00345 return EggGroupNode::determine_depth_test_mode();
00346 }
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357 EggRenderMode *EggGroup::
00358 determine_draw_order() {
00359 if (has_draw_order()) {
00360 return this;
00361 }
00362 return EggGroupNode::determine_draw_order();
00363 }
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374 EggRenderMode *EggGroup::
00375 determine_bin() {
00376 if (has_bin()) {
00377 return this;
00378 }
00379 return EggGroupNode::determine_bin();
00380 }
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390 void EggGroup::
00391 ref_vertex(EggVertex *vert, double membership) {
00392 VertexRef::iterator vri = _vref.find(vert);
00393
00394 if (vri != _vref.end()) {
00395
00396
00397 (*vri).second += membership;
00398
00399
00400 if ((*vri).second == 0.0) {
00401 unref_vertex(vert);
00402 }
00403
00404 } else {
00405
00406 if (membership != 0.0) {
00407 _vref[vert] = membership;
00408
00409 bool inserted = vert->_gref.insert(this).second;
00410
00411
00412
00413 nassertv(inserted);
00414 }
00415 }
00416 }
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426 void EggGroup::
00427 unref_vertex(EggVertex *vert) {
00428 VertexRef::iterator vri = _vref.find(vert);
00429
00430 if (vri != _vref.end()) {
00431 _vref.erase(vri);
00432 int count = vert->_gref.erase(this);
00433
00434
00435 nassertv(count == 1);
00436 }
00437 }
00438
00439
00440
00441
00442
00443
00444 void EggGroup::
00445 unref_all_vertices() {
00446
00447
00448 VertexRef::iterator vri;
00449 for (vri = _vref.begin(); vri != _vref.end(); ++vri) {
00450 EggVertex *vert = (*vri).first;
00451 int count = vert->_gref.erase(this);
00452
00453
00454 nassertv(count == 1);
00455 }
00456
00457 _vref.clear();
00458 }
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468 double EggGroup::
00469 get_vertex_membership(const EggVertex *vert) const {
00470 VertexRef::const_iterator vri = _vref.find((EggVertex *)vert);
00471
00472 if (vri != _vref.end()) {
00473 return (*vri).second;
00474 } else {
00475 return 0.0;
00476 }
00477 }
00478
00479
00480 #ifndef NDEBUG
00481
00482
00483
00484
00485
00486
00487
00488 void EggGroup::
00489 test_vref_integrity() const {
00490 test_ref_count_integrity();
00491
00492 VertexRef::const_iterator vri;
00493 for (vri = vref_begin(); vri != vref_end(); ++vri) {
00494 const EggVertex *vert = (*vri).first;
00495 vert->test_ref_count_integrity();
00496
00497 nassertv(vert->has_gref(this));
00498 }
00499 }
00500
00501 #endif // NDEBUG
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513 EggGroup::GroupType EggGroup::
00514 string_group_type(const string &string) {
00515 if (cmp_nocase_uh(string, "group") == 0) {
00516 return GT_group;
00517 } else if (cmp_nocase_uh(string, "instance") == 0) {
00518 return GT_instance;
00519 } else if (cmp_nocase_uh(string, "joint") == 0) {
00520 return GT_joint;
00521 } else {
00522 return GT_invalid;
00523 }
00524 }
00525
00526
00527
00528
00529
00530
00531
00532
00533 EggGroup::DartType EggGroup::
00534 string_dart_type(const string &string) {
00535 if (cmp_nocase_uh(string, "sync") == 0) {
00536 return DT_sync;
00537 } else if (cmp_nocase_uh(string, "nosync") == 0) {
00538 return DT_nosync;
00539 } else if (cmp_nocase_uh(string, "default") == 0) {
00540 return DT_default;
00541 } else {
00542 return DT_none;
00543 }
00544 }
00545
00546
00547
00548
00549
00550
00551
00552
00553 EggGroup::DCSType EggGroup::
00554 string_dcs_type(const string &string) {
00555 if (cmp_nocase_uh(string, "local") == 0) {
00556 return DC_local;
00557 } else if (cmp_nocase_uh(string, "net") == 0) {
00558 return DC_net;
00559 } else if (cmp_nocase_uh(string, "default") == 0) {
00560 return DC_default;
00561 } else {
00562 return DC_none;
00563 }
00564 }
00565
00566
00567
00568
00569
00570
00571
00572
00573 EggGroup::BillboardType EggGroup::
00574 string_billboard_type(const string &string) {
00575 if (cmp_nocase_uh(string, "axis") == 0) {
00576 return BT_axis;
00577 } else if (cmp_nocase_uh(string, "point_eye") == 0) {
00578 return BT_point_camera_relative;
00579 } else if (cmp_nocase_uh(string, "point_world") == 0) {
00580 return BT_point_world_relative;
00581 } else if (cmp_nocase_uh(string, "point") == 0) {
00582 return BT_point_world_relative;
00583 } else {
00584 return BT_none;
00585 }
00586 }
00587
00588
00589
00590
00591
00592
00593
00594
00595 EggGroup::CollisionSolidType EggGroup::
00596 string_cs_type(const string &string) {
00597 if (cmp_nocase_uh(string, "plane") == 0) {
00598 return CST_plane;
00599 } else if (cmp_nocase_uh(string, "polygon") == 0) {
00600 return CST_polygon;
00601 } else if (cmp_nocase_uh(string, "polyset") == 0) {
00602 return CST_polyset;
00603 } else if (cmp_nocase_uh(string, "sphere") == 0) {
00604 return CST_sphere;
00605 } else if (cmp_nocase_uh(string, "inversesphere") == 0) {
00606 return CST_inverse_sphere;
00607 } else if (cmp_nocase_uh(string, "geode") == 0) {
00608 return CST_geode;
00609 } else {
00610 return CST_none;
00611 }
00612 }
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623 EggGroup::CollideFlags EggGroup::
00624 string_collide_flags(const string &string) {
00625 if (cmp_nocase_uh(string, "intangible") == 0) {
00626 return CF_intangible;
00627 } else if (cmp_nocase_uh(string, "event") == 0) {
00628 return CF_event;
00629 } else if (cmp_nocase_uh(string, "descend") == 0) {
00630 return CF_descend;
00631 } else if (cmp_nocase_uh(string, "keep") == 0) {
00632 return CF_keep;
00633 } else if (cmp_nocase_uh(string, "solid") == 0) {
00634 return CF_solid;
00635 } else if (cmp_nocase_uh(string, "center") == 0) {
00636 return CF_center;
00637 } else if (cmp_nocase_uh(string, "turnstile") == 0) {
00638 return CF_turnstile;
00639 } else {
00640 return CF_none;
00641 }
00642 }
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653 void EggGroup::
00654 write_vertex_ref(ostream &out, int indent_level) const {
00655
00656
00657
00658
00659
00660 typedef pset<int> Indices;
00661 typedef pmap<double, Indices> Memberships;
00662 typedef pmap<EggVertexPool *, Memberships> Pools;
00663
00664 Pools _entries;
00665 bool all_membership_one = true;
00666
00667 VertexRef::const_iterator vri;
00668 for (vri = _vref.begin(); vri != _vref.end(); ++vri) {
00669 EggVertex *vert = (*vri).first;
00670 double membership = (*vri).second;
00671
00672 if (membership != 1.0) {
00673 all_membership_one = false;
00674 }
00675
00676 _entries[vert->get_pool()][membership].insert(vert->get_index());
00677 }
00678
00679
00680
00681 Pools::const_iterator pi;
00682 for (pi = _entries.begin(); pi != _entries.end(); ++pi) {
00683 EggVertexPool *pool = (*pi).first;
00684 const Memberships &memberships = (*pi).second;
00685 Memberships::const_iterator mi;
00686 for (mi = memberships.begin(); mi != memberships.end(); ++mi) {
00687 double membership = (*mi).first;
00688 const Indices &indices = (*mi).second;
00689
00690 indent(out, indent_level)
00691 << "<VertexRef> {\n";
00692 write_long_list(out, indent_level+2, indices.begin(), indices.end(),
00693 "", "", 72);
00694
00695
00696
00697 if (!all_membership_one) {
00698 indent(out, indent_level + 2)
00699 << "<Scalar> membership { " << membership << " }\n";
00700 }
00701 if (pool == (EggVertexPool *)NULL) {
00702 indent(out, indent_level + 2)
00703 << "// Invalid NULL vertex pool.\n";
00704 } else {
00705 indent(out, indent_level + 2)
00706 << "<Ref> { " << pool->get_name() << " }\n";
00707 }
00708 indent(out, indent_level)
00709 << "}\n";
00710 }
00711 }
00712 }
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723 bool EggGroup::
00724 egg_start_parse_body() {
00725 egg_start_group_body();
00726 return true;
00727 }
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739 void EggGroup::
00740 adjust_under() {
00741
00742
00743
00744
00745
00746 if (has_transform()) {
00747 _under_flags |= UF_under_transform;
00748
00749
00750 _node_frame =
00751 new MatrixFrame(get_transform() * get_node_frame());
00752 _node_frame_inv =
00753 new MatrixFrame(invert(get_node_frame()));
00754 _vertex_to_node =
00755 new MatrixFrame(get_vertex_frame() * get_node_frame_inv());
00756 _node_to_vertex =
00757 new MatrixFrame(get_node_frame() * get_vertex_frame_inv());
00758 }
00759
00760 if (is_instance_type()) {
00761 _under_flags |= UF_under_instance;
00762 if (_under_flags & UF_under_transform) {
00763
00764
00765
00766 _under_flags |= UF_local_coord;
00767 }
00768
00769
00770
00771
00772 _vertex_frame = _node_frame;
00773 _vertex_frame_inv = _node_frame_inv;
00774 _vertex_to_node = NULL;
00775 _node_to_vertex = NULL;
00776 }
00777 }
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790
00791
00792 void EggGroup::
00793 r_transform(const LMatrix4d &mat, const LMatrix4d &inv,
00794 CoordinateSystem to_cs) {
00795 if (!transform_is_identity()) {
00796
00797
00798
00799
00800
00801
00802
00803
00804 LMatrix4d mat1 = mat;
00805 LMatrix4d inv1 = inv;
00806
00807
00808
00809
00810 mat1.set_row(3, LVector3d(0.0, 0.0, 0.0));
00811 inv1.set_row(3, LVector3d(0.0, 0.0, 0.0));
00812
00813 internal_set_transform(inv1 * get_transform() * mat);
00814
00815 EggGroupNode::r_transform(mat1, inv1, to_cs);
00816 } else {
00817 EggGroupNode::r_transform(mat, inv, to_cs);
00818 }
00819
00820
00821 if (has_lod()) {
00822 _lod->transform(mat);
00823 }
00824 if (has_billboard_center()) {
00825 _billboard_center = _billboard_center * mat;
00826 }
00827 }
00828
00829
00830
00831
00832
00833
00834 void EggGroup::
00835 r_flatten_transforms() {
00836 EggGroupNode::r_flatten_transforms();
00837
00838 if (is_local_coord()) {
00839 LMatrix4d mat = get_vertex_frame();
00840 if (has_lod()) {
00841 _lod->transform(mat);
00842 }
00843
00844 if (get_billboard_type() != BT_none && !has_billboard_center()) {
00845
00846
00847 set_billboard_center(LPoint3d(0.0, 0.0, 0.0) * mat);
00848
00849 } else if (has_billboard_center()) {
00850 _billboard_center = _billboard_center * mat;
00851 }
00852 }
00853
00854 if (get_group_type() == GT_instance) {
00855 set_group_type(GT_group);
00856 }
00857
00858 if (get_group_type() != GT_joint) {
00859 internal_clear_transform();
00860 }
00861 }
00862
00863
00864
00865
00866
00867
00868
00869
00870 void EggGroup::
00871 transform_changed() {
00872
00873
00874
00875
00876 update_under(0);
00877 }
00878
00879
00880
00881
00882
00883
00884
00885 ostream &operator << (ostream &out, EggGroup::GroupType t) {
00886 switch (t) {
00887 case EggGroup::GT_invalid:
00888 return out << "invalid group";
00889 case EggGroup::GT_group:
00890 return out << "group";
00891 case EggGroup::GT_instance:
00892 return out << "instance";
00893 case EggGroup::GT_joint:
00894 return out << "joint";
00895 }
00896
00897 nassertr(false, out);
00898 return out << "(**invalid**)";
00899 }
00900
00901
00902
00903
00904
00905 ostream &operator << (ostream &out, EggGroup::DartType t) {
00906 switch (t) {
00907 case EggGroup::DT_none:
00908 return out << "none";
00909 case EggGroup::DT_sync:
00910 return out << "sync";
00911 case EggGroup::DT_nosync:
00912 return out << "nosync";
00913 case EggGroup::DT_default:
00914 return out << "1";
00915 }
00916
00917 nassertr(false, out);
00918 return out << "(**invalid**)";
00919 }
00920
00921
00922
00923
00924
00925 ostream &operator << (ostream &out, EggGroup::DCSType t) {
00926 switch (t) {
00927 case EggGroup::DC_none:
00928 return out << "none";
00929 case EggGroup::DC_local:
00930 return out << "local";
00931 case EggGroup::DC_net:
00932 return out << "net";
00933 case EggGroup::DC_default:
00934 return out << "1";
00935 }
00936
00937 nassertr(false, out);
00938 return out << "(**invalid**)";
00939 }
00940
00941
00942
00943
00944
00945 ostream &operator << (ostream &out, EggGroup::BillboardType t) {
00946 switch (t) {
00947 case EggGroup::BT_none:
00948 return out << "none";
00949 case EggGroup::BT_axis:
00950 return out << "axis";
00951 case EggGroup::BT_point_camera_relative:
00952 return out << "point_eye";
00953 case EggGroup::BT_point_world_relative:
00954 return out << "point_world";
00955 }
00956
00957 nassertr(false, out);
00958 return out << "(**invalid**)";
00959 }
00960
00961
00962
00963
00964
00965 ostream &operator << (ostream &out, EggGroup::CollisionSolidType t) {
00966 switch (t) {
00967 case EggGroup::CST_none:
00968 return out << "None";
00969 case EggGroup::CST_plane:
00970 return out << "Plane";
00971 case EggGroup::CST_polygon:
00972 return out << "Polygon";
00973 case EggGroup::CST_polyset:
00974 return out << "Polyset";
00975 case EggGroup::CST_sphere:
00976 return out << "Sphere";
00977 case EggGroup::CST_inverse_sphere:
00978 return out << "InverseSphere";
00979 case EggGroup::CST_geode:
00980 return out << "Geode";
00981 }
00982
00983 nassertr(false, out);
00984 return out << "(**invalid**)";
00985 }
00986
00987
00988
00989
00990
00991 ostream &operator << (ostream &out, EggGroup::CollideFlags t) {
00992 if (t == EggGroup::CF_none) {
00993 return out << "none";
00994 }
00995 int bits = (int)t;
00996 const char *space = "";
00997
00998 if (bits & EggGroup::CF_intangible) {
00999 out << space << "intangible";
01000 space = " ";
01001 }
01002 if (bits & EggGroup::CF_event) {
01003 out << space << "event";
01004 space = " ";
01005 }
01006 if (bits & EggGroup::CF_descend) {
01007 out << space << "descend";
01008 space = " ";
01009 }
01010 if (bits & EggGroup::CF_keep) {
01011 out << space << "keep";
01012 space = " ";
01013 }
01014 if (bits & EggGroup::CF_solid) {
01015 out << space << "solid";
01016 space = " ";
01017 }
01018 if (bits & EggGroup::CF_center) {
01019 out << space << "center";
01020 space = " ";
01021 }
01022 if (bits & EggGroup::CF_turnstile) {
01023 out << space << "turnstile";
01024 space = " ";
01025 }
01026 return out;
01027 }